diff --git a/.changeset/chilled-kings-fold.md b/.changeset/chilled-kings-fold.md new file mode 100644 index 0000000000..4f518e6ba3 --- /dev/null +++ b/.changeset/chilled-kings-fold.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-auth-backend-module-github-provider': patch +--- + +Fixed a bug where the requested scope was ignored when refreshing sessions for a GitHub OAuth App. This would lead to access tokens being returned that didn't have the requested scope, and in turn errors when trying to use these tokens. + +As part of this fix all existing sessions are being revoked in order to ensure that they receive the correct scope. diff --git a/.changeset/eighty-ways-train.md b/.changeset/eighty-ways-train.md new file mode 100644 index 0000000000..2b7950e567 --- /dev/null +++ b/.changeset/eighty-ways-train.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Added support for experimental form decorators when dry-running templates in the template editor. diff --git a/.changeset/fair-wolves-smash.md b/.changeset/fair-wolves-smash.md new file mode 100644 index 0000000000..cc3ffe99ee --- /dev/null +++ b/.changeset/fair-wolves-smash.md @@ -0,0 +1,12 @@ +--- +'@backstage/plugin-catalog-backend-module-backstage-openapi': patch +'@backstage/plugin-catalog-backend-module-bitbucket-server': patch +'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-catalog-backend-module-azure': patch +'@backstage/plugin-catalog-backend-module-ldap': patch +'@backstage/plugin-catalog-backend-module-aws': patch +--- + +Minor doc string update diff --git a/.changeset/funny-ladybugs-pump.md b/.changeset/funny-ladybugs-pump.md new file mode 100644 index 0000000000..2b3f17e974 --- /dev/null +++ b/.changeset/funny-ladybugs-pump.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Improved performance when applying conditional decisions to entities within the permission framework. diff --git a/.changeset/good-flowers-promise.md b/.changeset/good-flowers-promise.md new file mode 100644 index 0000000000..109cee9718 --- /dev/null +++ b/.changeset/good-flowers-promise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-backend': patch +--- + +Fixed issue `syncEntityDocs` that would cause the `/sync` endpoint to be continuously called if the request fails diff --git a/.changeset/hungry-cycles-hide.md b/.changeset/hungry-cycles-hide.md new file mode 100644 index 0000000000..55a5179029 --- /dev/null +++ b/.changeset/hungry-cycles-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-github': minor +--- + +**BREAKING**: The `remoteUrl` output is no longer required, it can be empty only when using the new `createWhenEmpty` boolean flag. diff --git a/.changeset/late-cycles-teach.md b/.changeset/late-cycles-teach.md new file mode 100644 index 0000000000..754afac42b --- /dev/null +++ b/.changeset/late-cycles-teach.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +'@backstage/plugin-scaffolder-node': minor +--- + +Added new `createTemplateFilter`, `createTemplateGlobalFunction`, `createTemplateGlobalValue` for template extensions. diff --git a/.changeset/little-mangos-reply.md b/.changeset/little-mangos-reply.md new file mode 100644 index 0000000000..5f1e96464c --- /dev/null +++ b/.changeset/little-mangos-reply.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Revert client side paginated catalog table to using built in Material Table toolbar component diff --git a/.changeset/long-camels-provide.md b/.changeset/long-camels-provide.md new file mode 100644 index 0000000000..3ebee75fc2 --- /dev/null +++ b/.changeset/long-camels-provide.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Add check to make sure that the `--link` option for the `start` command is a valid workspace. diff --git a/.changeset/polite-pumas-joke.md b/.changeset/polite-pumas-joke.md new file mode 100644 index 0000000000..9b026abc0e --- /dev/null +++ b/.changeset/polite-pumas-joke.md @@ -0,0 +1,18 @@ +--- +'@backstage/cli': minor +--- + +The `new` command is now powered by a new template system that allows you to define your own templates in a declarative way, as well as import existing templates from external sources. See the [CLI templates documentation](https://backstage.io/docs/tooling/cli/templates) for more information. + +The following flags for the `new` command have been deprecated and will be removed in a future release: + +- `--license=`: Configure the global `license` instead. +- `--no-private`: Configure the global `private` instead. +- `--baseVersion=`: Configure the global `version` instead. +- `--npmRegistry=`: Configure the global `publishRegistry` instead. +- `--scope=`: Configure the global `namePrefix` and/or `namePluginInfix` instead. + +As part of this change the template IDs and their options have changed. The following backwards compatibility mappings for the `--select` and `--option` flags are enabled when using the default set of templates, but they will also be removed in the future: + +- `--select=plugin` is mapped to `--select=frontend-plugin` instead. +- `--option=id=` is mapped to `--option=pluginId=` instead. diff --git a/.changeset/pre.json b/.changeset/pre.json index 843ffbb2f9..23c72bf7f9 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -223,6 +223,7 @@ "eleven-mice-sleep", "fair-mangos-sleep", "fair-rocks-dream", + "fast-rabbits-unite", "few-ducks-cross", "few-shrimps-kiss", "fifty-humans-repair", @@ -230,20 +231,29 @@ "fifty-trains-attend", "fifty-turtles-count", "fluffy-carrots-wink", + "fresh-bears-thank", "gentle-actors-sleep", "gold-cameras-battle", + "gold-rabbits-change", + "gorgeous-shoes-stare", "green-jokes-provide", "grumpy-crews-sneeze", "happy-windows-shop", + "heavy-bobcats-search", "heavy-olives-thank", "hip-hotels-type", "hot-mice-cry", "hot-planes-hear", + "hungry-chefs-relax", + "hungry-cycles-hide", "hungry-mirrors-sniff", "hungry-rings-live", "large-pans-brake", + "late-cycles-teach", + "little-mangos-reply", "loud-baboons-relate", "loud-walls-build", + "lucky-seas-live", "many-goats-flash", "metal-worms-live", "neat-singers-rhyme", @@ -260,7 +270,10 @@ "orange-brooms-lick", "plenty-bobcats-drum", "polite-apricots-exercise", + "polite-pumas-joke", + "pretty-apricots-poke", "pretty-worms-walk", + "proud-dolls-behave", "proud-dryers-act", "proud-hornets-cheer", "purple-years-cross", @@ -276,15 +289,24 @@ "slow-bananas-beam", "smart-ligers-sniff", "soft-planets-mate", + "soft-seahorses-relate", "tall-scissors-sip", "tame-hats-shout", "thirty-dancers-cheat", + "three-glasses-sell", + "tidy-forks-pay", "tidy-oranges-allow", + "tiny-spoons-repeat", "twelve-eyes-stare", + "two-schools-deny", "warm-masks-ring", + "wet-bees-heal", "wise-apes-juggle", + "wise-cycles-sparkle", "witty-clocks-thank", + "witty-ducks-cross", "witty-pets-march", + "yellow-rules-vanish", "young-plants-report" ] } diff --git a/.changeset/proud-dolls-behave.md b/.changeset/proud-dolls-behave.md new file mode 100644 index 0000000000..8a93bdb4bf --- /dev/null +++ b/.changeset/proud-dolls-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Support file globbing in fs:delete diff --git a/.changeset/seven-stingrays-suffer.md b/.changeset/seven-stingrays-suffer.md new file mode 100644 index 0000000000..a5b8695d2a --- /dev/null +++ b/.changeset/seven-stingrays-suffer.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-notifications': patch +'@backstage/plugin-signals': patch +--- + +Added support for the new frontend system via `/alpha` export. diff --git a/.changeset/soft-seahorses-relate.md b/.changeset/soft-seahorses-relate.md new file mode 100644 index 0000000000..a17ff1aea4 --- /dev/null +++ b/.changeset/soft-seahorses-relate.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Fix spacing props not being applied for custom values. diff --git a/.changeset/stale-eagles-cheer.md b/.changeset/stale-eagles-cheer.md new file mode 100644 index 0000000000..0f8d0970ed --- /dev/null +++ b/.changeset/stale-eagles-cheer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-node': patch +--- + +Added `scopeAlreadyGranted` property to `OAuthAuthenticatorRefreshInput`, signaling to the provider whether the requested scope has already been granted when persisting session scope. diff --git a/.changeset/stale-pens-serve.md b/.changeset/stale-pens-serve.md new file mode 100644 index 0000000000..b37e2054dc --- /dev/null +++ b/.changeset/stale-pens-serve.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Replaced dependency `node-libs-browser` with `node-stdlib-browser` diff --git a/.changeset/three-glasses-sell.md b/.changeset/three-glasses-sell.md new file mode 100644 index 0000000000..9714f6f184 --- /dev/null +++ b/.changeset/three-glasses-sell.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-app-api': patch +--- + +Add support for defining multiple attachment points for extensions and blueprints. diff --git a/.changeset/tough-crabs-hope.md b/.changeset/tough-crabs-hope.md new file mode 100644 index 0000000000..50b36b45f4 --- /dev/null +++ b/.changeset/tough-crabs-hope.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities': patch +--- + +Added alpha support for the New Frontend System diff --git a/.changeset/twenty-carrots-jog.md b/.changeset/twenty-carrots-jog.md new file mode 100644 index 0000000000..f4b4db8fcb --- /dev/null +++ b/.changeset/twenty-carrots-jog.md @@ -0,0 +1,31 @@ +--- +'@backstage/backend-app-api': patch +--- + +Added a configuration to permit backend plugin failures on startup: + +```yaml +backend: + ... + startup: + plugins: + plugin-x: + onPluginBootFailure: continue +``` + +This configuration permits `plugin-x` to fail on startup. Omitting the +`onPluginBootFailure` configuration matches the previous behavior, wherein any +individual plugin failure aborts backend startup. + +The default can also be changed, so that continuing on failure is the default +unless otherwise specified: + +```yaml +backend: + startup: + default: + onPluginBootFailure: continue + plugins: + catalog: + onPluginBootFailure: abort +``` diff --git a/.changeset/two-schools-deny.md b/.changeset/two-schools-deny.md new file mode 100644 index 0000000000..aca7242f15 --- /dev/null +++ b/.changeset/two-schools-deny.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +Updated core CSS tokens and fixing the Button component accordingly. diff --git a/.changeset/wise-cycles-sparkle.md b/.changeset/wise-cycles-sparkle.md new file mode 100644 index 0000000000..1605211caa --- /dev/null +++ b/.changeset/wise-cycles-sparkle.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder': patch +--- + +Added conditional rendering of `oneOf` output schemas on the Installed Actions page for scaffolder actions diff --git a/.changeset/yellow-rules-vanish.md b/.changeset/yellow-rules-vanish.md new file mode 100644 index 0000000000..23fff893f6 --- /dev/null +++ b/.changeset/yellow-rules-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Use template title for ongoing task page header diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index 936b0ec8b6..5e92157fdf 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -157,6 +157,7 @@ Gitiles gitlab GitLab globals +globbing Gource Grafana graphql @@ -328,6 +329,7 @@ preconfigured prepack Preprarer productional +projectSlug Protobuf proxied proxying @@ -425,6 +427,7 @@ subheaders subkey subpath subroutes +substring subtree superfences Superfences diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 34d34ef5a6..4a33b2241e 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -3,8 +3,6 @@ on: push: branches: - master - paths: - - '.github/workflows/sync_version-packages.yml' concurrency: group: sync-version-packages diff --git a/canon-docs/package.json b/canon-docs/package.json index 69fd1cae30..8558fac8e3 100644 --- a/canon-docs/package.json +++ b/canon-docs/package.json @@ -6,7 +6,7 @@ "build": "npm run build:css && next build", "build:css": "node scripts/build-css.js", "lint": "next lint", - "start": "next dev" + "start": "npm run build:css && next dev" }, "dependencies": { "@codemirror/lang-sass": "^6.0.2", diff --git a/canon-docs/scripts/build-css.js b/canon-docs/scripts/build-css.js index be85164347..dd9775150f 100644 --- a/canon-docs/scripts/build-css.js +++ b/canon-docs/scripts/build-css.js @@ -9,11 +9,6 @@ const source1 = path.join(__dirname, `${source}/core.css`); const destination1 = path.join(__dirname, `${destination}/core.css`); const source2 = path.join(__dirname, `${source}/components.css`); const destination2 = path.join(__dirname, `${destination}/components.css`); -const source3 = path.join( - __dirname, - `../../packages/canon/.storybook/themes/backstage.css`, -); -const destination3 = path.join(__dirname, `${destination}/backstage.css`); // Function to bundle and copy the CSS file const bundleAndCopyFile = async (source, destination) => { @@ -34,7 +29,6 @@ const bundleAndCopyFile = async (source, destination) => { Promise.all([ bundleAndCopyFile(source1, destination1), bundleAndCopyFile(source2, destination2), - bundleAndCopyFile(source3, destination3), ]) .then(() => { // Add an empty line after all operations are complete - It looks better in the terminal :) diff --git a/canon-docs/src/app/(docs)/components/button/page.mdx b/canon-docs/src/app/(docs)/components/button/page.mdx index b1276157b6..c92b5b9921 100644 --- a/canon-docs/src/app/(docs)/components/button/page.mdx +++ b/canon-docs/src/app/(docs)/components/button/page.mdx @@ -11,6 +11,7 @@ import { ButtonResponsive, } from '@/snippets/button'; import { buttonVariants } from '@/snippets/_snippets'; +import { buttonPropDefs } from '../../../../../../packages/canon/src/components/Button/Button.props'; # Button @@ -58,34 +59,7 @@ A button component that can be used to trigger actions. ## API reference - + ## Examples diff --git a/canon-docs/src/app/(docs)/theme/theming/page.mdx b/canon-docs/src/app/(docs)/theme/theming/page.mdx index 4cb03859f9..6ecc2cba58 100644 --- a/canon-docs/src/app/(docs)/theme/theming/page.mdx +++ b/canon-docs/src/app/(docs)/theme/theming/page.mdx @@ -186,7 +186,7 @@ are prefixed with `fg` to make it easier to identify. - --canon-fg-text-primary + --canon-fg-primary It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`. @@ -194,7 +194,7 @@ are prefixed with `fg` to make it easier to identify. - --canon-fg-text-secondary + --canon-fg-secondary It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`. @@ -280,7 +280,7 @@ low contrast to help as a separator with the different background colors. - --canon-border-focus + --canon-border-pressed Used when the component is interactive and focused. diff --git a/canon-docs/src/components/HeadlessBanners/styles.module.css b/canon-docs/src/components/HeadlessBanners/styles.module.css index 31d08aedb4..c87787634e 100644 --- a/canon-docs/src/components/HeadlessBanners/styles.module.css +++ b/canon-docs/src/components/HeadlessBanners/styles.module.css @@ -10,7 +10,7 @@ } .icon path { - fill: var(--canon-fg-text-primary); + fill: var(--canon-fg-primary); } .content { diff --git a/canon-docs/src/components/PropsTable/PropsTable.tsx b/canon-docs/src/components/PropsTable/PropsTable.tsx index 69bff9ffe6..aa6b9f296e 100644 --- a/canon-docs/src/components/PropsTable/PropsTable.tsx +++ b/canon-docs/src/components/PropsTable/PropsTable.tsx @@ -6,8 +6,9 @@ import { icons } from '../../../../packages/canon'; // Define a more specific type for the data object type PropData = { - type: string | string[]; - responsive: boolean; + values?: string | string[]; + responsive?: boolean; + default?: string; }; // Modify the PropsTable component to use the new type @@ -16,17 +17,34 @@ export const PropsTable = >({ }: { data: T; }) => { + const completeData = { + ...data, + children: { + values: 'ReactNode', + responsive: false, + }, + className: { + values: 'string', + responsive: false, + }, + style: { + values: 'CSSProperties', + responsive: false, + }, + }; + return ( Prop Type + Default Responsive - {Object.keys(data).map(n => ( + {Object.keys(completeData).map(n => ( {n} @@ -35,17 +53,22 @@ export const PropsTable = >({
- {data[n].type === 'icon' ? ( + {completeData[n].values === 'icon' ? ( Object.keys(icons).map(icon => {icon}) - ) : Array.isArray(data[n].type) ? ( - data[n].type.map(t => {t}) + ) : Array.isArray(completeData[n].values) ? ( + completeData[n].values.map(t => {t}) ) : ( - {data[n].type} + {completeData[n].values} )}
- {data[n].responsive ? 'Yes' : 'No'} + + {completeData[n].default ? completeData[n].default : '-'} + + + + {completeData[n].responsive ? 'Yes' : 'No'}
))} diff --git a/canon-docs/src/snippets/button.tsx b/canon-docs/src/snippets/button.tsx index 2ae2ab0be9..a7df4315f9 100644 --- a/canon-docs/src/snippets/button.tsx +++ b/canon-docs/src/snippets/button.tsx @@ -11,9 +11,6 @@ export const ButtonPreview = () => { - ); }; diff --git a/contrib/catalog/ImmediateEntityProvider.ts b/contrib/catalog/ImmediateEntityProvider.ts index 33ae6697f6..b5580b775a 100644 --- a/contrib/catalog/ImmediateEntityProvider.ts +++ b/contrib/catalog/ImmediateEntityProvider.ts @@ -61,12 +61,12 @@ export class ImmediateEntityProvider implements EntityProvider { this.entityValidator = entitySchemaValidator(); } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName() { return `ImmediateEntityProvider`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection) { this.connection = connection; } diff --git a/contrib/catalog/LoadTestingEntityProvider.ts b/contrib/catalog/LoadTestingEntityProvider.ts index 598099fce9..573d8a5fe0 100644 --- a/contrib/catalog/LoadTestingEntityProvider.ts +++ b/contrib/catalog/LoadTestingEntityProvider.ts @@ -6,7 +6,7 @@ import { import { EntityProvider, EntityProviderConnection, -} from '@backstage/plugin-catalog-backend'; +} from '@backstage/plugin-catalog-node'; import { Logger } from 'winston'; /** @@ -41,12 +41,12 @@ import { Logger } from 'winston'; export class LoadTestingEntityProvider implements EntityProvider { constructor(private readonly options: LoadTestingEntityProviderOptions) {} - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName() { return `LoadTestingEntityProvider`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection) { const delayStartup = this.options.delayStartup ?? 10_000; const logger = this.options.logger.child({ diff --git a/docs/conf/writing.md b/docs/conf/writing.md index 85a719e1d0..36a6f330e0 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -239,7 +239,8 @@ privateKey: | -----END RSA PRIVATE KEY----- ``` -> [!WARNING] -> Sensitive information, such as private keys, should not be hard coded. +:::warning +Sensitive information, such as private keys, should not be hard coded. +::: We recommend that this entire file should be a secret and stored as such in a secure storage solution like Vault, to ensure they are neither exposed nor misused. This example key part only shows the format on how to use the yaml | syntax to make sure that the key is valid. diff --git a/docs/features/kubernetes/installation.md b/docs/features/kubernetes/installation.md index aca4c19ac1..a99bd0b429 100644 --- a/docs/features/kubernetes/installation.md +++ b/docs/features/kubernetes/installation.md @@ -7,20 +7,17 @@ description: Installing Kubernetes plugin into Backstage The Kubernetes feature is a plugin to Backstage, and it is exposed as a tab when viewing entities in the software catalog. -If you haven't setup Backstage already, read the -[Getting Started](../../getting-started/index.md) guide. +If you haven't set up Backstage already, read the [Getting Started](../../getting-started/index.md) guide. ## Adding the Kubernetes frontend plugin -The first step is to add the Kubernetes frontend plugin to your Backstage -application. +The first step is to add the Kubernetes frontend plugin to your Backstage application. ```bash title="From your Backstage root directory" yarn --cwd packages/app add @backstage/plugin-kubernetes ``` -Once the package has been installed, you need to import the plugin in your app -by adding the "Kubernetes" tab to the respective catalog pages. +Once the package has been installed, you need to import the plugin in your app by adding the "Kubernetes" tab to the respective catalog pages. ```tsx title="packages/app/src/components/catalog/EntityPage.tsx" /* highlight-add-next-line */ @@ -40,73 +37,17 @@ const serviceEntityPage = ( ); ``` -**Notes:** +:::note Note -- The optional `refreshIntervalMs` property on the `EntityKubernetesContent` defines the interval in which the content automatically refreshes, if not set this will default to 10 seconds. +The optional `refreshIntervalMs` property on the `EntityKubernetesContent` defines the interval in which the content automatically refreshes, if not set this will default to 10 seconds. -That's it! But now, we need the Kubernetes Backend plugin for the frontend to -work. +::: + +That's it! But now, we need the Kubernetes Backend plugin for the frontend to work. ## Adding Kubernetes Backend plugin -Navigate to `packages/backend` of your Backstage app, and install the -`@backstage/plugin-kubernetes-backend` package. - -```bash title="From your Backstage root directory" -yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend -``` - -Create a file called `kubernetes.ts` inside `packages/backend/src/plugins/` and -add the following: - -```ts title="packages/backend/src/plugins/kubernetes.ts" -import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; -import { CatalogClient } from '@backstage/catalog-client'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const catalogApi = new CatalogClient({ discoveryApi: env.discovery }); - const { router } = await KubernetesBuilder.createBuilder({ - logger: env.logger, - config: env.config, - catalogApi, - discovery: env.discovery, - permissions: env.permissions, - }).build(); - return router; -} -``` - -And import the plugin to `packages/backend/src/index.ts`. There are three lines -of code you'll need to add, and they should be added near similar code in your -existing Backstage backend. - -```typescript title="packages/backend/src/index.ts" -// .. -/* highlight-add-next-line */ -import kubernetes from './plugins/kubernetes'; - -async function main() { - // ... - /* highlight-add-next-line */ - const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes')); - // ... - /* highlight-add-next-line */ - apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv)); -} -``` - -That's it! The Kubernetes frontend and backend have now been added to your -Backstage app. - -### New Backend System - -To get the Kubernetes plugin install using the New Backend System you will need to do the following: - -Run this command to add the package: +First, we need to add the backend package: ```bash title="From your Backstage root directory" yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend @@ -126,6 +67,9 @@ backend.add(import('@backstage/plugin-kubernetes-backend')); backend.start(); ``` +That's it! The Kubernetes frontend and backend have now been added to your +Backstage app. + ### Custom cluster discovery If either existing @@ -133,22 +77,18 @@ If either existing don't work for your use-case, it is possible to implement a custom [KubernetesClustersSupplier](https://backstage.io/docs/reference/plugin-kubernetes-backend.kubernetesclusterssupplier). -Change the following in `packages/backend/src/plugins/kubernetes.ts`: +Here's a very simplified example: -```ts title="packages/backend/src/plugins/kubernetes.ts" -import { - /* highlight-add-next-line */ - ClusterDetails, - KubernetesBuilder, - /* highlight-add-next-line */ - KubernetesClustersSupplier, -} from '@backstage/plugin-kubernetes-backend'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; -/* highlight-add-next-line */ +```ts title="packages/backend/src/index.ts" +import { createBackend } from '@backstage/backend-defaults'; +import { createBackendModule } from '@backstage/backend-plugin-api'; import { Duration } from 'luxon'; +import { + ClusterDetails, + KubernetesClustersSupplier, + kubernetesClusterSupplierExtensionPoint, +} from '@backstage/plugin-kubernetes-node'; -/* highlight-add-start */ export class CustomClustersSupplier implements KubernetesClustersSupplier { constructor(private clusterDetails: ClusterDetails[] = []) {} @@ -170,43 +110,6 @@ export class CustomClustersSupplier implements KubernetesClustersSupplier { return this.clusterDetails; } } -/* highlight-add-end */ - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - - /* highlight-remove-next-line */ - const { router } = await KubernetesBuilder.createBuilder({ - /* highlight-add-next-line */ - const builder = await KubernetesBuilder.createBuilder({ - logger: env.logger, - config: env.config, - /* highlight-remove-next-line */ - }).build(); - /* highlight-add-start */ - }); - builder.setClusterSupplier( - CustomClustersSupplier.create(Duration.fromObject({ minutes: 60 })), - ); - const { router } = await builder.build(); - /* highlight-add-end */ - - // .. - return router; -} -``` - -### New Backend System Custom cluster discovery - -To use Custom cluster discovery with the New Backend System you'll need to create a module and add it to your backend. Here's a very simplified example: - -```ts title="packages/backend/src/index.ts" -import { createBackend } from '@backstage/backend-defaults'; -import { createBackendModule } from '@backstage/backend-plugin-api'; -import { Duration } from 'luxon'; -import { kubernetesClusterSupplierExtensionPoint } from '@backstage/plugin-kubernetes-node'; -import { CustomClustersSupplier } from './path/to/class'; const backend = createBackend(); @@ -236,7 +139,7 @@ backend.start(); :::note Note -This example assumes the `CustomClustersSupplier` class is the same from the [previous example](#custom-cluster-discovery) +This example uses items from the `@backstage/plugin-kubernetes-node` and `luxon` packages, you'll need to add those for this example to work as is. ::: diff --git a/docs/features/software-catalog/catalog-customization.md b/docs/features/software-catalog/catalog-customization.md index c56f763a4e..5c786a6f48 100644 --- a/docs/features/software-catalog/catalog-customization.md +++ b/docs/features/software-catalog/catalog-customization.md @@ -17,7 +17,7 @@ Initial support for pagination of the `CatalogIndexPage` was added in v1.21.0 of ## Initially Selected Filter -By default the initially selected filter defaults to Owned. If you are still building up your catalog this may show an empty list to start. If you would prefer this to show All as the default, here's how you can make that change: +By default, the initially selected filter defaults to Owned. If you are still building up your catalog this may show an empty list to start. If you would prefer this to show All as the default, here's how you can make that change: ```tsx title="packages/app/src/App.tsx" } /> @@ -70,9 +70,7 @@ The columns you see in the `CatalogIndexPage` were selected to be a good startin Suppose we want to add a new User Email column to the `User` kind in the Catalog. We can do this by overriding the `columns` that we pass into the `CatalogIndexPage` component in our `App.tsx`. First, we need to match the entity kind that we want to override, and then define the columns to show: ```tsx title="packages/app/src/App.tsx" -{ - /* highlight-add-start */ -} +{/* prettier-ignore */ /* highlight-add-start */} const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => { if (entityListContext.filters.kind?.value === 'user') { return [ @@ -84,9 +82,7 @@ const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => { return CatalogTable.defaultColumnsFunc(entityListContext); }; -{ - /* highlight-add-end */ -} +{/* prettier-ignore */ /* highlight-add-end */} ``` Then, we can implement the `createUserEmailColumn` function and add it to the list of columns. `field` is used to access the data from the entity, while `render` lets us customize how we display the data: @@ -107,7 +103,6 @@ const createUserEmailColumn = (): TableColumn => ({ const myColumnsFunc: CatalogTableColumnsFunc = entityListContext => { if (entityListContext.filters.kind?.value === 'user') { - return [ return [ // Render existing columns ...CatalogTable.defaultColumnsFunc(entityListContext), @@ -391,13 +386,9 @@ export const EntitySecurityTierPicker = () => { Now we can add the component to `CatalogIndexPage`: ```tsx title="packages/app/src/App.tsx" -{ - /* highlight-add-start */ -} +{/* prettier-ignore */ /* highlight-add-start */} import { DefaultFilters } from '@backstage/plugin-catalog-react'; -{ - /* highlight-add-end */ -} +{/* prettier-ignore */ /* highlight-add-end */} const routes = ( diff --git a/docs/features/software-catalog/extending-the-model--old.md b/docs/features/software-catalog/extending-the-model--old.md index 7368a4a339..668d1d8311 100644 --- a/docs/features/software-catalog/extending-the-model--old.md +++ b/docs/features/software-catalog/extending-the-model--old.md @@ -495,8 +495,8 @@ want to have an isomorphic package that houses these types. Within the Backstage main repo the package naming pattern of `-common` is used for isomorphic packages, and you may choose to adopt this pattern as well. -You can generate an isomorphic plugin package by running:`yarn new --select plugin-common` -or you can run `yarn new` and then select "plugin-common" from the list of options +You can generate an isomorphic plugin package by running: `yarn new` and then +select "plugin-common" from the list of options There's at this point no existing templates for generating isomorphic plugins using the `@backstage/cli`. Perhaps the simplest way to get started right now is diff --git a/docs/features/software-catalog/extending-the-model.md b/docs/features/software-catalog/extending-the-model.md index f9d20d03c2..d289886a09 100644 --- a/docs/features/software-catalog/extending-the-model.md +++ b/docs/features/software-catalog/extending-the-model.md @@ -495,8 +495,8 @@ want to have an isomorphic package that houses these types. Within the Backstage main repo the package naming pattern of `-common` is used for isomorphic packages, and you may choose to adopt this pattern as well. -You can generate an isomorphic plugin package by running:`yarn new --select plugin-common` -or you can run `yarn new` and then select "plugin-common" from the list of options +You can generate an isomorphic plugin package by running: `yarn new` and then +selecting "plugin-common" from the list of options There's at this point no existing templates for generating isomorphic plugins using the `@backstage/cli`. Perhaps the simplest way to get started right now is @@ -520,7 +520,7 @@ validate entities of our new kind. Just like with the definition package, you can find inspiration in for example the existing [ScaffolderEntitiesProcessor](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend-module-scaffolder-entity-model/src/processor/ScaffolderEntitiesProcessor.ts). -The custom processor should be created as a separate module for the catalog plugin. For information on how to set that up, see the [plugin docs](../../plugins/backend-plugin.md#creating-a-backend-plugin). Use `yarn new --select backend-module` instead to create a module. For our case, the module ID will be `foobar` and the plugin ID will be `catalog`. +The custom processor should be created as a separate module for the catalog plugin. For information on how to set that up, see the [plugin docs](../../plugins/backend-plugin.md#creating-a-backend-plugin). Use `yarn new` and select `backend-module` instead to create a module. For our case, the module ID will be `foobar` and the plugin ID will be `catalog`. We also provide a high-level example of what a catalog process for a custom entity might look like: diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index f721c50ec2..6b57922166 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -72,7 +72,7 @@ putting all extensions like this in a backend module package of their own in the `plugins` folder of your Backstage repo: ```sh -yarn new --select backend-module --option id=catalog +yarn new --select backend-module --option pluginId=catalog ``` The class will have this basic structure: @@ -650,7 +650,7 @@ putting all extensions like this in a backend module package of their own in the `plugins` folder of your Backstage repo: ```sh -yarn new --select backend-module --option id=catalog +yarn new --select backend-module --option pluginId=catalog ``` The class will have this basic structure: diff --git a/docs/features/software-templates/template-extensions.md b/docs/features/software-templates/template-extensions.md new file mode 100644 index 0000000000..a43f590e9f --- /dev/null +++ b/docs/features/software-templates/template-extensions.md @@ -0,0 +1,502 @@ +--- +id: template-extensions +title: Template Extensions +description: Template extensions system +--- + +Backstage templating is powered by [Nunjucks][]. The basics: + +# Template Filters + +The [filter][] is a critical mechanism for the rendering of Nunjucks templates, +providing a means of transforming values in a familiar [piped][] fashion. +Template filters are functions that help you transform data, extract specific +information, and perform various operations in Scaffolder Templates. + +## Built-in + +Backstage provides out of the box the following set of "built-in" template +filters (to create your own custom filters, look to the section [Custom Filter](#custom-filter) hereafter): + +### parseRepoUrl + +The `parseRepoUrl` filter parses a repository URL into its constituent parts: +`owner`, repository name (`repo`), etc. + +**Usage Example:** + +```yaml +- id: log + name: Parse Repo URL + action: debug:log + input: + message: ${{ parameters.repoUrl | parseRepoUrl }} +``` + +- **Input**: `github.com?repo=backstage&owner=backstage` +- **Output**: "RepoSpec" (see [parseRepoUrl][]) + +### parseEntityRef + +The `parseEntityRef` filter allows you to extract different parts of +an entity reference, such as the `kind`, `namespace`, and `name`. + +**Usage example** + +1. Without context + +```yaml +- id: log + name: Parse Entity Reference + action: debug:log + input: + message: ${{ parameters.owner | parseEntityRef }} +``` + +- **Input**: `group:techdocs` +- **Output**: [CompoundEntityRef][] + +1. With context + +```yaml +- id: log + name: Parse Entity Reference + action: debug:log + input: + message: ${{ parameters.owner | parseEntityRef({ defaultKind:"group", defaultNamespace:"another-namespace" }) }} +``` + +- **Input**: `techdocs` +- **Output**: [CompoundEntityRef][] + +### pick + +The `pick` filter allows you to select a specific property (e.g. `kind`, `namespace`, `name`) from an object. + +**Usage Example** + +```yaml +- id: log + name: Pick + action: debug:log + input: + message: ${{ parameters.owner | parseEntityRef | pick('name') }} +``` + +- **Input**: `{ kind: 'Group', namespace: 'default', name: 'techdocs' }` +- **Output**: `techdocs` + +### projectSlug + +The `projectSlug` filter generates a project slug from a repository URL. + +**Usage Example** + +```yaml +- id: log + name: Project Slug + action: debug:log + input: + message: ${{ parameters.repoUrl | projectSlug }} +``` + +- **Input**: `github.com?repo=backstage&owner=backstage` +- **Output**: `backstage/backstage` + +# Template Globals + +In addition to its powerful filtering functionality, the Nunjucks engine allows +access from the template expression context to specified globally-accessible +references. Backstage propagates this capability via the scaffolder backend +plugin, which we shall soon see in action. + +# Customizing the templating environment + +Custom plugins make it possible to install your own template extensions, which +may be any combination of filters, global functions and global values. With the +new backend you would use a scaffolder plugin module for this; later we will +demonstrate the analogous approach with the old backend. + +## Streamlining Template Extension Module Creation with the Backstage CLI + +The creation of a "template environment customization" module in Backstage can +be accelerated using the Backstage CLI. + +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 +? What do you want to create? +> backend-module - A new backend module that extends an existing backend plugin with additional features + backend-plugin - A new backend plugin + plugin - A new frontend plugin + node-library - A new node-library package, exporting shared functionality for backend plugins and modules + plugin-common - A new isomorphic common plugin package + plugin-node - A new Node.js library plugin package + plugin-react - A new web library plugin package + scaffolder-module - An module exporting custom actions for @backstage/plugin-scaffolder-backend +``` + +When prompted, select the option to generate a backend module. +Since we want to extend the Scaffolder backend, enter `scaffolder` when prompted for the plugin to extend. +Next, enter a name for your module (relative to the generated `scaffolder-backend-module-` prefix), +and the CLI will generate the required files and directory structure. + +## Writing your Module + +Once the CLI has generated the essential structure for your new scaffolder +module, it's time to implement our template extensions. Here we'll demonstrate +how to create each of the supported extension types. + +`src/module.ts` is where the magic happens. First we prepare to utilize the +associated (_**alpha** phase_) API extension point by adding: + +```ts +import { scaffolderTemplatingExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +``` + +Considering the generated code, you may observe that everything rests on the +`createBackendModule` call, which after providing some minimal metadata to +establish context, specifies a `register` callback whose sole responsibility +here is to call, in turn, `registerInit` against the +`BackendModuleRegistrationPoints` argument it receives. Modify this call to +make the `scaffolderTemplatingExtensionPoint` available to the specified `init` +function: + +```ts + register(reg) { + reg.registerInit({ + deps: { + ..., + templating: scaffolderTemplatingExtensionPoint, + }, + async init({ + ..., + templating + }) { + ... + }; + }); + }; +``` + +Now we're ready to extend the scaffolder templating engine. For our purposes +here we'll drop everything in `module.ts`; use your own judgment as to the +organization of your real-world plugin modules. + +### Custom Filter + +In this contrived example we add a filter to test whether the incoming string +value contains (at least) a specified number of occurrences of a given +substring. We can easily define this by adding code to our `init` callback: + +```ts +async init({ + ..., + templating, +}) { + ... + templating.addTemplateFilters({ + containsOccurrences: (arg: string, substring: string, times: number) => { + let pos = 0; + let count = 0; + while (pos < arg.length) { + pos = arg.indexOf(substring, pos); + if (pos < 0) { + break; + } + count++; + } + return count === times; + }, + }); +}, +``` + +This demonstrates the bare minimum: a TypeScript `Record` of named template +filter implementations to register. However, by adopting an alternate structure +we can document our filter with additional metadata; to utilize this capability +we begin by adding a new import: + +```ts +import { createTemplateFilter } from '@backstage/plugin-scaffolder-node/alpha'; +``` + +Then, update your `init` implementation to specify an array rather than an +object/record: + +```ts +async init({ + ..., + templating, +}) { + ... + templating.addTemplateFilters([ + createTemplateFilter({ + id: 'containsOccurrences', + description: 'determine whether filter input contains a substring N times', + filter: (arg: string, substring: string, times: number) => { + let pos = 0; + let count = 0; + while (pos < arg.length) { + pos = arg.indexOf(substring, pos); + if (pos < 0) { + break; + } + count++; + } + return count === times; + }, + }), + ]); +}, +``` + +With this we have added a `description` to our filter, which helps a template +author to understand the filter's purpose. + +#### Schema + +To enhance our filter documentation further, we will specify its `schema` +using a callback against the [Zod][] schema declaration library: + +```ts + createTemplateFilter({ + id: 'containsOccurrences', + description: 'determine whether filter input contains a substring N times', + schema: z => + z.function( + z.tuple([ + z.string().describe('input'), + z.string().describe('substring whose occurrences to find'), + z.number().describe('number of occurrences to check for'), + ]), + z.boolean(), + ), + ..., + }), +``` + +Because a filter is, in fact, a function, its schema is defined by generating a +[Zod function schema][zod-fn] against the parameter supplied to our schema +callback. A filter function is required to have at least one argument; in this +example, we have two additional arguments. But what if we modify our filter's +implementation function to make `times` optional? Code: + +```ts + createTemplateFilter({ + id: 'containsOccurrences', + ..., + filter: (arg: string, substring: string, times?: number) => { + if (times === undefined) { + // note that, in real life, simply calling this function directly with Nunjucks would suffice rather than implementing a filter: + return arg.includes(substring); + } + // original implementation follows + ... + }, + }), +``` + +In this case we should modify our `schema`: + +```ts + createTemplateFilter({ + ..., + schema: z => + z.function( + z.tuple([ + z.string().describe('input'), + z.string().describe('substring whose occurrences to find'), + z + .number() + .describe('number of occurrences to check for') + .optional(), + ]), + z.boolean(), + ), + ..., + }), +``` + +#### Filter Example Documentation + +Our filter documentation may benefit from examples which we specify thus: + +```ts + createTemplateFilter({ + ..., + examples: [ + { + description: 'Basic Usage', + example: `\ +- name: Contains Occurrences + action: debug:log + input: + message: \${{ parameters.projectName | containsOccurrences('-', 2) }} + `, + notes: `\ +- **Input**: \`foo-bar-baz\` +- **Output**: \`true\` + `, + }, + { + description: 'Omitting Optional Parameter', + example: `\ +- name: Contains baz + action: debug:log + input: + message: \${{ parameters.projectName | containsOccurrences('baz') | dump }} + `, + notes: `\ +- **Input**: \`foo-bar\` +- **Output**: \`false\` + `, + }, + ], + }), +``` + +### Custom Global Function + +In case your template needs access to a value generated from a function not +appropriately modeled as a filter, Nunjucks supports the direct invocation of +[global functions][global-fn]. We might, for example, add to `init`: + +```ts +async init({ + ..., + templating, +}) { + ... + templating.addTemplateGlobals({ + now: () => new Date().toISOString(), + }); +}, +``` + +Here we have implemented a simple mechanism to obtain a timestamp (note that +because we can only pass JSON-compatible--or `undefined`--values we have chosen +to model a date/time as an ISO string) using a globally available function. + +Again we have the option to make our global function self-documenting. Import: + +```ts +import { + ..., + createTemplateGlobalFunction, +} from '@backstage/plugin-scaffolder-node/alpha'; +``` + +Then modify: + +```ts + ... + templating.addTemplateGlobals([ + createTemplateGlobalFunction({ + id: 'now', + description: + 'obtain an ISO representation of the current date and time', + fn: () => new Date().toISOString(), + }), + ]); +``` + +#### Schema + +Declaring a global function schema is quite like the schema declaration for a +template filter: + +```ts + createTemplateGlobal({ + ..., + schema: z => z.function().args().returns(z.string()), + ..., + }), +``` + +#### Template Global Function Example Documentation + +Again, this works in the same way as filter examples: + +```ts + createTemplateGlobal({ + ..., + examples: [ + { + description: 'Obtain the current date/time', + example: `\ +- name: Log Timestamp + action: debug:log + input: + message: Current date/time: \${{ now() }} + `, + // optional `notes` omitted from this example + }, + ], + ..., + }), + +``` + +### Custom Global Value + +Alternatively, your template may need access to a simple JSON value, which can +be registered in this manner: + +```ts +async init({ + ..., + templating, +}) { + ... + templating.addTemplateGlobals({ + ..., + preferredMetasyntacticIdentifier: 'foo', + }); +}, +``` + +Or the documenting form: + +```ts +async init({ + ..., + templating, +}) { + ... + templating.addTemplateGlobals([ + ..., + createTemplateGlobalValue({ + id: 'preferredMetasyntacticVariable', + value: 'foo', + description: + 'This description is as contrived as the global value it documents', + }), + ]); +}, +``` + +## Register Template Extensions with the Legacy Backend System + +Users of the original Backstage backend can register template extensions by +specifying options to the scaffolder backend plugin's `createRouter` function +(customarily called in `packages/backend/src/plugins/scaffolder.ts`): + +- `additionalTemplateFilters` - either of: + - object mapping filter name to implementation function, or + - array of documented template filters as returned by the + utility function `createTemplateFilter` +- `additionalTemplateGlobals` - either of: + - object mapping global name to value or function, or + - array of documented global functions and values as returned by the utility + functions `createTemplateGlobalFunction` and `createTemplateGlobalValue` + +[nunjucks]: https://mozilla.github.io/nunjucks +[filter]: https://mozilla.github.io/nunjucks/templating.html#filters +[global-fn]: https://mozilla.github.io/nunjucks/templating.html#global-functions +[parseRepoUrl]: https://backstage.io/docs/reference/plugin-scaffolder-node.parserepourl +[CompoundEntityRef]: https://backstage.io/docs/reference/catalog-model.compoundentityref +[Zod]: https://zod.dev/ +[zod-fn]: https://zod.dev/?id=functions +[piped]: https://en.wikipedia.org/wiki/Pipeline_(Unix)#Pipelines_in_command_line_interfaces diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 0f3d0af2e5..85f29cb359 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -631,7 +631,7 @@ output: ## The templating syntax -You might have noticed variables wrapped in `${{ }}` in the examples. These are +You might have noticed expressions wrapped in `${{ }}` in the examples. These are template strings for linking and gluing the different parts of the template together. All the form inputs from the `parameters` section will be available by using this template syntax (for example, `${{ parameters.firstName }}` inserts @@ -704,219 +704,16 @@ You can read more about all the `inputs` and `outputs` defined in the actions in code part of the `JSONSchema`, or you can read more about our [built in actions](./builtin-actions.md). -## Built in Filters +### More about expressions -Template filters are functions that help you transform data, extract specific information, -and perform various operations in Scaffolder Templates. +The `${{ }}` constructs in your template are evaluated using the +powerful [Nunjucks templating engine](https://mozilla.github.io/nunjucks/). +To learn more about basic Nunjucks templating please see +[templating documentation](https://mozilla.github.io/nunjucks/templating.html). -This section introduces the built-in filters provided by Backstage and offers examples of -how to use them in the Scaffolder templates. It's important to mention that Backstage also leverages the -native filters from the Nunjucks library. For a complete list of these native filters and their usage, -refer to the [Nunjucks documentation](https://mozilla.github.io/nunjucks/templating.html#builtin-filters). - -To create your own custom filters, look to the section [Custom Filters and Globals](#custom-filters-and-globals) hereafter. - -### parseRepoUrl - -The `parseRepoUrl` filter parse a repository URL into -its components, such as `owner`, repository `name`, and more. - -**Usage Example:** - -```yaml -- id: log - name: Parse Repo URL - action: debug:log - input: - extra: ${{ parameters.repoUrl | parseRepoUrl }} -``` - -- **Input**: `github.com?repo=backstage&org=backstage` -- **Output**: [RepoSpec](https://github.com/backstage/backstage/blob/v1.17.2/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts#L39) - -### parseEntityRef - -The `parseEntityRef` filter allows you to extract different parts of -an entity reference, such as the `kind`, `namespace`, and `name`. - -**Usage example** - -1. Without context - -```yaml -- id: log - name: Parse Entity Reference - action: debug:log - input: - extra: ${{ parameters.owner | parseEntityRef }} -``` - -- **Input**: `group:techdocs` -- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23) - -2. With context - -```yaml -- id: log - name: Parse Entity Reference - action: debug:log - input: - extra: ${{ parameters.owner | parseEntityRef({ defaultKind:"group", defaultNamespace:"another-namespace" }) }} -``` - -- **Input**: `techdocs` -- **Output**: [CompoundEntityRef](https://github.com/backstage/backstage/blob/v1.17.2/packages/catalog-model/src/types.ts#L23) - -### pick - -This `pick` filter allows you to select specific properties (`kind`, `namespace`, `name`) from an object. - -**Usage Example** - -```yaml -- id: log - name: Pick - action: debug:log - input: - extra: ${{ parameters.owner | parseEntityRef | pick('name') }} -``` - -- **Input**: `{ kind: 'Group', namespace: 'default', name: 'techdocs' }` -- **Output**: `techdocs` - -### projectSlug - -The `projectSlug` filter generates a project slug from a repository URL - -**Usage Example** - -```yaml -- id: log - name: Project Slug - action: debug:log - input: - extra: ${{ parameters.repoUrl | projectSlug }} -``` - -- **Input**: `github.com?repo=backstage&org=backstage` -- **Output**: `backstage/backstage` - -## Custom Filters and Globals - -You may wish to extend the filters and globals with your own custom ones. For example `${{ myGlobal | myFilter | myOtherFilter }}` or `${{ myFunctionGlobal(1,2) | myFilter }}`. -This can be achieved using the `additionalTemplateFilters` and `additionalTemplateGlobals` properties respectively. - -These properties accept a `Record` - -```ts title="plugins/scaffolder-backend/src/service/router.ts" - additionalTemplateFilters?: Record; - additionalTemplateGlobals?: Record; -``` - -where the first parameter is the identifier of the filter or global and the second is a `TemplateFilter` or a `TemplateGlobal` respectively. -A `TemplateFilter` is a function which will be called using the previous `JsonValue` objects and may return a `JsonValue` object. -A `TemplateGlobal` can either be a function which will be called using the passed `JsonValue` objects and may return a `JsonValue` object or it can be a `JsonValue` object itself. - -```ts title="plugins/scaffolder-node/src/types.ts" -export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; - -export type TemplateGlobal = - | ((...args: JsonValue[]) => JsonValue | undefined) - | JsonValue; -``` - -**Usage Example** - -Given you want to have the following filters and globals available in you template: - -```yaml -apiVersion: scaffolder.backstage.io/v1beta3 -kind: Template -metadata: - name: test - title: Test -spec: - owner: user:guest - type: service - - steps: - - id: debug1 - name: debug1 - action: debug:log - input: - message: ${{ myGlobal | myFilter | myOtherFilter }} - - - id: debug2 - name: debug2 - action: debug:log - input: - message: ${{ myFunctionGlobal(1,2) | myFilter }} -``` - -You will have to create a new [`BackendModule`](../../backend-system/architecture/06-modules.md) using the `scaffolderTemplatingExtensionPoint`. - -Here is a very simplified example of how to do that: - -```ts title="packages/backend-next/src/index.ts" -/* highlight-add-start */ -import { scaffolderTemplatingExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; -import { createBackendModule } from '@backstage/backend-plugin-api'; -/* highlight-add-end */ - -/* highlight-add-start */ -const scaffolderModuleCustomFilters = createBackendModule({ - pluginId: 'scaffolder', // name of the plugin that the module is targeting - moduleId: 'custom-filters', - register(env) { - env.registerInit({ - deps: { - scaffolder: scaffolderTemplatingExtensionPoint, - // ... and other dependencies as needed - }, - async init({ scaffolder /* ..., other dependencies */ }) { - scaffolder.addTemplateGlobals({ - myGlobal: () => 'myGlobal', - myFunctionGlobal: (...args: JsonValue[]) => args[0] + args[1], - }); - scaffolder.addTemplateFilters({ - myFilter: () => 'the value is this now', - myOtherFilter: (...args: JsonValue[]) => args.join(''), - }); - }, - }); - }, -}); -/* highlight-add-end */ - -const backend = createBackend(); -backend.add(import('@backstage/plugin-scaffolder-backend')); -/* highlight-add-next-line */ -backend.add(scaffolderModuleCustomFilters); -``` - -If you still use the legacy backend system, then you will use the `createRouter()` function of the `Scaffolder plugin` - -```ts title="packages/backend/src/plugins/scaffolder.ts" -export default async function createPlugin({ - logger, - config, -}: PluginEnvironment): Promise { - ... - return await createRouter({ - logger, - config, - - additionalTemplateFilters: { - - }, - additionalTemplateGlobals: { - - }, - }); -} -``` - -Note that additional template global functions are currently not supported in `fetch:template` (see #25445). +Information about Backstage's built-in Nunjucks extensions, as well as how to +create your own customizations, may be found at +[Template Extensions](./template-extensions.md). ## Template Editor diff --git a/docs/frontend-system/architecture/20-extensions.md b/docs/frontend-system/architecture/20-extensions.md index 02f4ba9d05..eae0405bda 100644 --- a/docs/frontend-system/architecture/20-extensions.md +++ b/docs/frontend-system/architecture/20-extensions.md @@ -337,3 +337,21 @@ const routableExtension = createExtension({ }, }); ``` + +## Multiple attachment points + +For some cases it can be useful to attach extensions to multiple parents. An example of this are Scaffolder field extensions or TechDocs addons that are consumed by multiple extensions. Specifying multiple attachments is done by providing an array of attachment points to the `attachTo` property of the extension. Keep in mind that this increases the complexity of your extension tree and should only be done when necessary. The following example shows how to attach our example extension to multiple parents: + +```tsx +const extension = createExtension({ + name: 'my-extension', + attachTo: [ + { id: 'my-first-parent', input: 'content' }, + { id: 'my-second-parent', input: 'children' }, // The input names do not need to match + ], + output: [coreExtensionData.reactElement], + factory() { + return [coreExtensionData.reactElement(
Hello World
)]; + }, +}); +``` diff --git a/docs/getting-started/config/database.md b/docs/getting-started/config/database.md index 699160d550..ecf461f02a 100644 --- a/docs/getting-started/config/database.md +++ b/docs/getting-started/config/database.md @@ -168,5 +168,6 @@ We recommend you read [Setting up authentication](./authentication.md) next. If you want to read more about the database configuration, here are some helpful links: - [Configuring Plugin Databases](../../tutorials/configuring-plugin-databases.md#privileges) +- [Manual Knex Rollback](../../tutorials/manual-knex-rollback.md) - [Read more about Knex](http://knexjs.org/), the database wrapper that we use. - [Install `pgAdmin` 4](https://www.pgadmin.org/), a helpful tool for querying your database. diff --git a/docs/getting-started/keeping-backstage-updated.md b/docs/getting-started/keeping-backstage-updated.md index b31409cfc2..a44cb3c8bc 100644 --- a/docs/getting-started/keeping-backstage-updated.md +++ b/docs/getting-started/keeping-backstage-updated.md @@ -160,3 +160,7 @@ export GLOBAL_AGENT_NO_PROXY=${NO_PROXY} export YARN_HTTP_PROXY=${HTTP_PROXY} # optional export YARN_HTTPS_PROXY=${HTTPS_PROXY} # optional ``` + +## Rollback migrations + +In some cases you could need to downgrade Backstage instance due to some problem or maybe because you are using a test environment to validate the new version of Backstage. You can check the [Manual Rollback using Knex](../tutorials/manual-knex-rollback.md) guide to know how to rollback migrations using Knex. diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md index e64bf42636..99feb9a806 100644 --- a/docs/integrations/azure/discovery.md +++ b/docs/integrations/azure/discovery.md @@ -85,7 +85,7 @@ The parameters available are: - **`scope`** _(optional)_: `'global'` or `'local'`. Sets the scope of concurrency control. -_Note:_ +:::note Note - The path parameter follows the same rules as the search on Azure DevOps web interface. For more details visit the [official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops). - To use branch parameters, it is necessary that the desired branch be added to the "Searchable branches" list within Azure DevOps Repositories. To do this, follow the instructions below: @@ -97,6 +97,8 @@ _Note:_ 5. In the window that appears, enter the name of the branch you want to add and click "Add". 6. The added branch will now appear in the "Searchable branches" list. +::: + It may take some time before the branch is indexed and searchable. As this provider is not one of the default providers, you will first need to install diff --git a/docs/integrations/bitbucketCloud/discovery.md b/docs/integrations/bitbucketCloud/discovery.md index b000a45ccd..ff0e60f498 100644 --- a/docs/integrations/bitbucketCloud/discovery.md +++ b/docs/integrations/bitbucketCloud/discovery.md @@ -121,9 +121,14 @@ catalog: workspace: workspace-name ``` -> **Note:** It is possible but certainly not recommended to skip the provider ID level. +:::note Note + +It is possible but certainly not recommended to skip the provider ID level. + > If you do so, `default` will be used as provider ID. +::: + - **`catalogPath`** _(optional)_: Default: `/catalog-info.yaml`. Path where to look for `catalog-info.yaml` files. diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index 1366a59099..bfd210aeb9 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -10,18 +10,12 @@ Backstage repository. ## Creating a Backend Plugin A new, bare-bones backend plugin package can be created by issuing the following -command in your Backstage repository root: +command in your Backstage repository root and selecting `backend-plugin`: ```sh -yarn new --select backend-plugin +yarn new ``` -Please also see the `--help` flag for the `new` command for some -further options that are available, notably the `--scope` and `--no-private` -flags that control naming and publishing of the newly created package. Your repo -root `package.json` will probably also have some default values already set up -for these. - You will be asked to supply a name for the plugin. This is an identifier that will be part of the NPM package name, so make it short and containing only lowercase characters separated by dashes, for example `carmen`, if it's a diff --git a/docs/plugins/create-a-plugin.md b/docs/plugins/create-a-plugin.md index d80e9cd57e..3810495707 100644 --- a/docs/plugins/create-a-plugin.md +++ b/docs/plugins/create-a-plugin.md @@ -15,9 +15,11 @@ invoking the from the root of your project. ```bash -yarn new --select plugin +yarn new ``` +And then select `frontend-plugin`. + ![Example of output when creating a new plugin](../assets/getting-started/create-plugin_output.png) This will create a new Backstage Plugin based on the ID that was provided. It diff --git a/docs/plugins/integrating-plugin-into-software-catalog.md b/docs/plugins/integrating-plugin-into-software-catalog.md index d47be7f4c0..ed2e25c601 100644 --- a/docs/plugins/integrating-plugin-into-software-catalog.md +++ b/docs/plugins/integrating-plugin-into-software-catalog.md @@ -21,7 +21,8 @@ should have a separate package in a folder, which represents your plugin. Example: ``` -$ yarn new --select plugin +$ yarn new +# Select `frontend-plugin` > ? Enter an ID for the plugin [required] my-plugin > ? Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional] diff --git a/docs/releases/v1.36.0-next.3-changelog.md b/docs/releases/v1.36.0-next.3-changelog.md new file mode 100644 index 0000000000..f17ca5e0be --- /dev/null +++ b/docs/releases/v1.36.0-next.3-changelog.md @@ -0,0 +1,1967 @@ +# Release v1.36.0-next.3 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.36.0-next.3](https://backstage.github.io/upgrade-helper/?to=1.36.0-next.3) + +## @backstage/canon@0.1.0-next.2 + +### Minor Changes + +- 8309bdb: Updated core CSS tokens and fixing the Button component accordingly. + +### Patch Changes + +- f44e5cf: Fix spacing props not being applied for custom values. + +## @backstage/cli@0.30.0-next.3 + +### Minor Changes + +- b30e788: The `new` command is now powered by a new template system that allows you to define your own templates in a declarative way, as well as import existing templates from external sources. See the [CLI templates documentation](https://backstage.io/docs/tooling/cli/templates) for more information. + + The following flags for the `new` command have been deprecated and will be removed in a future release: + + - `--license=`: Configure the global `license` instead. + - `--no-private`: Configure the global `private` instead. + - `--baseVersion=`: Configure the global `version` instead. + - `--npmRegistry=`: Configure the global `publishRegistry` instead. + - `--scope=`: Configure the global `namePrefix` and/or `namePluginInfix` instead. + + As part of this change the template IDs and their options have changed. The following backwards compatibility mappings for the `--select` and `--option` flags are enabled when using the default set of templates, but they will also be removed in the future: + + - `--select=plugin` is mapped to `--select=frontend-plugin` instead. + - `--option=id=` is mapped to `--option=pluginId=` instead. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/integration@1.16.1 + - @backstage/release-manifests@0.0.12 + - @backstage/types@1.2.1 + +## @backstage/plugin-catalog-backend@1.31.0-next.3 + +### Minor Changes + +- 5aebc13: The `UrlReaderProccessor` accepts a new config flag `catalog.useUrlReadersSearch` to always call the `search` method of `UrlReaders`. + + This flag currently defaults to `false`, but adopters are encouraged to enable it as this behavior will be the default in a future release. + + Previous behavior was to call the `search` method only if the parsed Git URL's filename contained a wildcard and use `readUrl` otherwise. `UrlReaderService` must implement this logic in the `search` method instead. + + This allows each `UrlReaderService` implementation to check whether it's a search URL (that contains a wildcard pattern) or not using logic that is specific to each provider. + +### Patch Changes + +- ef73f97: Updated permission integration to use new permission resource ref. +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-module-catalog@0.3.1-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-catalog-node@1.16.0-next.3 + +### Minor Changes + +- b4183ad: Added new `catalogEntityPermissionResourceRef` export via the `/alpha` sub-path. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-scaffolder@1.28.0-next.3 + +### Minor Changes + +- 5d469c9: Added support for autocompletion of GitHub branches in scaffolder +- 8e67e4a: Added support for autocompletion to GithubRepoPicker component + +### Patch Changes + +- 1e935f0: Added conditional rendering of `oneOf` output schemas on the Installed Actions page for scaffolder actions +- 65d7020: Use template title for ongoing task page header +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-scaffolder-common@1.5.9 + +## @backstage/plugin-scaffolder-backend@1.30.0-next.3 + +### Minor Changes + +- dc8dd4b: Added new `createTemplateFilter`, `createTemplateGlobalFunction`, `createTemplateGlobalValue` for template extensions. +- 3d6ef79: Support file globbing in fs:delete + +### Patch Changes + +- 1e935f0: Added conditional rendering of `oneOf` output schemas on the Installed Actions page for scaffolder actions +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.6.0-next.2 + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.7-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.0-next.3 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.6-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.27 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-scaffolder-common@1.5.9 + +## @backstage/plugin-scaffolder-backend-module-github@0.6.0-next.2 + +### Minor Changes + +- bb8302b: **BREAKING**: The `remoteUrl` output is no longer required, it can be empty only when using the new `createWhenEmpty` boolean flag. + +### Patch Changes + +- 5c187f9: **DEPRECATION**: The `getOctokitOptions` function signature with `repoUrl` option has been deprecated in favour of a function signature with individual `host`, `owner`, and `repo` parameters: + + ```diff + const octokitOptions = await getOctokitOptions({ + integrations, + credentialsProvider, + token, + - repoUrl, + + host, + + owner, + + repo, + }); + ``` + +- 5d469c9: Added support for autocompletion of GitHub branches in scaffolder + +- 8e67e4a: Added support for autocompletion to GithubRepoPicker component + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-node@0.7.0-next.2 + +### Minor Changes + +- dc8dd4b: Added new `createTemplateFilter`, `createTemplateGlobalFunction`, `createTemplateGlobalValue` for template extensions. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.9 + +## @backstage/backend-app-api@1.2.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/backend-defaults@0.8.0-next.3 + +### Patch Changes + +- 3740229: In the different `UrlReadersService`, the `search` method have been updated to use the `readUrl` if the given URL doesn't contain a pattern. + For `UrlReaders` that didn't implement the `search` method, `readUrl` is now called internally and throws if the given URL doesn't contain a pattern. +- 72cddf2: Updated `PermissionsRegistryService` to use `PermissionResourceRef`s and added the `getPermissionRuleset` method. +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/backend-app-api@1.2.0-next.3 + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + +## @backstage/backend-dynamic-feature-service@0.6.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-backend@0.4.2-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.30-next.2 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/backend-openapi-utils@0.5.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/backend-plugin-api@1.2.0-next.2 + +### Patch Changes + +- 72cddf2: Updated `PermissionsRegistryService` to use `PermissionResourceRef`s and added the `getPermissionRuleset` method. +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/backend-test-utils@1.3.0-next.3 + +### Patch Changes + +- 72cddf2: Added the new `getPermissionRuleset` method to `mockServices.permissionsRegistry`. +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/backend-app-api@1.2.0-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/core-compat-api@0.3.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/version-bridge@1.0.11-next.0 + +## @backstage/create-app@0.5.25-next.3 + +### Patch Changes + +- bfa4ea9: Enable `catalog.useUrlReadersSearch` configuration by default in newly created Backstage installations. + + This parameter makes `UrlReaderProcessor` always use the search method. + New adopters are encouraged to enable it as this behavior will be the default in a future release. + +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## @backstage/dev-utils@1.1.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + +## @backstage/frontend-app-api@0.10.5-next.3 + +### Patch Changes + +- f1efb47: Add support for defining multiple attachment points for extensions and blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.1.6-next.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + +## @backstage/frontend-defaults@0.1.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-app@0.1.6-next.3 + +## @backstage/frontend-plugin-api@0.9.5-next.3 + +### Patch Changes + +- f1efb47: Add support for defining multiple attachment points for extensions and blueprints. +- Updated dependencies + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + +## @backstage/frontend-test-utils@0.2.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/config@1.3.2 + - @backstage/test-utils@1.7.5-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-app@0.1.6-next.3 + +## @backstage/repo-tools@0.12.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## @techdocs/cli@1.9.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/plugin-techdocs-node@1.13.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## @backstage/plugin-api-docs@0.12.4-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + +## @backstage/plugin-app@0.1.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-permission-react@0.4.31-next.0 + +## @backstage/plugin-app-backend@0.4.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.30-next.2 + +## @backstage/plugin-app-node@0.1.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + +## @backstage/plugin-app-visualizer@0.1.16-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + +## @backstage/plugin-auth-backend@0.24.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-auth0-provider@0.2.0-next.2 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.0-next.3 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.5-next.2 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.0-next.2 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-github-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-google-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.5-next.2 + - @backstage/plugin-auth-backend-module-oidc-provider@0.4.0-next.3 + - @backstage/plugin-auth-backend-module-okta-provider@0.2.0-next.2 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.3.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @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.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-auth0-provider@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-google-provider@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-okta-provider@0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.5.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + +## @backstage/plugin-auth-node@0.6.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @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@1.27.0-next.3 + +### Patch Changes + +- 7a15cff: Revert client side paginated catalog table to using built in Material Table toolbar component +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-scaffolder-common@1.5.9 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + +## @backstage/plugin-catalog-backend-module-aws@0.4.8-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + +## @backstage/plugin-catalog-backend-module-azure@0.3.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.4.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.27 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.3.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.2.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-catalog-backend-module-github@0.7.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-catalog-backend-module-github@0.7.10-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + +## @backstage/plugin-catalog-backend-module-gitlab@0.6.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-catalog-backend-module-gitlab@0.6.3-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.6.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-catalog-backend-module-ldap@0.11.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @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-backend-module-logs@0.1.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + +## @backstage/plugin-catalog-backend-module-msgraph@0.6.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-scaffolder-common@1.5.9 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.5.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.7 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-catalog-graph@0.4.16-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-react@1.15.2-next.3 + +## @backstage/plugin-catalog-import@0.12.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + +## @backstage/plugin-catalog-react@1.15.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-test-utils@0.2.6-next.3 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-react@0.4.31-next.0 + +## @backstage/plugin-devtools@0.1.24-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-react@0.4.31-next.0 + +## @backstage/plugin-devtools-backend@0.5.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + - @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-events-backend@0.4.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-events-backend-module-azure@0.2.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + +## @backstage/plugin-events-backend-module-gerrit@0.2.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + +## @backstage/plugin-events-backend-module-github@0.2.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + +## @backstage/plugin-events-backend-module-gitlab@0.2.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + +## @backstage/plugin-events-backend-test-utils@0.1.41-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.8-next.2 + +## @backstage/plugin-events-node@0.4.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## @backstage/plugin-home@0.8.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-home-react@0.1.23-next.1 + +## @backstage/plugin-kubernetes@0.12.4-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + - @backstage/plugin-kubernetes-react@0.5.4-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + +## @backstage/plugin-kubernetes-backend@0.19.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-kubernetes-node@0.2.3-next.2 + - @backstage/catalog-client@1.9.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/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-kubernetes-cluster@0.0.22-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + - @backstage/plugin-kubernetes-react@0.5.4-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + +## @backstage/plugin-kubernetes-node@0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + +## @backstage/plugin-notifications-backend@0.5.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-notifications-node@0.2.12-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-node@0.1.17-next.2 + +## @backstage/plugin-notifications-backend-module-email@0.3.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-notifications-node@0.2.12-next.2 + - @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-notifications-common@0.0.8 + +## @backstage/plugin-notifications-node@0.2.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @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.17-next.2 + +## @backstage/plugin-org@0.6.36-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + +## @backstage/plugin-org-react@0.1.35-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + +## @backstage/plugin-permission-backend@0.5.54-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-permission-node@0.8.8-next.2 + +### Patch Changes + +- b71f634: Added a new `PermissionRuleset` type that encapsulates a lookup function for permission rules, which can be created by the new `PermissionsRegistryService` via the `getPermissionRuleset` method. The `createConditionTransformer` and `createConditionAuthorizer` functions have been adapted to receive these accessors as arguments, with their older counterparts being deprecated. +- a9621de: Added a new `createPermissionResourceRef` utility that encapsulates the constants and types related to a permission resource types. The `createConditionExports` and `createPermissionRule` functions have also been adapted to accept these references as arguments, deprecating their older counterparts. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.8.4 + +## @backstage/plugin-proxy-backend@0.5.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-proxy-node@0.1.1-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-proxy-node@0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.27 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.8.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-notifications-node@0.2.12-next.2 + - @backstage/plugin-notifications-common@0.0.8 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-scaffolder-node-test-utils@0.1.19-next.3 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-node-test-utils@0.1.19-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-test-utils@1.3.0-next.3 + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-scaffolder-react@1.14.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-scaffolder-common@1.5.9 + +## @backstage/plugin-search@1.4.23-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + +## @backstage/plugin-search-backend@1.8.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-catalog@0.3.1-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @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-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-elasticsearch@1.6.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-explore@0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-pg@0.5.41-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-module-techdocs@0.3.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/plugin-techdocs-node@1.13.0-next.2 + - @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-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-backend-node@1.3.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-search-react@1.8.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-search-common@1.2.17 + +## @backstage/plugin-signals-backend@0.3.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.17-next.2 + +## @backstage/plugin-signals-node@0.1.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + +## @backstage/plugin-techdocs@1.12.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.45-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/test-utils@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + +## @backstage/plugin-techdocs-backend@1.11.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-module-techdocs@0.3.6-next.3 + - @backstage/plugin-techdocs-node@1.13.0-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-node@1.13.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-user-settings@0.8.19-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-signals-react@0.0.10-next.0 + - @backstage/plugin-user-settings-common@0.0.1 + +## @backstage/plugin-user-settings-backend@0.2.30-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.17-next.2 + - @backstage/plugin-user-settings-common@0.0.1 + +## example-app@0.2.106-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/cli@0.30.0-next.3 + - @backstage/plugin-scaffolder@1.28.0-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/plugin-api-docs@0.12.4-next.3 + - @backstage/plugin-catalog-graph@0.4.16-next.3 + - @backstage/plugin-catalog-import@0.12.10-next.3 + - @backstage/plugin-org@0.6.36-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/plugin-user-settings@0.8.19-next.3 + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-catalog-unprocessed-entities@0.2.14-next.1 + - @backstage/plugin-devtools@0.1.24-next.3 + - @backstage/plugin-home@0.8.5-next.3 + - @backstage/plugin-kubernetes@0.12.4-next.3 + - @backstage/plugin-kubernetes-cluster@0.0.22-next.3 + - @backstage/plugin-notifications@0.5.2-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-search@1.4.23-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-signals@0.0.16-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.21-next.1 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + +## example-app-next@0.0.20-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/cli@0.30.0-next.3 + - @backstage/plugin-scaffolder@1.28.0-next.3 + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/plugin-api-docs@0.12.4-next.3 + - @backstage/plugin-catalog-graph@0.4.16-next.3 + - @backstage/plugin-catalog-import@0.12.10-next.3 + - @backstage/plugin-org@0.6.36-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/plugin-user-settings@0.8.19-next.3 + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/frontend-defaults@0.1.6-next.3 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-app@0.1.6-next.3 + - @backstage/plugin-app-visualizer@0.1.16-next.3 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-catalog-unprocessed-entities@0.2.14-next.1 + - @backstage/plugin-home@0.8.5-next.3 + - @backstage/plugin-kubernetes@0.12.4-next.3 + - @backstage/plugin-kubernetes-cluster@0.0.22-next.3 + - @backstage/plugin-notifications@0.5.2-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-search@1.4.23-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-signals@0.0.16-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.21-next.1 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + +## app-next-example-plugin@0.0.20-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + +## example-backend@0.0.35-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.6.0-next.2 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/plugin-scaffolder-backend@1.30.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.5-next.3 + - @backstage/plugin-catalog-backend-module-openapi@0.2.7-next.3 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.3 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.5-next.3 + - @backstage/plugin-kubernetes-backend@0.19.3-next.3 + - @backstage/plugin-notifications-backend@0.5.2-next.3 + - @backstage/plugin-search-backend-module-catalog@0.3.1-next.3 + - @backstage/plugin-search-backend-module-techdocs@0.3.6-next.3 + - @backstage/plugin-techdocs-backend@1.11.6-next.3 + - @backstage/plugin-devtools-backend@0.5.2-next.3 + - @backstage/plugin-permission-backend@0.5.54-next.2 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.5-next.2 + - @backstage/plugin-search-backend@1.8.2-next.3 + - @backstage/plugin-app-backend@0.4.5-next.2 + - @backstage/plugin-auth-backend-module-github-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.5-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-backend@0.4.2-next.3 + - @backstage/plugin-proxy-backend@0.5.11-next.2 + - @backstage/plugin-search-backend-module-explore@0.2.8-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/plugin-signals-backend@0.3.1-next.2 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.7-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-permission-common@0.8.4 + +## example-backend-legacy@0.2.107-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/plugin-scaffolder-backend@1.30.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.3 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.5-next.3 + - @backstage/plugin-kubernetes-backend@0.19.3-next.3 + - @backstage/plugin-search-backend-module-catalog@0.3.1-next.3 + - @backstage/plugin-search-backend-module-techdocs@0.3.6-next.3 + - @backstage/plugin-techdocs-backend@1.11.6-next.3 + - @backstage/plugin-permission-backend@0.5.54-next.2 + - @backstage/plugin-search-backend@1.8.2-next.3 + - @backstage/plugin-app-backend@0.4.5-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-backend@0.4.2-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-proxy-backend@0.5.11-next.2 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.0-next.3 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.6-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.6.5-next.2 + - @backstage/plugin-search-backend-module-explore@0.2.8-next.2 + - @backstage/plugin-search-backend-module-pg@0.5.41-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/plugin-signals-backend@0.3.1-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-signals-node@0.1.17-next.2 + +## e2e-test@0.2.25-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.25-next.3 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## @internal/frontend@0.0.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + +## @internal/scaffolder@0.0.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + +## techdocs-cli-embedded-app@0.2.105-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/cli@0.30.0-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/test-utils@1.7.5-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + +## @internal/plugin-todo-list-backend@1.0.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/errors@1.2.7 diff --git a/docs/tooling/cli/03-commands.md b/docs/tooling/cli/03-commands.md index 2da7e11ef4..9d8dd1543e 100644 --- a/docs/tooling/cli/03-commands.md +++ b/docs/tooling/cli/03-commands.md @@ -258,30 +258,28 @@ it is possible to pre-select what you want to create using the `--select` flag, and provide options using `--option`, for example: ```bash -backstage-cli new --select plugin --option id=foo +backstage-cli new --select plugin --option pluginId=foo ``` This command is typically added as script in the root `package.json` to be -executed with `yarn new`, using options that are appropriate for the organization -that owns the app repo. For example you may have it set up like this: +executed with `yarn new`. For example you may have it set up like this: ```json { "scripts": { - "new": "backstage-cli new --scope internal --no-private --npm-registry https://acme.org/npm" + "new": "backstage-cli new" } } ``` +The `new` command comes with a default collection of plugins/packages, however, +you can customize this list and even create your own CLI templates. For more +information see [CLI Templates](./04-templates.md). + ```text -Usage: backstage-cli create [options] +Usage: backstage-cli new Options: - --select Select the thing you want to be creating upfront - --option = Pre-fill options for the creation process (default: []) - --scope The scope to use for new packages - --npm-registry The package registry to use for new packages - --no-private Do not mark new packages as private -h, --help display help for command ``` diff --git a/docs/tooling/cli/04-templates.md b/docs/tooling/cli/04-templates.md new file mode 100644 index 0000000000..c3bfb57d11 --- /dev/null +++ b/docs/tooling/cli/04-templates.md @@ -0,0 +1,149 @@ +--- +id: templates +title: CLI Templates +description: Overview of the new CLI Declarative Templates +--- + +The behavior of the `backstage-cli new` command is configurable through your root `package.json`, and you can also create and add custom CLI templates to suit your needs. + +## Basic Configuration + +```json +{ + "name": "root", + "backstage": { + "cli": { + "new": { + "globals": { + "license": "MIT", + "namePrefix": "@my-org/" + } + } + } + } +} +``` + +- `globals` - Configures input for all generated packages and plugins. + - `version` - Sets the value of the `version` field in `package.json` of all generated packages. Defaults to `0.1.0`. + - `license` - Sets the value of the `license` field in `package.json` of all generated packages. Defaults to `Apache-2.0`. + - `private` - Sets the value of the `private` field in `package.json` of all generated packages. Defaults to `true`. + - `publishRegistry` - Sets the value of the `publishConfig.registry` field in `package.json` of all generated packages. + - `namePrefix` - The prefix used to generate the full package name. Defaults to `@internal/`. + - `namePluginInfix` - The infix used to generate the full package name for plugin packages. Defaults to `plugin-`. +- `templates` - Specifies custom templates. + - See [Installing custom templates](#installing-custom-templates) and [Creating your own CLI templates](#creating-your-own-cli-templates) for more information. + +The generated package name is based on the `namePrefix` and `namePluginInfix` globals, as well as the "base name" which is derived from the package role and user input. For plugin packages the final package name will be ``, and for other packages it will be ``. + +For example, if you want your plugin frontend packages to end up with the name `@acme/backstage-plugin-`, you should use the following configuration: + +```json +{ + "name": "root", + "backstage": { + "cli": { + "new": { + "globals": { + "namePrefix": "@acme/", + "namePluginInfix": "backstage-plugin-" + } + } + } + } +} +``` + +## Installing custom templates + +Custom templates can be installed from local directories. To install a template you add it to the `backstage.cli.new.templates` configuration array in your root `package.json`: + +```json +{ + "name": "root", + "backstage": { + "cli": { + "new": { + "templates": ["./templates/custom-plugin"] + } + } + } +} +``` + +Each entry in the `templates` array should be a relative path that points to a directory containing a `portable-template.yaml` file. If the path starts with `./` it will be used as is, otherwise it will be resolved as a module within `node_modules`. + +When defining the `templates` array it will override the default set of templates. If you want to keep using one of the build-in templates in the Backstage CLI you can reference them directly within the CLI package. This following is the full list of built-in templates: + +```json +{ + "name": "root", + "backstage": { + "cli": { + "new": { + "templates": [ + "@backstage/cli/templates/frontend-plugin", + "@backstage/cli/templates/backend-plugin", + "@backstage/cli/templates/backend-plugin-module", + "@backstage/cli/templates/plugin-web-library", + "@backstage/cli/templates/plugin-node-library", + "@backstage/cli/templates/plugin-common-library", + "@backstage/cli/templates/web-library", + "@backstage/cli/templates/node-library", + "@backstage/cli/templates/scaffolder-backend-module" + ] + } + } + } +} +``` + +## Creating your own CLI templates + +Each template lives in its own directory and must have a `portable-template.yaml` file that describes the template. The template directory can also contain any files that should be templated or copied to the generated package. + +Start by creating `portable-template.yaml` in a new directory somewhere in your project, in this example we're using `./templates/custom-plugin/portable-template.yaml`: + +```yaml title="in templates/custom-plugin/portable-template.yaml" +name: custom-plugin +role: frontend-plugin +description: Description of my CLI template # optional +values: # optional + pluginVar: '{{ camelCase pluginId }}Plugin' +``` + +The following properties are supported: + +- `name` **(required)** - The name of your template, used by the user to select it. +- `role` **(required)** - The role of the template, similar to package role. See [Template Roles](#template-roles) for more details. +- `description` - A description of the type of package that this template produces. +- `values` - A map of additional values that will be present during templating. The values are themselves templated and can reference other values. If the key matches any of the user prompts, such as `pluginId`, the value will be used directly instead of prompting the user. + +Next, add any other files you want to be part of the template to the same directory. All files will be copied as is, except any files with a `.hbs` extension. They will be treated as [Handlebars](https://handlebarsjs.com/) templates and will be rendered with the values from the `portable-template.yaml` file as well as additional prompts such as `pluginId`. For example, you could create a `src/index.ts` file with the following content: + +```typescript title="in templates/custom-plugin/src/index.ts.hbs" +export function getPluginId() { + return '{{ pluginId }}'; +} +``` + +If you'd like to see more examples, you can find all the default templates and their yaml files [here](https://github.com/backstage/backstage/tree/master/packages/cli/templates). + +Once your template is ready, [add it to your config](#installing-custom-templates), and you should now be able to select it when running `yarn new`. + +### Template Roles + +The `role` property in the template yaml file is used to determine what input will be gathered for the template, as well as what actions will be taken after the new package has been created. The following roles are supported: + +| Role | Prompts | Output Directory | Additional Actions | +| :----------------------- | :--------------------- | :--------------- | :-------------------------------------------------------------------------------- | +| `frontend-plugin` | `pluginId` | `plugins` | Add dependency to `packages/app` and entry to `packages/backend/src/App.tsx` | +| `frontend-plugin-module` | `pluginId`, `moduleId` | `plugins` | Add dependency to `packages/app` | +| `backend-plugin` | `pluginId` | `plugins` | Add dependency to `packages/backend` and entry to `packages/backend/src/index.ts` | +| `backend-plugin-module` | `pluginId`, `moduleId` | `plugins` | Add dependency to `packages/backend` and entry to `packages/backend/src/index.ts` | +| `web-library` | `name` | `packages` | none | +| `node-library` | `name` | `packages` | none | +| `common-library` | `name` | `packages` | none | +| `plugin-web-library` | `pluginId` | `plugins` | none | +| `plugin-node-library` | `pluginId` | `plugins` | none | +| `plugin-common-library` | `pluginId` | `plugins` | none | diff --git a/docs/tutorials/configuring-plugin-databases.md b/docs/tutorials/configuring-plugin-databases.md index db3f126de7..c6d9869d38 100644 --- a/docs/tutorials/configuring-plugin-databases.md +++ b/docs/tutorials/configuring-plugin-databases.md @@ -181,3 +181,11 @@ GRANT SHOW DATABASES ON *.* TO some_user; The mechanisms in this guide should help you tackle different database deployment situations. Good luck! + +## Further Reading + +If you want to read more about the database configuration, here are some helpful links: + +- [Manual Knex Rollback](./manual-knex-rollback.md) +- [Read more about Knex](http://knexjs.org/), the database wrapper that we use. +- [Install `pgAdmin` 4](https://www.pgadmin.org/), a helpful tool for querying your database. diff --git a/docs/tutorials/manual-knex-rollback.md b/docs/tutorials/manual-knex-rollback.md new file mode 100644 index 0000000000..14e00d6659 --- /dev/null +++ b/docs/tutorials/manual-knex-rollback.md @@ -0,0 +1,61 @@ +--- +id: manual-knex-rollback +title: Manual Rollback using Knex +# prettier-ignore +description: Guide on how to rollback Knex migrations. +--- + +The most common case to use Knex directly is when you want to **rollback a migration** that was applied when you upgraded your Backstage instance and want to downgrade due to some problem. You can use the `migrate:down` command to rollback a specific migration. You can also use the `migrate:rollback` command to rollback the last batch of migrations. This is necessary because Knex will mark migrations as corrupted if you try to downgrade your Backstage instance without the rollback. Be aware to run those commands in the new version of the Backstage instance, so you can avoid the corrupted migrations for lower versions. + +You are likely to receive a message like this when you try a downgrade without the rollback: + +```sh +Backend failed to start up Error: The migration directory is corrupt, the following files are missing: 20230428155633_sessions.js +``` + +Currently, we don't have a simple way to check which migrations and which plugins have been applied in the database, but you can follow this [issue](https://github.com/backstage/backstage/issues/22439) to get more information about this. +This guide covers a simple way to rollback migrations using Knex. We have plans to support this in Backstage's CLI ([issue](https://github.com/backstage/backstage/issues/6366)), but for now it is possible to use Knex CLI to manage migrations in necessary cases. + +To start, you are going to need two things: database access and the plugin migrations directory you want to handle. We are going to use environment variables to access the database and we'll be using the `@backstage/plugin-catalog-backend` plugin as an example. In most cases, there is a `migrations` directory in the root of the plugin package, but you can check the `package.json` file to confirm the directory. +You can get more information about how Backstage handles Databases in [Configuring Plugin Databases](./configuring-plugin-databases.md). This tutorial follows the information in the [Knex migration guide](https://knexjs.org/guide/migrations.html), so you can get more details about the commands there. + +You can interact with Knex running the commands below in the project root: + +We want to check the migration status: + +```sh +$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +Using environment: production +Found 2 Completed Migration file/files. +20211229105307_init.js +20240113144027_assets-namespace.js +No Pending Migration files Found. +``` + +Now lets rollback a specific migration called `20240113144027_assets-namespace.js`: + +```sh +$ node_modules/.bin/knex migrate:down 20240113144027_assets-namespace.js --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +Using environment: production +Batch 2 rolled back the following migrations: +20240113144027_assets-namespace.js +``` + +Now we can check the migration status again to confirm the rollback: + +```sh +$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +Using environment: production +Found 1 Completed Migration file/files. +20211229105307_init.js +Found 1 Pending Migration file/files. +20240113144027_assets-namespace.js +``` + +Now lets use `migrate:currentVersion` which retrieves the current migration version. If there aren't any migrations run yet, it will return "none". + +```sh +$ node_modules/.bin/knex migrate:currentVersion --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg +Using environment: production +Current Version: 20240113144027 +``` diff --git a/docs/tutorials/quickstart-app-plugin.md b/docs/tutorials/quickstart-app-plugin.md index a1b30829b3..e02d2f546f 100644 --- a/docs/tutorials/quickstart-app-plugin.md +++ b/docs/tutorials/quickstart-app-plugin.md @@ -30,7 +30,7 @@ title: Adding Custom Plugin to Existing Monorepo App # The Skeleton Plugin 1. Start by using the built-in creator. From the terminal and root of your - project run: `yarn new --select plugin` + project run: `yarn new` and select `frontend-plugin`. 1. Enter a plugin ID. I used `github-playground` 1. When the process finishes, let's start the backend: `yarn --cwd packages/backend start` diff --git a/docs/tutorials/react-router-stable-migration.md b/docs/tutorials/react-router-stable-migration.md index 017f69ba4f..f4dcc33559 100644 --- a/docs/tutorials/react-router-stable-migration.md +++ b/docs/tutorials/react-router-stable-migration.md @@ -181,13 +181,9 @@ It's crucial that you update to `RequirePermission` at the same time as you upda When migrating over to React Router v6 stable, you might also see browser console warnings for the `Navigate` component. This will need to be wrapped up in a `Route` component with the `Navigate` component in the `element` prop. ```tsx -{ - /* highlight-remove-next-line */ -} +{/* prettier-ignore */ /* highlight-remove-next-line */} ; -{ - /* highlight-add-next-line */ -} +{/* prettier-ignore */ /* highlight-add-next-line */} } />; ``` diff --git a/microsite/sidebars.js b/microsite/sidebars.js index b7d340c748..b9f7a5baa5 100644 --- a/microsite/sidebars.js +++ b/microsite/sidebars.js @@ -479,6 +479,7 @@ module.exports = { 'tooling/cli/overview', 'tooling/cli/build-system', 'tooling/cli/commands', + 'tooling/cli/templates', { type: 'category', label: 'Local Development', @@ -498,6 +499,7 @@ module.exports = { Technical: [ 'tutorials/quickstart-app-plugin', 'tutorials/configuring-plugin-databases', + 'tutorials/manual-knex-rollback', 'tutorials/switching-sqlite-postgres', 'tutorials/using-backstage-proxy-within-plugin', 'tutorials/enable-public-entry', diff --git a/microsite/yarn.lock b/microsite/yarn.lock index f468605ce0..f6837afd71 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -10796,11 +10796,11 @@ __metadata: linkType: hard "serialize-javascript@npm:^6.0.0, serialize-javascript@npm:^6.0.1": - version: 6.0.1 - resolution: "serialize-javascript@npm:6.0.1" + version: 6.0.2 + resolution: "serialize-javascript@npm:6.0.2" dependencies: randombytes: ^2.1.0 - checksum: 3c4f4cb61d0893b988415bdb67243637333f3f574e9e9cc9a006a2ced0b390b0b3b44aef8d51c951272a9002ec50885eefdc0298891bc27eb2fe7510ea87dc4f + checksum: c4839c6206c1d143c0f80763997a361310305751171dd95e4b57efee69b8f6edd8960a0b7fbfc45042aadff98b206d55428aee0dc276efe54f100899c7fa8ab7 languageName: node linkType: hard diff --git a/mkdocs.yml b/mkdocs.yml index a7e693e5fc..d56ec49b66 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -71,6 +71,7 @@ nav: - Builtin Actions: 'features/software-templates/builtin-actions.md' - Writing Custom Actions: 'features/software-templates/writing-custom-actions.md' - Writing Custom Step Layouts: 'features/software-templates/writing-custom-step-layouts.md' + - Template Extensions: 'features/software-templates/template-extensions.md' - Migrating from v1beta2 to v1beta3 templates: 'features/software-templates/migrating-from-v1beta2-to-v1beta3.md' - Dry Run Testing: 'features/software-templates/dry-run-testing.md' - Backstage Search: @@ -211,6 +212,7 @@ nav: - Package Role Migration: 'tutorials/package-role-migration.md' - Migrating away from @backstage/core: 'tutorials/migrating-away-from-core.md' - Adding Custom Plugin to Existing Monorepo App: 'tutorials/quickstart-app-plugin.md' + - Manual Rollback using Knex: 'tutorials/manual-knex-rollback.md' - Switching Backstage from SQLite to PostgreSQL: 'tutorials/switching-sqlite-postgres.md' - Using the Backstage Proxy from Within a Plugin: 'tutorials/using-backstage-proxy-within-plugin.md' - Migration to Yarn 3: 'tutorials/yarn-migration.md' diff --git a/package.json b/package.json index af4f1c98c1..d7c1307d69 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,21 @@ { "name": "root", - "version": "1.36.0-next.2", + "version": "1.36.0-next.3", "private": true, "repository": { "type": "git", "url": "https://github.com/backstage/backstage" }, + "backstage": { + "cli": { + "new": { + "globals": { + "private": false, + "namePrefix": "@backstage/" + } + } + } + }, "workspaces": { "packages": [ "packages/*", @@ -33,7 +43,7 @@ "lint:docs": "node ./scripts/check-docs-quality", "lint:peer-deps": "backstage-repo-tools peer-deps", "lint:type-deps": "backstage-repo-tools type-deps", - "new": "backstage-cli new --scope backstage --baseVersion 0.0.0 --no-private", + "new": "backstage-cli new", "prepare": "husky", "prettier:check": "prettier --check .", "prettier:fix": "prettier --write .", diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 16d0022f93..db2cdc0753 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.20-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + ## 0.0.20-next.2 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index ab5205036c..402fe1cc65 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.20-next.2", + "version": "0.0.20-next.3", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index a3c1a244bb..f06fc41e85 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,50 @@ # example-app-next +## 0.0.20-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/cli@0.30.0-next.3 + - @backstage/plugin-scaffolder@1.28.0-next.3 + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/plugin-api-docs@0.12.4-next.3 + - @backstage/plugin-catalog-graph@0.4.16-next.3 + - @backstage/plugin-catalog-import@0.12.10-next.3 + - @backstage/plugin-org@0.6.36-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/plugin-user-settings@0.8.19-next.3 + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/frontend-defaults@0.1.6-next.3 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-app@0.1.6-next.3 + - @backstage/plugin-app-visualizer@0.1.16-next.3 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-catalog-unprocessed-entities@0.2.14-next.1 + - @backstage/plugin-home@0.8.5-next.3 + - @backstage/plugin-kubernetes@0.12.4-next.3 + - @backstage/plugin-kubernetes-cluster@0.0.22-next.3 + - @backstage/plugin-notifications@0.5.2-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-search@1.4.23-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-signals@0.0.16-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.21-next.1 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + ## 0.0.20-next.2 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 08998404b2..971eff8f72 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.20-next.2", + "version": "0.0.20-next.3", "backstage": { "role": "frontend" }, diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 015aa37b36..3a38a78a7e 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,46 @@ # example-app +## 0.2.106-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/cli@0.30.0-next.3 + - @backstage/plugin-scaffolder@1.28.0-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/plugin-api-docs@0.12.4-next.3 + - @backstage/plugin-catalog-graph@0.4.16-next.3 + - @backstage/plugin-catalog-import@0.12.10-next.3 + - @backstage/plugin-org@0.6.36-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/plugin-user-settings@0.8.19-next.3 + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-catalog-unprocessed-entities@0.2.14-next.1 + - @backstage/plugin-devtools@0.1.24-next.3 + - @backstage/plugin-home@0.8.5-next.3 + - @backstage/plugin-kubernetes@0.12.4-next.3 + - @backstage/plugin-kubernetes-cluster@0.0.22-next.3 + - @backstage/plugin-notifications@0.5.2-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-search@1.4.23-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-signals@0.0.16-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.21-next.1 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + ## 0.2.106-next.2 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 48b6313c17..05354431b7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.106-next.2", + "version": "0.2.106-next.3", "backstage": { "role": "frontend" }, diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 2fca118c45..665ea682ab 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.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 1.2.0-next.2 ### Minor Changes diff --git a/packages/backend-app-api/config.d.ts b/packages/backend-app-api/config.d.ts index b5e56af67c..0cf91852a1 100644 --- a/packages/backend-app-api/config.d.ts +++ b/packages/backend-app-api/config.d.ts @@ -23,5 +23,31 @@ export interface Config { include?: string[]; exclude?: string[]; }; + + startup?: { + default?: { + /** + * The default value for `onPluginBootFailure` if not specified for a particular plugin. + * This defaults to 'abort', which means `onPluginBootFailure: continue` must be specified + * for backend startup to continue on plugin boot failure. This can also be set to + * 'continue', which flips the logic for individual plugins so that they must be set to + * `onPluginBootFailure: abort` to be required. + */ + onPluginBootFailure?: 'continue' | 'abort'; + }; + plugins?: { + [pluginId: string]: { + /** + * Used to control backend startup behavior when this plugin fails to boot up. Setting + * this to `continue` allows the backend to continue starting up, even if this plugin + * fails. This can enable leaving a crashing plugin installed, but still permit backend + * startup, which may help troubleshoot data-dependent issues. Plugin failures for plugins + * set to `abort` are fatal (this is the default unless overridden by the `default` + * setting). + */ + onPluginBootFailure?: 'continue' | 'abort'; + }; + }; + }; }; } diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 75f03041b2..9c297c18e7 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.0-next.2", + "version": "1.2.0-next.3", "description": "Core API used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index 39a54747d5..fe0a4f526b 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -560,6 +560,95 @@ describe('BackendInitializer', () => { ); }); + it('should permit startup errors for plugins with onPluginBootFailure: continue', async () => { + const init = new BackendInitializer([ + mockServices.rootLifecycle.factory(), + mockServices.rootLogger.factory(), + mockServices.rootConfig.factory({ + data: { + backend: { + startup: { plugins: { test: { onPluginBootFailure: 'continue' } } }, + }, + }, + }), + ]); + init.add( + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + throw new Error('NOPE'); + }, + }); + }, + }), + ); + await expect(init.start()).resolves.not.toThrow(); + }); + + it('should permit startup errors if the default onPluginBootFailure is continue', async () => { + const init = new BackendInitializer([ + mockServices.rootLifecycle.factory(), + mockServices.rootLogger.factory(), + mockServices.rootConfig.factory({ + data: { + backend: { + startup: { default: { onPluginBootFailure: 'continue' } }, + }, + }, + }), + ]); + init.add( + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + throw new Error('NOPE'); + }, + }); + }, + }), + ); + await expect(init.start()).resolves.not.toThrow(); + }); + + it('should forward errors for plugins explicitly marked to abort when the default is continue', async () => { + const init = new BackendInitializer([ + mockServices.rootLifecycle.factory(), + mockServices.rootLogger.factory(), + mockServices.rootConfig.factory({ + data: { + backend: { + startup: { + default: { onPluginBootFailure: 'continue' }, + plugins: { test: { onPluginBootFailure: 'abort' } }, + }, + }, + }, + }), + ]); + init.add( + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + throw new Error('NOPE'); + }, + }); + }, + }), + ); + await expect(init.start()).rejects.toThrow( + "Plugin 'test' startup failed; caused by Error: NOPE", + ); + }); + it('should forward errors when multiple plugins fail to start', async () => { const init = new BackendInitializer([]); init.add( diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index 02fad4f217..f521966d4d 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -24,6 +24,7 @@ import { RootLifecycleService, createServiceFactory, } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; import { ServiceOrExtensionPoint } from './types'; // Direct internal import to avoid duplication // eslint-disable-next-line @backstage/no-relative-monorepo-imports @@ -321,9 +322,19 @@ export class BackendInitializer { await this.#serviceRegistry.get(coreServices.rootLogger, 'root'), ); + const rootConfig = await this.#serviceRegistry.get( + coreServices.rootConfig, + 'root', + ); + // All plugins are initialized in parallel const results = await Promise.allSettled( allPluginIds.map(async pluginId => { + const isBootFailurePermitted = this.#getPluginBootFailurePredicate( + pluginId, + rootConfig, + ); + try { // Initialize all eager services await this.#serviceRegistry.initializeEagerServicesWithScope( @@ -392,8 +403,12 @@ export class BackendInitializer { await lifecycleService.startup(); } catch (error: unknown) { assertError(error); - initLogger.onPluginFailed(pluginId, error); - throw error; + if (isBootFailurePermitted) { + initLogger.onPermittedPluginFailure(pluginId, error); + } else { + initLogger.onPluginFailed(pluginId, error); + throw error; + } } }), ); @@ -620,6 +635,20 @@ export class BackendInitializer { } } } + + #getPluginBootFailurePredicate(pluginId: string, config?: Config): boolean { + const defaultStartupBootFailureValue = + config?.getOptionalString( + 'backend.startup.default.onPluginBootFailure', + ) ?? 'abort'; + + const pluginStartupBootFailureValue = + config?.getOptionalString( + `backend.startup.plugins.${pluginId}.onPluginBootFailure`, + ) ?? defaultStartupBootFailureValue; + + return pluginStartupBootFailureValue === 'continue'; + } } function toInternalBackendFeature( diff --git a/packages/backend-app-api/src/wiring/createInitializationLogger.ts b/packages/backend-app-api/src/wiring/createInitializationLogger.ts index e623d43074..0978cfb0c8 100644 --- a/packages/backend-app-api/src/wiring/createInitializationLogger.ts +++ b/packages/backend-app-api/src/wiring/createInitializationLogger.ts @@ -28,6 +28,7 @@ export function createInitializationLogger( ): { onPluginStarted(pluginId: string): void; onPluginFailed(pluginId: string, error: Error): void; + onPermittedPluginFailure(pluginId: string, error: Error): void; onAllStarted(): void; } { const logger = rootLogger?.child({ type: 'initialization' }); @@ -79,6 +80,13 @@ export function createInitializationLogger( error, ); }, + onPermittedPluginFailure(pluginId: string, error: Error) { + starting.delete(pluginId); + logger?.error( + `Plugin '${pluginId}' threw an error during startup, but boot failure is permitted for this plugin so startup will continue.`, + error, + ); + }, onAllStarted() { logger?.info(`Plugin initialization complete${getInitStatus()}`); diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index e981e021c9..ad2787af66 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/backend-defaults +## 0.8.0-next.3 + +### Patch Changes + +- 3740229: In the different `UrlReadersService`, the `search` method have been updated to use the `readUrl` if the given URL doesn't contain a pattern. + For `UrlReaders` that didn't implement the `search` method, `readUrl` is now called internally and throws if the given URL doesn't contain a pattern. +- 72cddf2: Updated `PermissionsRegistryService` to use `PermissionResourceRef`s and added the `getPermissionRuleset` method. +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/backend-app-api@1.2.0-next.3 + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + ## 0.8.0-next.2 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 23646481ab..33faa03fc8 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.8.0-next.2", + "version": "0.8.0-next.3", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index 8c3a2d1e1e..88d41ce229 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.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-backend@0.4.2-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.30-next.2 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 0.6.0-next.2 ### Minor Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 7c69bc6f01..c125cc47f4 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.0-next.2", + "version": "0.6.0-next.3", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-legacy/CHANGELOG.md b/packages/backend-legacy/CHANGELOG.md index 116bc590f0..a04ca5253e 100644 --- a/packages/backend-legacy/CHANGELOG.md +++ b/packages/backend-legacy/CHANGELOG.md @@ -1,5 +1,45 @@ # example-backend-legacy +## 0.2.107-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/plugin-scaffolder-backend@1.30.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.3 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.5-next.3 + - @backstage/plugin-kubernetes-backend@0.19.3-next.3 + - @backstage/plugin-search-backend-module-catalog@0.3.1-next.3 + - @backstage/plugin-search-backend-module-techdocs@0.3.6-next.3 + - @backstage/plugin-techdocs-backend@1.11.6-next.3 + - @backstage/plugin-permission-backend@0.5.54-next.2 + - @backstage/plugin-search-backend@1.8.2-next.3 + - @backstage/plugin-app-backend@0.4.5-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-backend@0.4.2-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-proxy-backend@0.5.11-next.2 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.0-next.3 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.6-next.2 + - @backstage/plugin-search-backend-module-elasticsearch@1.6.5-next.2 + - @backstage/plugin-search-backend-module-explore@0.2.8-next.2 + - @backstage/plugin-search-backend-module-pg@0.5.41-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/plugin-signals-backend@0.3.1-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-signals-node@0.1.17-next.2 + ## 0.2.107-next.2 ### Patch Changes diff --git a/packages/backend-legacy/package.json b/packages/backend-legacy/package.json index 92b07da170..55a58851e7 100644 --- a/packages/backend-legacy/package.json +++ b/packages/backend-legacy/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-legacy", - "version": "0.2.107-next.2", + "version": "0.2.107-next.3", "backstage": { "role": "backend" }, diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index afb10af077..4b3819d231 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-openapi-utils +## 0.5.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.5.0-next.2 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 48dce32ace..9177a6973d 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-openapi-utils", - "version": "0.5.0-next.2", + "version": "0.5.0-next.3", "description": "OpenAPI typescript support.", "backstage": { "role": "node-library" diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 6acabb94b8..405d62c695 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/backend-plugin-api +## 1.2.0-next.2 + +### Patch Changes + +- 72cddf2: Updated `PermissionsRegistryService` to use `PermissionResourceRef`s and added the `getPermissionRuleset` method. +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + ## 1.2.0-next.1 ### Minor Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 4af35d1c1a..d742f4ef48 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-plugin-api", - "version": "1.2.0-next.1", + "version": "1.2.0-next.2", "description": "Core API used by Backstage backend plugins", "backstage": { "role": "node-library" diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 198bbd3ff0..c5c91bb0d4 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.0-next.3 + +### Patch Changes + +- 72cddf2: Added the new `getPermissionRuleset` method to `mockServices.permissionsRegistry`. +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/backend-app-api@1.2.0-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 1.3.0-next.2 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index fadb217395..166d9947c7 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.0-next.2", + "version": "1.3.0-next.3", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index d4e20e4706..2d5c05db3b 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,43 @@ # example-backend +## 0.0.35-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.6.0-next.2 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/plugin-scaffolder-backend@1.30.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.5-next.3 + - @backstage/plugin-catalog-backend-module-openapi@0.2.7-next.3 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.3 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.5-next.3 + - @backstage/plugin-kubernetes-backend@0.19.3-next.3 + - @backstage/plugin-notifications-backend@0.5.2-next.3 + - @backstage/plugin-search-backend-module-catalog@0.3.1-next.3 + - @backstage/plugin-search-backend-module-techdocs@0.3.6-next.3 + - @backstage/plugin-techdocs-backend@1.11.6-next.3 + - @backstage/plugin-devtools-backend@0.5.2-next.3 + - @backstage/plugin-permission-backend@0.5.54-next.2 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.5-next.2 + - @backstage/plugin-search-backend@1.8.2-next.3 + - @backstage/plugin-app-backend@0.4.5-next.2 + - @backstage/plugin-auth-backend-module-github-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.5-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-backend@0.4.2-next.3 + - @backstage/plugin-proxy-backend@0.5.11-next.2 + - @backstage/plugin-search-backend-module-explore@0.2.8-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/plugin-signals-backend@0.3.1-next.2 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.7-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-permission-common@0.8.4 + ## 0.0.35-next.2 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 9990be9477..8a72499c05 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.35-next.2", + "version": "0.0.35-next.3", "backstage": { "role": "backend" }, diff --git a/packages/canon/.storybook/preview.tsx b/packages/canon/.storybook/preview.tsx index fbc319ccea..8ffa2e55df 100644 --- a/packages/canon/.storybook/preview.tsx +++ b/packages/canon/.storybook/preview.tsx @@ -1,15 +1,12 @@ import React from 'react'; import type { Preview, ReactRenderer } from '@storybook/react'; import { withThemeByDataAttribute } from '@storybook/addon-themes'; +import { CanonProvider } from '../src/contexts/canon'; // Canon specific styles import '../src/css/core.css'; import '../src/css/components.css'; -// Custom themes -import './themes/backstage.css'; -import { CanonProvider } from '../src/contexts/canon'; - const preview: Preview = { parameters: { controls: { @@ -79,8 +76,6 @@ const preview: Preview = { themes: { Light: 'light', Dark: 'dark', - 'Backstage Light': 'backstage-light', - 'Backstage Dark': 'backstage-dark', }, defaultTheme: 'Light', }), diff --git a/packages/canon/.storybook/themes/backstage.css b/packages/canon/.storybook/themes/backstage.css deleted file mode 100644 index 653836c558..0000000000 --- a/packages/canon/.storybook/themes/backstage.css +++ /dev/null @@ -1,88 +0,0 @@ -[data-theme-name='legacy'][data-theme='light'], -[data-theme-name='legacy'][data-theme='dark'] { - --canon-font-regular: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; - - .canon-Button { - border-radius: 60px; - font-weight: 400; - text-transform: uppercase; - font-size: 0.875rem; - transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, - box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, - border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; - line-height: 1.75; - border-radius: 4px; - background-color: #1f5493; - padding: 6px 16px; - box-shadow: none; - - &:hover { - box-shadow: none; - } - - &.canon-Button--variant-primary { - color: #fff; - background-color: rgb(21, 58, 102); - box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), - 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12); - - &:hover { - box-shadow: 0px 2px 4px -1px rgba(0, 0, 0, 0.2), - 0px 4px 5px 0px rgba(0, 0, 0, 0.14), - 0px 1px 10px 0px rgba(0, 0, 0, 0.12); - } - } - - &.canon-Button--variant-secondary { - background-color: transparent; - border: 1px solid rgba(31, 84, 147, 0.5); - color: #1f5493; - - &:hover { - border: 1px solid #1f5493; - background-color: rgba(31, 84, 147, 0.04); - } - } - - &.canon-Button--variant-tertiary { - background-color: transparent; - border: none; - color: #1f5493; - } - } -} - -[data-theme-name='legacy'][data-theme='light'] { - /* Colors */ - --canon-bg-accent: #2e77d0; - --canon-bg: #f8f8f8; - --canon-bg-elevated: #fff; - --canon-bg: #f4f4f4; - - /* Text colors */ - --canon-fg-text-primary: #000; - --canon-fg-accent: #fff; - --canon-fg-text-secondary: #646464; -} - -[data-theme-name='legacy'][data-theme='dark'] { - /* Colors */ - --canon-bg-accent: #fff; - --canon-bg: #000; - --canon-bg-elevated: #121212; - --canon-bg: #1a1a1a; - - /* Borders */ - --canon-border: rgba(255, 255, 255, 0.2); - --canon-border-warning: #f50000; - --canon-border-danger: #f87503; - --canon-border-focus: #25d262; - - /* States - Add more states */ - --canon-fg-danger: #f50000; - - /* Text colors */ - --canon-fg-text-primary: #fff; - --canon-fg-accent: #000; - --canon-fg-text-secondary: #b3b3b3; -} diff --git a/packages/canon/CHANGELOG.md b/packages/canon/CHANGELOG.md index dc2a6e2987..93b2a9b67b 100644 --- a/packages/canon/CHANGELOG.md +++ b/packages/canon/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/canon +## 0.1.0-next.2 + +### Minor Changes + +- 8309bdb: Updated core CSS tokens and fixing the Button component accordingly. + +### Patch Changes + +- f44e5cf: Fix spacing props not being applied for custom values. + ## 0.1.0-next.1 ### Minor Changes diff --git a/packages/canon/package.json b/packages/canon/package.json index 54efbe550e..9255268074 100644 --- a/packages/canon/package.json +++ b/packages/canon/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/canon", - "version": "0.1.0-next.1", + "version": "0.1.0-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index ef59d46f68..f194084a9b 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -121,6 +121,27 @@ export const Button: React_2.ForwardRefExoticComponent< ButtonProps & React_2.RefAttributes >; +// @public (undocumented) +export type ButtonOwnProps = GetPropDefTypes; + +// @public (undocumented) +export const buttonPropDefs: { + variant: { + type: 'enum'; + values: ('primary' | 'secondary')[]; + className: string; + default: 'primary'; + responsive: true; + }; + size: { + type: 'enum'; + values: ('small' | 'medium')[]; + className: string; + default: 'medium'; + responsive: true; + }; +}; + // @public export interface ButtonProps { // (undocumented) @@ -134,15 +155,11 @@ export interface ButtonProps { // (undocumented) iconStart?: IconNames; // (undocumented) - size?: 'small' | 'medium' | Partial>; + size?: ButtonOwnProps['size']; // (undocumented) style?: React.CSSProperties; // (undocumented) - variant?: - | 'primary' - | 'secondary' - | 'tertiary' - | Partial>; + variant?: ButtonOwnProps['variant']; } // @public (undocumented) @@ -670,14 +687,14 @@ export const marginPropDefs: (spacingValues: string[]) => { type: 'enum | string'; values: string[]; className: string; - customProperties: ('--ml' | '--mr')[]; + customProperties: '--mx'[]; responsive: true; }; my: { type: 'enum | string'; values: string[]; className: string; - customProperties: ('--mt' | '--mb')[]; + customProperties: '--my'[]; responsive: true; }; mt: { @@ -732,14 +749,14 @@ export const paddingPropDefs: (spacingValues: string[]) => { px: { type: 'enum | string'; className: string; - customProperties: ('--pl' | '--pr')[]; + customProperties: '--px'[]; values: string[]; responsive: true; }; py: { type: 'enum | string'; className: string; - customProperties: ('--pt' | '--pb')[]; + customProperties: '--py'[]; values: string[]; responsive: true; }; diff --git a/packages/canon/src/components/Box/Box.stories.tsx b/packages/canon/src/components/Box/Box.stories.tsx index 7b9aecca1a..aec2909743 100644 --- a/packages/canon/src/components/Box/Box.stories.tsx +++ b/packages/canon/src/components/Box/Box.stories.tsx @@ -54,6 +54,13 @@ const Card = () => { }; export const Default: Story = { + args: { + children: 'Hello World', + mb: '4', + }, +}; + +export const Preview: Story = { args: { children: , display: 'inline', @@ -141,6 +148,25 @@ export const Padding: Story = { Padding Left + + + Custom Padding + + + Custom Padding X + + + Custom Padding Y + + + + + Custom Padding Top Bottom + + + Custom Padding Left Right + + ), }; @@ -195,6 +221,35 @@ export const Margin: Story = { + + + + Custom Margin + + + + + Custom Margin X + + + + + Custom Margin Y + + + + + + + Custom Margin Top Bottom + + + + + Custom Margin Left Right + + + ), }; diff --git a/packages/canon/src/components/Box/Box.tsx b/packages/canon/src/components/Box/Box.tsx index 00e15a2b72..8718c77724 100644 --- a/packages/canon/src/components/Box/Box.tsx +++ b/packages/canon/src/components/Box/Box.tsx @@ -27,7 +27,7 @@ import { displayPropDefs } from '../../props/display.props'; /** @public */ export const Box = forwardRef((props, ref) => { - const { as = 'div', children } = props; + const { children } = props; const propDefs = { ...spacingPropDefs, @@ -37,9 +37,10 @@ export const Box = forwardRef((props, ref) => { ...displayPropDefs, ...boxPropDefs, }; + const { className, style } = extractProps(props, propDefs); - return createElement(as, { + return createElement(props.as || 'div', { ref, className: clsx('canon-Box', className), style, diff --git a/packages/canon/src/components/Box/styles.css b/packages/canon/src/components/Box/styles.css index 229f322343..f3b24793cf 100644 --- a/packages/canon/src/components/Box/styles.css +++ b/packages/canon/src/components/Box/styles.css @@ -1,4 +1,4 @@ .canon-Box { font-family: var(--canon-font-regular); - color: var(--canon-fg-text-primary); + color: var(--canon-fg-primary); } diff --git a/packages/canon/src/components/Button/Button.props.ts b/packages/canon/src/components/Button/Button.props.ts new file mode 100644 index 0000000000..e363360afa --- /dev/null +++ b/packages/canon/src/components/Button/Button.props.ts @@ -0,0 +1,41 @@ +/* + * 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 type { PropDef, GetPropDefTypes } from '../../props/prop-def'; + +/** @public */ +export const buttonPropDefs = { + variant: { + type: 'enum', + values: ['primary', 'secondary'], + className: 'canon-Button--variant', + default: 'primary', + responsive: true, + }, + size: { + type: 'enum', + values: ['small', 'medium'], + className: 'canon-Button--size', + default: 'medium', + responsive: true, + }, +} satisfies { + variant: PropDef<'primary' | 'secondary'>; + size: PropDef<'small' | 'medium'>; +}; + +/** @public */ +export type ButtonOwnProps = GetPropDefTypes; diff --git a/packages/canon/src/components/Button/Button.stories.tsx b/packages/canon/src/components/Button/Button.stories.tsx index cd52c7fdba..8ba060ad8e 100644 --- a/packages/canon/src/components/Button/Button.stories.tsx +++ b/packages/canon/src/components/Button/Button.stories.tsx @@ -29,9 +29,14 @@ const meta = { control: 'select', options: ['small', 'medium'], }, + variant: { + control: 'select', + options: ['primary', 'secondary'], + }, }, args: { size: 'medium', + variant: 'primary', }, } satisfies Meta; @@ -57,9 +62,6 @@ export const Variants: Story = { - ), }; @@ -94,7 +96,7 @@ export const FullWidth: Story = { children: 'Button', }, render: args => ( - + + + + ))} diff --git a/packages/canon/src/components/Button/Button.tsx b/packages/canon/src/components/Button/Button.tsx index abdd027bf0..97e9ec8fe5 100644 --- a/packages/canon/src/components/Button/Button.tsx +++ b/packages/canon/src/components/Button/Button.tsx @@ -42,7 +42,6 @@ export const Button = forwardRef( return ( ); }, diff --git a/packages/canon/src/components/Button/index.tsx b/packages/canon/src/components/Button/index.tsx index c323ba0329..9051f1145e 100644 --- a/packages/canon/src/components/Button/index.tsx +++ b/packages/canon/src/components/Button/index.tsx @@ -15,3 +15,5 @@ */ export { Button } from './Button'; export type { ButtonProps } from './types'; +export { buttonPropDefs } from './Button.props'; +export type { ButtonOwnProps } from './Button.props'; diff --git a/packages/canon/src/components/Button/styles.css b/packages/canon/src/components/Button/styles.css index 6bfd93a8ea..11a57a2571 100644 --- a/packages/canon/src/components/Button/styles.css +++ b/packages/canon/src/components/Button/styles.css @@ -22,62 +22,73 @@ user-select: none; font-family: var(--canon-font-regular); font-weight: var(--canon-font-weight-bold); - font-size: var(--canon-font-size-3); padding: 0; - transition: all 150ms ease; cursor: pointer; border-radius: var(--canon-radius-2); + gap: var(--canon-space-1_5); + + &:disabled { + cursor: not-allowed; + } } .canon-Button--variant-primary { - background-color: var(--canon-bg-accent); - color: var(--canon-fg-accent); + background-color: var(--canon-bg-solid); + color: var(--canon-fg-solid); + transition: background-color 150ms ease; &:hover { - background-color: transparent; - box-shadow: inset 0 0 0 1px var(--canon-border-focus); - color: var(--canon-fg-text-primary); + background-color: var(--canon-bg-solid-hover); + } + + &:active { + background-color: var(--canon-bg-solid-pressed); + } + + &:disabled { + background-color: var(--canon-bg-solid-disabled); + color: var(--canon-fg-solid-disabled); } } .canon-Button--variant-secondary { - background-color: transparent; + background-color: var(--canon-bg-elevated); box-shadow: inset 0 0 0 1px var(--canon-border); - color: var(--canon-fg-text-primary); + color: var(--canon-fg-primary); + transition: box-shadow 150ms ease; &:hover { box-shadow: inset 0 0 0 1px var(--canon-border-hover); } -} -.canon-Button--variant-tertiary { - background-color: transparent; - color: var(--canon-fg-text-primary); + &:active { + box-shadow: inset 0 0 0 1px var(--canon-border-pressed); + } - &:hover { - color: var(--canon-fg-text-secondary); + &:disabled { + box-shadow: inset 0 0 0 1px var(--canon-border-disabled); + color: var(--canon-fg-disabled); } } -.canon-Button--size-small { - padding-left: 6px; - padding-right: 6px; - height: 32px; -} - .canon-Button--size-medium { - padding-left: 8px; - padding-right: 8px; + font-size: var(--canon-font-size-4); + padding: 0 var(--canon-space-3); height: 40px; } -.canon-Button--content { - display: flex; - align-items: center; - gap: var(--canon-space-3); - font-weight: var(--canon-font-weight-bold); +.canon-Button--size-small { + font-size: var(--canon-font-size-3); + padding: 0 var(--canon-space-2); + height: 32px; } -.canon-Button--icon-start-end { - flex: 1; +.canon-Button--size-small .canon-Button--icon { + width: 1rem; + height: 1rem; +} + +.canon-Button--size-medium .canon-Button--icon { + width: 1.5rem; + height: 1.5rem; } diff --git a/packages/canon/src/components/Button/types.ts b/packages/canon/src/components/Button/types.ts index 3e2adc84fc..89bb8c79e5 100644 --- a/packages/canon/src/components/Button/types.ts +++ b/packages/canon/src/components/Button/types.ts @@ -14,19 +14,16 @@ * limitations under the License. */ import { IconNames } from '../Icon'; -import type { Breakpoint } from '../../types'; +import type { ButtonOwnProps } from './Button.props'; + /** * Properties for {@link Button} * * @public */ export interface ButtonProps { - size?: 'small' | 'medium' | Partial>; - variant?: - | 'primary' - | 'secondary' - | 'tertiary' - | Partial>; + size?: ButtonOwnProps['size']; + variant?: ButtonOwnProps['variant']; children: React.ReactNode; className?: string; disabled?: boolean; diff --git a/packages/canon/src/components/Checkbox/Checkbox.tsx b/packages/canon/src/components/Checkbox/Checkbox.tsx index 634dc7044c..78dea16c1a 100644 --- a/packages/canon/src/components/Checkbox/Checkbox.tsx +++ b/packages/canon/src/components/Checkbox/Checkbox.tsx @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; +import React from 'react'; import { Checkbox as CheckboxPrimitive } from '@base-ui-components/react/checkbox'; import { Icon } from '@backstage/canon'; import type { CheckboxProps } from './types'; diff --git a/packages/canon/src/components/Checkbox/styles.css b/packages/canon/src/components/Checkbox/styles.css index 1da96e8344..386c6f4671 100644 --- a/packages/canon/src/components/Checkbox/styles.css +++ b/packages/canon/src/components/Checkbox/styles.css @@ -11,7 +11,8 @@ transition: all 0.2s ease-in-out; &[data-checked] { - background-color: var(--canon-bg-accent); + background-color: var(--canon-bg-solid); + color: var(--canon-fg-solid); } } @@ -23,7 +24,7 @@ gap: var(--canon-space-2); font-size: var(--canon-font-size-xs); font-family: var(--canon-font-regular); - color: var(--canon-fg-text-primary); + color: var(--canon-fg-primary); user-select: none; &:hover { diff --git a/packages/canon/src/components/Field/Field.styles.css b/packages/canon/src/components/Field/Field.styles.css index 6bf695fc97..6a8e3c8e22 100644 --- a/packages/canon/src/components/Field/Field.styles.css +++ b/packages/canon/src/components/Field/Field.styles.css @@ -24,14 +24,14 @@ .canon-FieldLabel { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-text-primary); + color: var(--canon-fg-primary); margin-bottom: var(--canon-space-1_5); } .canon-FieldDescription { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-text-secondary); + color: var(--canon-fg-secondary); margin: 0; padding-top: var(--canon-space-1_5); } @@ -47,7 +47,7 @@ .canon-FieldValidity { font-size: var(--canon-font-size-2); font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-text-secondary); + color: var(--canon-fg-secondary); margin: 0; padding-top: var(--canon-space-1_5); } diff --git a/packages/canon/src/components/Heading/styles.css b/packages/canon/src/components/Heading/styles.css index a730afe9f0..2db11d7eb2 100644 --- a/packages/canon/src/components/Heading/styles.css +++ b/packages/canon/src/components/Heading/styles.css @@ -16,7 +16,7 @@ .canon-Heading { font-family: var(--canon-font-regular); - color: var(--canon-fg-text-primary); + color: var(--canon-fg-primary); line-height: 100%; padding: 0; margin: 0; diff --git a/packages/canon/src/components/Icon/Icon.tsx b/packages/canon/src/components/Icon/Icon.tsx index c761d0d104..4c7b55680a 100644 --- a/packages/canon/src/components/Icon/Icon.tsx +++ b/packages/canon/src/components/Icon/Icon.tsx @@ -21,7 +21,7 @@ import clsx from 'clsx'; /** @public */ export const Icon = (props: IconProps) => { - const { name, size = 16, className, style, ...restProps } = props; + const { name, size, className, style, ...restProps } = props; const { icons } = useCanon(); const CanonIcon = icons[name] as React.ComponentType>; @@ -34,7 +34,10 @@ export const Icon = (props: IconProps) => { return ( ); diff --git a/packages/canon/src/components/Input/Input.styles.css b/packages/canon/src/components/Input/Input.styles.css index cfeed27b48..fa196cfa20 100644 --- a/packages/canon/src/components/Input/Input.styles.css +++ b/packages/canon/src/components/Input/Input.styles.css @@ -21,13 +21,13 @@ background-color: var(--canon-bg-elevated); font-size: var(--canon-font-size-3); font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-text-primary); + color: var(--canon-fg-primary); transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out; width: 100%; } .canon-Input::placeholder { - color: var(--canon-fg-text-secondary); + color: var(--canon-fg-secondary); } .canon-Input:hover { @@ -35,9 +35,9 @@ } .canon-Input:focus-visible { - outline-color: var(--canon-border-focus); + outline-color: var(--canon-border-pressed); outline-width: 0px; - border-color: var(--canon-border-focus); + border-color: var(--canon-border-pressed); } .canon-Input[data-invalid] { diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css index 9cf8c884fb..871960b8db 100644 --- a/packages/canon/src/components/Text/styles.css +++ b/packages/canon/src/components/Text/styles.css @@ -16,7 +16,7 @@ .canon-Text { font-family: var(--canon-font-regular); - color: var(--canon-fg-text-primary); + color: var(--canon-fg-primary); padding: 0; margin: 0; } diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index d744d468d0..cc0e5ab85a 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -62,81 +62,90 @@ --canon-space-14: calc(var(--canon-space) * 14); /* 56px */ /* Radius */ - --canon-radius-factor: 1.5; - --canon-radius-1: calc(3px * var(--canon-radius-factor)); - --canon-radius-2: calc(4px * var(--canon-radius-factor)); - --canon-radius-3: calc(6px * var(--canon-radius-factor)); - --canon-radius-4: calc(8px * var(--canon-radius-factor)); - --canon-radius-5: calc(12px * var(--canon-radius-factor)); - --canon-radius-6: calc(16px * var(--canon-radius-factor)); + --canon-radius-1: calc(0.125rem); + --canon-radius-2: calc(0.25rem); + --canon-radius-3: calc(0.5rem); + --canon-radius-4: calc(0.75rem); + --canon-radius-5: calc(1rem); + --canon-radius-6: calc(1.25rem); --canon-radius-full: 9999px; - /* Colors */ + /* Background Colors */ --canon-bg: #f8f8f8; --canon-bg-elevated: #fff; - --canon-bg-accent: #000; - --canon-bg-accent-focus: #000; - --canon-bg-accent-disabled: #000; - --canon-bg-accent-hover: #000; - --canon-bg-tint: #edf2fe; - --canon-bg-tint-active: #c7d7ff; - --canon-bg-tint-disabled: #f8f8f8; - --canon-bg-tint-hover: #dbe4ff; - --canon-bg-danger: #f8f8f8; - --canon-bg-elevated: #fff; - --canon-bg-success: #f8f8f8; - --canon-bg-warning: #f8f8f8; - --canon-border: rgba(0, 0, 0, 0.1); - --canon-border-accent: #000; - --canon-border-accent-focus: #000; - --canon-border-accent-hover: #000; - --canon-border-danger: #e22b2b; - --canon-border-focus: rgba(0, 0, 0, 0.4); - --canon-border-hover: rgba(0, 0, 0, 0.2); - --canon-border-success: #008000; - --canon-border-warning: #e36d05; - --canon-fg-text-primary: #000; - --canon-fg-text-secondary: #646464; - --canon-fg-link: #fff; - --canon-fg-link-hover: #fff; - --canon-fg-accent: #fff; - --canon-fg-tint: #000; + --canon-bg-solid: #1f5493; + --canon-bg-solid-hover: #163a66; + --canon-bg-solid-pressed: #0f2b4e; + --canon-bg-solid-disabled: #ebebeb; + --canon-bg-tint: transparent; + --canon-bg-tint-hover: rgba(31, 84, 147, 0.4); + --canon-bg-tint-pressed: rgba(31, 84, 147, 0.6); + --canon-bg-tint-disabled: #ebebeb; + --canon-bg-danger: #feebe7; + --canon-bg-warning: #fff2b2; + --canon-bg-success: #e6f6eb; + + /* Foreground Colors */ + --canon-fg-primary: #000000; + --canon-fg-secondary: #757575; + --canon-fg-link: #1f5493; + --canon-fg-link-hover: #1f2d5c; + --canon-fg-disabled: #9e9e9e; + --canon-fg-solid: #ffffff; + --canon-fg-solid-disabled: #9c9c9c; + --canon-fg-tint: #1f5493; + --canon-fg-tint-disabled: #9e9e9e; --canon-fg-danger: #e22b2b; --canon-fg-warning: #e36d05; - --canon-fg-success: #008000; + --canon-fg-success: #1db954; + + /* Border Colors */ + --canon-border: rgba(0, 0, 0, 0.1); + --canon-border-hover: rgba(0, 0, 0, 0.2); + --canon-border-pressed: rgba(0, 0, 0, 0.4); + --canon-border-disabled: rgba(0, 0, 0, 0.1); + --canon-border-danger: #f87a7a; + --canon-border-warning: #e36d05; + --canon-border-success: #53db83; } /* Dark theme tokens */ [data-theme='dark'] { - --canon-bg: #f8f8f8; - --canon-bg-elevated: #fff; - --canon-bg-accent: #000; - --canon-bg-accent-focus: #000; - --canon-bg-accent-disabled: #000; - --canon-bg-accent-hover: #000; - --canon-bg-tint: #edf2fe; - --canon-bg-tint-active: #c7d7ff; - --canon-bg-tint-disabled: #f8f8f8; - --canon-bg-tint-hover: #dbe4ff; - --canon-bg-danger: #f8f8f8; - --canon-bg-elevated: #fff; - --canon-bg-success: #f8f8f8; - --canon-bg-warning: #f8f8f8; - --canon-border: rgba(0, 0, 0, 0.1); - --canon-border-accent: #000; - --canon-border-accent-focus: #000; - --canon-border-accent-hover: #000; - --canon-border-danger: #e22b2b; - --canon-border-focus: rgba(0, 0, 0, 0.4); - --canon-border-hover: rgba(0, 0, 0, 0.2); - --canon-border-success: #008000; - --canon-border-warning: #e36d05; - --canon-fg-accent: #000; + /* Background Colors */ + --canon-bg: #000000; + --canon-bg-elevated: #191919; + --canon-bg-solid: #9cc9ff; + --canon-bg-solid-hover: #83b9fd; + --canon-bg-solid-pressed: #83b9fd; + --canon-bg-solid-disabled: #222222; + --canon-bg-tint: transparent; + --canon-bg-tint-hover: rgba(156, 201, 255, 0.12); + --canon-bg-tint-pressed: rgba(156, 201, 255, 0.16); + --canon-bg-tint-disabled: transparent; + --canon-bg-danger: #3b1219; + --canon-bg-warning: #302008; + --canon-bg-success: #132d21; + + /* Foreground Colors */ + --canon-fg-primary: #ffffff; + --canon-fg-secondary: #9e9e9e; + --canon-fg-link: #9cc9ff; + --canon-fg-link-hover: #7eb5f7; + --canon-fg-disabled: #9e9e9e; + --canon-fg-solid: #101821; + --canon-fg-solid-disabled: #575757; + --canon-fg-tint: #9cc9ff; + --canon-fg-tint-disabled: #575757; --canon-fg-danger: #e22b2b; - --canon-fg-success: #008000; --canon-fg-warning: #e36d05; - --canon-fg-link: #fff; - --canon-fg-link-hover: #fff; - --canon-fg-text-primary: #000; - --canon-fg-text-secondary: #646464; + --canon-fg-success: #1db954; + + /* Border Colors */ + --canon-border: rgba(255, 255, 255, 0.2); + --canon-border-hover: rgba(255, 255, 255, 0.4); + --canon-border-pressed: rgba(255, 255, 255, 0.5); + --canon-border-disabled: rgba(255, 255, 255, 0.2); + --canon-border-danger: #f87a7a; + --canon-border-warning: #e36d05; + --canon-border-success: #53db83; } diff --git a/packages/canon/src/css/utilities/m.css b/packages/canon/src/css/utilities/m.css index 00a30cc8b1..ecee3aca35 100644 --- a/packages/canon/src/css/utilities/m.css +++ b/packages/canon/src/css/utilities/m.css @@ -1,5 +1,5 @@ .cu-m { - margin: var(--p); + margin: var(--m); } .cu-m-0\.5 { diff --git a/packages/canon/src/css/utilities/mb.css b/packages/canon/src/css/utilities/mb.css index 4fbf8d614f..6d3831bad3 100644 --- a/packages/canon/src/css/utilities/mb.css +++ b/packages/canon/src/css/utilities/mb.css @@ -1,5 +1,5 @@ .cu-mb { - margin-bottom: var(--pb); + margin-bottom: var(--mb); } .cu-mb-0\.5 { diff --git a/packages/canon/src/css/utilities/ml.css b/packages/canon/src/css/utilities/ml.css index 887c464df1..b495d1a8bf 100644 --- a/packages/canon/src/css/utilities/ml.css +++ b/packages/canon/src/css/utilities/ml.css @@ -1,5 +1,5 @@ .cu-ml { - margin-left: var(--pl); + margin-left: var(--ml); } .cu-ml-0\.5 { diff --git a/packages/canon/src/css/utilities/mr.css b/packages/canon/src/css/utilities/mr.css index a2c0f8668b..9a179e1103 100644 --- a/packages/canon/src/css/utilities/mr.css +++ b/packages/canon/src/css/utilities/mr.css @@ -1,5 +1,5 @@ .cu-mr { - margin-right: var(--pr); + margin-right: var(--mr); } .cu-mr-0\.5 { diff --git a/packages/canon/src/css/utilities/mt.css b/packages/canon/src/css/utilities/mt.css index b74525b142..2e0815946d 100644 --- a/packages/canon/src/css/utilities/mt.css +++ b/packages/canon/src/css/utilities/mt.css @@ -1,5 +1,5 @@ .cu-mt { - margin-top: var(--pt); + margin-top: var(--mt); } .cu-mt-0\.5 { diff --git a/packages/canon/src/css/utilities/mx.css b/packages/canon/src/css/utilities/mx.css index 6ebbc57367..43f5838e4e 100644 --- a/packages/canon/src/css/utilities/mx.css +++ b/packages/canon/src/css/utilities/mx.css @@ -1,6 +1,6 @@ .cu-mx { - margin-left: var(--px); - margin-right: var(--px); + margin-left: var(--mx); + margin-right: var(--mx); } .cu-mx-0\.5 { diff --git a/packages/canon/src/css/utilities/my.css b/packages/canon/src/css/utilities/my.css index feb4a6f5bd..ae72ea9209 100644 --- a/packages/canon/src/css/utilities/my.css +++ b/packages/canon/src/css/utilities/my.css @@ -1,6 +1,6 @@ .cu-my { - margin-top: var(--py); - margin-bottom: var(--py); + margin-top: var(--my); + margin-bottom: var(--my); } .cu-my-0\.5 { diff --git a/packages/canon/src/css/utilities/sm.css b/packages/canon/src/css/utilities/sm.css index 2a216a950e..01603f0218 100644 --- a/packages/canon/src/css/utilities/sm.css +++ b/packages/canon/src/css/utilities/sm.css @@ -38,7 +38,7 @@ } .cu-sm-border-selected { - border-color: var(--canon-border-focus); + border-color: var(--canon-border-pressed); border-width: 1px; border-style: solid; } diff --git a/packages/canon/src/css/utilities/xs.css b/packages/canon/src/css/utilities/xs.css index 1d8dae99cd..5470355fdb 100644 --- a/packages/canon/src/css/utilities/xs.css +++ b/packages/canon/src/css/utilities/xs.css @@ -37,7 +37,7 @@ } .cu-border-selected { - border-color: var(--canon-border-focus); + border-color: var(--canon-border-pressed); border-width: 1px; border-style: solid; } diff --git a/packages/canon/src/props/margin.props.ts b/packages/canon/src/props/margin.props.ts index 94ec01cfc8..12cb0989e2 100644 --- a/packages/canon/src/props/margin.props.ts +++ b/packages/canon/src/props/margin.props.ts @@ -29,14 +29,14 @@ const marginPropDefs = (spacingValues: string[]) => type: 'enum | string', values: spacingValues, className: 'cu-mx', - customProperties: ['--ml', '--mr'], + customProperties: ['--mx'], responsive: true, }, my: { type: 'enum | string', values: spacingValues, className: 'cu-my', - customProperties: ['--mt', '--mb'], + customProperties: ['--my'], responsive: true, }, mt: { diff --git a/packages/canon/src/props/padding.props.ts b/packages/canon/src/props/padding.props.ts index 4ae686adb9..ea42fcd4f1 100644 --- a/packages/canon/src/props/padding.props.ts +++ b/packages/canon/src/props/padding.props.ts @@ -29,14 +29,14 @@ const paddingPropDefs = (spacingValues: string[]) => px: { type: 'enum | string', className: 'cu-px', - customProperties: ['--pl', '--pr'], + customProperties: ['--px'], values: spacingValues, responsive: true, }, py: { type: 'enum | string', className: 'cu-py', - customProperties: ['--pt', '--pb'], + customProperties: ['--py'], values: spacingValues, responsive: true, }, diff --git a/packages/canon/src/utils/extractProps.ts b/packages/canon/src/utils/extractProps.ts index fdd98048cf..8631f4e228 100644 --- a/packages/canon/src/utils/extractProps.ts +++ b/packages/canon/src/utils/extractProps.ts @@ -24,36 +24,35 @@ export function extractProps( }, propDefs: { [key: string]: any }, ) { - let className: string = props.className || ''; - let style: React.CSSProperties = props.style || {}; + let className: string[] = (props.className || '').split(' '); + let style: React.CSSProperties = { ...props.style }; + const hasProp = (key: string) => props.hasOwnProperty(key); for (const key in propDefs) { // Check if the prop is present or has a default value - if ( - !props.hasOwnProperty(key) && - !propDefs[key].hasOwnProperty('default') - ) { + if (!hasProp(key) && !propDefs[key].hasOwnProperty('default')) { continue; // Skip processing if neither is present } - const value = props.hasOwnProperty(key) - ? props[key] - : propDefs[key].default; + const value = hasProp(key) ? props[key] : propDefs[key].default; const propDefsValues = propDefs[key].values; const propDefsCustomProperties = propDefs[key].customProperties; const propDefsClassName = propDefs[key].className; const isResponsive = propDefs[key].responsive; - const handleValue = (val: any, prefix: string = '') => { + const handleValue = (val: string, prefix: string = '') => { + // Skip adding class name if the key is "as" + if (key === 'as') return; + if (propDefsValues.includes(val)) { - className += ` ${prefix}${propDefsClassName}-${val}`; + className.push(`${prefix}${propDefsClassName}-${val}`); } else { const customPropertyKey = isResponsive && prefix ? `${propDefsCustomProperties}-${prefix.slice(0, -1)}` : propDefsCustomProperties; (style as any)[customPropertyKey] = val; - className += ` ${prefix}${propDefsClassName}`; + className.push(`${prefix}${propDefsClassName}`); } }; @@ -76,5 +75,9 @@ export function extractProps( return acc; }, {} as { [key: string]: any }); - return { ...cleanedProps, className, style }; + const newClassNames = className + .filter(name => name && name.trim() !== '') + .join(' '); + + return { ...cleanedProps, className: newClassNames, style }; } diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 07b61927eb..b72e8a6e73 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/cli +## 0.30.0-next.3 + +### Minor Changes + +- b30e788: The `new` command is now powered by a new template system that allows you to define your own templates in a declarative way, as well as import existing templates from external sources. See the [CLI templates documentation](https://backstage.io/docs/tooling/cli/templates) for more information. + + The following flags for the `new` command have been deprecated and will be removed in a future release: + + - `--license=`: Configure the global `license` instead. + - `--no-private`: Configure the global `private` instead. + - `--baseVersion=`: Configure the global `version` instead. + - `--npmRegistry=`: Configure the global `publishRegistry` instead. + - `--scope=`: Configure the global `namePrefix` and/or `namePluginInfix` instead. + + As part of this change the template IDs and their options have changed. The following backwards compatibility mappings for the `--select` and `--option` flags are enabled when using the default set of templates, but they will also be removed in the future: + + - `--select=plugin` is mapped to `--select=frontend-plugin` instead. + - `--option=id=` is mapped to `--option=pluginId=` instead. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/integration@1.16.1 + - @backstage/release-manifests@0.0.12 + - @backstage/types@1.2.1 + ## 0.30.0-next.2 ### Patch Changes diff --git a/packages/cli/cli-report.md b/packages/cli/cli-report.md index d9fec4db22..41b1ddab73 100644 --- a/packages/cli/cli-report.md +++ b/packages/cli/cli-report.md @@ -179,6 +179,7 @@ Usage: backstage-cli new [options] Options: --select --option = + --skip-install --scope --npm-registry --baseVersion diff --git a/packages/cli/package.json b/packages/cli/package.json index fc211f237a..cbcebaa9da 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.30.0-next.2", + "version": "0.30.0-next.3", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" @@ -127,7 +127,7 @@ "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.4.2", "minimatch": "^9.0.0", - "node-libs-browser": "^2.2.1", + "node-stdlib-browser": "^1.3.1", "npm-packlist": "^5.0.0", "ora": "^5.3.0", "p-queue": "^6.6.2", @@ -159,7 +159,8 @@ "yargs": "^16.2.0", "yml-loader": "^2.1.0", "yn": "^4.0.0", - "zod": "^3.22.4" + "zod": "^3.22.4", + "zod-validation-error": "^3.4.0" }, "devDependencies": { "@backstage/backend-plugin-api": "workspace:^", diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index f1119053ec..47c362988c 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -257,6 +257,10 @@ export function registerCommands(program: Command) { (opt, arr: string[]) => [...arr, opt], [], ) + .option( + '--skip-install', + `Skips running 'yarn install' and 'yarn lint --fix'`, + ) .option('--scope ', 'The scope to use for new packages') .option( '--npm-registry ', diff --git a/packages/cli/src/commands/new/new.ts b/packages/cli/src/commands/new/new.ts index 2733b0be9b..5c06555905 100644 --- a/packages/cli/src/commands/new/new.ts +++ b/packages/cli/src/commands/new/new.ts @@ -14,17 +14,66 @@ * limitations under the License. */ -import os from 'os'; -import fs from 'fs-extra'; -import { join as joinPath } from 'path'; -import { OptionValues } from 'commander'; -import { FactoryRegistry } from '../../lib/new/FactoryRegistry'; -import { isMonoRepo } from '@backstage/cli-node'; -import { paths } from '../../lib/paths'; -import { assertError } from '@backstage/errors'; -import { Task } from '../../lib/tasks'; +import { createNewPackage } from '../../lib/new/createNewPackage'; -function parseOptions(optionStrings: string[]): Record { +type ArgOptions = { + option: string[]; + select?: string; + skipInstall: boolean; + private?: boolean; + npmRegistry?: string; + scope?: string; + license?: string; + baseVersion?: string; +}; + +export default async (opts: ArgOptions) => { + const { + option: rawArgOptions, + select: preselectedTemplateId, + skipInstall, + scope, + private: isPrivate, + ...otherGlobals + } = opts; + + const prefilledParams = parseParams(rawArgOptions); + + let pluginInfix: string | undefined = undefined; + let packagePrefix: string | undefined = undefined; + if (scope) { + const backstagePrefix = scope.startsWith('backstage') ? '' : 'backstage-'; + packagePrefix = scope.includes('/') + ? `@${scope}${backstagePrefix}` + : `@${scope}/${backstagePrefix}`; + pluginInfix = scope.includes('backstage') ? 'plugin-' : 'backstage-plugin-'; + } + + if ( + isPrivate === false || // set to false with --no-private flag + Object.values(otherGlobals).filter(Boolean).length !== 0 + ) { + console.warn( + `Global template configuration via CLI flags is deprecated, see https://backstage.io/docs/cli/new for information on how to configure package templating`, + ); + } + + await createNewPackage({ + prefilledParams, + preselectedTemplateId, + configOverrides: { + license: otherGlobals.license, + version: otherGlobals.baseVersion, + private: isPrivate, + publishRegistry: otherGlobals.npmRegistry, + packageNamePrefix: packagePrefix, + packageNamePluginInfix: pluginInfix, + }, + skipInstall, + }); +}; + +function parseParams(optionStrings: string[]): Record { const options: Record = {}; for (const str of optionStrings) { @@ -40,81 +89,3 @@ function parseOptions(optionStrings: string[]): Record { return options; } - -export default async (opts: OptionValues) => { - const factory = await FactoryRegistry.interactiveSelect(opts.select); - - const providedOptions = parseOptions(opts.option); - const options = await FactoryRegistry.populateOptions( - factory, - providedOptions, - ); - - let defaultVersion = '0.1.0'; - if (opts.baseVersion) { - defaultVersion = opts.baseVersion; - } else { - const lernaVersion = await fs - .readJson(paths.resolveTargetRoot('lerna.json')) - .then(pkg => pkg.version) - .catch(() => undefined); - if (lernaVersion) { - defaultVersion = lernaVersion; - } - } - - const tempDirs = new Array(); - async function createTemporaryDirectory(name: string): Promise { - const dir = await fs.mkdtemp(joinPath(os.tmpdir(), name)); - tempDirs.push(dir); - return dir; - } - - const license = opts.license ?? 'Apache-2.0'; - - let modified = false; - try { - await factory.create(options, { - isMonoRepo: await isMonoRepo(), - defaultVersion, - license, - scope: opts.scope?.replace(/^@/, ''), - npmRegistry: opts.npmRegistry, - private: Boolean(opts.private), - createTemporaryDirectory, - markAsModified() { - modified = true; - }, - }); - - Task.log(); - Task.log(`🎉 Successfully created ${factory.name}`); - Task.log(); - } catch (error) { - assertError(error); - Task.error(error.message); - - if (modified) { - Task.log('It seems that something went wrong in the creation process 🤔'); - Task.log(); - Task.log( - 'We have left the changes that were made intact in case you want to', - ); - Task.log( - 'continue manually, but you can also revert the changes and try again.', - ); - - Task.error(`🔥 Failed to create ${factory.name}!`); - } - } finally { - for (const dir of tempDirs) { - try { - await fs.remove(dir); - } catch (error) { - console.error( - `Failed to remove temporary directory '${dir}', ${error}`, - ); - } - } - } -}; diff --git a/packages/cli/src/commands/start/command.ts b/packages/cli/src/commands/start/command.ts index 10dc19cad8..addae78fb1 100644 --- a/packages/cli/src/commands/start/command.ts +++ b/packages/cli/src/commands/start/command.ts @@ -14,15 +14,41 @@ * limitations under the License. */ +import fs from 'fs-extra'; import { OptionValues } from 'commander'; +import { resolve as resolvePath } from 'node:path'; import { PackageRole } from '@backstage/cli-node'; import { findRoleFromCommand } from '../../lib/role'; import { startBackend, startBackendPlugin } from './startBackend'; import { startFrontend } from './startFrontend'; +import { ForwardedError } from '@backstage/errors'; export async function command(opts: OptionValues): Promise { const role = await findRoleFromCommand(opts); + if (opts.link) { + const dir = resolvePath(opts.link); + if (!fs.pathExistsSync(dir)) { + throw new Error( + `Invalid workspace link, directory does not exist: ${dir}`, + ); + } + const pkgJson = await fs + .readJson(resolvePath(dir, 'package.json')) + .catch(error => { + throw new ForwardedError( + 'Failed to read package.json in linked workspace', + error, + ); + }); + + if (!pkgJson.workspaces) { + throw new Error( + `Invalid workspace link, directory is not a workspace: ${dir}`, + ); + } + } + const options = { configPaths: opts.config as string[], checksEnabled: Boolean(opts.check), diff --git a/packages/cli/src/commands/versions/migrate.test.ts b/packages/cli/src/commands/versions/migrate.test.ts index 043e1893a4..919eb18278 100644 --- a/packages/cli/src/commands/versions/migrate.test.ts +++ b/packages/cli/src/commands/versions/migrate.test.ts @@ -21,7 +21,6 @@ import * as run from '../../lib/run'; import migrate from './migrate'; import { withLogCollector } from '@backstage/test-utils'; import fs from 'fs-extra'; -import { expectLogsToMatch } from '../../lib/new/factories/common/testUtils'; // Remove log coloring to simplify log matching jest.mock('chalk', () => ({ @@ -52,6 +51,10 @@ jest.mock('../../lib/run', () => { }; }); +function expectLogsToMatch(recievedLogs: String[], expected: String[]): void { + expect(recievedLogs.filter(Boolean).sort()).toEqual(expected.sort()); +} + describe('versions:migrate', () => { mockDir = createMockDirectory(); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index cc0dbd7c2a..811d032380 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -417,7 +417,7 @@ export async function createConfig( extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json', '.wasm'], mainFields: ['browser', 'module', 'main'], fallback: { - ...pickBy(require('node-libs-browser')), + ...pickBy(require('node-stdlib-browser')), module: false, dgram: false, dns: false, diff --git a/packages/cli/src/lib/new/FactoryRegistry.ts b/packages/cli/src/lib/new/FactoryRegistry.ts deleted file mode 100644 index d16aa0615a..0000000000 --- a/packages/cli/src/lib/new/FactoryRegistry.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import chalk from 'chalk'; -import inquirer, { Answers } from 'inquirer'; -import { AnyFactory, Prompt } from './types'; -import * as factories from './factories'; -import partition from 'lodash/partition'; - -function applyPromptMessageTransforms( - prompt: Prompt, - transforms: { - message: (msg: string) => string; - error: (msg: string) => string; - }, -): Prompt { - return { - ...prompt, - message: - prompt.message && - (async answers => { - if (typeof prompt.message === 'function') { - return transforms.message(await prompt.message(answers)); - } - return transforms.message(await prompt.message!); - }), - validate: - prompt.validate && - (async (...args) => { - const result = await prompt.validate!(...args); - if (typeof result === 'string') { - return transforms.error(result); - } - return result; - }), - }; -} - -export class FactoryRegistry { - private static factoryMap = new Map( - Object.values(factories).map(factory => [factory.name, factory]), - ); - - static async interactiveSelect(preselected?: string): Promise { - let selected = preselected; - - if (!selected) { - const answers = await inquirer.prompt<{ name: string }>([ - { - type: 'list', - name: 'name', - message: 'What do you want to create?', - choices: Array.from(this.factoryMap.values()).map(factory => ({ - name: `${factory.name} - ${factory.description}`, - value: factory.name, - })), - }, - ]); - selected = answers.name; - } - - const factory = this.factoryMap.get(selected); - if (!factory) { - throw new Error(`Unknown selection '${selected}'`); - } - return factory; - } - - static async populateOptions( - factory: AnyFactory, - provided: Record, - ): Promise> { - let currentOptions = provided; - - if (factory.optionsDiscovery) { - const discoveredOptions = await factory.optionsDiscovery(); - currentOptions = { - ...currentOptions, - ...(discoveredOptions as Record), - }; - } - - if (factory.optionsPrompts) { - const [hasAnswers, needsAnswers] = partition( - factory.optionsPrompts, - option => option.name in currentOptions, - ); - - for (const option of hasAnswers) { - const value = provided[option.name]; - - if (option.validate) { - const result = option.validate(value); - if (result !== true) { - throw new Error(`Invalid option '${option.name}'. ${result}`); - } - } - } - - currentOptions = await inquirer.prompt( - needsAnswers.map(option => - applyPromptMessageTransforms(option, { - message: chalk.blue, - error: chalk.red, - }), - ), - currentOptions, - ); - } - - return currentOptions; - } -} diff --git a/packages/cli/src/lib/new/createNewPackage.ts b/packages/cli/src/lib/new/createNewPackage.ts new file mode 100644 index 0000000000..0e3d7e554e --- /dev/null +++ b/packages/cli/src/lib/new/createNewPackage.ts @@ -0,0 +1,56 @@ +/* + * 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 { + collectPortableTemplateInput, + loadPortableTemplate, + loadPortableTemplateConfig, + selectTemplateInteractively, +} from './preparation'; +import { executePortableTemplate } from './execution'; +import { PortableTemplateConfig, PortableTemplateParams } from './types'; + +export type CreateNewPackageOptions = { + preselectedTemplateId?: string; + configOverrides: Partial; + prefilledParams: PortableTemplateParams; + skipInstall?: boolean; +}; + +export async function createNewPackage(options: CreateNewPackageOptions) { + const config = await loadPortableTemplateConfig({ + overrides: options.configOverrides, + }); + + const selectedTemplate = await selectTemplateInteractively( + config, + options.preselectedTemplateId, + ); + const template = await loadPortableTemplate(selectedTemplate); + + const input = await collectPortableTemplateInput({ + config, + template, + prefilledParams: options.prefilledParams, + }); + + await executePortableTemplate({ + config, + template, + input, + skipInstall: options.skipInstall, + }); +} diff --git a/packages/cli/src/lib/new/defaultTemplates.ts b/packages/cli/src/lib/new/defaultTemplates.ts new file mode 100644 index 0000000000..1a2f7a89d4 --- /dev/null +++ b/packages/cli/src/lib/new/defaultTemplates.ts @@ -0,0 +1,27 @@ +/* + * 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 const defaultTemplates = [ + '@backstage/cli/templates/frontend-plugin', + '@backstage/cli/templates/backend-plugin', + '@backstage/cli/templates/backend-plugin-module', + '@backstage/cli/templates/plugin-web-library', + '@backstage/cli/templates/plugin-node-library', + '@backstage/cli/templates/plugin-common-library', + '@backstage/cli/templates/web-library', + '@backstage/cli/templates/node-library', + '@backstage/cli/templates/scaffolder-backend-module', +]; diff --git a/packages/cli/src/lib/new/execution/PortableTemplater.ts b/packages/cli/src/lib/new/execution/PortableTemplater.ts new file mode 100644 index 0000000000..8e5e9251d4 --- /dev/null +++ b/packages/cli/src/lib/new/execution/PortableTemplater.ts @@ -0,0 +1,107 @@ +/* + * 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 handlebars from 'handlebars'; +import { PortableTemplateParams } from '../types'; +import camelCase from 'lodash/camelCase'; +import kebabCase from 'lodash/kebabCase'; +import lowerCase from 'lodash/lowerCase'; +import snakeCase from 'lodash/snakeCase'; +import startCase from 'lodash/startCase'; +import upperCase from 'lodash/upperCase'; +import upperFirst from 'lodash/upperFirst'; +import lowerFirst from 'lodash/lowerFirst'; +import { Lockfile } from '../../versioning'; +import { paths } from '../../paths'; +import { createPackageVersionProvider } from '../../version'; + +const builtInHelpers = { + camelCase, + kebabCase, + lowerCase, + snakeCase, + startCase, + upperCase, + upperFirst, + lowerFirst, +}; + +type CreatePortableTemplaterOptions = { + values?: PortableTemplateParams; + templatedValues?: Record; +}; + +export class PortableTemplater { + static async create(options: CreatePortableTemplaterOptions = {}) { + let lockfile: Lockfile | undefined; + try { + lockfile = await Lockfile.load(paths.resolveTargetRoot('yarn.lock')); + } catch { + /* ignored */ + } + + const versionProvider = createPackageVersionProvider(lockfile); + + const templater = new PortableTemplater( + { + versionQuery(name: string, versionHint: string | unknown) { + return versionProvider( + name, + typeof versionHint === 'string' ? versionHint : undefined, + ); + }, + }, + options.values ?? {}, + ); + + if (options.templatedValues) { + templater.appendTemplatedValues(options.templatedValues); + } + + return templater; + } + + readonly #templater: typeof handlebars; + #values: PortableTemplateParams; + + private constructor( + helpers: handlebars.HelperDeclareSpec, + values: PortableTemplateParams, + ) { + this.#templater = handlebars.create(); + + this.#templater.registerHelper(builtInHelpers); + + if (helpers) { + this.#templater.registerHelper(helpers); + } + + this.#values = values; + } + + template(content: string): string { + return this.#templater.compile(content, { + strict: true, + })(this.#values); + } + + appendTemplatedValues(record: Record): void { + const newValues = Object.fromEntries( + Object.entries(record).map(([key, value]) => [key, this.template(value)]), + ); + this.#values = { ...this.#values, ...newValues }; + } +} diff --git a/packages/cli/src/lib/new/execution/executePortableTemplate.ts b/packages/cli/src/lib/new/execution/executePortableTemplate.ts new file mode 100644 index 0000000000..0041b931ba --- /dev/null +++ b/packages/cli/src/lib/new/execution/executePortableTemplate.ts @@ -0,0 +1,87 @@ +/* + * 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 { assertError } from '@backstage/errors'; +import { addCodeownersEntry } from '../../codeowners'; +import { Task } from '../../tasks'; +import { + PortableTemplate, + PortableTemplateConfig, + PortableTemplateInput, +} from '../types'; +import { installNewPackage } from './installNewPackage'; +import { writeTemplateContents } from './writeTemplateContents'; + +type ExecuteNewTemplateOptions = { + config: PortableTemplateConfig; + template: PortableTemplate; + input: PortableTemplateInput; + skipInstall?: boolean; +}; + +export async function executePortableTemplate( + options: ExecuteNewTemplateOptions, +) { + const { template, input } = options; + + let modified = false; + try { + const { targetDir } = await Task.forItem( + 'templating', + input.packagePath, + () => writeTemplateContents(template, input), + ); + + modified = true; + + await installNewPackage(input); + + if (input.owner) { + await addCodeownersEntry(targetDir, input.owner); + } + + if (!options.skipInstall) { + await Task.forCommand('yarn install', { + cwd: targetDir, + optional: true, + }); + await Task.forCommand('yarn lint --fix', { + cwd: targetDir, + optional: true, + }); + } + + Task.log(); + Task.log(`🎉 Successfully created ${template.name}`); + Task.log(); + } catch (error) { + assertError(error); + Task.error(error.message); + + if (modified) { + Task.log('It seems that something went wrong in the creation process 🤔'); + Task.log(); + Task.log( + 'We have left the changes that were made intact in case you want to', + ); + Task.log( + 'continue manually, but you can also revert the changes and try again.', + ); + + Task.error(`🔥 Failed to create ${template.name}!`); + } + } +} diff --git a/packages/cli/src/lib/new/factories/index.ts b/packages/cli/src/lib/new/execution/index.ts similarity index 53% rename from packages/cli/src/lib/new/factories/index.ts rename to packages/cli/src/lib/new/execution/index.ts index 44d687eda2..3b8ed3c090 100644 --- a/packages/cli/src/lib/new/factories/index.ts +++ b/packages/cli/src/lib/new/execution/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Backstage Authors + * 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. @@ -14,12 +14,4 @@ * limitations under the License. */ -export { frontendPlugin } from './frontendPlugin'; -export { backendPlugin } from './backendPlugin'; -export { backendModule } from './backendModule'; -export { nodeLibraryPackage } from './nodeLibraryPackage'; -export { webLibraryPackage } from './webLibraryPackage'; -export { pluginCommon } from './pluginCommon'; -export { pluginNode } from './pluginNode'; -export { pluginWeb } from './pluginWeb'; -export { scaffolderModule } from './scaffolderModule'; +export { executePortableTemplate } from './executePortableTemplate'; diff --git a/packages/cli/src/lib/new/execution/installNewPackage.ts b/packages/cli/src/lib/new/execution/installNewPackage.ts new file mode 100644 index 0000000000..6ed60541c1 --- /dev/null +++ b/packages/cli/src/lib/new/execution/installNewPackage.ts @@ -0,0 +1,147 @@ +/* + * 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 fs from 'fs-extra'; +import upperFirst from 'lodash/upperFirst'; +import camelCase from 'lodash/camelCase'; +import { paths } from '../../paths'; +import { Task } from '../../tasks'; +import { PortableTemplateInput } from '../types'; + +export async function installNewPackage(input: PortableTemplateInput) { + switch (input.roleParams.role) { + case 'web-library': + case 'node-library': + case 'common-library': + case 'plugin-web-library': + case 'plugin-node-library': + case 'plugin-common-library': + return; // No installation action needed for library packages + case 'frontend-plugin': + await addDependency(input, 'package/app/package.json'); + await tryAddFrontendLegacy(input); + return; + case 'frontend-plugin-module': + await addDependency(input, 'package/app/package.json'); + return; + case 'backend-plugin': + await addDependency(input, 'package/backend/package.json'); + await tryAddBackend(input); + return; + case 'backend-plugin-module': + await addDependency(input, 'package/backend/package.json'); + await tryAddBackend(input); + return; + default: + throw new Error( + `Unsupported role ${(input.roleParams as { role: string }).role}`, + ); + } +} + +async function addDependency(input: PortableTemplateInput, path: string) { + const pkgJsonPath = paths.resolveTargetRoot(path); + + const pkgJson = await fs.readJson(pkgJsonPath).catch(error => { + if (error.code === 'ENOENT') { + return undefined; + } + throw error; + }); + if (!pkgJson) { + return; + } + + try { + pkgJson.dependencies = { + ...pkgJson.dependencies, + [input.packageName]: `workspace:^`, + }; + + await fs.writeJson(path, pkgJson, { spaces: 2 }); + } catch (error) { + throw new Error(`Failed to add package dependencies, ${error}`); + } +} + +async function tryAddFrontendLegacy(input: PortableTemplateInput) { + const { roleParams } = input; + if (roleParams.role !== 'frontend-plugin') { + throw new Error( + 'add-frontend-legacy can only be used for frontend plugins', + ); + } + + const appDefinitionPath = paths.resolveTargetRoot('packages/app/src/App.tsx'); + if (!(await fs.pathExists(appDefinitionPath))) { + return; + } + + await Task.forItem('app', 'adding import', async () => { + const content = await fs.readFile(appDefinitionPath, 'utf8'); + const revLines = content.split('\n').reverse(); + + const lastImportIndex = revLines.findIndex(line => + line.match(/ from ("|').*("|')/), + ); + const lastRouteIndex = revLines.findIndex(line => + line.match(/<\/FlatRoutes/), + ); + + if (lastImportIndex !== -1 && lastRouteIndex !== -1) { + const extensionName = upperFirst(`${camelCase(roleParams.pluginId)}Page`); + const importLine = `import { ${extensionName} } from '${input.packageName}';`; + if (!content.includes(importLine)) { + revLines.splice(lastImportIndex, 0, importLine); + } + + const componentLine = `} />`; + if (!content.includes(componentLine)) { + const [indentation] = revLines[lastRouteIndex + 1].match(/^\s*/) ?? []; + revLines.splice(lastRouteIndex + 1, 0, indentation + componentLine); + } + + const newContent = revLines.reverse().join('\n'); + await fs.writeFile(appDefinitionPath, newContent, 'utf8'); + } + }); +} + +async function tryAddBackend(input: PortableTemplateInput) { + const backendIndexPath = paths.resolveTargetRoot( + 'packages/backend/src/index.ts', + ); + if (!(await fs.pathExists(backendIndexPath))) { + return; + } + + await Task.forItem('backend', `adding ${input.packageName}`, async () => { + const content = await fs.readFile(backendIndexPath, 'utf8'); + const lines = content.split('\n'); + const backendAddLine = `backend.add(import('${input.packageName}'));`; + + const backendStartIndex = lines.findIndex(line => + line.match(/backend.start/), + ); + + if (backendStartIndex !== -1) { + const [indentation] = lines[backendStartIndex].match(/^\s*/)!; + lines.splice(backendStartIndex, 0, `${indentation}${backendAddLine}`); + + const newContent = lines.join('\n'); + await fs.writeFile(backendIndexPath, newContent, 'utf8'); + } + }); +} diff --git a/packages/cli/src/lib/new/execution/writeTemplateContents.test.ts b/packages/cli/src/lib/new/execution/writeTemplateContents.test.ts new file mode 100644 index 0000000000..7e0e81f137 --- /dev/null +++ b/packages/cli/src/lib/new/execution/writeTemplateContents.test.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { relative as relativePath } from 'node:path'; +import { writeTemplateContents } from './writeTemplateContents'; +import { createMockDirectory } from '@backstage/backend-test-utils'; +import { paths } from '../../paths'; + +const baseConfig = { + version: '0.1.0', + license: 'Apache-2.0', + private: true, +}; + +describe('writeTemplateContents', () => { + const mockDir = createMockDirectory(); + + beforeEach(() => { + mockDir.clear(); + jest.resetAllMocks(); + jest + .spyOn(paths, 'resolveTargetRoot') + .mockImplementation((...args) => mockDir.resolve(...args)); + }); + + it('should write an empty template', async () => { + const { targetDir } = await writeTemplateContents( + { + name: 'test', + files: [], + role: 'frontend-plugin', + values: {}, + }, + { + ...baseConfig, + roleParams: { role: 'frontend-plugin', pluginId: 'test' }, + packageName: '@internal/plugin-test', + packagePath: 'plugins/plugin-test', + }, + ); + + expect(relativePath(mockDir.path, targetDir)).toBe('plugins/plugin-test'); + expect(mockDir.content()).toEqual({}); + }); + + it('should write template with various files', async () => { + await writeTemplateContents( + { + name: 'test', + files: [ + { + content: 'test', + path: 'test.txt', + }, + { + content: 'id={{ pluginId}}', + path: 'plugin.txt', + syntax: 'handlebars', + }, + { + content: '{"x":1}', + path: 'test.json', + }, + ], + role: 'frontend-plugin', + values: {}, + }, + { + ...baseConfig, + roleParams: { role: 'frontend-plugin', pluginId: 'test' }, + packageName: '@internal/plugin-test', + packagePath: 'out', + }, + ); + + expect(mockDir.content()).toEqual({ + out: { + 'test.txt': 'test', + 'plugin.txt': 'id=test', + 'test.json': '{"x":1}', + }, + }); + }); +}); diff --git a/packages/cli/src/lib/new/execution/writeTemplateContents.ts b/packages/cli/src/lib/new/execution/writeTemplateContents.ts new file mode 100644 index 0000000000..f54b2d73d6 --- /dev/null +++ b/packages/cli/src/lib/new/execution/writeTemplateContents.ts @@ -0,0 +1,147 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import { dirname, resolve as resolvePath } from 'path'; + +import { paths } from '../../paths'; +import { PortableTemplate, PortableTemplateInput } from '../types'; +import { ForwardedError, InputError } from '@backstage/errors'; +import { isMonoRepo as getIsMonoRepo } from '@backstage/cli-node'; +import { PortableTemplater } from './PortableTemplater'; + +export async function writeTemplateContents( + template: PortableTemplate, + input: PortableTemplateInput, +): Promise<{ targetDir: string }> { + const targetDir = paths.resolveTargetRoot(input.packagePath); + + if (await fs.pathExists(targetDir)) { + throw new InputError(`Package '${input.packagePath}' already exists`); + } + + try { + const isMonoRepo = await getIsMonoRepo(); + + const { role, ...roleValues } = input.roleParams; + + const templater = await PortableTemplater.create({ + values: { + ...roleValues, + packageName: input.packageName, + }, + templatedValues: template.values, + }); + + if (!isMonoRepo) { + await fs.writeJson( + resolvePath(targetDir, 'tsconfig.json'), + { + extends: '@backstage/cli/config/tsconfig.json', + include: ['src', 'dev', 'migrations'], + exclude: ['node_modules'], + compilerOptions: { + outDir: 'dist-types', + rootDir: '.', + }, + }, + { spaces: 2 }, + ); + } + + for (const file of template.files) { + const destPath = resolvePath(targetDir, file.path); + await fs.ensureDir(dirname(destPath)); + + let content = + file.syntax === 'handlebars' + ? templater.template(file.content) + : file.content; + + // Automatically inject input values into package.json + if (file.path === 'package.json') { + try { + content = injectPackageJsonInput(input, content); + } catch (error) { + throw new ForwardedError( + 'Failed to transform templated package.json', + error, + ); + } + } + + await fs.writeFile(destPath, content).catch(error => { + throw new ForwardedError(`Failed to copy file to ${destPath}`, error); + }); + } + + return { targetDir }; + } catch (error) { + await fs.rm(targetDir, { recursive: true, force: true, maxRetries: 10 }); + throw error; + } +} + +export function injectPackageJsonInput( + input: PortableTemplateInput, + content: string, +) { + const pkgJson = JSON.parse(content); + + const toAdd = new Array<[name: string, value: unknown]>(); + + if (pkgJson.version) { + pkgJson.version = input.version; + } else { + toAdd.push(['version', input.version]); + } + if (pkgJson.license) { + pkgJson.license = input.license; + } else { + toAdd.push(['license', input.license]); + } + if (input.private) { + if (pkgJson.private === false) { + pkgJson.private = true; + } else if (!pkgJson.private) { + toAdd.push(['private', true]); + } + } else { + delete pkgJson.private; + } + + if (input.publishRegistry) { + if (pkgJson.publishConfig) { + pkgJson.publishConfig = { + ...pkgJson.publishConfig, + registry: input.publishRegistry, + }; + } else { + toAdd.push(['publishConfig', { registry: input.publishRegistry }]); + } + } + + const entries = Object.entries(pkgJson); + + const nameIndex = entries.findIndex(([name]) => name === 'name'); + if (nameIndex === -1) { + throw new Error('templated package.json does not contain a name field'); + } + + entries.splice(nameIndex + 1, 0, ...toAdd); + + return JSON.stringify(Object.fromEntries(entries), null, 2); +} diff --git a/packages/cli/src/lib/new/factories/backendModule.test.ts b/packages/cli/src/lib/new/factories/backendModule.test.ts deleted file mode 100644 index 36ed9655a1..0000000000 --- a/packages/cli/src/lib/new/factories/backendModule.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { backendModule } from './backendModule'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -const backendIndexTsContent = ` -import { createBackend } from '@backstage/backend-defaults'; - -const backend = createBackend(); - -backend.start(); -`; - -describe('backendModule factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a backend plugin', async () => { - mockDir.setContent({ - packages: { - backend: { - 'package.json': JSON.stringify({}), - src: { - 'index.ts': backendIndexTsContent, - }, - }, - }, - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(backendModule, { - id: 'test', - moduleId: 'tester-two', - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await backendModule.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - 'Creating backend module backstage-plugin-test-backend-module-tester-two', - 'Checking Prerequisites:', - `availability plugins${sep}test-backend-module-tester-two`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.ts.hbs', - 'templating module.ts.hbs', - 'Installing:', - `moving plugins${sep}test-backend-module-tester-two`, - 'backend adding dependency', - 'backend adding module', - ]); - - await expect( - fs.readFile(mockDir.resolve('packages/backend/src/index.ts'), 'utf8'), - ).resolves.toBe(` -import { createBackend } from '@backstage/backend-defaults'; - -const backend = createBackend(); - -backend.add(import('backstage-plugin-test-backend-module-tester-two')); -backend.start(); -`); - - await expect( - fs.readJson(mockDir.resolve('packages/backend/package.json')), - ).resolves.toEqual({ - dependencies: { - 'backstage-plugin-test-backend-module-tester-two': '^1.0.0', - }, - }); - const moduleFile = await fs.readFile( - mockDir.resolve('plugins/test-backend-module-tester-two/src/module.ts'), - 'utf-8', - ); - - expect(moduleFile).toContain( - `const testModuleTesterTwo = createBackendModule({`, - ); - expect(moduleFile).toContain(`pluginId: 'test',`); - expect(moduleFile).toContain(`moduleId: 'tester-two',`); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/test-backend-module-tester-two'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/test-backend-module-tester-two'), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/backendModule.ts b/packages/cli/src/lib/new/factories/backendModule.ts deleted file mode 100644 index de759538b1..0000000000 --- a/packages/cli/src/lib/new/factories/backendModule.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import chalk from 'chalk'; -import camelCase from 'lodash/camelCase'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { addPackageDependency, addToBackend, Task } from '../../tasks'; -import { - moduleIdIdPrompt, - ownerPrompt, - pluginIdPrompt, -} from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - moduleId: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const backendModule = createFactory({ - name: 'backend-module', - description: - 'A new backend module that extends an existing backend plugin with additional features', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), moduleIdIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id: pluginId, moduleId } = options; - const dirName = `${pluginId}-backend-module-${moduleId}`; - const name = resolvePackageName({ - baseName: dirName, - scope: ctx.scope, - plugin: true, - }); - - Task.log(); - Task.log(`Creating backend module ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('plugins', dirName) - : paths.resolveTargetRoot(`backstage-plugin-${dirName}`); - - const moduleCamelCase = camelCase(moduleId); - const modulePascalCase = - moduleCamelCase[0].toUpperCase() + moduleCamelCase.slice(1); - const moduleVar = `${camelCase(pluginId)}Module${modulePascalCase}`; - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'default-backend-module', - values: { - pluginId, - moduleId, - name, - moduleVar, - packageVersion: ctx.defaultVersion, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - license: ctx.license, - }, - }); - - if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) { - await Task.forItem('backend', 'adding dependency', async () => { - await addPackageDependency( - paths.resolveTargetRoot('packages/backend/package.json'), - { - dependencies: { - [name]: `^${ctx.defaultVersion}`, - }, - }, - ); - }); - } - - await addToBackend(name, { - type: 'module', - }); - - if (options.owner) { - await addCodeownersEntry(`/plugins/${dirName}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/backendPlugin.test.ts b/packages/cli/src/lib/new/factories/backendPlugin.test.ts deleted file mode 100644 index 5415a3cd71..0000000000 --- a/packages/cli/src/lib/new/factories/backendPlugin.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { backendPlugin } from './backendPlugin'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -const backendIndexTsContent = ` -import { createBackend } from '@backstage/backend-defaults'; - -const backend = createBackend(); - -backend.start(); -`; - -describe('backendPlugin factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a backend plugin', async () => { - mockDir.setContent({ - packages: { - backend: { - 'package.json': JSON.stringify({}), - src: { - 'index.ts': backendIndexTsContent, - }, - }, - }, - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(backendPlugin, { - id: 'test', - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await backendPlugin.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - 'Creating backend plugin backstage-plugin-test-backend', - 'Checking Prerequisites:', - `availability plugins${sep}test-backend`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating index.ts.hbs', - 'templating index.ts.hbs', - 'templating package.json.hbs', - 'templating plugin.ts.hbs', - 'templating plugin.test.ts.hbs', - 'copying index.ts', - 'copying setupTests.ts', - 'copying router.ts', - 'copying router.test.ts', - 'copying createTodoListService.ts', - 'copying types.ts', - 'Installing:', - `moving plugins${sep}test-backend`, - 'backend adding dependency', - 'backend adding plugin', - ]); - - await expect( - fs.readJson(mockDir.resolve('packages/backend/package.json')), - ).resolves.toEqual({ - dependencies: { - 'backstage-plugin-test-backend': '^1.0.0', - }, - }); - - await expect( - fs.readFile(mockDir.resolve('packages/backend/src/index.ts'), 'utf8'), - ).resolves.toBe(` -import { createBackend } from '@backstage/backend-defaults'; - -const backend = createBackend(); - -backend.add(import('backstage-plugin-test-backend')); -backend.start(); -`); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/test-backend'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/test-backend'), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/backendPlugin.ts b/packages/cli/src/lib/new/factories/backendPlugin.ts deleted file mode 100644 index 050d05f0df..0000000000 --- a/packages/cli/src/lib/new/factories/backendPlugin.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import chalk from 'chalk'; -import camelCase from 'lodash/camelCase'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { addPackageDependency, addToBackend, Task } from '../../tasks'; -import { ownerPrompt, pluginIdPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const backendPlugin = createFactory({ - name: 'backend-plugin', - description: 'A new backend plugin', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - const pluginId = `${id}-backend`; - const name = resolvePackageName({ - baseName: pluginId, - scope: ctx.scope, - plugin: true, - }); - - Task.log(); - Task.log(`Creating backend plugin ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('plugins', pluginId) - : paths.resolveTargetRoot(`backstage-plugin-${pluginId}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'default-backend-plugin', - values: { - id, - name, - pluginVar: `${camelCase(id)}Plugin`, - pluginVersion: ctx.defaultVersion, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - license: ctx.license, - }, - }); - - if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) { - await Task.forItem('backend', 'adding dependency', async () => { - await addPackageDependency( - paths.resolveTargetRoot('packages/backend/package.json'), - { - dependencies: { - [name]: `^${ctx.defaultVersion}`, - }, - }, - ); - }); - } - - await addToBackend(name, { - type: 'plugin', - }); - - if (options.owner) { - await addCodeownersEntry(`/plugins/${id}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/common/prompts.ts b/packages/cli/src/lib/new/factories/common/prompts.ts deleted file mode 100644 index 428ba8f9d3..0000000000 --- a/packages/cli/src/lib/new/factories/common/prompts.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Prompt } from '../../types'; -import { parseOwnerIds } from '../../../codeowners'; - -export function pluginIdPrompt(): Prompt<{ id: string }> { - return { - type: 'input', - name: 'id', - message: 'Enter the ID of the plugin [required]', - validate: (value: string) => { - if (!value) { - return 'Please enter the ID of the plugin'; - } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) { - return 'Plugin IDs must be lowercase and contain only letters, digits, and dashes.'; - } - return true; - }, - }; -} - -export function moduleIdIdPrompt(): Prompt<{ moduleId: string }> { - return { - type: 'input', - name: 'moduleId', - message: 'Enter the ID of the module [required]', - validate: (value: string) => { - if (!value) { - return 'Please enter the ID of the module'; - } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) { - return 'Module IDs must be lowercase and contain only letters, digits, and dashes.'; - } - return true; - }, - }; -} - -export function ownerPrompt(): Prompt<{ - owner?: string; - codeOwnersPath?: string; -}> { - return { - type: 'input', - name: 'owner', - message: 'Enter an owner to add to CODEOWNERS [optional]', - when: opts => Boolean(opts.codeOwnersPath), - validate: (value: string) => { - if (!value) { - return true; - } - - const ownerIds = parseOwnerIds(value); - if (!ownerIds) { - return 'The owner must be a space separated list of team names (e.g. @org/team-name), usernames (e.g. @username), or the email addresses (e.g. user@example.com).'; - } - - return true; - }, - }; -} diff --git a/packages/cli/src/lib/new/factories/common/tasks.test.ts b/packages/cli/src/lib/new/factories/common/tasks.test.ts deleted file mode 100644 index 60783344fc..0000000000 --- a/packages/cli/src/lib/new/factories/common/tasks.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './testUtils'; -import { CreateContext } from '../../types'; -import { executePluginPackageTemplate } from './tasks'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -const mockDir = createMockDirectory(); - -mockPaths({ - ownDir: mockDir.resolve('own'), - targetRoot: mockDir.resolve('root'), -}); - -describe('executePluginPackageTemplate', () => { - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should execute template', async () => { - mockDir.setContent({ - root: { - 'yarn.lock': ` -some-package@^1.1.0: - version "1.5.0" -`, - }, - own: { - templates: { - 'test-template': { - 'package.json.hbs': ` -{ - "name": "my-{{id}}-plugin", - {{#if makePrivate}} - "private": true, - {{/if}} - "description": "testing", - "dependencies": { - "some-package": "{{ versionQuery 'some-package' '1.3.0' }}", - "other-package": "{{ versionQuery 'other-package' '2.3.0' }}" - } -} -`, - subdir: { - 'templated.txt.hbs': 'Hello {{id}}!', - 'not-templated.txt': 'Hello {{id}}!', - }, - }, - }, - }, - }); - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - - let modified = false; - await executePluginPackageTemplate( - { - createTemporaryDirectory: (name: string) => fs.mkdtemp(name), - markAsModified: () => { - modified = true; - }, - } as CreateContext, - { - templateName: 'test-template', - targetDir: mockDir.resolve('target'), - values: { - id: 'testing', - makePrivate: true, - }, - }, - ); - - expect(modified).toBe(true); - expectLogsToMatch(output, [ - 'Checking Prerequisites:', - `availability ..${sep}target`, - 'creating temp dir', - 'Executing Template:', - 'templating package.json.hbs', - 'copying not-templated.txt', - 'templating templated.txt.hbs', - 'Installing:', - `moving ..${sep}target`, - ]); - await expect(fs.readFile(mockDir.resolve('target/package.json'), 'utf8')) - .resolves.toBe(`{ - "name": "my-testing-plugin", - "private": true, - "description": "testing", - "dependencies": { - "some-package": "^1.1.0", - "other-package": "^2.3.0" - } -} -`); - await expect( - fs.readFile(mockDir.resolve('target/subdir/templated.txt'), 'utf8'), - ).resolves.toBe('Hello testing!'); - await expect( - fs.readFile(mockDir.resolve('target/subdir/not-templated.txt'), 'utf8'), - ).resolves.toBe('Hello {{id}}!'); - }); -}); diff --git a/packages/cli/src/lib/new/factories/common/tasks.ts b/packages/cli/src/lib/new/factories/common/tasks.ts deleted file mode 100644 index 99ffabfb24..0000000000 --- a/packages/cli/src/lib/new/factories/common/tasks.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import chalk from 'chalk'; -import { resolve as resolvePath, relative as relativePath } from 'path'; -import { paths } from '../../../paths'; -import { Task, templatingTask } from '../../../tasks'; -import { Lockfile } from '../../../versioning'; -import { createPackageVersionProvider } from '../../../version'; -import { CreateContext } from '../../types'; - -export async function executePluginPackageTemplate( - ctx: CreateContext, - options: { - templateName: string; - targetDir: string; - values: Record; - }, -) { - const { targetDir } = options; - - let lockfile: Lockfile | undefined; - try { - lockfile = await Lockfile.load(paths.resolveTargetRoot('yarn.lock')); - } catch { - /* ignored */ - } - - Task.section('Checking Prerequisites'); - const shortPluginDir = relativePath(paths.targetRoot, targetDir); - await Task.forItem('availability', shortPluginDir, async () => { - if (await fs.pathExists(targetDir)) { - throw new Error( - `A package with the same plugin ID already exists at ${chalk.cyan( - shortPluginDir, - )}. Please try again with a different ID.`, - ); - } - }); - - const tempDir = await Task.forItem('creating', 'temp dir', async () => { - return await ctx.createTemporaryDirectory('backstage-create'); - }); - - Task.section('Executing Template'); - await templatingTask( - paths.resolveOwn('templates', options.templateName), - tempDir, - options.values, - createPackageVersionProvider(lockfile), - ctx.isMonoRepo, - ); - - // Format package.json if it exists - const pkgJsonPath = resolvePath(tempDir, 'package.json'); - if (await fs.pathExists(pkgJsonPath)) { - const pkgJson = await fs.readJson(pkgJsonPath); - await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 }); - } - - Task.section('Installing'); - await Task.forItem('moving', shortPluginDir, async () => { - await fs.move(tempDir, targetDir).catch(error => { - throw new Error( - `Failed to move package from ${tempDir} to ${targetDir}, ${error.message}`, - ); - }); - }); - - ctx.markAsModified(); -} diff --git a/packages/cli/src/lib/new/factories/common/testUtils.ts b/packages/cli/src/lib/new/factories/common/testUtils.ts deleted file mode 100644 index 1ade13a996..0000000000 --- a/packages/cli/src/lib/new/factories/common/testUtils.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* eslint-disable no-control-regex */ - -import { WriteStream } from 'tty'; -import { resolve as resolvePath } from 'path'; -import { paths } from '../../../paths'; - -export function mockPaths(options: { - ownDir?: string; - ownRoot?: string; - targetDir?: string; - targetRoot?: string; -}): void { - const { ownDir, ownRoot, targetDir, targetRoot } = options; - if (ownDir) { - paths.ownDir = ownDir; - jest - .spyOn(paths, 'resolveOwn') - .mockImplementation((...ps) => resolvePath(ownDir, ...ps)); - } - if (ownRoot) { - jest.spyOn(paths, 'ownRoot', 'get').mockReturnValue(ownRoot); - jest - .spyOn(paths, 'resolveOwnRoot') - .mockImplementation((...ps) => resolvePath(ownRoot, ...ps)); - } - if (targetDir) { - paths.targetDir = targetDir; - jest - .spyOn(paths, 'resolveTarget') - .mockImplementation((...ps) => resolvePath(targetDir, ...ps)); - } - if (targetRoot) { - jest.spyOn(paths, 'targetRoot', 'get').mockReturnValue(targetRoot); - jest - .spyOn(paths, 'resolveTargetRoot') - .mockImplementation((...ps) => resolvePath(targetRoot, ...ps)); - } -} - -export function createMockOutputStream() { - const output = new Array(); - return [ - output, - { - cursorTo: () => {}, - clearLine: () => {}, - moveCursor: () => {}, - write: (msg: string) => { - let clean = msg; - // Remove terminal color escape sequences - clean = clean.replace(/\x1B\[\d\dm/g, ''); - // Remove any non-ascii - clean = clean.replace(/[^\x00-\x7F]+/g, ''); - clean = clean.trim(); - output.push(clean); - }, - } as unknown as WriteStream & { fd: any }, - ] as const; -} - -// Avoid flakes by comparing sorted log lines. File system access is async, which leads to the log line order being indeterministic -export function expectLogsToMatch( - recievedLogs: String[], - expected: String[], -): void { - expect(recievedLogs.filter(Boolean).sort()).toEqual(expected.sort()); -} diff --git a/packages/cli/src/lib/new/factories/common/util.test.ts b/packages/cli/src/lib/new/factories/common/util.test.ts deleted file mode 100644 index 6f168d8035..0000000000 --- a/packages/cli/src/lib/new/factories/common/util.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 { resolvePackageName } from './util'; - -describe('resolvePackageName', () => { - it('should generate correct name without scope', () => { - expect(resolvePackageName({ baseName: 'test', plugin: true })).toEqual( - 'backstage-plugin-test', - ); - expect(resolvePackageName({ baseName: 'test', plugin: false })).toEqual( - 'test', - ); - }); - - it('should generate correct name for backstage scope', () => { - expect( - resolvePackageName({ - baseName: 'test', - scope: 'backstage', - plugin: true, - }), - ).toEqual('@backstage/plugin-test'); - expect( - resolvePackageName({ - baseName: 'test', - scope: 'backstage', - plugin: false, - }), - ).toEqual('@backstage/test'); - }); - - it('should generate correct name for custom scope', () => { - expect( - resolvePackageName({ - baseName: 'test', - scope: 'custom', - plugin: true, - }), - ).toEqual('@custom/backstage-plugin-test'); - expect( - resolvePackageName({ - baseName: 'test', - scope: 'custom', - plugin: false, - }), - ).toEqual('@custom/test'); - }); - - it('should generate correct name for custom scope and custom prefix', () => { - expect( - resolvePackageName({ - baseName: 'test', - scope: 'custom/myapp.', - plugin: true, - }), - ).toEqual('@custom/myapp.backstage-plugin-test'); - expect( - resolvePackageName({ - baseName: 'test', - scope: 'custom/myapp.', - plugin: false, - }), - ).toEqual('@custom/myapp.test'); - }); -}); diff --git a/packages/cli/src/lib/new/factories/common/util.ts b/packages/cli/src/lib/new/factories/common/util.ts deleted file mode 100644 index d9c6970afd..0000000000 --- a/packages/cli/src/lib/new/factories/common/util.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const resolvePackageName = (options: { - baseName: string; - scope?: string; - plugin: boolean; -}) => { - const { baseName, scope, plugin } = options; - if (scope) { - if (plugin) { - const pluginName = scope.startsWith('backstage') - ? 'plugin' - : 'backstage-plugin'; - return scope.includes('/') - ? `@${scope}${pluginName}-${baseName}` - : `@${scope}/${pluginName}-${baseName}`; - } - return scope.includes('/') - ? `@${scope}${baseName}` - : `@${scope}/${baseName}`; - } - - return plugin ? `backstage-plugin-${baseName}` : baseName; -}; diff --git a/packages/cli/src/lib/new/factories/frontendPlugin.test.ts b/packages/cli/src/lib/new/factories/frontendPlugin.test.ts deleted file mode 100644 index a4a89c3245..0000000000 --- a/packages/cli/src/lib/new/factories/frontendPlugin.test.ts +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { frontendPlugin } from './frontendPlugin'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -const appTsxContent = ` -import { createApp } from '@backstage/app-defaults'; - -const router = ( - - } /> - -) -`; - -describe('frontendPlugin factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a frontend plugin', async () => { - mockDir.setContent({ - packages: { - app: { - 'package.json': JSON.stringify({}), - src: { - 'App.tsx': appTsxContent, - }, - }, - }, - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(frontendPlugin, { - id: 'test', - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await frontendPlugin.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - 'Creating frontend plugin backstage-plugin-test', - 'Checking Prerequisites:', - `availability plugins${sep}test`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.tsx.hbs', - 'templating index.ts.hbs', - 'templating plugin.test.ts.hbs', - 'templating plugin.ts.hbs', - 'templating routes.ts.hbs', - 'copying setupTests.ts', - 'templating ExampleComponent.test.tsx.hbs', - 'templating ExampleComponent.tsx.hbs', - 'copying index.ts', - 'templating ExampleFetchComponent.test.tsx.hbs', - 'templating ExampleFetchComponent.tsx.hbs', - 'copying index.ts', - 'Installing:', - `moving plugins${sep}test`, - 'app adding dependency', - 'app adding import', - ]); - - await expect( - fs.readJson(mockDir.resolve('packages/app/package.json')), - ).resolves.toEqual({ - dependencies: { - 'backstage-plugin-test': '^1.0.0', - }, - }); - - await expect( - fs.readFile(mockDir.resolve('packages/app/src/App.tsx'), 'utf8'), - ).resolves.toBe(` -import { createApp } from '@backstage/app-defaults'; -import { TestPage } from 'backstage-plugin-test'; - -const router = ( - - } /> - } /> - -) -`); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/test'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/test'), - optional: true, - }); - }); - - it('should create a frontend plugin with more options and codeowners', async () => { - mockDir.setContent({ - CODEOWNERS: '', - packages: { - app: { - 'package.json': JSON.stringify({}), - src: { - 'App.tsx': appTsxContent, - }, - }, - }, - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(frontendPlugin, { - id: 'test', - owner: '@test-user', - }); - - const [, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await frontendPlugin.create(options, { - scope: 'internal', - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => {}, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - await expect( - fs.readJson(mockDir.resolve('packages/app/package.json')), - ).resolves.toEqual({ - dependencies: { - '@internal/backstage-plugin-test': '^1.0.0', - }, - }); - - await expect( - fs.readFile(mockDir.resolve('packages/app/src/App.tsx'), 'utf8'), - ).resolves.toBe(` -import { createApp } from '@backstage/app-defaults'; -import { TestPage } from '@internal/backstage-plugin-test'; - -const router = ( - - } /> - } /> - -) -`); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/test'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/test'), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/frontendPlugin.ts b/packages/cli/src/lib/new/factories/frontendPlugin.ts deleted file mode 100644 index 41907d9d3a..0000000000 --- a/packages/cli/src/lib/new/factories/frontendPlugin.ts +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import chalk from 'chalk'; -import camelCase from 'lodash/camelCase'; -import upperFirst from 'lodash/upperFirst'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { addPackageDependency, Task } from '../../tasks'; -import { ownerPrompt, pluginIdPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const frontendPlugin = createFactory({ - name: 'plugin', - description: 'A new frontend plugin', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - - const name = resolvePackageName({ - baseName: id, - scope: ctx.scope, - plugin: true, - }); - const extensionName = `${upperFirst(camelCase(id))}Page`; - - Task.log(); - Task.log(`Creating frontend plugin ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('plugins', id) - : paths.resolveTargetRoot(`backstage-plugin-${id}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'default-plugin', - values: { - id, - name, - extensionName, - pluginVar: `${camelCase(id)}Plugin`, - pluginVersion: ctx.defaultVersion, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - license: ctx.license, - }, - }); - - if (await fs.pathExists(paths.resolveTargetRoot('packages/app'))) { - await Task.forItem('app', 'adding dependency', async () => { - await addPackageDependency( - paths.resolveTargetRoot('packages/app/package.json'), - { - dependencies: { - [name]: `^${ctx.defaultVersion}`, - }, - }, - ); - }); - - await Task.forItem('app', 'adding import', async () => { - const pluginsFilePath = paths.resolveTargetRoot( - 'packages/app/src/App.tsx', - ); - if (!(await fs.pathExists(pluginsFilePath))) { - return; - } - - const content = await fs.readFile(pluginsFilePath, 'utf8'); - const revLines = content.split('\n').reverse(); - - const lastImportIndex = revLines.findIndex(line => - line.match(/ from ("|').*("|')/), - ); - const lastRouteIndex = revLines.findIndex(line => - line.match(/<\/FlatRoutes/), - ); - - if (lastImportIndex !== -1 && lastRouteIndex !== -1) { - const importLine = `import { ${extensionName} } from '${name}';`; - if (!content.includes(importLine)) { - revLines.splice(lastImportIndex, 0, importLine); - } - - const componentLine = `} />`; - if (!content.includes(componentLine)) { - const [indentation] = - revLines[lastRouteIndex + 1].match(/^\s*/) ?? []; - revLines.splice(lastRouteIndex + 1, 0, indentation + componentLine); - } - - const newContent = revLines.reverse().join('\n'); - await fs.writeFile(pluginsFilePath, newContent, 'utf8'); - } - }); - } - - if (options.owner) { - await addCodeownersEntry(`/plugins/${id}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/nodeLibraryPackage.test.ts b/packages/cli/src/lib/new/factories/nodeLibraryPackage.test.ts deleted file mode 100644 index 929182d84e..0000000000 --- a/packages/cli/src/lib/new/factories/nodeLibraryPackage.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { join as joinPath } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { nodeLibraryPackage } from './nodeLibraryPackage'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -describe('nodeLibraryPackage factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a node library package', async () => { - const expectedNodeLibraryPackageName = 'test'; - - mockDir.setContent({ - packages: {}, - }); - - const options = await FactoryRegistry.populateOptions(nodeLibraryPackage, { - id: 'test', // name of node library package - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await nodeLibraryPackage.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - `Creating node-library package ${expectedNodeLibraryPackageName}`, - 'Checking Prerequisites:', - `availability ${joinPath('packages', expectedNodeLibraryPackageName)}`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.ts.hbs', - 'copying setupTests.ts', - 'Installing:', - `moving ${joinPath('packages', expectedNodeLibraryPackageName)}`, - ]); - - await expect( - fs.readJson( - mockDir.resolve( - 'packages', - expectedNodeLibraryPackageName, - 'package.json', - ), - ), - ).resolves.toEqual( - expect.objectContaining({ - name: expectedNodeLibraryPackageName, - private: true, - version: '1.0.0', - }), - ); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('packages', expectedNodeLibraryPackageName), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('packages', expectedNodeLibraryPackageName), - optional: true, - }); - }); - - it('should create a node library plugin with options and codeowners', async () => { - const expectedNodeLibraryPackageName = 'test'; - - mockDir.setContent({ - CODEOWNERS: '', - packages: {}, - }); - - const options = await FactoryRegistry.populateOptions(nodeLibraryPackage, { - id: 'test', - owner: '@backstage/test-owners', - }); - - const [, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await nodeLibraryPackage.create(options, { - scope: 'internal', - private: true, - isMonoRepo: false, - defaultVersion: '1.0.0', - markAsModified: () => {}, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve(expectedNodeLibraryPackageName), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve(expectedNodeLibraryPackageName), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/nodeLibraryPackage.ts b/packages/cli/src/lib/new/factories/nodeLibraryPackage.ts deleted file mode 100644 index 3d9029a01d..0000000000 --- a/packages/cli/src/lib/new/factories/nodeLibraryPackage.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import chalk from 'chalk'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { Task } from '../../tasks'; -import { ownerPrompt, pluginIdPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const nodeLibraryPackage = createFactory({ - name: 'node-library', - description: - 'A new node-library package, exporting shared functionality for backend plugins and modules', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - const name = resolvePackageName({ - baseName: id, - scope: ctx.scope, - plugin: false, - }); - - Task.log(); - Task.log(`Creating node-library package ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('packages', id) - : paths.resolveTargetRoot(`${id}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'node-library-package', - values: { - id, - name, - pluginVersion: ctx.defaultVersion, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - license: ctx.license, - }, - }); - - if (options.owner) { - await addCodeownersEntry(`/packages/${id}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/pluginCommon.test.ts b/packages/cli/src/lib/new/factories/pluginCommon.test.ts deleted file mode 100644 index 6dfcc83536..0000000000 --- a/packages/cli/src/lib/new/factories/pluginCommon.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { pluginCommon } from './pluginCommon'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -describe('pluginCommon factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a common plugin package', async () => { - mockDir.setContent({ - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(pluginCommon, { - id: 'test', - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await pluginCommon.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - 'Creating common plugin package backstage-plugin-test-common', - 'Checking Prerequisites:', - `availability plugins${sep}test-common`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.ts.hbs', - 'copying setupTests.ts', - 'Installing:', - `moving plugins${sep}test-common`, - ]); - - await expect( - fs.readJson(mockDir.resolve('plugins/test-common/package.json')), - ).resolves.toEqual( - expect.objectContaining({ - name: 'backstage-plugin-test-common', - description: 'Common functionalities for the test plugin', - private: true, - version: '1.0.0', - }), - ); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/test-common'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/test-common'), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/pluginCommon.ts b/packages/cli/src/lib/new/factories/pluginCommon.ts deleted file mode 100644 index eb17c70a5c..0000000000 --- a/packages/cli/src/lib/new/factories/pluginCommon.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import chalk from 'chalk'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { Task } from '../../tasks'; -import { ownerPrompt, pluginIdPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const pluginCommon = createFactory({ - name: 'plugin-common', - description: 'A new isomorphic common plugin package', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - const suffix = `${id}-common`; - const name = resolvePackageName({ - baseName: suffix, - scope: ctx.scope, - plugin: true, - }); - - Task.log(); - Task.log(`Creating common plugin package ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('plugins', suffix) - : paths.resolveTargetRoot(`backstage-plugin-${suffix}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'default-common-plugin-package', - values: { - id, - name, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - pluginVersion: ctx.defaultVersion, - license: ctx.license, - }, - }); - - if (options.owner) { - await addCodeownersEntry(`/plugins/${suffix}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/pluginNode.test.ts b/packages/cli/src/lib/new/factories/pluginNode.test.ts deleted file mode 100644 index e6ff093e22..0000000000 --- a/packages/cli/src/lib/new/factories/pluginNode.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { pluginNode } from './pluginNode'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -describe('pluginNode factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a node plugin package', async () => { - mockDir.setContent({ - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(pluginNode, { - id: 'test', - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await pluginNode.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - 'Creating Node.js plugin library backstage-plugin-test-node', - 'Checking Prerequisites:', - `availability plugins${sep}test-node`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.ts.hbs', - 'copying setupTests.ts', - 'Installing:', - `moving plugins${sep}test-node`, - ]); - - await expect( - fs.readJson(mockDir.resolve('plugins/test-node/package.json')), - ).resolves.toEqual( - expect.objectContaining({ - name: 'backstage-plugin-test-node', - description: 'Node.js library for the test plugin', - private: true, - version: '1.0.0', - }), - ); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/test-node'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/test-node'), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/pluginNode.ts b/packages/cli/src/lib/new/factories/pluginNode.ts deleted file mode 100644 index 8eaab3cadb..0000000000 --- a/packages/cli/src/lib/new/factories/pluginNode.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import chalk from 'chalk'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { Task } from '../../tasks'; -import { ownerPrompt, pluginIdPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const pluginNode = createFactory({ - name: 'plugin-node', - description: 'A new Node.js library plugin package', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - const suffix = `${id}-node`; - const name = resolvePackageName({ - baseName: suffix, - scope: ctx.scope, - plugin: true, - }); - - Task.log(); - Task.log(`Creating Node.js plugin library ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('plugins', suffix) - : paths.resolveTargetRoot(`backstage-plugin-${suffix}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'default-node-plugin-package', - values: { - id, - name, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - pluginVersion: ctx.defaultVersion, - license: ctx.license, - }, - }); - - if (options.owner) { - await addCodeownersEntry(`/plugins/${suffix}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/pluginWeb.test.ts b/packages/cli/src/lib/new/factories/pluginWeb.test.ts deleted file mode 100644 index ddc4920fb8..0000000000 --- a/packages/cli/src/lib/new/factories/pluginWeb.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { pluginWeb } from './pluginWeb'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -describe('pluginWeb factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a react plugin package', async () => { - mockDir.setContent({ - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(pluginWeb, { - id: 'test', - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await pluginWeb.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - 'Creating web plugin library backstage-plugin-test-react', - 'Checking Prerequisites:', - `availability plugins${sep}test-react`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.ts.hbs', - 'copying setupTests.ts', - 'copying index.ts', - 'copying ExampleComponent.test.tsx', - 'copying ExampleComponent.tsx', - 'copying index.ts', - 'copying index.ts', - 'copying index.ts', - 'copying useExample.ts', - 'Installing:', - `moving plugins${sep}test-react`, - ]); - - await expect( - fs.readJson(mockDir.resolve('plugins/test-react/package.json')), - ).resolves.toEqual( - expect.objectContaining({ - name: 'backstage-plugin-test-react', - description: 'Web library for the test plugin', - private: true, - version: '1.0.0', - }), - ); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/test-react'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/test-react'), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/pluginWeb.ts b/packages/cli/src/lib/new/factories/pluginWeb.ts deleted file mode 100644 index 6635b0b0e1..0000000000 --- a/packages/cli/src/lib/new/factories/pluginWeb.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import chalk from 'chalk'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { Task } from '../../tasks'; -import { ownerPrompt, pluginIdPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const pluginWeb = createFactory({ - name: 'plugin-react', - description: 'A new web library plugin package', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - const suffix = `${id}-react`; - const name = resolvePackageName({ - baseName: suffix, - scope: ctx.scope, - plugin: true, - }); - - Task.log(); - Task.log(`Creating web plugin library ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('plugins', suffix) - : paths.resolveTargetRoot(`backstage-plugin-${suffix}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'default-react-plugin-package', - values: { - id, - name, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - pluginVersion: ctx.defaultVersion, - license: ctx.license, - }, - }); - - if (options.owner) { - await addCodeownersEntry(`/plugins/${suffix}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/scaffolderModule.test.ts b/packages/cli/src/lib/new/factories/scaffolderModule.test.ts deleted file mode 100644 index 160177f5c0..0000000000 --- a/packages/cli/src/lib/new/factories/scaffolderModule.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { sep } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { scaffolderModule } from './scaffolderModule'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -const backendIndexTsContent = ` -import { createBackend } from '@backstage/backend-defaults'; - -const backend = createBackend(); - -backend.start(); -`; - -describe('scaffolderModule factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a scaffolder backend module package', async () => { - mockDir.setContent({ - packages: { - backend: { - 'package.json': JSON.stringify({}), - src: { - 'index.ts': backendIndexTsContent, - }, - }, - }, - plugins: {}, - }); - - const options = await FactoryRegistry.populateOptions(scaffolderModule, { - id: 'test', - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await scaffolderModule.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: (name: string) => fs.mkdtemp(name), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - 'Creating module backstage-plugin-scaffolder-backend-module-test', - 'Checking Prerequisites:', - `availability plugins${sep}scaffolder-backend-module-test`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.ts.hbs', - 'copying example.test.ts', - 'copying example.ts', - 'copying module.ts', - 'Installing:', - `moving plugins${sep}scaffolder-backend-module-test`, - 'backend adding dependency', - 'backend adding module', - ]); - - await expect( - fs.readFile(mockDir.resolve('packages/backend/src/index.ts'), 'utf8'), - ).resolves.toBe(` -import { createBackend } from '@backstage/backend-defaults'; - -const backend = createBackend(); - -backend.add(import('backstage-plugin-scaffolder-backend-module-test')); -backend.start(); -`); - - await expect( - fs.readJson(mockDir.resolve('packages/backend/package.json')), - ).resolves.toEqual({ - dependencies: { - 'backstage-plugin-scaffolder-backend-module-test': '^1.0.0', - }, - }); - - await expect( - fs.readJson( - mockDir.resolve('plugins/scaffolder-backend-module-test/package.json'), - ), - ).resolves.toEqual( - expect.objectContaining({ - name: 'backstage-plugin-scaffolder-backend-module-test', - description: 'The test module for @backstage/plugin-scaffolder-backend', - private: true, - version: '1.0.0', - }), - ); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('plugins/scaffolder-backend-module-test'), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('plugins/scaffolder-backend-module-test'), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/scaffolderModule.ts b/packages/cli/src/lib/new/factories/scaffolderModule.ts deleted file mode 100644 index 6cea7a36ff..0000000000 --- a/packages/cli/src/lib/new/factories/scaffolderModule.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import chalk from 'chalk'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { addPackageDependency, addToBackend, Task } from '../../tasks'; -import { ownerPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const scaffolderModule = createFactory({ - name: 'scaffolder-module', - description: - 'An module exporting custom actions for @backstage/plugin-scaffolder-backend', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [ - { - type: 'input', - name: 'id', - message: 'Enter the name of the module [required]', - validate: (value: string) => { - if (!value) { - return 'Please enter the name of the module'; - } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) { - return 'Module names must be lowercase and contain only letters, digits, and dashes.'; - } - return true; - }, - }, - ownerPrompt(), - ], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - const slug = `scaffolder-backend-module-${id}`; - - const name = resolvePackageName({ - baseName: slug, - scope: ctx.scope, - plugin: true, - }); - - Task.log(); - Task.log(`Creating module ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('plugins', slug) - : paths.resolveTargetRoot(`backstage-plugin-${slug}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'scaffolder-module', - values: { - id, - name, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - pluginVersion: ctx.defaultVersion, - license: ctx.license, - }, - }); - - if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) { - await Task.forItem('backend', 'adding dependency', async () => { - await addPackageDependency( - paths.resolveTargetRoot('packages/backend/package.json'), - { - dependencies: { - [name]: `^${ctx.defaultVersion}`, - }, - }, - ); - }); - } - - await addToBackend(name, { - type: 'module', - }); - - if (options.owner) { - await addCodeownersEntry(`/plugins/${slug}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/factories/webLibraryPackage.test.ts b/packages/cli/src/lib/new/factories/webLibraryPackage.test.ts deleted file mode 100644 index 942b66f350..0000000000 --- a/packages/cli/src/lib/new/factories/webLibraryPackage.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { join as joinPath } from 'path'; -import { Task } from '../../tasks'; -import { FactoryRegistry } from '../FactoryRegistry'; -import { - createMockOutputStream, - expectLogsToMatch, - mockPaths, -} from './common/testUtils'; -import { webLibraryPackage } from './webLibraryPackage'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -describe('webLibraryPackage factory', () => { - const mockDir = createMockDirectory(); - - beforeEach(() => { - mockPaths({ - targetRoot: mockDir.path, - }); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('should create a web library package', async () => { - const expectedwebLibraryPackageName = 'test'; - - mockDir.setContent({ - packages: {}, - }); - - const options = await FactoryRegistry.populateOptions(webLibraryPackage, { - id: 'test', // name of web library package - }); - - let modified = false; - - const [output, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await webLibraryPackage.create(options, { - private: true, - isMonoRepo: true, - defaultVersion: '1.0.0', - markAsModified: () => { - modified = true; - }, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(modified).toBe(true); - - expectLogsToMatch(output, [ - `Creating web-library package ${expectedwebLibraryPackageName}`, - 'Checking Prerequisites:', - `availability ${joinPath('packages', expectedwebLibraryPackageName)}`, - 'creating temp dir', - 'Executing Template:', - 'templating .eslintrc.js.hbs', - 'templating README.md.hbs', - 'templating package.json.hbs', - 'templating index.ts.hbs', - 'copying setupTests.ts', - 'Installing:', - `moving ${joinPath('packages', expectedwebLibraryPackageName)}`, - ]); - - await expect( - fs.readJson( - mockDir.resolve( - 'packages', - expectedwebLibraryPackageName, - 'package.json', - ), - ), - ).resolves.toEqual( - expect.objectContaining({ - name: expectedwebLibraryPackageName, - private: true, - version: '1.0.0', - }), - ); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve('packages', expectedwebLibraryPackageName), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve('packages', expectedwebLibraryPackageName), - optional: true, - }); - }); - - it('should create a web library plugin with options and codeowners', async () => { - const expectedwebLibraryPackageName = 'test'; - - mockDir.setContent({ - CODEOWNERS: '', - packages: {}, - }); - - const options = await FactoryRegistry.populateOptions(webLibraryPackage, { - id: 'test', - owner: '@backstage/test-owners', - }); - - const [, mockStream] = createMockOutputStream(); - jest.spyOn(process, 'stderr', 'get').mockReturnValue(mockStream); - jest.spyOn(Task, 'forCommand').mockResolvedValue(); - - await webLibraryPackage.create(options, { - scope: 'internal', - private: true, - isMonoRepo: false, - defaultVersion: '1.0.0', - markAsModified: () => {}, - createTemporaryDirectory: () => fs.mkdtemp('test'), - license: 'Apache-2.0', - }); - - expect(Task.forCommand).toHaveBeenCalledTimes(2); - expect(Task.forCommand).toHaveBeenCalledWith('yarn install', { - cwd: mockDir.resolve(expectedwebLibraryPackageName), - optional: true, - }); - expect(Task.forCommand).toHaveBeenCalledWith('yarn lint --fix', { - cwd: mockDir.resolve(expectedwebLibraryPackageName), - optional: true, - }); - }); -}); diff --git a/packages/cli/src/lib/new/factories/webLibraryPackage.ts b/packages/cli/src/lib/new/factories/webLibraryPackage.ts deleted file mode 100644 index 3c63127d7d..0000000000 --- a/packages/cli/src/lib/new/factories/webLibraryPackage.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import chalk from 'chalk'; -import { paths } from '../../paths'; -import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { CreateContext, createFactory } from '../types'; -import { Task } from '../../tasks'; -import { ownerPrompt, pluginIdPrompt } from './common/prompts'; -import { executePluginPackageTemplate } from './common/tasks'; -import { resolvePackageName } from './common/util'; - -type Options = { - id: string; - owner?: string; - codeOwnersPath?: string; -}; - -export const webLibraryPackage = createFactory({ - name: 'web-library', - description: - 'A new web-library package, exporting shared functionality for frontend plugins', - optionsDiscovery: async () => ({ - codeOwnersPath: await getCodeownersFilePath(paths.targetRoot), - }), - optionsPrompts: [pluginIdPrompt(), ownerPrompt()], - async create(options: Options, ctx: CreateContext) { - const { id } = options; - const name = resolvePackageName({ - baseName: id, - scope: ctx.scope, - plugin: false, - }); - - Task.log(); - Task.log(`Creating web-library package ${chalk.cyan(name)}`); - - const targetDir = ctx.isMonoRepo - ? paths.resolveTargetRoot('packages', id) - : paths.resolveTargetRoot(`${id}`); - - await executePluginPackageTemplate(ctx, { - targetDir, - templateName: 'web-library-package', - values: { - id, - name, - pluginVersion: ctx.defaultVersion, - privatePackage: ctx.private, - npmRegistry: ctx.npmRegistry, - license: ctx.license, - }, - }); - - if (options.owner) { - await addCodeownersEntry(`/packages/${id}`, options.owner); - } - - await Task.forCommand('yarn install', { cwd: targetDir, optional: true }); - await Task.forCommand('yarn lint --fix', { - cwd: targetDir, - optional: true, - }); - }, -}); diff --git a/packages/cli/src/lib/new/preparation/collectPortableTemplateInput.test.ts b/packages/cli/src/lib/new/preparation/collectPortableTemplateInput.test.ts new file mode 100644 index 0000000000..905ec0b0c5 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/collectPortableTemplateInput.test.ts @@ -0,0 +1,152 @@ +/* + * 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 inquirer from 'inquirer'; +import { PortableTemplateConfig } from '../types'; +import { collectPortableTemplateInput } from './collectPortableTemplateInput'; +import { withLogCollector } from '@backstage/test-utils'; + +describe('collectTemplateParams', () => { + const baseOptions = { + config: { + isUsingDefaultTemplates: false, + templatePointers: [], + version: '0.1.0', + license: 'Apache-2.0', + private: true, + packageNamePrefix: '@internal/', + packageNamePluginInfix: 'plugin-', + } satisfies PortableTemplateConfig, + template: { + name: 'test', + role: 'frontend-plugin' as const, + files: [], + values: {}, + }, + prefilledParams: {}, + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should prompt for missing parameters', async () => { + jest.spyOn(inquirer, 'prompt').mockResolvedValueOnce({ pluginId: 'other' }); + + await expect( + collectPortableTemplateInput({ + ...baseOptions, + prefilledParams: {}, + }), + ).resolves.toEqual({ + roleParams: { + role: 'frontend-plugin', + pluginId: 'other', + }, + owner: undefined, + version: '0.1.0', + license: 'Apache-2.0', + private: true, + packageName: '@internal/plugin-other', + packagePath: 'plugins/other', + }); + }); + + it('should pick up prefilled parameters', async () => { + await expect( + collectPortableTemplateInput({ + ...baseOptions, + prefilledParams: { + pluginId: 'test1', + owner: 'me', + }, + }), + ).resolves.toEqual({ + roleParams: { + role: 'frontend-plugin', + pluginId: 'test1', + }, + owner: 'me', + version: '0.1.0', + license: 'Apache-2.0', + private: true, + packageName: '@internal/plugin-test1', + packagePath: 'plugins/test1', + }); + }); + + it('should pick up template values', async () => { + await expect( + collectPortableTemplateInput({ + ...baseOptions, + template: { + ...baseOptions.template, + values: { + pluginId: 'test2', + owner: 'me', + }, + }, + }), + ).resolves.toEqual({ + roleParams: { + role: 'frontend-plugin', + pluginId: 'test2', + }, + owner: 'me', + version: '0.1.0', + license: 'Apache-2.0', + private: true, + packageName: '@internal/plugin-test2', + packagePath: 'plugins/test2', + }); + }); + + it('should map deprecated id param to pluginId', async () => { + const logs = await withLogCollector(async () => { + await expect( + collectPortableTemplateInput({ + ...baseOptions, + config: { + ...baseOptions.config, + isUsingDefaultTemplates: true, + }, + prefilledParams: { + id: 'test3', + owner: 'me', + }, + }), + ).resolves.toEqual({ + roleParams: { + role: 'frontend-plugin', + pluginId: 'test3', + }, + owner: 'me', + version: '0.1.0', + license: 'Apache-2.0', + private: true, + packageName: '@internal/plugin-test3', + packagePath: 'plugins/test3', + }); + }); + expect(logs).toEqual({ + error: [], + log: [], + warn: [ + `DEPRECATION WARNING: The 'id' parameter is deprecated, use 'pluginId' instead`, + ], + }); + }); +}); diff --git a/packages/cli/src/lib/new/preparation/collectPortableTemplateInput.ts b/packages/cli/src/lib/new/preparation/collectPortableTemplateInput.ts new file mode 100644 index 0000000000..720993e25c --- /dev/null +++ b/packages/cli/src/lib/new/preparation/collectPortableTemplateInput.ts @@ -0,0 +1,195 @@ +/* + * 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 inquirer, { DistinctQuestion } from 'inquirer'; +import { getCodeownersFilePath, parseOwnerIds } from '../../codeowners'; +import { paths } from '../../paths'; +import { + PortableTemplateConfig, + PortableTemplateInput, + PortableTemplateInputRoleParams, + PortableTemplateParams, + PortableTemplateRole, +} from '../types'; +import { PortableTemplate } from '../types'; +import { resolvePackageParams } from './resolvePackageParams'; + +type CollectTemplateParamsOptions = { + config: PortableTemplateConfig; + template: PortableTemplate; + prefilledParams: PortableTemplateParams; +}; + +export async function collectPortableTemplateInput( + options: CollectTemplateParamsOptions, +): Promise { + const { config, template, prefilledParams } = options; + + const codeOwnersFilePath = await getCodeownersFilePath(paths.targetRoot); + + const prompts = getPromptsForRole(template.role); + + if (codeOwnersFilePath) { + prompts.push(ownerPrompt()); + } + + const deprecatedParams: PortableTemplateParams = {}; + if (config.isUsingDefaultTemplates && prefilledParams.id) { + console.warn( + `DEPRECATION WARNING: The 'id' parameter is deprecated, use 'pluginId' instead`, + ); + deprecatedParams.pluginId = prefilledParams.id; + } + + const parameters = { + ...template.values, + ...prefilledParams, + ...deprecatedParams, + }; + + const needsAnswer = []; + const prefilledAnswers = {} as PortableTemplateParams; + for (const prompt of prompts) { + if (prompt.name && parameters[prompt.name] !== undefined) { + prefilledAnswers[prompt.name] = parameters[prompt.name]; + } else { + needsAnswer.push(prompt); + } + } + + const promptAnswers = await inquirer.prompt( + needsAnswer, + ); + + const answers = { + ...prefilledAnswers, + ...promptAnswers, + }; + + const roleParams = { + role: template.role, + name: answers.name, + pluginId: answers.pluginId, + moduleId: answers.moduleId, + } as PortableTemplateInputRoleParams; + + const packageParams = resolvePackageParams({ + roleParams, + packagePrefix: config.packageNamePrefix, + pluginInfix: config.packageNamePluginInfix, + }); + + return { + roleParams, + owner: answers.owner as string | undefined, + license: config.license, + version: config.version, + private: config.private, + publishRegistry: config.publishRegistry, + packageName: packageParams.packageName, + packagePath: packageParams.packagePath, + }; +} + +export function namePrompt(): DistinctQuestion { + return { + type: 'input', + name: 'name', + message: 'Enter the name of the package, without scope [required]', + validate: (value: string) => { + if (!value) { + return 'Please enter the name of the package'; + } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) { + return 'Package names must be lowercase and contain only letters, digits, and dashes.'; + } + return true; + }, + }; +} + +export function pluginIdPrompt(): DistinctQuestion { + return { + type: 'input', + name: 'pluginId', + message: 'Enter the ID of the plugin [required]', + validate: (value: string) => { + if (!value) { + return 'Please enter the ID of the plugin'; + } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) { + return 'Plugin IDs must be lowercase and contain only letters, digits, and dashes.'; + } + return true; + }, + }; +} + +export function moduleIdIdPrompt(): DistinctQuestion { + return { + type: 'input', + name: 'moduleId', + message: 'Enter the ID of the module [required]', + validate: (value: string) => { + if (!value) { + return 'Please enter the ID of the module'; + } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) { + return 'Module IDs must be lowercase and contain only letters, digits, and dashes.'; + } + return true; + }, + }; +} + +export function getPromptsForRole( + role: PortableTemplateRole, +): Array { + switch (role) { + case 'web-library': + case 'node-library': + case 'common-library': + return [namePrompt()]; + case 'plugin-web-library': + case 'plugin-node-library': + case 'plugin-common-library': + case 'frontend-plugin': + case 'backend-plugin': + return [pluginIdPrompt()]; + case 'frontend-plugin-module': + case 'backend-plugin-module': + return [pluginIdPrompt(), moduleIdIdPrompt()]; + default: + return []; + } +} + +export function ownerPrompt(): DistinctQuestion { + return { + type: 'input', + name: 'owner', + message: 'Enter an owner to add to CODEOWNERS [optional]', + validate: (value: string) => { + if (!value) { + return true; + } + + const ownerIds = parseOwnerIds(value); + if (!ownerIds) { + return 'The owner must be a space separated list of team names (e.g. @org/team-name), usernames (e.g. @username), or the email addresses (e.g. user@example.com).'; + } + + return true; + }, + }; +} diff --git a/packages/cli/src/lib/new/preparation/index.ts b/packages/cli/src/lib/new/preparation/index.ts new file mode 100644 index 0000000000..8b2b253997 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { collectPortableTemplateInput } from './collectPortableTemplateInput'; +export { loadPortableTemplateConfig } from './loadPortableTemplateConfig'; +export { selectTemplateInteractively } from './selectTemplateInteractively'; +export { loadPortableTemplate } from './loadPortableTemplate'; diff --git a/packages/cli/src/lib/new/preparation/loadPortableTemplate.test.ts b/packages/cli/src/lib/new/preparation/loadPortableTemplate.test.ts new file mode 100644 index 0000000000..21734f3da9 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/loadPortableTemplate.test.ts @@ -0,0 +1,110 @@ +/* + * 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 { createMockDirectory } from '@backstage/backend-test-utils'; +import { loadPortableTemplate } from './loadPortableTemplate'; +import { TEMPLATE_FILE_NAME } from '../types'; + +describe('loadTemplate', () => { + const mockDir = createMockDirectory(); + + afterEach(() => { + mockDir.clear(); + }); + + it('should load a valid template', async () => { + mockDir.setContent({ + 'path/to': { + [TEMPLATE_FILE_NAME]: ` + name: template1 + role: frontend-plugin + values: + foo: bar + `, + }, + 'path/to/hello.txt': 'hello world', + }); + + await expect( + loadPortableTemplate({ + name: 'template1', + target: mockDir.resolve('path/to', TEMPLATE_FILE_NAME), + }), + ).resolves.toEqual({ + name: 'template1', + role: 'frontend-plugin', + files: [{ path: 'hello.txt', content: 'hello world' }], + values: { foo: 'bar' }, + }); + }); + + it('should throw an error if template file does not exist', async () => { + mockDir.setContent({}); + + await expect( + loadPortableTemplate({ + name: 'template1', + target: mockDir.resolve('path/to/template1.yaml'), + }), + ).rejects.toThrow( + /^Failed to load template definition from '.*'; caused by Error: ENOENT/, + ); + }); + + it('should throw an error if template definition is invalid', async () => { + mockDir.setContent({ + 'path/to/template1.yaml': `invalid: definition`, + }); + + await expect( + loadPortableTemplate({ + name: 'template1', + target: mockDir.resolve('path/to/template1.yaml'), + }), + ).rejects.toThrow( + /Invalid template definition at '.*'; caused by Validation error/, + ); + }); + + it('should throw an error if target is a remote URL', async () => { + await expect( + loadPortableTemplate({ + name: 'template1', + target: 'http://example.com', + }), + ).rejects.toThrow('Remote templates are not supported yet'); + }); + + it('should throw an error if the package role is invalid', async () => { + mockDir.setContent({ + 'path/to/template1.yaml': ` + name: x + role: invalid-role + `, + }); + + await expect( + loadPortableTemplate({ + name: 'template1', + target: mockDir.resolve('path/to/template1.yaml'), + }), + ).rejects.toThrow( + `Invalid template definition at '${mockDir.resolve( + 'path/to/template1.yaml', + )}'; caused by Validation error: Invalid enum value`, + ); + }); +}); diff --git a/packages/cli/src/lib/new/preparation/loadPortableTemplate.ts b/packages/cli/src/lib/new/preparation/loadPortableTemplate.ts new file mode 100644 index 0000000000..c792119ae6 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/loadPortableTemplate.ts @@ -0,0 +1,108 @@ +/* + * 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 { z } from 'zod'; +import fs from 'fs-extra'; +import recursiveReaddir from 'recursive-readdir'; +import { resolve as resolvePath, relative as relativePath } from 'path'; +import { dirname } from 'node:path'; +import { parse as parseYaml } from 'yaml'; +import { paths } from '../../paths'; +import { + PortableTemplateFile, + PortableTemplatePointer, + TEMPLATE_ROLES, +} from '../types'; +import { PortableTemplate } from '../types'; +import { ForwardedError } from '@backstage/errors'; +import { fromZodError } from 'zod-validation-error'; + +const templateDefinitionSchema = z + .object({ + name: z.string(), + role: z.enum(TEMPLATE_ROLES), + description: z.string().optional(), + values: z.record(z.string()).optional(), + }) + .strict(); + +export async function loadPortableTemplate( + pointer: PortableTemplatePointer, +): Promise { + if (pointer.target.match(/https?:\/\//)) { + throw new Error('Remote templates are not supported yet'); + } + const templateContent = await fs + .readFile(paths.resolveTargetRoot(pointer.target), 'utf-8') + .catch(error => { + throw new ForwardedError( + `Failed to load template definition from '${pointer.target}'`, + error, + ); + }); + const rawTemplate = parseYaml(templateContent); + + const parsed = templateDefinitionSchema.safeParse(rawTemplate); + if (!parsed.success) { + throw new ForwardedError( + `Invalid template definition at '${pointer.target}'`, + fromZodError(parsed.error), + ); + } + + const { role, values = {} } = parsed.data; + + const templatePath = resolvePath(dirname(pointer.target)); + const filePaths = await recursiveReaddir(templatePath).catch(error => { + throw new ForwardedError( + `Failed to load template contents from '${templatePath}'`, + error, + ); + }); + + const loadedFiles = new Array(); + + for (const filePath of filePaths) { + const path = relativePath(templatePath, filePath); + if (filePath === pointer.target) { + continue; + } + + const content = await fs.readFile(filePath, 'utf-8').catch(error => { + throw new ForwardedError( + `Failed to load file contents from '${path}'`, + error, + ); + }); + + if (path.endsWith('.hbs')) { + loadedFiles.push({ + path: path.slice(0, -4), + content, + syntax: 'handlebars', + }); + } else { + loadedFiles.push({ path, content }); + } + } + + return { + name: pointer.name, + role, + files: loadedFiles, + values, + }; +} diff --git a/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.test.ts b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.test.ts new file mode 100644 index 0000000000..b8a7940bcf --- /dev/null +++ b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.test.ts @@ -0,0 +1,339 @@ +/* + * 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 { realpathSync } from 'node:fs'; +import { loadPortableTemplateConfig } from './loadPortableTemplateConfig'; +import { defaultTemplates } from '../defaultTemplates'; +import { createMockDirectory } from '@backstage/backend-test-utils'; +import { TEMPLATE_FILE_NAME } from '../types'; +import { basename } from 'node:path'; + +describe('loadPortableTemplateConfig', () => { + const mockDir = createMockDirectory(); + + afterEach(() => { + mockDir.clear(); + }); + + it('should load configuration from package.json', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + templates: ['./path/to/template1'], + globals: { + license: 'MIT', + private: true, + namePrefix: '@acme/', + namePluginInfix: 'backstage-plugin-', + }, + }, + }, + }, + }), + 'path/to/template1': { + [TEMPLATE_FILE_NAME]: 'name: template1\nrole: frontend-plugin\n', + }, + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).resolves.toEqual({ + isUsingDefaultTemplates: false, + templatePointers: [ + { + name: 'template1', + target: mockDir.resolve('path/to/template1', TEMPLATE_FILE_NAME), + }, + ], + license: 'MIT', + private: true, + version: '0.1.0', + packageNamePrefix: '@acme/', + packageNamePluginInfix: 'backstage-plugin-', + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + overrides: { + license: 'nope', + private: false, + }, + }), + ).resolves.toEqual({ + isUsingDefaultTemplates: false, + templatePointers: [ + { + name: 'template1', + target: mockDir.resolve('path/to/template1', TEMPLATE_FILE_NAME), + }, + ], + license: 'nope', + version: '0.1.0', + private: false, + packageNamePrefix: '@acme/', + packageNamePluginInfix: 'backstage-plugin-', + }); + }); + + it('should support pointing to built-in templates', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + templates: ['@my/package/templates/plugin', 'my-package'], + globals: { + license: 'MIT', + private: true, + namePrefix: '@acme/', + namePluginInfix: 'backstage-plugin-', + }, + }, + }, + }, + }), + node_modules: { + '@my': { + package: { + templates: { + plugin: { + [TEMPLATE_FILE_NAME]: + 'name: frontend-plugin\nrole: frontend-plugin\n', + }, + }, + }, + }, + 'my-package': { + [TEMPLATE_FILE_NAME]: 'name: backend-plugin\nrole: backend-plugin\n', + }, + }, + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).resolves.toEqual({ + isUsingDefaultTemplates: false, + templatePointers: [ + { + name: 'frontend-plugin', + target: realpathSync( + mockDir.resolve( + 'node_modules/@my/package/templates/plugin', + TEMPLATE_FILE_NAME, + ), + ), + }, + { + name: 'backend-plugin', + target: realpathSync( + mockDir.resolve('node_modules/my-package', TEMPLATE_FILE_NAME), + ), + }, + ], + license: 'MIT', + private: true, + version: '0.1.0', + packageNamePrefix: '@acme/', + packageNamePluginInfix: 'backstage-plugin-', + }); + }); + + it('should use default templates if none are specified', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + globals: { + license: 'MIT', + private: true, + }, + }, + }, + }, + }), + node_modules: Object.fromEntries( + defaultTemplates.map(t => [ + t, + { [TEMPLATE_FILE_NAME]: `name: ${basename(t)}\nrole: web-library\n` }, + ]), + ), + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).resolves.toEqual({ + isUsingDefaultTemplates: true, + templatePointers: defaultTemplates.map(t => ({ + name: basename(t), + target: realpathSync( + mockDir.resolve(`node_modules/${t}`, TEMPLATE_FILE_NAME), + ), + })), + license: 'MIT', + private: true, + version: '0.1.0', + packageNamePrefix: '@internal/', + packageNamePluginInfix: 'plugin-', + }); + }); + + it('should reject templates with conflicting names', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + templates: ['./template1', './template2'], + }, + }, + }, + }), + template1: { + [TEMPLATE_FILE_NAME]: 'name: test\nrole: frontend-plugin\n', + }, + template2: { + [TEMPLATE_FILE_NAME]: 'name: test\nrole: backend-plugin\n', + }, + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).rejects.toThrow( + `Invalid template configuration, received conflicting template name 'test' from './template1' and './template2'`, + ); + }); + + it('should throw an error if package.json is invalid', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + templates: 'invalid', + }, + }, + }, + }), + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).rejects.toThrow( + /^Failed to load templating configuration from '.*'; caused by Validation error: Expected array/, + ); + }); + + it('should throw an error if built-in template does not exist', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + templates: ['./invalid'], + }, + }, + }, + }), + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).rejects.toThrow( + `Failed to load template definition '.\/invalid'; caused by Error: ENOENT`, + ); + }); + + it('should throw an error if template point is absolute', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({ + backstage: { + cli: { + new: { + templates: ['/invalid'], + }, + }, + }, + }), + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).rejects.toThrow( + "Failed to load template definition '/invalid'; caused by Error: Template target may not be an absolute path", + ); + }); + + it('should handle missing backstage.new configuration', async () => { + mockDir.setContent({ + 'package.json': JSON.stringify({}), + node_modules: Object.fromEntries( + defaultTemplates.map(t => [ + t, + { [TEMPLATE_FILE_NAME]: `name: ${basename(t)}\nrole: web-library\n` }, + ]), + ), + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + }), + ).resolves.toEqual({ + isUsingDefaultTemplates: true, + templatePointers: expect.any(Array), + license: 'Apache-2.0', + version: '0.1.0', + private: true, + packageNamePrefix: '@internal/', + packageNamePluginInfix: 'plugin-', + }); + + await expect( + loadPortableTemplateConfig({ + packagePath: mockDir.resolve('package.json'), + overrides: { + license: 'nope', + }, + }), + ).resolves.toEqual({ + isUsingDefaultTemplates: true, + templatePointers: expect.any(Array), + license: 'nope', + version: '0.1.0', + private: true, + packageNamePrefix: '@internal/', + packageNamePluginInfix: 'plugin-', + }); + }); +}); diff --git a/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.ts b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.ts new file mode 100644 index 0000000000..d4705b0320 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/loadPortableTemplateConfig.ts @@ -0,0 +1,175 @@ +/* + * 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 fs from 'fs-extra'; +import { resolve as resolvePath, dirname, isAbsolute } from 'node:path'; +import { paths } from '../../paths'; +import { defaultTemplates } from '../defaultTemplates'; +import { + PortableTemplateConfig, + PortableTemplatePointer, + TEMPLATE_FILE_NAME, +} from '../types'; +import { parse as parseYaml } from 'yaml'; +import { z } from 'zod'; +import { fromZodError } from 'zod-validation-error'; +import { ForwardedError } from '@backstage/errors'; + +const defaults = { + license: 'Apache-2.0', + version: '0.1.0', + private: true, + publishRegistry: undefined, + packageNamePrefix: '@internal/', + packageNamePluginInfix: 'plugin-', +}; + +const newConfigSchema = z + .object({ + templates: z.array(z.string()).optional(), + globals: z + .object({ + license: z.string().optional(), + version: z.string().optional(), + private: z.boolean().optional(), + publishRegistry: z.string().optional(), + namePrefix: z.string().optional(), + namePluginInfix: z.string().optional(), + }) + .optional(), + }) + .strict(); + +const pkgJsonWithNewConfigSchema = z.object({ + backstage: z + .object({ + cli: z + .object({ + new: newConfigSchema.optional(), + }) + .optional(), + }) + .optional(), +}); + +type LoadConfigOptions = { + packagePath?: string; + overrides?: Partial; +}; + +export async function loadPortableTemplateConfig( + options: LoadConfigOptions = {}, +): Promise { + const { overrides = {} } = options; + const pkgPath = + options.packagePath ?? paths.resolveTargetRoot('package.json'); + const pkgJson = await fs.readJson(pkgPath); + + const parsed = pkgJsonWithNewConfigSchema.safeParse(pkgJson); + if (!parsed.success) { + throw new ForwardedError( + `Failed to load templating configuration from '${pkgPath}'`, + fromZodError(parsed.error), + ); + } + + const config = parsed.data.backstage?.cli?.new; + + const basePath = dirname(pkgPath); + const templatePointerEntries = await Promise.all( + (config?.templates ?? defaultTemplates).map(async rawPointer => { + try { + const templatePath = resolveLocalTemplatePath(rawPointer, basePath); + + const pointer = await peekLocalTemplateDefinition(templatePath); + return { pointer, rawPointer }; + } catch (error) { + throw new ForwardedError( + `Failed to load template definition '${rawPointer}'`, + error, + ); + } + }), + ); + + const templateNameConflicts = new Map(); + for (const { pointer, rawPointer } of templatePointerEntries) { + const conflict = templateNameConflicts.get(pointer.name); + if (conflict) { + throw new Error( + `Invalid template configuration, received conflicting template name '${pointer.name}' from '${conflict}' and '${rawPointer}'`, + ); + } + templateNameConflicts.set(pointer.name, rawPointer); + } + + return { + isUsingDefaultTemplates: !config?.templates, + templatePointers: templatePointerEntries.map(({ pointer }) => pointer), + license: overrides.license ?? config?.globals?.license ?? defaults.license, + version: overrides.version ?? config?.globals?.version ?? defaults.version, + private: overrides.private ?? config?.globals?.private ?? defaults.private, + publishRegistry: + overrides.publishRegistry ?? + config?.globals?.publishRegistry ?? + defaults.publishRegistry, + packageNamePrefix: + overrides.packageNamePrefix ?? + config?.globals?.namePrefix ?? + defaults.packageNamePrefix, + packageNamePluginInfix: + overrides.packageNamePluginInfix ?? + config?.globals?.namePluginInfix ?? + defaults.packageNamePluginInfix, + }; +} + +function resolveLocalTemplatePath(pointer: string, basePath: string): string { + if (isAbsolute(pointer)) { + throw new Error(`Template target may not be an absolute path`); + } + + if (pointer.startsWith('.')) { + return resolvePath(basePath, pointer, TEMPLATE_FILE_NAME); + } + + return require.resolve(`${pointer}/${TEMPLATE_FILE_NAME}`, { + paths: [basePath], + }); +} + +const partialTemplateDefinitionSchema = z.object({ + name: z.string(), + description: z.string().optional(), +}); + +async function peekLocalTemplateDefinition( + target: string, +): Promise { + const content = await fs.readFile(target, 'utf8'); + + const rawTemplate = parseYaml(content); + const parsed = partialTemplateDefinitionSchema.safeParse(rawTemplate); + if (!parsed.success) { + throw fromZodError(parsed.error); + } + + return { + name: parsed.data.name, + description: parsed.data.description, + target, + }; +} diff --git a/packages/cli/src/lib/new/preparation/resolvePackageParams.test.ts b/packages/cli/src/lib/new/preparation/resolvePackageParams.test.ts new file mode 100644 index 0000000000..df128d4755 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/resolvePackageParams.test.ts @@ -0,0 +1,99 @@ +/* + * 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 { resolvePackageParams } from './resolvePackageParams'; + +describe.each([ + [ + { role: 'web-library', name: 'test' }, + { + packageName: '@internal/test', + packagePath: 'packages/test', + }, + ], + [ + { role: 'node-library', name: 'test' }, + { + packageName: '@internal/test', + packagePath: 'packages/test', + }, + ], + [ + { role: 'common-library', name: 'test' }, + { + packageName: '@internal/test', + packagePath: 'packages/test', + }, + ], + [ + { role: 'plugin-web-library', pluginId: 'test' }, + { + packageName: '@internal/plugin-test-react', + packagePath: 'plugins/test-react', + }, + ], + [ + { role: 'plugin-node-library', pluginId: 'test' }, + { + packageName: '@internal/plugin-test-node', + packagePath: 'plugins/test-node', + }, + ], + [ + { role: 'plugin-common-library', pluginId: 'test' }, + { + packageName: '@internal/plugin-test-common', + packagePath: 'plugins/test-common', + }, + ], + [ + { role: 'frontend-plugin', pluginId: 'test' }, + { + packageName: '@internal/plugin-test', + packagePath: 'plugins/test', + }, + ], + [ + { role: 'backend-plugin', pluginId: 'test' }, + { + packageName: '@internal/plugin-test-backend', + packagePath: 'plugins/test-backend', + }, + ], + [ + { role: 'frontend-plugin-module', pluginId: 'test1', moduleId: 'test2' }, + { + packageName: '@internal/plugin-test1-module-test2', + packagePath: 'plugins/test1-module-test2', + }, + ], + [ + { role: 'backend-plugin-module', pluginId: 'test1', moduleId: 'test2' }, + { + packageName: '@internal/plugin-test1-backend-module-test2', + packagePath: 'plugins/test1-backend-module-test2', + }, + ], +] as const)('resolvePackageInfo', (roleParams, packageInfo) => { + it(`should generate correct info with default config for ${roleParams.role}`, () => { + expect( + resolvePackageParams({ + roleParams, + packagePrefix: '@internal/', + pluginInfix: 'plugin-', + }), + ).toEqual(packageInfo); + }); +}); diff --git a/packages/cli/src/lib/new/preparation/resolvePackageParams.ts b/packages/cli/src/lib/new/preparation/resolvePackageParams.ts new file mode 100644 index 0000000000..8ec1ca3069 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/resolvePackageParams.ts @@ -0,0 +1,68 @@ +/* + * 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 { join as joinPath } from 'path'; +import { PortableTemplateInputRoleParams } from '../types'; + +export type ResolvePackageParamsOptions = { + roleParams: PortableTemplateInputRoleParams; + pluginInfix: string; + packagePrefix: string; +}; + +export type PortableTemplatePackageInfo = { + packageName: string; + packagePath: string; +}; + +export function resolvePackageParams( + options: ResolvePackageParamsOptions, +): PortableTemplatePackageInfo { + const baseName = getBaseNameForRole(options.roleParams); + const isPlugin = options.roleParams.role.includes('plugin'); + const pluginInfix = isPlugin ? options.pluginInfix : ''; + return { + packageName: `${options.packagePrefix}${pluginInfix}${baseName}`, + packagePath: joinPath(isPlugin ? 'plugins' : 'packages', baseName), + }; +} + +function getBaseNameForRole( + roleParams: PortableTemplateInputRoleParams, +): string { + switch (roleParams.role) { + case 'web-library': + case 'node-library': + case 'common-library': + return roleParams.name; + case 'plugin-web-library': + return `${roleParams.pluginId}-react`; + case 'plugin-node-library': + return `${roleParams.pluginId}-node`; + case 'plugin-common-library': + return `${roleParams.pluginId}-common`; + case 'frontend-plugin': + return `${roleParams.pluginId}`; + case 'frontend-plugin-module': + return `${roleParams.pluginId}-module-${roleParams.moduleId}`; + case 'backend-plugin': + return `${roleParams.pluginId}-backend`; + case 'backend-plugin-module': + return `${roleParams.pluginId}-backend-module-${roleParams.moduleId}`; + default: + throw new Error(`Unknown role ${(roleParams as { role: string }).role}`); + } +} diff --git a/packages/cli/src/lib/new/preparation/selectTemplateInteractively.test.ts b/packages/cli/src/lib/new/preparation/selectTemplateInteractively.test.ts new file mode 100644 index 0000000000..adf815e7a6 --- /dev/null +++ b/packages/cli/src/lib/new/preparation/selectTemplateInteractively.test.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 { PortableTemplateConfig } from '../types'; +import inquirer from 'inquirer'; +import { withLogCollector } from '@backstage/test-utils'; +import { selectTemplateInteractively } from './selectTemplateInteractively'; + +describe('selectTemplateInteractively', () => { + const mockConfig = { + isUsingDefaultTemplates: false, + templatePointers: [ + { name: 'template1', target: '/path/to/template1' }, + { name: 'template2', target: '/path/to/template2' }, + ], + } as PortableTemplateConfig; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should select a template interactively', async () => { + jest.spyOn(inquirer, 'prompt').mockResolvedValueOnce({ name: 'template1' }); + + const result = await selectTemplateInteractively(mockConfig); + + expect(result).toEqual({ name: 'template1', target: '/path/to/template1' }); + }); + + it('should error if interactive selections is not found', async () => { + jest + .spyOn(inquirer, 'prompt') + .mockResolvedValueOnce({ name: 'nonexistent' }); + + await expect(selectTemplateInteractively(mockConfig)).rejects.toThrow( + "Template 'nonexistent' not found", + ); + }); + + it('should use preselected template name', async () => { + const result = await selectTemplateInteractively(mockConfig, 'template2'); + + expect(result).toEqual({ name: 'template2', target: '/path/to/template2' }); + }); + + it('should throw an error if template is not found', async () => { + await expect( + selectTemplateInteractively(mockConfig, 'nonexistent'), + ).rejects.toThrow("Template 'nonexistent' not found"); + }); + + it('should rewrite plugin to frontend-plugin if default templates are used', async () => { + await expect( + selectTemplateInteractively(mockConfig, 'plugin'), + ).rejects.toThrow("Template 'plugin' not found"); + + const logs = await withLogCollector(async () => { + await expect( + selectTemplateInteractively( + { ...mockConfig, isUsingDefaultTemplates: true }, + 'plugin', + ), + ).rejects.toThrow("Template 'frontend-plugin' not found"); + }); + expect(logs).toEqual({ + log: [], + warn: [ + "DEPRECATION WARNING: The 'plugin' template is deprecated, use 'frontend-plugin' instead", + ], + error: [], + }); + }); +}); diff --git a/packages/cli/src/lib/new/preparation/selectTemplateInteractively.ts b/packages/cli/src/lib/new/preparation/selectTemplateInteractively.ts new file mode 100644 index 0000000000..154e8af56f --- /dev/null +++ b/packages/cli/src/lib/new/preparation/selectTemplateInteractively.ts @@ -0,0 +1,54 @@ +/* + * 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 inquirer from 'inquirer'; +import { PortableTemplateConfig, PortableTemplatePointer } from '../types'; + +export async function selectTemplateInteractively( + config: PortableTemplateConfig, + preselectedTemplateName?: string, +): Promise { + let selectedName = preselectedTemplateName; + + if (config.isUsingDefaultTemplates && selectedName === 'plugin') { + console.warn( + `DEPRECATION WARNING: The 'plugin' template is deprecated, use 'frontend-plugin' instead`, + ); + selectedName = 'frontend-plugin'; + } + + if (!selectedName) { + const answers = await inquirer.prompt<{ name: string }>([ + { + type: 'list', + name: 'name', + message: 'What do you want to create?', + choices: config.templatePointers.map(t => + t.description + ? { name: `${t.name} - ${t.description}`, value: t.name } + : t.name, + ), + }, + ]); + selectedName = answers.name; + } + + const template = config.templatePointers.find(t => t.name === selectedName); + if (!template) { + throw new Error(`Template '${selectedName}' not found`); + } + return template; +} diff --git a/packages/cli/src/lib/new/types.ts b/packages/cli/src/lib/new/types.ts index 534f99f94f..9f221ea7aa 100644 --- a/packages/cli/src/lib/new/types.ts +++ b/packages/cli/src/lib/new/types.ts @@ -14,68 +14,104 @@ * limitations under the License. */ -import { Answers, DistinctQuestion } from 'inquirer'; +export type PortableTemplateConfig = { + /** + * The pointers to templates that can be used. + */ + templatePointers: PortableTemplatePointer[]; + + /** + * Whether the default set of templates are being used or not. + */ + isUsingDefaultTemplates: boolean; -export interface CreateContext { - /** The package scope to use for new packages */ - scope?: string; - /** The NPM registry to use for new packages */ - npmRegistry?: string; - /** Whether new packages should be marked as private */ - private: boolean; - /** Whether we are creating something in a monorepo or not */ - isMonoRepo: boolean; - /** The default version to use for new packages */ - defaultVersion: string; - /** License to use for new packages */ license: string; - /** Creates a temporary directory. This will always be deleted after creation is done. */ - createTemporaryDirectory(name: string): Promise; + version: string; - /** Signal that the creation process got to a point where permanent modifications were made */ - markAsModified(): void; -} + private: boolean; -export type AnyOptions = Record; + publishRegistry?: string; -export type Prompt = DistinctQuestion & { - name: string; + packageNamePrefix: string; + + packageNamePluginInfix: string; }; -export interface Factory { - /** - * The name used for this factory. - */ +export const TEMPLATE_FILE_NAME = 'portable-template.yaml'; + +export type PortableTemplatePointer = { name: string; + description?: string; + target: string; +}; - /** - * A description that describes what this factory creates to the user. - */ - description: string; +export const TEMPLATE_ROLES = [ + 'web-library', + 'node-library', + 'common-library', + 'plugin-web-library', + 'plugin-node-library', + 'plugin-common-library', + 'frontend-plugin', + 'frontend-plugin-module', + 'backend-plugin', + 'backend-plugin-module', +] as const; - /** - * An optional options discovery step that is run - * before the prompts to potentially fill in some of the options. - */ - optionsDiscovery?(): Promise>; +export type PortableTemplateRole = (typeof TEMPLATE_ROLES)[number]; - /** - * Inquirer prompts that will be filled in either interactively or - * through command line arguments. - */ - optionsPrompts?: ReadonlyArray>; +export type PortableTemplateFile = { + path: string; + content: string; + syntax?: 'handlebars'; +}; - /** - * The main method of the factory that handles creation. - */ - create(options: TOptions, context?: CreateContext): Promise; -} +export type PortableTemplate = { + name: string; + role: PortableTemplateRole; + files: PortableTemplateFile[]; + values: Record; +}; -export type AnyFactory = Factory; +export type PortableTemplateParams = { + [KName in string]?: string | number | boolean; +}; -export function createFactory( - config: Factory, -): AnyFactory { - return config as AnyFactory; -} +export type PortableTemplateInputRoleParams = + | { + role: 'web-library' | 'node-library' | 'common-library'; + name: string; + } + | { + role: + | 'plugin-web-library' + | 'plugin-node-library' + | 'plugin-common-library' + | 'frontend-plugin' + | 'backend-plugin'; + pluginId: string; + } + | { + role: 'frontend-plugin-module' | 'backend-plugin-module'; + pluginId: string; + moduleId: string; + }; + +export type PortableTemplateInput = { + roleParams: PortableTemplateInputRoleParams; + + owner?: string; + + license: string; + + version: string; + + private: boolean; + + publishRegistry?: string; + + packageName: string; + + packagePath: string; +}; diff --git a/packages/cli/src/lib/tasks.test.ts b/packages/cli/src/lib/tasks.test.ts deleted file mode 100644 index 1119adb7f7..0000000000 --- a/packages/cli/src/lib/tasks.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import fs from 'fs-extra'; -import { templatingTask } from './tasks'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -describe('templatingTask', () => { - const mockDir = createMockDirectory(); - - it('should template a directory with mix of regular files and templates', async () => { - // Testing template directory - const tmplDir = 'test-tmpl'; - - // Temporary dest dir to write the template to - const destDir = 'test-dest'; - - // Files content - const testFileContent = 'testing'; - const testVersionFileContent = - "version: {{pluginVersion}} {{versionQuery 'mock-pkg'}}"; - - mockDir.setContent({ - [tmplDir]: { - sub: { - 'version.txt.hbs': testVersionFileContent, - }, - 'test.txt': testFileContent, - }, - [destDir]: {}, - }); - - await templatingTask( - mockDir.resolve(tmplDir), - mockDir.resolve(destDir), - { - pluginVersion: '0.0.0', - }, - () => '^0.1.2', - true, - ); - - await expect( - fs.readFile(mockDir.resolve(destDir, 'test.txt'), 'utf8'), - ).resolves.toBe(testFileContent); - await expect( - fs.readFile(mockDir.resolve(destDir, 'sub/version.txt'), 'utf8'), - ).resolves.toBe('version: 0.0.0 ^0.1.2'); - }); -}); diff --git a/packages/cli/src/lib/tasks.ts b/packages/cli/src/lib/tasks.ts index ffff2892fb..2922f34f35 100644 --- a/packages/cli/src/lib/tasks.ts +++ b/packages/cli/src/lib/tasks.ts @@ -15,15 +15,10 @@ */ import chalk from 'chalk'; -import fs from 'fs-extra'; -import handlebars from 'handlebars'; import ora from 'ora'; import { promisify } from 'util'; -import { basename, dirname } from 'path'; -import recursive from 'recursive-readdir'; import { exec as execCb } from 'child_process'; import { assertError } from '@backstage/errors'; -import { paths } from './paths'; const exec = promisify(execCb); @@ -96,138 +91,3 @@ export class Task { } } } - -export async function templatingTask( - templateDir: string, - destinationDir: string, - context: any, - versionProvider: (name: string, versionHint?: string) => string, - isMonoRepo: boolean, -) { - const files = await recursive(templateDir).catch(error => { - throw new Error(`Failed to read template directory: ${error.message}`); - }); - - for (const file of files) { - const destinationFile = file.replace(templateDir, destinationDir); - await fs.ensureDir(dirname(destinationFile)); - - if (file.endsWith('.hbs')) { - await Task.forItem('templating', basename(file), async () => { - const destination = destinationFile.replace(/\.hbs$/, ''); - - const template = await fs.readFile(file); - const compiled = handlebars.compile(template.toString(), { - strict: true, - }); - const contents = compiled( - { name: basename(destination), ...context }, - { - helpers: { - versionQuery(name: string, versionHint: string | unknown) { - return versionProvider( - name, - typeof versionHint === 'string' ? versionHint : undefined, - ); - }, - }, - }, - ); - - await fs.writeFile(destination, contents).catch(error => { - throw new Error( - `Failed to create file: ${destination}: ${error.message}`, - ); - }); - }); - } else { - if (isMonoRepo && file.match('tsconfig.json')) { - continue; - } - - await Task.forItem('copying', basename(file), async () => { - await fs.copyFile(file, destinationFile).catch(error => { - const destination = destinationFile; - throw new Error( - `Failed to copy file to ${destination} : ${error.message}`, - ); - }); - }); - } - } -} - -export async function addPackageDependency( - path: string, - options: { - dependencies?: Record; - devDependencies?: Record; - peerDependencies?: Record; - }, -) { - try { - const pkgJson = await fs.readJson(path); - - const normalize = (obj: Record) => { - if (Object.keys(obj).length === 0) { - return undefined; - } - return Object.fromEntries( - Object.keys(obj) - .sort() - .map(key => [key, obj[key]]), - ); - }; - - pkgJson.dependencies = normalize({ - ...pkgJson.dependencies, - ...options.dependencies, - }); - pkgJson.devDependencies = normalize({ - ...pkgJson.devDependencies, - ...options.devDependencies, - }); - pkgJson.peerDependencies = normalize({ - ...pkgJson.peerDependencies, - ...options.peerDependencies, - }); - - await fs.writeJson(path, pkgJson, { spaces: 2 }); - } catch (error) { - throw new Error(`Failed to add package dependencies, ${error}`); - } -} - -export async function addToBackend( - name: string, - options: { - type: 'plugin' | 'module'; - }, -) { - if (await fs.pathExists(paths.resolveTargetRoot('packages/backend'))) { - await Task.forItem('backend', `adding ${options.type}`, async () => { - const backendFilePath = paths.resolveTargetRoot( - 'packages/backend/src/index.ts', - ); - if (!(await fs.pathExists(backendFilePath))) { - return; - } - - const content = await fs.readFile(backendFilePath, 'utf8'); - const lines = content.split('\n'); - const backendAddLine = `backend.add(import('${name}'));`; - - const backendStartIndex = lines.findIndex(line => - line.match(/backend.start/), - ); - - if (backendStartIndex !== -1) { - const [indentation] = lines[backendStartIndex].match(/^\s*/)!; - lines.splice(backendStartIndex, 0, `${indentation}${backendAddLine}`); - - const newContent = lines.join('\n'); - await fs.writeFile(backendFilePath, newContent, 'utf8'); - } - }); - } -} diff --git a/packages/cli/templates/default-backend-module/.eslintrc.js.hbs b/packages/cli/templates/backend-plugin-module/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/default-backend-module/.eslintrc.js.hbs rename to packages/cli/templates/backend-plugin-module/.eslintrc.js.hbs diff --git a/packages/cli/templates/default-backend-module/README.md.hbs b/packages/cli/templates/backend-plugin-module/README.md.hbs similarity index 86% rename from packages/cli/templates/default-backend-module/README.md.hbs rename to packages/cli/templates/backend-plugin-module/README.md.hbs index ad7669beda..b389ac8d1e 100644 --- a/packages/cli/templates/default-backend-module/README.md.hbs +++ b/packages/cli/templates/backend-plugin-module/README.md.hbs @@ -1,4 +1,4 @@ -# {{name}} +# {{packageName}} The {{moduleId}} backend module for the {{pluginId}} plugin. diff --git a/packages/cli/templates/default-backend-module/package.json.hbs b/packages/cli/templates/backend-plugin-module/package.json.hbs similarity index 81% rename from packages/cli/templates/default-backend-module/package.json.hbs rename to packages/cli/templates/backend-plugin-module/package.json.hbs index 7e82e047ce..20d78380ca 100644 --- a/packages/cli/templates/default-backend-module/package.json.hbs +++ b/packages/cli/templates/backend-plugin-module/package.json.hbs @@ -1,17 +1,9 @@ { - "name": "{{name}}", + "name": "{{packageName}}", "description": "The {{moduleId}} backend module for the {{pluginId}} plugin.", - "version": "{{packageVersion}}", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/backend-plugin-module/portable-template.yaml b/packages/cli/templates/backend-plugin-module/portable-template.yaml new file mode 100644 index 0000000000..b929d84591 --- /dev/null +++ b/packages/cli/templates/backend-plugin-module/portable-template.yaml @@ -0,0 +1,5 @@ +name: backend-plugin-module +role: backend-plugin-module +description: A new backend module that extends an existing backend plugin +values: + moduleVar: '{{ camelCase pluginId }}Module{{ upperFirst ( camelCase moduleId ) }}' diff --git a/packages/cli/templates/default-backend-module/src/index.ts.hbs b/packages/cli/templates/backend-plugin-module/src/index.ts.hbs similarity index 100% rename from packages/cli/templates/default-backend-module/src/index.ts.hbs rename to packages/cli/templates/backend-plugin-module/src/index.ts.hbs diff --git a/packages/cli/templates/default-backend-module/src/module.ts.hbs b/packages/cli/templates/backend-plugin-module/src/module.ts.hbs similarity index 100% rename from packages/cli/templates/default-backend-module/src/module.ts.hbs rename to packages/cli/templates/backend-plugin-module/src/module.ts.hbs diff --git a/packages/cli/templates/default-backend-plugin/.eslintrc.js.hbs b/packages/cli/templates/backend-plugin/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/default-backend-plugin/.eslintrc.js.hbs rename to packages/cli/templates/backend-plugin/.eslintrc.js.hbs diff --git a/packages/cli/templates/default-backend-plugin/README.md.hbs b/packages/cli/templates/backend-plugin/README.md.hbs similarity index 73% rename from packages/cli/templates/default-backend-plugin/README.md.hbs rename to packages/cli/templates/backend-plugin/README.md.hbs index 00e83a33ec..76189c07c2 100644 --- a/packages/cli/templates/default-backend-plugin/README.md.hbs +++ b/packages/cli/templates/backend-plugin/README.md.hbs @@ -1,14 +1,14 @@ -# {{id}} +# {{pluginId}} This plugin backend was templated using the Backstage CLI. You should replace this text with a description of your plugin backend. ## Installation -This plugin is installed via the `{{name}}` package. To install it to your backend package, run the following command: +This plugin is installed via the `{{packageName}}` package. To install it to your backend package, run the following command: ```bash # From your root directory -yarn --cwd packages/backend add {{name}} +yarn --cwd packages/backend add {{packageName}} ``` Then add the plugin to your backend in `packages/backend/src/index.ts`: @@ -16,7 +16,7 @@ Then add the plugin to your backend in `packages/backend/src/index.ts`: ```ts const backend = createBackend(); // ... -backend.add(import('{{name}}')); +backend.add(import('{{packageName}}')); ``` ## Development diff --git a/packages/cli/templates/default-backend-plugin/dev/index.ts.hbs b/packages/cli/templates/backend-plugin/dev/index.ts.hbs similarity index 75% rename from packages/cli/templates/default-backend-plugin/dev/index.ts.hbs rename to packages/cli/templates/backend-plugin/dev/index.ts.hbs index ce1d2919c9..765031bb97 100644 --- a/packages/cli/templates/default-backend-plugin/dev/index.ts.hbs +++ b/packages/cli/templates/backend-plugin/dev/index.ts.hbs @@ -11,17 +11,17 @@ import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; // // Create a new todo item, standalone or for the sample component: // -// curl http://localhost:7007/api/{{id}}/todos -H 'Content-Type: application/json' -d '{"title": "My Todo"}' -// curl http://localhost:7007/api/{{id}}/todos -H 'Content-Type: application/json' -d '{"title": "My Todo", "entityRef": "component:default/sample"}' +// curl http://localhost:7007/api/{{pluginId}}/todos -H 'Content-Type: application/json' -d '{"title": "My Todo"}' +// curl http://localhost:7007/api/{{pluginId}}/todos -H 'Content-Type: application/json' -d '{"title": "My Todo", "entityRef": "component:default/sample"}' // // List TODOs: // -// curl http://localhost:7007/api/{{id}}/todos +// curl http://localhost:7007/api/{{pluginId}}/todos // // Explicitly make an unauthenticated request, or with service auth: // -// curl http://localhost:7007/api/{{id}}/todos -H 'Authorization: Bearer mock-none-token' -// curl http://localhost:7007/api/{{id}}/todos -H 'Authorization: Bearer mock-service-token' +// curl http://localhost:7007/api/{{pluginId}}/todos -H 'Authorization: Bearer mock-none-token' +// curl http://localhost:7007/api/{{pluginId}}/todos -H 'Authorization: Bearer mock-service-token' const backend = createBackend(); diff --git a/packages/cli/templates/default-backend-plugin/package.json.hbs b/packages/cli/templates/backend-plugin/package.json.hbs similarity index 88% rename from packages/cli/templates/default-backend-plugin/package.json.hbs rename to packages/cli/templates/backend-plugin/package.json.hbs index 2f7c0931d8..ad3810dff3 100644 --- a/packages/cli/templates/default-backend-plugin/package.json.hbs +++ b/packages/cli/templates/backend-plugin/package.json.hbs @@ -1,16 +1,8 @@ { - "name": "{{name}}", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/backend-plugin/portable-template.yaml b/packages/cli/templates/backend-plugin/portable-template.yaml new file mode 100644 index 0000000000..d305ea77f3 --- /dev/null +++ b/packages/cli/templates/backend-plugin/portable-template.yaml @@ -0,0 +1,5 @@ +name: backend-plugin +role: backend-plugin +description: A new backend plugin +values: + pluginVar: '{{ camelCase pluginId }}Plugin' diff --git a/packages/cli/templates/default-backend-plugin/src/index.ts.hbs b/packages/cli/templates/backend-plugin/src/index.ts.hbs similarity index 100% rename from packages/cli/templates/default-backend-plugin/src/index.ts.hbs rename to packages/cli/templates/backend-plugin/src/index.ts.hbs diff --git a/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs b/packages/cli/templates/backend-plugin/src/plugin.test.ts.hbs similarity index 90% rename from packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs rename to packages/cli/templates/backend-plugin/src/plugin.test.ts.hbs index b6f387383e..e105c6ebd2 100644 --- a/packages/cli/templates/default-backend-plugin/src/plugin.test.ts.hbs +++ b/packages/cli/templates/backend-plugin/src/plugin.test.ts.hbs @@ -17,12 +17,12 @@ describe('plugin', () => { features: [{{pluginVar}}], }); - await request(server).get('/api/{{id}}/todos').expect(200, { + await request(server).get('/api/{{pluginId}}/todos').expect(200, { items: [], }); const createRes = await request(server) - .post('/api/{{id}}/todos') + .post('/api/{{pluginId}}/todos') .send({ title: 'My Todo' }); expect(createRes.status).toBe(201); @@ -36,13 +36,13 @@ describe('plugin', () => { const createdTodoItem = createRes.body; await request(server) - .get('/api/{{id}}/todos') + .get('/api/{{pluginId}}/todos') .expect(200, { items: [createdTodoItem], }); await request(server) - .get(`/api/{{id}}/todos/${createdTodoItem.id}`) + .get(`/api/{{pluginId}}/todos/${createdTodoItem.id}`) .expect(200, createdTodoItem); }); @@ -71,7 +71,7 @@ describe('plugin', () => { }); const createRes = await request(server) - .post('/api/{{id}}/todos') + .post('/api/{{pluginId}}/todos') .send({ title: 'My Todo', entityRef: 'component:default/my-component' }); expect(createRes.status).toBe(201); diff --git a/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs b/packages/cli/templates/backend-plugin/src/plugin.ts.hbs similarity index 97% rename from packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs rename to packages/cli/templates/backend-plugin/src/plugin.ts.hbs index a9cccc2af2..b71850ea84 100644 --- a/packages/cli/templates/default-backend-plugin/src/plugin.ts.hbs +++ b/packages/cli/templates/backend-plugin/src/plugin.ts.hbs @@ -12,7 +12,7 @@ import { createTodoListService } from './services/TodoListService'; * @public */ export const {{pluginVar}} = createBackendPlugin({ - pluginId: '{{id}}', + pluginId: '{{pluginId}}', register(env) { env.registerInit({ deps: { diff --git a/packages/cli/templates/default-backend-plugin/src/router.test.ts b/packages/cli/templates/backend-plugin/src/router.test.ts similarity index 100% rename from packages/cli/templates/default-backend-plugin/src/router.test.ts rename to packages/cli/templates/backend-plugin/src/router.test.ts diff --git a/packages/cli/templates/default-backend-plugin/src/router.ts b/packages/cli/templates/backend-plugin/src/router.ts similarity index 100% rename from packages/cli/templates/default-backend-plugin/src/router.ts rename to packages/cli/templates/backend-plugin/src/router.ts diff --git a/packages/cli/templates/default-backend-plugin/src/services/TodoListService/createTodoListService.ts b/packages/cli/templates/backend-plugin/src/services/TodoListService/createTodoListService.ts similarity index 100% rename from packages/cli/templates/default-backend-plugin/src/services/TodoListService/createTodoListService.ts rename to packages/cli/templates/backend-plugin/src/services/TodoListService/createTodoListService.ts diff --git a/packages/cli/templates/default-backend-plugin/src/services/TodoListService/index.ts b/packages/cli/templates/backend-plugin/src/services/TodoListService/index.ts similarity index 100% rename from packages/cli/templates/default-backend-plugin/src/services/TodoListService/index.ts rename to packages/cli/templates/backend-plugin/src/services/TodoListService/index.ts diff --git a/packages/cli/templates/default-backend-plugin/src/services/TodoListService/types.ts b/packages/cli/templates/backend-plugin/src/services/TodoListService/types.ts similarity index 100% rename from packages/cli/templates/default-backend-plugin/src/services/TodoListService/types.ts rename to packages/cli/templates/backend-plugin/src/services/TodoListService/types.ts diff --git a/packages/cli/templates/default-backend-plugin/src/setupTests.ts b/packages/cli/templates/backend-plugin/src/setupTests.ts similarity index 100% rename from packages/cli/templates/default-backend-plugin/src/setupTests.ts rename to packages/cli/templates/backend-plugin/src/setupTests.ts diff --git a/packages/cli/templates/default-backend-module/tsconfig.json b/packages/cli/templates/default-backend-module/tsconfig.json deleted file mode 100644 index 5ae9aeb62d..0000000000 --- a/packages/cli/templates/default-backend-module/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": ["src"], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/default-backend-plugin/tsconfig.json b/packages/cli/templates/default-backend-plugin/tsconfig.json deleted file mode 100644 index d77f0fe3b4..0000000000 --- a/packages/cli/templates/default-backend-plugin/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": [ - "src", - "dev", - "migrations" - ], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/default-common-plugin-package/README.md.hbs b/packages/cli/templates/default-common-plugin-package/README.md.hbs deleted file mode 100644 index 917e18d4b9..0000000000 --- a/packages/cli/templates/default-common-plugin-package/README.md.hbs +++ /dev/null @@ -1,5 +0,0 @@ -# {{name}} - -Welcome to the common package for the {{id}} plugin! - -_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/default-common-plugin-package/tsconfig.json b/packages/cli/templates/default-common-plugin-package/tsconfig.json deleted file mode 100644 index 5ae9aeb62d..0000000000 --- a/packages/cli/templates/default-common-plugin-package/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": ["src"], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/default-node-plugin-package/README.md.hbs b/packages/cli/templates/default-node-plugin-package/README.md.hbs deleted file mode 100644 index a9fc97935e..0000000000 --- a/packages/cli/templates/default-node-plugin-package/README.md.hbs +++ /dev/null @@ -1,5 +0,0 @@ -# {{name}} - -Welcome to the Node.js library package for the {{id}} plugin! - -_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/default-node-plugin-package/tsconfig.json b/packages/cli/templates/default-node-plugin-package/tsconfig.json deleted file mode 100644 index 5ae9aeb62d..0000000000 --- a/packages/cli/templates/default-node-plugin-package/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": ["src"], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/default-plugin/tsconfig.json b/packages/cli/templates/default-plugin/tsconfig.json deleted file mode 100644 index b61e496175..0000000000 --- a/packages/cli/templates/default-plugin/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": [ - "src", - "dev" - ], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/default-react-plugin-package/README.md.hbs b/packages/cli/templates/default-react-plugin-package/README.md.hbs deleted file mode 100644 index ab1c36da6b..0000000000 --- a/packages/cli/templates/default-react-plugin-package/README.md.hbs +++ /dev/null @@ -1,5 +0,0 @@ -# {{name}} - -Welcome to the web library package for the {{id}} plugin! - -_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/default-react-plugin-package/tsconfig.json b/packages/cli/templates/default-react-plugin-package/tsconfig.json deleted file mode 100644 index ce3409d31f..0000000000 --- a/packages/cli/templates/default-react-plugin-package/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": [ - "src", - ], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/default-common-plugin-package/.eslintrc.js.hbs b/packages/cli/templates/frontend-plugin/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/default-common-plugin-package/.eslintrc.js.hbs rename to packages/cli/templates/frontend-plugin/.eslintrc.js.hbs diff --git a/packages/cli/templates/default-plugin/README.md.hbs b/packages/cli/templates/frontend-plugin/README.md.hbs similarity index 77% rename from packages/cli/templates/default-plugin/README.md.hbs rename to packages/cli/templates/frontend-plugin/README.md.hbs index 6d68ca8250..5eae32b7dc 100644 --- a/packages/cli/templates/default-plugin/README.md.hbs +++ b/packages/cli/templates/frontend-plugin/README.md.hbs @@ -1,12 +1,12 @@ -# {{id}} +# {{pluginId}} -Welcome to the {{id}} plugin! +Welcome to the {{pluginId}} plugin! _This plugin was created through the Backstage CLI_ ## Getting started -Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/{{id}}](http://localhost:3000/{{id}}). +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/{{pluginId}}](http://localhost:3000/{{pluginId}}). You can also serve the plugin in isolation by running `yarn start` in the plugin directory. This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. diff --git a/packages/cli/templates/default-plugin/dev/index.tsx.hbs b/packages/cli/templates/frontend-plugin/dev/index.tsx.hbs similarity index 91% rename from packages/cli/templates/default-plugin/dev/index.tsx.hbs rename to packages/cli/templates/frontend-plugin/dev/index.tsx.hbs index da6c4fee04..61cd8f2695 100644 --- a/packages/cli/templates/default-plugin/dev/index.tsx.hbs +++ b/packages/cli/templates/frontend-plugin/dev/index.tsx.hbs @@ -7,6 +7,6 @@ createDevApp() .addPage({ element: <{{ extensionName }} />, title: 'Root Page', - path: '/{{ id }}', + path: '/{{pluginId}}', }) .render(); diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/frontend-plugin/package.json.hbs similarity index 90% rename from packages/cli/templates/default-plugin/package.json.hbs rename to packages/cli/templates/frontend-plugin/package.json.hbs index b4034fd742..64888c2d37 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/frontend-plugin/package.json.hbs @@ -1,16 +1,8 @@ { - "name": "{{name}}", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/frontend-plugin/portable-template.yaml b/packages/cli/templates/frontend-plugin/portable-template.yaml new file mode 100644 index 0000000000..3afb60eb22 --- /dev/null +++ b/packages/cli/templates/frontend-plugin/portable-template.yaml @@ -0,0 +1,6 @@ +name: frontend-plugin +role: frontend-plugin +description: A new frontend plugin +values: + pluginVar: '{{ camelCase pluginId }}Plugin' + extensionName: '{{ upperFirst ( camelCase pluginId ) }}Page' diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/frontend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs similarity index 93% rename from packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs rename to packages/cli/templates/frontend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs index c79977f301..81b4dec2c3 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs +++ b/packages/cli/templates/frontend-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs @@ -23,7 +23,7 @@ describe('ExampleComponent', () => { it('should render', async () => { await renderInTestApp(); expect( - screen.getByText('Welcome to {{ id }}!'), + screen.getByText('Welcome to {{pluginId}}!'), ).toBeInTheDocument(); }); }); diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs b/packages/cli/templates/frontend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs similarity index 93% rename from packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs rename to packages/cli/templates/frontend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs index 1820671fa5..95a8f044d1 100644 --- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs +++ b/packages/cli/templates/frontend-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs @@ -13,7 +13,7 @@ import { ExampleFetchComponent } from '../ExampleFetchComponent'; export const ExampleComponent = () => ( -
+
diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/index.ts b/packages/cli/templates/frontend-plugin/src/components/ExampleComponent/index.ts similarity index 100% rename from packages/cli/templates/default-plugin/src/components/ExampleComponent/index.ts rename to packages/cli/templates/frontend-plugin/src/components/ExampleComponent/index.ts diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/frontend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs similarity index 100% rename from packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs rename to packages/cli/templates/frontend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs b/packages/cli/templates/frontend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs similarity index 100% rename from packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs rename to packages/cli/templates/frontend-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/index.ts b/packages/cli/templates/frontend-plugin/src/components/ExampleFetchComponent/index.ts similarity index 100% rename from packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/index.ts rename to packages/cli/templates/frontend-plugin/src/components/ExampleFetchComponent/index.ts diff --git a/packages/cli/templates/default-plugin/src/index.ts.hbs b/packages/cli/templates/frontend-plugin/src/index.ts.hbs similarity index 100% rename from packages/cli/templates/default-plugin/src/index.ts.hbs rename to packages/cli/templates/frontend-plugin/src/index.ts.hbs diff --git a/packages/cli/templates/default-plugin/src/plugin.test.ts.hbs b/packages/cli/templates/frontend-plugin/src/plugin.test.ts.hbs similarity index 80% rename from packages/cli/templates/default-plugin/src/plugin.test.ts.hbs rename to packages/cli/templates/frontend-plugin/src/plugin.test.ts.hbs index 9d44a9c497..cf27fa33d2 100644 --- a/packages/cli/templates/default-plugin/src/plugin.test.ts.hbs +++ b/packages/cli/templates/frontend-plugin/src/plugin.test.ts.hbs @@ -1,6 +1,6 @@ import { {{ pluginVar }} } from './plugin'; -describe('{{ id }}', () => { +describe('{{pluginId}}', () => { it('should export plugin', () => { expect({{ pluginVar }}).toBeDefined(); }); diff --git a/packages/cli/templates/default-plugin/src/plugin.ts.hbs b/packages/cli/templates/frontend-plugin/src/plugin.ts.hbs similarity index 95% rename from packages/cli/templates/default-plugin/src/plugin.ts.hbs rename to packages/cli/templates/frontend-plugin/src/plugin.ts.hbs index 942eacf113..1ae984b20e 100644 --- a/packages/cli/templates/default-plugin/src/plugin.ts.hbs +++ b/packages/cli/templates/frontend-plugin/src/plugin.ts.hbs @@ -6,7 +6,7 @@ import { import { rootRouteRef } from './routes'; export const {{ pluginVar }} = createPlugin({ - id: '{{ id }}', + id: '{{pluginId}}', routes: { root: rootRouteRef, }, diff --git a/packages/cli/templates/default-plugin/src/routes.ts.hbs b/packages/cli/templates/frontend-plugin/src/routes.ts.hbs similarity index 83% rename from packages/cli/templates/default-plugin/src/routes.ts.hbs rename to packages/cli/templates/frontend-plugin/src/routes.ts.hbs index 7e5d8f85f0..491b05d933 100644 --- a/packages/cli/templates/default-plugin/src/routes.ts.hbs +++ b/packages/cli/templates/frontend-plugin/src/routes.ts.hbs @@ -1,5 +1,5 @@ import { createRouteRef } from '@backstage/core-plugin-api'; export const rootRouteRef = createRouteRef({ - id: '{{ id }}', + id: '{{pluginId}}', }); diff --git a/packages/cli/templates/default-plugin/src/setupTests.ts b/packages/cli/templates/frontend-plugin/src/setupTests.ts similarity index 100% rename from packages/cli/templates/default-plugin/src/setupTests.ts rename to packages/cli/templates/frontend-plugin/src/setupTests.ts diff --git a/packages/cli/templates/node-library-package/tsconfig.json b/packages/cli/templates/node-library-package/tsconfig.json deleted file mode 100644 index ce3409d31f..0000000000 --- a/packages/cli/templates/node-library-package/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": [ - "src", - ], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/default-node-plugin-package/.eslintrc.js.hbs b/packages/cli/templates/node-library/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/default-node-plugin-package/.eslintrc.js.hbs rename to packages/cli/templates/node-library/.eslintrc.js.hbs diff --git a/packages/cli/templates/node-library-package/README.md.hbs b/packages/cli/templates/node-library/README.md.hbs similarity index 78% rename from packages/cli/templates/node-library-package/README.md.hbs rename to packages/cli/templates/node-library/README.md.hbs index 0e2813c87a..0f5f9a08b6 100644 --- a/packages/cli/templates/node-library-package/README.md.hbs +++ b/packages/cli/templates/node-library/README.md.hbs @@ -1,4 +1,4 @@ -# {{name}} +# {{packageName}} _This package was created through the Backstage CLI_. @@ -8,5 +8,5 @@ Install the package via Yarn: ```sh cd # if within a monorepo -yarn add {{name}} +yarn add {{packageName}} ``` diff --git a/packages/cli/templates/node-library-package/package.json.hbs b/packages/cli/templates/node-library/package.json.hbs similarity index 76% rename from packages/cli/templates/node-library-package/package.json.hbs rename to packages/cli/templates/node-library/package.json.hbs index d56a51bcd6..941bf309fd 100644 --- a/packages/cli/templates/node-library-package/package.json.hbs +++ b/packages/cli/templates/node-library/package.json.hbs @@ -1,16 +1,8 @@ { - "name": "{{name}}", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/node-library/portable-template.yaml b/packages/cli/templates/node-library/portable-template.yaml new file mode 100644 index 0000000000..a49f31043d --- /dev/null +++ b/packages/cli/templates/node-library/portable-template.yaml @@ -0,0 +1,3 @@ +name: node-library +role: node-library +description: A library package, exporting shared functionality for Node.js environments diff --git a/packages/cli/templates/node-library-package/src/index.ts.hbs b/packages/cli/templates/node-library/src/index.ts.hbs similarity index 100% rename from packages/cli/templates/node-library-package/src/index.ts.hbs rename to packages/cli/templates/node-library/src/index.ts.hbs diff --git a/packages/cli/templates/default-common-plugin-package/src/setupTests.ts b/packages/cli/templates/node-library/src/setupTests.ts similarity index 100% rename from packages/cli/templates/default-common-plugin-package/src/setupTests.ts rename to packages/cli/templates/node-library/src/setupTests.ts diff --git a/packages/cli/templates/default-plugin/.eslintrc.js.hbs b/packages/cli/templates/plugin-common-library/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/default-plugin/.eslintrc.js.hbs rename to packages/cli/templates/plugin-common-library/.eslintrc.js.hbs diff --git a/packages/cli/templates/plugin-common-library/README.md.hbs b/packages/cli/templates/plugin-common-library/README.md.hbs new file mode 100644 index 0000000000..c29c19f19b --- /dev/null +++ b/packages/cli/templates/plugin-common-library/README.md.hbs @@ -0,0 +1,5 @@ +# {{packageName}} + +Welcome to the common package for the {{pluginId}} plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/default-common-plugin-package/package.json.hbs b/packages/cli/templates/plugin-common-library/package.json.hbs similarity index 71% rename from packages/cli/templates/default-common-plugin-package/package.json.hbs rename to packages/cli/templates/plugin-common-library/package.json.hbs index 689c3e6f53..085ccb40dd 100644 --- a/packages/cli/templates/default-common-plugin-package/package.json.hbs +++ b/packages/cli/templates/plugin-common-library/package.json.hbs @@ -1,17 +1,9 @@ { - "name": "{{name}}", - "description": "Common functionalities for the {{id}} plugin", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", + "description": "Common functionalities for the {{pluginId}} plugin", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.cjs.js", "module": "dist/index.esm.js", diff --git a/packages/cli/templates/plugin-common-library/portable-template.yaml b/packages/cli/templates/plugin-common-library/portable-template.yaml new file mode 100644 index 0000000000..34414bd10c --- /dev/null +++ b/packages/cli/templates/plugin-common-library/portable-template.yaml @@ -0,0 +1,3 @@ +name: plugin-common-library +role: plugin-common-library +description: A new isomorphic common plugin package diff --git a/packages/cli/templates/default-common-plugin-package/src/index.ts.hbs b/packages/cli/templates/plugin-common-library/src/index.ts.hbs similarity index 85% rename from packages/cli/templates/default-common-plugin-package/src/index.ts.hbs rename to packages/cli/templates/plugin-common-library/src/index.ts.hbs index 6584512572..c347bc7a65 100644 --- a/packages/cli/templates/default-common-plugin-package/src/index.ts.hbs +++ b/packages/cli/templates/plugin-common-library/src/index.ts.hbs @@ -1,6 +1,6 @@ /***/ /** - * Common functionalities for the {{id}} plugin. + * Common functionalities for the {{pluginId}} plugin. * * @packageDocumentation */ diff --git a/packages/cli/templates/default-node-plugin-package/src/setupTests.ts b/packages/cli/templates/plugin-common-library/src/setupTests.ts similarity index 100% rename from packages/cli/templates/default-node-plugin-package/src/setupTests.ts rename to packages/cli/templates/plugin-common-library/src/setupTests.ts diff --git a/packages/cli/templates/default-react-plugin-package/.eslintrc.js.hbs b/packages/cli/templates/plugin-node-library/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/default-react-plugin-package/.eslintrc.js.hbs rename to packages/cli/templates/plugin-node-library/.eslintrc.js.hbs diff --git a/packages/cli/templates/plugin-node-library/README.md.hbs b/packages/cli/templates/plugin-node-library/README.md.hbs new file mode 100644 index 0000000000..7eaa344f7c --- /dev/null +++ b/packages/cli/templates/plugin-node-library/README.md.hbs @@ -0,0 +1,5 @@ +# {{packageName}} + +Welcome to the Node.js library package for the {{pluginId}} plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/default-node-plugin-package/package.json.hbs b/packages/cli/templates/plugin-node-library/package.json.hbs similarity index 69% rename from packages/cli/templates/default-node-plugin-package/package.json.hbs rename to packages/cli/templates/plugin-node-library/package.json.hbs index d58f95b3b2..182ef1c93f 100644 --- a/packages/cli/templates/default-node-plugin-package/package.json.hbs +++ b/packages/cli/templates/plugin-node-library/package.json.hbs @@ -1,17 +1,9 @@ { - "name": "{{name}}", - "description": "Node.js library for the {{id}} plugin", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", + "description": "Node.js library for the {{pluginId}} plugin", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/plugin-node-library/portable-template.yaml b/packages/cli/templates/plugin-node-library/portable-template.yaml new file mode 100644 index 0000000000..e71a55d40a --- /dev/null +++ b/packages/cli/templates/plugin-node-library/portable-template.yaml @@ -0,0 +1,3 @@ +name: plugin-node-library +role: plugin-node-library +description: A new Node.js library plugin package diff --git a/packages/cli/templates/default-node-plugin-package/src/index.ts.hbs b/packages/cli/templates/plugin-node-library/src/index.ts.hbs similarity index 84% rename from packages/cli/templates/default-node-plugin-package/src/index.ts.hbs rename to packages/cli/templates/plugin-node-library/src/index.ts.hbs index 355b35c373..ce180ec7ec 100644 --- a/packages/cli/templates/default-node-plugin-package/src/index.ts.hbs +++ b/packages/cli/templates/plugin-node-library/src/index.ts.hbs @@ -1,6 +1,6 @@ /***/ /** - * Node.js library for the {{id}} plugin. + * Node.js library for the {{pluginId}} plugin. * * @packageDocumentation */ diff --git a/packages/cli/templates/node-library-package/src/setupTests.ts b/packages/cli/templates/plugin-node-library/src/setupTests.ts similarity index 100% rename from packages/cli/templates/node-library-package/src/setupTests.ts rename to packages/cli/templates/plugin-node-library/src/setupTests.ts diff --git a/packages/cli/templates/node-library-package/.eslintrc.js.hbs b/packages/cli/templates/plugin-web-library/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/node-library-package/.eslintrc.js.hbs rename to packages/cli/templates/plugin-web-library/.eslintrc.js.hbs diff --git a/packages/cli/templates/plugin-web-library/README.md.hbs b/packages/cli/templates/plugin-web-library/README.md.hbs new file mode 100644 index 0000000000..8d8647399a --- /dev/null +++ b/packages/cli/templates/plugin-web-library/README.md.hbs @@ -0,0 +1,5 @@ +# {{packageName}} + +Welcome to the web library package for the {{pluginId}} plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/default-react-plugin-package/package.json.hbs b/packages/cli/templates/plugin-web-library/package.json.hbs similarity index 83% rename from packages/cli/templates/default-react-plugin-package/package.json.hbs rename to packages/cli/templates/plugin-web-library/package.json.hbs index bea066630f..d104a29305 100644 --- a/packages/cli/templates/default-react-plugin-package/package.json.hbs +++ b/packages/cli/templates/plugin-web-library/package.json.hbs @@ -1,17 +1,9 @@ { - "name": "{{name}}", - "description": "Web library for the {{id}} plugin", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", + "description": "Web library for the {{pluginId}} plugin", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/plugin-web-library/portable-template.yaml b/packages/cli/templates/plugin-web-library/portable-template.yaml new file mode 100644 index 0000000000..19be5e79a3 --- /dev/null +++ b/packages/cli/templates/plugin-web-library/portable-template.yaml @@ -0,0 +1,3 @@ +name: plugin-web-library +role: plugin-web-library +description: A new web library plugin package diff --git a/packages/cli/templates/default-react-plugin-package/src/components/ExampleComponent/ExampleComponent.test.tsx b/packages/cli/templates/plugin-web-library/src/components/ExampleComponent/ExampleComponent.test.tsx similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/components/ExampleComponent/ExampleComponent.test.tsx rename to packages/cli/templates/plugin-web-library/src/components/ExampleComponent/ExampleComponent.test.tsx diff --git a/packages/cli/templates/default-react-plugin-package/src/components/ExampleComponent/ExampleComponent.tsx b/packages/cli/templates/plugin-web-library/src/components/ExampleComponent/ExampleComponent.tsx similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/components/ExampleComponent/ExampleComponent.tsx rename to packages/cli/templates/plugin-web-library/src/components/ExampleComponent/ExampleComponent.tsx diff --git a/packages/cli/templates/default-react-plugin-package/src/components/ExampleComponent/index.ts b/packages/cli/templates/plugin-web-library/src/components/ExampleComponent/index.ts similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/components/ExampleComponent/index.ts rename to packages/cli/templates/plugin-web-library/src/components/ExampleComponent/index.ts diff --git a/packages/cli/templates/default-react-plugin-package/src/components/index.ts b/packages/cli/templates/plugin-web-library/src/components/index.ts similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/components/index.ts rename to packages/cli/templates/plugin-web-library/src/components/index.ts diff --git a/packages/cli/templates/default-react-plugin-package/src/hooks/index.ts b/packages/cli/templates/plugin-web-library/src/hooks/index.ts similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/hooks/index.ts rename to packages/cli/templates/plugin-web-library/src/hooks/index.ts diff --git a/packages/cli/templates/default-react-plugin-package/src/hooks/useExample/index.ts b/packages/cli/templates/plugin-web-library/src/hooks/useExample/index.ts similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/hooks/useExample/index.ts rename to packages/cli/templates/plugin-web-library/src/hooks/useExample/index.ts diff --git a/packages/cli/templates/default-react-plugin-package/src/hooks/useExample/useExample.ts b/packages/cli/templates/plugin-web-library/src/hooks/useExample/useExample.ts similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/hooks/useExample/useExample.ts rename to packages/cli/templates/plugin-web-library/src/hooks/useExample/useExample.ts diff --git a/packages/cli/templates/default-react-plugin-package/src/index.ts.hbs b/packages/cli/templates/plugin-web-library/src/index.ts.hbs similarity index 83% rename from packages/cli/templates/default-react-plugin-package/src/index.ts.hbs rename to packages/cli/templates/plugin-web-library/src/index.ts.hbs index 9b22677a3e..70f1987895 100644 --- a/packages/cli/templates/default-react-plugin-package/src/index.ts.hbs +++ b/packages/cli/templates/plugin-web-library/src/index.ts.hbs @@ -1,6 +1,6 @@ /***/ /** - * Web library for the {{id}} plugin. + * Web library for the {{pluginId}} plugin. * * @packageDocumentation */ diff --git a/packages/cli/templates/default-react-plugin-package/src/setupTests.ts b/packages/cli/templates/plugin-web-library/src/setupTests.ts similarity index 100% rename from packages/cli/templates/default-react-plugin-package/src/setupTests.ts rename to packages/cli/templates/plugin-web-library/src/setupTests.ts diff --git a/packages/cli/templates/scaffolder-module/.eslintrc.js.hbs b/packages/cli/templates/scaffolder-backend-module/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/scaffolder-module/.eslintrc.js.hbs rename to packages/cli/templates/scaffolder-backend-module/.eslintrc.js.hbs diff --git a/packages/cli/templates/scaffolder-backend-module/README.md.hbs b/packages/cli/templates/scaffolder-backend-module/README.md.hbs new file mode 100644 index 0000000000..141699922d --- /dev/null +++ b/packages/cli/templates/scaffolder-backend-module/README.md.hbs @@ -0,0 +1,5 @@ +# {{packageName}} + +The {{moduleId}} module for [@backstage/plugin-scaffolder-backend](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend). + +_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/scaffolder-module/package.json.hbs b/packages/cli/templates/scaffolder-backend-module/package.json.hbs similarity index 77% rename from packages/cli/templates/scaffolder-module/package.json.hbs rename to packages/cli/templates/scaffolder-backend-module/package.json.hbs index 25cd8616f2..3866a83672 100644 --- a/packages/cli/templates/scaffolder-module/package.json.hbs +++ b/packages/cli/templates/scaffolder-backend-module/package.json.hbs @@ -1,17 +1,9 @@ { - "name": "{{name}}", - "description": "The {{id}} module for @backstage/plugin-scaffolder-backend", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", + "description": "The {{moduleId}} module for @backstage/plugin-scaffolder-backend", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.cjs.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/scaffolder-backend-module/portable-template.yaml b/packages/cli/templates/scaffolder-backend-module/portable-template.yaml new file mode 100644 index 0000000000..a131c14062 --- /dev/null +++ b/packages/cli/templates/scaffolder-backend-module/portable-template.yaml @@ -0,0 +1,6 @@ +name: scaffolder-backend-module +role: backend-plugin-module +description: A module exporting custom actions for @backstage/plugin-scaffolder-backend +values: + pluginId: scaffolder + moduleVar: '{{ camelCase pluginId }}Module{{ upperFirst ( camelCase moduleId ) }}' diff --git a/packages/cli/templates/scaffolder-module/src/actions/example.test.ts b/packages/cli/templates/scaffolder-backend-module/src/actions/example.test.ts similarity index 100% rename from packages/cli/templates/scaffolder-module/src/actions/example.test.ts rename to packages/cli/templates/scaffolder-backend-module/src/actions/example.test.ts diff --git a/packages/cli/templates/scaffolder-module/src/actions/example.ts b/packages/cli/templates/scaffolder-backend-module/src/actions/example.ts similarity index 100% rename from packages/cli/templates/scaffolder-module/src/actions/example.ts rename to packages/cli/templates/scaffolder-backend-module/src/actions/example.ts diff --git a/packages/cli/templates/scaffolder-module/src/index.ts.hbs b/packages/cli/templates/scaffolder-backend-module/src/index.ts.hbs similarity index 58% rename from packages/cli/templates/scaffolder-module/src/index.ts.hbs rename to packages/cli/templates/scaffolder-backend-module/src/index.ts.hbs index 58d5da4a57..e278e6f4b2 100644 --- a/packages/cli/templates/scaffolder-module/src/index.ts.hbs +++ b/packages/cli/templates/scaffolder-backend-module/src/index.ts.hbs @@ -1,6 +1,6 @@ /***/ /** - * The {{id}} module for @backstage/plugin-scaffolder-backend. + * The {{moduleId}} module for @backstage/plugin-scaffolder-backend. * * @packageDocumentation */ diff --git a/packages/cli/templates/scaffolder-module/src/module.ts b/packages/cli/templates/scaffolder-backend-module/src/module.ts similarity index 100% rename from packages/cli/templates/scaffolder-module/src/module.ts rename to packages/cli/templates/scaffolder-backend-module/src/module.ts diff --git a/packages/cli/templates/scaffolder-module/README.md.hbs b/packages/cli/templates/scaffolder-module/README.md.hbs deleted file mode 100644 index 8ee653a4ac..0000000000 --- a/packages/cli/templates/scaffolder-module/README.md.hbs +++ /dev/null @@ -1,5 +0,0 @@ -# {{name}} - -The {{id}} module for [@backstage/plugin-scaffolder-backend](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend). - -_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/scaffolder-module/tsconfig.json b/packages/cli/templates/scaffolder-module/tsconfig.json deleted file mode 100644 index 5ae9aeb62d..0000000000 --- a/packages/cli/templates/scaffolder-module/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": ["src"], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/web-library-package/tsconfig.json b/packages/cli/templates/web-library-package/tsconfig.json deleted file mode 100644 index ce3409d31f..0000000000 --- a/packages/cli/templates/web-library-package/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": [ - "src", - ], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "dist-types", - "rootDir": "." - } -} diff --git a/packages/cli/templates/web-library-package/.eslintrc.js.hbs b/packages/cli/templates/web-library/.eslintrc.js.hbs similarity index 100% rename from packages/cli/templates/web-library-package/.eslintrc.js.hbs rename to packages/cli/templates/web-library/.eslintrc.js.hbs diff --git a/packages/cli/templates/web-library-package/README.md.hbs b/packages/cli/templates/web-library/README.md.hbs similarity index 78% rename from packages/cli/templates/web-library-package/README.md.hbs rename to packages/cli/templates/web-library/README.md.hbs index 0e2813c87a..0f5f9a08b6 100644 --- a/packages/cli/templates/web-library-package/README.md.hbs +++ b/packages/cli/templates/web-library/README.md.hbs @@ -1,4 +1,4 @@ -# {{name}} +# {{packageName}} _This package was created through the Backstage CLI_. @@ -8,5 +8,5 @@ Install the package via Yarn: ```sh cd # if within a monorepo -yarn add {{name}} +yarn add {{packageName}} ``` diff --git a/packages/cli/templates/web-library-package/package.json.hbs b/packages/cli/templates/web-library/package.json.hbs similarity index 78% rename from packages/cli/templates/web-library-package/package.json.hbs rename to packages/cli/templates/web-library/package.json.hbs index 64a693dd4c..52e54b1783 100644 --- a/packages/cli/templates/web-library-package/package.json.hbs +++ b/packages/cli/templates/web-library/package.json.hbs @@ -1,16 +1,8 @@ { - "name": "{{name}}", - "version": "{{pluginVersion}}", + "name": "{{packageName}}", "main": "src/index.ts", "types": "src/index.ts", - "license": "{{license}}", -{{#if privatePackage}} - "private": {{privatePackage}}, -{{/if}} "publishConfig": { -{{#if npmRegistry}} - "registry": "{{npmRegistry}}", -{{/if}} "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" diff --git a/packages/cli/templates/web-library/portable-template.yaml b/packages/cli/templates/web-library/portable-template.yaml new file mode 100644 index 0000000000..595fc13616 --- /dev/null +++ b/packages/cli/templates/web-library/portable-template.yaml @@ -0,0 +1,3 @@ +name: web-library +role: web-library +description: A library package, exporting shared functionality for web environments diff --git a/packages/cli/templates/web-library-package/src/index.ts.hbs b/packages/cli/templates/web-library/src/index.ts.hbs similarity index 100% rename from packages/cli/templates/web-library-package/src/index.ts.hbs rename to packages/cli/templates/web-library/src/index.ts.hbs diff --git a/packages/cli/templates/web-library-package/src/setupTests.ts b/packages/cli/templates/web-library/src/setupTests.ts similarity index 100% rename from packages/cli/templates/web-library-package/src/setupTests.ts rename to packages/cli/templates/web-library/src/setupTests.ts diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 388cad75c5..64376dd667 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/core-compat-api +## 0.3.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/version-bridge@1.0.11-next.0 + ## 0.3.6-next.2 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 9830fd893c..cf3ac2eac7 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.6-next.2", + "version": "0.3.6-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 86e4c94b32..4a0ec56dda 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/create-app +## 0.5.25-next.3 + +### Patch Changes + +- bfa4ea9: Enable `catalog.useUrlReadersSearch` configuration by default in newly created Backstage installations. + + This parameter makes `UrlReaderProcessor` always use the search method. + New adopters are encouraged to enable it as this behavior will be the default in a future release. + +- Updated dependencies + - @backstage/cli-common@0.1.15 + ## 0.5.25-next.2 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index a442438c18..15966976aa 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.5.25-next.2", + "version": "0.5.25-next.3", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index 7b9413e63e..af519905e9 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -22,7 +22,7 @@ "lint": "backstage-cli repo lint --since origin/{{defaultBranch}}", "lint:all": "backstage-cli repo lint", "prettier:check": "prettier --check .", - "new": "backstage-cli new --scope internal" + "new": "backstage-cli new" }, "workspaces": { "packages": [ diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 0379797290..65b4af4a95 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.1.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + ## 1.1.7-next.2 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 836558e244..b5be27b216 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.7-next.2", + "version": "1.1.7-next.3", "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 9209c35009..6b62715b7c 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.25-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.25-next.3 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + ## 0.2.25-next.2 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index d7bd294f44..6b4a689fcc 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.25-next.2", + "version": "0.2.25-next.3", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/e2e-test/src/commands/run.ts b/packages/e2e-test/src/commands/run.ts index 77e2e83c4c..50ce3c6781 100644 --- a/packages/e2e-test/src/commands/run.ts +++ b/packages/e2e-test/src/commands/run.ts @@ -40,7 +40,7 @@ import { OptionValues } from 'commander'; const paths = findPaths(__dirname); const templatePackagePaths = [ - 'packages/cli/templates/default-plugin/package.json.hbs', + 'packages/cli/templates/frontend-plugin/package.json.hbs', 'packages/create-app/templates/default-app/package.json.hbs', 'packages/create-app/templates/default-app/packages/app/package.json.hbs', 'packages/create-app/templates/default-app/packages/backend/package.json.hbs', diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index c1055c8a7d..4d78cde805 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/frontend-app-api +## 0.10.5-next.3 + +### Patch Changes + +- f1efb47: Add support for defining multiple attachment points for extensions and blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.1.6-next.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + ## 0.10.5-next.2 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 03239ea840..17dd1b6b73 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.5-next.2", + "version": "0.10.5-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts index 0517e943aa..e93d311f8d 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts @@ -119,6 +119,56 @@ describe('buildAppTree', () => { `); }); + it('should create a tree with clones', () => { + const tree = resolveAppTree('a', [ + { ...baseSpec, id: 'a' }, + { ...baseSpec, id: 'b', attachTo: { id: 'a', input: 'x' } }, + { + ...baseSpec, + id: 'c', + attachTo: [ + { id: 'a', input: 'x' }, + { id: 'b', input: 'x' }, + ], + }, + { + ...baseSpec, + id: 'd', + attachTo: [ + { id: 'b', input: 'x' }, + { id: 'c', input: 'x' }, + ], + }, + ]); + + expect(Array.from(tree.nodes.keys())).toEqual(['a', 'b', 'c', 'd']); + + expect(String(tree.root)).toMatchInlineSnapshot(` + " + x [ + + x [ + + x [ + + ] + + + ] + + + x [ + + ] + + ] + " + `); + + const orphans = Array.from(tree.orphans).map(String); + expect(orphans).toMatchInlineSnapshot(`[]`); + }); + it('should create a tree out of order', () => { const tree = resolveAppTree('b', [ { ...baseSpec, attachTo: { id: 'b', input: 'x' }, id: 'bx2' }, @@ -239,30 +289,30 @@ describe('buildAppTree', () => { ]); expect(tree.root).toMatchInlineSnapshot(` - { - "attachments": { - "test": [ - { - "attachments": undefined, - "id": "b", - "output": undefined, - }, - ], - }, - "id": "a", - "output": undefined, - } - `); + { + "attachments": { + "test": [ + { + "attachments": undefined, + "id": "b", + "output": undefined, + }, + ], + }, + "id": "a", + "output": undefined, + } + `); expect(tree.orphans).toMatchInlineSnapshot(`[]`); expect(String(tree.root)).toMatchInlineSnapshot(` - " - test [ - - ] - " - `); + " + test [ + + ] + " + `); }); it('should not allow redirects for attachment points that already exist', () => { diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.ts b/packages/frontend-app-api/src/tree/resolveAppTree.ts index dc9985da30..dacd06407a 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.ts @@ -145,6 +145,7 @@ export function resolveAppTree( } const orphans = new Array(); + const clones = new Map>(); // A node with the provided rootNodeId must be found in the tree, and it must not be attached to anything let rootNode: AppNode | undefined = undefined; @@ -155,9 +156,42 @@ export function resolveAppTree( // TODO: For now we simply ignore the attachTo spec of the root node, but it'd be cleaner if we could avoid defining it if (spec.id === rootNodeId) { rootNode = node; - } else { - let attachTo = node.spec.attachTo; + } else if (Array.isArray(spec.attachTo)) { + let foundFirstParent = false; + for (const origAttachTo of spec.attachTo) { + let attachTo = origAttachTo; + if (!isValidAttachmentPoint(attachTo, nodes)) { + attachTo = + redirectTargetsByKey.get(makeRedirectKey(attachTo)) ?? attachTo; + } + + const parent = nodes.get(attachTo.id); + if (parent) { + const cloneParents = clones.get(attachTo.id) ?? []; + + if (!foundFirstParent) { + foundFirstParent = true; + node.setParent(parent, attachTo.input); + } else { + cloneParents.unshift(parent); + } + + for (const extraParent of cloneParents) { + const clonedNode = new SerializableAppNode(spec); + clonedNode.setParent(extraParent, attachTo.input); + clones.set( + spec.id, + clones.get(spec.id)?.concat(clonedNode) ?? [clonedNode], + ); + } + } + } + if (!foundFirstParent) { + orphans.push(node); + } + } else { + let attachTo = spec.attachTo; if (!isValidAttachmentPoint(attachTo, nodes)) { attachTo = redirectTargetsByKey.get(makeRedirectKey(attachTo)) ?? attachTo; diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx index 6c5331a972..a1399d786a 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx @@ -350,4 +350,84 @@ describe('createSpecializedApp', () => { expect(screen.getByText('link: /test')).toBeInTheDocument(); }); + + it('should support multiple attachment points', async () => { + let appTreeApi: AppTreeApi | undefined = undefined; + + createSpecializedApp({ + features: [ + createFrontendPlugin({ + id: 'test', + extensions: [ + createExtension({ + name: 'root', + attachTo: { id: 'root', input: 'app' }, + inputs: { + children: createExtensionInput([ + coreExtensionData.reactElement, + ]), + }, + output: [coreExtensionData.reactElement], + factory: ({ apis }) => { + appTreeApi = apis.get(appTreeApiRef); + return [coreExtensionData.reactElement(
)]; + }, + }), + createExtension({ + name: 'a', + attachTo: { id: 'test/root', input: 'children' }, + inputs: { + children: createExtensionInput([ + coreExtensionData.reactElement, + ]), + }, + output: [coreExtensionData.reactElement], + factory: () => [coreExtensionData.reactElement(
)], + }), + createExtension({ + name: 'b', + attachTo: { id: 'test/root', input: 'children' }, + inputs: { + children: createExtensionInput([ + coreExtensionData.reactElement, + ]), + }, + output: [coreExtensionData.reactElement], + factory: () => [coreExtensionData.reactElement(
)], + }), + createExtension({ + name: 'cloned', + attachTo: [ + { id: 'test/a', input: 'children' }, + { id: 'test/b', input: 'children' }, + ], + output: [coreExtensionData.reactElement], + factory: () => [coreExtensionData.reactElement(
)], + }), + ], + }), + ], + }); + + expect(String(appTreeApi!.getTree().tree.root)).toMatchInlineSnapshot(` + " + app [ + + children [ + + children [ + + ] + + + children [ + + ] + + ] + + ] + " + `); + }); }); diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index ee8167a458..f0c1cf7bf2 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/frontend-defaults +## 0.1.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-app@0.1.6-next.3 + ## 0.1.6-next.2 ### Patch Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index 9573b80f11..41b3a3133f 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.1.6-next.2", + "version": "0.1.6-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-internal/CHANGELOG.md b/packages/frontend-internal/CHANGELOG.md index 0a5277b646..8cc91a973d 100644 --- a/packages/frontend-internal/CHANGELOG.md +++ b/packages/frontend-internal/CHANGELOG.md @@ -1,5 +1,14 @@ # @internal/frontend +## 0.0.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + ## 0.0.6-next.2 ### Patch Changes diff --git a/packages/frontend-internal/package.json b/packages/frontend-internal/package.json index 87d46025c0..37e779c69c 100644 --- a/packages/frontend-internal/package.json +++ b/packages/frontend-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/frontend", - "version": "0.0.6-next.2", + "version": "0.0.6-next.3", "backstage": { "role": "web-library", "inline": true diff --git a/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts b/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts index 12027e0bb6..44dfb002ad 100644 --- a/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts +++ b/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts @@ -18,6 +18,7 @@ import { AnyExtensionDataRef, ApiHolder, AppNode, + ExtensionAttachToSpec, ExtensionDataValue, ExtensionDefinition, ExtensionDefinitionParameters, @@ -35,7 +36,7 @@ export const OpaqueExtensionDefinition = OpaqueType.create<{ readonly kind?: string; readonly namespace?: string; readonly name?: string; - readonly attachTo: { id: string; input: string }; + readonly attachTo: ExtensionAttachToSpec; readonly disabled: boolean; readonly configSchema?: PortableSchema; readonly inputs: { @@ -66,7 +67,7 @@ export const OpaqueExtensionDefinition = OpaqueType.create<{ readonly kind?: string; readonly namespace?: string; readonly name?: string; - readonly attachTo: { id: string; input: string }; + readonly attachTo: ExtensionAttachToSpec; readonly disabled: boolean; readonly configSchema?: PortableSchema; readonly inputs: { diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index e0d39a5eaa..c74b73a676 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/frontend-plugin-api +## 0.9.5-next.3 + +### Patch Changes + +- f1efb47: Add support for defining multiple attachment points for extensions and blueprints. +- Updated dependencies + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + ## 0.9.5-next.2 ### Patch Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 04b6fc5fe0..80540f777d 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.5-next.2", + "version": "0.9.5-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 6202eeaaa2..13eb751361 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -227,10 +227,7 @@ export interface AppNodeInstance { // @public export interface AppNodeSpec { // (undocumented) - readonly attachTo: { - id: string; - input: string; - }; + readonly attachTo: ExtensionAttachToSpec; // (undocumented) readonly config?: unknown; // (undocumented) @@ -593,10 +590,7 @@ export type CreateExtensionBlueprintOptions< }, > = { kind: TKind; - attachTo: { - id: string; - input: string; - }; + attachTo: ExtensionAttachToSpec; disabled?: boolean; inputs?: TInputs; output: Array; @@ -680,10 +674,7 @@ export type CreateExtensionOptions< > = { kind?: TKind; name?: TName; - attachTo: { - id: string; - input: string; - }; + attachTo: ExtensionAttachToSpec; disabled?: boolean; inputs?: TInputs; output: Array; @@ -813,10 +804,7 @@ export interface Extension { // (undocumented) $$type: '@backstage/Extension'; // (undocumented) - readonly attachTo: { - id: string; - input: string; - }; + readonly attachTo: ExtensionAttachToSpec; // (undocumented) readonly configSchema?: PortableSchema; // (undocumented) @@ -825,6 +813,17 @@ export interface Extension { readonly id: string; } +// @public (undocumented) +export type ExtensionAttachToSpec = + | { + id: string; + input: string; + } + | Array<{ + id: string; + input: string; + }>; + // @public (undocumented) export interface ExtensionBlueprint< T extends ExtensionBlueprintParameters = ExtensionBlueprintParameters, @@ -834,10 +833,7 @@ export interface ExtensionBlueprint< // (undocumented) make(args: { name?: TNewName; - attachTo?: { - id: string; - input: string; - }; + attachTo?: ExtensionAttachToSpec; disabled?: boolean; params: T['params']; }): ExtensionDefinition<{ @@ -867,10 +863,7 @@ export interface ExtensionBlueprint< }, >(args: { name?: TNewName; - attachTo?: { - id: string; - input: string; - }; + attachTo?: ExtensionAttachToSpec; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & @@ -1057,10 +1050,7 @@ export type ExtensionDefinition< >( args: Expand< { - attachTo?: { - id: string; - input: string; - }; + attachTo?: ExtensionAttachToSpec; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & diff --git a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts index 6ced6706b9..c9d9896600 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts @@ -15,7 +15,12 @@ */ import { createApiRef } from '@backstage/core-plugin-api'; -import { FrontendPlugin, Extension, ExtensionDataRef } from '../../wiring'; +import { + FrontendPlugin, + Extension, + ExtensionDataRef, + ExtensionAttachToSpec, +} from '../../wiring'; /** * The specification for this {@link AppNode} in the {@link AppTree}. @@ -28,7 +33,7 @@ import { FrontendPlugin, Extension, ExtensionDataRef } from '../../wiring'; */ export interface AppNodeSpec { readonly id: string; - readonly attachTo: { id: string; input: string }; + readonly attachTo: ExtensionAttachToSpec; readonly extension: Extension; readonly disabled: boolean; readonly config?: unknown; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 3eb6445fee..07a1bfcdb0 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -202,6 +202,20 @@ describe('createExtension', () => { }); }); + it('should create an extension with multiple attachment points', () => { + const extension = createExtension({ + attachTo: [ + { id: 'root', input: 'default' }, + { id: 'other', input: 'default' }, + ], + output: [stringDataRef, numberDataRef.optional()], + factory: () => [stringDataRef('bar')], + }); + expect(String(extension)).toBe( + 'ExtensionDefinition{attachTo=root@default+other@default}', + ); + }); + it('should create an extension with input', () => { const extension = createExtension({ attachTo: { id: 'root', input: 'default' }, diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index baf0b31215..f93c9d21ce 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -112,6 +112,11 @@ export type VerifyExtensionFactoryOutput< >}` : never; +/** @public */ +export type ExtensionAttachToSpec = + | { id: string; input: string } + | Array<{ id: string; input: string }>; + /** @public */ export type CreateExtensionOptions< TKind extends string | undefined, @@ -128,7 +133,7 @@ export type CreateExtensionOptions< > = { kind?: TKind; name?: TName; - attachTo: { id: string; input: string }; + attachTo: ExtensionAttachToSpec; disabled?: boolean; inputs?: TInputs; output: Array; @@ -183,7 +188,7 @@ export type ExtensionDefinition< >( args: Expand< { - attachTo?: { id: string; input: string }; + attachTo?: ExtensionAttachToSpec; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & @@ -338,7 +343,12 @@ export function createExtension< if (options.name) { parts.push(`name=${options.name}`); } - parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`); + parts.push( + `attachTo=${[options.attachTo] + .flat() + .map(a => `${a.id}@${a.input}`) + .join('+')}`, + ); return `ExtensionDefinition{${parts.join(',')}}`; }, override(overrideOptions) { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index 16786f9757..c6dcf74e1c 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -87,7 +87,11 @@ describe('createExtensionBlueprint', () => { it('should allow creation of extension blueprints with a generator', () => { const TestExtensionBlueprint = createExtensionBlueprint({ kind: 'test-extension', - attachTo: { id: 'test', input: 'default' }, + // Try multiple attachment points for this one + attachTo: [ + { id: 'test-1', input: 'default' }, + { id: 'test-2', input: 'default' }, + ], output: [coreExtensionData.reactElement], *factory(params: { text: string }) { yield coreExtensionData.reactElement(

{params.text}

); @@ -103,10 +107,10 @@ describe('createExtensionBlueprint', () => { expect(extension).toEqual({ $$type: '@backstage/ExtensionDefinition', - attachTo: { - id: 'test', - input: 'default', - }, + attachTo: [ + { id: 'test-1', input: 'default' }, + { id: 'test-2', input: 'default' }, + ], configSchema: undefined, disabled: false, inputs: {}, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 3d55b0bbe0..880caba967 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -17,6 +17,7 @@ import { ApiHolder, AppNode } from '../apis'; import { Expand } from '@backstage/types'; import { + ExtensionAttachToSpec, ExtensionDefinition, ResolvedExtensionInputs, VerifyExtensionFactoryOutput, @@ -57,7 +58,7 @@ export type CreateExtensionBlueprintOptions< TDataRefs extends { [name in string]: AnyExtensionDataRef }, > = { kind: TKind; - attachTo: { id: string; input: string }; + attachTo: ExtensionAttachToSpec; disabled?: boolean; inputs?: TInputs; output: Array; @@ -107,7 +108,7 @@ export interface ExtensionBlueprint< make(args: { name?: TNewName; - attachTo?: { id: string; input: string }; + attachTo?: ExtensionAttachToSpec; disabled?: boolean; params: T['params']; }): ExtensionDefinition<{ @@ -141,7 +142,7 @@ export interface ExtensionBlueprint< }, >(args: { name?: TNewName; - attachTo?: { id: string; input: string }; + attachTo?: ExtensionAttachToSpec; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index ebbaf4429f..8c90eeef51 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -19,6 +19,7 @@ export { createExtension, type ExtensionDefinition, type ExtensionDefinitionParameters, + type ExtensionAttachToSpec, type CreateExtensionOptions, type ResolvedExtensionInput, type ResolvedExtensionInputs, diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index 1e8ac77a01..7e771c6c2c 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -16,6 +16,7 @@ import { ApiHolder, AppNode } from '../apis'; import { + ExtensionAttachToSpec, ExtensionDefinition, ExtensionDefinitionParameters, ResolvedExtensionInputs, @@ -32,7 +33,7 @@ import { OpaqueExtensionDefinition } from '@internal/frontend'; export interface Extension { $$type: '@backstage/Extension'; readonly id: string; - readonly attachTo: { id: string; input: string }; + readonly attachTo: ExtensionAttachToSpec; readonly disabled: boolean; readonly configSchema?: PortableSchema; } diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 1b650a9f5f..248e62bd4b 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/frontend-test-utils +## 0.2.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/frontend-app-api@0.10.5-next.3 + - @backstage/config@1.3.2 + - @backstage/test-utils@1.7.5-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-app@0.1.6-next.3 + ## 0.2.6-next.2 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 284555f13e..79c746992a 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.6-next.2", + "version": "0.2.6-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 3b60d2524f..418f6a6719 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/repo-tools +## 0.12.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/cli-node@0.2.13-next.1 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + ## 0.12.2-next.2 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 1232374d81..b4dde33cb8 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.12.2-next.2", + "version": "0.12.2-next.3", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index 75c900729f..245cae7fd2 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/scaffolder +## 0.0.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + ## 0.0.6-next.2 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index 73f2ee9bf5..fa78fa8c1e 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.6-next.2", + "version": "0.0.6-next.3", "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 b996408f15..d54b12b8db 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.105-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/cli@0.30.0-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/app-defaults@1.5.17-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/test-utils@1.7.5-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + ## 0.2.105-next.2 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index a3edbf703d..43e573e57b 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.105-next.2", + "version": "0.2.105-next.3", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 7286a756fb..81c5f4e358 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.9.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/plugin-techdocs-node@1.13.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + ## 1.9.0-next.2 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index ed68631225..be896c5be9 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.0-next.2", + "version": "1.9.0-next.3", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index b6567b21e1..12f49db37c 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-api-docs +## 0.12.4-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + ## 0.12.4-next.2 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 3354bf1013..45652ce96c 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.4-next.2", + "version": "0.12.4-next.3", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index cad65a4b9a..e565514244 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-app-backend +## 0.4.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.30-next.2 + ## 0.4.5-next.1 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 778db22377..713be22a6a 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.4.5-next.1", + "version": "0.4.5-next.2", "description": "A Backstage backend plugin that serves the Backstage frontend app", "backstage": { "role": "backend-plugin", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index 2a407904f2..f78f2490e3 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-node +## 0.1.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + ## 0.1.30-next.1 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index e4e5a06b6c..65bcd74211 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.30-next.1", + "version": "0.1.30-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 4321fc5cac..8ebf5f6e37 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-visualizer +## 0.1.16-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + ## 0.1.16-next.2 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 9f9dae6916..f6cb22d3e8 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.16-next.2", + "version": "0.1.16-next.3", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index 758b3efded..286c1d7710 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-app +## 0.1.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-permission-react@0.4.31-next.0 + ## 0.1.6-next.2 ### Patch Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index 7898c292aa..c596a5bdeb 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.1.6-next.2", + "version": "0.1.6-next.3", "backstage": { "role": "frontend-plugin", "pluginId": "app", diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 39143a302f..19b6b11c33 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.4.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index c2c284459c..f39be48a9c 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", - "version": "0.4.0-next.1", + "version": "0.4.0-next.2", "description": "The atlassian-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md index 480beaf8a3..d17aedd86e 100644 --- a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-auth0-provider +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.2.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-auth0-provider/package.json b/plugins/auth-backend-module-auth0-provider/package.json index bca47e7565..fedd3576de 100644 --- a/plugins/auth-backend-module-auth0-provider/package.json +++ b/plugins/auth-backend-module-auth0-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-auth0-provider", - "version": "0.2.0-next.1", + "version": "0.2.0-next.2", "description": "The auth0-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index 09fd3926f5..56305208ef 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.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/errors@1.2.7 + ## 0.4.0-next.2 ### Minor Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index 7875691fed..614459046f 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.0-next.2", + "version": "0.4.0-next.3", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md index 61baeb1914..deaf00522e 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-azure-easyauth-provider +## 0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + ## 0.2.5-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-azure-easyauth-provider/package.json b/plugins/auth-backend-module-azure-easyauth-provider/package.json index d98ff82d16..a110cba378 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/package.json +++ b/plugins/auth-backend-module-azure-easyauth-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-azure-easyauth-provider", - "version": "0.2.5-next.1", + "version": "0.2.5-next.2", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index 2ba6bd4031..459fca1375 100644 --- a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-bitbucket-provider +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index 074c206a50..e564ef2493 100644 --- a/plugins/auth-backend-module-bitbucket-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-provider", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md index 7f6f0ec676..c9024a505a 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-bitbucket-server-provider +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.2.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-bitbucket-server-provider/package.json b/plugins/auth-backend-module-bitbucket-server-provider/package.json index 4250494814..3587169c79 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-server-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-server-provider", - "version": "0.2.0-next.1", + "version": "0.2.0-next.2", "description": "The bitbucket-server-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index b33a6815ad..d744fc2b65 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-cloudflare-access-provider +## 0.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.4.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index abb2707bb2..8a73f77a38 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/package.json +++ b/plugins/auth-backend-module-cloudflare-access-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-cloudflare-access-provider", - "version": "0.4.0-next.1", + "version": "0.4.0-next.2", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index b959b2514c..487e577261 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index c6ddeb0186..966e6af1f6 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", - "version": "0.4.0-next.1", + "version": "0.4.0-next.2", "description": "A GCP IAP auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index b84d2d3175..28d890263f 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index a87aee19e3..b0a7f7f17b 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "The github-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-github-provider/src/authenticator.test.ts b/plugins/auth-backend-module-github-provider/src/authenticator.test.ts index dc5c7208ea..48a297fa41 100644 --- a/plugins/auth-backend-module-github-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-github-provider/src/authenticator.test.ts @@ -42,7 +42,7 @@ describe('githubAuthenticator', () => { accessToken: 'my-token', scope: 'user:read', tokenType: 'bearer', - refreshToken: 'access-token.my-token', + refreshToken: 'access-token-v2.my-token', }, }); }); @@ -105,9 +105,10 @@ describe('githubAuthenticator', () => { await expect( githubAuthenticator.refresh( { - refreshToken: 'access-token.my-token', + refreshToken: 'access-token-v2.my-token', req: {} as any, scope: 'user:read', + scopeAlreadyGranted: true, }, { fetchProfile: async _input => ({ id: 'id' } as PassportProfile), @@ -119,11 +120,28 @@ describe('githubAuthenticator', () => { accessToken: 'my-token', scope: 'user:read', tokenType: 'bearer', - refreshToken: 'access-token.my-token', + refreshToken: 'access-token-v2.my-token', }, }); }); + it('should fail refresh if scope has not already been granted', async () => { + await expect( + githubAuthenticator.refresh( + { + refreshToken: 'access-token-v2.my-token', + req: {} as any, + scope: 'user:read', + }, + { + fetchProfile: async _input => ({ id: 'id' } as PassportProfile), + } as PassportOAuthAuthenticatorHelper, + ), + ).rejects.toThrow( + 'Refresh failed, session has not been granted the requested scope', + ); + }); + it('should refresh with refresh token', async () => { const res = {}; await expect( diff --git a/plugins/auth-backend-module-github-provider/src/authenticator.ts b/plugins/auth-backend-module-github-provider/src/authenticator.ts index 7c94a04c30..e9dc29e4a6 100644 --- a/plugins/auth-backend-module-github-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-github-provider/src/authenticator.ts @@ -22,7 +22,7 @@ import { PassportProfile, } from '@backstage/plugin-auth-node'; -const ACCESS_TOKEN_PREFIX = 'access-token.'; +const ACCESS_TOKEN_PREFIX = 'access-token-v2.'; /** @public */ export const githubAuthenticator = createOAuthAuthenticator({ @@ -98,6 +98,11 @@ export const githubAuthenticator = createOAuthAuthenticator({ // refresh token cookie. We use that token to fetch the user profile and // refresh the Backstage session when needed. if (input.refreshToken?.startsWith(ACCESS_TOKEN_PREFIX)) { + if (!input.scopeAlreadyGranted) { + throw new Error( + 'Refresh failed, session has not been granted the requested scope', + ); + } const accessToken = input.refreshToken.slice(ACCESS_TOKEN_PREFIX.length); const fullProfile = await helper diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index 274a0d21af..31d7b65487 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index 812e09bbcd..f9008cdf9b 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "The gitlab-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 706d724e0d..17689e2554 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index 7ace0be1d6..aa292b5cce 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "A Google auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-guest-provider/CHANGELOG.md b/plugins/auth-backend-module-guest-provider/CHANGELOG.md index 22d6e118bf..31f014b5c6 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + ## 0.2.5-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json index a5df8c8f66..78429a8260 100644 --- a/plugins/auth-backend-module-guest-provider/package.json +++ b/plugins/auth-backend-module-guest-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-guest-provider", - "version": "0.2.5-next.1", + "version": "0.2.5-next.2", "description": "The guest-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index d1f85a4fda..68ec815cbe 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 7327d66d1e..ee567d8ce0 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "The microsoft-provider backend module for the auth plugin.", "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 c61b2ba456..74590d6ddc 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.4.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index de488b3890..a08746d166 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.0-next.1", + "version": "0.4.0-next.2", "description": "The oauth2-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index f781f702b9..2313d17158 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/errors@1.2.7 + ## 0.2.5-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json index bdaaa527bc..081ccf6c96 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/package.json +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", - "version": "0.2.5-next.1", + "version": "0.2.5-next.2", "description": "The oauth2-proxy-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 08b27f9762..32128edfab 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.4.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend@0.24.3-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.4.0-next.2 ### Minor Changes diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 25b947cc61..96ce0e270e 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.0-next.2", + "version": "0.4.0-next.3", "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 2abed09032..6343e2260e 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.2.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.2.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 0ce099f600..ab3e8ef5bb 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.0-next.1", + "version": "0.2.0-next.2", "description": "The okta-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md index fa3826a02a..2e185b82e6 100644 --- a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-onelogin-provider +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index 77849e8693..b486495c8a 100644 --- a/plugins/auth-backend-module-onelogin-provider/package.json +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-onelogin-provider", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "The onelogin-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 940a9c29e1..b0456027f1 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index f633eda44e..12a34259dc 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "The pinniped-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md index ed44b8ff16..98e18a9e72 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.5.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + ## 0.5.0-next.1 ### Minor Changes diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index 51f53869b7..9ea66aa080 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", - "version": "0.5.0-next.1", + "version": "0.5.0-next.2", "description": "The vmware-cloud-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 93ba96b939..bd85f35505 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-auth-backend +## 0.24.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-auth0-provider@0.2.0-next.2 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.0-next.3 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.5-next.2 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.0-next.2 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-github-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-google-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.3.0-next.2 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.4.0-next.2 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.5-next.2 + - @backstage/plugin-auth-backend-module-oidc-provider@0.4.0-next.3 + - @backstage/plugin-auth-backend-module-okta-provider@0.2.0-next.2 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.3.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @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 + ## 0.24.3-next.2 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 09c08e3a62..cce8aaff0b 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.3-next.2", + "version": "0.24.3-next.3", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index efe9deaca4..8d45f802d3 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-auth-node +## 0.6.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @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 + ## 0.6.0-next.1 ### Minor Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index a2da220096..27c5c97050 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.6.0-next.1", + "version": "0.6.0-next.2", "backstage": { "role": "node-library", "pluginId": "auth", diff --git a/plugins/auth-node/report.api.md b/plugins/auth-node/report.api.md index e5f33b4e7e..3b467c077f 100644 --- a/plugins/auth-node/report.api.md +++ b/plugins/auth-node/report.api.md @@ -326,6 +326,7 @@ export interface OAuthAuthenticatorRefreshInput { req: Request_2; // (undocumented) scope: string; + scopeAlreadyGranted?: boolean; } // @public (undocumented) diff --git a/plugins/auth-node/src/oauth/CookieScopeManager.test.ts b/plugins/auth-node/src/oauth/CookieScopeManager.test.ts index 6cdee1168f..bc8e7a233a 100644 --- a/plugins/auth-node/src/oauth/CookieScopeManager.test.ts +++ b/plugins/auth-node/src/oauth/CookieScopeManager.test.ts @@ -193,6 +193,55 @@ describe('CookieScopeManager', () => { ); }); + it('should signal whether persisted scopes have already been granted when refreshing', async () => { + const getGrantedScopes = jest.fn(); + const manager = CookieScopeManager.create({ + authenticator: { + scopes: { + persist: true, + } as OAuthAuthenticatorScopeOptions, + } as OAuthAuthenticator, + cookieManager: { + getGrantedScopes, + } as unknown as OAuthCookieManager, + }); + + getGrantedScopes.mockReturnValue('x y'); + await expect(manager.refresh(makeReq('x,y'))).resolves.toEqual({ + scope: 'x y', + scopeAlreadyGranted: true, + commit: expect.any(Function), + }); + + getGrantedScopes.mockReturnValueOnce('x y'); + await expect(manager.refresh(makeReq('x'))).resolves.toEqual({ + scope: 'x y', + scopeAlreadyGranted: true, + commit: expect.any(Function), + }); + + getGrantedScopes.mockReturnValueOnce('x y'); + await expect(manager.refresh(makeReq('x,y,z'))).resolves.toEqual({ + scope: 'x y z', + scopeAlreadyGranted: false, + commit: expect.any(Function), + }); + + getGrantedScopes.mockReturnValueOnce(''); + await expect(manager.refresh(makeReq('x,y'))).resolves.toEqual({ + scope: 'x y', + scopeAlreadyGranted: false, + commit: expect.any(Function), + }); + + getGrantedScopes.mockReturnValueOnce(undefined); + await expect(manager.refresh(makeReq('x,y'))).resolves.toEqual({ + scope: 'x y', + scopeAlreadyGranted: false, + commit: expect.any(Function), + }); + }); + it('should use custom scope transform', async () => { const manager = CookieScopeManager.create({ additionalScopes: ['b'], diff --git a/plugins/auth-node/src/oauth/CookieScopeManager.ts b/plugins/auth-node/src/oauth/CookieScopeManager.ts index 3cbf977e09..d2db6d20a9 100644 --- a/plugins/auth-node/src/oauth/CookieScopeManager.ts +++ b/plugins/auth-node/src/oauth/CookieScopeManager.ts @@ -138,6 +138,7 @@ export class CookieScopeManager { async refresh(req: express.Request): Promise<{ scope: string; + scopeAlreadyGranted?: boolean; commit(result: OAuthAuthenticatorResult): Promise; }> { const requestScope = splitScope(req.query.scope?.toString()); @@ -147,6 +148,9 @@ export class CookieScopeManager { return { scope, + scopeAlreadyGranted: this.cookieManager + ? hasScopeBeenGranted(grantedScope, scope) + : undefined, commit: async result => { if (this.cookieManager) { this.cookieManager.setGrantedScopes( @@ -162,3 +166,16 @@ export class CookieScopeManager { }; } } + +function hasScopeBeenGranted( + grantedScope: Iterable, + requestedScope: string, +): boolean { + const granted = new Set(grantedScope); + for (const requested of splitScope(requestedScope)) { + if (!granted.has(requested)) { + return false; + } + } + return true; +} diff --git a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts index 5bc9fa2551..a943717553 100644 --- a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts @@ -720,6 +720,7 @@ describe('createOAuthRouteHandlers', () => { req: expect.anything(), refreshToken: 'refresh-token', scope: 'persisted-scope', + scopeAlreadyGranted: true, }, { ctx: 'authenticator' }, ); diff --git a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts index 72f45d8ab1..3849bc5f9e 100644 --- a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.ts @@ -315,7 +315,12 @@ export function createOAuthRouteHandlers( const scopeRefresh = await scopeManager.refresh(req); const result = await authenticator.refresh( - { req, scope: scopeRefresh.scope, refreshToken }, + { + req, + scope: scopeRefresh.scope, + scopeAlreadyGranted: scopeRefresh.scopeAlreadyGranted, + refreshToken, + }, authenticatorCtx, ); diff --git a/plugins/auth-node/src/oauth/types.ts b/plugins/auth-node/src/oauth/types.ts index c4ae2ee1cf..50d81f99dc 100644 --- a/plugins/auth-node/src/oauth/types.ts +++ b/plugins/auth-node/src/oauth/types.ts @@ -59,6 +59,11 @@ export interface OAuthAuthenticatorAuthenticateInput { /** @public */ export interface OAuthAuthenticatorRefreshInput { + /** + * Signals whether the requested scope has already been granted for the session. Will only be set if the `scopes.persist` option is enabled. + */ + scopeAlreadyGranted?: boolean; + scope: string; refreshToken: string; req: Request; diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 1a45d47e45..618227f807 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.8-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + ## 0.4.8-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index b577a11ba8..7a54b8b22d 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.8-next.2", + "version": "0.4.8-next.3", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts index f9246d01dd..97857559d1 100644 --- a/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts +++ b/plugins/catalog-backend-module-aws/src/providers/AwsS3EntityProvider.ts @@ -146,12 +146,12 @@ export class AwsS3EntityProvider implements EntityProvider { }; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName(): string { return `awsS3-provider:${this.config.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection): Promise { this.connection = connection; const { accountId, region, bucketName } = this.config; diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index a23e789593..8042929d71 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.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.3.2-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 7c49fc13d8..4200fb5353 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.2-next.2", + "version": "0.3.2-next.3", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts index cd3b4bbbad..2b6070539f 100644 --- a/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts +++ b/plugins/catalog-backend-module-azure/src/providers/AzureDevOpsEntityProvider.ts @@ -138,12 +138,12 @@ export class AzureDevOpsEntityProvider implements EntityProvider { }; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName(): string { return `AzureDevOpsEntityProvider:${this.config.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection): Promise { this.connection = connection; await this.scheduleFn(); diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index 5a562b0209..3faa4ad63f 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.4.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.4.5-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index 7ac41566af..e7325a184f 100644 --- a/plugins/catalog-backend-module-backstage-openapi/package.json +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", - "version": "0.4.5-next.2", + "version": "0.4.5-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-backstage-openapi/src/InternalOpenApiDocumentationProvider.ts b/plugins/catalog-backend-module-backstage-openapi/src/InternalOpenApiDocumentationProvider.ts index 959d07ad76..3f5253d102 100644 --- a/plugins/catalog-backend-module-backstage-openapi/src/InternalOpenApiDocumentationProvider.ts +++ b/plugins/catalog-backend-module-backstage-openapi/src/InternalOpenApiDocumentationProvider.ts @@ -194,12 +194,12 @@ export class InternalOpenApiDocumentationProvider implements EntityProvider { taskRunner, ); } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName() { return `InternalOpenApiDocumentationProvider`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection) { this.connection = connection; return await this.scheduleFn(); diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 0517cba616..19d488d227 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.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.27 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.4.5-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 25f1a801ad..45b270a875 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.5-next.2", + "version": "0.4.5-next.3", "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-cloud/src/providers/BitbucketCloudEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProvider.ts index 6c2de0ac10..e4ba2f2dc0 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProvider.ts @@ -169,17 +169,17 @@ export class BitbucketCloudEntityProvider implements EntityProvider { }; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName(): string { return `bitbucketCloud-provider:${this.config.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getTaskId} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getTaskId} */ getTaskId(): string { return `${this.getProviderName()}:refresh`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection): Promise { this.connection = connection; await this.scheduleFn(); diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 15961b0521..fc98db3388 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.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.3.2-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 5c9c3f9831..9f008f7e0f 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.2-next.2", + "version": "0.3.2-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index 8e26d6a866..46f492d64c 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -145,12 +145,12 @@ export class BitbucketServerEntityProvider implements EntityProvider { }; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName(): string { return `bitbucketServer-provider:${this.config.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection): Promise { this.connection = connection; await this.scheduleFn(); diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 9a8481eb29..a0b8aa05ea 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + ## 0.3.5-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 3e40a3d0f1..611f90c6ef 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.3.5-next.2", + "version": "0.3.5-next.3", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 53433b4c56..be13e05e09 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.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.2.7-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 64dc0feb33..7434dec6b6 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.7-next.2", + "version": "0.2.7-next.3", "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 21832d6c43..4870b9f458 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.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-catalog-backend-module-github@0.7.10-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + ## 0.3.7-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index fc1633c1f2..9c9ebe84f7 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.7-next.2", + "version": "0.3.7-next.3", "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 86da279117..a2bb1d04c8 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.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.7.10-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 4f79935239..9656f79762 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.10-next.2", + "version": "0.7.10-next.3", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts index a18846b157..e0837de54e 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts @@ -158,12 +158,12 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { SingleInstanceGithubCredentialsProvider.create(integration.config); } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName(): string { return `github-provider:${this.config.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection): Promise { this.connection = connection; await this.events?.subscribe({ diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts index fbfdc8a20d..62b997d7e2 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts @@ -232,12 +232,12 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { }, ) {} - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName() { return `GithubMultiOrgEntityProvider:${this.options.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection) { this.connection = connection; await this.options.events?.subscribe({ diff --git a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts index a8dc045567..6dfa8a07f8 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubOrgEntityProvider.ts @@ -191,12 +191,12 @@ export class GithubOrgEntityProvider implements EntityProvider { SingleInstanceGithubCredentialsProvider.create(this.options.gitHubConfig); } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName() { return `GithubOrgEntityProvider:${this.options.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection) { this.connection = connection; await this.options.events?.subscribe({ diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 90a5f5018c..7267dd1572 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.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-catalog-backend-module-gitlab@0.6.3-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + ## 0.2.6-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 8247261732..b66983d2b4 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.6-next.2", + "version": "0.2.6-next.3", "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 339952af93..77230d4831 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.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.6.3-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index a50b017a11..c3ea323bdc 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.3-next.2", + "version": "0.6.3-next.3", "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 32d7bcf87c..b7c9d2b181 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.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + ## 0.6.3-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 9c4804c841..2172a45894 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.3-next.2", + "version": "0.6.3-next.3", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 61a23dd26d..6e943516f8 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.11.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @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 + ## 0.11.2-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index b92331dc80..04393f0f03 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.2-next.2", + "version": "0.11.2-next.3", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts index a8d27855dc..5edd3b06d0 100644 --- a/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-ldap/src/processors/LdapOrgEntityProvider.ts @@ -257,12 +257,12 @@ export class LdapOrgEntityProvider implements EntityProvider { }, ) {} - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName() { return `LdapOrgEntityProvider:${this.options.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection) { this.connection = connection; await this.scheduleFn?.(); diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index 9901c9e364..92a33ed1df 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.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.31.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + ## 0.1.7-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index 87c26abd5a..aaeccd38d7 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.7-next.2", + "version": "0.1.7-next.3", "description": "A module that subscribes to catalog releated events and logs them.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 05d2ddb7b6..e97cf8159f 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.6.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.6.7-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index fa56a87c2e..952a761ab9 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.6.7-next.2", + "version": "0.6.7-next.3", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts index 4c92a5f17e..c490c0473d 100644 --- a/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/processors/MicrosoftGraphOrgEntityProvider.ts @@ -301,12 +301,12 @@ export class MicrosoftGraphOrgEntityProvider implements EntityProvider { }, ) {} - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ getProviderName() { return `MicrosoftGraphOrgEntityProvider:${this.options.id}`; } - /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ async connect(connection: EntityProviderConnection) { this.connection = connection; await this.scheduleFn?.(); diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 1ae81d51e5..2683cf69d8 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.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + ## 0.2.7-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 3289b5b37c..e76343cc17 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.7-next.2", + "version": "0.2.7-next.3", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index f13655e736..95f9ec5b74 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.2.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.2.7-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index fd99eae02a..eb1e7ce38e 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", - "version": "0.2.7-next.2", + "version": "0.2.7-next.3", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index 9586d25b19..4868da1ec8 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.2.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-scaffolder-common@1.5.9 + ## 0.2.5-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index d90b02acb2..3d2c9c822d 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.2.5-next.2", + "version": "0.2.5-next.3", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 84f5ad70fb..8a2f43888e 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.5.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.7 + - @backstage/plugin-permission-common@0.8.4 + ## 0.5.5-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index a4c2963f10..a3271019b7 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", - "version": "0.5.5-next.2", + "version": "0.5.5-next.3", "description": "Backstage Catalog module to view unprocessed entities", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 45f32473c5..94e75da1cf 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-catalog-backend +## 1.31.0-next.3 + +### Minor Changes + +- 5aebc13: The `UrlReaderProccessor` accepts a new config flag `catalog.useUrlReadersSearch` to always call the `search` method of `UrlReaders`. + + This flag currently defaults to `false`, but adopters are encouraged to enable it as this behavior will be the default in a future release. + + Previous behavior was to call the `search` method only if the parsed Git URL's filename contained a wildcard and use `readUrl` otherwise. `UrlReaderService` must implement this logic in the `search` method instead. + + This allows each `UrlReaderService` implementation to check whether it's a search URL (that contains a wildcard pattern) or not using logic that is specific to each provider. + +### Patch Changes + +- ef73f97: Updated permission integration to use new permission resource ref. +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-module-catalog@0.3.1-next.3 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 1.31.0-next.2 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 0ffd2587ca..ee61f35fe1 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "1.31.0-next.2", + "version": "1.31.0-next.3", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index ddf4b5d185..79121c86c5 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -26,9 +26,7 @@ import { FieldFormatEntityPolicy, makeValidator, NoForeignRootFieldsEntityPolicy, - parseEntityRef, SchemaValidEntityPolicy, - stringifyEntityRef, Validators, } from '@backstage/catalog-model'; import { ScmIntegrations } from '@backstage/integration'; @@ -116,7 +114,6 @@ import { createRouter } from './createRouter'; import { DefaultEntitiesCatalog } from './DefaultEntitiesCatalog'; import { DefaultLocationService } from './DefaultLocationService'; import { DefaultRefreshService } from './DefaultRefreshService'; -import { basicEntityFilter } from './request'; import { entitiesResponseToObjects } from './response'; import { catalogEntityPermissionResourceRef } from '@backstage/plugin-catalog-node/alpha'; @@ -381,7 +378,7 @@ export class CatalogBuilder { return [ new FileReaderProcessor(), - new UrlReaderProcessor({ reader, logger, config }), + new UrlReaderProcessor({ reader, logger }), CodeOwnersProcessor.fromConfig(config, { logger, reader }), new AnnotateLocationEntityProcessor({ integrations }), ]; @@ -559,60 +556,35 @@ export class CatalogBuilder { const entitiesCatalog = new AuthorizedEntitiesCatalog( unauthorizedEntitiesCatalog, permissionsService, - permissionsRegistry - ? createConditionTransformer( - permissionsRegistry.getPermissionRuleset( - catalogEntityPermissionResourceRef, - ), - ) - : createConditionTransformer(this.permissionRules), + createConditionTransformer(this.permissionRules), ); - const catalogPermissionResource = { - resourceType: RESOURCE_TYPE_CATALOG_ENTITY, - getResources: async (resourceRefs: string[]) => { - const { entities } = await unauthorizedEntitiesCatalog.entities({ - credentials: await auth.getOwnServiceCredentials(), - filter: { - anyOf: resourceRefs.map(resourceRef => { - const { kind, namespace, name } = parseEntityRef(resourceRef); + const getResources = async (resourceRefs: string[]) => { + const { items } = await unauthorizedEntitiesCatalog.entitiesBatch({ + credentials: await auth.getOwnServiceCredentials(), + entityRefs: resourceRefs, + }); - return basicEntityFilter({ - kind, - 'metadata.namespace': namespace, - 'metadata.name': name, - }); - }), - }, - }); - - const entitiesByRef = Object.fromEntries( - entitiesResponseToObjects(entities) - .filter((x): x is Entity => Boolean(x)) - .map(entity => [stringifyEntityRef(entity), entity]), - ); - - return resourceRefs.map( - resourceRef => - entitiesByRef[stringifyEntityRef(parseEntityRef(resourceRef))], - ); - }, - permissions: this.permissions, - rules: this.permissionRules, - } as const; + return entitiesResponseToObjects(items).map(e => e || undefined); + }; let permissionIntegrationRouter: | ReturnType | undefined; if (permissionsRegistry) { permissionsRegistry.addResourceType({ - ...catalogPermissionResource, resourceRef: catalogEntityPermissionResourceRef, + getResources, + permissions: this.permissions, + rules: this.permissionRules, }); } else { - permissionIntegrationRouter = createPermissionIntegrationRouter( - catalogPermissionResource, - ); + permissionIntegrationRouter = createPermissionIntegrationRouter({ + resourceType: RESOURCE_TYPE_CATALOG_ENTITY, + getResources, + permissions: this.permissions, + rules: this.permissionRules, + }); } const locationStore = new DefaultLocationStore(dbClient); diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 825bfde1a7..447964fafc 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-graph +## 0.4.16-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-react@1.15.2-next.3 + ## 0.4.16-next.2 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index b07e35e03e..59c075f7cd 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.16-next.2", + "version": "0.4.16-next.3", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 8ebc316190..ebe5cc231a 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.12.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + ## 0.12.10-next.2 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 8d503a847d..32b4efa2b7 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.10-next.2", + "version": "0.12.10-next.3", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 5dde24abe8..fbca2424c8 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-node +## 1.16.0-next.3 + +### Minor Changes + +- b4183ad: Added new `catalogEntityPermissionResourceRef` export via the `/alpha` sub-path. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + ## 1.16.0-next.2 ### Patch Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index f0972ec820..ec9f831ca3 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "1.16.0-next.2", + "version": "1.16.0-next.3", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 4ead6479f3..1bb556e87f 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-react +## 1.15.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-test-utils@0.2.6-next.3 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-permission-react@0.4.31-next.0 + ## 1.15.2-next.2 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 7af65bde71..ceb366f6fc 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.15.2-next.2", + "version": "1.15.2-next.3", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index c2e5109d5c..ebadbb7054 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -44,6 +44,26 @@ import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unproc />; ``` +### Integrating with the New Frontend System + +Follow this section if you are using Backstage's [new frontend system](https://backstage.io/docs/frontend-system/). + +Import `catalogUnprocessedEntitiesPlugin` in your `App.tsx` and add it to your app's `features` array: + +```typescript +import catalogUnprocessedEntitiesPlugin from '@backstage/plugin-catalog-unprocessed-entities'; + +// ... + +export const app = createApp({ + features: [ + // ... + catalogUnprocessedEntitiesPlugin, + // ... + ], +}); +``` + ## Customization If you want to use the provided endpoints in a different way, you can use the ApiRef doing the following: diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index 7bdf6da524..5c22053871 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -10,9 +10,7 @@ ] }, "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" }, "homepage": "https://backstage.io", "repository": { @@ -22,8 +20,23 @@ }, "license": "Apache-2.0", "sideEffects": false, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, "main": "src/index.ts", "types": "src/index.ts", + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, "files": [ "dist" ], @@ -38,9 +51,11 @@ }, "dependencies": { "@backstage/catalog-model": "workspace:^", + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.60", diff --git a/plugins/catalog-unprocessed-entities/report-alpha.api.md b/plugins/catalog-unprocessed-entities/report-alpha.api.md new file mode 100644 index 0000000000..6758179ffb --- /dev/null +++ b/plugins/catalog-unprocessed-entities/report-alpha.api.md @@ -0,0 +1,93 @@ +## API Report File for "@backstage/plugin-catalog-unprocessed-entities" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AnyApiFactory } from '@backstage/frontend-plugin-api'; +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; +import { IconComponent } from '@backstage/core-plugin-api'; +import { default as React_2 } from 'react'; +import { RouteRef } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +const _default: FrontendPlugin< + { + root: RouteRef; + }, + {}, + { + 'page:catalog-unprocessed-entities': ExtensionDefinition<{ + kind: 'page'; + name: undefined; + config: { + path: string | undefined; + }; + configInput: { + path?: string | undefined; + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; + }>; + 'nav-item:catalog-unprocessed-entities': ExtensionDefinition<{ + kind: 'nav-item'; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }, + 'core.nav-item.target', + {} + >; + inputs: {}; + params: { + title: string; + icon: IconComponent; + routeRef: RouteRef; + }; + }>; + 'api:catalog-unprocessed-entities': ExtensionDefinition<{ + kind: 'api'; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + params: { + factory: AnyApiFactory; + }; + }>; + } +>; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/catalog-unprocessed-entities/src/alpha.ts b/plugins/catalog-unprocessed-entities/src/alpha.ts new file mode 100644 index 0000000000..e80f131817 --- /dev/null +++ b/plugins/catalog-unprocessed-entities/src/alpha.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './alpha/index'; +export { default } from './alpha/index'; diff --git a/plugins/catalog-unprocessed-entities/src/alpha/index.ts b/plugins/catalog-unprocessed-entities/src/alpha/index.ts new file mode 100644 index 0000000000..2f137f09ee --- /dev/null +++ b/plugins/catalog-unprocessed-entities/src/alpha/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './plugin'; diff --git a/plugins/catalog-unprocessed-entities/src/alpha/plugin.tsx b/plugins/catalog-unprocessed-entities/src/alpha/plugin.tsx new file mode 100644 index 0000000000..85c021510c --- /dev/null +++ b/plugins/catalog-unprocessed-entities/src/alpha/plugin.tsx @@ -0,0 +1,86 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { + createApiFactory, + createFrontendPlugin, + discoveryApiRef, + fetchApiRef, + ApiBlueprint, + PageBlueprint, + NavItemBlueprint, +} from '@backstage/frontend-plugin-api'; + +import { + catalogUnprocessedEntitiesApiRef, + CatalogUnprocessedEntitiesClient, +} from '../api'; +import { + compatWrapper, + convertLegacyRouteRef, +} from '@backstage/core-compat-api'; +import QueueIcon from '@material-ui/icons/Queue'; +import { rootRouteRef } from '../routes'; + +/** @alpha */ +export const catalogUnprocessedEntitiesApi = ApiBlueprint.make({ + params: { + factory: createApiFactory({ + api: catalogUnprocessedEntitiesApiRef, + deps: { + discoveryApi: discoveryApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ discoveryApi, fetchApi }) => + new CatalogUnprocessedEntitiesClient(discoveryApi, fetchApi), + }), + }, +}); + +/** @alpha */ +export const catalogUnprocessedEntitiesPage = PageBlueprint.make({ + params: { + defaultPath: '/catalog-unprocessed-entities', + routeRef: convertLegacyRouteRef(rootRouteRef), + loader: () => + import('../components/UnprocessedEntities').then(m => + compatWrapper(), + ), + }, +}); + +/** @alpha */ +export const catalogUnprocessedEntitiesNavItem = NavItemBlueprint.make({ + params: { + title: 'Unprocessed Entities', + routeRef: convertLegacyRouteRef(rootRouteRef), + icon: QueueIcon, + }, +}); + +/** @alpha */ +export default createFrontendPlugin({ + id: 'catalog-unprocessed-entities', + routes: { + root: convertLegacyRouteRef(rootRouteRef), + }, + extensions: [ + catalogUnprocessedEntitiesApi, + catalogUnprocessedEntitiesPage, + catalogUnprocessedEntitiesNavItem, + ], +}); diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 5ada90b470..e58acd4616 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog +## 1.27.0-next.3 + +### Patch Changes + +- 7a15cff: Revert client side paginated catalog table to using built in Material Table toolbar component +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-scaffolder-common@1.5.9 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + ## 1.27.0-next.2 ### Minor Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index a6436edbb4..169b651e28 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.27.0-next.2", + "version": "1.27.0-next.3", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index c565cffa93..0b2d2e4a4c 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -49,7 +49,6 @@ import { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { catalogTranslationRef } from '../../alpha/translation'; import { FavoriteToggleIcon } from '@backstage/core-components'; -import { CatalogTableToolbar } from './CatalogTableToolbar'; /** * Props for {@link CatalogTable}. @@ -247,9 +246,6 @@ export const CatalogTable = (props: CatalogTableProps) => { pageSizeOptions: [20, 50, 100], ...options, }} - components={{ - Toolbar: CatalogTableToolbar, - }} title={title} data={rows} actions={actions} diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index b77e1d3280..9215c7e491 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-devtools-backend +## 0.5.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config-loader@1.9.6-next.0 + - @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 + ## 0.5.2-next.2 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 67fd3d7ea6..1fc78d86fe 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.2-next.2", + "version": "0.5.2-next.3", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index 4102037cc2..efae416f21 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-devtools +## 0.1.24-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.15 + - @backstage/plugin-permission-react@0.4.31-next.0 + ## 0.1.24-next.2 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 1b36569cc2..3095f96350 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.24-next.2", + "version": "0.1.24-next.3", "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 e4bfcb9c0b..bbccb3820f 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.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.4.8-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 fc4a61e422..60ad635023 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.8-next.1", + "version": "0.4.8-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 11160c410c..26a529ca1c 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.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + ## 0.2.17-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 9465a02eac..88c5d416bc 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.17-next.1", + "version": "0.2.17-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 81d9ee188a..f97260efb9 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.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + ## 0.2.17-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 82bbe3c003..8236f076a3 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.17-next.1", + "version": "0.2.17-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 12b474da81..75792f9258 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.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + ## 0.2.17-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 7050e052a7..4cd56e246c 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.17-next.1", + "version": "0.2.17-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 365af7db7d..22a818ad22 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.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + ## 0.2.17-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index 1c896f5513..190aef30a2 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.17-next.1", + "version": "0.2.17-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 7d7d4695f4..22483534bf 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.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + ## 0.2.17-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index bcb9d118b7..701cfa818c 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.17-next.1", + "version": "0.2.17-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 848528af2a..3057058d43 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.41-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.8-next.2 + ## 0.1.41-next.1 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index 9cea3585f7..ca07c624ea 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.41-next.1", + "version": "0.1.41-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 dd172d0ad2..ee6fb3d28e 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-events-backend +## 0.4.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.2-next.2 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index b4d9d89c3f..7e8c3c8f84 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.2-next.2", + "version": "0.4.2-next.3", "backstage": { "role": "backend-plugin", "pluginId": "events", diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 58395bd1e5..70829c3e19 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-node +## 0.4.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.8-next.1 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index f691c4ad5b..3d859f49a0 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.8-next.1", + "version": "0.4.8-next.2", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index 05ec8db28c..05c8c16829 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-todo-list-backend +## 1.0.36-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/errors@1.2.7 + ## 1.0.36-next.1 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index 7d0247cfdc..3589a2cd30 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-backend", - "version": "1.0.36-next.1", + "version": "1.0.36-next.2", "backstage": { "role": "backend-plugin", "pluginId": "todo-list", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 6c04aed32b..1f736314c6 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-home +## 0.8.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-home-react@0.1.23-next.1 + ## 0.8.5-next.2 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 58e3432707..e4fb7007e1 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.8.5-next.2", + "version": "0.8.5-next.3", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 6127e1d143..8d67126a35 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-kubernetes-backend +## 0.19.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-kubernetes-node@0.2.3-next.2 + - @backstage/catalog-client@1.9.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/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + - @backstage/plugin-permission-common@0.8.4 + ## 0.19.3-next.2 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index bf0681fb0c..76e053f4ac 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.19.3-next.2", + "version": "0.19.3-next.3", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index eebe63bec4..4e5e9a0bdc 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.22-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + - @backstage/plugin-kubernetes-react@0.5.4-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + ## 0.0.22-next.2 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 75a0d461f4..c54dfa1790 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.22-next.2", + "version": "0.0.22-next.3", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 1351de9073..ddb91a7dd0 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-node +## 0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + ## 0.2.3-next.1 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 9f48204708..e3b47b97a5 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.2.3-next.1", + "version": "0.2.3-next.2", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 14661aa41f..2920aa7e33 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-kubernetes +## 0.12.4-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-kubernetes-common@0.9.3-next.1 + - @backstage/plugin-kubernetes-react@0.5.4-next.2 + - @backstage/plugin-permission-react@0.4.31-next.0 + ## 0.12.4-next.2 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 6a7aef69ea..6b8abed1a6 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.4-next.2", + "version": "0.12.4-next.3", "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 e00d41201c..f676ab813c 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.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-notifications-node@0.2.12-next.2 + - @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-notifications-common@0.0.8 + ## 0.3.6-next.2 ### Patch Changes diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index dd3e4dbb78..9a11ce8187 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.6-next.2", + "version": "0.3.6-next.3", "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 216570fde6..a7f9ffbc94 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-notifications-backend +## 0.5.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-notifications-node@0.2.12-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-node@0.1.17-next.2 + ## 0.5.2-next.2 ### Patch Changes diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index 582421b25e..a49fe7c763 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.2-next.2", + "version": "0.5.2-next.3", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index 1eb8c07da5..6ae8ccc07c 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-notifications-node +## 0.2.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @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.17-next.2 + ## 0.2.12-next.1 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index 6994e92d95..b1cec6f5f6 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.12-next.1", + "version": "0.2.12-next.2", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index 38765e5acd..b6d3b7af7c 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -12,9 +12,7 @@ ] }, "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" }, "repository": { "type": "git", @@ -23,8 +21,23 @@ }, "license": "Apache-2.0", "sideEffects": false, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.tsx", + "./package.json": "./package.json" + }, "main": "src/index.ts", "types": "src/index.ts", + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.tsx" + ], + "package.json": [ + "package.json" + ] + } + }, "files": [ "dist" ], @@ -38,9 +51,11 @@ "test": "backstage-cli package test" }, "dependencies": { + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-notifications-common": "workspace:^", "@backstage/plugin-signals-react": "workspace:^", "@backstage/theme": "workspace:^", diff --git a/plugins/notifications/report-alpha.api.md b/plugins/notifications/report-alpha.api.md new file mode 100644 index 0000000000..96bb31a925 --- /dev/null +++ b/plugins/notifications/report-alpha.api.md @@ -0,0 +1,71 @@ +## API Report File for "@backstage/plugin-notifications" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AnyApiFactory } from '@backstage/frontend-plugin-api'; +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; +import { default as React_2 } from 'react'; +import { RouteRef } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +const _default: FrontendPlugin< + { + root: RouteRef; + }, + {}, + { + 'page:notifications': ExtensionDefinition<{ + kind: 'page'; + name: undefined; + config: { + path: string | undefined; + }; + configInput: { + path?: string | undefined; + }; + output: + | ConfigurableExtensionDataRef< + React_2.JSX.Element, + 'core.reactElement', + {} + > + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + params: { + defaultPath: string; + loader: () => Promise; + routeRef?: RouteRef | undefined; + }; + }>; + 'api:notifications': ExtensionDefinition<{ + kind: 'api'; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + params: { + factory: AnyApiFactory; + }; + }>; + } +>; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/notifications/src/alpha.tsx b/plugins/notifications/src/alpha.tsx new file mode 100644 index 0000000000..60f2136533 --- /dev/null +++ b/plugins/notifications/src/alpha.tsx @@ -0,0 +1,63 @@ +/* + * 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'; +import { + ApiBlueprint, + PageBlueprint, + createApiFactory, + createFrontendPlugin, + discoveryApiRef, + fetchApiRef, +} from '@backstage/frontend-plugin-api'; +import { rootRouteRef } from './routes'; +import { + convertLegacyRouteRef, + convertLegacyRouteRefs, +} from '@backstage/core-compat-api'; +import { NotificationsClient, notificationsApiRef } from './api'; + +const page = PageBlueprint.make({ + params: { + defaultPath: '/notifications', + routeRef: convertLegacyRouteRef(rootRouteRef), + loader: () => + import('./components/NotificationsPage').then(m => ( + + )), + }, +}); + +const api = ApiBlueprint.make({ + params: { + factory: createApiFactory({ + api: notificationsApiRef, + deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef }, + factory: ({ discoveryApi, fetchApi }) => + new NotificationsClient({ discoveryApi, fetchApi }), + }), + }, +}); + +/** @alpha */ +export default createFrontendPlugin({ + id: 'notifications', + routes: convertLegacyRouteRefs({ + root: rootRouteRef, + }), + // TODO(Rugvip): Nav item (i.e. NotificationsSidebarItem) currently needs to be installed manually + extensions: [page, api], +}); diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index dba060cf7b..84a0731779 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.35-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + ## 0.1.35-next.2 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 383865b984..68b7a7c82f 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.35-next.2", + "version": "0.1.35-next.3", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index e483f8e578..9614d54993 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-org +## 0.6.36-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + ## 0.6.36-next.2 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index ff75e37bf0..a1e94245ff 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.36-next.2", + "version": "0.6.36-next.3", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 9ce9838346..e566cd8e37 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.2.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-permission-common@0.8.4 + ## 0.2.5-next.1 ### Patch Changes diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index 68738f8ed4..2569ff6d26 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", - "version": "0.2.5-next.1", + "version": "0.2.5-next.2", "description": "Allow all policy backend module for the permission plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 44376eee11..44c79bf098 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-backend +## 0.5.54-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.8.4 + ## 0.5.54-next.1 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 520bacd2c0..60c20573be 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.5.54-next.1", + "version": "0.5.54-next.2", "backstage": { "role": "backend-plugin", "pluginId": "permission", diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index f892a0d9ed..fff5f924a3 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-permission-node +## 0.8.8-next.2 + +### Patch Changes + +- b71f634: Added a new `PermissionRuleset` type that encapsulates a lookup function for permission rules, which can be created by the new `PermissionsRegistryService` via the `getPermissionRuleset` method. The `createConditionTransformer` and `createConditionAuthorizer` functions have been adapted to receive these accessors as arguments, with their older counterparts being deprecated. +- a9621de: Added a new `createPermissionResourceRef` utility that encapsulates the constants and types related to a permission resource types. The `createConditionExports` and `createPermissionRule` functions have also been adapted to accept these references as arguments, deprecating their older counterparts. +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.8.4 + ## 0.8.8-next.1 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 93b22aa203..33a69157a3 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-node", - "version": "0.8.8-next.1", + "version": "0.8.8-next.2", "description": "Common permission and authorization utilities for backend plugins", "backstage": { "role": "node-library", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 1fec970464..fae1447117 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-proxy-backend +## 0.5.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-proxy-node@0.1.1-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.5.11-next.1 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index f054e5f938..a054f6f9dd 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.5.11-next.1", + "version": "0.5.11-next.2", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", "backstage": { "role": "backend-plugin", diff --git a/plugins/proxy-node/CHANGELOG.md b/plugins/proxy-node/CHANGELOG.md index 1bc35db5d4..20c7acc278 100644 --- a/plugins/proxy-node/CHANGELOG.md +++ b/plugins/proxy-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-proxy-node +## 0.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + ## 0.1.1-next.1 ### Patch Changes diff --git a/plugins/proxy-node/package.json b/plugins/proxy-node/package.json index e0f4b79213..d899e3232c 100644 --- a/plugins/proxy-node/package.json +++ b/plugins/proxy-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-node", - "version": "0.1.1-next.1", + "version": "0.1.1-next.2", "description": "The plugin-proxy-node module for @backstage/plugin-proxy-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index fabf83c3b8..3cd0294a08 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.2.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index d036686678..ebcfb27c26 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.6-next.1", + "version": "0.2.6-next.2", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index 0150ce1a7f..972673ab17 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.27 + ## 0.2.6-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 7712e056b6..ace869f572 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.6-next.1", + "version": "0.2.6-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-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index cf34c6ecf9..8b772bab7b 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.2.6-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 fa95a6622d..192a4eba0d 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.6-next.1", + "version": "0.2.6-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/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index 855f5f91ec..e8e8f4fce3 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.3.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index 15ac989a5b..37baa1e348 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.7-next.1", + "version": "0.3.7-next.2", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 34dc13e371..acdf3d476a 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.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.3.6-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 a2fd1bacb4..00d3043530 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.6-next.1", + "version": "0.3.6-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-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 02391279ca..3885cc7487 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.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + ## 0.3.7-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 125ed7e284..898962dc12 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.7-next.2", + "version": "0.3.7-next.3", "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-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index ec15e14da6..636a13b097 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.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.2.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 244a764a5f..d9e1fedc0b 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.6-next.1", + "version": "0.2.6-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 df0a1b1083..b176dcfb0b 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.2.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index f4f634f781..8683b2076f 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.6-next.1", + "version": "0.2.6-next.2", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index 19e6d88c48..7bb14dfa9b 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.2.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 25ea683553..fb593d04a0 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.6-next.1", + "version": "0.2.6-next.2", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 2b75f9c8ce..6bd9c3636c 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.6.0-next.2 + +### Minor Changes + +- bb8302b: **BREAKING**: The `remoteUrl` output is no longer required, it can be empty only when using the new `createWhenEmpty` boolean flag. + +### Patch Changes + +- 5c187f9: **DEPRECATION**: The `getOctokitOptions` function signature with `repoUrl` option has been deprecated in favour of a function signature with individual `host`, `owner`, and `repo` parameters: + + ```diff + const octokitOptions = await getOctokitOptions({ + integrations, + credentialsProvider, + token, + - repoUrl, + + host, + + owner, + + repo, + }); + ``` + +- 5d469c9: Added support for autocompletion of GitHub branches in scaffolder +- 8e67e4a: Added support for autocompletion to GithubRepoPicker component +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.5.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 4a75fad7b3..aa425e5507 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.5.6-next.1", + "version": "0.6.0-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 476e8fde1e..7e8759db60 100644 --- a/plugins/scaffolder-backend-module-github/report.api.md +++ b/plugins/scaffolder-backend-module-github/report.api.md @@ -469,6 +469,7 @@ export const createPublishGithubPullRequestAction: ( gitAuthorName?: string | undefined; gitAuthorEmail?: string | undefined; forceEmptyGitAuthor?: boolean | undefined; + createWhenEmpty?: boolean | undefined; }, JsonObject >; diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.test.ts index 26469dfcb4..fb2763db51 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.test.ts @@ -617,13 +617,7 @@ describe('publish:github:pull-request examples', () => { expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); }); - it('Create a pull request with all parameters', async () => { - mockDir.setContent({ - [workspacePath]: { - source: { 'foo.txt': 'Hello there!' }, - irrelevant: { 'bar.txt': 'Nothing to see here' }, - }, - }); + it('Does not create an empty pull request', async () => { const input = yaml.parse(examples[12].example).steps[0].input; await action.handler({ @@ -638,8 +632,56 @@ describe('publish:github:pull-request examples', () => { title: 'Create my new app', body: 'This PR is really good', head: 'new-app', - draft: true, + draft: undefined, + createWhenEmpty: false, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with all parameters', async () => { + mockDir.setContent({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + const input = yaml.parse(examples[13].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', base: 'test', + draft: true, + createWhenEmpty: true, changes: [ { commit: 'Commit for foo changes', diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.ts index b6d31e732b..e35e6d9a81 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.examples.ts @@ -23,7 +23,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest', + name: 'Create a pull request', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -40,7 +40,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest with target branch name', + name: 'Create a pull request with target branch name', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -58,7 +58,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest as draft', + name: 'Create a pull request as draft', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -76,7 +76,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest with target path', + name: 'Create a pull request with target path', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -94,7 +94,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest with source path', + name: 'Create a pull request with source path', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -112,7 +112,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest', + name: 'Create a pull request', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -130,7 +130,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest with reviewers', + name: 'Create a pull request with reviewers', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -148,7 +148,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest with team reviewers', + name: 'Create a pull request with team reviewers', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -166,7 +166,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest', + name: 'Create a pull request', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -184,7 +184,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest', + name: 'Create a pull request', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -203,7 +203,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest', + name: 'Create a pull request', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -223,7 +223,7 @@ export const examples: TemplateExample[] = [ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest', + name: 'Create a pull request', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -237,13 +237,31 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: 'Do not create empty pull request', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull request', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + createWhenEmpty: false, + }, + }, + ], + }), + }, { description: 'Create a pull request with all parameters', example: yaml.stringify({ steps: [ { action: 'publish:github:pull-request', - name: 'Create a pull reuqest', + name: 'Create a pull request', input: { repoUrl: 'github.com?repo=repo&owner=owner', branchName: 'new-app', @@ -259,6 +277,7 @@ export const examples: TemplateExample[] = [ commitMessage: 'Commit for foo changes', gitAuthorName: 'Foo Bar', gitAuthorEmail: 'foo@bar.example', + createWhenEmpty: true, }, }, ], diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts index ccb1efa29b..9133b0d300 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts @@ -1062,4 +1062,119 @@ describe('createPublishGithubPullRequestAction', () => { }); }); }); + + describe('with createWhenEmpty equals true', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext; + + beforeEach(() => { + input = { + repoUrl: 'github.com?owner=myorg&repo=myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + createWhenEmpty: true, + }; + + mockDir.setContent({ + [workspacePath]: { 'file.txt': 'Hello there!' }, + }); + + ctx = createMockActionContext({ input, workspacePath }); + }); + it('creates a pull request', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + head: 'new-app', + body: 'This PR is really good', + createWhenEmpty: true, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + }); + + it('creates outputs for the pull request url and number', async () => { + await instance.handler(ctx); + + expect(ctx.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(ctx.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('throws when creating a pull request fails', async () => { + fakeClient.createPullRequest.mockResolvedValueOnce(null); + + await expect(instance.handler(ctx)).rejects.toThrow( + 'null response from Github', + ); + }); + }); + + describe('with createWhenEmpty equals false', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext; + + beforeEach(() => { + fakeClient.createPullRequest.mockResolvedValueOnce(null); + input = { + repoUrl: 'github.com?owner=myorg&repo=myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + createWhenEmpty: false, + }; + + mockDir.setContent({ + [workspacePath]: { 'file.txt': 'Hello there!' }, + }); + + ctx = createMockActionContext({ input, workspacePath }); + }); + it('creates a pull request', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + head: 'new-app', + body: 'This PR is really good', + createWhenEmpty: false, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + }); + + it('does not create outputs for the pull request url and number', async () => { + await instance.handler(ctx); + + expect(ctx.output).not.toHaveBeenCalled(); + }); + }); }); diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts index 44830b3047..c9a1b3d1b5 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts @@ -145,6 +145,7 @@ export const createPublishGithubPullRequestAction = ( gitAuthorName?: string; gitAuthorEmail?: string; forceEmptyGitAuthor?: boolean; + createWhenEmpty?: boolean; }>({ id: 'publish:github:pull-request', examples, @@ -251,10 +252,16 @@ export const createPublishGithubPullRequestAction = ( description: 'Forces the author to be empty. This is useful when using a Github App, it permit the commit to be verified on Github', }, + createWhenEmpty: { + type: 'boolean', + title: 'Create When Empty', + description: + 'Set whether to create pull request when there are no changes to commit. The default value is true. If set to false, remoteUrl is no longer a required output.', + }, }, }, output: { - required: ['remoteUrl'], + required: [], type: 'object', properties: { targetBranchName: { @@ -293,6 +300,7 @@ export const createPublishGithubPullRequestAction = ( gitAuthorEmail, gitAuthorName, forceEmptyGitAuthor, + createWhenEmpty, } = ctx.input; const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); @@ -379,6 +387,7 @@ export const createPublishGithubPullRequestAction = ( draft, update, forceFork, + createWhenEmpty, }; const gitAuthorInfo = { @@ -417,6 +426,11 @@ export const createPublishGithubPullRequestAction = ( } const response = await client.createPullRequest(createOptions); + if (createWhenEmpty === false && !response) { + ctx.logger.info('No changes to commit, pull request was not created'); + return; + } + if (!response) { throw new GithubResponseError('null response from Github'); } diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 7f99b391f8..deae46449b 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.8.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + ## 0.8.0-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index fb42f28b63..8ee967aea8 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.0-next.2", + "version": "0.8.0-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index f11e1d0fcd..40981aa76d 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.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-notifications-node@0.2.12-next.2 + - @backstage/plugin-notifications-common@0.0.8 + ## 0.1.7-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index f37400e2e6..1790db3d71 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.7-next.1", + "version": "0.1.7-next.2", "description": "The notifications backend module for the scaffolder plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 441f97bc24..97c2140bce 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.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + ## 0.5.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 9ff19a3f56..601b516f2a 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.6-next.1", + "version": "0.5.6-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-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index cca59946ae..dcedf96100 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.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.6-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 93feb59c08..7ff03ca1aa 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.6-next.1", + "version": "0.2.6-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index ab4458c19d..2dbd1b2deb 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.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-scaffolder-node-test-utils@0.1.19-next.3 + - @backstage/types@1.2.1 + ## 0.4.7-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 84413f88e0..a26882cb97 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.7-next.2", + "version": "0.4.7-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index ee59812a77..4eb9967168 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-scaffolder-backend +## 1.30.0-next.3 + +### Minor Changes + +- dc8dd4b: Added new `createTemplateFilter`, `createTemplateGlobalFunction`, `createTemplateGlobalValue` for template extensions. +- 3d6ef79: Support file globbing in fs:delete + +### Patch Changes + +- 1e935f0: Added conditional rendering of `oneOf` output schemas on the Installed Actions page for scaffolder actions +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.6.0-next.2 + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.3 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.7-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.6-next.2 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.8.0-next.3 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.6-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.27 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-scaffolder-common@1.5.9 + ## 1.30.0-next.2 ### Minor Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 9e45865447..94a5be4b35 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.0-next.2", + "version": "1.30.0-next.3", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index 8fe17d26f8..3e58f8c4db 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -13,6 +13,8 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { BackstageCredentials } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; +import { CreatedTemplateFilter } from '@backstage/plugin-scaffolder-node/alpha'; +import { CreatedTemplateGlobal } from '@backstage/plugin-scaffolder-node/alpha'; import { createGithubActionsDispatchAction as createGithubActionsDispatchAction_2 } from '@backstage/plugin-scaffolder-backend-module-github'; import { createGithubDeployKeyAction as createGithubDeployKeyAction_2 } from '@backstage/plugin-scaffolder-backend-module-github'; import { createGithubEnvironmentAction as createGithubEnvironmentAction_2 } from '@backstage/plugin-scaffolder-backend-module-github'; @@ -345,6 +347,7 @@ export const createPublishGithubPullRequestAction: ( gitAuthorName?: string | undefined; gitAuthorEmail?: string | undefined; forceEmptyGitAuthor?: boolean | undefined; + createWhenEmpty?: boolean | undefined; }, JsonObject >; @@ -549,9 +552,13 @@ export interface RouterOptions { // (undocumented) actions?: TemplateAction_2[]; // (undocumented) - additionalTemplateFilters?: Record; + additionalTemplateFilters?: + | Record + | CreatedTemplateFilter[]; // (undocumented) - additionalTemplateGlobals?: Record; + additionalTemplateGlobals?: + | Record + | CreatedTemplateGlobal[]; // (undocumented) additionalWorkspaceProviders?: Record; // (undocumented) diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index a670c1a1c8..abb35d86c2 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -82,10 +82,27 @@ export const scaffolderPlugin = createBackendPlugin({ const additionalTemplateGlobals: Record = {}; env.registerExtensionPoint(scaffolderTemplatingExtensionPoint, { addTemplateFilters(newFilters) { - Object.assign(additionalTemplateFilters, newFilters); + Object.assign( + additionalTemplateFilters, + Array.isArray(newFilters) + ? Object.fromEntries( + newFilters.map(tf => [tf.id, tf.filter as TemplateFilter]), + ) + : newFilters, + ); }, addTemplateGlobals(newGlobals) { - Object.assign(additionalTemplateGlobals, newGlobals); + Object.assign( + additionalTemplateGlobals, + Array.isArray(newGlobals) + ? Object.fromEntries( + newGlobals.map(g => [ + g.id, + ('value' in g ? g.value : g.fn) as TemplateGlobal, + ]), + ) + : newGlobals, + ); }, }); diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts index b24d859bb7..fea04c0c1c 100644 --- a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts @@ -190,7 +190,8 @@ export class SecureTemplater { if (!Object.hasOwn(templateFilters, filterName)) { return ''; } - const rz = templateFilters[filterName](...args); + const [input, ...rest] = args; + const rz = templateFilters[filterName](input, ...rest); return rz === undefined ? '' : JSON.stringify(rz); }, ); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts index 8aea076b9d..a32249f210 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.examples.ts @@ -33,4 +33,19 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: 'Delete files with wildcard', + example: yaml.stringify({ + steps: [ + { + action: 'fs:delete', + id: 'deleteFiles', + name: 'Delete files', + input: { + files: ['*.txt'], + }, + }, + ], + }), + }, ]; 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 8226133376..f037323c04 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 @@ -95,6 +95,15 @@ describe('fs:delete', () => { ).rejects.toThrow( /Relative path is not allowed to refer to a directory outside its parent/, ); + + await expect( + action.handler({ + ...mockContext, + input: { files: ['../../../**/index.js'] }, + }), + ).rejects.toThrow( + /Relative path is not allowed to refer to a directory outside its parent/, + ); }); it('should call fs.rm with the correct values', async () => { @@ -114,4 +123,25 @@ describe('fs:delete', () => { expect(fileExists).toBe(false); }); }); + + it('should handle wildcards', 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: ['unit-*.js'] }, + }); + + 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 13dfc69525..5a06c7d2aa 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/filesystem/delete.ts @@ -18,6 +18,7 @@ import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { InputError } from '@backstage/errors'; import { resolveSafeChildPath } from '@backstage/backend-plugin-api'; import fs from 'fs-extra'; +import globby from 'globby'; import { examples } from './delete.examples'; /** @@ -52,14 +53,20 @@ export const createFilesystemDeleteAction = () => { } for (const file of ctx.input.files) { - const filepath = resolveSafeChildPath(ctx.workspacePath, file); + const safeFilepath = resolveSafeChildPath(ctx.workspacePath, file); + const resolvedPaths = await globby(safeFilepath, { + cwd: ctx.workspacePath, + absolute: true, + }); - try { - await fs.remove(filepath); - ctx.logger.info(`File ${filepath} deleted successfully`); - } catch (err) { - ctx.logger.error(`Failed to delete file ${filepath}:`, err); - throw err; + for (const filepath of resolvedPaths) { + try { + await fs.remove(filepath); + ctx.logger.info(`File ${filepath} deleted successfully`); + } catch (err) { + ctx.logger.error(`Failed to delete file ${filepath}:`, err); + throw err; + } } } }, diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index c39ee29a1e..bd04eaad1b 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -81,6 +81,8 @@ import { } from '@backstage/plugin-scaffolder-node'; import { AutocompleteHandler, + CreatedTemplateFilter, + CreatedTemplateGlobal, WorkspaceProvider, } from '@backstage/plugin-scaffolder-node/alpha'; import { HumanDuration, JsonObject, JsonValue } from '@backstage/types'; @@ -173,8 +175,12 @@ export interface RouterOptions { */ concurrentTasksLimit?: number; taskBroker?: TaskBroker; - additionalTemplateFilters?: Record; - additionalTemplateGlobals?: Record; + additionalTemplateFilters?: + | Record + | CreatedTemplateFilter[]; + additionalTemplateGlobals?: + | Record + | CreatedTemplateGlobal[]; additionalWorkspaceProviders?: Record; permissions?: PermissionsService; permissionRules?: Array< @@ -363,6 +369,24 @@ export async function createRouter( } const actionRegistry = new TemplateActionRegistry(); + const templateExtensions = { + additionalTemplateFilters: Array.isArray(additionalTemplateFilters) + ? Object.fromEntries( + additionalTemplateFilters.map(f => [ + f.id, + f.filter as TemplateFilter, + ]), + ) + : additionalTemplateFilters, + additionalTemplateGlobals: Array.isArray(additionalTemplateGlobals) + ? Object.fromEntries( + additionalTemplateGlobals.map(g => [ + g.id, + ('value' in g ? g.value : g.fn) as TemplateGlobal, + ]), + ) + : additionalTemplateGlobals, + }; const workers: TaskWorker[] = []; if (concurrentTasksLimit !== 0) { @@ -378,11 +402,10 @@ export async function createRouter( logger, auditor, workingDirectory, - additionalTemplateFilters, - additionalTemplateGlobals, concurrentTasksLimit, permissions, gracefulShutdown, + ...templateExtensions, }); workers.push(worker); } @@ -395,9 +418,8 @@ export async function createRouter( catalogClient, reader, config, - additionalTemplateFilters, - additionalTemplateGlobals, auth, + ...templateExtensions, }); actionsToRegister.forEach(action => actionRegistry.register(action)); @@ -421,9 +443,8 @@ export async function createRouter( logger, auditor, workingDirectory, - additionalTemplateFilters, - additionalTemplateGlobals, permissions, + ...templateExtensions, }); const templateRules: TemplatePermissionRuleInput[] = Object.values( diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index d23e2c92a6..62c734f1fb 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.1.19-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-test-utils@1.3.0-next.3 + - @backstage/plugin-scaffolder-node@0.7.0-next.2 + - @backstage/types@1.2.1 + ## 0.1.19-next.2 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index adecf392b2..bbad8a56ee 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.19-next.2", + "version": "0.1.19-next.3", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 924a1e57dd..05713cd3bc 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-scaffolder-node +## 0.7.0-next.2 + +### Minor Changes + +- dc8dd4b: Added new `createTemplateFilter`, `createTemplateGlobalFunction`, `createTemplateGlobalValue` for template extensions. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.9 + ## 0.7.0-next.1 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 23660f2d02..86f32a3d49 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.0-next.1", + "version": "0.7.0-next.2", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", @@ -26,7 +26,7 @@ "license": "Apache-2.0", "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", + "./alpha": "./src/alpha/index.ts", "./package.json": "./package.json" }, "main": "src/index.ts", @@ -34,7 +34,7 @@ "typesVersions": { "*": { "alpha": [ - "src/alpha.ts" + "src/alpha/index.ts" ], "package.json": [ "package.json" diff --git a/plugins/scaffolder-node/report-alpha.api.md b/plugins/scaffolder-node/report-alpha.api.md index eef9acc1c1..b561fe93e1 100644 --- a/plugins/scaffolder-node/report-alpha.api.md +++ b/plugins/scaffolder-node/report-alpha.api.md @@ -6,10 +6,12 @@ /// import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { JsonValue } from '@backstage/types'; import { TaskBroker } from '@backstage/plugin-scaffolder-node'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; -import { TemplateFilter } from '@backstage/plugin-scaffolder-node'; -import { TemplateGlobal } from '@backstage/plugin-scaffolder-node'; +import { TemplateFilter as TemplateFilter_2 } from '@backstage/plugin-scaffolder-node'; +import { TemplateGlobal as TemplateGlobal_2 } from '@backstage/plugin-scaffolder-node'; +import { z } from 'zod'; // @alpha export type AutocompleteHandler = ({ @@ -27,6 +29,92 @@ export type AutocompleteHandler = ({ }[]; }>; +// @alpha (undocumented) +export type CreatedTemplateFilter< + TSchema extends + | TemplateFilterSchema + | undefined + | unknown = unknown, + TFilterSchema extends TSchema extends TemplateFilterSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : TemplateFilter = TSchema extends TemplateFilterSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : TemplateFilter, +> = { + id: string; + description?: string; + examples?: TemplateFilterExample[]; + schema?: TSchema; + filter: TFilterSchema; +}; + +// @alpha (undocumented) +export type CreatedTemplateGlobal = + | CreatedTemplateGlobalValue + | CreatedTemplateGlobalFunction; + +// @alpha (undocumented) +export type CreatedTemplateGlobalFunction< + TSchema extends + | TemplateGlobalFunctionSchema + | undefined + | unknown = unknown, + TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : Exclude< + TemplateGlobal, + JsonValue + > = TSchema extends TemplateGlobalFunctionSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : Exclude, +> = { + id: string; + description?: string; + examples?: TemplateGlobalFunctionExample[]; + schema?: TSchema; + fn: TFilterSchema; +}; + +// @alpha (undocumented) +export type CreatedTemplateGlobalValue = { + id: string; + value: T; + description?: string; +}; + +// @alpha +export const createTemplateFilter: < + TSchema extends TemplateFilterSchema | undefined, + TFunctionSchema extends TSchema extends TemplateFilterSchema + ? z.TypeOf> + : (arg: JsonValue, ...rest: JsonValue[]) => JsonValue | undefined, +>( + filter: CreatedTemplateFilter, +) => CreatedTemplateFilter; + +// @alpha +export const createTemplateGlobalFunction: < + TSchema extends TemplateGlobalFunctionSchema | undefined, + TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema + ? z.TypeOf> + : (...args: JsonValue[]) => JsonValue | undefined, +>( + fn: CreatedTemplateGlobalFunction, +) => CreatedTemplateGlobalFunction; + +// @alpha +export const createTemplateGlobalValue: ( + v: CreatedTemplateGlobalValue, +) => CreatedTemplateGlobalValue; + // @alpha export const restoreWorkspace: (opts: { path: string; @@ -69,9 +157,13 @@ export const scaffolderTaskBrokerExtensionPoint: ExtensionPoint): void; + addTemplateFilters( + filters: Record | CreatedTemplateFilter[], + ): void; // (undocumented) - addTemplateGlobals(filters: Record): void; + addTemplateGlobals( + globals: Record | CreatedTemplateGlobal[], + ): void; } // @alpha @@ -91,6 +183,50 @@ export const serializeWorkspace: (opts: { path: string }) => Promise<{ contents: Buffer; }>; +// @public (undocumented) +export type TemplateFilter = ( + arg: JsonValue, + ...rest: JsonValue[] +) => JsonValue | undefined; + +// @alpha (undocumented) +export type TemplateFilterExample = { + description?: string; + example: string; + notes?: string; +}; + +// @alpha (undocumented) +export type TemplateFilterSchema< + Args extends z.ZodTuple< + | [z.ZodType] + | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], + z.ZodType | z.ZodUnknown | null + >, + Result extends z.ZodType | z.ZodUndefined, +> = (zod: typeof z) => z.ZodFunction; + +// @public (undocumented) +export type TemplateGlobal = + | ((...args: JsonValue[]) => JsonValue | undefined) + | JsonValue; + +// @alpha (undocumented) +export type TemplateGlobalFunctionExample = { + description?: string; + example: string; + notes?: string; +}; + +// @alpha (undocumented) +export type TemplateGlobalFunctionSchema< + Args extends z.ZodTuple< + [] | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], + z.ZodType | z.ZodUnknown | null + >, + Result extends z.ZodType | z.ZodUndefined, +> = (zod: typeof z) => z.ZodFunction; + // @alpha export interface WorkspaceProvider { // (undocumented) diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index c02d36eb54..2d3e418efb 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -482,7 +482,10 @@ export type TemplateExample = { }; // @public (undocumented) -export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; +export type TemplateFilter = ( + arg: JsonValue, + ...rest: JsonValue[] +) => JsonValue | undefined; // @public (undocumented) export type TemplateGlobal = diff --git a/plugins/scaffolder-node/src/alpha/filters/createTemplateFilter.ts b/plugins/scaffolder-node/src/alpha/filters/createTemplateFilter.ts new file mode 100644 index 0000000000..1f4bac4ab7 --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/filters/createTemplateFilter.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. + */ + +import { JsonValue } from '@backstage/types'; +import { CreatedTemplateFilter, TemplateFilterSchema } from './types'; +import { z } from 'zod'; + +/** + * This function is used to create new template filters in type-safe manner. + * @alpha + */ +export const createTemplateFilter = < + TSchema extends TemplateFilterSchema | undefined, + TFunctionSchema extends TSchema extends TemplateFilterSchema + ? z.infer> + : (arg: JsonValue, ...rest: JsonValue[]) => JsonValue | undefined, +>( + filter: CreatedTemplateFilter, +): CreatedTemplateFilter => filter; diff --git a/plugins/scaffolder-node/src/alpha/filters/index.ts b/plugins/scaffolder-node/src/alpha/filters/index.ts new file mode 100644 index 0000000000..1df12e2483 --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/filters/index.ts @@ -0,0 +1,17 @@ +/* + * 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 './types'; +export * from './createTemplateFilter'; diff --git a/plugins/scaffolder-node/src/alpha/filters/types.ts b/plugins/scaffolder-node/src/alpha/filters/types.ts new file mode 100644 index 0000000000..2f65baaefc --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/filters/types.ts @@ -0,0 +1,60 @@ +/* + * 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 { z } from 'zod'; +import { TemplateFilter } from '../../types'; +import { JsonValue } from '@backstage/types'; + +export type { TemplateFilter } from '../../types'; + +/** @alpha */ +export type TemplateFilterSchema< + Args extends z.ZodTuple< + | [z.ZodType] + | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], + z.ZodType | z.ZodUnknown | null + >, + Result extends z.ZodType | z.ZodUndefined, +> = (zod: typeof z) => z.ZodFunction; + +/** @alpha */ +export type TemplateFilterExample = { + description?: string; + example: string; + notes?: string; +}; + +/** @alpha */ +export type CreatedTemplateFilter< + TSchema extends + | TemplateFilterSchema + | undefined + | unknown = unknown, + TFilterSchema extends TSchema extends TemplateFilterSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : TemplateFilter = TSchema extends TemplateFilterSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : TemplateFilter, +> = { + id: string; + description?: string; + examples?: TemplateFilterExample[]; + schema?: TSchema; + filter: TFilterSchema; +}; diff --git a/plugins/scaffolder-node/src/alpha/globals/createTemplateGlobal.ts b/plugins/scaffolder-node/src/alpha/globals/createTemplateGlobal.ts new file mode 100644 index 0000000000..1f9e090d7f --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/globals/createTemplateGlobal.ts @@ -0,0 +1,48 @@ +/* + * 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 { z } from 'zod'; +import { + CreatedTemplateGlobalFunction, + CreatedTemplateGlobalValue, + TemplateGlobalFunctionSchema, +} from './types'; +import { JsonValue } from '@backstage/types'; + +/** + * This function is used to create new template global values in type-safe manner. + * @param t - CreatedTemplateGlobalValue | CreatedTemplateGlobalFunction + * @returns t + * @alpha + */ +export const createTemplateGlobalValue = ( + v: CreatedTemplateGlobalValue, +): CreatedTemplateGlobalValue => v; + +/** + * This function is used to create new template global functions in type-safe manner. + * @param fn - CreatedTemplateGlobalFunction + * @returns fn + * @alpha + */ +export const createTemplateGlobalFunction = < + TSchema extends TemplateGlobalFunctionSchema | undefined, + TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema + ? z.infer> + : (...args: JsonValue[]) => JsonValue | undefined, +>( + fn: CreatedTemplateGlobalFunction, +): CreatedTemplateGlobalFunction => fn; diff --git a/plugins/scaffolder-node/src/alpha/globals/index.ts b/plugins/scaffolder-node/src/alpha/globals/index.ts new file mode 100644 index 0000000000..6263a52728 --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/globals/index.ts @@ -0,0 +1,17 @@ +/* + * 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 './types'; +export * from './createTemplateGlobal'; diff --git a/plugins/scaffolder-node/src/alpha/globals/types.ts b/plugins/scaffolder-node/src/alpha/globals/types.ts new file mode 100644 index 0000000000..cbe74f1610 --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/globals/types.ts @@ -0,0 +1,74 @@ +/* + * 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 { JsonValue } from '@backstage/types'; +import { z } from 'zod'; +import { TemplateGlobal } from '../../types'; + +export type { TemplateGlobal } from '../../types'; + +/** @alpha */ +export type CreatedTemplateGlobalValue = { + id: string; + value: T; + description?: string; +}; + +/** @alpha */ +export type TemplateGlobalFunctionSchema< + Args extends z.ZodTuple< + [] | [z.ZodType, ...(z.ZodType | z.ZodUnknown)[]], + z.ZodType | z.ZodUnknown | null + >, + Result extends z.ZodType | z.ZodUndefined, +> = (zod: typeof z) => z.ZodFunction; + +/** @alpha */ +export type TemplateGlobalFunctionExample = { + description?: string; + example: string; + notes?: string; +}; + +/** @alpha */ +export type CreatedTemplateGlobalFunction< + TSchema extends + | TemplateGlobalFunctionSchema + | undefined + | unknown = unknown, + TFilterSchema extends TSchema extends TemplateGlobalFunctionSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : Exclude< + TemplateGlobal, + JsonValue + > = TSchema extends TemplateGlobalFunctionSchema + ? z.infer> + : TSchema extends unknown + ? unknown + : Exclude, +> = { + id: string; + description?: string; + examples?: TemplateGlobalFunctionExample[]; + schema?: TSchema; + fn: TFilterSchema; +}; + +/** @alpha */ +export type CreatedTemplateGlobal = + | CreatedTemplateGlobalValue + | CreatedTemplateGlobalFunction; diff --git a/plugins/scaffolder-node/src/alpha.ts b/plugins/scaffolder-node/src/alpha/index.ts similarity index 90% rename from plugins/scaffolder-node/src/alpha.ts rename to plugins/scaffolder-node/src/alpha/index.ts index 38a43d4865..1b573d0650 100644 --- a/plugins/scaffolder-node/src/alpha.ts +++ b/plugins/scaffolder-node/src/alpha/index.ts @@ -21,8 +21,12 @@ import { TemplateFilter, TemplateGlobal, } from '@backstage/plugin-scaffolder-node'; +import { CreatedTemplateFilter } from './filters'; +import { CreatedTemplateGlobal } from './globals'; -export * from './tasks/alpha'; +export * from '../tasks/alpha'; +export * from './filters'; +export * from './globals'; /** * Extension point for managing scaffolder actions. @@ -68,9 +72,13 @@ export const scaffolderTaskBrokerExtensionPoint = * @alpha */ export interface ScaffolderTemplatingExtensionPoint { - addTemplateFilters(filters: Record): void; + addTemplateFilters( + filters: Record | CreatedTemplateFilter[], + ): void; - addTemplateGlobals(filters: Record): void; + addTemplateGlobals( + globals: Record | CreatedTemplateGlobal[], + ): void; } /** diff --git a/plugins/scaffolder-node/src/index.ts b/plugins/scaffolder-node/src/index.ts index 0ec492dd32..5d49ee874c 100644 --- a/plugins/scaffolder-node/src/index.ts +++ b/plugins/scaffolder-node/src/index.ts @@ -23,4 +23,4 @@ export * from './actions'; export * from './tasks'; export * from './files'; -export type { TemplateFilter, TemplateGlobal } from './types'; +export * from './types'; diff --git a/plugins/scaffolder-node/src/types.ts b/plugins/scaffolder-node/src/types.ts index bae106c84e..e25ee4db80 100644 --- a/plugins/scaffolder-node/src/types.ts +++ b/plugins/scaffolder-node/src/types.ts @@ -17,7 +17,10 @@ import { JsonValue } from '@backstage/types'; /** @public */ -export type TemplateFilter = (...args: JsonValue[]) => JsonValue | undefined; +export type TemplateFilter = ( + arg: JsonValue, + ...rest: JsonValue[] +) => JsonValue | undefined; /** @public */ export type TemplateGlobal = diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 982043e031..b0f4c17e65 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-scaffolder-react +## 1.14.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-scaffolder-common@1.5.9 + ## 1.14.5-next.2 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 738db62f5f..08d13cfd76 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.5-next.2", + "version": "1.14.5-next.3", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 3ea4f647ec..d93c3c2e01 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-scaffolder +## 1.28.0-next.3 + +### Minor Changes + +- 5d469c9: Added support for autocompletion of GitHub branches in scaffolder +- 8e67e4a: Added support for autocompletion to GithubRepoPicker component + +### Patch Changes + +- 1e935f0: Added conditional rendering of `oneOf` output schemas on the Installed Actions page for scaffolder actions +- 65d7020: Use template title for ongoing task page header +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/plugin-scaffolder-react@1.14.5-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-permission-react@0.4.31-next.0 + - @backstage/plugin-scaffolder-common@1.5.9 + ## 1.28.0-next.2 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index ecb5f03474..7f12a7b8f3 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.28.0-next.2", + "version": "1.28.0-next.3", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunContext.tsx b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunContext.tsx index 1765e8cb94..ced6f0f909 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunContext.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunContext.tsx @@ -16,7 +16,7 @@ import yaml from 'yaml'; import { useApi } from '@backstage/core-plugin-api'; -import { JsonObject } from '@backstage/types'; +import { JsonObject, JsonValue } from '@backstage/types'; import React, { createContext, ReactNode, @@ -30,6 +30,7 @@ import { scaffolderApiRef, ScaffolderDryRunResponse, } from '@backstage/plugin-scaffolder-react'; +import { useFormDecorators } from '../../hooks/useFormDecorators'; const MAX_CONTENT_SIZE = 64 * 1024; const CHUNK_SIZE = 32 * 1024; @@ -81,6 +82,7 @@ export function base64EncodeContent(content: string): string { } export function DryRunProvider(props: DryRunProviderProps) { + const decorators = useFormDecorators(); const scaffolderApi = useApi(scaffolderApiRef); const [state, setState] = useState< @@ -130,10 +132,16 @@ export function DryRunProvider(props: DryRunProviderProps) { const parsed = yaml.parse(options.templateContent); + const { formState: values, secrets } = await decorators.run({ + formState: options.values as Record, + secrets: {}, + manifest: parsed?.spec, + }); + const response = await scaffolderApi.dryRun({ template: parsed, - values: options.values, - secrets: {}, + values, + secrets, directoryContents: options.files.map(file => ({ path: file.path, base64Content: base64EncodeContent(file.content), @@ -150,7 +158,7 @@ export function DryRunProvider(props: DryRunProviderProps) { selectedResult: prevState.selectedResult ?? result, })); }, - [scaffolderApi], + [scaffolderApi, decorators], ); const dryRun = useMemo( diff --git a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResults.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResults.test.tsx index 0267093ee6..277de1cf32 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResults.test.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResults.test.tsx @@ -21,6 +21,7 @@ import React, { useEffect } from 'react'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import { DryRunProvider, useDryRun } from '../DryRunContext'; import { DryRunResults } from './DryRunResults'; +import { formDecoratorsApiRef } from '../../../api'; function DryRunRemote({ execute, @@ -50,19 +51,30 @@ function DryRunRemote({ return null; } -const mockScaffolderApi = { - dryRun: async () => ({ - directoryContents: [], - log: [], - output: {}, - steps: [], - }), -}; +const mockApis = [ + [ + scaffolderApiRef, + { + dryRun: async () => ({ + directoryContents: [], + log: [], + output: {}, + steps: [], + }), + }, + ], + [ + formDecoratorsApiRef, + { + getFormDecorators: async () => [], + }, + ], +] as const; describe('DryRunResults', () => { it('renders without exploding', async () => { await renderInTestApp( - + @@ -73,7 +85,7 @@ describe('DryRunResults', () => { it('expands when dry-run result is added and toggles on click, and disappears when results are gone', async () => { const { rerender } = await renderInTestApp( - + @@ -85,7 +97,7 @@ describe('DryRunResults', () => { await act(async () => { rerender( - + @@ -104,7 +116,7 @@ describe('DryRunResults', () => { await act(async () => { rerender( - + diff --git a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsList.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsList.test.tsx index 706f284db1..6fbd5b3a60 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsList.test.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsList.test.tsx @@ -21,6 +21,7 @@ import React, { useEffect } from 'react'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import { DryRunProvider, useDryRun } from '../DryRunContext'; import { DryRunResultsList } from './DryRunResultsList'; +import { formDecoratorsApiRef } from '../../../api'; function DryRunRemote({ execute }: { execute?: number }) { const dryRun = useDryRun(); @@ -39,19 +40,30 @@ function DryRunRemote({ execute }: { execute?: number }) { return null; } -const mockScaffolderApi = { - dryRun: async () => ({ - directoryContents: [], - log: [], - output: {}, - steps: [], - }), -}; +const mockApis = [ + [ + scaffolderApiRef, + { + dryRun: async () => ({ + directoryContents: [], + log: [], + output: {}, + steps: [], + }), + }, + ], + [ + formDecoratorsApiRef, + { + getFormDecorators: async () => [], + }, + ], +] as const; describe('DryRunResultsList', () => { it('renders without exploding', async () => { const rendered = await renderInTestApp( - + @@ -62,7 +74,7 @@ describe('DryRunResultsList', () => { it('adds new result items and deletes them', async () => { const { rerender } = await renderInTestApp( - + @@ -75,7 +87,7 @@ describe('DryRunResultsList', () => { await act(async () => { rerender( - + diff --git a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsView.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsView.test.tsx index c5c391907e..e1d5bc874e 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsView.test.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/DryRunResults/DryRunResultsView.test.tsx @@ -22,6 +22,7 @@ import React, { ReactNode, useEffect } from 'react'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import { DryRunProvider, useDryRun } from '../DryRunContext'; import { DryRunResultsView } from './DryRunResultsView'; +import { formDecoratorsApiRef } from '../../../api'; // The inside needs mocking to render in jsdom jest.mock('react-virtualized-auto-sizer', () => ({ @@ -70,6 +71,12 @@ describe('DryRunResultsView', () => { }), }, ], + [ + formDecoratorsApiRef, + { + getFormDecorators: async () => [], + }, + ], ]} > diff --git a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorPage.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorPage.test.tsx index 7f7c66bc5d..e034ece21a 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorPage.test.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorPage.test.tsx @@ -21,10 +21,14 @@ import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import { TemplateEditorPage } from './TemplateEditorPage'; import { rootRouteRef } from '../../../routes'; +import { formDecoratorsApiRef } from '../../api'; describe('TemplateEditorPage', () => { const catalogApiMock = { getEntities: jest.fn().mockResolvedValue([]) }; const scaffolderApiMock = {}; + const formDecoratorsApiMock = { + getFormDecorators: jest.fn().mockResolvedValue([]), + }; it('Should render without exploding', async () => { await renderInTestApp( @@ -32,6 +36,7 @@ describe('TemplateEditorPage', () => { apis={[ [catalogApiRef, catalogApiMock], [scaffolderApiRef, scaffolderApiMock], + [formDecoratorsApiRef, formDecoratorsApiMock], ]} > @@ -53,6 +58,7 @@ describe('TemplateEditorPage', () => { apis={[ [catalogApiRef, catalogApiMock], [scaffolderApiRef, scaffolderApiMock], + [formDecoratorsApiRef, formDecoratorsApiMock], ]} > diff --git a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.tsx b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.tsx index 2ed6de4479..0c2356de54 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.tsx @@ -91,7 +91,7 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => { }); const { manifest } = useTemplateParameterSchema(templateRef); - const decorators = useFormDecorators({ manifest }); + const decorators = useFormDecorators(); const { value: editUrl } = useAsync(async () => { const data = await catalogApi.getEntityByRef(templateRef); @@ -108,6 +108,7 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => { const { formState: values, secrets } = await decorators.run({ formState: initialValues, secrets: contextSecrets, + manifest, }); const { taskId } = await scaffolderApi.scaffold({ diff --git a/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx b/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx index 8f486bbba1..4da8b5e424 100644 --- a/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx +++ b/plugins/scaffolder/src/alpha/hooks/useFormDecorators.test.tsx @@ -51,7 +51,7 @@ describe('useFormDecorators', () => { }; it('should run the form decorators for a given manifest with the correct input', async () => { - const renderedHook = renderHook(() => useFormDecorators({ manifest }), { + const renderedHook = renderHook(() => useFormDecorators(), { wrapper: ({ children }) => ( { await result.run({ formState: {}, secrets: {}, + manifest, }); expect(mockApiImplementation.test).toHaveBeenCalledWith('hello'); @@ -83,7 +84,7 @@ describe('useFormDecorators', () => { }); it('should return existing secrets and formstate', async () => { - const renderedHook = renderHook(() => useFormDecorators({ manifest }), { + const renderedHook = renderHook(() => useFormDecorators(), { wrapper: ({ children }) => ( { const { secrets, formState } = await result.run({ formState: { test: 'formState' }, secrets: { test: 'hello' }, + manifest, }); expect(secrets).toEqual({ test: 'hello' }); @@ -122,7 +124,7 @@ describe('useFormDecorators', () => { setSecrets(state => ({ ...state, new: 'hello' })); }, }); - const renderedHook = renderHook(() => useFormDecorators({ manifest }), { + const renderedHook = renderHook(() => useFormDecorators(), { wrapper: ({ children }) => ( { const { secrets, formState } = await result.run({ formState: { test: 'formState' }, secrets: { test: 'hello' }, + manifest, }); expect(secrets).toEqual({ test: 'hello', new: 'hello' }); diff --git a/plugins/scaffolder/src/alpha/hooks/useFormDecorators.ts b/plugins/scaffolder/src/alpha/hooks/useFormDecorators.ts index d74c4a9823..a27d47fb59 100644 --- a/plugins/scaffolder/src/alpha/hooks/useFormDecorators.ts +++ b/plugins/scaffolder/src/alpha/hooks/useFormDecorators.ts @@ -16,7 +16,7 @@ import { errorApiRef, useApi, useApiHolder } from '@backstage/core-plugin-api'; import { formDecoratorsApiRef } from '../api/ref'; import useAsync from 'react-use/esm/useAsync'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { ScaffolderFormDecoratorContext } from '@backstage/plugin-scaffolder-react/alpha'; import { OpaqueFormDecorator } from '@internal/scaffolder'; import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; @@ -27,11 +27,7 @@ type BoundFieldDecorator = { decorator: (ctx: ScaffolderFormDecoratorContext) => Promise; }; -export const useFormDecorators = ({ - manifest, -}: { - manifest?: TemplateParameterSchema; -}) => { +export const useFormDecorators = () => { const formDecoratorsApi = useApi(formDecoratorsApiRef); const errorApi = useApi(errorApiRef); const { value: decorators } = useAsync( @@ -69,18 +65,20 @@ export const useFormDecorators = ({ return decoratorsMap; }, [apiHolder, decorators, errorApi]); - return { - run: async (opts: { + const run = useCallback( + async (opts: { formState: Record; secrets: Record; + manifest?: TemplateParameterSchema; }) => { let formState: Record = { ...opts.formState }; let secrets: Record = { ...opts.secrets }; - if (manifest?.EXPERIMENTAL_formDecorators) { + const formDecorators = opts.manifest?.EXPERIMENTAL_formDecorators; + if (formDecorators) { // for each of the form decorators, go and call the decorator with the context await Promise.all( - manifest.EXPERIMENTAL_formDecorators.map(async decorator => { + formDecorators.map(async decorator => { const formDecorator = boundDecorators?.get(decorator.id); if (!formDecorator) { errorApi.post( @@ -113,5 +111,13 @@ export const useFormDecorators = ({ return { formState, secrets }; }, - }; + [boundDecorators, errorApi], + ); + + return useMemo( + () => ({ + run, + }), + [run], + ); }; diff --git a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx index acfdabac5b..a3b3049beb 100644 --- a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx +++ b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx @@ -125,6 +125,63 @@ describe('TemplatePage', () => { expect(rendered.getByText('Test output')).toBeInTheDocument(); }); + it('renders action with oneOf output', async () => { + scaffolderApiMock.listActions.mockResolvedValue([ + { + id: 'test', + description: 'example description', + schema: { + input: { + type: 'object', + required: ['foobar'], + properties: { + foobar: { + title: 'Test title', + type: 'string', + }, + }, + }, + output: { + oneOf: [ + { + type: 'object', + properties: { + buzz: { + title: 'Test output1', + type: 'string', + }, + }, + }, + { + type: 'object', + properties: { + buzz: { + title: 'Test output2', + type: 'string', + }, + }, + }, + ], + }, + }, + }, + ]); + const rendered = await renderInTestApp( + + + , + { + mountedRoutes: { + '/create/actions': rootRouteRef, + }, + }, + ); + expect(rendered.getByText('oneOf')).toBeInTheDocument(); + expect(rendered.getByText('Test title')).toBeInTheDocument(); + expect(rendered.getByText('Test output1')).toBeInTheDocument(); + expect(rendered.getByText('Test output2')).toBeInTheDocument(); + }); + it('renders action with multiple input types', async () => { scaffolderApiMock.listActions.mockResolvedValue([ { diff --git a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx index f85c1f0e6b..50f7726202 100644 --- a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx +++ b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx @@ -340,11 +340,16 @@ export const ActionPageContent = () => { return undefined; } - const oneOf = renderTables( + const oneOfInput = renderTables( 'oneOf', `${action.id}.input`, action.schema?.input?.oneOf, ); + const oneOfOutput = renderTables( + 'oneOf', + `${action.id}.output`, + action.schema?.output?.oneOf, + ); return ( @@ -374,7 +379,7 @@ export const ActionPageContent = () => { {renderTable( formatRows(`${action.id}.input`, action?.schema?.input), )} - {oneOf} + {oneOfInput} )} {action.schema?.output && ( @@ -385,6 +390,7 @@ export const ActionPageContent = () => { {renderTable( formatRows(`${action.id}.output`, action?.schema?.output), )} + {oneOfOutput} )} {action.examples && ( diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx index d62ef6f95e..7a04854c6f 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx @@ -17,9 +17,9 @@ import { OngoingTask } from './OngoingTask'; import React from 'react'; import { + mockApis, renderInTestApp, TestApiProvider, - mockApis, } from '@backstage/test-utils'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import { act, fireEvent, waitFor, within } from '@testing-library/react'; @@ -30,6 +30,7 @@ import { import { rootRouteRef } from '../../routes'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { SWRConfig } from 'swr'; +import { entityPresentationApiRef } from '@backstage/plugin-catalog-react'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -47,7 +48,10 @@ jest.mock('@backstage/plugin-scaffolder-react', () => ({ task: { spec: { steps: [], - templateInfo: { entity: { metadata: { name: 'my-template' } } }, + templateInfo: { + entityRef: 'template:default/my-template', + entity: { metadata: { name: 'my-template' } }, + }, }, }, }), @@ -59,6 +63,12 @@ describe('OngoingTask', () => { getTask: jest.fn().mockImplementation(async () => {}), }; + const mockEntityPresentationApi = { + forEntity: jest.fn().mockReturnValue({ + promise: new Promise(resolve => resolve({ primaryTitle: 'My template' })), + }), + }; + beforeEach(async () => { jest.clearAllMocks(); }); @@ -71,6 +81,7 @@ describe('OngoingTask', () => { apis={[ [scaffolderApiRef, mockScaffolderApi], [permissionApiRef, permissionApi || mockApis.permission()], + [entityPresentationApiRef, mockEntityPresentationApi], ]} > @@ -79,6 +90,12 @@ describe('OngoingTask', () => { { mountedRoutes: { '/': rootRouteRef } }, ); }; + + it('should render title', async () => { + const rendered = await render(); + expect(rendered.getByText('My template')).toBeInTheDocument(); + }); + it('should trigger cancel api on "Cancel" click in context menu', async () => { const rendered = await render(); const cancelOptionLabel = 'Cancel'; diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx index 97047d00c9..06620e7280 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx @@ -21,8 +21,8 @@ import Button from '@material-ui/core/Button'; import Paper from '@material-ui/core/Paper'; import { makeStyles } from '@material-ui/core/styles'; import { - ScaffolderTaskOutput, scaffolderApiRef, + ScaffolderTaskOutput, useTaskEventStream, } from '@backstage/plugin-scaffolder-react'; import { selectedTemplateRouteRef } from '../../routes'; @@ -38,11 +38,13 @@ import { useAsync } from '@react-hookz/web'; import { usePermission } from '@backstage/plugin-permission-react'; import { taskCancelPermission, - taskReadPermission, taskCreatePermission, + taskReadPermission, } from '@backstage/plugin-scaffolder-common/alpha'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { scaffolderTranslationRef } from '../../translation'; +import { entityPresentationApiRef } from '@backstage/plugin-catalog-react'; +import { default as reactUseAsync } from 'react-use/esm/useAsync'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -79,6 +81,7 @@ export const OngoingTask = (props: { const navigate = useNavigate(); const analytics = useAnalytics(); const scaffolderApi = useApi(scaffolderApiRef); + const entityPresentationApi = useApi(entityPresentationApiRef); const taskStream = useTaskEventStream(taskId!); const classes = useStyles(); const steps = useMemo( @@ -123,6 +126,14 @@ export const OngoingTask = (props: { } }, [taskStream.error, taskStream.completed]); + const { value: presentation } = reactUseAsync(async () => { + const templateEntityRef = taskStream.task?.spec.templateInfo?.entityRef; + if (!templateEntityRef) { + return undefined; + } + return entityPresentationApi.forEntity(templateEntityRef).promise; + }, [entityPresentationApi, taskStream.task?.spec.templateInfo?.entityRef]); + const activeStep = useMemo(() => { for (let i = steps.length - 1; i >= 0; i--) { if (steps[i].status !== 'open') { @@ -184,22 +195,22 @@ export const OngoingTask = (props: { const Outputs = props.TemplateOutputsComponent ?? DefaultTemplateOutputs; - const templateName = - taskStream.task?.spec.templateInfo?.entity?.metadata.name || ''; - const cancelEnabled = !(taskStream.cancelled || taskStream.completed); return (
- {t('ongoingTask.title')} {templateName} + {t('ongoingTask.title')}{' '} + {presentation ? presentation.primaryTitle : ''}
} subtitle={t('ongoingTask.subtitle', { taskId: taskId as string })} diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index 0cb0f8d3d4..16b8362677 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend-module-catalog +## 0.3.1-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @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-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 0.3.1-next.2 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 0d661d63a4..500d69b659 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-catalog", - "version": "0.3.1-next.2", + "version": "0.3.1-next.3", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index 73ebc9d558..4137e92a27 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.6.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-search-common@1.2.17 + ## 1.6.5-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index a8250270b2..2ac6e32ba6 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", - "version": "1.6.5-next.1", + "version": "1.6.5-next.2", "description": "A module for the search backend that implements search using ElasticSearch", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 279dc5ef6d..67ab8b0a89 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-explore +## 0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-common@1.2.17 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index c4aa6af303..ec3dd4bd63 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-explore", - "version": "0.2.8-next.1", + "version": "0.2.8-next.2", "description": "A module for the search backend that exports explore modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index 9d9a987b0d..24f87c57e1 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.41-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-common@1.2.17 + ## 0.5.41-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index c002849225..6583195ff5 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-pg", - "version": "0.5.41-next.1", + "version": "0.5.41-next.2", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index 19692e88a9..32fbf65ff8 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.3.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-common@1.2.17 + ## 0.3.6-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index 5fbed6aecc..e22470fca9 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", - "version": "0.3.6-next.1", + "version": "0.3.6-next.2", "description": "A module for the search backend that exports stack overflow modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 8eac0e266d..411b8b4ed1 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.3.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/plugin-techdocs-node@1.13.0-next.2 + - @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-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 0.3.6-next.2 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index b56ddf56b3..04e6773b34 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.3.6-next.2", + "version": "0.3.6-next.3", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index a455c14c7e..cd7a622bd1 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-node +## 1.3.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 1.3.8-next.1 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index fc0a62cd50..3ca79a8f29 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "1.3.8-next.1", + "version": "1.3.8-next.2", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", "backstage": { "role": "node-library", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 890ccf31d2..49e3836fec 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend +## 1.8.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-node@0.8.8-next.2 + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-node@1.3.8-next.2 + - @backstage/backend-openapi-utils@0.5.0-next.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-search-common@1.2.17 + ## 1.8.2-next.2 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 843672118f..589846848a 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "1.8.2-next.2", + "version": "1.8.2-next.3", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index 0687e61c77..9a28994a9f 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-react +## 1.8.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-search-common@1.2.17 + ## 1.8.6-next.2 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index f35aec45d9..749dbcf115 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.8.6-next.2", + "version": "1.8.6-next.3", "backstage": { "role": "web-library", "pluginId": "search", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index bf05b1167c..98fc621b5e 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search +## 1.4.23-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + ## 1.4.23-next.2 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 6f408cc511..b2525f2a80 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.23-next.2", + "version": "1.4.23-next.3", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin", diff --git a/plugins/signals-backend/CHANGELOG.md b/plugins/signals-backend/CHANGELOG.md index e4c905c143..414edbabd0 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-signals-backend +## 0.3.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.17-next.2 + ## 0.3.1-next.1 ### Patch Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index c27c20de5c..4c9e31c5b0 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-backend", - "version": "0.3.1-next.1", + "version": "0.3.1-next.2", "backstage": { "role": "backend-plugin", "pluginId": "signals", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index ad6027d5c7..78bcb6441f 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-signals-node +## 0.1.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/plugin-events-node@0.4.8-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.1.17-next.1 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index c0cb03c4ee..5f5fc2d62e 100644 --- a/plugins/signals-node/package.json +++ b/plugins/signals-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-node", - "version": "0.1.17-next.1", + "version": "0.1.17-next.2", "description": "Node.js library for the signals plugin", "backstage": { "role": "node-library", diff --git a/plugins/signals/package.json b/plugins/signals/package.json index 974e46bcc5..a0832ec149 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -12,9 +12,7 @@ ] }, "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" }, "repository": { "type": "git", @@ -23,8 +21,23 @@ }, "license": "Apache-2.0", "sideEffects": false, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.tsx", + "./package.json": "./package.json" + }, "main": "src/index.ts", "types": "src/index.ts", + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.tsx" + ], + "package.json": [ + "package.json" + ] + } + }, "files": [ "dist" ], @@ -40,6 +53,7 @@ "dependencies": { "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-signals-react": "workspace:^", "@backstage/theme": "workspace:^", "@backstage/types": "workspace:^", diff --git a/plugins/signals/report-alpha.api.md b/plugins/signals/report-alpha.api.md new file mode 100644 index 0000000000..45ee0da3b8 --- /dev/null +++ b/plugins/signals/report-alpha.api.md @@ -0,0 +1,36 @@ +## API Report File for "@backstage/plugin-signals" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AnyApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { FrontendPlugin } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +const _default: FrontendPlugin< + {}, + {}, + { + 'api:signals': ExtensionDefinition<{ + kind: 'api'; + name: undefined; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + AnyApiFactory, + 'core.api.factory', + {} + >; + inputs: {}; + params: { + factory: AnyApiFactory; + }; + }>; + } +>; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/signals/src/alpha.tsx b/plugins/signals/src/alpha.tsx new file mode 100644 index 0000000000..6d4eb73af3 --- /dev/null +++ b/plugins/signals/src/alpha.tsx @@ -0,0 +1,49 @@ +/* + * 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 { + ApiBlueprint, + createApiFactory, + createFrontendPlugin, + discoveryApiRef, + identityApiRef, +} from '@backstage/frontend-plugin-api'; +import { signalApiRef } from '@backstage/plugin-signals-react'; +import { SignalClient } from './api/SignalClient'; + +const api = ApiBlueprint.make({ + params: { + factory: createApiFactory({ + api: signalApiRef, + deps: { + identity: identityApiRef, + discoveryApi: discoveryApiRef, + }, + factory: ({ identity, discoveryApi }) => { + return SignalClient.create({ + identity, + discoveryApi, + }); + }, + }), + }, +}); + +/** @alpha */ +export default createFrontendPlugin({ + id: 'signals', + extensions: [api], +}); diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 367a1ac8ab..9da44305c8 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.45-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.27.0-next.3 + - @backstage/plugin-techdocs@1.12.3-next.3 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/test-utils@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + ## 1.0.45-next.2 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 64689c4d71..ebf345c345 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-addons-test-utils", - "version": "1.0.45-next.2", + "version": "1.0.45-next.3", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index cdb6a52416..dc653227ec 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-techdocs-backend +## 1.11.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-backend-module-techdocs@0.3.6-next.3 + - @backstage/plugin-techdocs-node@1.13.0-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/plugin-catalog-common@1.1.3 + - @backstage/plugin-permission-common@0.8.4 + - @backstage/plugin-techdocs-common@0.1.0 + ## 1.11.6-next.2 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index c3260a0d0d..ffd4194ad7 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "1.11.6-next.2", + "version": "1.11.6-next.3", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 0fe2efeca9..3cbd5a3c18 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-techdocs-node +## 1.13.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + ## 1.13.0-next.1 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 67521d6306..3f3cb4ebb1 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.13.0-next.1", + "version": "1.13.0-next.2", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index ca1b8de1e5..48d783941e 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-techdocs +## 1.12.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + ## 1.12.3-next.2 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index a6a9793fcd..b32283d5b2 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.12.3-next.2", + "version": "1.12.3-next.3", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/techdocs/src/client.test.ts b/plugins/techdocs/src/client.test.ts index f66587a071..70c5f3568e 100644 --- a/plugins/techdocs/src/client.test.ts +++ b/plugins/techdocs/src/client.test.ts @@ -180,7 +180,11 @@ describe('TechDocsStorageClient', () => { mockFetchEventSource.mockImplementation(async (_url, options) => { const { onerror } = options; - onerror?.(new NotFoundError('Some not found warning')); + try { + onerror?.(new NotFoundError('Some not found warning')); + } catch (e) { + // do nothing + } }); // we await later after we emitted the error @@ -202,7 +206,11 @@ describe('TechDocsStorageClient', () => { mockFetchEventSource.mockImplementation(async (_url, options) => { const { onerror } = options; - onerror?.(new Error('Some other error')); + try { + onerror?.(new Error('Some other error')); + } catch (e) { + // do nothing + } }); await expect(promise).rejects.toThrow(Error); diff --git a/plugins/techdocs/src/client.ts b/plugins/techdocs/src/client.ts index d00be2a01f..9ea753b1e6 100644 --- a/plugins/techdocs/src/client.ts +++ b/plugins/techdocs/src/client.ts @@ -236,6 +236,7 @@ export class TechDocsStorageClient implements TechDocsStorageApi { onerror(err) { ctrl.abort(); reject(err); + throw err; // rethrow to stop the operation }, }); }); diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 4148403a9b..2955d9d9c3 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-user-settings-backend +## 0.2.30-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-auth-node@0.6.0-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.17-next.2 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.2.30-next.2 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index 2fc3b336cd..05591f078b 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings-backend", - "version": "0.2.30-next.2", + "version": "0.2.30-next.3", "description": "The Backstage backend plugin to manage user settings", "backstage": { "role": "backend-plugin", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index ccd8b3fc64..66e732c4d1 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-user-settings +## 0.8.19-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/core-app-api@1.15.5-next.0 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-signals-react@0.0.10-next.0 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.8.19-next.2 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index cbf36470b0..557c7ecf3a 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.8.19-next.2", + "version": "0.8.19-next.3", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", diff --git a/yarn.lock b/yarn.lock index 074909bfa3..ad2aa54234 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4016,7 +4016,7 @@ __metadata: mini-css-extract-plugin: ^2.4.2 minimatch: ^9.0.0 msw: ^1.0.0 - node-libs-browser: ^2.2.1 + node-stdlib-browser: ^1.3.1 nodemon: ^3.0.1 npm-packlist: ^5.0.0 ora: ^5.3.0 @@ -4050,6 +4050,7 @@ __metadata: yml-loader: ^2.1.0 yn: ^4.0.0 zod: ^3.22.4 + zod-validation-error: ^3.4.0 peerDependencies: "@rspack/core": ^1.0.10 "@rspack/dev-server": ^1.0.9 @@ -6404,10 +6405,12 @@ __metadata: dependencies: "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@material-ui/core": ^4.9.13 "@material-ui/icons": ^4.9.1 "@material-ui/lab": ^4.0.0-alpha.60 @@ -7129,10 +7132,12 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" + "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-notifications-common": "workspace:^" "@backstage/plugin-signals": "workspace:^" "@backstage/plugin-signals-react": "workspace:^" @@ -8329,6 +8334,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-signals-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" @@ -23286,15 +23292,14 @@ __metadata: languageName: node linkType: hard -"asn1.js@npm:^5.2.0": - version: 5.4.1 - resolution: "asn1.js@npm:5.4.1" +"asn1.js@npm:^4.10.1": + version: 4.10.1 + resolution: "asn1.js@npm:4.10.1" dependencies: bn.js: ^4.0.0 inherits: ^2.0.1 minimalistic-assert: ^1.0.0 - safer-buffer: ^2.1.0 - checksum: 3786a101ac6f304bd4e9a7df79549a7561950a13d4bcaec0c7790d44c80d147c1a94ba3d4e663673406064642a40b23fcd6c82a9952468e386c1a1376d747f9a + checksum: 9289a1a55401238755e3142511d7b8f6fc32f08c86ff68bd7100da8b6c186179dd6b14234fba2f7f6099afcd6758a816708485efe44bc5b2a6ec87d9ceeddbb5 languageName: node linkType: hard @@ -23314,13 +23319,16 @@ __metadata: languageName: node linkType: hard -"assert@npm:^1.1.1": - version: 1.5.0 - resolution: "assert@npm:1.5.0" +"assert@npm:^2.0.0": + version: 2.1.0 + resolution: "assert@npm:2.1.0" dependencies: - object-assign: ^4.1.1 - util: 0.10.3 - checksum: 9be48435f726029ae7020c5888a3566bf4d617687aab280827f2e4029644b6515a9519ea10d018b342147c02faf73d9e9419e780e8937b3786ee4945a0ca71e5 + call-bind: ^1.0.2 + is-nan: ^1.3.2 + object-is: ^1.1.5 + object.assign: ^4.1.4 + util: ^0.12.5 + checksum: 1ed1cabba9abe55f4109b3f7292b4e4f3cf2953aad8dc148c0b3c3bd676675c31b1abb32ef563b7d5a19d1715bf90d1e5f09fad2a4ee655199468902da80f7c2 languageName: node linkType: hard @@ -24210,7 +24218,16 @@ __metadata: languageName: node linkType: hard -"browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4": +"browser-resolve@npm:^2.0.0": + version: 2.0.0 + resolution: "browser-resolve@npm:2.0.0" + dependencies: + resolve: ^1.17.0 + checksum: 69225e73b555bd6d2a08fb93c7342cfcf3b5058b975099c52649cd5c3cec84c2066c5385084d190faedfb849684d9dabe10129f0cd401d1883572f2e6650f440 + languageName: node + linkType: hard + +"browserify-aes@npm:^1.0.4, browserify-aes@npm:^1.2.0": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" dependencies: @@ -24224,7 +24241,7 @@ __metadata: languageName: node linkType: hard -"browserify-cipher@npm:^1.0.0": +"browserify-cipher@npm:^1.0.1": version: 1.0.1 resolution: "browserify-cipher@npm:1.0.1" dependencies: @@ -24257,20 +24274,21 @@ __metadata: languageName: node linkType: hard -"browserify-sign@npm:^4.0.0": - version: 4.2.2 - resolution: "browserify-sign@npm:4.2.2" +"browserify-sign@npm:^4.2.3": + version: 4.2.3 + resolution: "browserify-sign@npm:4.2.3" dependencies: bn.js: ^5.2.1 browserify-rsa: ^4.1.0 create-hash: ^1.2.0 create-hmac: ^1.1.7 - elliptic: ^6.5.4 + elliptic: ^6.5.5 + hash-base: ~3.0 inherits: ^2.0.4 - parse-asn1: ^5.1.6 - readable-stream: ^3.6.2 + parse-asn1: ^5.1.7 + readable-stream: ^2.3.8 safe-buffer: ^5.2.1 - checksum: b622730c0fc183328c3a1c9fdaaaa5118821ed6822b266fa6b0375db7e20061ebec87301d61931d79b9da9a96ada1cab317fce3c68f233e5e93ed02dbb35544c + checksum: 403a8061d229ae31266670345b4a7c00051266761d2c9bbeb68b1a9bcb05f68143b16110cf23a171a5d6716396a1f41296282b3e73eeec0a1871c77f0ff4ee6b languageName: node linkType: hard @@ -24367,18 +24385,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^4.3.0": - version: 4.9.2 - resolution: "buffer@npm:4.9.2" - dependencies: - base64-js: ^1.0.2 - ieee754: ^1.1.4 - isarray: ^1.0.0 - checksum: 8801bc1ba08539f3be70eee307a8b9db3d40f6afbfd3cf623ab7ef41dffff1d0a31de0addbe1e66e0ca5f7193eeb667bfb1ecad3647f8f1b0750de07c13295c3 - languageName: node - linkType: hard - -"buffer@npm:^5.5.0": +"buffer@npm:^5.5.0, buffer@npm:^5.7.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -24629,7 +24636,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.7, call-bind@npm:^1.0.8": version: 1.0.8 resolution: "call-bind@npm:1.0.8" dependencies: @@ -26025,13 +26032,13 @@ __metadata: languageName: node linkType: hard -"create-ecdh@npm:^4.0.0": - version: 4.0.3 - resolution: "create-ecdh@npm:4.0.3" +"create-ecdh@npm:^4.0.4": + version: 4.0.4 + resolution: "create-ecdh@npm:4.0.4" dependencies: bn.js: ^4.1.0 - elliptic: ^6.0.0 - checksum: 0678955daf937c188c69b2a601ebcbe4ab02ca3c1aa04f62d5fb5511430d0141802207eabf9aa100351920ea89bfcbe53ba8bd4c013a1a3453fd807549a5ede2 + elliptic: ^6.5.3 + checksum: 0dd7fca9711d09e152375b79acf1e3f306d1a25ba87b8ff14c2fd8e68b83aafe0a7dd6c4e540c9ffbdd227a5fa1ad9b81eca1f233c38bb47770597ba247e614b languageName: node linkType: hard @@ -26048,7 +26055,7 @@ __metadata: languageName: node linkType: hard -"create-hmac@npm:^1.1.0, create-hmac@npm:^1.1.4, create-hmac@npm:^1.1.7": +"create-hmac@npm:^1.1.4, create-hmac@npm:^1.1.7": version: 1.1.7 resolution: "create-hmac@npm:1.1.7" dependencies: @@ -26079,7 +26086,7 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0": +"create-require@npm:^1.1.0, create-require@npm:^1.1.1": version: 1.1.1 resolution: "create-require@npm:1.1.1" checksum: a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff @@ -26204,22 +26211,23 @@ __metadata: languageName: node linkType: hard -"crypto-browserify@npm:^3.11.0": - version: 3.12.0 - resolution: "crypto-browserify@npm:3.12.0" +"crypto-browserify@npm:^3.12.1": + version: 3.12.1 + resolution: "crypto-browserify@npm:3.12.1" dependencies: - browserify-cipher: ^1.0.0 - browserify-sign: ^4.0.0 - create-ecdh: ^4.0.0 - create-hash: ^1.1.0 - create-hmac: ^1.1.0 - diffie-hellman: ^5.0.0 - inherits: ^2.0.1 - pbkdf2: ^3.0.3 - public-encrypt: ^4.0.0 - randombytes: ^2.0.0 - randomfill: ^1.0.3 - checksum: c1609af82605474262f3eaa07daa0b2140026bd264ab316d4bf1170272570dbe02f0c49e29407fe0d3634f96c507c27a19a6765fb856fed854a625f9d15618e2 + browserify-cipher: ^1.0.1 + browserify-sign: ^4.2.3 + create-ecdh: ^4.0.4 + create-hash: ^1.2.0 + create-hmac: ^1.1.7 + diffie-hellman: ^5.0.3 + hash-base: ~3.0.4 + inherits: ^2.0.4 + pbkdf2: ^3.1.2 + public-encrypt: ^4.0.3 + randombytes: ^2.1.0 + randomfill: ^1.0.4 + checksum: 4e643dd5acfff80fbe2cc567feb75a22d726cc4df34772c988f326976c3c1ee1f8a611a33498dab11568cff3e134f0bd44a0e1f4c216585e5877ab5327cdb6fc languageName: node linkType: hard @@ -27201,7 +27209,7 @@ __metadata: languageName: node linkType: hard -"diffie-hellman@npm:^5.0.0": +"diffie-hellman@npm:^5.0.3": version: 5.0.3 resolution: "diffie-hellman@npm:5.0.3" dependencies: @@ -27369,10 +27377,10 @@ __metadata: languageName: node linkType: hard -"domain-browser@npm:^1.1.1": - version: 1.2.0 - resolution: "domain-browser@npm:1.2.0" - checksum: 8f1235c7f49326fb762f4675795246a6295e7dd566b4697abec24afdba2460daa7dfbd1a73d31efbf5606b3b7deadb06ce47cf06f0a476e706153d62a4ff2b90 +"domain-browser@npm:4.22.0": + version: 4.22.0 + resolution: "domain-browser@npm:4.22.0" + checksum: e7ce1c19073e17dec35cfde050a3ddaac437d3ba8b870adabf9d5682e665eab3084df05de432dedf25b34303f0a2c71ac30f1cdba61b1aea018047b10de3d988 languageName: node linkType: hard @@ -27679,9 +27687,9 @@ __metadata: languageName: node linkType: hard -"elliptic@npm:^6.0.0, elliptic@npm:^6.5.4": - version: 6.6.0 - resolution: "elliptic@npm:6.6.0" +"elliptic@npm:^6.5.3, elliptic@npm:^6.5.5": + version: 6.6.1 + resolution: "elliptic@npm:6.6.1" dependencies: bn.js: ^4.11.9 brorand: ^1.1.0 @@ -27690,7 +27698,7 @@ __metadata: inherits: ^2.0.4 minimalistic-assert: ^1.0.1 minimalistic-crypto-utils: ^1.0.1 - checksum: e912349b883e694bfe65005214237a470c9a098a6ba36fd24396d0ab07feb399920c0738aeed1aed6cf5dca9c64fd479e212faed3a75c9d81453671ef0de5157 + checksum: 27b14a52f68bbbc0720da259f712cb73e953f6d2047958cd02fb0d0ade2e83849dc39fb4af630889c67df8817e24237428cf59c4f4c07700f755b401149a7375 languageName: node linkType: hard @@ -31277,13 +31285,13 @@ __metadata: languageName: node linkType: hard -"hash-base@npm:^3.0.0": - version: 3.0.4 - resolution: "hash-base@npm:3.0.4" +"hash-base@npm:^3.0.0, hash-base@npm:~3.0, hash-base@npm:~3.0.4": + version: 3.0.5 + resolution: "hash-base@npm:3.0.5" dependencies: - inherits: ^2.0.1 - safe-buffer: ^5.0.1 - checksum: 878465a0dfcc33cce195c2804135352c590d6d10980adc91a9005fd377e77f2011256c2b7cfce472e3f2e92d561d1bf3228d2da06348a9017ce9a258b3b49764 + inherits: ^2.0.4 + safe-buffer: ^5.2.1 + checksum: 6a82675a5de2ea9347501bbe655a2334950c7ec972fd9810ae9529e06aeab8f7e8ef68fc2112e5e6f0745561a7e05326efca42ad59bb5fd116537f5f8b0a216d languageName: node linkType: hard @@ -32055,20 +32063,13 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.1, inherits@npm:~2.0.3, inherits@npm:~2.0.4": +"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 languageName: node linkType: hard -"inherits@npm:2.0.1": - version: 2.0.1 - resolution: "inherits@npm:2.0.1" - checksum: 6536b9377296d4ce8ee89c5c543cb75030934e61af42dba98a428e7d026938c5985ea4d1e3b87743a5b834f40ed1187f89c2d7479e9d59e41d2d1051aefba07b - languageName: node - linkType: hard - "inherits@npm:2.0.3": version: 2.0.3 resolution: "inherits@npm:2.0.3" @@ -32439,12 +32440,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1": - version: 2.15.1 - resolution: "is-core-module@npm:2.15.1" +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.0": + version: 2.16.1 + resolution: "is-core-module@npm:2.16.1" dependencies: hasown: ^2.0.2 - checksum: df134c168115690724b62018c37b2f5bba0d5745fa16960b329c5a00883a8bea6a5632fdb1e3efcce237c201826ba09f93197b7cd95577ea56b0df335be23633 + checksum: 6ec5b3c42d9cbf1ac23f164b16b8a140c3cec338bf8f884c076ca89950c7cc04c33e78f02b8cae7ff4751f3247e3174b2330f1fe4de194c7210deb8b1ea316a7 languageName: node linkType: hard @@ -32645,6 +32646,16 @@ __metadata: languageName: node linkType: hard +"is-nan@npm:^1.3.2": + version: 1.3.2 + resolution: "is-nan@npm:1.3.2" + dependencies: + call-bind: ^1.0.0 + define-properties: ^1.1.3 + checksum: 5dfadcef6ad12d3029d43643d9800adbba21cf3ce2ec849f734b0e14ee8da4070d82b15fdb35138716d02587c6578225b9a22779cab34888a139cc43e4e3610a + languageName: node + linkType: hard + "is-network-error@npm:^1.0.0": version: 1.0.1 resolution: "is-network-error@npm:1.0.1" @@ -32998,13 +33009,6 @@ __metadata: languageName: node linkType: hard -"isarray@npm:^1.0.0, isarray@npm:~1.0.0": - version: 1.0.0 - resolution: "isarray@npm:1.0.0" - checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab - languageName: node - linkType: hard - "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -33012,6 +33016,13 @@ __metadata: languageName: node linkType: hard +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab + languageName: node + linkType: hard + "isbinaryfile@npm:^4.0.10, isbinaryfile@npm:^4.0.8": version: 4.0.10 resolution: "isbinaryfile@npm:4.0.10" @@ -33106,6 +33117,13 @@ __metadata: languageName: node linkType: hard +"isomorphic-timers-promises@npm:^1.0.1": + version: 1.0.1 + resolution: "isomorphic-timers-promises@npm:1.0.1" + checksum: 16ef59f0fbcceba1a037c74b5f7195d252ae058724ccd3e53b37ad034e8498f5532084e8ab18e7940ba3fa8fca2f21403d00eed15802ab1f7cab7c099cba62a8 + languageName: node + linkType: hard + "isomorphic-ws@npm:5.0.0, isomorphic-ws@npm:^5.0.0": version: 5.0.0 resolution: "isomorphic-ws@npm:5.0.0" @@ -37754,37 +37772,6 @@ __metadata: languageName: node linkType: hard -"node-libs-browser@npm:^2.2.1": - version: 2.2.1 - resolution: "node-libs-browser@npm:2.2.1" - dependencies: - assert: ^1.1.1 - browserify-zlib: ^0.2.0 - buffer: ^4.3.0 - console-browserify: ^1.1.0 - constants-browserify: ^1.0.0 - crypto-browserify: ^3.11.0 - domain-browser: ^1.1.1 - events: ^3.0.0 - https-browserify: ^1.0.0 - os-browserify: ^0.3.0 - path-browserify: 0.0.1 - process: ^0.11.10 - punycode: ^1.2.4 - querystring-es3: ^0.2.0 - readable-stream: ^2.3.3 - stream-browserify: ^2.0.1 - stream-http: ^2.7.2 - string_decoder: ^1.0.0 - timers-browserify: ^2.0.4 - tty-browserify: 0.0.0 - url: ^0.11.0 - util: ^0.11.0 - vm-browserify: ^1.0.1 - checksum: 41fa7927378edc0cb98a8cc784d3f4a47e43378d3b42ec57a23f81125baa7287c4b54d6d26d062072226160a3ce4d8b7a62e873d2fb637aceaddf71f5a26eca0 - languageName: node - linkType: hard - "node-machine-id@npm:^1.1.12": version: 1.1.12 resolution: "node-machine-id@npm:1.1.12" @@ -37846,6 +37833,41 @@ __metadata: languageName: node linkType: hard +"node-stdlib-browser@npm:^1.3.1": + version: 1.3.1 + resolution: "node-stdlib-browser@npm:1.3.1" + dependencies: + assert: ^2.0.0 + browser-resolve: ^2.0.0 + browserify-zlib: ^0.2.0 + buffer: ^5.7.1 + console-browserify: ^1.1.0 + constants-browserify: ^1.0.0 + create-require: ^1.1.1 + crypto-browserify: ^3.12.1 + domain-browser: 4.22.0 + events: ^3.0.0 + https-browserify: ^1.0.0 + isomorphic-timers-promises: ^1.0.1 + os-browserify: ^0.3.0 + path-browserify: ^1.0.1 + pkg-dir: ^5.0.0 + process: ^0.11.10 + punycode: ^1.4.1 + querystring-es3: ^0.2.1 + readable-stream: ^3.6.0 + stream-browserify: ^3.0.0 + stream-http: ^3.2.0 + string_decoder: ^1.0.0 + timers-browserify: ^2.0.4 + tty-browserify: 0.0.1 + url: ^0.11.4 + util: ^0.12.4 + vm-browserify: ^1.0.1 + checksum: 2012dbd84de654c60414c7d88817c1c8214324fa4e77f09395aa2a9d3722f49fafc0abf1643dc5998a96ebcee2409ee0d957ec4c4fd50d3ff5b40e031d1feb90 + languageName: node + linkType: hard + "node-watch@npm:0.7.3": version: 0.7.3 resolution: "node-watch@npm:0.7.3" @@ -38221,6 +38243,16 @@ __metadata: languageName: node linkType: hard +"object-is@npm:^1.1.5": + version: 1.1.6 + resolution: "object-is@npm:1.1.6" + dependencies: + call-bind: ^1.0.7 + define-properties: ^1.2.1 + checksum: 3ea22759967e6f2380a2cbbd0f737b42dc9ddb2dfefdb159a1b927fea57335e1b058b564bfa94417db8ad58cddab33621a035de6f5e5ad56d89f2dd03e66c6a1 + languageName: node + linkType: hard + "object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" @@ -38906,16 +38938,17 @@ __metadata: languageName: node linkType: hard -"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.6": - version: 5.1.6 - resolution: "parse-asn1@npm:5.1.6" +"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.7": + version: 5.1.7 + resolution: "parse-asn1@npm:5.1.7" dependencies: - asn1.js: ^5.2.0 - browserify-aes: ^1.0.0 - evp_bytestokey: ^1.0.0 - pbkdf2: ^3.0.3 - safe-buffer: ^5.1.1 - checksum: 9243311d1f88089bc9f2158972aa38d1abd5452f7b7cabf84954ed766048fe574d434d82c6f5a39b988683e96fb84cd933071dda38927e03469dc8c8d14463c7 + asn1.js: ^4.10.1 + browserify-aes: ^1.2.0 + evp_bytestokey: ^1.0.3 + hash-base: ~3.0 + pbkdf2: ^3.1.2 + safe-buffer: ^5.2.1 + checksum: 93c7194c1ed63a13e0b212d854b5213ad1aca0ace41c66b311e97cca0519cf9240f79435a0306a3b412c257f0ea3f1953fd0d9549419a0952c9e995ab361fd6c languageName: node linkType: hard @@ -39171,13 +39204,6 @@ __metadata: languageName: node linkType: hard -"path-browserify@npm:0.0.1": - version: 0.0.1 - resolution: "path-browserify@npm:0.0.1" - checksum: ae8dcd45d0d3cfbaf595af4f206bf3ed82d77f72b4877ae7e77328079e1468c84f9386754bb417d994d5a19bf47882fd253565c18441cd5c5c90ae5187599e35 - languageName: node - linkType: hard - "path-browserify@npm:^1.0.1": version: 1.0.1 resolution: "path-browserify@npm:1.0.1" @@ -39324,16 +39350,16 @@ __metadata: languageName: node linkType: hard -"pbkdf2@npm:^3.0.3": - version: 3.0.17 - resolution: "pbkdf2@npm:3.0.17" +"pbkdf2@npm:^3.1.2": + version: 3.1.2 + resolution: "pbkdf2@npm:3.1.2" dependencies: create-hash: ^1.1.2 create-hmac: ^1.1.4 ripemd160: ^2.0.1 safe-buffer: ^5.0.1 sha.js: ^2.4.8 - checksum: 9c9062b4bf300bfc03214a8665ab1c8ede227fca1d5bd8b8d0a9d317a941ff64c80b19810288a8cc0f774d603dce249d4b734e62b68dfc784be4ad1e6c0a81f5 + checksum: 2c950a100b1da72123449208e231afc188d980177d021d7121e96a2de7f2abbc96ead2b87d03d8fe5c318face097f203270d7e27908af9f471c165a4e8e69c92 languageName: node linkType: hard @@ -39635,6 +39661,15 @@ __metadata: languageName: node linkType: hard +"pkg-dir@npm:^5.0.0": + version: 5.0.0 + resolution: "pkg-dir@npm:5.0.0" + dependencies: + find-up: ^5.0.0 + checksum: b167bb8dac7bbf22b1d5e30ec223e6b064b84b63010c9d49384619a36734caf95ed23ad23d4f9bd975e8e8082b60a83395f43a89bb192df53a7c25a38ecb57d9 + languageName: node + linkType: hard + "pkg-up@npm:^3.1.0": version: 3.1.0 resolution: "pkg-up@npm:3.1.0" @@ -40634,7 +40669,7 @@ __metadata: languageName: node linkType: hard -"public-encrypt@npm:^4.0.0": +"public-encrypt@npm:^4.0.3": version: 4.0.3 resolution: "public-encrypt@npm:4.0.3" dependencies: @@ -40665,14 +40700,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:1.3.2": - version: 1.3.2 - resolution: "punycode@npm:1.3.2" - checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6 - languageName: node - linkType: hard - -"punycode@npm:^1.2.4, punycode@npm:^1.3.2": +"punycode@npm:^1.3.2, punycode@npm:^1.4.1": version: 1.4.1 resolution: "punycode@npm:1.4.1" checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 @@ -40718,12 +40746,12 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.7.0, qs@npm:^6.9.4": - version: 6.13.1 - resolution: "qs@npm:6.13.1" +"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.7.0, qs@npm:^6.9.4": + version: 6.14.0 + resolution: "qs@npm:6.14.0" dependencies: - side-channel: ^1.0.6 - checksum: 86c5059146955fab76624e95771031541328c171b1d63d48a7ac3b1fdffe262faf8bc5fcadc1684e6f3da3ec87a8dedc8c0009792aceb20c5e94dc34cf468bb9 + side-channel: ^1.1.0 + checksum: 189b52ad4e9a0da1a16aff4c58b2a554a8dad9bd7e287c7da7446059b49ca2e33a49e570480e8be406b87fccebf134f51c373cbce36c8c83859efa0c9b71d635 languageName: node linkType: hard @@ -40746,20 +40774,13 @@ __metadata: languageName: node linkType: hard -"querystring-es3@npm:^0.2.0": +"querystring-es3@npm:^0.2.1": version: 0.2.1 resolution: "querystring-es3@npm:0.2.1" checksum: 691e8d6b8b157e7cd49ae8e83fcf86de39ab3ba948c25abaa94fba84c0986c641aa2f597770848c64abce290ed17a39c9df6df737dfa7e87c3b63acc7d225d61 languageName: node linkType: hard -"querystring@npm:0.2.0": - version: 0.2.0 - resolution: "querystring@npm:0.2.0" - checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69 - languageName: node - linkType: hard - "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -40881,7 +40902,7 @@ __metadata: languageName: node linkType: hard -"randomfill@npm:^1.0.3": +"randomfill@npm:^1.0.4": version: 1.0.4 resolution: "randomfill@npm:1.0.4" dependencies: @@ -41783,7 +41804,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": +"readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -41794,7 +41815,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:^2.3.6, readable-stream@npm:~2.3.6": +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:^2.3.8, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -42349,7 +42370,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:1.22.8, resolve@npm:^1.1.6, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.22.8, resolve@npm:~1.22.1, resolve@npm:~1.22.2": +"resolve@npm:1.22.8": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -42362,6 +42383,19 @@ __metadata: languageName: node linkType: hard +"resolve@npm:^1.1.6, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.22.8, resolve@npm:~1.22.1, resolve@npm:~1.22.2": + version: 1.22.10 + resolution: "resolve@npm:1.22.10" + dependencies: + is-core-module: ^2.16.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: ab7a32ff4046fcd7c6fdd525b24a7527847d03c3650c733b909b01b757f92eb23510afa9cc3e9bf3f26a3e073b48c88c706dfd4c1d2fb4a16a96b73b6328ddcf + languageName: node + linkType: hard + "resolve@npm:^2.0.0-next.5": version: 2.0.0-next.5 resolution: "resolve@npm:2.0.0-next.5" @@ -42375,7 +42409,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@1.22.8#~builtin, resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@^1.22.8#~builtin, resolve@patch:resolve@~1.22.1#~builtin, resolve@patch:resolve@~1.22.2#~builtin": +"resolve@patch:resolve@1.22.8#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -42388,6 +42422,19 @@ __metadata: languageName: node linkType: hard +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@^1.22.8#~builtin, resolve@patch:resolve@~1.22.1#~builtin, resolve@patch:resolve@~1.22.2#~builtin": + version: 1.22.10 + resolution: "resolve@patch:resolve@npm%3A1.22.10#~builtin::version=1.22.10&hash=c3c19d" + dependencies: + is-core-module: ^2.16.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 8aac1e4e4628bd00bf4b94b23de137dd3fe44097a8d528fd66db74484be929936e20c696e1a3edf4488f37e14180b73df6f600992baea3e089e8674291f16c9d + languageName: node + linkType: hard + "resolve@patch:resolve@^2.0.0-next.5#~builtin": version: 2.0.0-next.5 resolution: "resolve@patch:resolve@npm%3A2.0.0-next.5#~builtin::version=2.0.0-next.5&hash=c3c19d" @@ -44143,7 +44190,7 @@ __metadata: languageName: node linkType: hard -"stream-browserify@npm:3.0.0": +"stream-browserify@npm:3.0.0, stream-browserify@npm:^3.0.0": version: 3.0.0 resolution: "stream-browserify@npm:3.0.0" dependencies: @@ -44153,16 +44200,6 @@ __metadata: languageName: node linkType: hard -"stream-browserify@npm:^2.0.1": - version: 2.0.2 - resolution: "stream-browserify@npm:2.0.2" - dependencies: - inherits: ~2.0.1 - readable-stream: ^2.0.2 - checksum: 8de7bcab5582e9a931ae1a4768be7efe8fa4b0b95fd368d16d8cf3e494b897d6b0a7238626de5d71686e53bddf417fd59d106cfa3af0ec055f61a8d1f8fc77b3 - languageName: node - linkType: hard - "stream-buffers@npm:^3.0.2": version: 3.0.3 resolution: "stream-buffers@npm:3.0.3" @@ -44186,16 +44223,15 @@ __metadata: languageName: node linkType: hard -"stream-http@npm:^2.7.2": - version: 2.8.3 - resolution: "stream-http@npm:2.8.3" +"stream-http@npm:^3.2.0": + version: 3.2.0 + resolution: "stream-http@npm:3.2.0" dependencies: builtin-status-codes: ^3.0.0 - inherits: ^2.0.1 - readable-stream: ^2.3.6 - to-arraybuffer: ^1.0.0 - xtend: ^4.0.0 - checksum: f57dfaa21a015f72e6ce6b199cf1762074cfe8acf0047bba8f005593754f1743ad0a91788f95308d9f3829ad55742399ad27b4624432f2752a08e62ef4346e05 + inherits: ^2.0.4 + readable-stream: ^3.6.0 + xtend: ^4.0.2 + checksum: c9b78453aeb0c84fcc59555518ac62bacab9fa98e323e7b7666e5f9f58af8f3155e34481078509b02929bd1268427f664d186604cdccee95abc446099b339f83 languageName: node linkType: hard @@ -45411,13 +45447,6 @@ __metadata: languageName: node linkType: hard -"to-arraybuffer@npm:^1.0.0": - version: 1.0.1 - resolution: "to-arraybuffer@npm:1.0.1" - checksum: 31433c10b388722729f5da04c6b2a06f40dc84f797bb802a5a171ced1e599454099c6c5bc5118f4b9105e7d049d3ad9d0f71182b77650e4fdb04539695489941 - languageName: node - linkType: hard - "to-readable-stream@npm:^1.0.0": version: 1.0.0 resolution: "to-readable-stream@npm:1.0.0" @@ -45794,10 +45823,10 @@ __metadata: languageName: node linkType: hard -"tty-browserify@npm:0.0.0": - version: 0.0.0 - resolution: "tty-browserify@npm:0.0.0" - checksum: a06f746acc419cb2527ba19b6f3bd97b4a208c03823bfb37b2982629d2effe30ebd17eaed0d7e2fc741f3c4f2a0c43455bd5fb4194354b378e78cfb7ca687f59 +"tty-browserify@npm:0.0.1": + version: 0.0.1 + resolution: "tty-browserify@npm:0.0.1" + checksum: 93b745d43fa5a7d2b948fa23be8d313576d1d884b48acd957c07710bac1c0d8ac34c0556ad4c57c73d36e11741763ef66b3fb4fb97b06b7e4d525315a3cd45f5 languageName: node linkType: hard @@ -46636,13 +46665,13 @@ __metadata: languageName: node linkType: hard -"url@npm:^0.11.0": - version: 0.11.0 - resolution: "url@npm:0.11.0" +"url@npm:^0.11.0, url@npm:^0.11.4": + version: 0.11.4 + resolution: "url@npm:0.11.4" dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - checksum: 50d100d3dd2d98b9fe3ada48cadb0b08aa6be6d3ac64112b867b56b19be4bfcba03c2a9a0d7922bfd7ac17d4834e88537749fe182430dfd9b68e520175900d90 + punycode: ^1.4.1 + qs: ^6.12.3 + checksum: c25e587723d343d5d4248892393bfa5039ded9c2c07095a9d005bc64b7cb8956d623c0d8da8d1a28f71986a7a8d80fc2e9f9cf84235e48fa435a5cb4451062c6 languageName: node linkType: hard @@ -46773,24 +46802,6 @@ __metadata: languageName: node linkType: hard -"util@npm:0.10.3": - version: 0.10.3 - resolution: "util@npm:0.10.3" - dependencies: - inherits: 2.0.1 - checksum: bd800f5d237a82caddb61723a6cbe45297d25dd258651a31335a4d5d981fd033cb4771f82db3d5d59b582b187cb69cfe727dc6f4d8d7826f686ee6c07ce611e0 - languageName: node - linkType: hard - -"util@npm:^0.11.0": - version: 0.11.1 - resolution: "util@npm:0.11.1" - dependencies: - inherits: 2.0.3 - checksum: 80bee6a2edf5ab08dcb97bfe55ca62289b4e66f762ada201f2c5104cb5e46474c8b334f6504d055c0e6a8fda10999add9bcbd81ba765e7f37b17dc767331aa55 - languageName: node - linkType: hard - "util@npm:^0.12.3, util@npm:^0.12.4, util@npm:^0.12.5": version: 0.12.5 resolution: "util@npm:0.12.5" @@ -47882,7 +47893,7 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.0": +"xtend@npm:^4.0.0, xtend@npm:^4.0.2": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a