From c6ead25801826a894e3a0802edc360cd2e35d98a Mon Sep 17 00:00:00 2001 From: ElaineDeMattosSilvaB Date: Sun, 23 Jun 2024 18:24:28 +0200 Subject: [PATCH 001/317] feat: add config options to allow better group membership modeling Signed-off-by: ElaineDeMattosSilvaB --- docs/integrations/gitlab/org.md | 17 ++++++++++++++++- .../src/lib/types.ts | 12 +++++++++++- .../GitlabOrgDiscoveryEntityProvider.ts | 17 +++++++++++------ .../src/providers/config.test.ts | 8 ++++++++ .../src/providers/config.ts | 7 +++++++ 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/docs/integrations/gitlab/org.md b/docs/integrations/gitlab/org.md index ccf8f19239..23323bae24 100644 --- a/docs/integrations/gitlab/org.md +++ b/docs/integrations/gitlab/org.md @@ -172,7 +172,9 @@ catalog: host: gitlab.com orgEnabled: true group: org/teams # Required for gitlab.com when `orgEnabled: true`. Optional for self managed. Must not end with slash. Accepts only groups under the provided path (which will be stripped) - allowInherited: true # Allow groups to be ingested even if there are no direct members. + allowInherited: true # Optional. Members of any ancestor groups will also be considered members of the current group. + allowDescendants: true # Optional. Members of any descendant groups will also be considered members of the current group. + allowSharedFromGroups: true # Optional. Members of any invited groups will also be considered members of the current group. groupPattern: '[\s\S]*' # Optional. Filters found groups based on provided pattern. Defaults to `[\s\S]*`, which means to not filter anything schedule: # Same options as in TaskScheduleDefinition. Optional for the Legacy Backend System. # supports cron, ISO duration, "human duration" as used in code @@ -194,6 +196,19 @@ entities will only be ingested for the configured group, or its descendant group but not any ancestor groups higher than the configured group path. Only groups which contain members will be ingested. +### Subgroup Membership + +GitLab groups and subgroups provide a hierarchical structure to organize projects and users. Membership in a parent group automatically extends to its subgroups, ensuring consistent permissions across all levels. Additionally, membership can be managed using invited groups, allowing one group to be added to another. For Backstage users integrating with GitLab, understanding this [inheritance model](https://docs.gitlab.co.jp/ee/user/group/subgroups/#subgroup-membership) and the use of invited groups is crucial for accurately mapping and managing group and user entities. + +The `GitLabOrgDiscoveryEntityProvider` mirrors GitLab's membership behavior as follows: + +- by default, every direct member of a GitLab group is also a member of the corresponding group in Backstage; +- to include members of subgroups as members of the parent group, enable the `allowDescendants` option; +- to include members of parent groups as members of their subgroups, enable the `allowInherited` option. This also has the effect that subgroups with no direct members will not be skipped in the group ingestion process and will be added as a group entity in Backstage; +- to include members of invited groups as members of the inviting group, enable the `allowSharedFromGroups` option. + +Refer to the [GitLab Group Member Relation](https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation) documentation for more information. + ### Users For self hosted, all `User` entities are ingested from the entire instance. diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index 2857fea162..a674d4c222 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -184,10 +184,20 @@ export type GitlabProviderConfig = { groupPattern: RegExp; /** - * If true, the provider will also ingest add inherited users to the ingested groups + * If true, the provider will also add members of inherited groups (ascendant) to the ingested groups. See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation */ allowInherited?: boolean; + /** + * If true, the provider will also add members of descendant groups to the ingested groups. See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation + */ + allowDescendants?: boolean; + + /** + * If true, the provider will also add members of invited groups to the ingested groups. See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation + */ + allowSharedFromGroups?: boolean; + orgEnabled?: boolean; schedule?: TaskScheduleDefinition; /** diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index e10f9ee313..2c750a2930 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -424,9 +424,7 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { let groupUsers: PagedResponse = { items: [] }; try { - const relations = this.config.allowInherited - ? ['DIRECT', 'INHERITED'] - : ['DIRECT']; + const relations = this.getRelations(this.config); groupUsers = await this.gitLabClient.getGroupMembers( group.full_path, relations, @@ -703,9 +701,7 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { return; } - const relations = this.config.allowInherited - ? ['DIRECT', 'INHERITED'] - : ['DIRECT']; + const relations = this.getRelations(this.config); // fetch group members from GitLab const groupMembers = await this.gitLabClient.getGroupMembers( @@ -781,4 +777,13 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { entity, ) as Entity; } + + private getRelations(config: any) { + return [ + 'DIRECT', + ...(config.allowInherited ? ['INHERITED'] : []), + ...(config.allowSharedFromGroups ? ['SHARED_FROM_GROUPS'] : []), + ...(config.allowDescendants ? ['DESCENDANTS'] : []), + ]; + } } diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts index 09b42e6475..c2c1fd14f7 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -59,6 +59,8 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, + allowDescendants: false, + allowSharedFromGroups: false, schedule: undefined, skipForkedRepos: false, }), @@ -97,6 +99,8 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, + allowDescendants: false, + allowSharedFromGroups: false, schedule: undefined, skipForkedRepos: false, }), @@ -136,6 +140,8 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, + allowDescendants: false, + allowSharedFromGroups: false, schedule: undefined, skipForkedRepos: true, }), @@ -177,6 +183,8 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, + allowDescendants: false, + allowSharedFromGroups: false, skipForkedRepos: false, schedule: { frequency: Duration.fromISO('PT30M'), diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.ts index 8606a525bd..abed6d83c4 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.ts @@ -45,6 +45,11 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { const orgEnabled: boolean = config.getOptionalBoolean('orgEnabled') ?? false; const allowInherited: boolean = config.getOptionalBoolean('allowInherited') ?? false; + const allowDescendants: boolean = + config.getOptionalBoolean('allowDescendants') ?? false; + const allowSharedFromGroups: boolean = + config.getOptionalBoolean('allowSharedFromGroups') ?? false; + const skipForkedRepos: boolean = config.getOptionalBoolean('skipForkedRepos') ?? false; @@ -65,6 +70,8 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { schedule, orgEnabled, allowInherited, + allowDescendants, + allowSharedFromGroups, skipForkedRepos, }; } From 0476be3c07ca7a1539a51d1006b967541f4ef576 Mon Sep 17 00:00:00 2001 From: ElaineDeMattosSilvaB Date: Sun, 23 Jun 2024 18:24:57 +0200 Subject: [PATCH 002/317] feat: add changeset Signed-off-by: ElaineDeMattosSilvaB --- .changeset/loud-singers-happen.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/loud-singers-happen.md diff --git a/.changeset/loud-singers-happen.md b/.changeset/loud-singers-happen.md new file mode 100644 index 0000000000..7d86f96e62 --- /dev/null +++ b/.changeset/loud-singers-happen.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +Allow Backstage to mirror GitLab Member Relation behavior by adding the `allowDescendants` and `allowSharedFromGroups` configuration options. From 020ad7224bea98f030002a848acd8c42a3029881 Mon Sep 17 00:00:00 2001 From: ElaineDeMattosSilvaB Date: Sun, 23 Jun 2024 18:28:25 +0200 Subject: [PATCH 003/317] feat: add api report Signed-off-by: ElaineDeMattosSilvaB --- plugins/catalog-backend-module-gitlab/api-report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/catalog-backend-module-gitlab/api-report.md b/plugins/catalog-backend-module-gitlab/api-report.md index d7fcd1b94d..5f4f3aa622 100644 --- a/plugins/catalog-backend-module-gitlab/api-report.md +++ b/plugins/catalog-backend-module-gitlab/api-report.md @@ -106,6 +106,8 @@ export type GitlabProviderConfig = { userPattern: RegExp; groupPattern: RegExp; allowInherited?: boolean; + allowDescendants?: boolean; + allowSharedFromGroups?: boolean; orgEnabled?: boolean; schedule?: TaskScheduleDefinition; skipForkedRepos?: boolean; From 0fa986a2c8980d0d775e40aa690712492489231c Mon Sep 17 00:00:00 2001 From: Himesh Ladva Date: Wed, 17 Jul 2024 11:15:44 +0100 Subject: [PATCH 004/317] docs: Add GlobalLogic Terraform plugin to Plugin Directory Signed-off-by: Himesh Ladva --- microsite/data/plugins/terraform.yaml | 10 ++++++++++ microsite/static/img/terraform-logo.svg | 1 + 2 files changed, 11 insertions(+) create mode 100644 microsite/data/plugins/terraform.yaml create mode 100644 microsite/static/img/terraform-logo.svg diff --git a/microsite/data/plugins/terraform.yaml b/microsite/data/plugins/terraform.yaml new file mode 100644 index 0000000000..de33d37fa3 --- /dev/null +++ b/microsite/data/plugins/terraform.yaml @@ -0,0 +1,10 @@ +--- +title: Terraform +author: GlobalLogic UK&I +authorUrl: https://www.globallogic.com +category: Infrastructure +description: Easily view Terraform runs for a specific workspace. +documentation: https://github.com/globallogicuki/globallogic-backstage-plugins/tree/main/plugins/terraform +iconUrl: /img/terraform-logo.svg +npmPackageName: '@globallogicuki/backstage-plugin-terraform' +addedDate: '2024-07-17' diff --git a/microsite/static/img/terraform-logo.svg b/microsite/static/img/terraform-logo.svg new file mode 100644 index 0000000000..9ec37bdab4 --- /dev/null +++ b/microsite/static/img/terraform-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file From 98c1c1fe76c6d9096e1093ffba36db6c758fa628 Mon Sep 17 00:00:00 2001 From: AmbrishRamachandiran Date: Mon, 22 Jul 2024 15:08:43 +0530 Subject: [PATCH 005/317] Updated documentation of utility apis Signed-off-by: AmbrishRamachandiran --- docs/api/utility-apis.md | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/docs/api/utility-apis.md b/docs/api/utility-apis.md index 44c00d1ab7..5df8fc334a 100644 --- a/docs/api/utility-apis.md +++ b/docs/api/utility-apis.md @@ -6,10 +6,7 @@ description: Backstage Utility APIs ## Introduction -Backstage Plugins strive to be self-contained, with as much functionality as -possible residing within the plugin itself and its backend APIs. There will -however always be a need for plugins to communicate outside of its boundaries, -both with other plugins and the app itself. +Backstage plugins strive to be self-contained, with as much functionality as possible residing within the plugin itself and its backend APIs. There will, however, always be a need for plugins to communicate outside of their boundaries, both with other plugins and with the app itself. Backstage provides two primary methods for plugins to communicate across their boundaries in client-side code. The first one being the @@ -61,7 +58,7 @@ Note that there is no explicit type given for embedded, and [`useApi`](../reference/core-plugin-api.useapi.md) is able to infer the type. -Also note that consuming Utility APIs is not limited to plugins, it can be done +Also note that consuming Utility APIs is not limited to plugins; it can be done from any component inside Backstage, including the ones in [`@backstage/core-plugin-api`](../reference/core-plugin-api.md). The only requirement is that they are beneath the `AppProvider` in the react tree. @@ -93,7 +90,7 @@ createApiFactory({ }); ``` -In this example the [`errorApiRef`](../reference/core-plugin-api.errorapiref.md) +In this example, the [`errorApiRef`](../reference/core-plugin-api.errorapiref.md) is our API, which encapsulates the [`ErrorApi`](../reference/core-plugin-api.errorapi.md) type. The [`alertApiRef`](../reference/core-plugin-api.alertapiref.md) is our single @@ -135,11 +132,11 @@ there is no step that needs to be taken to include these APIs in an app. ### Plugin APIs In addition to the core APIs, plugins can define and export their own APIs. -While doing so they should usually also provide default implementations of their -own APIs, for example, the `catalog` plugin exports `catalogApiRef`, and also +While doing so, they should usually also provide default implementations of their +own APIs; for example, the `catalog` plugin exports `catalogApiRef` and also supplies a default [`ApiFactory`](../reference/core-plugin-api.apifactory.md) of that API using the `CatalogClient`. There is one restriction to plugin-provided -API Factories: plugins may not supply factories for core APIs, trying to do so +API Factories: plugins may not supply factories for core APIs; trying to do so will cause the app to refuse to start. Plugins supply their APIs through the `apis` option of @@ -165,16 +162,16 @@ export const techdocsPlugin = createPlugin({ ### App APIs Lastly, the app itself is the final point where APIs can be added, and what has -the final say in what APIs will be loaded at runtime. The app may override the +the final say in what APIs will be loaded at runtime? The app may override the factories for any of the core or plugin APIs, with the exception of the config, app theme, and identity APIs. These are static APIs that are tied into the -[`createApp`](../reference/app-defaults.createapp.md) implementation, and +[`createApp`](../reference/app-defaults.createapp.md) implementation and therefore not possible to override. Overriding APIs is useful for apps that want to switch out behavior to tailor it -to their environment. In some cases plugins may also export multiple +to their environment. In some cases plugins, may also export multiple implementations of the same API, where they each have their own different -requirements on for example backend storage and surrounding environment. +requirements, for example, backend storage and the surrounding environment. Supplying APIs to the app works just like for plugins: @@ -238,21 +235,14 @@ checked by the type embedded in the ## Defining custom Utility APIs Plugins are free to define their own Utility APIs. Simply define the TypeScript -interface for the API, and create an +interface for the API and create an [`ApiRef`](../reference/core-plugin-api.apiref.md) using [`createApiRef`](../reference/core-plugin-api.createapiref.md) exported from -[`@backstage/core-plugin-api`](../reference/core-plugin-api.md). Also be sure to -provide at least one implementation of the API, and to declare a default factory +[`@backstage/core-plugin-api`](../reference/core-plugin-api.md). Also, be sure to +provide at least one implementation of the API and to declare a default factory for the API in [`createPlugin`](../reference/core-plugin-api.createplugin.md). -Custom Utility APIs can be either public or private, which is up to the plugin -to choose. Private APIs do not expose an external API surface, and it's -therefore possible to make breaking changes to the API without affecting other -users of the plugin. If an API is made public however, it opens up for other -plugins to make use of the API, and it also makes it possible for users for your -plugin to override the API in the app. It is however important to maintain -backwards compatibility of public APIs, as you may otherwise break apps that are -using your plugin. +Custom Utility APIs can be either public or private, which is up to the plugin to choose. Private APIs do not expose an external API surface, and it's therefore possible to make breaking changes to the API without affecting other users of the plugin. If an API is made public, however, it opens up for other plugins to make use of the API, and it also makes it possible for users for your plugin to override the API in the app. It is, however, important to maintain backward compatibility of public APIs, as you may otherwise break apps that are using your plugin. To make an API public, simply export the [`ApiRef`](../reference/core-plugin-api.apiref.md) of the API, and any associated From 8c8a62dc0689b1d29fd8d5ea2b721b84b9dee55c Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Mon, 29 Jul 2024 15:38:20 -0500 Subject: [PATCH 006/317] Updated Auth Getting started Guide Signed-off-by: Andre Wanlin --- docs/getting-started/config/authentication.md | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index e97ed3d89c..72990539ca 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -8,43 +8,48 @@ Audience: Admins or Developers ### Setting up authentication -There are multiple authentication providers available for you to use with -Backstage, feel free to follow -[the instructions for adding authentication](../../auth/index.md). +There are multiple authentication providers available for you to use with Backstage, feel free to follow [the instructions for adding authentication](../../auth/index.md). -For this tutorial we choose to use GitHub, a free service most of you might be -familiar with. For other options, see +For this tutorial we choose to use GitHub, a free service most of you might be familiar with. For other options, see [the auth provider documentation](../../auth/github/provider.md#create-an-oauth-app-on-github). -Go to -[https://github.com/settings/applications/new](https://github.com/settings/applications/new) -to create your OAuth App. The `Homepage URL` should point to Backstage's -frontend, in our tutorial it would be `http://localhost:3000`. The -`Authorization callback URL` will point to the auth backend, which will most -likely be `http://localhost:7007/api/auth/github/handler/frame`. +Go to [https://github.com/settings/applications/new](https://github.com/settings/applications/new) to create your OAuth App. The `Homepage URL` should point to Backstage's frontend, in our tutorial it would be `http://localhost:3000`. The `Authorization callback URL` will point to the auth backend, which will most likely be `http://localhost:7007/api/auth/github/handler/frame`. ![Screenshot of the GitHub OAuth creation page](../../assets/getting-started/gh-oauth.png) -Take note of the `Client ID` and the `Client Secret`. Open `app-config.yaml`, -and add your `clientId` and `clientSecret` to this file. It should end up -looking like this: +Take note of the `Client ID` and the `Client Secret` (clicking the "Generate a new client secret" button will get this value for you). Open `app-config.yaml`, and add your `clientId` and `clientSecret` to this file. It should end up looking like this: ```yaml title="app-config.yaml" auth: # see https://backstage.io/docs/auth/ to learn about auth providers + /* highlight-add-start */ environment: development + /* highlight-add-end */ providers: + # See https://backstage.io/docs/auth/guest/provider + guest: {} + /* highlight-add-start */ github: development: clientId: YOUR CLIENT ID clientSecret: YOUR CLIENT SECRET + signIn: + resolvers: + # Matches the GitHub username with the Backstage user entity name. + # See https://backstage.io/docs/auth/github/provider#resolvers for more resolvers. + - resolver: usernameMatchingUserEntityName + /* highlight-add-end */ ``` +:::note Note + +The default Backstage app comes with a guest Sign In Resolver. This resolver makes all users share a single "guest" identity and is only intended as a minimum requirement to quickly get up and running. You can read more about how [Sign In Resolvers](../../auth/identity-resolver.md#sign-in-resolvers) play a role in creating a [Backstage User Identity](../../auth/identity-resolver.md#backstage-user-identity) for logged in users. + +::: + ### Add sign-in option to the frontend -Backstage will re-read the configuration. If there's no errors, that's great! We -can continue with the last part of the configuration. The next step is needed to -change the sign-in page, this you actually need to add in the source code. +Backstage will re-read the configuration. If there's no errors, that's great! We can continue with the next part of the configuration. The next step is needed to change the sign-in page, this you actually need to add in the source code. Open `packages/app/src/App.tsx` and below the last `import` line, add: @@ -71,13 +76,24 @@ components: { }, ``` -:::note Note +### Add the auth provider to the backend -The default Backstage app comes with a guest Sign In Resolver. This resolver makes all users share a single "guest" identity and is only intended as a minimum requirement to quickly get up and running. You can read more about how [Sign In Resolvers](../../auth/identity-resolver.md#sign-in-resolvers) play a role in creating a [Backstage User Identity](../../auth/identity-resolver.md#backstage-user-identity) for logged in users. +To add the auth provider to the backend we will first need to install the package by running this command: -::: +```bash title="from your Backstage root directory" +yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-github-provider +``` -Restart Backstage from the terminal, by stopping it with `Control-C`, and starting it with `yarn dev` . You should be welcomed by a login prompt! +Then we will need to add this line: + +```ts title="in packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-auth-backend')); +/* highlight-add-start */ +backend.add(import('@backstage/plugin-auth-backend-module-github-provider')); +/* highlight-add-end */ +``` + +Restart Backstage from the terminal, by stopping it with `Control-C`, and starting it with `yarn dev`. You should be welcomed by a login prompt! :::note Note @@ -85,6 +101,33 @@ Sometimes the frontend starts before the backend resulting in errors on the sign ::: +If you try to login at this point you will get a "Failed to sign-in, unable to resolve user identity" message, read on as we'll fix that next. + +### Adding a User + +As part of the authentication process it will try to match details from the Auth Provider User Profile to User in your Catalog. This is what the `resolver` in the config we added to the `app-config.yaml` in the "[Setting up authentication](#setting-up-authentication)" section is doing. + +The recommended approach for adding Users, and Groups, into your Catalog is to use one of the existing Org Entity Providers - [like this one for GitHub](https://backstage.io/docs/integrations/github/org) - or if those don't work you may need to [create one](https://backstage.io/docs/features/software-catalog/external-integrations#custom-entity-providers) that fits your Organization's needs. + +For the sake of this guide we'll simply step you though adding a User to the `org.yaml` file that is included when you create a new Backstage instance. Let's do that: + +1. Fist open the `/examples/org.yaml` file in your text editor of choice +2. At the bottom we'll add the following YAML: + + ```yaml + --- + apiVersion: backstage.io/v1alpha1 + kind: User + metadata: + name: YOUR GITHUB USERNAME + spec: + memberOf: [guests] + ``` + +3. Now make sure to replace the text "YOUR GITHUB USERNAME" with your actual GitHub User name. + +Let's restart Backstage from the terminal ponce more, by stopping it with `Control-C`, and starting it with `yarn dev`. You should now be able to log into Backstage and see items in you Catalog. + To learn more about Authentication in Backstage, here are some docs you could read: @@ -93,25 +136,15 @@ could read: ### Setting up a GitHub Integration -The GitHub integration supports loading catalog entities from GitHub or GitHub -Enterprise. Entities can be added to static catalog configuration, registered -with the catalog-import plugin, or discovered from a GitHub organization. Users -and Groups can also be loaded from an organization. While using [GitHub Apps](../../integrations/github/github-apps.md) -might be the best way to set up integrations, for this tutorial you'll use a -Personal Access Token. +The GitHub integration supports loading catalog entities from GitHub or GitHub Enterprise. Entities can be added to static catalog configuration, registered with the catalog-import plugin, or discovered from a GitHub organization. Users and Groups can also be loaded from an organization. While using [GitHub Apps](../../integrations/github/github-apps.md) might be the best way to set up integrations, for this tutorial you'll use a Personal Access Token. -Create your Personal Access Token by opening -[the GitHub token creation page](https://github.com/settings/tokens/new). Use a -name to identify this token and put it in the notes field. Choose a number of -days for expiration. If you have a hard time picking a number, we suggest to go -for 7 days, it's a lucky number. +Create your Personal Access Token by opening [the GitHub token creation page](https://github.com/settings/tokens/new). Use a name to identify this token and put it in the notes field. Choose a number of days for expiration. If you have a hard time picking a number, we suggest to go for 7 days, it's a lucky number. ![Screenshot of the GitHub Personal Access Token creation page](../../assets/getting-started/gh-pat.png) Set the scope to your likings. For this tutorial, selecting `repo` and `workflow` is required as the scaffolding job in this guide configures a GitHub actions workflow for the newly created project. -For this tutorial, we will be writing the token to `app-config.local.yaml`. This file might not exist for you, so if it doesn't go ahead and create it alongside the `app-config.yaml` at the root of the project. -This file should also be excluded in `.gitignore`, to avoid accidental committing of this file. +For this tutorial, we will be writing the token to `app-config.local.yaml`. This file might not exist for you, so if it doesn't go ahead and create it alongside the `app-config.yaml` at the root of the project. This file should also be excluded in `.gitignore`, to avoid accidental committing of this file. In your `app-config.local.yaml` go ahead and add the following: From a442a4a718591190a65749d8a5243272c4cf6b0c Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Tue, 30 Jul 2024 06:29:21 -0500 Subject: [PATCH 007/317] Update docs/getting-started/config/authentication.md Co-authored-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/getting-started/config/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index 72990539ca..fa62e49ff3 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -126,7 +126,7 @@ For the sake of this guide we'll simply step you though adding a User to the `or 3. Now make sure to replace the text "YOUR GITHUB USERNAME" with your actual GitHub User name. -Let's restart Backstage from the terminal ponce more, by stopping it with `Control-C`, and starting it with `yarn dev`. You should now be able to log into Backstage and see items in you Catalog. +Let's restart Backstage from the terminal once more, by stopping it with `Ctrl+C`, and starting it with `yarn dev`. You should now be able to log into Backstage and see items in your Catalog. To learn more about Authentication in Backstage, here are some docs you could read: From e1289cb42f8371f2fd5d4c52558caa1836c44c8e Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Tue, 30 Jul 2024 06:29:32 -0500 Subject: [PATCH 008/317] Update docs/getting-started/config/authentication.md Co-authored-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/getting-started/config/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index fa62e49ff3..a0299f9d50 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -93,7 +93,7 @@ backend.add(import('@backstage/plugin-auth-backend-module-github-provider')); /* highlight-add-end */ ``` -Restart Backstage from the terminal, by stopping it with `Control-C`, and starting it with `yarn dev`. You should be welcomed by a login prompt! +Restart Backstage from the terminal, by stopping it with `Ctrl+C`, and starting it with `yarn dev`. You should be welcomed by a login prompt! :::note Note From 18d56263171e9778574225d8329d8f4aaddad0b0 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Tue, 30 Jul 2024 06:29:43 -0500 Subject: [PATCH 009/317] Update docs/getting-started/config/authentication.md Co-authored-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/getting-started/config/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index a0299f9d50..e8c31e8ea1 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -78,7 +78,7 @@ components: { ### Add the auth provider to the backend -To add the auth provider to the backend we will first need to install the package by running this command: +To add the auth provider to the backend, we will first need to install the package by running this command: ```bash title="from your Backstage root directory" yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-github-provider From be1de36e7cc122b2f5fcc552e47189ca50d173bb Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Tue, 30 Jul 2024 06:36:05 -0500 Subject: [PATCH 010/317] Update docs/getting-started/config/authentication.md Co-authored-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/getting-started/config/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index e8c31e8ea1..63c6145b69 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -17,7 +17,7 @@ Go to [https://github.com/settings/applications/new](https://github.com/settings ![Screenshot of the GitHub OAuth creation page](../../assets/getting-started/gh-oauth.png) -Take note of the `Client ID` and the `Client Secret` (clicking the "Generate a new client secret" button will get this value for you). Open `app-config.yaml`, and add your `clientId` and `clientSecret` to this file. It should end up looking like this: +Take note of the `Client ID` and the `Client Secret` (clicking the "Generate a new client secret" button will get this value for you). Open `app-config.yaml`, and add them as `clientId` and `clientSecret` in this file. It should end up looking like this: ```yaml title="app-config.yaml" auth: From e7aa7cd74ed97cb9c9bb475bc8f0532c25703ec7 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Tue, 30 Jul 2024 06:36:38 -0500 Subject: [PATCH 011/317] Update docs/getting-started/config/authentication.md Co-authored-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/getting-started/config/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index 63c6145b69..2292987f76 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -49,7 +49,7 @@ The default Backstage app comes with a guest Sign In Resolver. This resolver mak ### Add sign-in option to the frontend -Backstage will re-read the configuration. If there's no errors, that's great! We can continue with the next part of the configuration. The next step is needed to change the sign-in page, this you actually need to add in the source code. +Backstage will re-read the configuration. If there's no errors, that's great! We can continue with the next part of the configuration. The next step is needed to change the sign-in page. For this, you'll actually need to write some code. Open `packages/app/src/App.tsx` and below the last `import` line, add: From 8faf3fd1db78490f69fd56566a01d95d5410cef4 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Tue, 30 Jul 2024 07:31:52 -0500 Subject: [PATCH 012/317] Changes based on feedback Signed-off-by: Andre Wanlin --- docs/getting-started/config/authentication.md | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index 2292987f76..8267dbeeea 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -1,15 +1,25 @@ --- id: authentication title: Authentication -description: How to set up authentication into your Backstage installation +description: How to setup authentication for your Backstage app --- Audience: Admins or Developers -### Setting up authentication +## Summary + +We'll be walking you through how to setup authentication for your Backstage app using Github. After finishing this guide, you'll have both working authentication and users in your Backstage app to match to the users logging in! There are multiple authentication providers available for you to use with Backstage, feel free to follow [the instructions for adding authentication](../../auth/index.md). +:::note Note + +The default Backstage app comes with a guest Sign In Resolver. This resolver makes all users share a single "guest" identity and is only intended as a minimum requirement to quickly get up and running. You can read more about how [Sign In Resolvers](../../auth/identity-resolver.md#sign-in-resolvers) play a role in creating a [Backstage User Identity](../../auth/identity-resolver.md#backstage-user-identity) for logged in users. + +::: + +## Setting up authentication + For this tutorial we choose to use GitHub, a free service most of you might be familiar with. For other options, see [the auth provider documentation](../../auth/github/provider.md#create-an-oauth-app-on-github). @@ -41,15 +51,9 @@ auth: /* highlight-add-end */ ``` -:::note Note +## Add sign-in option to the frontend -The default Backstage app comes with a guest Sign In Resolver. This resolver makes all users share a single "guest" identity and is only intended as a minimum requirement to quickly get up and running. You can read more about how [Sign In Resolvers](../../auth/identity-resolver.md#sign-in-resolvers) play a role in creating a [Backstage User Identity](../../auth/identity-resolver.md#backstage-user-identity) for logged in users. - -::: - -### Add sign-in option to the frontend - -Backstage will re-read the configuration. If there's no errors, that's great! We can continue with the next part of the configuration. The next step is needed to change the sign-in page. For this, you'll actually need to write some code. +The next step is to change the sign-in page. For this, you'll actually need to write some code. Open `packages/app/src/App.tsx` and below the last `import` line, add: @@ -76,7 +80,7 @@ components: { }, ``` -### Add the auth provider to the backend +## Add the auth provider to the backend To add the auth provider to the backend, we will first need to install the package by running this command: @@ -93,7 +97,7 @@ backend.add(import('@backstage/plugin-auth-backend-module-github-provider')); /* highlight-add-end */ ``` -Restart Backstage from the terminal, by stopping it with `Ctrl+C`, and starting it with `yarn dev`. You should be welcomed by a login prompt! +Restart Backstage from the terminal, by stopping it with `Ctrl+C`, and starting it with `yarn dev`. You should be welcomed by a login prompt! If you try to login at this point you will get a "Failed to sign-in, unable to resolve user identity" message, read on as we'll fix that next. :::note Note @@ -101,9 +105,7 @@ Sometimes the frontend starts before the backend resulting in errors on the sign ::: -If you try to login at this point you will get a "Failed to sign-in, unable to resolve user identity" message, read on as we'll fix that next. - -### Adding a User +## Adding a User As part of the authentication process it will try to match details from the Auth Provider User Profile to User in your Catalog. This is what the `resolver` in the config we added to the `app-config.yaml` in the "[Setting up authentication](#setting-up-authentication)" section is doing. @@ -134,7 +136,7 @@ could read: - [Authentication in Backstage](../../auth/index.md) - [Using organizational data from GitHub](../../integrations/github/org.md) -### Setting up a GitHub Integration +## Setting up a GitHub Integration The GitHub integration supports loading catalog entities from GitHub or GitHub Enterprise. Entities can be added to static catalog configuration, registered with the catalog-import plugin, or discovered from a GitHub organization. Users and Groups can also be loaded from an organization. While using [GitHub Apps](../../integrations/github/github-apps.md) might be the best way to set up integrations, for this tutorial you'll use a Personal Access Token. From d2d23136c159b20a76497a4c225436c66bcdfbe3 Mon Sep 17 00:00:00 2001 From: Thomas Cardonne Date: Tue, 30 Jul 2024 23:46:47 +0200 Subject: [PATCH 013/317] feat: add config.d.ts in tsconfig included files Signed-off-by: Thomas Cardonne --- .changeset/afraid-fans-cross.md | 8 ++++++++ packages/cli/src/commands/repo/list-deprecations.ts | 4 ++-- packages/create-app/templates/default-app/tsconfig.json | 2 ++ tsconfig.json | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/afraid-fans-cross.md diff --git a/.changeset/afraid-fans-cross.md b/.changeset/afraid-fans-cross.md new file mode 100644 index 0000000000..1901d8ce0f --- /dev/null +++ b/.changeset/afraid-fans-cross.md @@ -0,0 +1,8 @@ +--- +'@backstage/create-app': patch +'@backstage/cli': patch +--- + +Add `config.d.ts` files to the list of included file in `tsconfig.json`. + +This allows ESLint to detect issues or deprecations in those files. diff --git a/packages/cli/src/commands/repo/list-deprecations.ts b/packages/cli/src/commands/repo/list-deprecations.ts index 69fe51f78e..772038025a 100644 --- a/packages/cli/src/commands/repo/list-deprecations.ts +++ b/packages/cli/src/commands/repo/list-deprecations.ts @@ -17,7 +17,7 @@ import chalk from 'chalk'; import { ESLint } from 'eslint'; import { OptionValues } from 'commander'; -import { join as joinPath, relative as relativePath } from 'path'; +import { relative as relativePath } from 'path'; import { PackageGraph } from '@backstage/cli-node'; import { paths } from '../../lib/paths'; @@ -45,7 +45,7 @@ export async function command(opts: OptionValues) { const deprecations = []; for (const [index, pkg] of packages.entries()) { - const results = await eslint.lintFiles(joinPath(pkg.dir, 'src')); + const results = await eslint.lintFiles(pkg.dir); for (const result of results) { for (const message of result.messages) { if (message.ruleId !== 'deprecation/deprecation') { diff --git a/packages/create-app/templates/default-app/tsconfig.json b/packages/create-app/templates/default-app/tsconfig.json index ba3f90177d..2aa745d3a9 100644 --- a/packages/create-app/templates/default-app/tsconfig.json +++ b/packages/create-app/templates/default-app/tsconfig.json @@ -2,7 +2,9 @@ "extends": "@backstage/cli/config/tsconfig.json", "include": [ "packages/*/src", + "packages/*/config.d.ts", "plugins/*/src", + "plugins/*/config.d.ts", "plugins/*/dev", "plugins/*/migrations" ], diff --git a/tsconfig.json b/tsconfig.json index 789743363d..8d72a3315b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,9 @@ "extends": "@backstage/cli/config/tsconfig.json", "include": [ "packages/*/src", + "packages/*/config.d.ts", "plugins/*/src", + "plugins/*/config.d.ts", "plugins/*/dev", "plugins/*/migrations" ], From 19f545fa1494352bce2cda6f4659410ef74230ed Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Thu, 1 Aug 2024 09:29:29 -0500 Subject: [PATCH 014/317] Changes based on new feedback Signed-off-by: Andre Wanlin --- docs/getting-started/config/authentication.md | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/docs/getting-started/config/authentication.md b/docs/getting-started/config/authentication.md index 8267dbeeea..5d0262c682 100644 --- a/docs/getting-started/config/authentication.md +++ b/docs/getting-started/config/authentication.md @@ -10,7 +10,7 @@ Audience: Admins or Developers We'll be walking you through how to setup authentication for your Backstage app using Github. After finishing this guide, you'll have both working authentication and users in your Backstage app to match to the users logging in! -There are multiple authentication providers available for you to use with Backstage, feel free to follow [the instructions for adding authentication](../../auth/index.md). +There are multiple authentication providers available for you to use with Backstage, feel free to follow [their instructions for adding authentication](../../auth/index.md). :::note Note @@ -20,10 +20,10 @@ The default Backstage app comes with a guest Sign In Resolver. This resolver mak ## Setting up authentication -For this tutorial we choose to use GitHub, a free service most of you might be familiar with. For other options, see -[the auth provider documentation](../../auth/github/provider.md#create-an-oauth-app-on-github). +For this tutorial we choose to use GitHub, a free service most of you might be familiar with, and we'll be using an OAuth app. For detailed options, see +[the GitHub auth provider documentation](../../auth/github/provider.md#create-an-oauth-app-on-github). -Go to [https://github.com/settings/applications/new](https://github.com/settings/applications/new) to create your OAuth App. The `Homepage URL` should point to Backstage's frontend, in our tutorial it would be `http://localhost:3000`. The `Authorization callback URL` will point to the auth backend, which will most likely be `http://localhost:7007/api/auth/github/handler/frame`. +Go to [https://github.com/settings/applications/new](https://github.com/settings/applications/new) to create your OAuth App. The "Homepage URL" should point to Backstage's frontend, in our tutorial it would be `http://localhost:3000`. The "Authorization callback URL" will point to the auth backend, which will most likely be `http://localhost:7007/api/auth/github/handler/frame`. ![Screenshot of the GitHub OAuth creation page](../../assets/getting-started/gh-oauth.png) @@ -43,11 +43,6 @@ auth: development: clientId: YOUR CLIENT ID clientSecret: YOUR CLIENT SECRET - signIn: - resolvers: - # Matches the GitHub username with the Backstage user entity name. - # See https://backstage.io/docs/auth/github/provider#resolvers for more resolvers. - - resolver: usernameMatchingUserEntityName /* highlight-add-end */ ``` @@ -80,6 +75,34 @@ components: { }, ``` +## Add sign-in resolver(s) + +Next we need to add the sign-in resolver to our configuration. Here's how: + +```yaml title="app-config.yaml" +auth: + # see https://backstage.io/docs/auth/ to learn about auth providers + environment: development + providers: + # See https://backstage.io/docs/auth/guest/provider + guest: {} + github: + development: + clientId: YOUR CLIENT ID + clientSecret: YOUR CLIENT SECRET + /* highlight-add-start */ + signIn: + resolvers: + # Matches the GitHub username with the Backstage user entity name. + # See https://backstage.io/docs/auth/github/provider#resolvers for more resolvers. + - resolver: usernameMatchingUserEntityName + /* highlight-add-end */ +``` + +What this will do is take the user details provided by the auth provider and match that against a User in the Catalog. In this case - `usernameMatchingUserEntityName` - will match the GitHub user name with the `metadata.name` value of a User in the Catalog, if none is found you will get an "Failed to sign-in, unable to resolve user identity" message. We'll cover this in the next few sections. + +Learn more about this topic in the [Sign-in Resolvers](../../auth/identity-resolver.md#sign-in-resolvers) documentation. + ## Add the auth provider to the backend To add the auth provider to the backend, we will first need to install the package by running this command: @@ -107,13 +130,11 @@ Sometimes the frontend starts before the backend resulting in errors on the sign ## Adding a User -As part of the authentication process it will try to match details from the Auth Provider User Profile to User in your Catalog. This is what the `resolver` in the config we added to the `app-config.yaml` in the "[Setting up authentication](#setting-up-authentication)" section is doing. - The recommended approach for adding Users, and Groups, into your Catalog is to use one of the existing Org Entity Providers - [like this one for GitHub](https://backstage.io/docs/integrations/github/org) - or if those don't work you may need to [create one](https://backstage.io/docs/features/software-catalog/external-integrations#custom-entity-providers) that fits your Organization's needs. For the sake of this guide we'll simply step you though adding a User to the `org.yaml` file that is included when you create a new Backstage instance. Let's do that: -1. Fist open the `/examples/org.yaml` file in your text editor of choice +1. First open the `/examples/org.yaml` file in your text editor of choice 2. At the bottom we'll add the following YAML: ```yaml @@ -146,7 +167,7 @@ Create your Personal Access Token by opening [the GitHub token creation page](ht Set the scope to your likings. For this tutorial, selecting `repo` and `workflow` is required as the scaffolding job in this guide configures a GitHub actions workflow for the newly created project. -For this tutorial, we will be writing the token to `app-config.local.yaml`. This file might not exist for you, so if it doesn't go ahead and create it alongside the `app-config.yaml` at the root of the project. This file should also be excluded in `.gitignore`, to avoid accidental committing of this file. +For this tutorial, we will be writing the token to `app-config.local.yaml`. This file might not exist for you, so if it doesn't go ahead and create it alongside the `app-config.yaml` at the root of the project. This file should also be excluded in `.gitignore`, to avoid accidental committing of this file. More details on this file can be found in the [Static Configuration documentation](../../conf/index.md). In your `app-config.local.yaml` go ahead and add the following: From 726cab926931966822a78d8e26d679cbbb21c6cc Mon Sep 17 00:00:00 2001 From: Mahendra Suthar Date: Wed, 7 Aug 2024 17:35:35 +0530 Subject: [PATCH 015/317] catalog-graph: fix navigation in catalog graph page Signed-off-by: Mahendra Suthar --- .../CatalogGraphPage/useCatalogGraphPage.ts | 142 ++++++++---------- 1 file changed, 60 insertions(+), 82 deletions(-) diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts index 6e47dedc50..f5f50e25dd 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts @@ -27,8 +27,7 @@ import { useMemo, useState, } from 'react'; -import { useLocation } from 'react-router-dom'; -import usePrevious from 'react-use/esm/usePrevious'; +import { useLocation, useSearchParams } from 'react-router-dom'; import { Direction } from '../EntityRelationsGraph'; export type CatalogGraphPageValue = { @@ -70,6 +69,7 @@ export function useCatalogGraphPage({ }; }): CatalogGraphPageValue { const location = useLocation(); + const [_, setSearchParams] = useSearchParams(); const query = useMemo( () => (qs.parse(location.search, { arrayLimit: 0, ignoreQueryPrefix: true }) || @@ -87,19 +87,53 @@ export function useCatalogGraphPage({ [location.search], ); - // Initial state - const [rootEntityNames, setRootEntityNames] = useState( + const rootEntityNames = useMemo( () => (Array.isArray(query.rootEntityRefs) ? query.rootEntityRefs : initialState?.rootEntityRefs ?? [] ).map(r => parseEntityRef(r)), + [initialState?.rootEntityRefs, query.rootEntityRefs], ); + + const setRootEntityNames = useCallback( + (value: React.SetStateAction) => { + const prev = Array.isArray(query.rootEntityRefs) + ? query.rootEntityRefs + : []; + + const hasChanged = + Array.isArray(value) && + (value.length !== prev.length || + value.some((r, i) => stringifyEntityRef(r) !== prev[i])); + + if (!hasChanged) { + return; + } + + setSearchParams(params => { + const rootEntityRefs = Array.isArray(value) + ? value.map(r => stringifyEntityRef(r)) + : []; + + params.delete('rootEntityRefs[]'); + + rootEntityRefs.forEach(r => { + params.set('rootEntityRefs[]', r); + }); + + return params; + }); + }, + [query.rootEntityRefs, setSearchParams], + ); + const [maxDepth, setMaxDepth] = useState(() => typeof query.maxDepth === 'string' ? parseMaxDepth(query.maxDepth) : initialState?.maxDepth ?? Number.POSITIVE_INFINITY, ); + const [selectedRelations, setSelectedRelations] = useState< string[] | undefined >(() => @@ -107,105 +141,52 @@ export function useCatalogGraphPage({ ? query.selectedRelations : initialState?.selectedRelations, ); + const [selectedKinds, setSelectedKinds] = useState(() => (Array.isArray(query.selectedKinds) ? query.selectedKinds : initialState?.selectedKinds )?.map(k => k.toLocaleLowerCase('en-US')), ); + const [unidirectional, setUnidirectional] = useState(() => typeof query.unidirectional === 'string' ? query.unidirectional === 'true' : initialState?.unidirectional ?? true, ); + const [mergeRelations, setMergeRelations] = useState(() => typeof query.mergeRelations === 'string' ? query.mergeRelations === 'true' : initialState?.mergeRelations ?? true, ); + const [direction, setDirection] = useState(() => typeof query.direction === 'string' ? query.direction : initialState?.direction ?? Direction.LEFT_RIGHT, ); + const [curve, setCurve] = useState<'curveStepBefore' | 'curveMonotoneX'>(() => typeof query.curve === 'string' ? query.curve : initialState?.curve ?? 'curveMonotoneX', ); + const [showFilters, setShowFilters] = useState(() => typeof query.showFilters === 'string' ? query.showFilters === 'true' : initialState?.showFilters ?? true, ); + const toggleShowFilters = useCallback( () => setShowFilters(s => !s), [setShowFilters], ); - // Update from query parameters - const prevQueryParams = usePrevious(location.search); useEffect(() => { - // Only respond to changes to url query params - if (location.search === prevQueryParams) { - return; - } - - if (Array.isArray(query.rootEntityRefs)) { - setRootEntityNames(query.rootEntityRefs.map(r => parseEntityRef(r))); - } - - if (typeof query.maxDepth === 'string') { - setMaxDepth(parseMaxDepth(query.maxDepth)); - } - - if (Array.isArray(query.selectedKinds)) { - setSelectedKinds(query.selectedKinds); - } - - if (Array.isArray(query.selectedRelations)) { - setSelectedRelations(query.selectedRelations); - } - - if (typeof query.unidirectional === 'string') { - setUnidirectional(query.unidirectional === 'true'); - } - - if (typeof query.mergeRelations === 'string') { - setMergeRelations(query.mergeRelations === 'true'); - } - - if (typeof query.direction === 'string') { - setDirection(query.direction); - } - - if (typeof query.showFilters === 'string') { - setShowFilters(query.showFilters === 'true'); - } - }, [ - prevQueryParams, - location.search, - query, - setRootEntityNames, - setMaxDepth, - setSelectedKinds, - setSelectedRelations, - setUnidirectional, - setMergeRelations, - setDirection, - setShowFilters, - ]); - - // Update query parameters - const previousRootEntityRefs = usePrevious( - rootEntityNames.map(e => stringifyEntityRef(e)), - ); - - useEffect(() => { - const rootEntityRefs = rootEntityNames.map(e => stringifyEntityRef(e)); const newParams = qs.stringify( { - rootEntityRefs, maxDepth: isFinite(maxDepth) ? maxDepth : '∞', selectedKinds, selectedRelations, @@ -213,36 +194,33 @@ export function useCatalogGraphPage({ mergeRelations, direction, showFilters, + curve, }, { arrayFormat: 'brackets', addQueryPrefix: true }, ); - const newUrl = `${window.location.pathname}${newParams}`; - // We directly manipulate window history here in order to not re-render - // infinitely (state => location => state => etc). The intention of this - // code is just to ensure the right query/filters are loaded when a user - // clicks the "back" button after clicking a result. - // Only push a new history entry if we switched to another entity, but not - // if we just changed a viewer setting. - if ( - !previousRootEntityRefs || - (rootEntityRefs.length === previousRootEntityRefs.length && - rootEntityRefs.every((v, i) => v === previousRootEntityRefs[i])) - ) { - window.history.replaceState(null, document.title, newUrl); - } else { - window.history.pushState(null, document.title, newUrl); - } + const searchParams = new URLSearchParams(newParams); + + setSearchParams( + params => { + searchParams.forEach((value, key) => { + params.set(key, value); + }); + + return params; + }, + { replace: true }, + ); }, [ - rootEntityNames, maxDepth, + curve, selectedKinds, selectedRelations, unidirectional, mergeRelations, direction, showFilters, - previousRootEntityRefs, + setSearchParams, ]); return { From 530d96f7166bb8db366aec0efb5e2fc94eec5857 Mon Sep 17 00:00:00 2001 From: Mahendra Suthar Date: Wed, 7 Aug 2024 21:55:17 +0530 Subject: [PATCH 016/317] catalog-graph: refactor useCatalogGraphPage hook remove setSearchParam and use qs.stringify and navigate to set search params Signed-off-by: Mahendra Suthar --- .../CatalogGraphPage/useCatalogGraphPage.ts | 63 +++++++------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts index f5f50e25dd..1f6fb74997 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.ts @@ -27,12 +27,12 @@ import { useMemo, useState, } from 'react'; -import { useLocation, useSearchParams } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { Direction } from '../EntityRelationsGraph'; export type CatalogGraphPageValue = { rootEntityNames: CompoundEntityRef[]; - setRootEntityNames: Dispatch>; + setRootEntityNames: (value: CompoundEntityRef[]) => void; maxDepth: number; setMaxDepth: Dispatch>; selectedRelations: string[] | undefined; @@ -69,7 +69,8 @@ export function useCatalogGraphPage({ }; }): CatalogGraphPageValue { const location = useLocation(); - const [_, setSearchParams] = useSearchParams(); + const navigate = useNavigate(); + const query = useMemo( () => (qs.parse(location.search, { arrayLimit: 0, ignoreQueryPrefix: true }) || @@ -97,35 +98,28 @@ export function useCatalogGraphPage({ ); const setRootEntityNames = useCallback( - (value: React.SetStateAction) => { - const prev = Array.isArray(query.rootEntityRefs) - ? query.rootEntityRefs - : []; + (value: CompoundEntityRef[]) => { + const areSame = + rootEntityNames.length === value.length && + rootEntityNames.every( + (r, i) => stringifyEntityRef(r) === stringifyEntityRef(value[i]), + ); - const hasChanged = - Array.isArray(value) && - (value.length !== prev.length || - value.some((r, i) => stringifyEntityRef(r) !== prev[i])); - - if (!hasChanged) { + if (areSame) { return; } - setSearchParams(params => { - const rootEntityRefs = Array.isArray(value) - ? value.map(r => stringifyEntityRef(r)) - : []; + const newSearch = qs.stringify( + { + ...query, + rootEntityRefs: value.map(r => stringifyEntityRef(r)), + }, + { arrayFormat: 'brackets', addQueryPrefix: true }, + ); - params.delete('rootEntityRefs[]'); - - rootEntityRefs.forEach(r => { - params.set('rootEntityRefs[]', r); - }); - - return params; - }); + navigate(newSearch); }, - [query.rootEntityRefs, setSearchParams], + [rootEntityNames, navigate, query], ); const [maxDepth, setMaxDepth] = useState(() => @@ -187,6 +181,7 @@ export function useCatalogGraphPage({ useEffect(() => { const newParams = qs.stringify( { + rootEntityRefs: rootEntityNames.map(stringifyEntityRef), maxDepth: isFinite(maxDepth) ? maxDepth : '∞', selectedKinds, selectedRelations, @@ -199,18 +194,7 @@ export function useCatalogGraphPage({ { arrayFormat: 'brackets', addQueryPrefix: true }, ); - const searchParams = new URLSearchParams(newParams); - - setSearchParams( - params => { - searchParams.forEach((value, key) => { - params.set(key, value); - }); - - return params; - }, - { replace: true }, - ); + navigate(newParams, { replace: true }); }, [ maxDepth, curve, @@ -220,7 +204,8 @@ export function useCatalogGraphPage({ mergeRelations, direction, showFilters, - setSearchParams, + rootEntityNames, + navigate, ]); return { From d976ba2bf96604b52671276c325cdfbeff4200ac Mon Sep 17 00:00:00 2001 From: ElaineDeMattosSilvaB Date: Wed, 14 Aug 2024 18:04:46 +0200 Subject: [PATCH 017/317] feat: use a relations array instead of each individual configuration option Signed-off-by: ElaineDeMattosSilvaB --- .changeset/loud-singers-happen.md | 2 +- docs/integrations/gitlab/org.md | 30 ++++++++++---- .../api-report.md | 3 +- .../src/lib/types.ts | 39 ++++++++++++++----- .../GitlabOrgDiscoveryEntityProvider.ts | 14 ++++--- .../src/providers/config.test.ts | 12 ++---- .../src/providers/config.ts | 8 +--- 7 files changed, 68 insertions(+), 40 deletions(-) diff --git a/.changeset/loud-singers-happen.md b/.changeset/loud-singers-happen.md index 7d86f96e62..32dbdb8481 100644 --- a/.changeset/loud-singers-happen.md +++ b/.changeset/loud-singers-happen.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-backend-module-gitlab': patch --- -Allow Backstage to mirror GitLab Member Relation behavior by adding the `allowDescendants` and `allowSharedFromGroups` configuration options. +Add the `relations` array to allow Backstage to mirror GitLab's membership behavior, including descendant, inherited, and shared-from-group memberships. diff --git a/docs/integrations/gitlab/org.md b/docs/integrations/gitlab/org.md index 23323bae24..da20af9d4a 100644 --- a/docs/integrations/gitlab/org.md +++ b/docs/integrations/gitlab/org.md @@ -172,9 +172,10 @@ catalog: host: gitlab.com orgEnabled: true group: org/teams # Required for gitlab.com when `orgEnabled: true`. Optional for self managed. Must not end with slash. Accepts only groups under the provided path (which will be stripped) - allowInherited: true # Optional. Members of any ancestor groups will also be considered members of the current group. - allowDescendants: true # Optional. Members of any descendant groups will also be considered members of the current group. - allowSharedFromGroups: true # Optional. Members of any invited groups will also be considered members of the current group. + relations: # Optional + - INHERITED # Optional. Members of any ancestor groups will also be considered members of the current group. + - DESCENDANTS # Optional. Members of any descendant groups will also be considered members of the current group. + - SHARED_FROM_GROUPS # Optional. Members of any invited groups will also be considered members of the current group. groupPattern: '[\s\S]*' # Optional. Filters found groups based on provided pattern. Defaults to `[\s\S]*`, which means to not filter anything schedule: # Same options as in TaskScheduleDefinition. Optional for the Legacy Backend System. # supports cron, ISO duration, "human duration" as used in code @@ -198,14 +199,27 @@ which contain members will be ingested. ### Subgroup Membership -GitLab groups and subgroups provide a hierarchical structure to organize projects and users. Membership in a parent group automatically extends to its subgroups, ensuring consistent permissions across all levels. Additionally, membership can be managed using invited groups, allowing one group to be added to another. For Backstage users integrating with GitLab, understanding this [inheritance model](https://docs.gitlab.co.jp/ee/user/group/subgroups/#subgroup-membership) and the use of invited groups is crucial for accurately mapping and managing group and user entities. +GitLab groups and subgroups provide a hierarchical structure for organizing projects and users. Membership in a parent group extends automatically to its subgroups, ensuring consistent permissions at all levels. Additionally, membership can be managed using invited groups, where one group can be added to another. For Backstage users integrating with GitLab, understanding this [inheritance model](https://docs.gitlab.co.jp/ee/user/group/subgroups/#subgroup-membership) and the concept of invited groups is crucial for accurately mapping and managing group and user entities. The `GitLabOrgDiscoveryEntityProvider` mirrors GitLab's membership behavior as follows: -- by default, every direct member of a GitLab group is also a member of the corresponding group in Backstage; -- to include members of subgroups as members of the parent group, enable the `allowDescendants` option; -- to include members of parent groups as members of their subgroups, enable the `allowInherited` option. This also has the effect that subgroups with no direct members will not be skipped in the group ingestion process and will be added as a group entity in Backstage; -- to include members of invited groups as members of the inviting group, enable the `allowSharedFromGroups` option. +- By default, every direct member of a GitLab group is also a member of the corresponding group in Backstage. +- To include members of subgroups as members of the parent group, configure the `relations` array with the `DESCENDANTS` option. +- To include members of parent groups as members of their subgroups, configure the `relations` array with the `INHERITED` option. This also has the effect that subgroups with no direct members will not be skipped in the group ingestion process and will be added as a group entity in Backstage; +- To include members of invited groups as members of the inviting group, configure the `relations` array with the `SHARED_FROM_GROUPS` option. + +The previous `allowInherited` will be deprecated in future versions. Use the `relations` array with the `INHERITED` option instead. + +```yaml +catalog: + providers: + gitlab: + development: + relations: + - INHERITED + - DESCENDANTS + - SHARED_FROM_GROUPS +``` Refer to the [GitLab Group Member Relation](https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation) documentation for more information. diff --git a/plugins/catalog-backend-module-gitlab/api-report.md b/plugins/catalog-backend-module-gitlab/api-report.md index 5f4f3aa622..47113af957 100644 --- a/plugins/catalog-backend-module-gitlab/api-report.md +++ b/plugins/catalog-backend-module-gitlab/api-report.md @@ -106,8 +106,7 @@ export type GitlabProviderConfig = { userPattern: RegExp; groupPattern: RegExp; allowInherited?: boolean; - allowDescendants?: boolean; - allowSharedFromGroups?: boolean; + relations: string[]; orgEnabled?: boolean; schedule?: TaskScheduleDefinition; skipForkedRepos?: boolean; diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index a674d4c222..829cb27f6d 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -184,19 +184,40 @@ export type GitlabProviderConfig = { groupPattern: RegExp; /** - * If true, the provider will also add members of inherited groups (ascendant) to the ingested groups. See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation - */ + * If true, the provider will also add inherited (ascendant) users to the ingested groups. + * See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation + * + * @deprecated Use the `relations` array to configure group membership relations instead. + **/ allowInherited?: boolean; /** - * If true, the provider will also add members of descendant groups to the ingested groups. See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation + * Specifies the types of group membership relations that should be included when ingesting data. + * + * The following values are valid: + * - 'DIRECT': Direct members of the group. This is the default relation and is always included. + * - 'INHERITED': Members inherited from parent (ascendant) groups. + * - 'DESCENDANTS': Members from child (descendant) groups. + * - 'SHARED_FROM_GROUPS': Members shared from other groups. + * + * See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation + * + * If the `relations` array is provided in the app-config.yaml, it should contain any combination of the above values. + * The 'DIRECT' relation is automatically included and cannot be excluded, even if not specified. + * Example configuration: + * + * ```yaml + * catalog: + * providers: + * gitlab: + * development: + * relations: + * - INHERITED + * - DESCENDANTS + * - SHARED_FROM_GROUPS + * ``` */ - allowDescendants?: boolean; - - /** - * If true, the provider will also add members of invited groups to the ingested groups. See: https://docs.gitlab.com/ee/api/graphql/reference/#groupmemberrelation - */ - allowSharedFromGroups?: boolean; + relations?: string[]; orgEnabled?: boolean; schedule?: TaskScheduleDefinition; diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index 2c750a2930..3415e01974 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -779,11 +779,13 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { } private getRelations(config: any) { - return [ - 'DIRECT', - ...(config.allowInherited ? ['INHERITED'] : []), - ...(config.allowSharedFromGroups ? ['SHARED_FROM_GROUPS'] : []), - ...(config.allowDescendants ? ['DESCENDANTS'] : []), - ]; + if (Array.isArray(config.relations)) { + // filter out duplicates + const relationsSet = new Set(['DIRECT', ...config.relations]); + return Array.from(relationsSet); + } + + // TODO: remove this fallback in the next major version by ensuring the method returns only `['DIRECT']` if no `relations` array is provided. + return ['DIRECT', ...(config.allowInherited ? ['INHERITED'] : [])]; } } diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts index c2c1fd14f7..1db5e24f01 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -59,8 +59,7 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, - allowDescendants: false, - allowSharedFromGroups: false, + relations: [], schedule: undefined, skipForkedRepos: false, }), @@ -99,8 +98,7 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, - allowDescendants: false, - allowSharedFromGroups: false, + relations: [], schedule: undefined, skipForkedRepos: false, }), @@ -140,8 +138,7 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, - allowDescendants: false, - allowSharedFromGroups: false, + relations: [], schedule: undefined, skipForkedRepos: true, }), @@ -183,8 +180,7 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, - allowDescendants: false, - allowSharedFromGroups: false, + relations: [], skipForkedRepos: false, schedule: { frequency: Duration.fromISO('PT30M'), diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.ts index abed6d83c4..32d3ad0cf2 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.ts @@ -45,10 +45,7 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { const orgEnabled: boolean = config.getOptionalBoolean('orgEnabled') ?? false; const allowInherited: boolean = config.getOptionalBoolean('allowInherited') ?? false; - const allowDescendants: boolean = - config.getOptionalBoolean('allowDescendants') ?? false; - const allowSharedFromGroups: boolean = - config.getOptionalBoolean('allowSharedFromGroups') ?? false; + const relations: string[] = config.getOptionalStringArray('relations') ?? []; const skipForkedRepos: boolean = config.getOptionalBoolean('skipForkedRepos') ?? false; @@ -70,8 +67,7 @@ function readGitlabConfig(id: string, config: Config): GitlabProviderConfig { schedule, orgEnabled, allowInherited, - allowDescendants, - allowSharedFromGroups, + relations, skipForkedRepos, }; } From 43af3da413568da72d48d0eec55d034bf9cd8aa2 Mon Sep 17 00:00:00 2001 From: ElaineDeMattosSilvaB Date: Thu, 15 Aug 2024 09:10:22 +0200 Subject: [PATCH 018/317] fix: add relations array to config tests Signed-off-by: ElaineDeMattosSilvaB --- .../catalog-backend-module-gitlab/src/providers/config.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts index 7c044367b9..bb493278c9 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/config.test.ts @@ -184,6 +184,7 @@ describe('config', () => { userPattern: /[\s\S]*/, orgEnabled: false, allowInherited: false, + relations: [], schedule: undefined, restrictUsersToGroup: false, skipForkedRepos: false, From a9d154b370b84ea68c5d427f319c7bda479ce932 Mon Sep 17 00:00:00 2001 From: ElaineDeMattosSilvaB Date: Thu, 15 Aug 2024 09:19:13 +0200 Subject: [PATCH 019/317] fix: fix api report Signed-off-by: ElaineDeMattosSilvaB --- plugins/catalog-backend-module-gitlab/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-gitlab/api-report.md b/plugins/catalog-backend-module-gitlab/api-report.md index 4bdca5c5ef..9f82d3acbb 100644 --- a/plugins/catalog-backend-module-gitlab/api-report.md +++ b/plugins/catalog-backend-module-gitlab/api-report.md @@ -107,7 +107,7 @@ export type GitlabProviderConfig = { userPattern: RegExp; groupPattern: RegExp; allowInherited?: boolean; - relations: string[]; + relations?: string[]; orgEnabled?: boolean; schedule?: TaskScheduleDefinition; skipForkedRepos?: boolean; From 8c3639e60a1f31470499bee13bfe3917e2c64da5 Mon Sep 17 00:00:00 2001 From: Mahendra Suthar Date: Fri, 16 Aug 2024 01:07:39 +0530 Subject: [PATCH 020/317] fix useCatalogGraphPage tests update tests to test back button behaviour Signed-off-by: Mahendra Suthar --- .../useCatalogGraphPage.test.tsx | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.test.tsx diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.test.tsx new file mode 100644 index 0000000000..85cb606482 --- /dev/null +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.test.tsx @@ -0,0 +1,182 @@ +/* + * 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 { RELATION_MEMBER_OF } from '@backstage/catalog-model'; +import { renderHook, waitFor } from '@testing-library/react'; +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { BrowserRouter } from 'react-router-dom'; +import { Direction } from '../EntityRelationsGraph'; +import { useCatalogGraphPage } from './useCatalogGraphPage'; + +const wrapper = ({ children }: { children?: React.ReactNode }) => { + return {children}; +}; + +describe('useCatalogGraphPage', () => { + test('should use initial state', () => { + const { result } = renderHook(props => useCatalogGraphPage(props), { + initialProps: { + initialState: { + rootEntityRefs: ['b:d/c'], + maxDepth: 2, + direction: Direction.RIGHT_LEFT, + mergeRelations: false, + unidirectional: false, + showFilters: false, + selectedKinds: ['API'], + selectedRelations: [RELATION_MEMBER_OF], + }, + }, + wrapper, + }); + + expect(result.current.rootEntityNames).toEqual([ + { kind: 'b', namespace: 'd', name: 'c' }, + ]); + expect(result.current.maxDepth).toEqual(2); + expect(result.current.direction).toEqual(Direction.RIGHT_LEFT); + expect(result.current.mergeRelations).toEqual(false); + expect(result.current.unidirectional).toEqual(false); + expect(result.current.showFilters).toEqual(false); + expect(result.current.selectedKinds).toEqual(['api']); + expect(result.current.selectedRelations).toEqual([RELATION_MEMBER_OF]); + }); + + test('should use state from url', () => { + act(() => { + history.pushState( + {}, + '', + '?rootEntityRefs[]=b:d/c&maxDepth=2&direction=RL&mergeRelations=false&unidirectional=false&showFilters=false&selectedKinds[]=api&selectedRelations[]=memberOf', + ); + }); + + const { result } = renderHook(props => useCatalogGraphPage(props), { + initialProps: {}, + wrapper, + }); + + expect(result.current.rootEntityNames).toEqual([ + { kind: 'b', namespace: 'd', name: 'c' }, + ]); + expect(result.current.maxDepth).toEqual(2); + expect(result.current.direction).toEqual(Direction.RIGHT_LEFT); + expect(result.current.mergeRelations).toEqual(false); + expect(result.current.unidirectional).toEqual(false); + expect(result.current.showFilters).toEqual(false); + expect(result.current.selectedKinds).toEqual(['api']); + expect(result.current.selectedRelations).toEqual([RELATION_MEMBER_OF]); + }); + + test('should update state in url (replace if setting changes)', () => { + const { result } = renderHook(props => useCatalogGraphPage(props), { + wrapper, + initialProps: {}, + }); + + act(() => result.current.setMaxDepth(5)); + + expect(window.location.search).toEqual( + '?rootEntityRefs%5B%5D=b%3Ad%2Fc&maxDepth=5&selectedKinds%5B%5D=api&selectedRelations%5B%5D=memberOf&unidirectional=false&mergeRelations=false&direction=RL&showFilters=false&curve=curveMonotoneX', + ); + + act(() => result.current.setUnidirectional(false)); + + expect(window.location.search).toEqual( + '?rootEntityRefs%5B%5D=b%3Ad%2Fc&maxDepth=5&selectedKinds%5B%5D=api&selectedRelations%5B%5D=memberOf&unidirectional=false&mergeRelations=false&direction=RL&showFilters=false&curve=curveMonotoneX', + ); + }); + + test('should update state in url (only push if different root entity)', () => { + const oldLength = window.history.length; + const { result } = renderHook(props => useCatalogGraphPage(props), { + initialProps: { + initialState: { + rootEntityRefs: ['component:default/first'], + }, + }, + wrapper, + }); + + act(() => + result.current.setRootEntityNames([ + { kind: 'component', namespace: 'default', name: 'my' }, + ]), + ); + + expect(window.history.length).toEqual(oldLength + 1); + expect(window.location.search).toEqual( + '?rootEntityRefs%5B%5D=component%3Adefault%2Fmy&maxDepth=5&selectedKinds%5B%5D=api&selectedRelations%5B%5D=memberOf&unidirectional=false&mergeRelations=false&direction=RL&showFilters=false&curve=curveMonotoneX', + ); + }); + + test('should update state to last state on back', async () => { + const { result } = renderHook(props => useCatalogGraphPage(props), { + wrapper, + initialProps: { + initialState: { + rootEntityRefs: ['component:default/first'], + }, + }, + }); + + act(() => + result.current.setRootEntityNames([ + { kind: 'component', namespace: 'default', name: 'first' }, + ]), + ); + + expect(window.location.search).toEqual( + '?rootEntityRefs%5B%5D=component%3Adefault%2Ffirst&maxDepth=5&selectedKinds%5B%5D=api&selectedRelations%5B%5D=memberOf&unidirectional=false&mergeRelations=false&direction=RL&showFilters=false&curve=curveMonotoneX', + ); + + act(() => + result.current.setRootEntityNames([ + { kind: 'component', namespace: 'default', name: 'second' }, + ]), + ); + + expect(window.location.search).toEqual( + '?rootEntityRefs%5B%5D=component%3Adefault%2Fsecond&maxDepth=5&selectedKinds%5B%5D=api&selectedRelations%5B%5D=memberOf&unidirectional=false&mergeRelations=false&direction=RL&showFilters=false&curve=curveMonotoneX', + ); + + act(() => { + result.current.setRootEntityNames([ + { kind: 'component', namespace: 'default', name: 'third' }, + ]); + }); + + act(() => { + window.history.back(); + }); + + await waitFor(() => { + expect(result.current.rootEntityNames).toEqual([ + { kind: 'component', namespace: 'default', name: 'second' }, + ]); + }); + + act(() => { + window.history.back(); + }); + + await waitFor(() => { + expect(result.current.rootEntityNames).toEqual([ + { kind: 'component', namespace: 'default', name: 'first' }, + ]); + }); + }); +}); From da9107852cd4afef9e7c6c2e59ddeb00ee8c362c Mon Sep 17 00:00:00 2001 From: Mahendra Suthar Date: Fri, 16 Aug 2024 01:15:41 +0530 Subject: [PATCH 021/317] add changesets for patch update Signed-off-by: Mahendra Suthar --- .changeset/quiet-spies-clean.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/quiet-spies-clean.md diff --git a/.changeset/quiet-spies-clean.md b/.changeset/quiet-spies-clean.md new file mode 100644 index 0000000000..ba277f4021 --- /dev/null +++ b/.changeset/quiet-spies-clean.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-graph': patch +--- + +Fixed a bug in the `CatalogGraphPage` component where, after clicking on some nodes, clicking the back button would break the navigation. This issue caused the entire navigation to fail and behaved differently across various browsers. From 8279366558f9f283a1cd73636ef5c16254565564 Mon Sep 17 00:00:00 2001 From: Mahendra Suthar Date: Fri, 16 Aug 2024 14:49:58 +0530 Subject: [PATCH 022/317] remove useCatalogGraphPage.test.ts Signed-off-by: Mahendra Suthar --- .../useCatalogGraphPage.test.ts | 147 ------------------ 1 file changed, 147 deletions(-) delete mode 100644 plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.test.ts diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.test.ts b/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.test.ts deleted file mode 100644 index a89fa353d6..0000000000 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/useCatalogGraphPage.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 { RELATION_MEMBER_OF } from '@backstage/catalog-model'; -import { act, renderHook } from '@testing-library/react'; -import { useLocation as useLocationMocked } from 'react-router-dom'; -import { Direction } from '../EntityRelationsGraph'; -import { useCatalogGraphPage } from './useCatalogGraphPage'; - -jest.mock('react-router-dom', () => ({ - useLocation: jest.fn(), -})); - -jest.spyOn(window.history, 'replaceState'); -jest.spyOn(window.history, 'pushState'); - -const useLocation = useLocationMocked as jest.Mock< - ReturnType ->; -const windowHistoryReplaceState = window.history.replaceState as jest.Mock< - ReturnType ->; -const windowHistoryPushState = window.history.pushState as jest.Mock< - ReturnType ->; - -describe('useCatalogGraphPage', () => { - beforeEach(() => { - useLocation.mockReturnValue({ - search: '?', - state: {}, - key: '', - pathname: '', - hash: '', - }); - }); - - afterEach(() => jest.resetAllMocks()); - - test('should use initial state', () => { - const { result } = renderHook(() => - useCatalogGraphPage({ - initialState: { - rootEntityRefs: ['b:d/c'], - maxDepth: 2, - direction: Direction.RIGHT_LEFT, - mergeRelations: false, - unidirectional: false, - showFilters: false, - selectedKinds: ['API'], - selectedRelations: [RELATION_MEMBER_OF], - }, - }), - ); - - expect(result.current.rootEntityNames).toEqual([ - { kind: 'b', namespace: 'd', name: 'c' }, - ]); - expect(result.current.maxDepth).toEqual(2); - expect(result.current.direction).toEqual(Direction.RIGHT_LEFT); - expect(result.current.mergeRelations).toEqual(false); - expect(result.current.unidirectional).toEqual(false); - expect(result.current.showFilters).toEqual(false); - expect(result.current.selectedKinds).toEqual(['api']); - expect(result.current.selectedRelations).toEqual([RELATION_MEMBER_OF]); - }); - - test('should use state from url', () => { - useLocation.mockReturnValueOnce({ - search: - '?rootEntityRefs[]=b:d/c&maxDepth=2&direction=RL&mergeRelations=false&unidirectional=false&showFilters=false&selectedKinds[]=api&selectedRelations[]=memberOf', - state: {}, - key: '', - pathname: '', - hash: '', - }); - - const { result } = renderHook(() => useCatalogGraphPage({})); - - expect(result.current.rootEntityNames).toEqual([ - { kind: 'b', namespace: 'd', name: 'c' }, - ]); - expect(result.current.maxDepth).toEqual(2); - expect(result.current.direction).toEqual(Direction.RIGHT_LEFT); - expect(result.current.mergeRelations).toEqual(false); - expect(result.current.unidirectional).toEqual(false); - expect(result.current.showFilters).toEqual(false); - expect(result.current.selectedKinds).toEqual(['api']); - expect(result.current.selectedRelations).toEqual([RELATION_MEMBER_OF]); - }); - - test('should update state in url (replace if setting changes)', () => { - const { result } = renderHook(() => useCatalogGraphPage({})); - - act(() => result.current.setMaxDepth(5)); - - expect(windowHistoryReplaceState).toHaveBeenCalledWith( - null, - '', - '/?maxDepth=5&unidirectional=true&mergeRelations=true&direction=LR&showFilters=true', - ); - - act(() => result.current.setUnidirectional(false)); - - expect(windowHistoryReplaceState).toHaveBeenCalledWith( - null, - '', - '/?maxDepth=5&unidirectional=false&mergeRelations=true&direction=LR&showFilters=true', - ); - }); - - test('should update state in url (only push if different root entity)', () => { - const { result, rerender } = renderHook(() => - useCatalogGraphPage({ - initialState: { - rootEntityRefs: ['component:default/first'], - }, - }), - ); - - act(() => - result.current.setRootEntityNames([ - { kind: 'Component', namespace: 'default', name: 'my' }, - ]), - ); - - rerender(); - - expect(windowHistoryPushState).toHaveBeenCalledWith( - null, - '', - '/?rootEntityRefs%5B%5D=component%3Adefault%2Fmy&maxDepth=%E2%88%9E&unidirectional=true&mergeRelations=true&direction=LR&showFilters=true', - ); - }); -}); From e7cdbcedc0597760b51193ff26a0ac8b992da6e4 Mon Sep 17 00:00:00 2001 From: Himesh Ladva Date: Thu, 22 Aug 2024 16:25:57 +0100 Subject: [PATCH 023/317] chore: add Hashicorp to plugin name Signed-off-by: Himesh Ladva --- .../data/plugins/{terraform.yaml => hashicorp-terraform.yaml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename microsite/data/plugins/{terraform.yaml => hashicorp-terraform.yaml} (75%) diff --git a/microsite/data/plugins/terraform.yaml b/microsite/data/plugins/hashicorp-terraform.yaml similarity index 75% rename from microsite/data/plugins/terraform.yaml rename to microsite/data/plugins/hashicorp-terraform.yaml index de33d37fa3..c254667015 100644 --- a/microsite/data/plugins/terraform.yaml +++ b/microsite/data/plugins/hashicorp-terraform.yaml @@ -1,9 +1,9 @@ --- -title: Terraform +title: Hashicorp Terraform author: GlobalLogic UK&I authorUrl: https://www.globallogic.com category: Infrastructure -description: Easily view Terraform runs for a specific workspace. +description: Easily view Hashicorp Terraform runs for a specific workspace. documentation: https://github.com/globallogicuki/globallogic-backstage-plugins/tree/main/plugins/terraform iconUrl: /img/terraform-logo.svg npmPackageName: '@globallogicuki/backstage-plugin-terraform' From 9b660d670ecca23b53a2578a6c78b9963dc1f8de Mon Sep 17 00:00:00 2001 From: zeshanziya Date: Tue, 27 Aug 2024 10:55:35 +0530 Subject: [PATCH 024/317] add base structure for incremental entity provider Signed-off-by: zeshanziya --- .../software-catalog/external-integrations.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 98fae5a95e..732a55fdad 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1031,3 +1031,35 @@ backend.add(catalogModuleCustomDataParser); backend.start(); ``` + +## Incremental Entity Provider + +WIP + +### Why did we create it? + +WIP + +### Requirements + +WIP + +### Installation + +WIP + +### Writing an Incremental Entity Provider + +WIP + +### Installing the Incremental Entity Provider + +WIP + +### Administrative Routes + +WIP + +### Error handling + +WIP From 81437afcb3d86ddb24f5bc1d3eab5157cd44e3ed Mon Sep 17 00:00:00 2001 From: zeshanziya Date: Tue, 27 Aug 2024 11:06:57 +0530 Subject: [PATCH 025/317] copy doc from github readme Signed-off-by: zeshanziya --- .../software-catalog/external-integrations.md | 338 +++++++++++++++++- 1 file changed, 330 insertions(+), 8 deletions(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 732a55fdad..d8a9e3783d 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1034,32 +1034,354 @@ backend.start(); ## Incremental Entity Provider -WIP +The Incremental Ingestion catalog backend module provides an Incremental Entity Provider that can be used to ingest data from sources using delta mutations, while retaining the orphan prevention mechanism provided by full mutations. ### Why did we create it? -WIP +Backstage provides an [Entity Provider mechanism that has two kinds of mutations](https://backstage.io/docs/features/software-catalog/external-integrations#provider-mutations): `delta` and `full`. `delta` mutations tell the Backstage Catalog which entities should be added and removed from the catalog. `full` mutations accept a list of entities and automatically computes which entities must be removed by comparing the provided entities against existing entities to create a diff between the two sets. These two kinds of mutations are convenient for different kinds of data sources. A `delta` mutation can be used with a data source that emits UPDATE and DELETE events for its data. A `full` mutation is useful for APIs that produce fewer entities than can fit in the Backstage processes' memory. + +Unfortunately, these two kinds of mutations are insufficient for very large data sources for the following reasons, + +1. Even when the API provides DELETE events, we still need a way to create the initial list of entities. For example, if you ingest all repositories from GitHub into Backstage and you use webhooks, you still need the initial list of entities. +2. A `delta` mutation can not guarantee that mutations will not be missed. For example, if your Backstage portal is down while receiving a DELETE event, you might miss the event which leaves your catalog in an unclear state. How can you replay the missed events? Some data sources, like GitHub, provide an API for replaying missed events, but this increases complexity and is not available on all APIs. +3. Addressing the above two use case with `full` mutation is not an option on very large datasets because a `full` mutation requires that all entities are in memory to create a diff. If your data source has 100k+ records, this can easily cause your processes to run out of memory. +4. In cases when you can use `full` mutation, committing many entities into the processing pipeline fills up the processing queue and delays the processing of entities from other entity providers. + +We created the Incremental Entity Provider to address all of the above issues. The Incremental Entity Provider addresses these issues with a combination of `delta` mutations and a mark-and-sweep mechanism. Instead of doing a single `full` mutation, it performs a series of bursts. At the end of each burst, the Incremental Entity Provider performs the following operations, + +1. Marks each received entity in the database. +2. Commits all of the entities with a `delta` mutation. + +Incremental Entity Providers will wait a configurable interval before proceeding to the next burst. + +Once the source has no more results, Incremental Entity Provider compares all entities annotated with `@backstage/incremental-entity-provider: ` against all marked entities to determine which entities committed by same entity provider were not marked during the last ingestion cycle. All unmarked entities are deleted at the end of the cycle. The Incremental Entity Provider rests for a fixed interval before restarting the ingestion process. + +![Diagram of execution of an Incremental Entity Provider](https://user-images.githubusercontent.com/74687/185822734-ee6279c7-64fa-46b9-9aa8-d4092ab73858.png) + +This approach has the following benefits, + +1. Reduced ingestion latency - each burst commits entities which are processed before the entire list is processed. +2. Stable pressure - each period between bursts provides an opportunity for the processing pipeline to settle without overwhelming the pipeline with a large number of unprocessed entities. +3. Built-in retry / back-off - Failed bursts are automatically retried with a built-in back-off interval providing an opportunity for the data source to reset its rate limits before retrying the burst. +4. Prevents orphan entities - Deleted entities are removed as with `full` mutation with a low memory footprint. ### Requirements -WIP +The Incremental Entity Provider backend is designed for data sources that provide paginated results. Each burst attempts to handle one or more pages of the query. The plugin will attempt to fetch as many pages as it can within a configurable burst length. At every iteration, it expects to receive the next cursor that will be used to query in the next iteration. Each iteration may happen on a different replica. This has several consequences: + +1. The cursor must be serializable to JSON (not an issue for most RESTful or GraphQL based APIs). +2. The client must be stateless - a client is created from scratch for each iteration to allow distributing processing over multiple replicas. +3. There must be sufficient storage in Postgres to handle the additional data. (Presumably, this is also true of sqlite, but it has only been tested with Postgres.) ### Installation -WIP +1. Install `@backstage/plugin-catalog-backend-module-incremental-ingestion` with `yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-incremental-ingestion` from the Backstage root directory. + +2. Add the following code to the `packages/backend/src/index.ts` file: + +```diff ++ import { catalogModuleCustomIncrementalIngestionProvider } from './extensions/catalogCustomIncrementalIngestion'; + + +const backend = createBackend(); + ++ backend.add( ++ import( ++ '@backstage/plugin-catalog-backend-module-incremental-ingestion/alpha' ++ ), ++ ); + +// We have created this in section **Adding an Incremental Entity Provider to the catalog** ++ backend.add(catalogModuleCustomIncrementalIngestionProvider); + +backend.start(); +``` ### Writing an Incremental Entity Provider -WIP +To create an Incremental Entity Provider, you need to know how to retrieve a single page of the data that you wish to ingest into the Backstage catalog. If the API has pagination and you know how to make a paginated request to that API, you'll be able to implement an Incremental Entity Provider for this API. For more information about compatibility, check out the [requirements](#requirements) section of this page. + +Here is the type definition for an Incremental Entity Provider. + +```ts +interface IncrementalEntityProvider { + /** + * This name must be unique between all of the entity providers + * operating in the catalog. + */ + getProviderName(): string; + /** + * Return a single page of entities from a specific point in the + * ingestion. + * + * @param context - anything needed in order to fetch a single page. + * @param cursor - a unique value identifying the page to ingest. + * @returns The entities to be ingested, as well as the cursor of + * the next page after this one. + */ + next( + context: TContext, + cursor?: TCursor, + ): Promise>; + /** + * Do any setup and teardown necessary in order to provide the + * context for fetching pages. This should always invoke `burst` in + * order to fetch the individual pages. + * + * @param burst - a function which performs a series of iterations + */ + around(burst: (context: TContext) => Promise): Promise; +} +``` + +For this tutorial, we'll write an Incremental Entity Provider that will call an imaginary API. This imaginary API will return a list of imaginary services. The imaginary API has an imaginary API client with the following interface. + +```ts +interface MyApiClient { + getServices(page: number): MyPaginatedResults; +} + +interface MyPaginatedResults { + items: T[]; + totalPages: number; +} + +interface Service { + name: string; +} +``` + +These are the only 3 methods that you need to implement. `getProviderName()` is pretty self explanatory and it's identical to the `getProviderName()` method on a regular Entity Provider. + +```ts +import { IncrementalEntityProvider } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; + +// This will include your pagination information, let's say our API accepts a `page` parameter. +// In this case, the cursor will include `page` +interface Cursor { + page: number; +} + +// This interface describes the type of data that will be passed to your burst function. +interface Context { + apiClient: MyApiClient; +} + +export class MyIncrementalEntityProvider + implements IncrementalEntityProvider +{ + getProviderName() { + return `MyIncrementalEntityProvider`; + } +} +``` + +`around` method is used for setup and tear-down. For example, if you need to create a client that will connect to the API, you would do that here. + +```ts +export class MyIncrementalEntityProvider + implements IncrementalEntityProvider +{ + getProviderName() { + return `MyIncrementalEntityProvider`; + } + + async around(burst: (context: Context) => Promise): Promise { + const apiClient = new MyApiClient(); + + await burst({ apiClient }); + + // If you need to do any teardown, you can do it here. + } +} +``` + +If you need to pass a token to your API, then you can create a constructor that will receive a token and use the token to setup the client. + +```ts +export class MyIncrementalEntityProvider + implements IncrementalEntityProvider +{ + token: string; + + constructor(token: string) { + this.token = token; + } + + getProviderName() { + return `MyIncrementalEntityProvider`; + } + + async around(burst: (context: Context) => Promise): Promise { + const apiClient = new MyApiClient(this.token); + + await burst({ apiClient }); + } +} +``` + +The last step is to implement the actual `next` method that will accept the cursor, call the API, process the result and return the result. + +```ts +export class MyIncrementalEntityProvider implements IncrementalEntityProvider { + + token: string; + + constructor(token: string) { + this.token = token; + } + + getProviderName() { + return `MyIncrementalEntityProvider`; + } + + + async around(burst: (context: Context) => Promise): Promise { + + const apiClient = new MyApiClient(this.token) + + await burst({ apiClient }) + } + + async next(context: Context, cursor?: Cursor = { page: 1 }): Promise> { + const { apiClient } = context; + + // call your API with the current cursor + const data = await apiClient.getServices(cursor); + + // calculate the next page + const nextPage = page + 1; + + // figure out if there are any more pages to fetch + const done = nextPage > data.totalPages; + + // convert returned items into entities + const entities = data.items.map(item => ({ + entity: { + apiVersion: 'backstage.io/v1beta1', + kind: 'Component', + metadata: { + name: item.name, + annotations: { + // You need to define these, otherwise they'll fail validation + [ANNOTATION_LOCATION]: this.getProviderName(), + [ANNOTATION_ORIGIN_LOCATION]: this.getProviderName(), + } + } + spec: { + type: 'service' + } + } + })); + + // create the next cursor + const nextCursor = { + page: nextPage + }; + + return { + done, + entities, + cursor: nextCursor + } + } +} +``` + +Now that you have your new Incremental Entity Provider, we can connect it to the catalog. ### Installing the Incremental Entity Provider -WIP +We'll assume you followed the [Installation](#installation) instructions. Now create a module inside `packages/backend/src/extensions/catalogCustomIncrementalIngestion.ts`. + +```ts +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { incrementalIngestionProvidersExtensionPoint } from '@backstage/plugin-catalog-backend-module-incremental-ingestion/alpha'; + +export const catalogModuleCustomIncrementalIngestionProvider = + createBackendModule({ + pluginId: 'catalog', + moduleId: 'custom-incremental-ingestion-provider', + register(env) { + env.registerInit({ + deps: { + incrementalBuilder: incrementalIngestionProvidersExtensionPoint, + config: coreServices.rootConfig, + }, + async init({ incrementalBuilder, config }) { + // Assuming the token for the API comes from config + const token = config.getString('myApiClient.token'); + const myEntityProvider = new MyIncrementalEntityProvider(token); + + const options = { + // How long should it attempt to read pages from the API in a + // single burst? Keep this short. The Incremental Entity Provider + // will attempt to read as many pages as it can in this time + burstLength: { seconds: 3 }, + + // How long should it wait between bursts? + burstInterval: { seconds: 3 }, + + // How long should it rest before re-ingesting again? + restLength: { day: 1 }, + + // Optional back-off configuration - how long should it wait to retry + // in the event of an error? + backoff: [ + { seconds: 5 }, + { seconds: 30 }, + { minutes: 10 }, + { hours: 3 }, + ], + + // Optional. Use this to prevent removal of entities above a given + // percentage. This can be helpful if a data source is flaky and + // sometimes returns a successful status, but fewer than expected + // assets to add or maintain in the catalog. + rejectRemovalsAbovePercentage: 5, + + // Optional. Similar to rejectRemovalsAbovePercentage, except it + // applies to complete, 100% failure of a data source. If true, + // a data source that returns a successful status but does not + // provide any assets to turn into entities will have its empty + // data set rejected. + rejectEmptySourceCollections: true, + }; + + incrementalBuilder.addProvider({ + provider: myEntityProvider, + options, + }); + }, + }); + }, + }); +``` + +That's it!!! ### Administrative Routes -WIP +If you want to manage your incremental entity providers via REST endpoints, the following endpoints are available: + +| Method | Path | Description | +| ------ | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | +| GET | `/api/catalog/incremental/health` | Checks the health of all incremental providers. Returns array of any unhealthy ones. | +| GET | `/api/catalog/incremental/providers` | Get a list of all known incremental entity providers | +| GET | `/api/catalog/incremental/providers/:provider` | Checks the status of an incremental provider (resting, interstitial, etc). | +| POST | `/api/catalog/incremental/providers/:provider/trigger` | Triggers a provider's next action immediately. E.g., if it's currently interstitial, it will trigger the next burst. | +| POST | `/api/catalog/incremental/providers/:provider/start` | Stop the current ingestion cycle and start a new one immediately. | +| POST | `/api/catalog/incremental/providers/:provider/cancel` | Stop the current ingestion cycle and start a new one in 24 hours. | +| DELETE | `/api/catalog/incremental/providers/:provider` | Completely remove all records for the provider and schedule it to start again in 24 hours. | +| GET | `/api/catalog/incremental/providers/:provider/marks` | Retrieve a list of all ingestion marks for the current ingestion cycle. | +| DELETE | `/api/catalog/incremental/providers/:provider/marks` | Remove all ingestion marks for the current ingestion cycle. | +| POST | `/api/catalog/incremental/cleanup` | Completely remove all records for ALL providers and schedule them to start again in 24 hours. (CAUTION! Can cause orphans!) | + +In all cases, `:provider` is the name of the incremental entity provider. ### Error handling -WIP +If `around` or `next` methods throw an error, the error will show up in logs and it'll trigger the Incremental Entity Provider to try again after a back-off period. It'll keep trying until it reaches the last back-off attempt, at which point it will cancel the current ingestion and start over. You don't need to do anything special to handle the retry logic. From 321a6a6e3c5aed0fe0a71919d7ccf6e3d62942bd Mon Sep 17 00:00:00 2001 From: zeshanziya Date: Tue, 27 Aug 2024 11:29:31 +0530 Subject: [PATCH 026/317] add highlights Signed-off-by: zeshanziya --- .../software-catalog/external-integrations.md | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index d8a9e3783d..f49814f9d3 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1079,20 +1079,23 @@ The Incremental Entity Provider backend is designed for data sources that provid 2. Add the following code to the `packages/backend/src/index.ts` file: -```diff -+ import { catalogModuleCustomIncrementalIngestionProvider } from './extensions/catalogCustomIncrementalIngestion'; - +```ts title="packages/backend/src/index.ts" +/* highlight-add-next-line */ +import { catalogModuleCustomIncrementalIngestionProvider } from './extensions/catalogCustomIncrementalIngestion'; const backend = createBackend(); -+ backend.add( -+ import( -+ '@backstage/plugin-catalog-backend-module-incremental-ingestion/alpha' -+ ), -+ ); +/* highlight-add-start */ +backend.add( + import( + '@backstage/plugin-catalog-backend-module-incremental-ingestion/alpha' + ), +); +/* highlight-add-end */ // We have created this in section **Adding an Incremental Entity Provider to the catalog** -+ backend.add(catalogModuleCustomIncrementalIngestionProvider); +/* highlight-add-next-line */ +backend.add(catalogModuleCustomIncrementalIngestionProvider); backend.start(); ``` @@ -1294,7 +1297,7 @@ Now that you have your new Incremental Entity Provider, we can connect it to the We'll assume you followed the [Installation](#installation) instructions. Now create a module inside `packages/backend/src/extensions/catalogCustomIncrementalIngestion.ts`. -```ts +```ts title="packages/backend/src/extensions/catalogCustomIncrementalIngestion.ts" import { coreServices, createBackendModule, From f99a2620fb0064bd22da6cbfb84bf3c5df55724b Mon Sep 17 00:00:00 2001 From: zeshanziya Date: Tue, 27 Aug 2024 11:31:26 +0530 Subject: [PATCH 027/317] add execution diagram image Signed-off-by: zeshanziya --- ...re-catalog-incremental-provider-diagram.png | Bin 0 -> 325253 bytes .../software-catalog/external-integrations.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/assets/software-catalog/software-catalog-incremental-provider-diagram.png diff --git a/docs/assets/software-catalog/software-catalog-incremental-provider-diagram.png b/docs/assets/software-catalog/software-catalog-incremental-provider-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..884210b815abc9e34536d4d86eda68e9c9f58045 GIT binary patch literal 325253 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%0D(YyK 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�uRBwrVX1o9A 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 z zr zN&r{x2jfl+1DQgsUcxWRkFSaYe*n;2&HDUE0wyzo8~}Q!v5?F$1K)awK?6l-{>;Dm zL#d2(V-+2fSUApgoB*uvjs)Uj#!fOXAKkiEd_w}gfN^6n;M`t=-8q`iT0?6DVW$s;oprCgH) z{Fj?U?Jo=HI)*}Pr4JN&|1nmm)%wej=@Ul>9Rf7<^*!UUnuh?d<@pJ2anXY|C_ zYi04v!oy^(0j8b@E4WDMUq{i&SvYAEKb|8)xu%lBRmfUhd4vtTX(T9o%N(%4%E=EK zTL;i(i+%a;ItIRyz=t03|9mC_{oJ{*2Gam2MG{~+i!$y!Fp$S~n0?Ba0JJR;5T2G) zDd5mFHQ%zB4DHFc8)OWYcR?gcFF{Lu4>D~H06^I)%`|O@gn{^V;UrnOTl_ zw`Txt_c(ZU@m>DNE00bKYSpd{SyaYr3YJ&Df0lav^OlutbzYdqw9|xNm z{i8^eF!b5xIafLJ(*ooVnJ5Cex6Kd7y`o-EOl_}(==++0*vr%D~%SOReGhY1!SlTc%&+AWpGJtYp3XJ`*lFVjOB9kFN;VjpaV*;vdzq=#6JukJ` zco&NsQeZ)j1d=))2E6=+g{w9N=l zozri;`T!u*KsW9lBH)>h4D0T0`YEHP*gO+lfaIU_bY0+ecPrL)=`j`Goj6ms4>G$z?pvlP>f9nqTLU)CQ9)Lk3mLVYn&y%C}ZJt(DCyioaG z;m3~bS^9FZQym`S?F9**va`!SwkAFC@>c3+X(fI3eFcG9f5_yZ4(C8*6O>>IH44%% zprwbMLDMeDU-9cWHZ&Zv|9uI{SY4=lRnS$m!wQ$V8o7Q|+>J^--|%~WGXaB!I}eRQ zmv$b5*2d{FwSEDP@8)xxhirITwXM%|D!E<*c=5efgLS=L9J?r%-NhMe9_D*sGZE$e z$m7qvzP37TmrS?gsdE>2v+}c4J2=}5rhA`<-aE0v{&Ol2vm2A4tIQG!cn$)H@I2A~ zp1eJzrZ9+-qMj$_@2PS$UmIG|p^EvoZmxutE#Vd-JpO3_|aQWWZL z{`tfb{N1{?Vzu(mMdImM|J$B`rWn)hVmx|RDCUHJz6f%}m=7fzN3gw)a=Z zEGC0EZH&J;1cE+rb8}C$x;t{8G;U6{eg0Xs!)dcrbo~YBzJHvl(4jwb1lscp4H55~ zKSlGM_g5V{lKow}Fh9cI`W$9lfa{R_1WeHCv< z8JB%KQ5tg0oGBfmw#P4cr4Ao&i=`s}qsdlHI_=Pskwx1k;r0iQ<~rUzp<;y`FBOi) zuY&#ouo4}QVP}L$lzx8P!&6kVpqPx50<3Xz*AdObNFS8I|2QxB#C z4=3s_Z=Fmm%yhe@YFg*34A+&o2&Q(80QdpY_qY3nSzADX`RdSUwnE2bIQaw6R~V|g z6k%Mdw@Q-963}^iyw+nZ@tcE+sA>S+j`?rc9*kdjq4e+MGt0n)>o2W^we2^;s4aiX zut6~W-txMXwchTe)wumOWJE63=?GJ)P!ru5OJm9n&R`5>f!hOgAzOEcl}Ug)!aOHz zQn1Qz75KAdNA9JE+CI12HI~yQ*_VXumXy!;tIv6!*C)bB9e~(tqEb(A zzRp5fF?)IkM54!|iu&>2mEp4r#|P=Jm!Yg5N)yQ1AjUf18W;omUe9|~=whth!Q zgfId)qh(?jTJzOR$L!u#bA=;V19Y;Pcadk53ka6gH(kz>q-luBsMG>oVSVufW|NQz zGd;2yuFa|V4GR`ph~;br)o!M8tsiZOD#Y21G=XI zR(FtAH6Wn+{KCEm^CL*>t&1+DJv>WY>-ix41fY1Ta)HI)Sc;G#wr#uowwmjbXl34o z)bo1UYUXQKAi|KAZEHoPmJe>Xm#{>9G!gG0AQKCm5n0^qi$Pjk@n5QVds{C;%Ju3~Sj`4+ceC_FK$~SjR{K>3-FZLT8Mo^u$6~#x zqQ8uZ`^FyQN|hf>=P8MY$7C#V0bvDjNi!*E zW*Y%<*ET@%{Z$;X%;6{01=ZQPX^rHGad4Cr8KAyzT!VE7?w!vE{Glo2l6qo$#rt~s zPPW1Umu|q|qw}w&19o%DH5Wh?GX#*@2~`zfQ8V3l2!Ll%99{(L2ZVUIk-h=HKt!J# zCNzIn6a+?D)CW{VAsUW|WsN37c#kjNJfPJ@HFT5bQ=!%2-w>e_V_FA-kCHxFM0sOa zRBS3%*&1~OgF)pRWK!WFdUfmaq?}CZV>HT;z2&%1yH?NjM$f_Cpj}$8T<}rIZyG?a z@EbnffCQ5ElPo#%Vin+DletMd0awGHLnTly7_n`@Zf^q}u>?S5DR{uCwfW0+fz)3J zwh^yFqgrG&B-s-UbZ<3PgsGvbhb=XX`oX9x0RDW%^K^d`<{B8sPg6nF_;SN7wBTs2 zS`@uozoK%-r}u52o7Gb^?}>sYn68mS(D%t}f3>5->vlVd>JIt7v0dPL$?@)$TSKqe za59TDwHGvh<<0d8qWOT^c8>sol*axM?ui1u#u&)w0VyQ) z_F6YDs2pOtEK&Tq>FZRfvT})P@s>Km?gEf?N}XbdkoYtbPay4n#QR%=;==HS)H3dH zegi+sl&DMj*H9-)@|)>F)3^V80Ay)m2Fw0|yJZq^mq*|RNXZ*`i?Hw9=D6%P zda6oE=lrLB+K5wsI=qLfO%XLxde1NpGd)j!+2*9UL$b2G&cpD22<>5)v02Xn>80)H z<=!ufxs9reDx%8f1G-yaU!|u4$37LJ#-YIm+n}&aWskXyAITI)>LUcCVcaYMI!~o0 z2a|>ZXEh=|>tHZIEYbn_$>{HYm45%L3wO468d=|_=d(oii^Y+IPcZ)Z%5vOPZ)!b= z5OE?`tD-jk5cZkW!P{!`Job*+9}Ugo+9X@S%7W#TW z*m|Tdc19)xR6zZtE1gZSeZRSP)3H@OJjc7-Zy<{{lerimk97EZ+KRZwUAuPfrzut= z;~#mnmTvjMHP*Mt?mko49t%zH3#xt5Uo&@71CuSI9pP3ah}p~))^e*>*Qo>i8nY19 zd0bD*eiqw^QTJE*3t_v3(DysAcwSj|)z3hnhU74%_|YQAy3slI7aU|674!EHQ^($Y zm0g!|U6|v$6`8)Yd&o8r8NXc}FQSN5tRweGA`UlmyKY3eoCVL(YIm{23u`0RfKe(a zFc5?puq)pBKKu?ir0nUmL5@ztq>u>GrdoI;d|6mh_6qK#QTl-0n(kuvfokM)oys-H z#AKq-=(24T2H|b?Pf?7_Mn(*A!+s=CIT-`THK_5l4-$xo&>^3T>-q%f1cKs; zwsP0>oB*I8zZ3|yq6)a>8nyH3_K9#Ls5t(Gn7hQH|2T)JPx8Z-IuzT^H|qW!X9wXg zo*)d2lb@w)+L0Gw;I1~NXwS3&&+S=V&yj+X3dada>V14zYc;NmVb<+Hbb+eklEbwn zqN8h<77AXeKnYos%{ynM?`q%_Zg5SV`HGC5G&#Y?1hm}Pg@gQ9h3;w4LT4<`WP3*b z$#)dhcX1mG9?R-(b88lH?j*YuC}vdFCI{T|3wDGBwt(yLdN__t9sik z)bN~h4E_~5{+rN}_ydhZq1dkpTYPE%RbnLZV^O&&B`gIB}kzb zy}E=da9e81iMO;Cd!N<{iq<4^Jp4$0wIkQyJ^{6WjSPMXXB3L65Opp%(WFp;-`vGS zD@+y{pm48+LR9rj72w0Q=hZfHM5v0w&9&fpkrSDJB!Vx`>J*S#$rsen=}{DCWre+| zPQuK!Oo$zf)Mmec6BQ|P!R*8y8Xc9*Pio#}kE~{yPb#_CBMNOP1iv62u_vjVkT#DN z!CWwI9)pT7y1KTCU3ryo;7d$!X{UIO)b+_A1UVS5FlZLfgDnqswE#4`)IS{EF4aCcCa&Uz9Y?Rcb?CI*hWh>z#+Ip2_c*O>5^*oCoq$ zJhat{Dhu9+0$xB;LN$ZVDH_I2ac3RPx^&-2E0tgl)`FK=PJXXK&T6$tcLqE!)=SMr zwQj7&nyABqb%K56FkJ0Z$1 z)aP}4$yb`)qqC%~$zFYeCwRdulavt_@$xgNi zEN&%r>_i~T?Q>L3RPc9u4QsW^sJxJV%6zUY(*HKB(M4)}uDD}0~sXTWsA zuzLD~WwneqcgkzrhdAbIoLj7yA)*lQYB4LXn3IP*ys@N%a;$q7WUiga_DD99Z(c^? z!ATjvmpMHeY_E*Z5%+2kA~yIM-y9*O+Zv^YsF~K^wlqWwf4+GsL2PAnod-7=-2Wg& z*Nlws9xemAE{e3bK@RTfR3MK-zQSB`1y&mry2|-ZIxWpP!`WU3ozpn9_I+Xe#}~VK zKxJ~!P#Blks~H=HrpXp)vSK&mC}+e)uxrb;TVr0Zo(Ar^oZfLwsK%#tOehYKpL20U z-P*<9v#Q04495x%#R~3U5|_TL$C;fL++o0?LRFkybw9rsM47gWoK7D}Oy<}{OlK{b zEh?@k6rY`tJ>Utq+Pfm+HQB9LhO!0DRlcz~R{BSp%3d$D1hpx!Ud%M%dGPEfbrnRHW_MEj5)uv=&D~}?$1fLFa~7baC7j)H znz)&WgaQG($4<6GQ$1RhV&7?H7M;Y$Rzv_oYrhU z9>uhNXHKk}Y`<>bDokUfW!=Lgr)2}%V-u)k6EMJqcSb63CLcpvxl;;a@q7wb3M*~! z%vi3<45kSI8l^%eD7A8CB%e_4_oc^T(9(&n?vvu%5nGm;><-j_cKS1?`nWlMzB8j$7`h<~7?A0k#9wxkNz%oqQ_^Wu8dA*C|F?gEb~ksIZC zGT>vO?Ro(w;-mMd$gSfExuD|kTdMGs!T^tpL=Drfjv-6bIR5(rGlt|##&1+xklUfiXAe4UIi4r~~F& zBCp6^TYKNUGAIMvJFcQE(qYGyTiAQX>|%p(mpAB4oc9WrLjCoBj$BTc-z=3Ya5+_q zNkqRTu=$WgmC^|v00T_3z(`c2HXeFy+BY(ULARMxRF*_`+@xdocsZ+88E~(Gx^1F` zX6~7d2Yzbss&9Y4?%{8R0HkrBpSWGC@d6+2uDuwCt-l{EeuG075JO=ktC@n8b|#vK zc!#w6tTmU^r0$VfO<8ByYD-xx;;WY~9| zW>_gS&{IFEkbZXDA zK?w!#C?oyS9HY}#VUZlU#DjD zyR`2);ElW~UgskyoR{m1%(oM?Zvz~)3(rnXy28By&m82djSeL@tV-e%VXvB`H_t>c zB&&nWh1!?bkjALH%fpB_9N*m}l4;q8BheV()qc;U%+_(7XBYwHO>hpU7fZJMbq=RUu#Lp)78?$u!@XuU~2yc@~6{-p8JBu5*Nd%88F&-m&_mh2L2n=havmnZo+lJ(I+yQ z!=Djo7PE4Y62AgRZ=*$DUB#A=w+^AIGqiPn%{EfNtQ7cJj}^ zF&<-Q{gK*uTF+RelP`tq{J?cuE>d}{#zvxok$2^Qw zRMWs7g%AqIR0lGEFxrOGxs}Hb05q@2fLr`kh(XC2D>7_jNXvRKVOhZWy4Xp}^l4m1 zOTw8P{E2JvSjNG{*P+U9HMphyLri?sLxB&(iyg9B+y_Nh=UsEx-Qh6zdXD8zr}kf; zWWQGa>=YMSBR8l@Rb!$BjS8OrkoLZp;$q-hVQOrQDI(kdp?ab9(*EbA`gH`2T&L}= zkNVZM7Ux9L)9fncst>D<$#<2GxBwhS!lfz$iha*7yo+s2?J7?Nh3czWZ1$FC^eDvvEY}=f0)ZTMjK?pzFbbi>kWi%UBPV3f&ap&fVcufHCB}4;x7fE8sq=z1W=(+x%QNH(O?U=^pDLJ$=>a z^k%6g1#7+7K*Sn__+8uC(&YKfVI|TEjfBN~P5#>P%Fz2383Yk9xLWrCQC~3V1@cB1 z(7$QoKwVcXyXL_lpU3N|Ok-P?e$r{}x5RzRTaZk49NB@`5C4rKnop|zZo-BJPn&5u z0b5=`_38Q?MXhx0sYjOkG$3Lt$KYC{+~MgFQAj`Zm+bK zHdDr{H*Z*KugaF?!a}T38ri`MD%Q14LRCgu%*GN;4%^zVl(9x3i#aNNF(gcF9+yh6 z^BY#Qpp@5iNVDRoR5&B<-03m%HK>~Q)`1#7MVZ5i-++f8#{&LgO*SBhL+8=kfQ~3a z&VHLjDwI=9Aq{lJBm#kg7%VxNw7bNI7V{i^E|O5HI9a5QV*6;pqfm;=GkbqXx3^%k z6=&1s6|c#bQFkIu^_~D{l>msW0rf2~Yzhts7@OdY?0K3HcPi_u*jmI!G&p`uNPRp^HLbdzO|#oIm%XD8l@ z5SiD{#cu+XGKQu`^$b>);}wmzUM0&d*T=Qv zuVq-ieM;BhQzZb6>OE4OSD%Tjc=sFIADPyi{$vXLZ;!PnLx=}OE|$f6JX_cUF|2bK z%WV!G6Pb8p#bps}(4=3s^@>jCq*+!gPkxnt+y6c|sGP*c0AP4*mspV_Fs5@%A~#H~j~$?7~CuHAHSr1f*#C zeAec5eaz{BsquY{ERl6?T9|(+ki-Ug;=+g5$V2SieNkuaBIkHT`<29{khP`Q2N+_m ztB#YGoHi&APwV%m)`z- zw^k3t+Zn>=+L~pZX=mkVZb(a{_cgdwRbf_kf8tW(_9#B4Ol{v`xLWN}Cf}&pMSb&Y zhf#a*Bj3)P4Ovp!(F=^!>@|!dmj|SCmjJhGzDqWOi{VM}DDfM7^!8<(qgwP8&M8aW zhORB2gSS?W+AleLuH4(;%)UHC%QBmQgoSra#c_b}rI|EnemDa0Xt*!ZIPnNAqLjQ&|gcUL( zo+j+|IgmUi^IIns?v0qUQGv;q!SPGJbhey+fYFYu65f{wX-YNz$}3*Gh7I-&D_~P1;FE}c z^SxH7LbqP&p7-+5qQ>r*VHe8Zp)7`x=GNa&`Q?s^pK~<@CwTf>H?FRmCr#O@3IuOe zT(6%-FX0La*{o?A@$c5!`a6>Tu;*A)b)9`=FIAsjf;;}r``47?{oGe9j9Am)sq=A) z_+v_}6{^vo|M2fe=pA-v)Q6RD6m{k!(1Z`f7q)4CTdZ|3 z0iT;*hke8vBa@l?)qdRhMzWcxK&$JFv)txLfhUQV*g`3%*U~peEsh~py7Aw)W}C*f z9zurE$6p|n^RgJhr;jjiHA4KizK4b_7J`l zX8e-w5H~g%S46VSY`Y#!8@!@cBIT}ejZmvG-0x7TG%zyhh&)<#Ai3;4`FZz>w$D;6 z{OlO}aooYoHoD}C0n^1{r<%za2dz1|F^^$i8vhE{h-BUZ`b-X8uo7k1D zqtN*Fx+%Hmpy)OFwCxRiX-LD-X`^S6UsGBDiDlLO6R+(TonviWuOM$uc#Dn7wvx(+ zB9PcRazN1cOi$Vk_bv-Km=I~tEQ;sHmO1X09=2d#kL@T|Z(G@KMHt4t&GlAWKQ26w zh!2A4xj6KEcj1b^2kX@^0+XeZB1ZY?H^%Xt6OTDpR3V3RixSP?IkZHV#hcdyr(L4K z2b)cL9Yd;XJK`2AWhv>7E=)~%*Yh=$B7@$>;%>OD({P510R~(Z?X58ncSG_9>f8_g zSLc8B@O6%<-EjP!mPmZhd^8pjMb<{t8{qq>K#aUDH(95m^W2Zax-47m2<0T7$C97w zwO60R6?VKX09`2qFt~1Fp2Lfk8&e&&v7I6|O#d8Ul}pV@ynsUcNp`}EvRaURR2AOe zY7KN0#KZ+GvvWwhu!KRKd%m`9OpMo7xQ#Sh*ItQj=ABWq1GCD7L1lsPW*Lr|OZ4zvPQ{gb?X8xCNe_ z1zC}zhlg7Eq20~Cfz;`r1U~>mWhqMo+w}EYj53Ml4%F#|y2_5PNC&z|yXN^eiAt89 z2MtxK*%#UE#99I;eYCERDBiGjjqzf;o-{|Jngv#D+T;$$Y zJ-{AI1B@m1RvBzuAwJFJ820Ery&~j(pR1oY^3#teEE)bUc9oBI8~b=AO)~6jPvIdS zE=!p%`H4?R?8icKm>s4xL=ek1R_f9`HU&~)4Q>EnW9Hh8ej4H=5Zya2Q{`>)d^qK8 zD?Yo^wYk#eYU!_3m8b==WV|l+Y*j12o{ey>QUY6f;UQ@Sn0a@dC+T_Vu6XMajg>DB z{%Gld)n{Y3f_){Pg#_C+v#F;@E8v2kmc zx$--?bQ)lkZfixe^CH@1@3|=>2U1clZt+ov8v1;k6bUR0DDq6R)P-n1e?o*pAu{9^ zpm0nMok>h#9voLYY#aJIw$YTL67MC=2;;Qg9(-Xj>TO_ox){q>E{r~(lou>USmfP4 zX*p5q3PAqQ6hqpB$NtW#9nGoeMJ%$@+6XJ3P*gpGZk<9KcY_K#eQu!iAZc&Xiab08 z22LoXOs_+(J@Y)DK^YPO%jUTqkZ>Fg4-U%l$+ZdEEr!;)pA|GK9o*6ldkkcN`C_Fdie5@)D>PjVDV!gGCd;q=815@QUE*5YdH@h$B#4k)+Se{SzDylO!NafBooq`UdfD;XXOuS_jnP_ap4mTpRm9M< zlVpu{=m!umJg?ttIvkF0()>Q{_|^Ch81yzJ(*AhOgtb5Snt;ct=&~NoaCm>Ra50%t z5#h((z`iN3*dG-I& zNkfHRXo{s4k?J*`Cm~xmy5GDW{dUZ9^QC$;!&Wp{%k>C>qy%SA$B6?Qw4;F~s(<&# z*7yg$-lwGZ8PkNZg8Qf>uFKp-VnR$G$|pRNYKm z4<};wvg4^YkC4~EPyC?>rnbD5Wb2BR^rJ&7WODLQ24v)j05_AVv^#dyY$AV?Z#P(t zk28eMSFnVojXsa~WN=YYM9RDM4J+{tD~xYUx7P0whJ?Y(W~MBVueY~n-ziKMB2zqk zy>6QdrxuIq4Q<+epPy!IW4*8D%?yJ>=IVLX)yh886aGNuhW*|1(P%ZER!>f;Z~AkF z4$3Zmx)muY@7bE@A8wd{*mK+jn!EwsfHH*voT&Dv-ZWUUs;D(e(>XR^N{f_!DwYH= zicyTaSsFj(cG>mwxWm{u84~wQ#EAv+ber%Ia0A%gV}qU-`CG&$MbaN#O~?ca1-|^^ z5#!7%$S3%*@UK$5uNJIq8U2JX8tQm<*IRefR#?Zby~k?s_1tuA+V|g;wt=olUq_nu zH$gey_eq)9g&AIkV))Q00>C(S^DvJ! z_kuUkTgZu2i1X(fN*_Coi5Fd0@mPGHV4KS1NxIk~JYhSY&TCsQcB}#z)cRe*hu>i| ziE7}MthG)Rn6T1fhv2J<)OXziK=k~_&ZxC5!^^UlnyWR{JwDf|QB!kf|2=U13b5iS z96ww~)&5|vgtz3smgs}%@Y$8Bv_ZLj<#4GX3Nm1H;uYN6^X95n(brY^4~k!PSS&%9KV@< za!LGUeAyhubEuhjkBnG9DsT$>Xtw<6D?J{aJwfuoqqyD2lv0^B_owu<2mKPW=0G(*G=6JHyCcJ{P5(JP>lTtjL z9LG+!+w%sm1JNz0Zyc3lxvi#CUrjU$8~XS*|Ni0(khNJt{F|7!c0%lrvL4DFkMR$4Js~&Q!abA(tTu2wo1lDt6H*G_yx(wgc|cI6@>Ni zg+Bq#V=r-UfUtpGQ>ZBN6aVqB?u>uUf~7fxsD&J1&AMeB2N!S~CRT!qmm9~|>D-Q# zc8JAx?doNpm3J7l$q6~YzmeE}Oke+CVFLRyo#n+)Vnn`x>+32?NLR1)@ouu{1XJOI zQfh679*vg^%7wp)Wo{=xJa0K@7OV^b!8vHQ1CYiJqIW#dK6BoQhs%<*v$VqWBX*Z_6Nnxd7re5oqE+vj9xc@~RAw<&+S!WC01(e+sc? zOkgGQ$Q%7Rn-(BoLQM4#K$<+h=1C67I&Uy&M(vaq;@0!_z=t<=LQuJQupd)**kH4M z0OO7(q_}%*3)6h6sCwzXlR{0D?hn_#vF6RH!+g7=y7pTq8&{^E9~|CB8-VHE5#;e7Z;SdlI>Bac4b z_!fSLUaeAE&CwcqsSFBKRKnUViFbr?ME;-G2Hs!D7-sYE)2^6r%1)Cvl*l~IP)*BhwRUT;WzC+a@I=1wjK2Ff*@qCkxGvZ*qz zz7SJiB{cN;kOSbdEc1g@)r9t|HpfIM=NOz-lqa2cyqmH6dHx;|O9^+E2dM*f&t)Cd zTAKS4+)9NmBgv28<>q=e+ZDdtjD`P4*EdJk-F4kIY0}tgnxs)2B(% zwN}#m7PON_y;+dad&XYs&MS#fIR%&7-tWY0< zJ%7F_H8<_y0r)Ttnlouu`fRi13*A>J0A7d8FVqGio}eBF;Wv&)Y%M-j-5AXoBr)&0%L!oU4mBI`+{uq76e{(T9>p#ikYP5@J199omVYp z8y(`C6kpY@ye-S(ay2)=9uRNnR`8Rbg>EtiEXBhnTI`OH6+exX{Dg$VVvr$7pRSnK z-8&_Ys=Q-}n|2XS&vE-SY7oWw%>VicPQMGs@u(93bBAGS`)!9ew2I+mT$<_&D&$j8 z&MNP9BoVqY7V5LaUU;A3zGRNr z2$D|bDC;)%>A@J2OsHA4vO^nu2XW>~P}#<$j7V=~+y|IMwbEE{M;e=9dGz$3w3H407Lt0VCog%~-usnYhm_$|^TDEUO zW~)2`O3jE>Uq!CQRErZNa>Vjlglnk1bQ8fARYvzhI`Eb8G{SN=dp z4RR1u7-afdomA^k?#xmdyc$M;hdY&RiQGhIC8Xs0via(0-2K90Qds)-oY?Ql718mq z4dVg`<>&19--8gj5kTadH^N2R$i~XLYtQHn^8KxSKm1P+GI!8FKTD`$MF6w4E`nsK z6@lZ`i8B)2-Jxv$y&dwUfer21Mx)~6vuQW`lkU3N7l!@TsEzOoKvKTKVBdeXKtvfxo!O}LVG|zw<6Gs zEjHVdjHNO~>gehgyFFgbw!1opgoV|8=4{2;J_wLP$kt;4%z%iEO2Cg@+0G7dsp7=- zI?IBt=2b1F!@n>T35254VrIT?icJjADywabG9y&5zhRMGMqsPb%uusQ6(qz^ne*9P z)##)dZ-*p%5+!7w4Hoa)q+{FVVUp)~eecH;FvM6S9b3?0GB0(;et(0~qppxcejQSu zN+y}Xeq6mo=y;yjJUfIF{uf&Mxvd4K6V%I55{OezH)h zSe8L=Jk77{M0>pGM#V&tlEy^S(miPUlaE9gulAFDYH9EL=YJv55t5K8H#YTCnG z>ilZNShw+qkYq-_O<5S`cG%++2X~V%FxeC554s0fyN~+Y_+( zag;*ChIY=GjK3a74u8DS+K2EVJ+Qx#40nXtmEckh?%eeNKM1ULUJNqx7V0 zsEFtbag*;qq98S{)_mIF&3}#TC6+$Pet!uiQg`2l^yO_`3kc~;U=?1+1Vq9Uz%0!U zX{uGX{+$(%5`%Y5@LA*L`I=mxq>Q7KYFXHd0yi|Im)8N9Uq!IV&%tX2sTpu@ukOCZee zi7WJ-hUqS8TcQ-wXt(LxUt5NTGGDF>MyFO2K3lG@9q;+^epZ-?vJAgsLz75I6QKNi z%TR)WmRs4}ohmQan_}!Chn@gnT>)cmG4E=w>BOMhV$r$;nk0FKDs2^wb8!rNaawHS z&3Bczi!*-%b1@Rx4><(3%#QZD@o4tiKT!{vlZP8Wt7R6+V1IA5K5gU^fiBA7>#$gz zO&=G!@1{sOuIEd%#d@0v5I0pjNVmhYdJ9Gezp|HfQIMyi+QraKM2`9aBoa!Pf!bJo zbSFS+iVD=)dckNo5BG4>nJfN3@vt)?NL(T|NMQg}`~LF3;!kceHq)hM~9J|M??0p(IPH$x8bRhYLN&<8G|o zk!YH~vGyxRmLMMFplxB(tVpW7uRqHD5R>_OOu^4oo;6lSyff+`s#GSPtzP%A0k}3nls%wwSZGmOL&PBdLEja{(V*lF1(QLyc*DU9ZKV|M>zC4wYC2yw<(edU` zdN`SmB%V?^2HEcoGdN$2uhH^u-)GjkLD zL-A`Na+M?WVC=DX4WH;*mnQN&2XK*)cl>c>v7aG&<)!fo z(cYINN?*whlqAMijF9l~e7F;)Nx-8c4?1z<3HmO8$y`aOUDHLQ zF-h5fq{Q`{OiX`7J}V@iT(+=6QpTZL_6rEuDUuXMG~KY7H%B(_H!7CLPwlt;rRc-N zHbxK#U-QP(*=7KRmb(XTf;-sN4966Y&NNG~0^S`AQE<9|bCa zBdLd-DICrTPazewP|JwZ1gfd{eL4_(|u_|0+3^+T&*%1>9id)X+ zVWQbpKe#>F!J4adMp%3!k%g~5Db?9?jqjtcM?%_4&wIW*U`%a>MCtktzy;!1&&`;qg3Op~y**Y)V^&pWA!r=2f)f%~xoL z88v%Bd-|r}^V+X140b%Hv^!BrNk-A_T5s6H=@gAI3auLM$0v${6=c^+1zY&SY z{nJ{<`4wW94SbQkE^7p+;e&hHI5IzKy!_U;8rY>)RgMn%+;PgbxU2(X2xNa%BboxH z%r|-qtoUW!=e-A)7}}f5fdLS34~$ukIIooNor&mgr4J9p!TL$Ywl# zJuKlp_QUDFxJ?w>Xv)vjYm3gQCF3R>8H8%X0?v^7NS0 z45+OhueXZ!zCO7&-5D8_6)b?YnFEjHJRPiZL{wPd%*^vJOX#i{k%q{_ErWXskt?F)w zB$L^EyG_Y$Klt3WboHPPo0cx4E1lj^xa|Xl<5qW1@Y}Tw5WEvAN+hl)2a`j*RZ!YD(X{mth10jWeq9~bU3dvDI6&!esJ(*#$EF&E@L znm`C@e)q?x>2r0qIlG9}4*jFh1jihSblT*CpKspCD9Dt<8ZCCw5x8ukBZ*Yf(_--v z1rvhd*}!{IIquow)M&ZgHc%ji{o!iHZsYEvjVq1S-dV;V`HGH6eCwO-e$|bD0g5Ue zw9w+_{!^7d>&^Mdjb7~__nbLzW z4@J21K$M~GhP{`a(CS|h#xF)zcH+wqr}Bdgj~-qqyT0YI5ZaRNz`f=N2m0pUp|%m% z0a8j&E~82<9?Z-ATgc>h{iBE42L?Ujd*N=KEpjbEP=c# zhXnK86E2Q3Ao>FTkw0r;q&;1Q&2+U6b8je>hI={-3n+MW$X14YB1Jac4O3DawVyHm zuAdWk7mhy)N6Okf!DtFYC@=zRkZxw1rl)_@Rqz4X$Hz-8Xb-^NsW9jgH|bv*lB#rD z_o&Ng@8jkcf_$eN{d^!PQmuk0QTMXq^l2&F+W(Amv%2or=?A0}Qa73n5wNi|h>%EP z7k*u1o!P|aJDQ3}H-o|JBl2c*l!>Dq_HkVn#kdhy)46W(s-ye`cPZhi8TctYg>?So z-Rb&X-OPAiSj7xXi5R4g2;)}RID8KZtCwnS>EEb!* zzUtd6J| z;0muXDy52b{LQbT0WAFiGy?K486wahMFFKM!m@@Ln4Ejr_e7H2XoQ9J1Cp4ciV-bZw|h&HuP(Am)4m~AjxJv zLm)O%1qQwzSN0Pb8m`io-`JCFl))RH{b=|13Q%xp$ixqc#aeoKn%JK&Ej=CrGi4g7 zae_SwBX}9J)p}~I9&D#%zHl9p5M@xgGXA1$dVLxBnbN40O>loW!IcNSClh~?K%*(4 z+NeUlPUc_s*6eVbA`cC})(*H&p#Euiy2IU_pHu|eF70$xxI~adsF+Gedi$0$#Ro7n zL*Lv|;=JgsKoeQdJwfF9u%!h$XqrmK&M`W1uH#E}=ymVhTKI~gvoa&F$tfS?9h6Cu zpi!w*;*MvF`A8)Cq{dppQKx!-)lN_GJp`r{|s!O%I! zsY=9Zy(BUhqDUpBPNe;@C>q71 z!L}MXkrhM+rD#ole9P?@sb*+2KN(>OZ4c&3@)JMnjUwxa-+3okmT-6)s0*JQim#7^ zaEnA4MzvlXb%WJGtsfj-zSTR$x-d}we4tUUo?j(?k*6(=W+QWq6W;$`JxJiF@T)f% zjc5ax^N*V3a_65906#KyA6{5L*qY?pep52z-vAUC^46?o(XuK#f?xhHN7l*cWtkW> z8ZrW{h_2G|%$QsrZ7JuZJ)wzH`oqk_jOHzE4S>kPqq~l>4by36pUQZ+4s*8L79!O3 zAz>^Pw7{z8AWGcIa44oPH9M<<3)09#Q6`%Ab?h;>6VWJk15PjM0_JLQ%5cxMR4UeB z%=mWr4>p?gG)im?sVaZTUbd1o%@3W=bOx{-w?A9J3xd*E=CrF+mj>9lvcD2;bAouP zMA#`ppO}8^q&r(NKPs5jP_4o?jtn_4KKtqs_b{Q+6zw*mK|B5aw(Y_PIWcYh?KlC1R8PWP49V1_pwhl@+f_9;g%6K(#L zfk#`zqv7}w6PYQUzT!Vwjx%GUr7ZmSjTU^ALYAud@3^kjXjD2K;LMlIN|Tgx_@SPFVa2&Tr&yTqKvTxstzsL>5aL!3r_pv(uDy_WF2U2%cRtd{a_ECp1 z8~08EcxYVUuo&}zR8FAt`K*Zf6~VXssouS)U5DY<99t~|3O35)icMZ?SiVFPrKkM$ z__vudE^U_SlpxPYXZW20D~?nKm;}LBu`lJmUM#?&>Pi{fB6~SgiPcRco z8Q`~>#bUd!berO_m?f=CuG#I`IymFXJBk5T2Th`_T};+_w;u0Iqw+8J++Kmqe+ z`i@yglDMcX@Jxoj+)l{=hs@6z`co~%>@Aia_00I2cZO+)WEwb!(`_uBGicgSWCR30 zi#0=l-2so8k0xBo+THqU?A#qF>QJ0R=H~gi$vN6p>)Fq+0}ZDoGRX0 znK(AduYL0R0G19aC#!C2)3U^wuTmO5zH%IT$YAovTpH48fq$R_Dlz z+^d8Jb|lwWS=my>8lT=<&O!3RL*)g zQ{j4-)X@I1VVrP>xL~k++!Jg}8<`K1tstY{KiHsmwECh9>F)Oxt2Uo| zeIT-p7Agm3LOC)E=FPw6-3LVGsTsqtt%nT1ZPs&csn;heHjOzqJK`4h)fhXJ+wf=z zKO0{m^rC+`1=kSHygoo~v8PiWGRXfznz~51$9^`RKD(iyWN@h(8#?->@8dyzv?& zjV|quWV&5COlqi(NAxrl^tQTez2fO*J|a%z9_HmLi}I9l>g~mp#M&w#jqS;)_j^5L zYc|$*ry!Ry`DJ$NZRW8g)?g1(<(LpuGSPRoX|0uV?Z+xQTWvi)@U9ep$KXM();hdC zjG=(Hr0_r_4v~ZPK(^eG(UaNi=Mt&v7=h-C{)mD#Hb$fM|T1QJAjV5umI~;8d znq`Ri!8?k$YdB{9ynxT8%h(s-STM(F`yu~cyB3I611-T=W6V@CKmmIKj317Y4KuPo zkDX8U_`}RhB4r{zp9_A6_qKx}N%o(HqD&j48;ISC=rOBu7!qBCeT<2&-6T@Au z8A+hb>Fovk!e)`f$f9-0diw>DBApp#&U97=z1wm_Li6{N%j`kj%SHap?_8WCPxG%i zZ90afjJrr|Wk_v=Q3P?IEPCjzE7eV>I3rq>V~R7Y&DV?ajM_^p4Hg1Relf(UtCrJ~ zf6hJJ`bt$32a2t-edLO?cuJHsPERFZF50f)snnXH>K0Iw2SQuhHx4NfE{z4asm+v$ zb|yc9mN~WSJ}ZQ#v=MS3;@3*8#(S3sSToaqPZa~;Q^#tR zHHuP%9frn-k?I~O(D#?iLnzR6co9H9^Z8$H{R8A@AFozulKEIt7l;L5V2Rc45Rpo= zBMpd$7U&DXC@ez0@7ng(h~kmbkY`3h$C$;*G_K(E;ckOP@-ARNnxYc_G2z4_;BRdlPz z>l3~ElO13!l9&@k0`t3~>dkjOzh69c5tT^?LmNfq&r2nIE_&~IGt zHs@=y(3?9o_Vp&`(61&l%bw{FS$nFD7n7Gswsx6S^FEudpl>zUM-h2f9+V?B1Rb>> zMxw_LA82SWp-J9+Llb!Lz@U^gSJdIAqS=@gZ^JSW08*XHHwyQE>gxwEH-0=RXc9^b zhpm`gz-S<3_dthcW(g+IjUJjFMHrN9x-U>EPMTzMPgDdC{D+a|3)|u4>9t*^((j{< zdxjR5y%5%z%MJHfd}tD2yg^al3LW!ptFVVbA!pIKWwqdQXBaN46Zhk@`IB59fe=Tw z)Aae4Q1O`WS)6XFsIZj?H?R|^gQvyQn#uw|KM`d)%8X?FlIuI4&WEWtTZ#dEp9DxG zGxE%knv0^P=*i@m*W9C2J|o3*i$bHf%YRltdV^z0Io0=HI=QsAd%V(LZqY{>%VbNv zyI9km>d{Sak?9kSr<#7T?@D;9(T6H5U8q>H_Z>)3IEIGN+as4l6vbyDi|j{U0`7dJ zpuTgLbthA=a7@ep5AseYzBPd~H4Q$#oJKPpT1^`oND6{^ImzBXdwYhLorAoBXELzjo~pjh3UR;q32(?{@H(%UcOOj9SsL zMou^+x*n=7PtU_yTdXPDdHbQk>`JXN@n~+O5k)9q1t!bXFB?H8e5C=lmTw8U>i z3a@7@#Q~Imkc5SQ=D0bBnQ|aFGTnDzxsB1L>msCp11-yIIi`aQ!A#0MbRuY?WQCm>A@3Yo-mo|;lp=Me&urLB@n;hQA_iTZ&o_W+u2b! z8u#g=zinK)nJ%rA4Ns{*r4A8Uip1gIt?Zm0(K)Ca{Z(iSTH=qg)e{2uOu_T^J55ob z0tD(XeU^3;5hnyYGYU)}CD4wNZug#_TBAi}v8F7X&=qXaN4WhHoWG4=zpCVB`s=9- ze@kt3g5#f#c65=l50q#Gl(f_EDU6mP;L!Xf%7B_68!((J83^(ilEAjy>~;|t z^wQFo1G|UZ%sBZ7HYw5*$QB*U+Etq z%wq)7&=mJyn*9>Wm16H26987oMEVoN9i(tPiV}0R5DDS*Voz|iFJ^fX__3SFNc8es z-kz{|%6e040wD)Wo1{YcgN{raw;Ks%-PHqJ2S~dYUlfoBJ&lqODw4-`W3PP zC!Q`{P?wG7T?70`HhLq0rr95d!M=-s38GC?&~x&%L-$FCZzLC_&D0J*2tOxzsUe|3 zL%iTZND?o>{_HZ$xh9CVCj}U8`xuwg2+76tmB!*dcg(Fs!}9!Gz`#$5eu51ai_s+K z<#PPg-c)gB^UYnv)LQ={W9>$GV1Vj)tBVDM#W-4-G;Po+N$0HBnPc}$tXJiEy&$&x zsy%61lk56!y^@D!-c@1iEnhpfDx@{4#YPL!xtO)xk2}yyj-lo00ov@($w^7vx&z{N zvwoU0C$py9Wp>+?a+T30@;_TcuTG>hXRbOEr+-B{th!g_qhi1%Q1bQtOnMD^)D)aMvgVHk~iRKTsvkEBB;9k`1b-Kg$Z)j|7~+ z`+qSDw;&>9T@TRUeY3xhMuHs!-~+{;F+EUR9q&BeZ`$UoiXT-{xTrS=ATr=q25{Zo?C3 z1bw|ZmvRh2=K47CV$K@=dJH3<9Z6B4Tp#nM@;!2yW3{{7!A-eH?Gc(fH8dzF>-~)* z;7L_Q+Uj}$zuhS*2v1^B=&A7YWqT^{ke)&%Bzh9<=DFNpPgky5 zD@rO_mW479sVoD{;Q#nJ(^v+6X={Gzrl@k7hB+j28hHGWNMub z`T^uWt1A?}s?b_zdoX-fL^_Rw7!`}2{A;mCBt!-pngm|!k(31=-@D-DhVJ$GqLbcm zTn)GWSc8sids(ahgCpzHcuSoX0rf*M+FrYoCS3u|SVFT4`!joZ<_+CD1L!mGaM4lC z3_r`$;g(>-eC+tnt-cr4vHb%tJ_v+K%b7x%jRnnY^zajzi^PmVzzbG8TS>Q(7Fiy2 zIz=g@>ApbqMz$7d2oH1t?o~wGuqZb1 z>o(>)Un!|{>QJadjzhU!9t8p|#2Py{H+Bxd-pB4r@7Mr3IYRQC>-rtwmDYM$07p3Q z-xG{9@kQ@n)C(7)mEu;f1F}Hp_r%Kn8Jui8A3*T1`z2BB8ah4t(eRsDzvDrK`HjIv zqS5Ig4OJYUli>=M`?IQ@T9K4C5LbxBGz~OcDW`Y4r~6=x=fgDPf0ZfM2Ct2F$e%Yg zhUBvxL+y0f8yzS6Z<_XR`+JIB#!Z+om?~paKwOE%CD>~H(O97+mS~Ozg(30gd@Zvg zAPHT(6~z0s7H8*=a_$P`E#1s1YC`oTvh>>cn+SI}Q^*DGCt8d6qdb~hca-g6An}$= zsn{PcE77sw*oMT!wAq3<1JGxKx!UH~Ft>jiDB3bdhqMyUTPK(oF}SovT=qwc8Nhq^ zu#TNp7OE7@>@aUjtjq@?kqqJegSJ^_>EpczYz#~Asr=1$9;`kMx@OlQRbd%{$3E6lBY zfPx5nC9E#)WV_w1#}5krIvV8tY3p*(y(I&&f%%t``~V`00-4t%a=KSqV1PsGt4Ola z=P^+S<(oX)t7)BBzt%F z=qsCzp9KsB?RXDhw3wz(_z#e`LyAgOu#<=O?m1#i;nVv5`JcBl@O*y%0Fs_ndFIQu zz}b`ql$b-oD{;$Io>x4)tn(stwx((Eu#s6j#P7cRi$I%nfTPi6)01BB5Hj@%;4AWg z9_m#0$LBfrhL|hh1$pC=`U1aJ^DgfIfihOTaE^)7JLMRURX<8SKbzOR9f=aUlB z@WJS8Wwv|c;s@gy`GkQ8#UW%hRsiE8GiNflm;Iz%B4@mvzDs+$)GkID(4K|eas6hs z?P36LGwl=qv(B21?(%-IKJ=5ejbdvjo4Dzp^EFhJS_3%yv*kI1?uVDhYhKI~dzXv$ z`=SFnN!cv@Yg7=LdFp3Ff2pq{5@V_KH7j8W&1;~-a!^L`KOa)MV76?2s8RU^S%A7B z)WT!XxEMn19?wzCi$CopO>ogDU2lk0c1XOKb()_T^py;i{83m`lHPQ^-w;EeBE-SH z4`t7uE~>MNiMeCjvEEYIdpiBf<)KDqceP8l*W8C8S`!=`d~X}r>KX2!13?lAJPv8 zqxtBKUzk;hP|xeUD?`s!BFdyo8yZTG7gQqZhUyaW;xul+8nBx!&P?i|RU=-WRxZ&i z&?n`W60(_fY?)ih(Dcz=Wd`u^_}_%(9WHO)D)mp_W5NH+Gr05!KT z@_TKLg_;N2p3}gz-_KM=a~bq5(}iLZ^pW)}n>`A@Z(?-tqUyWTEJFr@3U~c!OqLqQ zx4^jF?NH&VO1W0Yjz>i;v1l!OHUPE9mC8LQ5uVhNNX z&bAE%sDn`iYR3Q)9Ti;i(EQwgOoLjs2;Q88(_98H$xn671$dTcuxdX6$5 z$xcT`PdJzL>nvR-Pg6rm=y17p3jtm{;#2w*QtP1phZlQd{-w=;uw#}q+v&LcJc zrAg&c2~GebUH()YLi;T5J-O0-0rG1seh)!XcJr3u1ED6FZ<1@n3OKh}+yUs|9^VM!XFryUS!alXO z7192z+9>Uy_viPkqr(943hhZ@S{&qmK$9PW~l>(MQLdAa#6wzu{5+8NRiYOrBLmyjI~@OH5&7k`c zN(DEjF?J{iB@wkRk%F-KxkB(TMB)7=)u-DYPU1?4#~Yno!0^D&=ES089&{EmHBA#f z{)ih_Xa4b7r_o|rDds6&Ub~IQ?XHF|1p(4NGehhh@V~t<2(Xyg zSe#v_jM0KP=f-3XA}|Fj%%^eglt!!MnIY>5S0V9E6sbn5{M1Xu*q3!@E4!|O;Kp+Y zOZ*`Wb8pwr-i8k^VR%+k_0YY7=rvm!X-j*F`r3bpja$r1N-ATcP?oViZJvmRA5FilS<5 zjqWrRr~bhX1DXzKC*+4o2VDiPdtd>Lt^VR5<0Cx4tYq8+ z%_Khl4<744?M}@xod9p@7MtvZft$U=T%(Qiv*X`DCW6?@LZKy>iy-MJeiN})xfKCM zpZWqnlq=YSvi)!#)Xl|}KKfGq$$YI;T-?2qveBOo4qB6YTLA1kWcrrFXiAC}%%9;F z?g+sHzP0Dd{A6V3l|Kq)Fshvi+%7WI<=O?QP70_ZY*p7nZCj-r>Wj>#KhEVxNZrIQ z{BXTsB<|a>T3qS_eTBR97@8no=d7%?C+M}dcq`~T!=Fr- zAXNHUh?Ue+gNgWymKe>weEEK80dGk-n=!iiVqIt2@81p}BzYhF7quz|STvGi7Bs%r zL{~HeE3?;O{p*c->qoATbbEk!QH@on|7Mz3cn{E3VTRbpW8$F%#bvPxA`ZtDDpjbc zBJJB)VtTr36sC|Xs_OfpioMcd4>MOJze13XkU*^=Tx;-R5^Is(W#MeGE-Ft>DP0n_ z0!>n;*YbW*HUqDN{K`%o%WS+{gAhmx5lW4RLdM{dxw_n}9t4LJE8u;g1Fl;8X)b>p z$Due@l*bi^kpe?;=lu6!Zk@v#Yl%WRxjs_J;5v}?9kakLbXIjTuLb{}dih(gt~m6jG(CY9IW^7*fAa&m`thLTh|E5R4kt&8y#p+_+rm5WZ$k+M zMR0Wb>4tGpYm{qUi%x$Ocri!czq+Ky_f&0sQLFz5q*6lazG!*L$=$+X)oF?4q| zO2HXlHYvX*a5*|%xE8(K=&JYf81$fpp)~GC-^Cv>M-vD~AV(r9%2aCR z23c>hrZPRL@*q?i^VeMOn%utv8Uk~A&ek1bWKY>B(s&JU#?$?^h$EK=pHA0hq}g+<`w#REAg z0z;bidDqXG%)rN>dZDGb_6JEH_49Y&S%6w^zBbItW)!(nZ!X^Ic$QGI0f4Tx$vi7s#T0pyQgU$R_wFJH6-G#~BTf1wKZJ%d)w z95qa&(`mRIpf=hGankIgCP)I1$+?+4?pL^MDIKKxmHWxE1Hpf0`y^fceJI2A;SgFW zb{#BUN0TW!@UUb8LUjMR9_WDi$U}C^^|o2h$<~{rM9md@@n6IE?}!%|U~(|6>l2I1 zo9KM%H&#At7Hdv585=eR7dOcC_*m?8iJTHQdnm6huKnClUgxssIb3033S$@Vw^H(s zdS`e1;3L_pVRah?jYvdcNZfD}hI!`M`BJ(2BdOJzcC|;?upijdm|cRXHLBz#*sV6X z`(aMFeb(DuS9a65Tvaf|L{nCJNm{tfWj{gn6X2E04K?cNir}_P6B92rmKEYyQ)~Bq z0yJhH05y1&?T@>w)BGbU%St%H0ss*N8N$*2omKL}g={Ld$}#l01v=f-MB&!ezjtdu zPzS$*$~61Mi+=~n)Gh?UN%iwetK0Oa!3+duOgJ7-kAKM<0ih_R1U!jc=F9Ynv}cPA zefpTFcyscha;!0-+XnivM5gF#d1S z4J-rgXWGNvELFtsA3#|CY*!SWtJ$uaj|lJegny&d`xmZ4{EZ9ZNt%X=f^up&o@PyM z7aY@Ve79IUIar6E^;Ac4dQju-3x)`Y!dQkt5?MLDjJ=4|49 zmOZV1o(*WQg@9fSxq2c}Yk~R@<3qY52RdD$k>}!|bCNmT~9<;6N0%?cqno zSM}4(*ncgC-eNgzF(kCMYlBFE`DlGdnbqU;vpPnFnA3LJN^_wZi=jLptt2__rMnU} z@riU7f(R+*_ad46+@sy#9SK+X{Sz&a>>@_pe%RyX`qH%@Zk$h8!&GWo_@7g`ljxs~ zPqqEhZUjQm?O6%{fZ9Uo`(8u$zhx#J%awYncn**3l2 zplhCzF48`Y$$XhOO~;jrlp3GUmkb`i*o)pzNDK5j4enJO;$OHV+yiUAIsr}VVn7A} zh=~5-M*ZKfe~iHOtMdk|)4fd@lEn$c-G70#4@khvoPQ#s129o*iR-XW4SW~zYZ2j< zCkHLbTy`Ss$y9~FHgk7*f$jSF%Y6?HA^+Ld%IVqR_6Zw~-yx8jn#qBMgoLDZMouao za3zTpbN+g9l{Z35sx^=wQ6=jAKCI^onoL*t)^JWrZ7;L3aLu{v8a%qR-E3>94$scY zx(u8S>Pf%`Hsx*Qr%JmuR{45Wc)tf1(l9ZmyNuhSXTi@VjAz8ciIfoZgn{>V_S)TI z{5X(u^=)-_)}iMFU1) zvzrz8)h+6)*$U&UGY0jy*PdnN+=ZQ;bbShE!Dbp>u8qB7dNc1Y3?;Im`(tya{%%5y zr(V3mzY8;+BLqFKIp)gb18WQ?B%J+Rn;~#bBmwRuCjbsBU{d%8D)7ZMQ6@{d37B9X zRU))35vc8bUSWAS|88?g#P=q|P00285M?kjkx_3DQXP@I&a$MhuQ8xVfW z7THu(G>$}O%iVa>`wy&HaPRc2~=3sD~x<#ZPb|C z^v(p|xH9W4UO)6fffoZz0ojC2Mx8I~8~!A~_il4x+Ux~oUFnMs^0~dscf8a=Z8-Za z3ex%8_s;0G2fqRM=&Fv#=Y{DJIuaS~^Xwt&bme=&c#1CZdlSOeO(WYcw%p za2WJ&3heLHqz=B`@6B$H^R><~CX?A9VCrA6>@+1VE{`;eom5R>^XoR}eVK9%r2hK2 zT#l145uqWsq36}VT`HRw{9RIfrjHNz4n44u z2T>DP1GlO^(7vNKZp29dXEeGMaR>OvO0mR#hWJZHI*D{;qD$1}XbUAK1XSMe^6Pm1%yryFNq zo6vBGb+8Uiw}+!dxl#%BlUG zK)kD9ojM%xpksi7=9A>55lZAT0&zKM9}IwZ^(Lv$eyydeM_iMn(hJ{ z-^^=19WXhKPh9E_%%y)Gh`{mf5&50=MOX(B6tm27dvg%`Bm+?~rm5KGr2 z@B-manUcT|lch}Rd^xFYQu&lRm94LvxDW47(p0cMV=qKtWxjw8L}p4wEQZVhF7Tv| z%{D1&6FFse({ED)MZ%XdsPfDqD)i>U{)BLOMlp?j+O*W}riZonumi4n@twoOW{+6|MBJng&hOrhk=glr)>a|QH$qWQ|=I!e2~{}b>( z7=PD?v*7Lm6)6uHsL}5m(kcK@MWu;^ef+y*ZFo^>X{UB1^$o_B7`}diAN->5@gxhR z488ngv)D?PH%i#Kcu2m1fqLT+OEpR1yKFrk90FPZfCvir5_4JThNHXNCX!EpsjfC* zUnIn+v(W^s2wlgZ@2a^u5r0E?r}jKa0-=kqQ}oMV-d-q>GfLId``c1q)K&oLs4)=HtOHsSsh1G{hpo2^ z%WCVshZR9ex&-NNB&1WiyGszHyBk5IySqyo>24&HlAr*k7#^V_?$GCTZ5eF} z>~Enly<*uAsSKM9K5&aNF~U!l>xP1Olq+NU-NqGCwSPpjHCh$n;j+&2_!i&+!*hku zV8l9gg)x4c&@pAD9sknhU_mmHZz{U0o7s3@yUUbe6dC}c*DG(+kAHAM*7Hm0AO`vJJM@iU2lHo49w}U%CJ7aT+8Fk7%9my$bQUVB=gbmz zc27~{CuU+$bH_d*FI==fZTK<2Nfi8if3rJ4m2q@3)s>!ew{(=VFeW_cOu7w~Bi z^S(w)=fx6s!edu%LL9;0I<_9;2gfS`ls$5{9Cpm#SW5v(u`B#S=At{v6cRbE;lEsi z&HtZk1rf>}C{qsQd9shJw2LywpvUW9f0Fu+7*4Q<>m@fT0JNS(sJ4#nrK-7jNVt=c zYwPQ~pA&u}u`ay4*c;7y^unZ2Z?-R7R(i!s35NjHM)vFpukB-S!u#Y7VU)Uj(D1;; z!V(2~JzVo5poB+jy*q0DHmp5Wk%Q0WFqVQPU9{(=T6ET`I>{>nZuegP8DNr{gb@l7 zW5VsI5|3%~W4=Cp4U%NV%x>ANO|a<~7W5TX5%T!JJBahU{b-T*;;Z7=sQS8bkV!5ECh^uYGgV)-(bFm_Fjd5ekfd zrf)SBhtw4TaxB@+CNPs0f`s=-N>|xWcQOuF!G?ENH(7!IFeBY6xPHBkF*F z(*M|z#Uq4DO%4BAa&==`-zPsV4l@uRg@{`M_VU;vDH75w7=d4CathV+xN*A$Qdw@N zGw4&pw{+=Y1gt5*Jfc7Y>Ab~h60mCEoWNtqohP7RdH?@C#l7(=IU{3)R-L7!cD=P~ zerRxTHlS{dCV;B*M6SpW z{H4ICT^GjxaIQSp;>LO!;pD9c&SwWq0^L5%cN?EU!_su7^7$zdXm9)8L4S!B?f&wy zKcX=%9;bs&wQU;6#Ao%g8K3iL*tQWkpaGn1b5R@o?^4 zlkFnhhYM{#{jn8c{+>4-ub_d7`AdvP42#O@zNdjCrq*WCBp z$cIIe4=Iy*dPwfy_SfH=o!WGVbrTrJ$QWpRV;+VN$ofj!G3B2Y0J8Mh3@&C9D-yt7 zAU=oN@#1h2A7DGDMq8z!#2lgR1I5UHpN1QL$E~rhUeKt5$J;D2GO|7~zo0jbySNXH znea30d|f_cI*%gY@Pt$Gl0!oBXJV=jo(7N014+Q7XOB5+Z2;bmj%z*esVyJmufg~X z%wAk^`wVV3bTv8rP9bi{p_3HVF_}p@(c$g21GsmCJ2!+4E+dTY_f-++z~1+@pG4A| zLGo4CLYAJ!FHwDDQ8D)Y`J>w3VwjEH1@L*^D(PeuERUqE2*F@pFO~a%jp@5*Jx<=bhd$F|o* zbN>yvtF57s1DLBZq@wyd>GxS5if~&Fqly@9Z&>&clK9bfVm)_Nv)63JyvS zgIcYnVS*jlcZdKbp`+HzWbo1K(p?2JAeFZdCwsL2Rb+l!7v@WL3U6^7T!RN zNlwx_Kr_vBQayKfNHMo1K#OA8PS#O%Sdj5W8me#39vE9Ls0&cG)NVVI<@Ljs)&XB z>!<=|J>)BqFmy{K>upW zTZZPHyMc+KMY2f6Rcgzww}UtSX?^6Qwe!jb5+^$Qi!B9gw>1s0awhu%D)?!HOTOF3c>um{V;4Zl!ZiHhpF z|BAa;)D9Jn8!9>)MkSe)jO@2-7Ab{PLiN1rl zP2pM!A`vP$ufZ>nyovGPZ>(;CCXCH#k4>w=l$k-ZNhLOv8oY=K_{z;i=H&;$iq_f4 zoS_9_5K8jbdZkBw>z51S>C|JQl$Vs0#KhH%0&`d1Gqs%48vhFGSA0rWt+8~5a-q@X zqE{A6GI@Ik-*Js3p9OquCjZQuQ1kO=0M|molhZgl_0Iq-l>`%xzVGmmk_Ni#w1~jJ zBRq&ktin-6OJ%c?x^uQtc5W{TCPgPClsoo0A=w;GmQD@hgiG@402XEOqHmR7Vf(*w zgEBDK9sIS_Ao*RbEi6bvm1Wgr{-6s?^D=}|E!7y);O!;51u+P7pNak&j8_Yz5Xggh z{sR3&ek>5sO5Vh-sTV7Jt^U-R7Ps*kLSemwK5zOlroH4{(I?&ed(w6L75cv#BqQ)3 z>l~ZsVw!mXGn#wwmbQBhCvK$e+*1YJz>iVjDhKRvtKkJsAs^^1Ja|qy9}1%15@^`H z-nY9{=3_Z9z*J9j!n}z^livWThgzuQS`Elt89=fl7WP^Q4Bx^)C`vB&_J4Uk{D(=g zcJEy{3;7RtD1Nd^qX8wYMsqCS!xWscx3p+-m_vg4gBl9$J+8>hI?5F@*Xywewufz^ zco)h1a7R9D!$t5BrYZw&5uX*m@Sa(Gmokh*dxc4mAgCK;_K5~C#_#Hg0-mofh$tareX1Ne9`kuZ^aPRxxtST_Fss)#g<9cXiWE|A5eYo_`W zvheu z294Ua=K1#n%sP$O@>Ft}C|M#q2+)3k9aC)5VxXC;P7h^{v+pl1B>@F>1jse&!@I1I zmIvND%N=jgk{E~sllP0|>=6ONVlC)sQ2_0W2s~cr^0G}8#;=7{eF>}~M#Y1!87atS zCJloE$q5nkIc-!&SFpe@sJXxVaPZC`*IVf4eAsduT~SmoF!rS(@9sHilz@-7`z;Nq z+)%9@UqJCwbzEsbhGHM>Qp#tP*7>Zg80_)b2HES7f2&LZ8<#X-!kB>`8{Rl7_Q6#w zN(55Lxf~Z1bzk;91kJ&b5=V7H&xp=-AI{&1gitl{$!-p*=jj*7W#?({w#PR*Y$+tO z7<-P33%)@+?z#E1N!Ips>Xnf>iw4pPD0$-8z6FOUi8m#41Pr3aV1+EMY36;k&TZ;#1dr~klHkLRUb7V z^rV^Xsfl-aH1GKMp1#&CD?hxYj`5{s2=R01_ep{UsDy96zJyxBDAKJbJSA$cx1Mc^ z2R>k8SafP9ZL6L~w)$X+R?PH8Qxb24TSps9@0G z`0?QjivlNem!@CX|H8|P$=?E05%091JQFPo@)bcY5K3v@uaXZ(s5n_n*Tmx8OJ3}8 z>Zx?IzCXznkk}D`I&fEdX{A%J3e4h&;~}mB#df)3gF=)n-*VKx&5qa`51~JifPWpd zmTTX;tWXI4Yw1KWb8T%Y3R~R})t#dLl`j*c8baDahE$l{|0uWM;GtZUhu1@q8J*s* z(S-`RN@S@HCB(@$%$QEq(46{lL&^^es4lW_W`8&9!#-Yts5wmVCk#U-!i4;M<^NW& zC0T12B>q`w8l3QcvCkmy^FJ4HF2waCcjVbE@Obh`ar$3_uJR5-Th4HNKecIU8AeSgb)8@KhCsyKXXIeNDhxc~nB zXV5Z|kOF|iw=!u1iEr|s7UkPF&#BXr4#PeIWfde>%a(seOO@>t!QAzMDvD;fh%&}6 zoqprR=TWl1DS&940?Et&FGwr&_YX^vQO*?|_Rb2b{^zwxP}p)%z)=6+GnT@={@pY% zS*%>F92i&(3X#bsH?mvcsJU~0+ z$|Cr?hrOVZUu_|M>RAUWxYg~^`%ZZc87 z>@D5wa-6Nx=%Dx=`$~CaTR)H2?a!zC6_V%wBou;hX?zYTeV!Kq;>In?dAYe*lh5fBl< zC=6^`ls0KqiwtrpDGU6wQ=>b|wCi_9J73gTY&*}9(VO+8aZ5r_kg7F`qG3Mc;Ny01 zn;g6;GDh^_v1StmT*ht26Jp#xNA#jtG<(1@OsL}x6htmR0E7i>c0T*;=Uvhj?h|R* zd)^nlRBTRzMygwM9841<0#7nN4;MIPTD2jx>gA5dFf`aW_0WKGAqe2| zf}kL{W%jTs!HySDh)?(a3G}zp#hTSByIJa-jo3Ja5iD{T>KHFjtJKamK9T_!@jzNi z%CBIk`wOiGTN37FP!cF@Q)Mb)3m?XdomK^^S{1jd}#JeX4ecpUTPGUwQzfS zaeg$42BAu(e1(JiLi^dlWJICDBr|8C@FPC59Zh8WD&yAs)gB5;%#)~&3CV2ltlo_q|eEij^}!>l#oU zjJ-->V6YQ_qnGurD~IH-EMSHVlqw)HU*57A;_2+YJ}njc^uH|zhURT-7!lPc%`FH# z-G9smj*sM9lQg$R8@5*HYhca!$t%q-%psKr-o2lp632wa$uboknZ_0(p4=3Fy zh6d+sz|407yXy7ZqNh|gXUA_?_!ZoOn^BZe;QZHfZwu5l#@PS-)O>HVH=fN0I7VaK zi2cNrk#&LgmgzZ8=2%eV1jA8rJlgZmsKgz|_Dt<3uRtsFl_!DKWCKlniNfc#^)<6- z4ciBgUBj4T`vbik?{2Y>R07TZ#7xU~E8yiy#U5!~*-mk2|G=lyIb(S8pgApHMTkGj z<#yU1zgiAsukpx|FxX{v$?(K+g9F;i zE_gZ@vcJXpyXSlkTRQ4CZu^g6?yo@6mcjJ{2@(VTx%f%5`qPnJXsBowcb9%MMgydi zo4_IFY^!Wf&Vl*`6mgb+hpqvx5E8CYy3ipJnljo4io)b}IgG9KNSe?t;GV_2aZN@x zp=*md+4F)c>A2s^B-^sfYx`Gf2l4IZwxiGa=Qvp#ebD^OzY_O;Z&|x3 z<(V_Fhln!_97dJjZ~X*z3X~7bE)fXCwBO)l(^%3&;D-cQCW|zs6$N*1&SM=nMujMD zAa)btBiqHE0U_0|`Gts$;7T>#N+n>>_IKQnX-`l+d<~@3IwN@Ab#&RZ~6ko#`{iq&%aG1Wt6@x1XGfUj$(6+o1gWkTin6FSbR;-f|xg!=Xe|g0Hk7 zTdHaX_!+aAXUN&+kT`@U_wBa@91a47<=azfl;i7lR7y%GL~L3kWiiSVw}?<_r}ijT zak;@{y2N26{@IpGg6wBKvDu)ij%UkQ*@XZ0y(`nzUrZL#FreQ%VH^bZFlgFLK{(|E zQlg33_M1bY!Ta{s$LuZkH!O!ZJkf4Kw?N;cQmtZU%v|IdAQeX@jvV?}1Do-qq|D}P z!xXnLB?4b~EQnSi%Y)Wcv0g-r5>h#a6jBPs<6|oIdM`FTEWIKydKl-wu(2Mryx=D# z?P>~gr*GFEE>kd&-bZ5@p>B^S@1*4Cm&1Ld9Sc|y%^D~2*;d*dbO$r!53!3LMIWz3 zH&U({BGGuF!wG4F_Gc&L`9u%eazW1tmn99Hi?io%(4xJqc)i#1WW6gXb(%ay71<4L zNPE0~XmWaS_h~u{p4o=m9ed;NSPb9Rmi*qveI)g~{H+1N>6^_T@pK%mcaN~=3ls7` z$mbfpv=!*mP7KvSOHtL$pV@C&)b^3_QL|0vp=m)o|66!<@UUm8GENHLAC9?tm-?8z zT1@hx>pkT=!XG098DA^-pF}|TWVg>-6li$(sa-$rx>~(mJFWMp=EnDTXhcNa4&D2f zZHaopvkZ+=ji$9U(N$ue4ku#HiWiq{ly-J@=SNwOYd3VNyP?O-pHxbJ;D0JUF^-Ja zWI&(!_?Zo)E{;$cI&XN3W0!L6K7xYasL_Ap|2|*h6^y?}x#OOUfneSG$fGfl*!4zw zJ(1VjIM>xa?k~acF_)3NQWb4ui3m6mO5agJb(?jGJT&<9YQ0OsNrQ*eI3KlB?z>$# zD-VSacXLO?2I$uuMw>)GZYELj*B@{Tw#z+fCY3)bZR8Jj?*d<2*-0M`XSN4sn+R^V ze0ECGmZ6if=l;bO^@TM9*rz~tV0P_sJbGpW*sW=wn zAhL+yHMGtFKXVc@Law;Tf1)hQ59Yh3ycT8-ZkNG5!-I2Ie1xh<$r-PYK1 z;IUy-&L?-$c`}&f=GY2YpHIJ*{_yLlUx#IMItl2hOQ2|_8h?K)>7R-=e{YZ&f1qI& z)}8GSTN(=e^K{&JXs7*2O({PFYj?MhKtRAauH#x)BL<~z8HO%!r~}N12tcQh1`ga+ zRhPP{TqVcWdLkk~@=zF}Vi~Zx?DMELuShyKANW@5Eg%L8Q>THUpy`ydDMnn_7t|QP zzbPl?gkY*F`!W0N`)+E&*X{e}I~I#ujs;Un6pmvxkxSvZyPA~2czAR46^p$1 z*(Xx^*T4+I_YE|!BH?LX#G4#wFEr{j=m#1<-+%2QeXxu8&y&zM;E~i<>yjkc99DX} z&cNb%^4MGh zc=f=9xt17&Bzsmd}I9JVcoGjK!K46-yo+Z&J54c9d++c>Uu~ zq(vBmmC4mdMuHuSFdfeIw&TV^dLPbLo%451Xp1h@>Nl{k`PC(4kgo96u1t!?Jz#n51>|qVi{{o_n$=$>RyxUE&_o=y*%DNW zVp~o>w}1yyQAr;r-BR-JEECPb0$F4Fp60s4nEky06f)}Bj=SUKA;geU@e4GsUF5oC zx`H4gs82aVU%&WY#_uX5SkV=nQJ#~VtBcK)S2eR*Z3~B;1T?-tx_)!ET?oKd#WF3Y zFQLgLH76rCj^{rNj5kVc&n}8ab`Cp_Z{mA64O&HBF6}B4R}-HiGa_gDuqXlJts%JZ zRJMHXt^jy#y;aczHwW)R;3`dRoXDUj0fvm!RVsC1>nwR5QWy@Quq-S$Apvy?5GoDH zgX%1=wnTkw0HZ(Nvd)nCD0M8P7gKZTb|SfD%J`?FPM!psdqMvM39FHF@I-~Y%~~%k z^8I3|Qf)?D*fYG-#T z?yAMov$S%qIADNQX+gljEI~7KHBHzT6)3-aCkex8h05O8HoVgITZa+CrBx{f5|XRp2ZH5` zu7{us*sse0%-%T41tYfmli#75r=%2W@!levsU54$@&=F;#JZ@n1Bm1aY=796Uqz)lw)m6zRB7$~d$Lv?8>Kh+-ZBHL)M&ZdQ@mVus1$FWv#VR4QF}i9V^|C2 zdX3KgA>3Vzfx~Jng#TblQvpwts_*-NUZZx}Zu`BZ0{f~(vCR`xk->VwS8lxuZGA_S zG0Dm=K;OjA?7ekv=I0UTj$MHzeTGMidoWKYa`?G;e07oaTx=Ho4-Ol5-)AeR2Bvd6ed=_{I?f;E z&evW$Nm(^*+Lh?_JM}kTOG|x?_q<1=ilWoizXaP)*nSvsZu0u`wUQ!uD)gMac%sS4 zf>y5p{ejL?Jsq>OU8l`HKnjz|dQMiBV4W+gV*Ap{)PZv+M6*-Tt=%ft*4g zIGz(m*{V}(R+C@s{g$qB*V_fnJd&Jw#NJols5gJ)1;!ulu6iwP^X2Eb01M>T!MbdOEH7UNhGO3Yxp2+0aI(2SCQ(v#8aTs4; z&K_UY(8Rv#|EbmTG0&2e;tTwzjsCEw*(~C&STSWv*{GPG-V6hDBQxKx3TrGkx$W*8 zZp_`#FP#L5cl(^|R8z>!fA75;j+5mq^X!#U5v+744Ir4WlWMlUsdYRwyU;U$E1m4M z{-&`yq5`!3b`@`74!>B=e#8uDv^%6t=X27jJi-vYAD)z^j!KH0tF@Nw4aNCRTU|&c zbQ)&&^XVghv6gG!iTQoT;&LHhvkPwdcf4BjHxE8<$xV&Kt$&NR9m}5D>hIjO%`|uu zv}zV&zq2`~L^^mXj;@BMi<3`d=^j4J=cwSwue;$JVyg^ISMHA-{h5a|gy=Mlb3ZfD2+kYv( zSfC4tRxDO(1)aMtcsAF4tCBL{cQ+YoweQ_iWbH|qX8k9u)`w37Yx*=I@a4}14mlLK zCpT;Ww>W6oi&m{j@Zd-W@%dH2@d8f4^aPNX?Hso!B#BuZagvfM*=AxGs4}X}jq0Md ztnE^)K)$t(t?+#$soQ&jn-ZJA*qF5hy>bEdp@H%jZusUCm#vN~CyFWTR(PAk#kwwP z_@V7JE*8 zqd&eb#U^Z`)8Mdr?3_-EtYLF!Ru%*e*>ciG%UXPp8|x8A1QoC#&iU!2?yw{b~r zPJbxBY3B2)mZ|yEs+Td!p;o_tHT1?HC#?(SS0;}|5eG???@9^plx=B^@;4dHlv|%r zYMlgz^gIcJBjo;=7AYrExlHI^snnXA1F<89;;zKe9P-f8(-~^V@e6KesPZkOJAr~F zQ&y&TQ?lpN`b_axik^E>c0uX|BzS2kG@&-vmZoT56^xvNrxycAvn# zwwr|hJ3kYdQq`@?M}f;N)~$tERyNNaRIT=@nGoD|eUN-i4g012M)n}7%Ci;tQ;_d_ zM>@5)J9_IC4aUkC4)I44@?kD&W?(NZ&vL`Ts{MFyAKmtoPBAhkp}sMDcfujMcxwZX zv7<~MGE)%lyjf3P6LBsJH}Y9Sx==A}dfdUcEEsubzJKM{b@!4NwMiN>R&^=5i!P~v z{y*RV_VafLR3n-Dy8^8YP?v=OL-$q#olD{@R;a~hmqOiUy_C`To~P4(qT&xi*VYNH zD>xvFV}?>LkRj2Fr$YfOkzCNe6DG_r@e*f9?@ZTt4!?D1TYU|ag3UWW3X|zXVR28J z9q8sw9Znne@H_9gqMo1d0ui{PHj@^_TGPx%lQVLOuO6-Zfd~RnwM@>*r^d3t_nfrv3v{!UR zd4nT>2ifxT(TSU4i%Ej4|6Zb$xI<`Z`_-;~kK|^Yw_CLYA^k;XO3T^V%rw*IP!k&yQ;pXTY{oBtradB-X|#pKSTAIi`duV^`l+RG9O)jEfW=1=C_O(K z*#-|;>S2pbE?|&MJJ;ZyE~HU+*`Rl=#({Kpd>RVQ;)&-46Dy8~)cu>9E0)q^ z>|K`e%Q7om+~BZF+Z$LHnq8)ie{T<@Mv6tI3PM~M!}q7grx;RAw=b=FK6!4gj&?pw zmi=jFbK26ebFwovO(MBj?S}p8CCxKkq0cHc81pW5j2ZbpP%!Gi8y>l{)zE#*t|AHj zG=1H~+bClCKWl4Ja~-BxXH?aYqRkJ7kCPe7zc-NxCiQ)26_%(CeRhPnp9JBrZ^i)V zAf)Rh3=A4wpf)1X8;*R%?)G=oHvn1L^@Fl<#;dy;WJ z*~n2gX6Ail=;`Uf!)kEfWZ9IKU<-*QI=~s00F5u{Fe#Z~dHX-DXPya{jo#{kVYsYg z+TkfdZ>GHtcF=%B{yT`ZYu+WNHPAt;G|6y<3`BPmcLBGJS9b}{8!uKC^joztswd@p z<|$EQ$)=(to9bSnQS$JfR@)p~Gqdxma$+&)b&&^NbOTM1#z+<^)>R0CDIRs#)AzdL z86n+e!WpeJEl!`4^1tPS{Sl zDQ%n=$3pOR;j~?3ANd8DCbOW3k@z8SizBF`6CBJLFK%6tPr9%gVTh&G&kB(CcXpz2 zLR)4IbwEnqTe{jc^Q9ezy6k2iSKLQPkl=ak$1Md;#r`+>{}(~Pe>lo?C?Zn*a{L|Y zAo(lku&z2F1T*_L-4ojW(|Uctx%U-{=rn5q(%D91o@Eyuwsbrr-KXH7Ft$PWjT4f! z&twyCPy4IRudAvM=_YtD@%1@SNxHbk7 ziH);?Bhp09@|NPd2WpL? zAR*5TqkFGaUh|1Ie-gPl1JO7b2NUT)l{Q1;ZtuJUZ!Y(ZC=$Ikhr>D5l!tvsx43U! z3v}Nq&B{m9o0)W@7hC_>FUR`rnJEowGAhg19y6s#5@pT=jsN$KxnFy#QX=>4L*CppEf zC*K{SE4hTYJu=q9#hNcnt5o`>lUAb1QBlmle!?f2p4R>~3nGt&dPUxVKzO2f@sd2f z!l=R}=*9O?9>#Y82x)iav)9+W_fP8BU~%69F6|hqfcL>wjc#X3kFG&Xy+b`5Db>Da0Y?6UiStrO>V4` zgk|PLS0S{Pb9Ce!jA!G55#pPkTjD7xO9!-n-&Bj`fMGYEMu83ttzJ&t^-<4YP#wFT zS!H8eH0OZeSYo+iJ+K(nINWxSkf1Z-$vA@`Bf`Qldv ztI1eTu)vsnEM;SpdFOcUP>DzY0`v*~XupHt>LkhF%g>muK{Lc(j6l4i1ZEF+6)zel zt1_|dA+pndE?;@{lMN9zz!G30(F-`geRBaO4te8X%*;u?5u(X?pHCb2PMe+E>nyLx zl2kV9yHl(+(K(w+SHESa0Wd8yT!7>qqOtYR`4I?f8#haf%|Y=3I#^QijATFJ(;4+R znbdCd<`X7cR4r)LTF8xuYjeez3&{a|3(0I<2pja)oNUBga`hd88 zIFqqtKKTU!lU_tfsMGO&MscmvmK~Ncz-6uuQrAiae)Dc@d+@j_qAi(zPxUl%lJMbT z9gu9}m_NR3bry|FRbMI7yokK+d^%Yb-kZA7{q}JCre~1g9)ErR6L1gt%3nQSnA0o$ zI-&&5y2u-!M~ipMWXyMjKU=k{wfkyrmenL^QLAc`KplF4t~@Jm=?uuyCx>MD3^?fjAEJdyb^u-4T zpnJ|^dWlp3>>EHSp-EHq!N7dJJY5^hC+(2%Z;a*i$apAgVMQ#y568L%{BfC%G0_xs zIv*8BkdrxHp8l642hs{}sSd>e4Dj7%#(Fk3WI}Q5I#W+9t=dNFfzfBk4dHCLRJgAwf~`DaIrrW@!e0o`HC9> z+lMkpb2g2at~lwX9)+ir!eo={sh?TQVt<4Bfmr@-*lx}^?nY=t2+M(oz4$=afT+Qm zbw}bdDxGOsvu(7y^ZtQkv`W3x@e&wR(2>~HddGAq;F{F7Gu>arXwy!%f}8vB)X!`1 z6Jy)Fn{~Anrjr?9b2UEtC%8XxahMwVb|gQFg}>E+TL7L?rv;cwd2m^bi|(&!wrDpU z*ySDxDH~@T>wdxbb@f$!>^I%~rm9l!i)7 z42@PQ%H> zFaMWk--q~WSy{b{YH4-X3?DvXjP-q76Z@|h0HrM6?PFl@xnAKFL;2f{-@)rtviYAF zmY6Z9JYZ#U7Fz5<-unCMT0N(*7A!Z`1_(uF7 z%;2q2nU9jU{&jqb%gvyzRycU|bEP+!Hl6n#wi0l))>ni-)2>qAK9BA@U~_(0A#-oP z3lx)5P%4O{5$?Tw^IDZB31T!cF&MKl+x~`P~PyF%67zhFZw9r%vEv|NRwfp&v zx{?b(lorH*_i=A;ui{dRTd$XTEzNHS{kKf*z-nR4a`)!Xb}*GVX#Pra z*=uA_#|=ug-;cNfrn}?qxmh%pkA_1lX!+yp(*RQ;(MGpTY7_}uf$Nsjz9Yg0l;OzP zS%)@Y_y&fo9AtsW7WD9;}|kvyKlTD z;BgnZ7Z^O-8c3Bt_%49#r)ii^`PhHD9`K}&)0;zdbtH;Lc|A;6O@g4!FJ2HK-1YFjsNW zh$&n~;4H=SVu?lI$#PgPpkmG6c}-1IFgl4vK4z5bwlg{#w4C^mi(UKV1dbNU6LrQ6 zDEJpe@8%R)=HsoM9;QmxxGf5>d32Jl@=$k1iI?!WBAY&OW&O73PqnWy9+G#zAfA8$ zp2}}Q;0A5AWR70pdC1HyS1p)G-k0iWAIxBc3@)l7^&=a6CbeCvlrO~w$OBdI z30~uQlIK86iAFa}4a2YRneh?{PXzdgG7qu19O<04RA|@#S`&1&wXG}~X8X|b%#i;V zw2hs^cI{G$I+J09k^(nQDK$C<2AMIM&uon)!0E!)WKcjCQfk@fiBC42JDw0O_Pp*r zF#RZ!O=UknYCaAbq^di+b8p&(tuK3=%f0@f)xR^AyW5Q-z}Dom^)p%Wsq{M3JB7u6 zZOtJW`*i4|n$B=y6t%VaGZ6$So6;Za!5t$lfKC*WI(OO7UaHge#%~|KMeXjLR_mvC zn_zt3cARXInu-dWYN?9o0)BqKnuGo3FFNap1Sd9uBP{V?l6IQsdQ{{O$HvA+tF^yT z&uhL&?7cu7@zSa?gn_6sb68v?B!aSvG-9}&cPVM{@wj(-33xxYz%8SsCSVWYb31CN z7@2HZGQ3>C(c-1+V>$JJr5iijI#g))=9_D<%gvIA-$6IVIEwV#EInAxu5UK~?xpwY zBb^Q=HqwsJD)h*6Z4>leEi&7uB|g0K8YC2V6Ls~2Er1Km06)qI$-7O%IzZ49&4m#07FXP>6&IRAfrHy2jkG`%bfx6+QBlOS(6hOg zT~5plAtKDhoQW04;5trJ{g6ly(1_!>IVCd`y#X?t zF@UbaObbr$g1(DVNdc1C$WeOJE(SErb9FRVN)LH8cN1q-o+4OiUP)rP$`5l=l@Zzt z0oJ>uqx4E=~HgxD<6YECv(Le>J3L}ezV`E zt@P(*QMYXJzue&@^tTUBRw>rI8dhA=7rP{cK(NUv9fbw}Bn;@8J$O3%gZcUq1GgDG z1Z|~Cj+0Pppl25yA~vt323kugaM%`ra4<7pNIiP-QNvbdYnrj`tW?3I#KyOGsg!u}R+zrB3&unQ*pyHjeif@1EqRnYwAv`t5 z>G(k)9aG1U_&@a_@Z`k!z+sUu9bW+UY-kAXeR-3~72{mBHHE17SLJ@^vT#JxYieFq z+S|*akz%36X|vS@eXQl>89u`&d4amyrL0xP_kiaH5p{5&%~W~O)mo1wQAQ95i6bB6 z$BHA59#c*QFHY7-f0K>wa>d&^%?+$^S~urz@!=p?xO+b~PDE9x1BDas>x%@0se0AX z{t{ZgW~^i^;0~{`p1*U@e)WUssqjtv?)7hDzh8eHl^&pHx+FlTPRG065LyEy;W^7V z1~5O2(|K{t5)7aGwv+J{i}(P^GJhR#`IPgEx~wWi(bS-9MFxZ=*$HJ#Fy_tU<(!X~ zWRdXLY-g%p4RtZ01K)hCk?zy=J*v)Sk7K^n1l_zgi-1(B3a8>@b8nl)CQ*pY3m}_C zG}dZgZ@%YaW^A1FuUI2e9ncP_aasfh8)!Qb6F`m_aCwGzB>LSP&)3@|JMDiOcS?C2 z8mR#s+3#!&;u`}5@{-exXsPV=n4pA~_khXA0~UEyP|u$~|9Uidg%myW{J8*SrTZqr z-o5pkxpo&63IT7KLV0`$dpU?Pd6uD?&@X0wM_ZC(XL#VFU+*%NA&yME()lRU4>%}y z;*{Yuyf~a!r)QMF9yhw_pAJ?^>Ull&LeOl8~%kxH3d8^q5@sO_(ns= zVc3s-VcDsEblCZ*SXdNILW7A+nL8|0yu4`|H+$euv)L^1&NVreC^4TtK7dSFq+Tur z*OPO65rOLS=Q@2ZBHo7M?k{5y@iyeLO?yVCcd%SPZQcpc_+Y7VHC8ivMSCwZ*;0^L zE7@7#M!kgM7t1pLOH&=NN|rVQ@%-0EhsTOrK^UHq}0 z{{XV~2{4UC9>@`5b6&OQibodkd)-FO#Vehjo~nFPp`leNiYlt?jGY+nU~}A?9LO<_ zjiHbZ!PWy_fyP=gDvvAP-@^)|NYj$n-*8yV0Ex>xItv@!mNIEHdL=j?sfUTdzo=H#oj&DE)kBTRLWR~K7;M_4oY?vswwqEfZl zfEZ&-0-m|!&n_iVBcF3*zZ8+SC}ik#B@)U(x{sHC#twyHu%C7pcFlT8Xx8O3AZT{; zkDjSbJ>25$37F6E$yLaGJ+T$Le*)v5#HpWDJGOuIy|b*8`Ra8XW<<rUN z)2BN4=>M1iXZy=2%kOD}lL{@@`+N&7+=m_)z;wd`%*2LEBYh&~$O5C~+NFv3E*H%} zkfQ^za8QqUd_h*##hxqWt}a~lXH>FyuHJsXd9uN$#=l&GX6JdyW>+smLY!RA*>w8= zptaMxY>z9)Erb1g^Kv@dVI7Sy_j>kQOa8qe$EWs}|Dlc-9R9zeK9-)+|4=MeEK^O) zUx~x(m-~%@fx-E|f85EJ6_L#IoT*YB+D3CFh7(|I8eyj!H@fz2cOl=P)#sS5qot4r`E@`rX*$K99?#k} zqvBTGu8OxEc-B_a6_n^iuY%%kCQ=CK)Ju}YQCV%^#0P}7f#Xujg7>-JSl-9TnfKB_ z3hLv^8sdDT3z+iQ?#}K%o2hyO0^q>PjAKyD1QC69jA0ZIxKo^@JjLSps5+rtFN_qPxj<^NMU0Ku6R`5EnRb#7#IbZ;$TTF(S^x4tfg zWM$vmn7%0Qf01Yk3kmt9fvTx?H4WiF-et3Qe?%n#(xfH|A^jW5cB9JBP{|^-YnPOl z=5Y9Qn%%!wGQF`5wIHT_8dxP}I%MbTFCFw{VM)&;NFH>&U*a7pn75YwJvH`{DQ$Dh zMnoO_nsOY&V&v7IpQ?2p*Fv)J?B?n-@#J7*$3|Y?-=7w!muiPwN`Kyc^^A$BpULw6 z?$+$xB$ej7;CQ-So&aeeAoGcc?Podznnwzky?lwg5%F|Z7oZ}_W|xv}bbk&^ z`cT@2!U4w86#El+oXoCPB3SXpWT|m+aAdPPS9hl3HP$TRQ=7_N#3eDr80ga~@G~F? zDBOyu*yS%?p-+~KB>g$fH3s?3GNv2Njtox6xaFwgjAQS4MjT=*?3ja{boS6)qB&Uq zY`?-}D19E($ISH$tnWg2y4a>*}KIS-~Kin;8v?L$6g z0iuOmQ;;!uB!oCfs50X4h>WZuYcJpGuhF@E-EYrR!UrsaLFDPba%JP)t^0!kk@TuG7n3;XQ6Wjla-iEK6# zS@ljTs6~g38ZFzw(FuDDaPW_@7;U(TK~9~a*@;8l)i3(2DX4O4Mcmo>n-RyZ`P9N+ zGIH{A%?~uexTTIx=QU+WJ^tN5m`w!;+z3+09Q(4Bm{t2~$`&7VP63GICj^U|Xk3YUL?=hq)ZT6HD8ed6YJu{AWrIk&+I{B2E~zI}l9Ec~lFf z)qZrj5|0dUvL3D>#MLgyam><$B7pUQgIF=gyBpuM!E={zVq0+Fhy7c*#}JU9z1aO- z-4Tva(&=!zt(LtjjY12?Mk#3eXl)9`q&zwcM1$d7H)8qR?6UDB@Pr!O4&);*V5D@u8GMSXz{)#DU#?h4bMekY8ZcKg0&bU?UDG@8WvY?pF0%74 zVkWh^Z`^i^<2nknUZW~~59{ij?Q&42>(*%SE0caPKwioLzGI=i03IOB1yHZzQB3Yl z3?J{mp{xvBNR#Vco)E!3Jqi&PmL!cd-g!YA0t!LiblN-TKPU8Et}~`Ij1oJ{aV&Z% zFvX{ly}ZKR=9n)U8C3sE$#D&L$oX5$Y*&7Me)qIoCAcMQN&yafXaEVjL>(@u#HBNp zd_l9;yWC1K>;x9730c$gzCt~0)^T!s{5>(A{5PKv3UaXPo+BNic&Q*@ctRt>!(XXz zD;;aKnD!BLZ3A5jy9xAQBTCAuH=l@C%;@LUy)s45t|QmOtn@D}iKj=yX$aZ!JmjL- zOuB`efpP5s-4+m5dGl)$L*HW^jCGc8qQw9euJdjqY_FLlU= z(rD@x5%OQt0~}gCuAMWyNpb3(&Lc&(0h^roOrWL%*Cz0TmxLbgH=41tOFg%>?$4bG z$fr+Q?yjsyzEht2$M<$Xr-V&=6A1B@s(%?mgrhd_9t6MraQq_(S#cO~-*6kg)YMln znf<2PVCACy-7%PdwqocW^LLH4Irroh#)%y4*?kqlTH1L0Z)o(jaOq0utm9dC&IJFH z^{MS-e_gOMQ>fa)sUtOSqJuViJU!ci(lCBCDDvcAF3f)h$yHqwa{9N89fOTGCJ*}KsIe3`YUI>gG> zkRhVv6J>SfN0l1-HRKzkJbqbW-*Y}=Ler=#41b8uGV>F%nf8k5EgocTPWCnsD1yQ9 zws|NHDqaqL@#OII2(={uTsjY?0sfs0k~{8P!+pDky~0-U2!hV1t>pY(X|liMXYR}F z6>hu1oFFVn>Or0ccaec47We=*y6s3?BCKO`ERXrX!$hy0_jie^R{|fSXMyB0rHdLU zOgw@zVM~67pwK2vQvg-7wxoYa&_|uADQpn?Tv{lY!7fZhs<3|Q76J+(M)e_eL4MNd zQN$)l71vy|$C%M1@aoZ$Rfz?S5csBu%JwiuQ4^CK#-f+SuZ!pAyh~I49Yi{RetKF6 zUgvLbJ01sughO>lL(RRnJuJszMZ;l43+s@|AD31kytFzV_l}&HjBH=ra+n=yGuqFK zsEY7cuCcMP_>PTZK-2ktz6a`k259anS$$djUD6%y2ECl=dXSU2cnq6H4kHEl8ZscM zXw|eM>+Gby^n{c9dc|ftSCJkdDoXG46Ky=(Q{(;VgvKd?jAB(>yOuvDYYBsT3EkE^ zrZ?M&J|e>Q&~cWmQ5_*lR&eWoq2oUJU|8AXtl&X_$16tI!lFRu8hBGOcU_I4l24># zM7B8VsH8d6>;l~`SB=6`DUOHt$nG5Z8}%v>G};1o7lRRV`3b}fYg&#jS3|9-;C_UVr>Fc3SST0&}t-8;k>n=?U^_+#=1IMZmbDX#3cVa%*uO zlwMmKJ+Wc)c3%bDBDLA=|HxGibAkfhAJ3t1Rb%7n7X&zSqyjY(C7nSct?zmS1oF+^ zkB7m-^v<%inNz$# zI=Zs-F(qYsZU1QX*A`fNtMF@Gq9`z<&*Qj>g=CTDXM>Qtq#&`{F3OKJOy=J%9PfOKc0BKNRjP} z6CKreLrW*sNF|9{>+ZFA_NA$@2F>wcL7>C^r!8v4uQflS6B zA;bz_Vmx7tO#^7={WlB!l@f~%&4e*nB8rS&+?ru9MI5c>&V$)y_$f;WQ_qSGe|;e> zxA`E+6@ZL)i~LHVK;1`W4nVUADm7gN?nf(eu)Jq2R$=qEeQQlInZeLnVd9ecagV*h zhwwXpzsE>W1jN`SnGfz2LUeSJ@Hiq+tYJbp^)Uv`48N&h&KltABdJZ#95uFj=X4yO zzWG_uWD?>pbgwI#T4$3f8jPY8`}5N`3LKM1aQVnVJtYVq4n651$bxHPD2}Ei4KzP8p}ghD^L_sWjV^b^o$U-dhugKt;W=cOmXg- zm_{9$G%UWgL2s6I=G4rIQE1Dk!rsHuzBXh?LLBBviG$KrhX93!=3TIv_GeV7+v0cd1=J2A<{a_EHPBGcVWw|H0gng@xfJ&C z`p?$PXJT*OC@YW3p&%mzF$VU1>^b#sq^Pt;elibVZb;Q% zR|Yg7y4x4M$znp|phgl`_$r7`=mbSdz=ZL`fedS*t4ILr2s~{ak?ci-BPo`v@OM*lC>X`;<6pq<=@SS*Q-{;@fjH=yY}VA zgTR}qt;)32(#<7PraJyDGx*igpJ^@k!zH-+2B%*oia;DQ?3SDl2M7thZl-LeSlaBX zo=r59eZCz2KQo43$ozi;eKUr(8dePzAyBVS;Zv&#=s5+pYK;|slU~r3;`s3G2gAUoy4nyxeFf>C~s|MW^VV;ut1*_!%_ z0rY`vY4F#iIEvQGVtdmK7}!K&A?;ys2(GY=)&>m9$YI)?cnBBEAIR}mO5r+@FsqQY*ERurmQ?!f@&aJsEj*ZRqy?Jm0ss z^TcVIu=~c=Y%t=fv78CwwAEE;&iGUZIy0&6FDZ0TBp!AX4LC{UfRjeqR<&m?fOLJV5?^WwYslvL{S~wc1@ne7s040)>F7KvS<=|9k)3 zHJuqAm4})HAQxOVb8H=4EQ_yriCuhC5( zekD3|x>Oz@%J|l1glr|zMqA`0zoMRUMFN%~;IaeSeE3n>$D51QDEwj_&@2S!kN>n| zSd9aaWqi2^cE&(eE|!-j82;HXJ8HpN9-4D^{K0;RxG-6&BbSZ-3uIUvyM1o%?ExCJ zny5f@mLQ%CPzK(I_Rk8?zD)w zgWUo?=eSD;XW$T-p8i*u^mXH#593rB^>0f|xgmRZwjKlb~M7JRZ{I?0l9kOPG zYVSsBtTn`Cr~ujv^MIzG-0JNHeu6NXNSP*$jwQW*GxsMFo*3XC$cVw@^k%=KNUd1x z5(cL$jbzv_wg8Pony$uEvaO*xBO@a`1#$z4-QO2Y2QAd<7|T1lD1c8A=laQK#zh5W z9~MBHp|eaUr|r45$E(!=u0_1kY&xw+F~`TOjP{?%h4h;fGu)|YSLg14bXKW)h5LxT z`q*crhZGg)F-jWFr>JFZ9So#$hEK}fT^|U|f@prB4+c4(=uyEE_8QBvH%Cpuiya}} zY)LF{gX>s^n1dd4%H436q`n&X+=W@GvdCQG;)eagn?@-j!+UxAZlz~_3Vs_A&J(#m z-BKqx=-2#%SXo<~pLQ_r*!!Dh?qPD?Fu{j*lRlEkK#JNo)pltlY5#Z*_iC;~Y026r zIM~>4?Xf2=FIGK3L{%-(e97y!pQa@MMi2<+*Cmkw+?oe=xzq;&U#$@nFl$0EcACC4 zGzUQRT=%Q#-kuz(STxIB$cK0ZZs*^1e zfHul*mPA<^)%xn$GiwnW+pd_Hh`2>UA7(9us&Ouf$9Z)QD2bSe8PCpe0~IN z7WYF;thJke0~$;1*TTqADF^0m&H|0DRnw<{u7Rubvi~#nhc13?SDOXu+1;xK`uR{F60|BNdm0BXim~>q;c#!|# zJ}^$-R%`ovYlue1Vuajxc;WlCtn$3)#n7VfjE!(vhBaIDn^zS96{UtlIew+Q(f_E< zOFB>tvT6G~8>$0J*17i=hnf7+D%WQpV@(HR)Kp%*eu8y~4*`>RH8c3us~X6p$u$k^Q`vLTsfAzJ&Zi*X>O(JNb!l9N;&J)St$Y`ZMy{-zS}4 z%Q{Ph4BYrrsC+uN9W~+z_$He_BifHWOMW2~D>6)K3sGYJ%PEk(6ePslVdfdt%Dhz*A-t}96=Dq8=C7=|70>S=wyf3(rlR>@%%>lm6 z9s8|M@6k*Ch){tyWfj`;QoI?uSA$E)zstTF6g;&{`H`SZ)&bJTjl_*V)c@F8<*XVr;gcjSceD@ z&^gVWohWJjRcHG<;kj{U2y2?%2^>^cdI^>xC`Y7UAubFMId%6LVx7os#Gr$@lXQUnpG%B*(f}+Wx>{4wbo;tB#{1hpc#33bZ z3<#GkcpHO@qo%zhE8?wMw(a`s3dQm6+#_V`$G|DwQ}mc5}7y;ANOH3hT1%mk=) zV6@tZ7zf1ro5V*L052RgYC}HBIKr=5r`*>|IN9|f(onh3dRafs>gY3A@BEZ(BsVKB) zs^c-YC^85kaIb=6@1cEJgF%3MHp{e7qQ#c#(H!qzT0e`TT{CDTW0w=?M}~BAeIxTD z<=wZe0#G)B_Iwe29wiPqwOV1$ICpg zMA-2`LW@O0!bD&c+y$uZzZiv)u@0ReB_v9c4%bOZNlWi=l3$tE&X{?5H4pG>_ZhxM3;OMmF$C~TLl$HXazl_1-KJ4WO0k|kwPuW% z8r@T>ciwPonBt8L5fP?_R{dJbFxa?#gHFsz<=QYXln?qyqX1sF05CZ5XB;DN*%qH* zhi&O;2ktSrBiy)=fAZQ7_j7>-=lQrVXTJ)SSqBc@x)zC&6grMwU{Fp5sJ$bpr#sk9 zn6JU1FpTN1U1N~nmO&8#fpzB?)W7XVEEv6IeVztVc(a|Uj8Lms>{k~AaAe&J)#l~X zKy%IQT76^qY3uW7(1Ka^RYL&m0SdK3-5z=ZBDNXz&b6~|#Jlh4ZHMZr)vpm7goQDo z(>3ziwW-~)leI6=x?X=rFIo$_kGTKF#iCT z_wWhx6NsI&GFZsvJOslG8L||3AaxY}Z#Be(TO4r?x>}sgMhNc+N^ZA{$8+8phlhqbpmbZiY@?uz zCorGUJbR`HIGbOrI=%$&ZM6!h>(0?rZ$F0frNByOhzkg0W^}oRhd%*aur?pa@wxN0 z%;y!iX6p6RBtpOvfuER#w9}Ah2^^c`_v~015G0v^&~JIlLZ0y&qytPv?t(`Z00r7nwIUfaJ?lLr=6#31yq z1L&z-7=|C#zX`b;zhjT(nMvHj9w~kHc{6a2za|R0fnnPJK1Fz^mJP{N8K&;k)8Vd_c7?8d2 z^&(Y^^#`UJKiv8G^&#b8U}o15vuA(;a13l)7&gsrJ;~G?OB>ZU>!2YH6GgL`dDRP2 zP%Yfk1sgg|`^#=7GHh^BWi-)(45KypS)AZkf2CbwTwG4)R#Hqohnjx-Kk5!X(tflF zE%$ex+khjJ+>!$r+@p!FTmH(F+6#Rr8Mgg*-SJS_`wvAWKXhhoAF4VN|MQ9`5-18S;EZE#m$s^4O&Z2n~F2$w;n3<4&E z$Nf*Ru%Rb%6fa)*jc(l#WgW0R+7c1|3fc=F_^LEjzMAys0$_#*xAXsP4)BNiXkkDB zqc>>rI=O6P-CX(+h=+qCn%kF_;LOuArLjt270`EG-PbE(-t%E=Ez z@?k(K9=PgAkh?k~w;+f@`foK1WrSOnPy$PRTW-&Oueh#X$u)X0Bh$ik6X2nv`4KsI z<;^Sp3Byoudb48twTIoBRg231_XS{c97;jmGFypjdG zr@;445FV!T{0}(=uTGHgpN~q;Hzx=}Tg;6QgBq}nL4CN}V6VyjITX;)(%fS|0K&mY zZZX%I()9myvOoJR%x}?s`H4rbteA(kwRw|kE!`6e(+RQ=W?{>5*`!C)nkN>R=j{p7Lbrh%FoET`((MsHF zXcwv2HqZLA)a-d=>!uvOuxF3p{P$h#@)V%)92}fCULUvJX)I2>|Id&J$s|;Ir=?ek z4Xld-uHrRmnd`3Z(2KXg$kiOP>i_r&3?iBIJdtxd5IVW){4!V}qWf>%Sh1#?Jy4mi zcUUXehldpz6f^t?bKFPPs?A$Wm&K4xEPAm}(9kmS74B7}-;g{krQcT^!yRY~vAZZk zgdy_Dctc;ytoR8kv{{;h%7t6+yJIq^&9tyWI)5gpK~zLV12jxUSHaW1qFmYwroEyR zhoaKH=hcbE|NocAS>8{>{_@x_b5H@Z^ZX^G8E18Lz3)V|V$|;+Ud0Ex&?xiqdrzqZ z>a$%<;}ON3rBp1HfAogQ2xsBIZew3=(QU{s=ljg@8ime1GRX#ThaSc8phy-|T^dCihWKAKVwv zn|TO(u>U*Iz?mDQu%Y@C+0QpfSQ*Uf%@B?wKw4y%rjn~SfzB?F{6uqcKM*k3XSjL{ zv7@4>W1#AH_jq~Hci%rApz|fQoe%QR+4l3hLq5DOBljVJxm&0f7>WRI%*u{0M&g|r z6pLJ==`N_Z#G=nkKQCV*nO*6sSs#5<(u{-e}yz!~+M9 zuM_(n?P1!EbpKMzhgd8q+BWtFE)W7w|5`OCYWOvmuy7Mp{tG`nird*P&y|Me`~$>A z0mr(Ps{8$YaKhas;%%)V3#ovkCf+Q$?zV%3%7=W%!)N~zXg@!7WPbNUVO1a*I+!L< zEG_F>8JWz$P9vU&tkH!O3?5!TavHBkfZ5R11po5=OVG~F1;w4Vti;1@zksVjp&ewI z9H;hP)WDov)$;HE_nO20zqAT{C6=H2v~GWRET2J+@tT|585622A}pvOKlCY5S4m(S z7QM0XNliD6Rmc0hU*|-)xxcVJ3C(18x>BW{g2!jn$b3sJf!k%~@-AYwn1$|zpQuX6 zaQ3gBSzHX9f2wTUVie-xiPm}(_2bA$k|G+c206$=ck)2yBKP9LeFXmvp!B?U+@x_z z_&DOcu>ikZLwd0Hp2s{|r3vaoEHwE?>}Y$UxDcR7vwP5IAPyYz!qwczW;(2#svyGs zzjy=wz%SP}l4!2c%#Mf@7?nJlpN*PW?%CL6feF0{@w?G?IT1X6D!ECAq)Dj=s4gg| z=($VLSiPyVQSoVIQp9Xl+;aD(Ra&MyQ)QXwd@Zo9#q-OxFIsQN!wl1!X%kwUs%*Xci_%>($nqNT>Ek%S^p=8^L;eltN)u^UZD8{Y+4z# zC^rGaAw+O}AW?D=gIpZU{i44E)ORdJ1q!7na~tR&?F0%P?khG6yUCovURNiD1N`p? z+eoIv`!ZjD*d#(w3>#gi(>dFHzmH>r2-k$f58&aZV@h14&CkL=0uVyEB-%%X?lTxQ zSag`HTz1rpx3m@3@W;RyWfosRd$Q$HPbv1I_iNK$wG2Se%6qUWuMqX4^%%>Qv8MSI zkppx32naBIH}#wj+?)_?#$-FNySlk)7daPc`-;?`!Vv+|z>OmoOu!cfOpwhZ*ZG+x zurIbi6YiB26&Q_V_dK)7=JpkISU*hw1Gv46NgP9fg8sHI>1DRi>Z#`9ckfb6a=|$p ze3e}3cnjd}NoKrMj9xe~sorM{_7ret=Bi$ih$6|7Kd8-XK)ug794{Og#r|L3*dl2N zCiR_nCgoD~7}bj6z#|%{u%2Rpaf!EpDR2nOVebX!9-m_3J~__rmnRj5zYvkT7rSoX zIm{v|&^N9@;nJrsJyxPPt)q2Mr~3;=&`Mr_t}%ARXl2Y6rtzDRVeCbn{y-Q{+6l5n zrK|$pasx`2as2IDL-VcdKvyLb-k& zKFTTw{pDs zz$4CKPnRgZCIaNgH{~r?4DxG0`bFVL5)oTfo7>KQ%N!T9x{a9MO=d{#_WwwJEkKh@m)$*9 zi3xd68_lA}%b-;S6$g9+P*R>QkalB2;$O2}14(OqMfT8h@x$?CM-@Ps!ixe{<(Xb2g(}Rv{bNjc4>A8Of zmL4FFi4_L?`A#@8vbE2(fKAj;TAgk;WJ@|C*;Uf^_jJlYbH7l0qY#=F3#(rOq-c1JX~Dk zYE(Wq8)~uxSpHlg=U194Ny*Eb@@Mc1Ba6E>7#OTPwOu%|BbC}F_W>y|Ki~qkhneS$ zsWSo-&CI?+Tu2rv z!s+}TV!47kqGkFGkm6)p5zz+^MvyXN(J6o92Fkg=jSK_m|r%GKe2;Tis?1+ejDtOOzkzV(6wmJ{7wH2WJXwzjAZ zdtkkwji!+yfYq@c0TD(kD=WIHxJ5?@5OJWk14e*mc2RT;>OId{XlQ6gfPq4ohKRP4 z+c(e&Cgyj4;eE}lE;<_&fkr`r0Ipy^ zC7chfGWnR-9m>vbH^&Xoy!hq&%w|6C>$BYUVAQ^B=cYvvoUTB>5OzuvTt|a5pf`mF zcfK)DT#bN|5)KN5IPN@u^zbg0Q31je(1k1Gg$JX9!oqb>*sv|2icFQ>EMVPaM+pMf z!6%JpRWpLn-%wb%g3;7%%-MT>^gj2!zvTkmwA4s)A-kBX6$ik&$=W)SO@2k6M?Lxu zQa?#*{->BG&D(Mx$Uy_fPZ(g)zr|MyT3Y#M%Lne}#$VXM;l(NGi_nFgxp?`n{PH|i zrg^V&Wpwn`_ZW3Gv0W|>mpPg7^qbv(Xf%HG`4{byuq5#lVzNf_u#Ho>>{;&ajZqC^ zUcCVWTA}fBfGKXMtlatm0I%$>yP8G%-y5?W(a%5r`d(!cn)Tc5z|%Q1)TdC?(f3(f zg50Qh&e^ZB>>*gpdQQRo_?;7*pppPn+C`_O7%jNKW~mJD`gT4I*g!h8QaGE@0bst> zaE5<@m(eUQV{SjNXKn;zZYe>=@lc1-V(14WGbj~;cnmlMXSWUC%0_C2M+}*&6TpHZ z?6cGdHL5bt-^TA#7WTV$7dBJb08yPwt@p%DgIg2DiOZ=xyn0YQg+|*>Bw+5q3bk91#nENl zfBUyNu2q;)`s?!gUn)t(x$uFIu8=!N<_zGbX$d>F()3EUwtO111ONjrfC?n}u!wVr z;$KnmB8A84IX<1DA3(#5o_7F_4KTcVgMdw8DBq^^*RRao(&v2M%@R1selzL8Ve=0N z2(VbnC|@#|`iMpa+ovE1CJReCV6FFlo+t-%G=FD+bPJ$Fr@Wo#9EKtCR7&sTiuFUBm}jKLf3wtM!UL2GOVp2hwMuZNc;E1!=YJOSuj=%C0U8ryH$1IGZPBx`h#vkt=Ela>@JI7Z`;$?DE7NUu6a{<+nq`0(J%Z}W z3zr7tCZc8qqGUQ`1;#?LK?nz>A@{og80`rRJK`|wR4l4+mL~)DB{&nGnf4_NG-DZg zB|K`HQc~t_c-PT@`7Zs@Vv`%|-op1((5z4Z-P%kryNh#;QA=CFGY;xXfYGLIf_!|X zrdM4Hcv{m38s5DG?nqYS1tNe`aw;Q((Z6wVw7TN$1-y`96Dak({tYjT1zpVztD6Dv zoefqiVgpR8UZj~5%FZ}lmt3@=$nB#I>Km8Ee9{PYr#`fbJ7~F%1jA8G=^p^IEqlYQ z<>E=B!#eML)BW(%l}x$+osURxu3vG#Qlo-3lE2{rcu^4O{WCO9YPEPZTnv(cfy-Sp zKQ0WneSr*#sMuQ~+^dJqwxBqLb)E&v;pln6f^c3`cn+)pj-~xs=5{iP2`oYT^~2g_Q?rpuM?VL%exW4w45KAcV#)Laz{eP|Z3H45^5- zG&9@S55?FOeEMDkUQ?Kypv-creG4TSRy?A*d$OQ+BY$|BZ@Emzx&DKFjzX!U{-NBc3~MlDKK2^fDv3DRN){K;PfTiG{T)Nej!C}qDP>Z-Ii ztgUMEH+Tl(rWCAH1*sU(y74-e4hk(`N#+6zt~8(fTenK@Y!p0=XL^b7#6W-EDU$OIamf7eFEMUI{k!CRYs#QIkhWavRQhQ4N} zwl~RhiX?L|FlZ|cul-Y~i^A0aV924teKegP3Ayv|- zg7FlGMq2U%jLi55E)myHhV=R~C~p6$;x3s%_@(TNPTnoIBUDZRHl#9`lCc1ql$^Os zz7Sw#OjrjfL!r_P(g`?mvnoY%s?W!I?qH6Zz>NK4c-1M&*pjSgiQ-!Z#3><{ZB?T# zYzf+khFxHAo~4;jR@wm|A-|@aClF|LcAo!GCjAhb`C#bGMT)UFqF{OCW3nga2%kX3 z7!JR7mSDa^Yv6Shy!j@mRN%12jy^{wQC^8t-N7gqpTobLT7^s(Q|Brg(X-segXr=O z0R7^o&(+T=POHv9zJ3Lg)~-^}8(@F8BIco1bT$)?!dM}f{Hn|BdhQV;Q{^a@Jpu|I zR=mk{K#af-y{RMt+v1+=KSWu1q&22g%K2IB%Ws`;|JaVi3Erp|(}X$x3%ltQ6_y>m z#C-3c0SOmHn@D^PM)e|5Zz1w$nhkvxSpKWV)uwr>Z(ktT`5oVr)*4!ZkrJu5^sk|b z5X)ulUwBU*yomrd5J}D_O~{&-U$A)+m(=RFB+3$9W&Vn4*f|z`gyj(Qi1kXR`hD)axF!$LLj!0| z3Kmn{dUGZkK}?-sgKq$V77Yj>NC7Zpz>AakmF7$`INA)Yj>EECuo%186UV<l{!>oZGnxj;jT}UH%FhTCGbp5)`@cctsW-ZmI?tEQLAW3 znJ021iljw;#wuji0T32o=IpkMEnnV5kufK!VR9CSmqs$_HI6g+{QWbKWt{@Fi<5G- zA`F4@+EcC+Q!qW53*N}2uHsLIGS5LRmj#f19$f^+v5IyAM(+Z2FZs(kX=$6tNhi=7 zG1_?r$~bjfwb0+re~6t_%_NTbiX2v~P-)M)du`1^rDe9LMu*p}rQ)2gBQv;9Ru=di zFQPLjPJ+NJ+&4>_?)2J!R9)~;#<8X=Z5cmZ`#js5H@5lo8Q*rQR8KBnn+s?}-MuMP zX|1pzq?Y|wsZ0}K44rnq+G{%BdUuTb3xx{Mo?(gpFXY|dO{pzUUnRjfM;3h6wY%(& zC+DiqUM5mv@I~S-xrM6T-^ekiaNy1lOL34)w6e z-n1zlWS$UuGces%ap5tl_b-%mKftp^U|NIw=;w4p5k_N$A`0PTa1pq0;kI??v?wy8 zPQ^M>&0BC%vOE2et?A-WVgYhtJtS-red9)o?7p7$xY>5A^7J3J0kQrfaEkvhi9|6>6X+BcQ`;InhjC*`x=Bao`%L8Xw8a z%d3~HfjKOoTRzp!VHl6f%Z1J_ds_{5n%pCQX99OAmJp%SMgGd&v9y>+Kg-Mj8t_c}(hwCTp&cOq zg}@~HO^;3K65x@A)Pue;U8BfLwG%optC#tans2an3P6b7cyp~#9FHZn4D zXYWhUZ_EbA{0KNc;=)6vfeTaRc%`vKm$S_0L0#j7ZD+OSY+w*PQO0(Pc5O%J{rA)` zn_(Iq2h-*-f!P!7^kUPvNIg42iHzi5%IW7b)?ZM4JWPk{5c36ke$8cC!I1E0qBYkC6Eu${9E?_Gy#9 zf8dt!;okUw_fgLC*)z}E`p1YQ->F0KM3gESU75bFSDzn;ea{Tg#P(#>6x_`!m=?TM zW71R<7Rd?3+xp&d>T}&XWMba4_q?fRVSj#M{#w*7rpLtW_*tr~S-Qsp#eS4XjIi%7 z1)*D2R1T{oaYIKv@Q}J=b#Bh7-Y107W|R5wn`qxdFMg&ASUL_x?)fk{w?58Q@hdbD|MM-+cJ znb#U}@1UJsWUU@(5@oK+bB8_mx%I8JtbjL$Y}UR0nw~^KfrJDZ5fjFfPs?Tu!rjo6 zk`@uh3Wb9%#TcJ#jyR7iNvIX0Cxw9YN1XvBn1K8n0ECpcA3IcL*-lry4HExdn4d1K zsQOX|WP~8SmikjrF=eeyhDVzrsI020T8iO-{R&8Yt?y^gVx)cod;BwgPbMf&!k@f# z(v*F(+Gy~8EBBl8mU15mq6aMrNAlF5$dE@Qb;hD3kH$XC{Ny=o;~?>OTty){+iM3z zASx{0tMh|{h%rXR*anbUBdw9wkHEeAxw!xRT)*8LSu|*7N&Q#217sAhn+a@4PTD#> z1Hf0Ja=W121kRY9`@uwPX2XE!wPz4y@3}dgc>)B*a8$keL;lt;hdgapwtczb1R~%p zLn?{;lE3t=MfrCKBIWSsSD+9lerXg$gN=UVkQw*oi!ICGX=AKszf0a*lBEO(e2H)?i%~n%kYq>=BUxpFHOgR#ckIIL?WqN zw%?eZQ?teH`yOJ8V^zqueRgN{?ZXx2tmu6@_h$7;eM^W=?rTiGfKd-UCP$5Hu;+1GL-7kTgePbpFGP3Y2Txh6_OHCP=<{N!mZivq2(G$Fp0}VL^M% z!146Bi|48-NK_jOU--$(r=zcr{xt5Cu}Sp#N1jm}3*K~RTeJUmjNLA{RUcR@DJW4qc*##YqU3oKMyn#BzK$~Ghp3vwX*(x; zH6K#TyzM>OlRqgL1_!?%+p_xJURQzaz(v-O%VclXMG+p(_IC|x-RySq_0-ofq675D zWXMNxaYkV zVEG&PUWV1$|NcWK@eT7xk9N^2atTFpJS*4HGC#>wWT`A zx<|~J=pSp}qtubdCO3~q{+^IHJQp4_<4}qH-6K6~p`oF!S`~oU!f_}toSB8?L25tY~;(4=Moq)fVBe(fQjBkjzuR`o8i{_qut@u(}u*?|rug zZQLB~+d5lz)7h*2r1Q&fyvi)RqUu!P$bNxBmyf#M8r~(Fr@vEmWNH}m z@5>2BB@8Ly(%;p&eC-It(yV?M{snI$@dI9yN0o?GAAgZJ$qwICI)Mgcg5U=c zqen;7cr#*zk7(jF^gX&_DB#bF#+=cj!l6f8bExvjv?H$h@LaqU^O{75JnL~P zC9Y}_fiXyraj_aXiZ>n|`*~7hd89qj-SgXgoTz>ma`A!OXPSfO*MPtu1G8_n{*_Oi zQWxYFNUcmK60etW>ItJ9j)fx3mo57x@v|o80jtv)f!q0+vdyM`HG=Zvt~J;(kL!0+YT;a?7>8KwPuqfuJ|@IbuX-+fl*w zn^8O0R5Mfe`vt!31uY9-ibnNr{%9?pC1l9S3?kxBP{&r^9l3ua4UNFrIu8{F=G^3VaU=ni~UmcvZ z%~RJi*LJ-k-9H+M;wPH?*wfss>ni>i&HH>{%8DSb!7qW?$)dY3e`I*aZY^^C(5w}Z z0K-gMT2)xMUt;-R6ZakaCKN6TrgKH#|7_Fr@E|&!ZX(q4>GHMc?#WlA5*ibpmNF5= zBESm7!CgHsDXw={TW9&gH-hH6y3#qT#OTgO$ot!u+{cS*N^bb}x*ATF!SH2Bo{ZOX_`at#!`X@3;4RzQ2$M`7!61;~wLVE3T&IxE$jVc1M1P6B9e?Ot4;D zUYG*Lu<}lqI}V+`{>4Nj=9`Bv4}QdWo*lqE&56);m8PE(*4}jw(&eh_VgJ_bGkw)^ z7ChN4v;239hLfEh!ZPohL2+}1ugeX6)R_F)i1)mkk?1F%$h^nyMAp#{v~dLhz5b?K zeuss96P{uwjJCd6F3svtMnkIE@ptP`{f0&y`FhSP`HA0C_GijI-}Sm`!NNnozFRn0 zaOp_+{)6Vz59vI!SGZ*9Ggw0JwSiUm7AXj{8TNr*n3E>TjAAKFD!>Lf;f_+3ad|xN z#~k1#)k#MF_!Y2R|J>xw(*IDwq*~cI^&|}l^O|)$L6}h7k&x_LMw^tvu>;D#j7iyq z7?E8O5t!BR7he1MN%Y~J;sT#2t&UnR$3EZQ1Jd!uL_xk{G>)BZU*`q+fhnb8XFpR2 zcjK(a#mJQ|j6U88thfKS;Dm6z7Y9)Ns@oyMu%S4iZ2(1eDE}SMY*P%NeRUy$2`8PtMaA&J~6e4rosus4A*LAeNOK)^cf)>T+ z;(Ts%-n%czIx9j=jtXQyzO%J{_$KJp7zJCuZZcOkn$D8(HY|8LaWstq)pE4_``Rft z;Ttgf-H~q(Vko<6w}{9 z>U?WeQi?rKsoDB+yWfA+o11$a3RUqnN5iVcpRxT&a=6-Gbi)hp7IY0#ws&&~$sC1z zqBteWhiMvp@GJ9YXFvLWXnKHU5o<*3JjJM0o&3E@@jUsZeLXBYz@Vb>&I6)pXlMc+ zVc&89FY4h8)BG3xB5jHh;YmvgQ%ABORb>tK@Jo(Gd2{^A%O2aoQa(@7)Cq3yOPi_Q z0G<bx|4omMtwyw#Y~PZd?1EA27Z#$gTkv2v$zrM1Fu4>Ski|tu7}ui_pC15 zu;Nk%SWYNdEi}ehHJ_OO-YBsyem8q}!BQ;}xndVl>2un3S<#rBINvC?)b2^pb~~oQ z9z3Y29f0zdZy)5y)`iUa3s%F3+O*k?4E7!E1CPYy8_oB%L zN}Yymf9tkdtm9t=V1g#(F~s0}k2N^okHL>`T3EM=0vZ7%z3_akk2MbPE#{iyq_o?+ zyYHOQ!;oBObn}a$z7fHwPc|iwAjp&I=+^@dS{ch&Z_WP1O$a^&?pLuz^i_6f==)Zz z31hD)f6}Eu*l|ou45bg6?;GS9xk34!4=smu<~!ZTM?{aAIEW(f_2A?yAMn6<*BBg8YDf#Z$nODt2W@>x`)-iB!i`!b?_3}3Wrk+BX!>HtLV zhS}GftswS$vuykz1j)~-(_c|BP44HiQ4hpcL8NSWBS$cz<&7I*y20;Pg5BntYMKD} zE77ihfSH*YyT_f)cEQhm-z?9Y6eERrWO&5}m{|ek+3S81ZwFCsTlc7@GLy#rLxOd~ ziEN<{x^B1t`e1bO9f`*Tcz_zq+k7L%z6`T2<0t8R%W8aSr+5}>R%4^8uQnjMgO1f_ z5Gn9-}jCpouUe_0otSbL1c;sm21 zwTVQ=+s{~Cpxg%_d1SGRiNYQ`;rEVbb&%bn_FB)0-O{i{`n<0~xvU8-4OPqNl}f{M zP?M!`j_$KB#qPQqx%Q{R)fjrC&UCjd8HWcRexGlS0LR12R?qDn*T|_Yf312()iy&X zoV*7ia*J(YuG{7kzsoHHzT1y{p<%95Lf8H>p{&n5mJ$*y>&+rvBSpyGI}#ezq3@#t zrK3F)Oy;{sGe7q{+;ch|zB%F|p=T-_779IH^Upw*^Rcm8Fe{h@7-B{#3>U5!gVgYO z8dU=n6MiOQyc~}x&&FmhG#b!skM)&;dWiltw@Z$&s})jyMEWhLjJ?d5&DI^BmeWv+ zRf$Su9rp)_%5Ivu2T%!_@rG12HfcA>n;;v}g&I0oNFi<=UgtQceu>uCiW?TZF~yga zL=z4N8=H262JXx9T-nFQ`nyxnQhqMsA`i7~7L%J&pFMbzR`Dbqj(96$h5FC__*>Ia z0vKxt3okB>m5#+@2i~P$PKCgpboNO6rb3rL@?I6)$!_^eb^cX%%*!rQc-`^%le2WZbs5Ykqdt|4Xy?1HapRx??8E6YtnHt;bf=Depd zgd*f9f?r>Ai|Wg zUNPy(3N8S14a^9Uw;P_P+CK$*9f~Mi&c+lD=K4*v;P30hH^26?dmxD#L0^7^VS`Ju zahC&gId+#bU1~L`ye?hngSFM@6dp%&LG_QCfVx^D6n1o!FgmDoX}=TIym5&689Sha zawPMX3#pONdCB&9lB{78{TgW5Tzr{&j5dqu%nHdx^^=t24%&^zUN-~~N#)f*;oLOT z7!29!DSY+3ji20j4g}kagKCLoQkyEYBhlEMC^Gb-{0X{!g^lr0_cYj zVuH?yMIR80X>8))u-PBQ1etv`I^$&d=ZkSe!_gq*_ACIve2cgpXIHj;aQ#7(zYwY> z^(BN~=>2^V=ZiOEN61ofeu^w|K!SzZq}fn>5mW*v>hn2Y-%DH0YQ5PD?`f-y^9ujS z&C}gb@~+w3vGY_}kJV~i*1k(F;o+l{cA%2bfu#lR;@?-;z?y;Ph_B_U9fe z$+TqI{&SuMJa6eV;lt#{kOs5?`nzA|J+7!9)=2ZgHo4g(XyAHDA#55#A6gn4Ll&>| zKj>Ln4xaMk=N>BAt zc`tJsqzRtnK0va&-|Ln37#?RxI~uP;%?k<-@Mcn9<9-^#9&TdL7#xp<67T1Jdq`Tt ze)3>%Aoo7?WaD8>;8eNPhi79Mo7`t!D`<01Bqy$K6y;&t#U(Mup3Ul;*f{_8Npj55W8{ z{^ zYP7SQv!=$&;j8N4@I0{%iQ}{kmo2P{W4i4V>OR-qiqt`&Mp@LWL;umPB8hVnYKMQcN{^uOV< zGvI{zL+*3DZW}@VQT$=4c{~Jg?rXlV<{vpHO`=v`LeP6&hX6dZhOZr_=}HZMy6ZK9 z+ahX`0xwVZ>9z!r&c6Xy3|D78ki-E1~p5x0!| zew>z_3yeAEl`)Lfwo|AUihqBv$n)YCP7~7*;SsS}cA$qHtad2yas^qY(*Qaa%Yw?x zuwk<6jS-I{a3)i}KixU4MJD)lF8D1NBe^uOlkGPk)$n=K!%^)H2VxNClqQ;Z2}$_i z!;$cs8Pyp?+9zBu5)Dpk1zKWU-5+gW(&_;=m;}W~M7;HY=$}W$jIWIj-IqJA=~Q_1 z{w(g^1i$Pi`a(4R-xHAR*A0UQ#B8TMZqz3_u4_5{K($=uMK}jh)ZI?Q}ki>5OI)-VK-&3=D>tPEr1psHa8F4D`G`DUPp% zyhI%No{Sxsjz3`Bc6;z;i)e#%hS2hH`7zQ~-*oY4(PI483Jl9Dh0y+pMVjCzCh;d_K?If(3{D=LH7ykR8rAWdVpSxhua8A8}rVP$>ZU7@T$W3#`bfF0p3`7 z0UdN?9HZ?pDO%;^CUod;5sqrSL?FlNs}5rv;j}kFwr2}+y4j!ZmtajW25{7gX28tJ z3h$I1BM-C0*B@^%^ALL z=pu}f*8Cg-q5WA&JkET{bF%Gs<_w~gYFd{X4gJ4?T{!UASo9GkUmgA&CB$}EMux|L zq+8~xLrl$brOpHgZf}jM`0CI0Y1^hF zPo2*2PNC2wGmm>DZ4~II@mtJ>5Il(!W5wY<>(+7 zqP8a2<6!;@C!5YAn|`_&W(so&a^tBo$?~jUSo5khSc4ftUf;@3(ncUnQ{5H-SQyvK z_e=KU=Pl^(W=Kl%+POW3)1AlWg%&@Xs_59xzYQDlo4*AiA5+tyE;LmDDnoRmSzJlq zWuFpWDY+bt1(#xM6f`-3fXrwM6%o&sxm3Q`7Uah0rtF6w0f~qq_vGd_N^$DN(LRWi zi^U(FKO2=!>@cM5uf$L!k6ajZI*PVzu8E-LJ`m%UX;R|3lJZ)66}~@+62MVbR+hAG z<4-(%!E4c*S7Z>Ca(Zytkf5HPIJ7n-*Ho_a1aVF%2Q0Q%v_ERb*fvLnd*%Y5L1-pCO=uX9?;BqnGFW?t`bFf5XtOvn*(#TcR z0>Xl+?-T}emiSe6+HO=kf+fWFazE-QGxt3CJK~yHvl_yA_wbN36O0Dzo~q7MVRaFC z76E$M%q=_ik%LwJNZ*}TJynXHgMOcg62Q`m?LBluiPw&wonZYL9A>wZXD_y2*FD#% zS?^kxEtDq2zXfL4em-x&3o>jYkeS)%j^U`!7R`;612B<_MTY{zlu~jwPFgQP!n=h8 zJxB0QWCuHqa%Sg3C*LpUPjc~!5jj{!ZfaOR1w3GaqEW0IOW`Q*DjR?fNN&G5GjdHt zB+mg&$+w=cnmG1~(A3E3E~* zTi8$QUfQzvW0v!{y@B{KnEurX1U=bIFm>t4L=uxHsxv?K*Vv|-U9}-a8b$5(tzZhG zb=X6pNcq|UpEOXhOhI)CF_#m-5T67%-zR&~fB^ogJWmE3lmSvJPgO}p`+@|kPAV|P zUbcOJumtCF0#fi|cV?`6cqkxKwy2VNqo zDk||M-tb)On9RLJ4Dn72Qs|7OhyK7g@ zp@VNYSe}+3x+pV#zRmWF8KjJp+p?Qs(u6RPIiTwog8`S8ggOU*3Y9DFy6%NSMuTIX zM>y5wHPX6z%0>=_w|9avxQbbOdhL-k6`y(DE*DU#1(FM_c{x7t*#H(DJXGnr8{q^Z z0w*s|t1t7h_>S5xNiCKYK5M?#$h9fQAq{scK?l+R%-TUdQbbxA%9l89QQ2K7`K%2( zmg_g>d0Rw1&Tj_amcN>U$k5=+PLA&UphZ)w%ZU9eq{P?f?7~ouHN-mi;E1TnGkEx= zGiY)oPTuIso~rlcq)f(ju%qDu@eq;gV)hdTmD9y5()e($nvL80U++c8vfCTKFR>KG z-3dQ8)<_R$RJXHFYF4$Zy6BRp{#e)pbJ;qU_$uB29Hssc6#4a)dxb%5X3WDF<%$M6tAVxb#j>4TUc2&g9VL zA(T23ZeaSrd6Z2aa!QTwlpMXE$U5;ls+Z3=#WtS2tlW~>qj8f)lB4M13ji8El8}{k zc0{!m4-bZw(p1WnARs;ofmcKE&UQ%-Sf{nloILEZ?J6g`AwwQVb4s`cFh5(3D@VLb zN-F?X>e_}{X~2yF8I`hWh~QL+dNu)ei|>QrKCQJf&qb}(GLK=~BFz{`C2kT&OU;>c}$JGy<7DeR_PcERjAY|ByFZgxEMgEs?qg>cvM`bjs5 zYvc0rl4v)=dQaCiE!0ia@BB+(F16F$C8&Xt^VXJ-D9XI^73S|rod#r*fBGoZq_ZaRP|7; z)gxl})LYpdhe2aoZD80hpN{V`sq}0_qqUpu_H;XQOe=kL^^WSr;u&N7C~%mNp=*J2 zXBGL^d75*a^9%R`Z(nZ*{g*7)Umbk+fuiM^dXd&}5_8$;ou@>Wy2=A*%y=_BhJxc- z&db|+`3$Hf(?Ommcf?4)j7=22nk+Ti>TQn)>_vCHp*jt|b{EVdEE%8M89xcs^o~lE zjg<%?c3R!l(+}l&0ZuqgOSbjNbm?Dzh7o8`gyfx=FqT>K%9d0_{ZJmkZ`sogzu5pH zTGBuTum1*IPjpd-Um&Mr_wGBo0eo7V#{QYnTt%zau#PZkPt2E$@m;@3Hs4kSoySoP9aN72mI_pMLLEDCC^ z3YaGLkA=a~%4YXbvw4IsyXYF>IwAayI7j5G;mm`tS=iKh{54ar;IQmctSLGir9XK| zP!6G!L$odDlaC4UMW3|Hl6zx+4+}9({;NJ>^2dW=g8MafFw8>CK!1)4AIW)OcDh-@ z6%WbII6&~g63$NOj{%=4(x|hx0dI*Efyn&+cFEpk}XXU@{QFNe%DW~_eIE4cy` zX!$Tor&amqer`wT;LU7dKa+%Mx5Xx~iwem47-fNuvYUtgCixN}b*``|mtbCwyucDF z>Y&vwz}0Y_vPklhk8Vv!rf2^Y)+!-=!OqLs$vkcIj_uF?1T$cxh^XQm;mM5W_9^ZSRQUz%P&z4z+V%AMmIyvZA7Tz?l) zzoRG%ePNu(ff6S7S!G!0R3$evjmsU4w0`T+AQFQl$#0swk%qVmX{Gpe)KTOGUy(lN z4BCix3x0YP@(#!MLW&#ajlhS&z+{(i3+;C(J_uxr0ohm(TzniX9tDk#<7#miTdIFb zQhXi=)1>hCB7$(#V>Yphe_%(wgb+4?{wTH%4k^gxifd8RUqUXq90;2eZf?hOS*#b4 zJ-)2AFJJq7!|MkSy~$jjhkIL9Y4q!g>8KXn8>KsLZ zu_uF%7^6lqHmHQT=va3$^vwoSfblB9HT~g(h?D^uAQ*QISjQb_Rm#u>EXCv?7kR9g zC4#^=Y@Xb5S8zT0E)DCP1&JMJTrZKg2j@6aaX0-yA0;*|~e!IkwK$<2|0#l^?%uoW=lc%z(Qi?%XLidN+pV3(AA z7$L;S1?i)VEl9Mk4lBiu1cg99$^3ZoP04x*mNZbb$LBpvP}Ro;8(AN~#B$nDOcv?@xd9OPk;#rctTnsxext9vx*(v03Lu|o zV2^29dopl8X?HE0JiD%W9>mi?l7COTrL_5)*tHz_sgvji#z+kiBv6p+}Rp{JXcP)OJT;_@?;F*WQG zhd#IC;oL9a*>~&l!$M}~0=L8_JZ|5IQ2>TdN3UPBIV2%0L@Eu%-b9WMg;-mZ`?I`{ zZ8wA>QcfzlfHC+_i$ zBQQY%EevE1R-K*c{q-Gsf)kPK>siY6km!{JXT6=Zi+D#nOUFD&{^)O+!{5~OK0US8 zXhfLT{ASizEbjRoMpw?PLPmb*bf?SAxrBGj{VQ}nAp+daNoIpGTrBB33#XrmmLU?4 zkB<*DjKJh1^9LqocL97rjG0S+)@X7-9lf9VkQX0@RhB(ds?Y+cYX(SWYp71aw)9MJ zj}XQ9Yk5H%(7t_N!4{gWl)mA!Y1^Irf)g~7q~jDqMmWWwk;@qkO&ORA_Vu8-Uek~s zukPywL!*2KZV@`aVuC3ca|nGlxX+})x>U6Z>uIl|j+{+qQ z76-pa>BUbxYfutqD`CIfGGUe-m?r!}tOtpX<36IIRd7X~U_ zhPN5JI>hnQ>9I04^aFzcGxuh^&yL_W{Bt!tt6*B6*@|gL(;z_yQGu(0T>$X#z>{6J z&?Q*uFntbSDq~m8OQ`4hDog_0rVX0=4CfU<`w0X^ehf6Vg@0u%H%tLwnsQWo!WH{* zz5_KV4*U`nFGQKp4y7% zv`i}gujB7;T+{(TE|Z3(knu;uMYf}b@mQaNJ!zas4CH~1C(hP{VMT-)UPijG=*_7Q zIQ`f-=x@PM*eC@3sl-Ho0? zp{{a~Za}d`GcOgl`#eH?Lt;iT?nG&9WSMz*o#HzTIL>PA0t@N@pogXaYVDQr1lmxt z6_O_mz!-CI2w7~+u`=2Ynj#9xhR)YkZFj_(_M=*yZ@8NQx6VD%Keo z_7FiNyBZtql_JkF4_B6!(y!T!WFs2wxpX7Z9&?Hmh?_9MQHL~;lXR8Ac>EAsI9l2P z9AarozjademC{iMjX66&J#fJZ!9cVpC(t6MR*?Lo1)xj>-VhLXAU>SxL%5u{0IvImUu%ve3T+X}uaU-)|@Jvm!364-l-%leUVicbDZ5y#Q_b zDWKy}u1G3q%r(x_>x>*Plj*VnpS4I|U?eyQZio|2e>C$+@WeNbw_!-)<@am{oaFnQ z8U3*jm(qT8Z@FMLVA?6lsJb+knHmJRs*rw$F{$#SP(9cM8Y!uS^K5>FGezV!wI*Dfgvmm`R5PLI%CPcV_W)^qm5+E^W$@esNR))pSj>2x4A0y; zG&QY{7sIsHXr9qrA>0Fq*J3MjOEx7z{?Am{tduWa21Ej|cvnwmn$u-8Gpl>ra12;6|-ltQ_ogVs_| zawkQc%hETh{lBY~KfzR}0~a8|ZM!(G-GNqBQHBOOs*2(J^J&c>LNP-Q0|!-@$Y{5f z9By@sK8Hv)U?Y`yz`8dgTtLXvw}hLQc5Iwa1b_k}tA&Hh1=gVq<3cg1i+>1Ujyxg+ zd#q0PPqrMzQD7)_DE)%01!?>rYS59ZF#!VDzz30%o z0@agez{toHz)2)N9FYSCn+16_WjDNUA=QWoq-=$`L-R|>9MS*BVfPA${qLjizrHJ! zg6fv%M(>u;(q?(5@%n#!aYaeFxEho#a6qo4{GBgV9eQ$}fJXH5r2JbvD6C(d6wKix zL$CGMw#t%umeLYHMYJ^es^$1oNjX@xPly=TZt(dAplG1Qi;e_6P~l_D?y$ZRDlWfU z6Io@hp)N`IBhMv24Lz{JSNWxUk;|#7<~?cj4+{@3bAs+wQdFD({6FIr8(zi?BHsrA zQ;tm%Ev5zl1adSJcd$JumH$8Y-oJfQuMaUHKz&TogFlNGy_{Y)4-e}bXNoFRKnDx} z;l>vr9F6@#mZA(hTBxH#CjP*Up(gqPl`gA?*ZCw5m_yUNw8t22Nv=mrM zPHD6bt47ZXN9Z)C2Q|wATvSK{Hh}CeU@xqm{}uh~&oFkA3`ZP5N|k~%3K()XIq^X{ zO8`Rs$OFRy8^B+%L4F4RE45NPjiC*1$Ugh}RR*WFn@(Z2LKw zcOmHL@^#7@?F5WrhssbwxX*a4OwK4GS8+1IpJBSkfJ|qNM9aE?z(S;PMVq;(@t@88 zx-I{8lm7Fa;0zjC63^s3>R8Sf^r64lI;2Byq{zm;GNH$BKBqgj+_`C0sd<&0PEb(f;_st zn2&R^Il-tp!kom|h|_;!qo?@xEv2ZUDHsC_9^UP4(jv&^whs z@32x+W%zjTFl8nWTq#)ZB&h%6YW)@9z}m^JqQQx;Q_gK|{&sq{Isn*pT&5E0K#-eG z!0AS>3^A`g0f^;0r!JykOb+x_a89}y#!~<)`5m1?Y%bpTV~g!L20O+4zKCwe(@iem zAUyrrGYV&`5R`#LL}IiSEbLr+2DGt81|fdE(xrHI)9oQPGCm4!Zvm<~4Kje$z-OMz z5jM+PaXZ)$%3QICZ|=)*MlP)oz$o6 z>FVlAop<_El5-dI8?+^lIkjLEqP^GN&mzM)hagzF1`z0p(eD8)6^*b6O*OotBXD4o zunM$5k&-}v&i2uG8?9qLoDE~wgUmHk$XyuKdb40rerXC|ik<*?7nHMh&ItTGR0l=j zcXsygtRZs$l0N^Zz3oFPNXh7!z6yOq5jS=j9sg&~v%^7E*VOEPPqQknvk!G}1w>Tx zawB<$>Yr_LrY2wpEoJKyoqd+TZ@GZ6l}!Xo`&O*f9&k?QZf$KZny2q`(Ce#snq0C!Udj`J*i@I^z zvGgn;C9zeueeM?kXmIR+_ll&1#QCaGi)BKEQo0h%db~+ORHT1(V1k~_-4IULhRuh{>M{qy7N*9Or=9ttkMl zO?(g*Ro64bHc3J~xdqfEKLbWbN3n|G{hsb8O}kUAu9sM9y~Xq;jpivXT!6i{D8Nn6 z0eWwX*m_XN9}CfCGI$cao~~8Q7wgNEZoqAoudBllxho^dmoPvFg{caq|ND~okfJ$4 z1=ws5Zg401pL5GHzWa0Zn-aox*7X6RvOg8n)FhgknqKt{IOJA zjX6X5hzu1X5l3||KJ0{ro-E+a?+Lg@UM)MXPQLmg0MvFxZS8W&k)a`SG*ZAi`XhC^ z4Mc|1g4qlJB%t|4RbO3RSeQ-X{V3n`J%z;p2H?v2A^>{irw9jRjlsc4c*1YAU2?i< zrs|j&7?ifXflT}gsBb0!H5$|1v5ey6^mIbM#dLrQ{<*{JX)o2HJXDg1+!MqxdJ0^R z4qG^qNo)Xv44-2AX)>D)n*|8P|QbKn8J3^pj{_c(D%%c`$#cYr;G z6^a>}874um+JMC0zefY54XivEW%e(d-}?oyudjy!%m|ak0_q_4iyuCRQxa>$(_`7G zWGMFkz6z#a;XHI{|3li$^J7T_%~VHf4vEO3CEesy)V?x0~7@cA)A0J*>;CtBb?w0ShB17rwP{yWm^qxqVZ z85LSu;FUUtgMBjp-|nIb8+4bQz$CMi$G`)qhucGur-$q9eAMGu;#UIcZ*x z;72=z_1^c&u?l}m1RinHw{HUhF)V5O-Ld@E)(?Ifh%_mH%&9m7poYl*dMP?;%ND(U z3NuW=uPbzvC(S7T6@aAN5YW(pwPu&b3#~?UWoj*%$`xAu>pTmUwx8=Dr&HO1Vua(P zp~(LyT=7{U7VD4Y3^8Bq>1pWmRtv@z!&j1GVnFX1G|<}9QqN)&6U8JXLa|8R$ey3u zV!atBi<0fN53xlEpv}Ccrlc(VQW2skZ5$uN54B(rop_{R^cp#0h{CI>#M9`CX^Hq2EZQBeA)P>PfN@AUB52A z@H#dY!#p!XrtRhgJ;RS8xn$An1`bE7Kn;F9544`t{b%3W1Pk)8MF2D!6K7Zn^FNU# zOd5R@+Y}Y`=`XC@YcK&GHESR-vl^d-n4dlKptHeoWg2;!nRxQ&a=Dx3>>we)5A< zR*_eI_a3Ar)2S0r^%u&3&gUjb+zZrpDbxU8?TDCIGygi zP3qWo9por8Nb5KkVg@%G7^`Mw+t0PFSUirDY^6!(4p#af8~g9*X8-gWu$mc3Xdun5 zx1oO)qp$(46B>CdC;?!^@+2DEsH!zFm4Ds*`m{1(O+b{ejg4(irEVnp)|cK-%}jM* zbKXCEg9YDE?2+SG-t6My;$0GN_Jl+jPhd4wQPE7jrG@$8cVs|9CQ-cx%Yn7yFg7+$ zv0uNVy$#;1_urgfrbuue*Q2-WUfl%;8b`5+W5{kp*pJ96TS_TDl3UH!1o3W^pPuDn zmPW|hk4s@T>!u{8OQp>h*uzte@uUFF#djIl3dJ9kwu&waE5k%k<7Ms^nZXaJOT+X7 zN~1BdIv3%P>#(B5vQzZ~(l&Qy%^$BghCxavU++z|23P@id2@vOM__r4-Y@jn8ixUOsT5%msC!dS(s3(ePoaL$wyoaR(66(t0l6?6c88W zB)aYKKUc#h6l}?{iG}_7$KOx&5q%{(KsmPp_g_KHcSr}-&d!p$9K}$H@5&)2a~lJO z!qklC2oiA;nyc=Q4BOkFEW?GxKY_&-~)xu(-tOrfYTZXAwId2zpm{;+vh4i50 z#5gbM*GX;s^M`$TMAl~CbxfC7x5@yj5NVM-kIXz*Ymh}%L)EkMqS1bTC3x9zK6~Ky zQw71!vN)Jxm9=^o&z(=dVY4A(yZU5~`W!Bm7it4TX$bwkRy!_o=IdA4&sueF{90?K zrr?ulI+NIbmduyzCi_)|x|r${-De2ZI0ruXmW9QanvwDymjG#U9yt-Frk_k|57%Zy zkJtntYmhy8yZqw-KfkzH&Z$U7>KYV8WB4OQl{K27-bc6gXJh@rZ>#0<@+ztXuopQP zqgdtl4A|iCy}8CRX$qXer#v@3N|@yu7_08dG=A(=y6XT>-(pb)j=IXGUy|sbxBtr1 zEfj%TYM!yZuvHH(*eu>#TKeGTG*aSlN9MnGXAtG&SW1cWngo$1qN%oeytiDeOd-zv z?OpqxD|lgaS@PJ#>ieCfcU!c2DaSZ>F6GVgs2<_3CQN2V^g}|s4;vdwgvJ%;-G}Fc zq46PVru#*$?DB{MY(re!!M00T*!@I0=WwoKguey}f-O-ozkCfe89E>NQ0njSS!ryD zwsfr8Y7k*)@29WVeEX32(0)dW$h?G%IXdn;$$!pcX0%S9(b9SQpA7?;(}4Nw||NE>j^9s>xRbrIzT{3iMjFQ+ruSskWSTpnM_wdgV3n5ODGnXEM_6A=#}~q zsz~&BnR3&QBue9XTxRR=BmWI6OWz$1!^S0y)1GtPfqNjiv=X-WqMMRlHhI{!g zfg+97X4NI}8+2b$#7o>1S>EN*GsMuQQYyHrV09)=QTeN>VtP46a3ARZ&QH)x|} ziEKdcpspQSbR~QYG8nY?`V2{#%9f$!q25UJpU=IJ z0cyqdROEMx(op;J39J$&Oa@5c+Ax*RnV3PVKHYdUr2t1O3*~rmN?V$Lf2s(x&U!W| zsouO33DOk#C$oi@vlU~RdQIYr%dV%}QI~ts3qS}y*dKEJGXO&aTtFm!d`Iq%$K%xU z^?A!yD`R*nc~d?SU=gdck1K~SwyqC}qsyw+RoCXl3qJc?z~Mww3o3w<<%j&_;dn1v z;a@2~axOMtwuTcjZP0bUFkimhP!WG2n?M3uX4gh-3NlVs&mb(xCR8yv^eUx9reE?q1$)a&fJySS4P z3(QlvDv{;6&2--=g2ew>(AT8B0gl}`5cB#+kB=Tq7t80Byldzd8eX!%R4$2vog%y$ z2t7Oey0w&32JSDJBYGIt?jlE0dL32zeu5&R*|A2>NpDW_(&HVJeLlTC!8a`|P(K}~ z<9n-=A+ivuFG*jvMYo;vSic|NR201tPDc~^rY6CNQr_07xQbmWr+UPYcAL9bMK~c3 zT-~)@xaR?l9JxDeNRoD=>pjw$UNi{#%2suRQua&F&kO`tG&H?;IGIYv z>%LU^R(%0V`3^93Y});#i3&oOR&)vMj3%RzWi$z#SSQ~FcTvof9{=ulR|*NX@cY`q z47`zX6%@cX#7x^D68Cp0!WJMT!J;Ia`CMC#Alz!=e6k?QtEz`YYmmliofGKI^xZ*O zSmK&C-E@{ms2bUQwiEox>^@8>jGoQ?#N%M8C|A9oLt^LFBOyui8$6YbeTf9B`aKkx z=0s;D%D3PuCg0VZxuiTep2hl8IJNN|X(aQEIFT}qZ8dx)Uu!c( z69a}Bx>V89oq5|_Vpj#npl|5{FR3!4FXXJf>=Ci+O^y^y1RHH^cdlXrFnV<8!VIK% zY`^bw8Q+G+-sfWRtE`LTC}fOQR~Nb}e>R`5#WU)Admj5~zco*uJm2k(`&qYCcV8-b zvcH7aH3BW8w3sbIejvcVnQOjh&*Pg-|BZO;ulW zMEaruDQ>k!e|YM*ij|(!HL@Sq^K6%(?GpdgOT9P{Zlngim1%pSCjDn?IvSEk(=orS z7bTUT-);@==10S-bN3?Y^7bH|Aro*F{a}p9bzvWDwS*Vnw_usPxR+8ivqxx{7_3t* z$NGay7Zec$>xIVsou=!4J0}&0{Diu+r(4BFV+t^s*Ii$zuCTkmtt-HO99HRT|Iy@5 zL&xTyYQkl}QFDL9SV6G8Rhx%Orw@d>)c4-Aqr(q!>R)#~(}B#J8^0Nrh*mUj#U^Cmol4}i9ZQ@%U>y2c5! z_Y(<{SQm5?tH3<>bso1d^GrT`9&bMx&o9bCn`H&9~vm63!&(5fXfrqfhDz+7Hr0$I&_E@-kx$IfRE5HW<4< zXhTg*{~}JlHfJ1teQuTV;2LQJIO^cHi2-+D%Ptks&pHWbW0JI`q=gP|*=$+ibDSk; zi`T3ZB)1t+SLtoi3Kk4nPZso}zf_m0*bk{y$04ZOFIq{Va~NPt{!`pJ!wnqFOAhVjb?9s0i#URf&jg{7It! z?g^Pv!rj5VBg(WE0Tjml=O0k#R7PoVJNY|J`i{SMYc<8kj%PGL8-;z@K*LBtnwm;@ z7pnNf|BP7KW+JQ1zAp zZMDtTa0?Xo0L2}G7bm!DA-EPOP~3`J(GuJVUZ50rhvM$;Zb4hzo%YRr&U3!+etzZe z&g^T?T(f4)S_aupNwnX+F9;1<>;-Q&+w}3O)i4bka8pppoThmy;~RR1g-jO_>RNK- zlDg>&|GCN6S?X`ZFU~jONu_io%K~Gv33Y~C+KTVS^j)o|f)M?JWleR7vh0x_-+Ucr@F&eg?v&}1vf@LI-y9PX&}eq@pJ z@ojO+s$p6bz7Lfo@36t0bS18?id>zgj8D73Dv-M32YeI6c6==fim>A5QAP!#BR>F4 zk+wYO($J+KQP5+gAOm>`0C=JNL)FOX%rdpXoR6$oc~_;8P#I~3acj!s?S^~>iY5WZ z&qow0w^uPuFO~($UvGuA;y(JlIOwQQblLKV{(6ZM`++eb8Z}I(uk>55wMz1DiX@9^ zVua_6c9VOt>L4t=9INu-aK!h7_bs85aLugj0#}w6Dv-<1JKyF1V7q@%BpIsLKe1qs zBe4Q>5wFRKcUetc58j#I;Gec>eHfr>8fX5T*kom@i%D4WrIFQ(s*!ce7})Rh^OKN> z31J;JKuQ1R2c~JkJLN40%W<`*u4@f-N4rKbhqLim1iypW_+r*Vs01~++pjYP>7 z_`4gOuWq3XclSe>EiMiL9IFb7rjM`jdi6RO)v^4mB4YM6fzp^lK@yt|+iQKup7l2# zb^3F%l~W zM=msD#2DJN%0^JR=K^ znA}VJkR+BIW?QtE-jLmuIa86|F>e0c_@eV7cQ23=z;6g7F}im9Q68bw=`^IJ z*ouClq0k!@mS2cEa`EpkWzH1|^NvtC(j88b`k z-CSV>yl>+{ftJuR)H>?3K-1EC-lyj_Lc*d+6)Zc@EXm9+sT=15O9Zr@nsK^QV|Tcs zrixm6vLcWLyB3YA!a0cVRV@iqEceiOK^3Cx>F0nO%f#OFzd{UckwPL$o36yyDCuTf z{)lrqsj zj-UQ2X}FYoysi%I4%kCjrZWrow6`9d$YLq^v&B#KcKn8GQmj(!uqyj614DJwsBc z=8PtD10idMxkt`|2rKGt61;&eR;k-hGESDbkhRd#*OT&Yekf;zzHjwBU=h%CYHx68 zg?+_1!ceg=xk-O({3959>0`gg(TZ${`;9zn<}dxBG+8z{hc6!0v}w`gP=DAo5*JKB zS#ysf1LgI|f?F_i=($KX(^ix``3i4)lz16VpKQFj^H26U_=*{semNjPx}E$*gwbc< z3Ss*L8`%bi+8j4QQya}^AWMIR=qmNN$Kg^?i~ET(G~ipIrObx^C{~THEmn&o_4Iaz z2{P;2tA&vDzf(Kci^t7uMH^p5GIsN&>#@9z@Upb5nQCpg(JaLm>s2)09*{exfB5N8 z=GudXSXmm^$bp%!LuDh^KSVXoix5GwAcYFVYh{)bxkjzft4XX)Yg*0=_$*sZ={6-{ zn>=hA5K%HmKQPzlak8PZ>Upd+icNURSwZI?Mw*6+Ld}H2YvnG@C7JVsR!!TY&8pFU zec)tN=5VURNLo}|_n_bj1*<2*+1jB#L_;%Lk&QYt@E7)#-z?d@^;`hg@+;)KAvGip z(-}o0T*02^w}-jeHBNPKjwVs%*Ez|5wa!68a<3fSueSn
    t(0QbTnHu`dIExIR* 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

    |2Si+G6K%T2qOP_> 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%0D1=h&?se+#G6#tFg$_}f4od1FF1sB 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 ZBb?{b` against all marked entities to determine which entities committed by same entity provider were not marked during the last ingestion cycle. All unmarked entities are deleted at the end of the cycle. The Incremental Entity Provider rests for a fixed interval before restarting the ingestion process. -![Diagram of execution of an Incremental Entity Provider](https://user-images.githubusercontent.com/74687/185822734-ee6279c7-64fa-46b9-9aa8-d4092ab73858.png) +![Diagram of execution of an Incremental Entity Provider](../../assets/software-catalog/software-catalog-incremental-provider-diagram.png) This approach has the following benefits, From 15730145546d6115b6d29c48b2bb99831a4f4bf2 Mon Sep 17 00:00:00 2001 From: Niklas Aronsson Date: Thu, 8 Feb 2024 12:16:48 +0100 Subject: [PATCH 028/317] feat(gerrit-integration): Update the gerrit url resolver Currently the only Gitiles url that can be resolved is urls that point to the tip of a specific branch. This change adds support for resolving urls that points to the following refs: * HEAD * A commit sha * Tag The old Gitiles url parser may be deprecated. Signed-off-by: Niklas Aronsson --- .changeset/red-squids-own.md | 11 ++ packages/integration/api-report.md | 12 ++ .../src/gerrit/GerritIntegration.test.ts | 40 ++++-- .../src/gerrit/GerritIntegration.ts | 6 +- packages/integration/src/gerrit/core.test.ts | 126 +++++++++++++++++- packages/integration/src/gerrit/core.ts | 108 ++++++++++++++- packages/integration/src/gerrit/index.ts | 1 + 7 files changed, 287 insertions(+), 17 deletions(-) create mode 100644 .changeset/red-squids-own.md diff --git a/.changeset/red-squids-own.md b/.changeset/red-squids-own.md new file mode 100644 index 0000000000..4f6a1635d6 --- /dev/null +++ b/.changeset/red-squids-own.md @@ -0,0 +1,11 @@ +--- +'@backstage/integration': minor +--- + +The Gerrit integration can now resolve Gitiles urls that point to the following +refs: + +- HEAD +- A SHA +- Tag +- Branch diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index cf4b380ad9..e9b67bb6c1 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -766,6 +766,18 @@ export function parseGiteaUrl( path: string; }; +// @public +export function parseGitilesUrlRef( + config: GerritIntegrationConfig, + url: string, +): { + project: string; + path: string; + ref: string; + refType: 'sha' | 'branch' | 'tag' | 'head'; + basePath: string; +}; + // @public export function parseHarnessUrl( config: HarnessIntegrationConfig, diff --git a/packages/integration/src/gerrit/GerritIntegration.test.ts b/packages/integration/src/gerrit/GerritIntegration.test.ts index 1878744f68..2cfc748550 100644 --- a/packages/integration/src/gerrit/GerritIntegration.test.ts +++ b/packages/integration/src/gerrit/GerritIntegration.test.ts @@ -83,11 +83,10 @@ describe('GerritIntegration', () => { }); describe('resolves with a relative url', () => { - it('works for valid urls', () => { - const integration = new GerritIntegration({ - host: 'gerrit-review.example.com', - } as any); - + const integration = new GerritIntegration({ + host: 'gerrit-review.example.com', + } as any); + it('works for valid urls pointing to a branch', () => { expect( integration.resolveUrl({ url: './skeleton', @@ -97,15 +96,24 @@ describe('GerritIntegration', () => { 'https://gerrit-review.example.com/gerrit/plugins/repo/+/refs/heads/master/skeleton', ); }); + it('works for urls pointing to a tag', () => { + expect( + integration.resolveUrl({ + url: './skeleton.yaml', + base: 'https://gerrit-review.example.com/gerrit/plugins/repo/+/refs/tags/v.1.2.3/src/template.yaml', + }), + ).toBe( + 'https://gerrit-review.example.com/gerrit/plugins/repo/+/refs/tags/v.1.2.3/src/skeleton.yaml', + ); + }); }); describe('resolves with an absolute url', () => { - it('works for valid urls', () => { - const integration = new GerritIntegration({ - host: 'gerrit-review.example.com', - gitilesBaseUrl: 'https://gerrit-review.example.com/gitiles', - } as any); - + const integration = new GerritIntegration({ + host: 'gerrit-review.example.com', + gitilesBaseUrl: 'https://gerrit-review.example.com/gitiles', + } as any); + it('works for valid urls pointing to a branch', () => { expect( integration.resolveUrl({ url: '/catalog-info.yaml', @@ -115,6 +123,16 @@ describe('GerritIntegration', () => { 'https://gerrit-review.example.com/gitiles/repo/+/refs/heads/master/catalog-info.yaml', ); }); + it('works for urls pointing to a tag', () => { + expect( + integration.resolveUrl({ + url: '/skeleton.yaml', + base: 'https://gerrit-review.example.com/gerrit/plugins/repo/+/refs/tags/v.1.2.3/src/template.yaml', + }), + ).toBe( + 'https://gerrit-review.example.com/gerrit/plugins/repo/+/refs/tags/v.1.2.3/skeleton.yaml', + ); + }); }); it('resolve edit URL', () => { diff --git a/packages/integration/src/gerrit/GerritIntegration.ts b/packages/integration/src/gerrit/GerritIntegration.ts index 6ea426e407..7e2e757c6b 100644 --- a/packages/integration/src/gerrit/GerritIntegration.ts +++ b/packages/integration/src/gerrit/GerritIntegration.ts @@ -20,7 +20,7 @@ import { GerritIntegrationConfig, readGerritIntegrationConfigs, } from './config'; -import { parseGerritGitilesUrl, buildGerritGitilesUrl } from './core'; +import { parseGitilesUrlRef } from './core'; /** * A Gerrit based integration. @@ -60,8 +60,8 @@ export class GerritIntegration implements ScmIntegration { const { url, base, lineNumber } = options; let updated; if (url.startsWith('/')) { - const { branch, project } = parseGerritGitilesUrl(this.config, base); - return buildGerritGitilesUrl(this.config, project, branch, url); + const { basePath } = parseGitilesUrlRef(this.config, base); + return basePath + url; } if (url) { updated = new URL(url, base); diff --git a/packages/integration/src/gerrit/core.test.ts b/packages/integration/src/gerrit/core.test.ts index 35632c654e..c031612c40 100644 --- a/packages/integration/src/gerrit/core.test.ts +++ b/packages/integration/src/gerrit/core.test.ts @@ -26,6 +26,7 @@ import { getGerritCloneRepoUrl, getGerritRequestOptions, parseGerritJsonResponse, + parseGitilesUrlRef, parseGerritGitilesUrl, getGerritFileContentsApiUrl, } from './core'; @@ -148,8 +149,129 @@ describe('gerrit core', () => { ).toBeUndefined(); }); }); - - describe('parseGitilesUrl', () => { + describe('parseGitilesUrlRef', () => { + const config: GerritIntegrationConfig = { + host: 'gerrit.com', + gitilesBaseUrl: 'https://gerrit.googlesource.com', + }; + it('can parse a gitiles urls that points to specific sha.', () => { + const gitUrl = parseGitilesUrlRef( + config, + 'https://gerrit.googlesource.com/modules/cached-refdb/+/157f862803d45b9d269f0e390f88aece1ded51e8/Jenkinsfile', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.googlesource.com/modules/cached-refdb/+/157f862803d45b9d269f0e390f88aece1ded51e8', + path: 'Jenkinsfile', + project: 'modules/cached-refdb', + ref: '157f862803d45b9d269f0e390f88aece1ded51e8', + refType: 'sha', + }); + }); + it('can parse gitiles urls that points to tags.', () => { + const gitUrl = parseGitilesUrlRef( + config, + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/tags/v3.5.6/src/main/java/com/gerritforge/gerrit/eventbroker/BrokerApi.java', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/tags/v3.5.6', + path: 'src/main/java/com/gerritforge/gerrit/eventbroker/BrokerApi.java', + project: 'modules/events-broker', + ref: 'v3.5.6', + refType: 'tag', + }); + }); + it('can parse gitiles urls that points to HEAD.', () => { + const gitUrl = parseGitilesUrlRef( + config, + 'https://gerrit.googlesource.com/modules/events-broker/+/HEAD/src/main/java/com/gerritforge/gerrit/eventbroker/BrokerApi.java', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.googlesource.com/modules/events-broker/+/HEAD', + path: 'src/main/java/com/gerritforge/gerrit/eventbroker/BrokerApi.java', + project: 'modules/events-broker', + ref: 'HEAD', + refType: 'head', + }); + }); + it('can parse gitiles urls that points to HEAD without path.', () => { + const gitUrl = parseGitilesUrlRef( + config, + 'https://gerrit.googlesource.com/modules/events-broker/+/HEAD', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.googlesource.com/modules/events-broker/+/HEAD', + path: '/', + project: 'modules/events-broker', + ref: 'HEAD', + refType: 'head', + }); + }); + it('can parse gitiles urls that points to branches.', () => { + const gitUrl = parseGitilesUrlRef( + config, + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/heads/master/src/main/java/com/gerritforge/gerrit/eventbroker/BrokerApiModule.java', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/heads/master', + path: 'src/main/java/com/gerritforge/gerrit/eventbroker/BrokerApiModule.java', + project: 'modules/events-broker', + ref: 'master', + refType: 'branch', + }); + }); + it('can parse gitiles urls that points directly to a branch without a path.', () => { + const gitUrl = parseGitilesUrlRef( + config, + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/heads/master', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/heads/master', + path: '/', + project: 'modules/events-broker', + ref: 'master', + refType: 'branch', + }); + }); + it('can parse gitiles urls that points to the repo root.', () => { + const gitUrl = parseGitilesUrlRef( + config, + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/heads/master/', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.googlesource.com/modules/events-broker/+/refs/heads/master', + path: '/', + project: 'modules/events-broker', + ref: 'master', + refType: 'branch', + }); + }); + it('can parse a valid authenticated gitiles url.', () => { + const gitilesConfig: GerritIntegrationConfig = { + host: 'gerrit.com', + gitilesBaseUrl: 'https://gerrit.com/gitiles', + }; + const gitUrl = parseGitilesUrlRef( + gitilesConfig, + 'https://gerrit.com/a/gitiles/web/project/+/refs/heads/master/README.md', + ); + expect(gitUrl).toEqual({ + basePath: + 'https://gerrit.com/a/gitiles/web/project/+/refs/heads/master', + path: 'README.md', + project: 'web/project', + ref: 'master', + refType: 'branch', + }); + }); + }); + describe('parseGerritGitilesUrl', () => { it('can parse a valid gitiles urls.', () => { const config: GerritIntegrationConfig = { host: 'gerrit.com', diff --git a/packages/integration/src/gerrit/core.ts b/packages/integration/src/gerrit/core.ts index b3bdbc30e4..e50ce97bbb 100644 --- a/packages/integration/src/gerrit/core.ts +++ b/packages/integration/src/gerrit/core.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { trimStart } from 'lodash'; +import { join, takeWhile, trimEnd, trimStart } from 'lodash'; import { GerritIntegrationConfig } from './config'; const GERRIT_BODY_PREFIX = ")]}'"; @@ -81,6 +81,112 @@ export function parseGerritGitilesUrl( }; } +/** + * Parses Gitiles urls and returns the following: + * + * - The project + * - The type of ref. I.e: branch name, SHA, HEAD or tag. + * - The file path from the repo root. + * - The base path as the path that points to the repo root. + * + * Supported types of gitiles urls that point to: + * + * - Branches + * - Tags + * - A commit SHA + * - HEAD + * + * @param config - A Gerrit provider config. + * @param url - An url to a file or folder in Gitiles. + * @public + */ +export function parseGitilesUrlRef( + config: GerritIntegrationConfig, + url: string, +): { + project: string; + path: string; + ref: string; + refType: 'sha' | 'branch' | 'tag' | 'head'; + basePath: string; +} { + const baseUrlParse = new URL(config.gitilesBaseUrl!); + const urlParse = new URL(url); + // Remove the gerrit authentication prefix '/a/' from the url + // In case of the gitilesBaseUrl is https://review.gerrit.com/plugins/gitiles + // and the url provided is https://review.gerrit.com/a/plugins/gitiles/... + // remove the prefix only if the pathname start with '/a/' + const urlPath = trimStart( + urlParse.pathname + .substring(urlParse.pathname.startsWith('/a/') ? 2 : 0) + .replace(baseUrlParse.pathname, ''), + '/', + ); + + // Find the project by taking everything up to "/+/". + const parts = urlPath.split('/').filter(p => !!p); + const projectParts = takeWhile(parts, p => p !== '+'); + if (projectParts.length === 0) { + throw new Error(`Unable to parse gitiles url: ${url}`); + } + // Also remove the "+" after the project. + const rest = parts.slice(projectParts.length + 1); + const project = join(projectParts, '/'); + + // match /+/HEAD/ + if (rest.length > 0 && rest[0] === 'HEAD') { + const ref = rest.shift()!; + const path = join(rest, '/'); + return { + project, + ref, + refType: 'head' as const, + path: path || '/', + basePath: trimEnd(url.replace(path, ''), '/'), + }; + } + // match /+// + if (rest.length > 0 && rest[0].length === 40) { + const ref = rest.shift()!; + const path = join(rest, '/'); + return { + project, + ref, + refType: 'sha' as const, + path: path || '/', + basePath: trimEnd(url.replace(path, ''), '/'), + }; + } + const remainingPath = join(rest, '/'); + // Regexp for matching "refs/tags/" or "refs/heads//" + const refsRegexp = /^refs\/(?heads|tags)\/(?.*?)(\/|$)/; + const result = refsRegexp.exec(remainingPath); + if (result) { + const matchString = result[0]; + let refType; + const { refsReference, ref } = result.groups || {}; + const path = remainingPath.replace(matchString, ''); + switch (refsReference) { + case 'heads': + refType = 'branch' as const; + break; + case 'tags': + refType = 'tag' as const; + break; + default: + throw new Error(`Unable to parse gitiles url: ${url}`); + } + return { + project, + ref, + refType, + path: path || '/', + basePath: trimEnd(url.replace(path, ''), '/'), + }; + } + throw new Error(`Unable to parse gitiles : ${url}`); +} + /** * Build a Gerrit Gitiles url that targets a specific path. * diff --git a/packages/integration/src/gerrit/index.ts b/packages/integration/src/gerrit/index.ts index 6c6295f7a3..f2e5934c95 100644 --- a/packages/integration/src/gerrit/index.ts +++ b/packages/integration/src/gerrit/index.ts @@ -27,6 +27,7 @@ export { getGerritRequestOptions, parseGerritJsonResponse, parseGerritGitilesUrl, + parseGitilesUrlRef, } from './core'; export type { GerritIntegrationConfig } from './config'; From 653f60b8fee2c49fedef503920beff558dcb493f Mon Sep 17 00:00:00 2001 From: Marek Libra Date: Tue, 27 Aug 2024 13:07:11 +0200 Subject: [PATCH 029/317] fix: change severity filter label to "Min severity" Signed-off-by: Marek Libra --- .changeset/five-walls-carry.md | 5 +++++ .../NotificationsFilters/NotificationsFilters.tsx | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .changeset/five-walls-carry.md diff --git a/.changeset/five-walls-carry.md b/.changeset/five-walls-carry.md new file mode 100644 index 0000000000..34b5fb75cb --- /dev/null +++ b/.changeset/five-walls-carry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Severity filter label newly contains "Min severity" to better describe range instead of exact value. diff --git a/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx b/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx index 3175af6f6f..2676016695 100644 --- a/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx +++ b/plugins/notifications/src/components/NotificationsFilters/NotificationsFilters.tsx @@ -249,10 +249,12 @@ export const NotificationsFilters = ({ - Severity + + Min severity + + + + + + + `); + }); +}); diff --git a/packages/frontend-defaults/src/createPublicSignInApp.tsx b/packages/frontend-defaults/src/createPublicSignInApp.tsx new file mode 100644 index 0000000000..a8fd456c73 --- /dev/null +++ b/packages/frontend-defaults/src/createPublicSignInApp.tsx @@ -0,0 +1,108 @@ +/* + * 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 { + coreExtensionData, + createFrontendModule, + identityApiRef, + useApi, +} from '@backstage/frontend-plugin-api'; +import React from 'react'; +import { useAsync, useMountEffect } from '@react-hookz/web'; +import { CreateAppOptions, createApp } from './createApp'; +import appPlugin from '@backstage/plugin-app'; + +// This is a copy of the CookieAuthRedirect component from the auth-react +// plugin, to avoid a dependency on that package. Long-term we want this to be +// the only implementation and remove the one in auth-react once the old frontend system is gone. + +// TODO(Rugvip): Should this be part of the app plugin instead? since it owns the backend part of it. + +/** @internal */ +export function InternalCookieAuthRedirect() { + const identityApi = useApi(identityApiRef); + + const [state, actions] = useAsync(async () => { + const { token } = await identityApi.getCredentials(); + if (!token) { + throw new Error('Expected Backstage token in sign-in response'); + } + return token; + }); + + useMountEffect(actions.execute); + + if (state.status === 'error' && state.error) { + return <>An error occurred: {state.error.message}; + } + + if (state.status === 'success' && state.result) { + return ( +

    form?.submit()} + action={window.location.href} + method="POST" + style={{ visibility: 'hidden' }} + > + + + + + ); + } + + return null; +} + +/** + * Creates an app that is suitable for the public sign-in page, for use in the `index-public-experimental.tsx` file. + * + * @remarks + * + * This app has an override for the `app/layout` extension, which means that + * most extension typically installed in an app will be ignored. However, you + * can still for example install API and root element extensions. + * + * A typical setup of this app will only install a custom sign-in page. + * + * @example + * ```ts + * const app = createPublicSignInApp({ + * features: [signInPageModule], + * }); + * ``` + * + * @public + */ +export function createPublicSignInApp(options?: CreateAppOptions) { + return createApp({ + ...options, + features: [ + ...(options?.features ?? []), + // This is a rather than app plugin override in order for it to take precedence over any supplied app plugin override + createFrontendModule({ + pluginId: 'app', + extensions: [ + appPlugin.getExtension('app/layout').override({ + factory: () => [ + coreExtensionData.reactElement(), + ], + }), + ], + }), + ], + }); +} diff --git a/packages/frontend-defaults/src/index.ts b/packages/frontend-defaults/src/index.ts index 913cd3934b..7ee60a2116 100644 --- a/packages/frontend-defaults/src/index.ts +++ b/packages/frontend-defaults/src/index.ts @@ -20,4 +20,9 @@ * @packageDocumentation */ -export { createApp, type CreateAppFeatureLoader } from './createApp'; +export { + createApp, + type CreateAppOptions, + type CreateAppFeatureLoader, +} from './createApp'; +export { createPublicSignInApp } from './createPublicSignInApp'; diff --git a/yarn.lock b/yarn.lock index 22062e72bf..450f6e53de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4456,6 +4456,7 @@ __metadata: "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-app": "workspace:^" "@backstage/test-utils": "workspace:^" + "@react-hookz/web": ^24.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^15.0.0 "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -26627,6 +26628,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/frontend-app-api": "workspace:^" + "@backstage/frontend-defaults": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/integration-react": "workspace:^" "@backstage/plugin-api-docs": "workspace:^" From 7bcad83a91895aa7a1994157fd728ad804fb235f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:42:55 +0000 Subject: [PATCH 045/317] chore(deps): update github/codeql-action action to v3.26.6 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/verify_codeql.yml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 0d22deeca1..c6dc5be30a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 99fb8e16bd..da13ec653f 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -58,6 +58,6 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} NODE_OPTIONS: --max-old-space-size=7168 - name: Upload Snyk report - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: snyk.sarif diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index ea31ea6431..177348c30f 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +80,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 From fe6fd8c0899582655f995ca0d877f2c540e556d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 29 Aug 2024 15:31:35 +0200 Subject: [PATCH 046/317] prefer ConfigService MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/quick-suns-swim.md | 5 +++++ packages/backend-defaults/api-report-cache.md | 4 ++-- packages/backend-defaults/api-report-database.md | 3 ++- packages/backend-defaults/api-report-discovery.md | 4 ++-- packages/backend-defaults/api-report-urlReader.md | 6 +++--- .../src/entrypoints/cache/CacheManager.ts | 4 ++-- .../src/entrypoints/database/DatabaseManager.ts | 3 ++- .../src/entrypoints/discovery/HostDiscovery.ts | 10 ++++++++-- .../src/entrypoints/urlReader/lib/UrlReaders.ts | 9 ++++++--- .../src/entrypoints/urlReader/lib/types.ts | 4 ++-- 10 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 .changeset/quick-suns-swim.md diff --git a/.changeset/quick-suns-swim.md b/.changeset/quick-suns-swim.md new file mode 100644 index 0000000000..6113253e22 --- /dev/null +++ b/.changeset/quick-suns-swim.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Accept `ConfigService` instead of `Config` in constructors/factories diff --git a/packages/backend-defaults/api-report-cache.md b/packages/backend-defaults/api-report-cache.md index 58106430ab..f5ea85ad24 100644 --- a/packages/backend-defaults/api-report-cache.md +++ b/packages/backend-defaults/api-report-cache.md @@ -5,15 +5,15 @@ ```ts import { CacheService } from '@backstage/backend-plugin-api'; import { CacheServiceOptions } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public export class CacheManager { forPlugin(pluginId: string): PluginCacheManager; static fromConfig( - config: Config, + config: RootConfigService, options?: CacheManagerOptions, ): CacheManager; } diff --git a/packages/backend-defaults/api-report-database.md b/packages/backend-defaults/api-report-database.md index 10de8df1ff..49ffa75ca7 100644 --- a/packages/backend-defaults/api-report-database.md +++ b/packages/backend-defaults/api-report-database.md @@ -8,6 +8,7 @@ import { DatabaseService } from '@backstage/backend-plugin-api'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { PluginMetadataService } from '@backstage/backend-plugin-api'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public @@ -20,7 +21,7 @@ export class DatabaseManager implements LegacyRootDatabaseService { }, ): DatabaseService; static fromConfig( - config: Config, + config: RootConfigService, options?: DatabaseManagerOptions, ): DatabaseManager; } diff --git a/packages/backend-defaults/api-report-discovery.md b/packages/backend-defaults/api-report-discovery.md index de60256f02..f12daf49c1 100644 --- a/packages/backend-defaults/api-report-discovery.md +++ b/packages/backend-defaults/api-report-discovery.md @@ -3,8 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Config } from '@backstage/config'; import { DiscoveryService } from '@backstage/backend-plugin-api'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public @@ -17,7 +17,7 @@ export const discoveryServiceFactory: ServiceFactory< // @public export class HostDiscovery implements DiscoveryService { static fromConfig( - config: Config, + config: RootConfigService, options?: { basePath?: string; }, diff --git a/packages/backend-defaults/api-report-urlReader.md b/packages/backend-defaults/api-report-urlReader.md index 73fbcd60ac..f8b8db2954 100644 --- a/packages/backend-defaults/api-report-urlReader.md +++ b/packages/backend-defaults/api-report-urlReader.md @@ -12,7 +12,6 @@ import { AzureIntegration } from '@backstage/integration'; import { BitbucketCloudIntegration } from '@backstage/integration'; import { BitbucketIntegration } from '@backstage/integration'; import { BitbucketServerIntegration } from '@backstage/integration'; -import { Config } from '@backstage/config'; import { GerritIntegration } from '@backstage/integration'; import { GiteaIntegration } from '@backstage/integration'; import { GithubCredentialsProvider } from '@backstage/integration'; @@ -21,6 +20,7 @@ import { GitLabIntegration } from '@backstage/integration'; import { HarnessIntegration } from '@backstage/integration'; import { LoggerService } from '@backstage/backend-plugin-api'; import { Readable } from 'stream'; +import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; import { UrlReaderService } from '@backstage/backend-plugin-api'; @@ -360,7 +360,7 @@ export class HarnessUrlReader implements UrlReaderService { // @public export type ReaderFactory = (options: { - config: Config; + config: RootConfigService; logger: LoggerService; treeResponseFactory: ReadTreeResponseFactory; }) => UrlReaderPredicateTuple[]; @@ -442,7 +442,7 @@ export const urlReaderServiceFactory: ServiceFactory< // @public export type UrlReadersOptions = { - config: Config; + config: RootConfigService; logger: LoggerService; factories?: ReaderFactory[]; }; diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts index 9dd42862c9..fa4487a50f 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts @@ -17,8 +17,8 @@ import { CacheServiceOptions, LoggerService, + RootConfigService, } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; import Keyv from 'keyv'; import { DefaultCacheClient } from './CacheClient'; import { @@ -62,7 +62,7 @@ export class CacheManager { * @param config - The loaded application configuration. */ static fromConfig( - config: Config, + config: RootConfigService, options: CacheManagerOptions = {}, ): CacheManager { // If no `backend.cache` config is provided, instantiate the CacheManager diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts index b3e69f0ae2..88d95443bb 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts @@ -19,6 +19,7 @@ import { LifecycleService, LoggerService, PluginMetadataService, + RootConfigService, } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { stringifyError } from '@backstage/errors'; @@ -200,7 +201,7 @@ export class DatabaseManager implements LegacyRootDatabaseService { * @param options - An optional configuration object. */ static fromConfig( - config: Config, + config: RootConfigService, options?: DatabaseManagerOptions, ): DatabaseManager { const databaseConfig = config.getConfig('backend.database'); diff --git a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts index 559dda2aa0..26e2a01a23 100644 --- a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts +++ b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts @@ -15,7 +15,10 @@ */ import { Config } from '@backstage/config'; -import { DiscoveryService } from '@backstage/backend-plugin-api'; +import { + DiscoveryService, + RootConfigService, +} from '@backstage/backend-plugin-api'; import { readHttpServerOptions } from '../rootHttpRouter/http/config'; type Target = string | { internal: string; external: string }; @@ -55,7 +58,10 @@ export class HostDiscovery implements DiscoveryService { * The basePath defaults to `/api`, meaning the default full internal * path for the `catalog` plugin will be `http://localhost:7007/api/catalog`. */ - static fromConfig(config: Config, options?: { basePath?: string }) { + static fromConfig( + config: RootConfigService, + options?: { basePath?: string }, + ) { const basePath = options?.basePath ?? '/api'; const externalBaseUrl = config .getString('backend.baseUrl') diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts index cb496dd702..96e9ed6509 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/UrlReaders.ts @@ -14,8 +14,11 @@ * limitations under the License. */ -import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; +import { + LoggerService, + RootConfigService, + UrlReaderService, +} from '@backstage/backend-plugin-api'; import { ReaderFactory } from './types'; import { UrlReaderPredicateMux } from './UrlReaderPredicateMux'; import { AzureUrlReader } from './AzureUrlReader'; @@ -40,7 +43,7 @@ import { HarnessUrlReader } from './HarnessUrlReader'; */ export type UrlReadersOptions = { /** Root config object */ - config: Config; + config: RootConfigService; /** Logger used by all the readers */ logger: LoggerService; /** A list of factories used to construct individual readers that match on URLs */ diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/types.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/types.ts index 510b12545f..5afbed178f 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/types.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/types.ts @@ -15,11 +15,11 @@ */ import { Readable } from 'stream'; -import { Config } from '@backstage/config'; import { UrlReaderService, UrlReaderServiceReadTreeResponse, LoggerService, + RootConfigService, } from '@backstage/backend-plugin-api'; /** @@ -40,7 +40,7 @@ export type UrlReaderPredicateTuple = { * @public */ export type ReaderFactory = (options: { - config: Config; + config: RootConfigService; logger: LoggerService; treeResponseFactory: ReadTreeResponseFactory; }) => UrlReaderPredicateTuple[]; From a27aefa0f6d63a62f059f2ae2441f83f0e36f327 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 29 Aug 2024 15:34:07 +0200 Subject: [PATCH 047/317] docs: create old page Signed-off-by: Camila Belo --- ...02-adding-a-basic-permission-check--old.md | 387 ++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md new file mode 100644 index 0000000000..7c51214359 --- /dev/null +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md @@ -0,0 +1,387 @@ +--- +id: 02-adding-a-basic-permission-check +title: 2. Adding a basic permission check +description: Explains how to add a basic permission check to a Backstage plugin +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./02-adding-a-basic-permission-check.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + +If the outcome of a permission check doesn't need to change for different [resources](../../references/glossary.md#resource-permission-plugin), you can use a _basic permission check_. For this kind of check, we simply need to define a permission, and call `authorize` with it. + +For this tutorial, we'll use a basic permission check to authorize the `create` endpoint in our todo-backend. This will allow Backstage integrators to control whether each of their users is authorized to create todos by adjusting their [permission policy](../../references/glossary.md#policy-permission-plugin). + +We'll start by creating a new permission, and then we'll use the permission api to call `authorize` with it during todo creation. + +## Creating a new permission + +Let's navigate to the file `plugins/todo-list-common/src/permissions.ts` and add our first permission: + +```ts title="plugins/todo-list-common/src/permissions.ts" +import { createPermission } from '@backstage/plugin-permission-common'; + +/* highlight-remove-start */ +export const tempExamplePermission = createPermission({ + name: 'temp.example.noop', + attributes: {}, +/* highlight-remove-end */ +/* highlight-add-start */ +export const todoListCreatePermission = createPermission({ + name: 'todo.list.create', + attributes: { action: 'create' }, +/* highlight-add-end */ +}); + +/* highlight-remove-next-line */ +export const todoListPermissions = [tempExamplePermission]; +/* highlight-add-next-line */ +export const todoListPermissions = [todoListCreatePermission]; +``` + +For this tutorial, we've automatically exported all permissions from this file (see `plugins/todo-list-common/src/index.ts`). + +:::note Note + +We use a separate `todo-list-common` package since all permissions authorized by your plugin should be exported from a ["common-library" package](https://backstage.io/docs/tooling/cli/build-system#package-roles). This allows Backstage integrators to reference them in frontend components as well as permission policies. + +::: + +## Authorizing using the new permission + +Install the following module: + +``` +$ yarn workspace @internal/plugin-todo-list-backend \ + add @backstage/plugin-permission-common @backstage/plugin-permission-node @internal/plugin-todo-list-common +``` + +Edit `plugins/todo-list-backend/src/service/router.ts`: + +```ts title="plugins/todo-list-backend/src/service/router.ts" +/* highlight-remove-start */ +import { InputError } from '@backstage/errors'; +import { IdentityApi } from '@backstage/plugin-auth-node'; +/* highlight-remove-end */ +/* highlight-add-start */ +import { InputError, NotAllowedError } from '@backstage/errors'; +import { getBearerTokenFromAuthorizationHeader, IdentityApi } from '@backstage/plugin-auth-node'; +import { PermissionEvaluator, AuthorizeResult } from '@backstage/plugin-permission-common'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; +import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; +/* highlight-add-end */ + +export interface RouterOptions { + logger: Logger; + identity: IdentityApi; + /* highlight-add-next-line */ + permissions: PermissionEvaluator; +} + +export async function createRouter( + options: RouterOptions, +): Promise { + /* highlight-remove-next-line */ + const { logger, identity } = options; + /* highlight-add-next-line */ + const { logger, identity, permissions } = options; + + /* highlight-add-start */ + const permissionIntegrationRouter = createPermissionIntegrationRouter({ + permissions: [todoListCreatePermission], + }); + /* highlight-add-end */ + + const router = Router(); + router.use(express.json()); + + router.get('/health', (_, response) => { + logger.info('PONG!'); + response.json({ status: 'ok' }); + }); + + /* highlight-add-next-line */ + router.use(permissionIntegrationRouter); + + router.get('/todos', async (_req, res) => { + res.json(getAll()); + }); + + router.post('/todos', async (req, res) => { + let author: string | undefined = undefined; + + const user = await identity.getIdentity({ request: req }); + author = user?.identity.userEntityRef; + /* highlight-add-start */ + const token = getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ); + const decision = ( + await permissions.authorize([{ permission: todoListCreatePermission }], { + token, + }) + )[0]; + + if (decision.result === AuthorizeResult.DENY) { + throw new NotAllowedError('Unauthorized'); + } + /* highlight-add-end */ + + if (!isTodoCreateRequest(req.body)) { + throw new InputError('Invalid payload'); + } + + const todo = add({ title: req.body.title, author }); + res.json(todo); + }); + + // ... +``` + +Pass the `permissions` object to the plugin in `packages/backend/src/plugins/todolist.ts`: + +```ts title="packages/backend/src/plugins/todolist.ts" +import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; +import { createRouter } from '@internal/plugin-todo-list-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ + logger, + discovery, + /* highlight-add-next-line */ + permissions, +}: PluginEnvironment): Promise { + return await createRouter({ + logger, + identity: DefaultIdentityClient.create({ + discovery, + issuer: await discovery.getExternalBaseUrl('auth'), + }), + /* highlight-add-next-line */ + permissions, + }); +} +``` + +That's it! Now your plugin is fully configured. Let's try to test the logic by denying the permission. + +## Test the authorized create endpoint + +Before running this step, please make sure you followed the steps described in [Getting started](../getting-started.md) section. + +In order to test the logic above, the integrators of your backstage instance need to change their permission policy to return `DENY` for our newly-created permission: + +```ts title="packages/backend/src/plugins/permission.ts" +import { + PermissionPolicy, + /* highlight-add-start */ + PolicyQuery, + PolicyQueryUser, + /* highlight-add-end */ +} from '@backstage/plugin-permission-node'; +/* highlight-add-start */ +import { isPermission } from '@backstage/plugin-permission-common'; +import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; +/* highlight-add-end */ + +class TestPermissionPolicy implements PermissionPolicy { + /* highlight-remove-next-line */ + async handle(): Promise { + /* highlight-add-start */ + async handle( + request: PolicyQuery, + _user?: PolicyQueryUser, + ): Promise { + if (isPermission(request.permission, todoListCreatePermission)) { + return { + result: AuthorizeResult.DENY, + }; + } + /* highlight-add-end */ + + return { + result: AuthorizeResult.ALLOW, + }; +} +``` + +Now the frontend should show an error whenever you try to create a new Todo item. + +Let's flip the result back to `ALLOW` before moving on. + +```ts +if (isPermission(request.permission, todoListCreatePermission)) { + return { + /* highlight-remove-next-line */ + result: AuthorizeResult.DENY, + /* highlight-add-next-line */ + result: AuthorizeResult.ALLOW, + }; +} +``` + +At this point everything is working but if you run `yarn tsc` you'll get some errors, let's fix those up. + +First we'll clean up the `plugins/todo-list-backend/src/service/router.test.ts`: + +```ts title="plugins/todo-list-backend/src/service/router.test.ts" +import { getVoidLogger } from '@backstage/backend-common'; +import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; +/* highlight-add-next-line */ +import { PermissionEvaluator } from '@backstage/plugin-permission-common'; +import express from 'express'; +import request from 'supertest'; + +import { createRouter } from './router'; + +/* highlight-add-start */ +const mockedAuthorize: jest.MockedFunction = + jest.fn(); +const mockedPermissionQuery: jest.MockedFunction< + PermissionEvaluator['authorizeConditional'] +> = jest.fn(); + +const permissionEvaluator: PermissionEvaluator = { + authorize: mockedAuthorize, + authorizeConditional: mockedPermissionQuery, +}; +/* highlight-add-end */ + +describe('createRouter', () => { + let app: express.Express; + + beforeAll(async () => { + const router = await createRouter({ + logger: getVoidLogger(), + identity: {} as DefaultIdentityClient, + /* highlight-add-next-line */ + permissions: permissionEvaluator, + }); + app = express().use(router); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /health', () => { + it('returns ok', async () => { + const response = await request(app).get('/health'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); +}); +``` + +Then we want to update the `plugins/todo-list-backend/src/service/standaloneServer.ts`: + +```ts title="plugins/todo-list-backend/src/service/standaloneServer.ts" +import { + createServiceBuilder, + loadBackendConfig, + SingleHostDiscovery, + /* highlight-add-next-line */ + ServerTokenManager, +} from '@backstage/backend-common'; +import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; +/* highlight-add-next-line */ +import { ServerPermissionClient } from '@backstage/plugin-permission-node'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './router'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'todo-list-backend' }); + logger.debug('Starting application server...'); + const config = await loadBackendConfig({ logger, argv: process.argv }); + const discovery = SingleHostDiscovery.fromConfig(config); + /* highlight-add-start */ + const tokenManager = ServerTokenManager.fromConfig(config, { + logger, + }); + const permissions = ServerPermissionClient.fromConfig(config, { + discovery, + tokenManager, + }); + /* highlight-add-end */ + const router = await createRouter({ + logger, + identity: DefaultIdentityClient.create({ + discovery, + issuer: await discovery.getExternalBaseUrl('auth'), + }), + /* highlight-add-next-line */ + permissions, + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/todo-list', router); + if (options.enableCors) { + service = service.enableCors({ origin: 'http://localhost:3000' }); + } + + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); +``` + +Finally, we need to update `plugins/todo-list-backend/src/plugin.ts`: + +```ts title="plugins/todo-list-backend/src/plugin.ts" +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; + +/** +* The example TODO list backend plugin. +* +* @public +*/ +export const exampleTodoListPlugin = createBackendPlugin({ + pluginId: 'exampleTodoList', + register(env) { + env.registerInit({ + deps: { + identity: coreServices.identity, + logger: coreServices.logger, + httpRouter: coreServices.httpRouter, + /* highlight-add-next-line */ + permissions: coreServices.permissions, + }, + /* highlight-remove-next-line */ + async init({ identity, logger, httpRouter }) { + /* highlight-add-next-line */ + async init({ identity, logger, httpRouter, permissions }) { + httpRouter.use( + await createRouter({ + identity, + logger: loggerToWinstonLogger(logger), + permissions, + }), + ); + }, + }); + }, +}); +``` + +Now when you run `yarn tsc` you should have no more errors. From d6c4b41d2635a240844349e348aeb899bfd55345 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 29 Aug 2024 15:34:21 +0200 Subject: [PATCH 048/317] docs: create new page Signed-off-by: Camila Belo --- .../02-adding-a-basic-permission-check.md | 255 ++++++------------ 1 file changed, 89 insertions(+), 166 deletions(-) diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 724edb7ccb..a1873ea1d3 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -4,6 +4,10 @@ title: 2. Adding a basic permission check description: Explains how to add a basic permission check to a Backstage plugin --- +:::info +This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./02-adding-a-basic-permission-check--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + If the outcome of a permission check doesn't need to change for different [resources](../../references/glossary.md#resource-permission-plugin), you can use a _basic permission check_. For this kind of check, we simply need to define a permission, and call `authorize` with it. For this tutorial, we'll use a basic permission check to authorize the `create` endpoint in our todo-backend. This will allow Backstage integrators to control whether each of their users is authorized to create todos by adjusting their [permission policy](../../references/glossary.md#policy-permission-plugin). @@ -57,18 +61,20 @@ Edit `plugins/todo-list-backend/src/service/router.ts`: ```ts title="plugins/todo-list-backend/src/service/router.ts" /* highlight-remove-start */ import { InputError } from '@backstage/errors'; -import { IdentityApi } from '@backstage/plugin-auth-node'; +import { HttpAuthService, LoggerService } from '@backstage/backend-plugin-api'; /* highlight-remove-end */ /* highlight-add-start */ import { InputError, NotAllowedError } from '@backstage/errors'; -import { getBearerTokenFromAuthorizationHeader, IdentityApi } from '@backstage/plugin-auth-node'; -import { PermissionEvaluator, AuthorizeResult } from '@backstage/plugin-permission-common'; +import { AuthService, HttpAuthService, LoggerService } from '@backstage/backend-plugin-api'; +import { AuthorizeResult, PermissionEvaluator } from '@backstage/plugin-permission-common'; import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; /* highlight-add-end */ export interface RouterOptions { - logger: Logger; + logger: LoggerService; + /* highlight-add-next-line */ + auth: AuthService; identity: IdentityApi; /* highlight-add-next-line */ permissions: PermissionEvaluator; @@ -78,9 +84,9 @@ export async function createRouter( options: RouterOptions, ): Promise { /* highlight-remove-next-line */ - const { logger, identity } = options; + const { logger, httpAuth } = options; /* highlight-add-next-line */ - const { logger, identity, permissions } = options; + const { logger, auth, httpAuth, permissions } = options; /* highlight-add-start */ const permissionIntegrationRouter = createPermissionIntegrationRouter({ @@ -109,13 +115,14 @@ export async function createRouter( const user = await identity.getIdentity({ request: req }); author = user?.identity.userEntityRef; /* highlight-add-start */ - const token = getBearerTokenFromAuthorizationHeader( - req.header('authorization'), - ); const decision = ( - await permissions.authorize([{ permission: todoListCreatePermission }], { - token, - }) + await permissions.authorize( + [{ permission: todoListCreatePermission }], + await auth.getPluginRequestToken({ + onBehalfOf: credentials, + targetPluginId: 'permission', + }), + ) )[0]; if (decision.result === AuthorizeResult.DENY) { @@ -134,30 +141,47 @@ export async function createRouter( // ... ``` -Pass the `permissions` object to the plugin in `packages/backend/src/plugins/todolist.ts`: +Pass the `auth` and `permissions` objects to the plugin in `plugins/todo-list-backend/src/plugin.ts`: -```ts title="packages/backend/src/plugins/todolist.ts" -import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; -import { createRouter } from '@internal/plugin-todo-list-backend'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; +```ts title="plugins/todo-list-backend/src/plugin.ts" +import { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; -export default async function createPlugin({ - logger, - discovery, - /* highlight-add-next-line */ - permissions, -}: PluginEnvironment): Promise { - return await createRouter({ - logger, - identity: DefaultIdentityClient.create({ - discovery, - issuer: await discovery.getExternalBaseUrl('auth'), - }), - /* highlight-add-next-line */ - permissions, - }); -} +export const exampleTodoListPlugin = createBackendPlugin({ + pluginId: 'todolist', + register(env) { + env.registerInit({ + deps: { + logger: coreServices.logger, + /* highlight-add-start */ + auth: coreServices.auth, + permissions: coreServices.permissions, + /* highlight-add-end */ + httpAuth: coreServices.httpAuth, + httpRouter: coreServices.httpRouter, + }, + /* highlight-remove-next-line */ + async init({ logger, httpAuth, httpRouter }) { + /* highlight-add-next-line */ + async init({ logger, auth, httpAuth, httpRouter, permissions }) { + httpRouter.use( + await createRouter({ + logger, + /* highlight-add-start */ + auth, + permissions, + /* highlight-add-end */ + httpAuth, + }), + ); + httpRouter.addAuthPolicy({ + path: '/health', + allow: 'unauthenticated', + }); + }, + }); + }, +}); ``` That's it! Now your plugin is fully configured. Let's try to test the logic by denying the permission. @@ -168,7 +192,15 @@ Before running this step, please make sure you followed the steps described in [ In order to test the logic above, the integrators of your backstage instance need to change their permission policy to return `DENY` for our newly-created permission: -```ts title="packages/backend/src/plugins/permission.ts" +```ts title="packages/backend/src/extensions/permissionsPolicyExtension.ts" +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + PolicyDecision, + /* highlight-add-start */ + isPermission, + /* highlight-add-end */ + AuthorizeResult, +} from '@backstage/plugin-permission-common'; import { PermissionPolicy, /* highlight-add-start */ @@ -177,9 +209,9 @@ import { /* highlight-add-end */ } from '@backstage/plugin-permission-node'; /* highlight-add-start */ -import { isPermission } from '@backstage/plugin-permission-common'; import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; /* highlight-add-end */ +import { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha'; class TestPermissionPolicy implements PermissionPolicy { /* highlight-remove-next-line */ @@ -200,6 +232,19 @@ class TestPermissionPolicy implements PermissionPolicy { result: AuthorizeResult.ALLOW, }; } + +export default createBackendModule({ + pluginId: 'permission', + moduleId: 'permission-policy', + register(reg) { + reg.registerInit({ + deps: { policy: policyExtensionPoint }, + async init({ policy }) { + policy.setPolicy(new TestPermissionPolicy()); + }, + }); + }, +}); ``` Now the frontend should show an error whenever you try to create a new Todo item. @@ -217,42 +262,28 @@ if (isPermission(request.permission, todoListCreatePermission)) { } ``` -At this point everything is working but if you run `yarn tsc` you'll get some errors, let's fix those up. +At this point everything is working but if you run `yarn tsc` you'll get an error, let's fix this up. -First we'll clean up the `plugins/todo-list-backend/src/service/router.test.ts`: +Clean up the `plugins/todo-list-backend/src/service/router.test.ts`: ```ts title="plugins/todo-list-backend/src/service/router.test.ts" -import { getVoidLogger } from '@backstage/backend-common'; -import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; -/* highlight-add-next-line */ -import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import express from 'express'; import request from 'supertest'; import { createRouter } from './router'; - -/* highlight-add-start */ -const mockedAuthorize: jest.MockedFunction = - jest.fn(); -const mockedPermissionQuery: jest.MockedFunction< - PermissionEvaluator['authorizeConditional'] -> = jest.fn(); - -const permissionEvaluator: PermissionEvaluator = { - authorize: mockedAuthorize, - authorizeConditional: mockedPermissionQuery, -}; -/* highlight-add-end */ +import { mockServices } from '@backstage/backend-test-utils'; describe('createRouter', () => { let app: express.Express; beforeAll(async () => { const router = await createRouter({ - logger: getVoidLogger(), - identity: {} as DefaultIdentityClient, + logger: mockServices.logger.mock(), /* highlight-add-next-line */ - permissions: permissionEvaluator, + auth: mockServices.auth.mock(), + httpAuth: mockServices.httpAuth.mock(), + /* highlight-add-next-line */ + permissions: mockServices.permissions.mock(), }); app = express().use(router); }); @@ -272,112 +303,4 @@ describe('createRouter', () => { }); ``` -Then we want to update the `plugins/todo-list-backend/src/service/standaloneServer.ts`: - -```ts title="plugins/todo-list-backend/src/service/standaloneServer.ts" -import { - createServiceBuilder, - loadBackendConfig, - SingleHostDiscovery, - /* highlight-add-next-line */ - ServerTokenManager, -} from '@backstage/backend-common'; -import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; -/* highlight-add-next-line */ -import { ServerPermissionClient } from '@backstage/plugin-permission-node'; -import { Server } from 'http'; -import { Logger } from 'winston'; -import { createRouter } from './router'; - -export interface ServerOptions { - port: number; - enableCors: boolean; - logger: Logger; -} - -export async function startStandaloneServer( - options: ServerOptions, -): Promise { - const logger = options.logger.child({ service: 'todo-list-backend' }); - logger.debug('Starting application server...'); - const config = await loadBackendConfig({ logger, argv: process.argv }); - const discovery = SingleHostDiscovery.fromConfig(config); - /* highlight-add-start */ - const tokenManager = ServerTokenManager.fromConfig(config, { - logger, - }); - const permissions = ServerPermissionClient.fromConfig(config, { - discovery, - tokenManager, - }); - /* highlight-add-end */ - const router = await createRouter({ - logger, - identity: DefaultIdentityClient.create({ - discovery, - issuer: await discovery.getExternalBaseUrl('auth'), - }), - /* highlight-add-next-line */ - permissions, - }); - - let service = createServiceBuilder(module) - .setPort(options.port) - .addRouter('/todo-list', router); - if (options.enableCors) { - service = service.enableCors({ origin: 'http://localhost:3000' }); - } - - return await service.start().catch(err => { - logger.error(err); - process.exit(1); - }); -} - -module.hot?.accept(); -``` - -Finally, we need to update `plugins/todo-list-backend/src/plugin.ts`: - -```ts title="plugins/todo-list-backend/src/plugin.ts" -import { loggerToWinstonLogger } from '@backstage/backend-common'; -import { - coreServices, - createBackendPlugin, -} from '@backstage/backend-plugin-api'; -import { createRouter } from './service/router'; - -/** -* The example TODO list backend plugin. -* -* @public -*/ -export const exampleTodoListPlugin = createBackendPlugin({ - pluginId: 'exampleTodoList', - register(env) { - env.registerInit({ - deps: { - identity: coreServices.identity, - logger: coreServices.logger, - httpRouter: coreServices.httpRouter, - /* highlight-add-next-line */ - permissions: coreServices.permissions, - }, - /* highlight-remove-next-line */ - async init({ identity, logger, httpRouter }) { - /* highlight-add-next-line */ - async init({ identity, logger, httpRouter, permissions }) { - httpRouter.use( - await createRouter({ - identity, - logger: loggerToWinstonLogger(logger), - permissions, - }), - ); - }, - }); - }, -}); -``` - Now when you run `yarn tsc` you should have no more errors. From 3f4b9f4c756e8d723055ccd90c10ceca784a7bb5 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Thu, 29 Aug 2024 16:00:47 +0200 Subject: [PATCH 049/317] Add support for omitExtraData and liveOmit properties. Signed-off-by: Jasper Boeijenga --- .../scaffolder-react/src/components/types.ts | 7 ++++- .../src/next/components/Stepper/Stepper.tsx | 30 ++++++++++++++----- .../src/components/Router/Router.tsx | 1 + .../TemplateEditorPage/TemplateEditor.tsx | 7 ++++- .../TemplateEditorPage/TemplateEditorForm.tsx | 6 +++- .../TemplateEditorPage/TemplateEditorPage.tsx | 4 +++ .../TemplateFormPreviewer.tsx | 4 +++ 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/plugins/scaffolder-react/src/components/types.ts b/plugins/scaffolder-react/src/components/types.ts index d5d317015d..3df6209cad 100644 --- a/plugins/scaffolder-react/src/components/types.ts +++ b/plugins/scaffolder-react/src/components/types.ts @@ -32,7 +32,12 @@ export type TemplateGroupFilter = { */ export type FormProps = Pick< SchemaFormProps, - 'transformErrors' | 'noHtml5Validate' | 'uiSchema' | 'formContext' + | 'transformErrors' + | 'noHtml5Validate' + | 'uiSchema' + | 'formContext' + | 'omitExtraData' + | 'liveOmit' >; /** diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 3a8858144b..16a3bb0761 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -155,9 +155,13 @@ export const Stepper = (stepperProps: StepperProps) => { }; const handleChange = useCallback( - (e: IChangeEvent) => - setFormState(current => ({ ...current, ...e.formData })), - [setFormState], + (e: IChangeEvent) => { + setFormState(current => ({ + ...current, + [`step${activeStep}`]: e.formData, + })); + }, + [setFormState, activeStep], ); const handleCreate = useCallback(() => { @@ -191,7 +195,7 @@ export const Stepper = (stepperProps: StepperProps) => { return stepNum; }); } - setFormState(current => ({ ...current, ...formData })); + setFormState(current => ({ ...current, [`step${activeStep}`]: formData })); }; const { @@ -202,6 +206,16 @@ export const Stepper = (stepperProps: StepperProps) => { const mergedUiSchema = merge({}, propUiSchema, currentStep?.uiSchema); + const mergedState = useMemo(() => { + const { [`step${activeStep}`]: activeState, ...historicalState } = + formState; + const chronologicalState = { + ...historicalState, + [`step${activeStep}`]: activeState, + }; + return merge({}, ...Object.values(chronologicalState)); + }, [formState, activeStep]); + return ( <> {isValidating && } @@ -237,8 +251,8 @@ export const Stepper = (stepperProps: StepperProps) => {
    { ReviewStepComponent ? ( {}} steps={steps} @@ -282,7 +296,7 @@ export const Stepper = (stepperProps: StepperProps) => { /> ) : ( <> - +
    @@ -206,7 +209,7 @@ export function TemplateEditorForm(props: TemplateEditorFormProps) { export function TemplateEditorFormDirectoryEditorDryRun( props: Pick< TemplateEditorFormProps, - 'setErrorText' | 'fieldExtensions' | 'layouts' + 'setErrorText' | 'fieldExtensions' | 'layouts' | 'formProps' >, ) { const { setErrorText, fieldExtensions = [], layouts } = props; @@ -245,6 +248,7 @@ export function TemplateEditorFormDirectoryEditorDryRun( setErrorText={setErrorText} content={content} layouts={layouts} + formProps={props.formProps} /> ); } diff --git a/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx index 6e7c921e91..03d656c98d 100644 --- a/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx +++ b/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx @@ -24,6 +24,7 @@ import { TemplateEditor } from './TemplateEditor'; import { TemplateFormPreviewer } from './TemplateFormPreviewer'; import { FieldExtensionOptions, + FormProps, type LayoutOptions, } from '@backstage/plugin-scaffolder-react'; import { TemplateEditorIntro } from './TemplateEditorIntro'; @@ -54,6 +55,7 @@ interface TemplateEditorPageProps { defaultPreviewTemplate?: string; customFieldExtensions?: FieldExtensionOptions[]; layouts?: LayoutOptions[]; + formProps?: FormProps; } export function TemplateEditorPage(props: TemplateEditorPageProps) { @@ -79,6 +81,7 @@ export function TemplateEditorPage(props: TemplateEditorPageProps) { fieldExtensions={props.customFieldExtensions} onClose={() => setSelection(undefined)} layouts={props.layouts} + formProps={props.formProps} /> ); } else if (selection?.type === 'form') { @@ -88,6 +91,7 @@ export function TemplateEditorPage(props: TemplateEditorPageProps) { customFieldExtensions={props.customFieldExtensions} onClose={() => setSelection(undefined)} layouts={props.layouts} + formProps={props.formProps} /> ); } else if (selection?.type === 'field-explorer') { diff --git a/plugins/scaffolder/src/next/TemplateEditorPage/TemplateFormPreviewer.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/TemplateFormPreviewer.tsx index 07f2fd7f07..610d2a22f0 100644 --- a/plugins/scaffolder/src/next/TemplateEditorPage/TemplateFormPreviewer.tsx +++ b/plugins/scaffolder/src/next/TemplateEditorPage/TemplateFormPreviewer.tsx @@ -33,6 +33,7 @@ import yaml from 'yaml'; import { LayoutOptions, FieldExtensionOptions, + FormProps, } from '@backstage/plugin-scaffolder-react'; import { TemplateEditorForm } from './TemplateEditorForm'; import { TemplateEditorTextArea } from './TemplateEditorTextArea'; @@ -114,11 +115,13 @@ export const TemplateFormPreviewer = ({ customFieldExtensions = [], onClose, layouts = [], + formProps, }: { defaultPreviewTemplate?: string; customFieldExtensions?: FieldExtensionOptions[]; onClose?: () => void; layouts?: LayoutOptions[]; + formProps?: FormProps; }) => { const classes = useStyles(); const { t } = useTranslationRef(scaffolderTranslationRef); @@ -213,6 +216,7 @@ export const TemplateFormPreviewer = ({ fieldExtensions={customFieldExtensions} setErrorText={setErrorText} layouts={layouts} + formProps={formProps} /> From 51c28e89e59043d438b6830777cfd6f2769891ee Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 28 Aug 2024 13:49:35 +0200 Subject: [PATCH 050/317] docs: create the old version page Signed-off-by: Camila Belo --- .../plugin-authors/01-setup--old.md | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 docs/permissions/plugin-authors/01-setup--old.md diff --git a/docs/permissions/plugin-authors/01-setup--old.md b/docs/permissions/plugin-authors/01-setup--old.md new file mode 100644 index 0000000000..3e681c2cb3 --- /dev/null +++ b/docs/permissions/plugin-authors/01-setup--old.md @@ -0,0 +1,134 @@ +--- +id: 01-setup +title: 1. Tutorial setup +description: How to get started with the permission framework as a plugin author +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./01-setup.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + +The following tutorial is designed to help plugin authors add support for permissions to their plugins. We'll add support for permissions to example `todo-list` and `todo-list-backend` plugins, but the process should be similar for other plugins! + +The rest of this page is focused on adding the `todo-list` and `todo-list-backend` plugins to your Backstage instance. If you want to add support for permissions to your own plugin instead, feel free to skip to the [next section](./02-adding-a-basic-permission-check.md). + +## Setup for the Tutorial + +We will use a "Todo list" feature, composed of the `todo-list` and `todo-list-backend` plugins, as well as their dependency, `todo-list-common`. + +The source code is available here: + +- [todo-list](https://github.com/backstage/backstage/blob/master/plugins/example-todo-list) +- [todo-list-backend](https://github.com/backstage/backstage/blob/master/plugins/example-todo-list-backend) +- [todo-list-common](https://github.com/backstage/backstage/blob/master/plugins/example-todo-list-common) + +1. Copy-paste the three folders into the plugins folder of your backstage application repository (removing the `example-` prefix from each folder) or run the following script from the root of your backstage application: + + ```bash + $ cd $(mktemp -d) + git clone --depth 1 --quiet --no-checkout --filter=blob:none https://github.com/backstage/backstage.git . + git checkout master -- plugins/example-todo-list/ + git checkout master -- plugins/example-todo-list-backend/ + git checkout master -- plugins/example-todo-list-common/ + sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list/package.json + sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list-backend/package.json + sed -i '' 's/workspace:\^/\*/g' plugins/example-todo-list-common/package.json + for file in plugins/*; do mv "$file" "$OLDPWD/${file/example-todo/todo}"; done + cd - + ``` + + The `plugins` directory of your project should now include `todo-list`, `todo-list-backend`, and `todo-list-common`. + + **Important**: if you are on **Windows**, make sure you have WSL and git installed on your machine before executing the script above. + +2. Add these packages as dependencies for your Backstage app: + + ```sh title="From your Backstage root directory" + yarn --cwd packages/backend add @internal/plugin-todo-list-backend @internal/plugin-todo-list-common + yarn --cwd packages/app add @internal/plugin-todo-list + ``` + +3. Include the backend and frontend plugin in your application: + + Create a new `packages/backend/src/plugins/todolist.ts` with the following content: + + ```typescript title="packages/backend/src/plugins/todolist.ts" + import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; + import { createRouter } from '@internal/plugin-todo-list-backend'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin({ + logger, + discovery, + }: PluginEnvironment): Promise { + return await createRouter({ + logger, + identity: DefaultIdentityClient.create({ + discovery, + issuer: await discovery.getExternalBaseUrl('auth'), + }), + }); + } + ``` + + Apply the following changes to `packages/backend/src/index.ts`: + + ```ts title="packages/backend/src/index.ts" + import techdocs from './plugins/techdocs'; + /* highlight-add-next-line */ + import todoList from './plugins/todolist'; + import search from './plugins/search'; + + async function main() { + const searchEnv = useHotMemoize(module, () => createEnv('search')); + const appEnv = useHotMemoize(module, () => createEnv('app')); + /* highlight-add-next-line */ + const todoListEnv = useHotMemoize(module, () => createEnv('todolist')); + // .. + + apiRouter.use('/proxy', await proxy(proxyEnv)); + apiRouter.use('/search', await search(searchEnv)); + apiRouter.use('/permission', await permission(permissionEnv)); + /* highlight-add-next-line */ + apiRouter.use('/todolist', await todoList(todoListEnv)); + // Add backends ABOVE this line; this 404 handler is the catch-all fallback + apiRouter.use(notFoundHandler()); + // .. + } + ``` + + Apply the following changes to `packages/app/src/App.tsx`: + + ```tsx title="packages/app/src/App.tsx" + /* highlight-add-next-line */ + import { TodoListPage } from '@internal/plugin-todo-list'; + + const routes = ( + + }> + {searchPage} + + } /> + {/* highlight-add-next-line */} + } /> + {/* ... */} + + ); + ``` + +Now if you start your application you should be able to reach the `/todo-list` page: + +![Todo List plugin page](../../assets/permissions/permission-todo-list-page.png) + +--- + +## Integrate the new plugin + +If you play with the UI, you will notice that it is possible to perform a few actions: + +- create a new todo item (`POST /todos`) +- view todo items (`GET /todos`) +- edit an existing todo item (`PUT /todos`) + +Let's try to bring authorization on top of each one of them. From fc893ae4c9380df95fd45add44b1bf190e0c190a Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 28 Aug 2024 18:24:03 +0200 Subject: [PATCH 051/317] fix: use the same id passed in the todolist page Signed-off-by: Camila Belo --- plugins/example-todo-list-backend/src/plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/example-todo-list-backend/src/plugin.ts b/plugins/example-todo-list-backend/src/plugin.ts index 1c7bba929a..d05fa54c68 100644 --- a/plugins/example-todo-list-backend/src/plugin.ts +++ b/plugins/example-todo-list-backend/src/plugin.ts @@ -26,7 +26,7 @@ import { createRouter } from './service/router'; * @public */ export const exampleTodoListPlugin = createBackendPlugin({ - pluginId: 'exampleTodoList', + pluginId: 'todolist', register(env) { env.registerInit({ deps: { From eda9984c77240ccf748b0a5b8f5a1a2fa4e2a6af Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 28 Aug 2024 18:24:56 +0200 Subject: [PATCH 052/317] docs: update the permissions setup guide Signed-off-by: Camila Belo --- docs/permissions/plugin-authors/01-setup.md | 71 +++++++-------------- 1 file changed, 23 insertions(+), 48 deletions(-) diff --git a/docs/permissions/plugin-authors/01-setup.md b/docs/permissions/plugin-authors/01-setup.md index f11bdff930..f3c361e8b5 100644 --- a/docs/permissions/plugin-authors/01-setup.md +++ b/docs/permissions/plugin-authors/01-setup.md @@ -4,6 +4,10 @@ title: 1. Tutorial setup description: How to get started with the permission framework as a plugin author --- +:::info +This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./01-setup--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + The following tutorial is designed to help plugin authors add support for permissions to their plugins. We'll add support for permissions to example `todo-list` and `todo-list-backend` plugins, but the process should be similar for other plugins! The rest of this page is focused on adding the `todo-list` and `todo-list-backend` plugins to your Backstage instance. If you want to add support for permissions to your own plugin instead, feel free to skip to the [next section](./02-adding-a-basic-permission-check.md). @@ -40,58 +44,31 @@ The source code is available here: 2. Add these packages as dependencies for your Backstage app: ```sh title="From your Backstage root directory" - yarn --cwd packages/backend add @internal/plugin-todo-list-backend @internal/plugin-todo-list-common + yarn --cwd packages/backend add @backstage/plugin-permission-backend @backstage/plugin-permission-backend-module-allow-all-policy @internal/plugin-todo-list-backend @internal/plugin-todo-list-common yarn --cwd packages/app add @internal/plugin-todo-list ``` 3. Include the backend and frontend plugin in your application: - Create a new `packages/backend/src/plugins/todolist.ts` with the following content: - - ```typescript title="packages/backend/src/plugins/todolist.ts" - import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; - import { createRouter } from '@internal/plugin-todo-list-backend'; - import { Router } from 'express'; - import { PluginEnvironment } from '../types'; - - export default async function createPlugin({ - logger, - discovery, - }: PluginEnvironment): Promise { - return await createRouter({ - logger, - identity: DefaultIdentityClient.create({ - discovery, - issuer: await discovery.getExternalBaseUrl('auth'), - }), - }); - } - ``` - Apply the following changes to `packages/backend/src/index.ts`: ```ts title="packages/backend/src/index.ts" - import techdocs from './plugins/techdocs'; - /* highlight-add-next-line */ - import todoList from './plugins/todolist'; - import search from './plugins/search'; - - async function main() { - const searchEnv = useHotMemoize(module, () => createEnv('search')); - const appEnv = useHotMemoize(module, () => createEnv('app')); - /* highlight-add-next-line */ - const todoListEnv = useHotMemoize(module, () => createEnv('todolist')); - // .. - - apiRouter.use('/proxy', await proxy(proxyEnv)); - apiRouter.use('/search', await search(searchEnv)); - apiRouter.use('/permission', await permission(permissionEnv)); - /* highlight-add-next-line */ - apiRouter.use('/todolist', await todoList(todoListEnv)); - // Add backends ABOVE this line; this 404 handler is the catch-all fallback - apiRouter.use(notFoundHandler()); - // .. - } + import { createBackend } from '@backstage/backend-defaults'; + //... + const backend = createBackend(); + //... + /* highlight-add-start */ + // Installing the permission plugin + backend.add(import('@backstage/plugin-permission-backend/alpha')); + // Installing the allow all permission policy module + backend.add( + import('@backstage/plugin-permission-backend-module-allow-all-policy'), + ); + // Installing the todolist plugin + backend.add(import('@internal/plugin-todo-list-backend')); + /* highlight-add-end */ + //... + backend.start(); ``` Apply the following changes to `packages/app/src/App.tsx`: @@ -99,13 +76,11 @@ The source code is available here: ```tsx title="packages/app/src/App.tsx" /* highlight-add-next-line */ import { TodoListPage } from '@internal/plugin-todo-list'; + //... const routes = ( - }> - {searchPage} - - } /> + {/* ... */} {/* highlight-add-next-line */} } /> {/* ... */} From 4baad345f1d90a270f2c1f724f5c69998cfc98b3 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Thu, 29 Aug 2024 16:08:24 +0200 Subject: [PATCH 053/317] Added changeset Signed-off-by: Jasper Boeijenga --- .changeset/funny-rocks-pull.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/funny-rocks-pull.md diff --git a/.changeset/funny-rocks-pull.md b/.changeset/funny-rocks-pull.md new file mode 100644 index 0000000000..7fc6ea7fa3 --- /dev/null +++ b/.changeset/funny-rocks-pull.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-react': minor +'@backstage/plugin-scaffolder': minor +--- + +Added support for omitExtraData and liveOmit for rjsf in the scaffolder From 474a568987b29216fe26dbb726ebab53e29fb967 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Thu, 29 Aug 2024 16:10:26 +0200 Subject: [PATCH 054/317] Update funny-rocks-pull.md Signed-off-by: Jasper Boeijenga --- .changeset/funny-rocks-pull.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/funny-rocks-pull.md b/.changeset/funny-rocks-pull.md index 7fc6ea7fa3..9145464137 100644 --- a/.changeset/funny-rocks-pull.md +++ b/.changeset/funny-rocks-pull.md @@ -3,4 +3,4 @@ '@backstage/plugin-scaffolder': minor --- -Added support for omitExtraData and liveOmit for rjsf in the scaffolder +Added support for `omitExtraData` and `liveOmit` for rjsf in the scaffolder From 5d05ab03d073fe1bd50ae89cefc39fa1ca7b5e92 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 29 Aug 2024 14:30:42 +0200 Subject: [PATCH 055/317] microsite: add versioned docs config Signed-off-by: Patrik Oldsberg --- .gitignore | 3 ++ docs/.gitignore | 2 - microsite/docusaurus.config.ts | 20 ++++++++++ microsite/sidebars.json | 70 +++++++++++++++++----------------- 4 files changed, 58 insertions(+), 37 deletions(-) delete mode 100644 docs/.gitignore diff --git a/.gitignore b/.gitignore index 6db4bf549c..865918c1f7 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,9 @@ coverage # Bower dependency directory (https://bower.io/) bower_components +# Documentation reference, generated by build:api-docs +docs/reference + # node-waf configuration .lock-wscript diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 90b1e2c7ef..0000000000 --- a/docs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# This is generated by build:api-docs in the root -reference diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index a6909f139f..5eef7d0061 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -57,6 +57,22 @@ const config: Config = { editUrl: 'https://github.com/backstage/backstage/edit/master/docs/', path: '../docs', sidebarPath: 'sidebars.json', + includeCurrentVersion: true, + lastVersion: 'stable', + versions: { + stable: { + label: 'Stable', + path: '/', + banner: 'none', + badge: false, + }, + current: { + label: 'Next', + path: '/next', + banner: 'unreleased', + badge: true, + }, + }, }, blog: { path: 'blog', @@ -259,6 +275,10 @@ const config: Config = { label: 'Community', position: 'left', }, + { + type: 'docsVersionDropdown', + position: 'right', + }, ], }, image: 'img/sharing-opengraph.png', diff --git a/microsite/sidebars.json b/microsite/sidebars.json index baca4f4db6..2aef951e01 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -1,39 +1,4 @@ { - "releases": { - "Release Notes": [ - "releases/v1.30.0", - "releases/v1.29.0", - "releases/v1.28.0", - "releases/v1.27.0", - "releases/v1.26.0", - "releases/v1.25.0", - "releases/v1.24.0", - "releases/v1.23.0", - "releases/v1.22.0", - "releases/v1.21.0", - "releases/v1.20.0", - "releases/v1.19.0", - "releases/v1.18.0", - "releases/v1.17.0", - "releases/v1.16.0", - "releases/v1.15.0", - "releases/v1.14.0", - "releases/v1.13.0", - "releases/v1.12.0", - "releases/v1.11.0", - "releases/v1.10.0", - "releases/v1.9.0", - "releases/v1.8.0", - "releases/v1.7.0", - "releases/v1.6.0", - "releases/v1.5.0", - "releases/v1.4.0", - "releases/v1.3.0", - "releases/v1.2.0", - "releases/v1.1.0", - "releases/v1.0.0" - ] - }, "docs": { "Overview": [ "overview/what-is-backstage", @@ -555,5 +520,40 @@ "contribute/project-structure" ], "References": ["references/glossary"] + }, + "releases": { + "Release Notes": [ + "releases/v1.30.0", + "releases/v1.29.0", + "releases/v1.28.0", + "releases/v1.27.0", + "releases/v1.26.0", + "releases/v1.25.0", + "releases/v1.24.0", + "releases/v1.23.0", + "releases/v1.22.0", + "releases/v1.21.0", + "releases/v1.20.0", + "releases/v1.19.0", + "releases/v1.18.0", + "releases/v1.17.0", + "releases/v1.16.0", + "releases/v1.15.0", + "releases/v1.14.0", + "releases/v1.13.0", + "releases/v1.12.0", + "releases/v1.11.0", + "releases/v1.10.0", + "releases/v1.9.0", + "releases/v1.8.0", + "releases/v1.7.0", + "releases/v1.6.0", + "releases/v1.5.0", + "releases/v1.4.0", + "releases/v1.3.0", + "releases/v1.2.0", + "releases/v1.1.0", + "releases/v1.0.0" + ] } } From ead2c11ba57edd9cfe0d5807026ebd4ed538b546 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 29 Aug 2024 16:27:42 +0200 Subject: [PATCH 056/317] backend-test-utils: add test to make sure startTestBackend forwards errors Signed-off-by: Patrik Oldsberg --- .../src/next/wiring/TestBackend.test.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts index 7bf3887c93..b0ef9802f9 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -334,4 +334,47 @@ describe('TestBackend', () => { "Unable to determine the plugin ID of extension point(s) 'a'. Tested extension points must be depended on by one or more tested modules.", ); }); + + it('should forward errors from plugins', async () => { + await expect( + startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + throw new Error('nah'); + }, + }); + }, + }), + ], + }), + ).rejects.toThrow("Plugin 'test' startup failed; caused by Error: nah"); + }); + + it('should forward errors from modules', async () => { + await expect( + startTestBackend({ + features: [ + createBackendModule({ + pluginId: 'test', + moduleId: 'tester', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + throw new Error('nah'); + }, + }); + }, + }), + ], + }), + ).rejects.toThrow( + "Module 'tester' for plugin 'test' startup failed; caused by Error: nah", + ); + }); }); From fb79ba76dcb9281e1b9a968c0b57d223d2b8a665 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 29 Aug 2024 17:00:47 +0200 Subject: [PATCH 057/317] Update docs/permissions/plugin-authors/01-setup--old.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Camila Belo --- docs/permissions/plugin-authors/01-setup--old.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/permissions/plugin-authors/01-setup--old.md b/docs/permissions/plugin-authors/01-setup--old.md index 3e681c2cb3..34802d004f 100644 --- a/docs/permissions/plugin-authors/01-setup--old.md +++ b/docs/permissions/plugin-authors/01-setup--old.md @@ -1,5 +1,5 @@ --- -id: 01-setup +id: 01-setup--old title: 1. Tutorial setup description: How to get started with the permission framework as a plugin author --- From 8ce7dff0b0c10fca8186238146c62d70212e3f66 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 29 Aug 2024 17:05:12 +0200 Subject: [PATCH 058/317] refactor: apply review suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Camila Belo --- .../integrating-search-into-plugins--old.md | 2 +- .../integrating-search-into-plugins.md | 36 ++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/docs/plugins/integrating-search-into-plugins--old.md b/docs/plugins/integrating-search-into-plugins--old.md index caa6179606..2729dd3d21 100644 --- a/docs/plugins/integrating-search-into-plugins--old.md +++ b/docs/plugins/integrating-search-into-plugins--old.md @@ -1,5 +1,5 @@ --- -id: integrating-search-into-plugins +id: integrating-search-into-plugins--old title: Integrating Search into a plugin description: How to integrate Search into a Backstage plugin --- diff --git a/docs/plugins/integrating-search-into-plugins.md b/docs/plugins/integrating-search-into-plugins.md index f02c596716..62b3942537 100644 --- a/docs/plugins/integrating-search-into-plugins.md +++ b/docs/plugins/integrating-search-into-plugins.md @@ -30,11 +30,11 @@ The search platform provides an interface (`DocumentCollatorFactory` from packag #### 1. Create a collator module package -In order to add a FAQ collator to the Backstage index registry, we have to create a [plugin module](https://backstage.io/docs/backend-system/building-plugins-and-modules/index#modules) and the best way to do this is to create it in a separate package, e.g., `plugins/search-backend-module-faq-snippets-collator`, using the `yarn new` command: +In order to add an FAQ collator to the Backstage index registry we have to create a [plugin module](https://backstage.io/docs/backend-system/building-plugins-and-modules/index#modules), and the best way to do this is to create it in a separate package, e.g., `plugins/search-backend-module-faq-snippets-collator`, using the `yarn new` command: 1. Access your Backstage project root directory and run `yarn new`; 2. When asked about what do you want to create, please select `backend-module` and hit enter; -3. Inform `search` as the plugin id and `faq-snippets-collator` as module id; +3. Input `search` as the plugin ID and `faq-snippets-collator` as the module ID; 4. A `search-backend-module-faq-snippets-collator` folder should have been created in your project's "plugins" directory. #### 2. Install the collator dependencies @@ -46,7 +46,7 @@ We will use some libraries in the module, so let's add them to your plugin modul git checkout -b tutorials/new-faq-snippets-collator # Install the package containing the interface -yarn workspace @internal/backstage-plugin-search-backend-module-faq-snippets-collator add node-fetch @backstage/config @backstage/plugin-search-common @backstage/plugin-search-backend-node +yarn workspace @internal/backstage-plugin-search-backend-module-faq-snippets-collator add node-fetch @backstage/plugin-search-common @backstage/plugin-search-backend-node ``` #### 3. Use Backstage App configuration @@ -60,14 +60,16 @@ faq: It is optional to define a schedule for the collator to run, or else it defaults to the value in the collator factory code (See [5. Implement the collator factory](#5-implement-the-collator-factory)): -```diff +```yaml faq: baseUrl: https://backstage.example.biz/faq-snippets -+ schedule: -+ # supports cron, ISO duration, "human duration" as used in code -+ frequency: { minutes: 30 } -+ # supports ISO duration, "human duration" as used in code -+ timeout: { minutes: 3 } + /* highlight-add-start */ + schedule: + # supports cron, ISO duration, "human duration" as used in code + frequency: { minutes: 30 } + # supports ISO duration, "human duration" as used in code + timeout: { minutes: 3 } + /* highlight-add-end */ ``` #### 4. Define the collator document type @@ -107,8 +109,10 @@ Below we provide an example implementation of how the FAQ collator factory could import fetch from 'node-fetch'; import { Readable } from 'stream'; -import { Config } from '@backstage/config'; -import { LoggerService } from '@backstage/backend-plugin-api'; +import { + LoggerService, + RootConfigService, +} from '@backstage/backend-plugin-api'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; import { FaqSnippetDocument } from './types'; @@ -126,7 +130,7 @@ export class FaqSnippetsCollatorFactory implements DocumentCollatorFactory { } static fromConfig( - config: Config, + config: RootConfigService, options: { logger: LoggerService; }, @@ -205,7 +209,7 @@ export const searchFaqSnippetsCollatorModule = createBackendModule({ In the fragment above, the module is registered, and when the Backstage backend initializes it, it adds the FAQ Snippets collator to the search index registry. Now let's export the module as default from the `index.ts` file: ```ts title='plugins/search-backend-module-faq-snippets-collator/src/index.ts' -export { export const searchFaqSnippetsCollatorModule as default } from './module'; +export { searchFaqSnippetsCollatorModule as default } from './module'; ``` #### 7. Install the collator module @@ -213,7 +217,7 @@ export { export const searchFaqSnippetsCollatorModule as default } from './modul The newly created module should be added to the backend package dependencies as follows: ```sh -yarn --cwd backend add @internal/backstage-plugin-search-backend-module-faq-snippets-collator +yarn --cwd packages/backend add @internal/backstage-plugin-search-backend-module-faq-snippets-collator ``` After that, install the module on your Backstage backend instance: @@ -240,7 +244,7 @@ To verify your implementation works as expected make sure to add tests for it. F Look at [DefaultTechDocsCollatorFactory test](https://github.com/backstage/backstage/blob/de294ce5c410c9eb56da6870a1fab795268f60e3/plugins/techdocs-backend/src/search/DefaultTechDocsCollatorFactory.test.ts), for an example. -You can also check out the documentation on [how to test Backstage plugin modules](https://backstage.io/docs/backend-system/building-plugins-and-modules/testing). +You can also check out the documentation on [how to test Backstage plugin modules](../backend-system/building-plugins-and-modules/02-testing.md). #### 9. Running the collator locally @@ -249,7 +253,7 @@ Run `yarn dev` in the root folder of your Backstage project and look for logs li ```sh [backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Task worker starting: search_index_faq_snippets, {"version":2,"cadence":"PT10M","initialDelayDuration":"PT3S","timeoutAfterDuration":"PT15M"} task=search_index_faq_snippets [backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Collating documents for faq-snippets via FaqSnippetsCollatorFactory documentType=faq-snippets -[backend]: 2024-08-28T07:00:50.520Z search info Fetching faq snippets from https://backstage.example.biz/faq-snippets +[backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Fetching faq snippets from https://backstage.example.biz/faq-snippets [backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Collating documents for faq-snippets succeeded documentType=faq-snippets ``` From 88467142509e91a4574ae6a4924691651bfc0d73 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 29 Aug 2024 21:43:56 +0530 Subject: [PATCH 059/317] Show additional info on DevTools unprocessed entities table * Location path (so that it's easier to search the failed entity from the YAML URL) * Time info of last discovery and next refresh time so that users can be aware of it and can sort the errors based on the time. Signed-off-by: Himanshu Mishra --- .../catalog-unprocessed-entities/package.json | 1 + .../src/components/FailedEntities.tsx | 47 ++++++++++++++++++- yarn.lock | 10 +++- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index 8ce8c8b0d3..1b571e344f 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -45,6 +45,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.60", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", + "luxon": "^3.5.0", "react-use": "^17.2.4" }, "devDependencies": { diff --git a/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx b/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx index 5d1800cb29..65e6eb731e 100644 --- a/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx +++ b/plugins/catalog-unprocessed-entities/src/components/FailedEntities.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import React, { useState } from 'react'; - +import { DateTime } from 'luxon'; import { ErrorPanel, MarkdownContent, @@ -88,6 +88,24 @@ const RenderErrorContext = ({ return null; }; +/** + * Converts input datetime which lacks timezone info into user's local time so that they can + * easily understand the times. + */ +const convertTimeToLocalTimezone = (strDateTime: string | Date) => { + const dateTime = DateTime.fromFormat( + strDateTime.toLocaleString(), + 'yyyy-MM-dd hh:mm:ss', + { + zone: 'UTC', + }, + ); + + const dateTimeLocalTz = dateTime.setZone(DateTime.local().zoneName); + + return dateTimeLocalTz.toFormat('yyyy-MM-dd hh:mm:ss ZZZZ'); +}; + export const FailedEntities = () => { const classes = useStyles(); const unprocessedApi = useApi(catalogUnprocessedEntitiesApiRef); @@ -140,6 +158,13 @@ export const FailedEntities = () => { render: (rowData: UnprocessedEntity | {}) => (rowData as UnprocessedEntity).entity_ref, }, + { + title: Location Path, + sorting: true, + field: 'location_key', + render: (rowData: UnprocessedEntity | {}) => + (rowData as UnprocessedEntity).location_key, + }, { title: Kind, sorting: true, @@ -156,7 +181,25 @@ export const FailedEntities = () => { 'unknown', }, { - title: Raw, + title: Last Discovery At, + sorting: true, + field: 'last_discovery_at', + render: (rowData: UnprocessedEntity | {}) => + convertTimeToLocalTimezone( + (rowData as UnprocessedEntity).last_discovery_at, + ) || 'unknown', + }, + { + title: Next Refresh At, + sorting: true, + field: 'next_update_at', + render: (rowData: UnprocessedEntity | {}) => + convertTimeToLocalTimezone( + (rowData as UnprocessedEntity).next_update_at, + ) || 'unknown', + }, + { + title: Raw Entity Definition, sorting: false, render: (rowData: UnprocessedEntity | {}) => ( diff --git a/yarn.lock b/yarn.lock index c14251269c..13f06feba9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5953,6 +5953,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^15.0.0 "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 + luxon: ^3.5.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -33393,7 +33394,14 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.0.0, luxon@npm:^3.2.1, luxon@npm:^3.4.3, luxon@npm:~3.4.0": +"luxon@npm:^3.0.0, luxon@npm:^3.2.1, luxon@npm:^3.4.3, luxon@npm:^3.5.0": + version: 3.5.0 + resolution: "luxon@npm:3.5.0" + checksum: f290fe5788c8e51e748744f05092160d4be12150dca70f9fadc0d233e53d60ce86acd82e7d909a114730a136a77e56f0d3ebac6141bbb82fd310969a4704825b + languageName: node + linkType: hard + +"luxon@npm:~3.4.0": version: 3.4.4 resolution: "luxon@npm:3.4.4" checksum: 36c1f99c4796ee4bfddf7dc94fa87815add43ebc44c8934c924946260a58512f0fd2743a629302885df7f35ccbd2d13f178c15df046d0e3b6eb71db178f1c60c From 4f08c85431151c061a1acec78195aaa7f17430cb Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 29 Aug 2024 21:45:00 +0530 Subject: [PATCH 060/317] add changeset Signed-off-by: Himanshu Mishra --- .changeset/smart-gifts-report.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/smart-gifts-report.md diff --git a/.changeset/smart-gifts-report.md b/.changeset/smart-gifts-report.md new file mode 100644 index 0000000000..fa3c8d96e9 --- /dev/null +++ b/.changeset/smart-gifts-report.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities': patch +--- + +Show additional info on DevTools unprocessed entities table + +- Location path (so that it's easier to search the failed entity from the YAML URL) +- Time info of last discovery and next refresh time so that users can be aware of it and can sort the errors based on the time. From 716a19bf4fe8d40ddc059699640d5585253e858f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 29 Aug 2024 23:17:22 +0200 Subject: [PATCH 061/317] microsite: conditional versioned docs config Signed-off-by: Patrik Oldsberg --- microsite/docusaurus.config.ts | 50 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index 5eef7d0061..c6ceb21085 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -24,6 +24,8 @@ import { Config } from '@docusaurus/types'; const backstageTheme = themes.vsDark; backstageTheme.plain.backgroundColor = '#232323'; +const useVersionedDocs = require('fs').existsSync('versions.json'); + const config: Config = { title: 'Backstage Software Catalog and Developer Platform', tagline: 'An open source framework for building developer portals', @@ -57,22 +59,26 @@ const config: Config = { editUrl: 'https://github.com/backstage/backstage/edit/master/docs/', path: '../docs', sidebarPath: 'sidebars.json', - includeCurrentVersion: true, - lastVersion: 'stable', - versions: { - stable: { - label: 'Stable', - path: '/', - banner: 'none', - badge: false, - }, - current: { - label: 'Next', - path: '/next', - banner: 'unreleased', - badge: true, - }, - }, + ...(useVersionedDocs + ? { + includeCurrentVersion: true, + lastVersion: 'stable', + versions: { + stable: { + label: 'Stable', + path: '/', + banner: 'none', + badge: false, + }, + current: { + label: 'Next', + path: '/next', + banner: 'unreleased', + badge: true, + }, + }, + } + : undefined), }, blog: { path: 'blog', @@ -275,10 +281,14 @@ const config: Config = { label: 'Community', position: 'left', }, - { - type: 'docsVersionDropdown', - position: 'right', - }, + ...(useVersionedDocs + ? [ + { + type: 'docsVersionDropdown', + position: 'right' as const, + }, + ] + : []), ], }, image: 'img/sharing-opengraph.png', From 8eb75423a54a1f2fcb05c60b32bb7db2c1d37cd8 Mon Sep 17 00:00:00 2001 From: Efren Aguilar Date: Thu, 29 Aug 2024 15:13:43 -0700 Subject: [PATCH 062/317] Add custom prop functionality Signed-off-by: Efren Aguilar --- .../src/actions/github.test.ts | 57 +++++++++++++++++++ .../src/actions/github.ts | 4 ++ .../src/actions/githubRepoCreate.test.ts | 57 +++++++++++++++++++ .../src/actions/githubRepoCreate.ts | 4 ++ .../src/actions/helpers.ts | 3 + .../src/actions/inputProperties.ts | 8 +++ 6 files changed, 133 insertions(+) diff --git a/plugins/scaffolder-backend-module-github/src/actions/github.test.ts b/plugins/scaffolder-backend-module-github/src/actions/github.test.ts index 31ec7ecc90..3af922eb4f 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/github.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/github.test.ts @@ -287,6 +287,36 @@ describe('publish:github', () => { has_projects: false, has_issues: false, }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + customProperties: { + foo: 'bar', + foo2: 'bar2', + }, + }, + }); + + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + org: 'owner', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', + squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_merge_commit: true, + allow_rebase_merge: true, + allow_auto_merge: false, + visibility: 'private', + custom_properties: { + foo: 'bar', + foo2: 'bar2', + }, + }); }); it('should call the githubApis with the correct values for createForAuthenticatedUser', async () => { @@ -412,6 +442,33 @@ describe('publish:github', () => { has_projects: false, has_issues: false, }); + + // Custom properties on user repos should be ignored + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + customProperties: { + foo: 'bar', + foo2: 'bar2', + }, + }, + }); + + expect( + mockOctokit.rest.repos.createForAuthenticatedUser, + ).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', + squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_merge_commit: true, + allow_rebase_merge: true, + allow_auto_merge: false, + }); }); it('should call initRepoAndPush with the correct values', async () => { diff --git a/plugins/scaffolder-backend-module-github/src/actions/github.ts b/plugins/scaffolder-backend-module-github/src/actions/github.ts index c8b22de7e7..982c68d80f 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/github.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/github.ts @@ -115,6 +115,7 @@ export function createPublishGithubAction(options: { includeClaimKeys?: string[]; }; requiredCommitSigning?: boolean; + customProperties?: { [key: string]: string }; }>({ id: 'publish:github', description: @@ -164,6 +165,7 @@ export function createPublishGithubAction(options: { secrets: inputProps.secrets, oidcCustomization: inputProps.oidcCustomization, requiredCommitSigning: inputProps.requiredCommitSigning, + customProperties: inputProps.customProperties, }, }, output: { @@ -213,6 +215,7 @@ export function createPublishGithubAction(options: { secrets, oidcCustomization, token: providedToken, + customProperties, requiredCommitSigning = false, } = ctx.input; @@ -253,6 +256,7 @@ export function createPublishGithubAction(options: { repoVariables, secrets, oidcCustomization, + customProperties, ctx.logger, ); diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts index 1750acdb9a..66d54366c5 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.test.ts @@ -235,6 +235,36 @@ describe('github:repo:create', () => { has_projects: false, has_issues: false, }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + customProperties: { + foo: 'bar', + foo2: 'bar2', + }, + }, + }); + + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + org: 'owner', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', + squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_merge_commit: true, + allow_rebase_merge: true, + allow_auto_merge: false, + visibility: 'private', + custom_properties: { + foo: 'bar', + foo2: 'bar2', + }, + }); }); it('should call the githubApis with the correct values for createForAuthenticatedUser', async () => { @@ -360,6 +390,33 @@ describe('github:repo:create', () => { has_projects: false, has_issues: false, }); + + // Custom properties on user repos should be ignored + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + customProperties: { + foo: 'bar', + foo2: 'bar2', + }, + }, + }); + + expect( + mockOctokit.rest.repos.createForAuthenticatedUser, + ).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + squash_merge_commit_title: 'COMMIT_OR_PR_TITLE', + squash_merge_commit_message: 'COMMIT_MESSAGES', + allow_merge_commit: true, + allow_rebase_merge: true, + allow_auto_merge: false, + }); }); it('should add access for the team when it starts with the owner', async () => { diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts index 52bf9be9c0..1c3d035ee3 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubRepoCreate.ts @@ -100,6 +100,7 @@ export function createGithubRepoCreateAction(options: { includeClaimKeys?: string[]; }; requireCommitSigning?: boolean; + customProperties?: { [key: string]: string }; }>({ id: 'github:repo:create', description: 'Creates a GitHub repository.', @@ -139,6 +140,7 @@ export function createGithubRepoCreateAction(options: { secrets: inputProps.secrets, oidcCustomization: inputProps.oidcCustomization, requiredCommitSigning: inputProps.requiredCommitSigning, + customProperties: inputProps.customProperties, }, }, output: { @@ -171,6 +173,7 @@ export function createGithubRepoCreateAction(options: { repoVariables, secrets, oidcCustomization, + customProperties, token: providedToken, } = ctx.input; @@ -211,6 +214,7 @@ export function createGithubRepoCreateAction(options: { repoVariables, secrets, oidcCustomization, + customProperties, ctx.logger, ); diff --git a/plugins/scaffolder-backend-module-github/src/actions/helpers.ts b/plugins/scaffolder-backend-module-github/src/actions/helpers.ts index eec6f73f92..0ae1cad3fb 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/helpers.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/helpers.ts @@ -148,6 +148,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics( includeClaimKeys?: string[]; } | undefined, + customProperties: { [key: string]: string } | undefined, logger: LoggerService, ) { // eslint-disable-next-line testing-library/no-await-sync-queries @@ -179,6 +180,8 @@ export async function createGithubRepoWithCollaboratorsAndTopics( has_projects: hasProjects, has_wiki: hasWiki, has_issues: hasIssues, + // Custom properties only available on org repos + custom_properties: customProperties, }) : client.rest.repos.createForAuthenticatedUser({ name: repo, diff --git a/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts b/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts index f6fc592b72..4a52c1d69f 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/inputProperties.ts @@ -304,6 +304,13 @@ const oidcCustomization = { }, }; +const customProperties = { + title: 'Custom Repository Properties', + description: + 'Custom properties to be added to the repository (note, this only works for organization repositories)', + type: 'object', +}; + export { access }; export { allowMergeCommit }; export { allowRebaseMerge }; @@ -342,3 +349,4 @@ export { requiredCommitSigning }; export { repoVariables }; export { secrets }; export { oidcCustomization }; +export { customProperties }; From 22a19e687301f9c28271e15a238cb33689b035cc Mon Sep 17 00:00:00 2001 From: Efren Aguilar Date: Thu, 29 Aug 2024 15:25:11 -0700 Subject: [PATCH 063/317] Add changeset Signed-off-by: Efren Aguilar --- .changeset/plenty-hounds-poke.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/plenty-hounds-poke.md diff --git a/.changeset/plenty-hounds-poke.md b/.changeset/plenty-hounds-poke.md new file mode 100644 index 0000000000..3d308d763a --- /dev/null +++ b/.changeset/plenty-hounds-poke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-github': patch +--- + +Added the ability for the actions `publish:github` and `github:repo:create` to take inputs for 'custom properties' for organization repositories. From 2a6142234f131adbc90258b0aa041e8e5455c9f2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 30 Aug 2024 00:42:37 +0200 Subject: [PATCH 064/317] frontend-plugin-api: no longer require factory for extension overrides Signed-off-by: Patrik Oldsberg --- .changeset/breezy-guests-scream.md | 5 +++ .changeset/three-socks-call.md | 5 +++ packages/frontend-plugin-api/api-report.md | 2 +- .../src/wiring/createExtension.test.ts | 35 +++++++++++++++++++ .../src/wiring/createExtension.ts | 2 +- .../src/app/createExtensionTester.tsx | 16 ++++++--- 6 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 .changeset/breezy-guests-scream.md create mode 100644 .changeset/three-socks-call.md diff --git a/.changeset/breezy-guests-scream.md b/.changeset/breezy-guests-scream.md new file mode 100644 index 0000000000..e33344f992 --- /dev/null +++ b/.changeset/breezy-guests-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-test-utils': patch +--- + +The extension tester will no longer unconditionally enable any additional extensions that have been added. diff --git a/.changeset/three-socks-call.md b/.changeset/three-socks-call.md new file mode 100644 index 0000000000..39b75f2fea --- /dev/null +++ b/.changeset/three-socks-call.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +The `factory` option is no longer required when overriding an extension. diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index b42efddffa..e694f1d8c8 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -1108,7 +1108,7 @@ export type ExtensionDefinition< string}' is already defined in parent schema`; }; }; - factory( + factory?( originalFactory: (context?: { config?: T['config']; inputs?: ResolveInputValueOverrides>; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 68478a756e..ee79c277d0 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -705,6 +705,41 @@ describe('createExtension', () => { ).toBe('foo-hello-override-world'); }); + it('should be able to disable extension with override', () => { + const subject = createExtension({ + name: 'root', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + input: createExtensionInput([stringDataRef], { + singleton: true, + optional: true, + }), + }, + output: [stringDataRef.optional()], + factory({ inputs }) { + return inputs.input ?? []; + }, + }); + + const attached = createExtension({ + attachTo: { id: 'root', input: 'input' }, + output: [stringDataRef], + factory() { + return [stringDataRef('test')]; + }, + }); + + expect( + createExtensionTester(subject).add(attached).get(stringDataRef), + ).toBe('test'); + + expect( + createExtensionTester(subject) + .add(attached.override({ disabled: true })) + .get(stringDataRef), + ).toBe(undefined); + }); + it('should be able to override input values', () => { const outputRef = createExtensionDataRef().with({ id: 'output', diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 445bbd8a68..1dcc2fef6f 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -191,7 +191,7 @@ export type ExtensionDefinition< string}' is already defined in parent schema`; }; }; - factory( + factory?( originalFactory: (context?: { config?: T['config']; inputs?: ResolveInputValueOverrides>; diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.tsx index f9c1b3cb89..232b41b435 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.tsx @@ -213,11 +213,17 @@ export class ExtensionTester { const [subject, ...rest] = this.#extensions; const extensionsConfig: JsonArray = [ - ...rest.map(extension => ({ - [extension.id]: { - config: extension.config, - }, - })), + ...rest.flatMap(extension => + extension.config + ? [ + { + [extension.id]: { + config: extension.config, + }, + }, + ] + : [], + ), { [subject.id]: { config: subject.config, From 76c94be56a9d1d9f6c9f8e5a8645c1bae6e95670 Mon Sep 17 00:00:00 2001 From: Efren Aguilar Date: Thu, 29 Aug 2024 16:06:43 -0700 Subject: [PATCH 065/317] Add API report file Signed-off-by: Efren Aguilar --- plugins/scaffolder-backend-module-github/api-report.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/scaffolder-backend-module-github/api-report.md b/plugins/scaffolder-backend-module-github/api-report.md index 1fec7f0422..ec9cb2e31a 100644 --- a/plugins/scaffolder-backend-module-github/api-report.md +++ b/plugins/scaffolder-backend-module-github/api-report.md @@ -230,6 +230,11 @@ export function createGithubRepoCreateAction(options: { } | undefined; requireCommitSigning?: boolean | undefined; + customProperties?: + | { + [key: string]: string; + } + | undefined; }, JsonObject >; @@ -385,6 +390,11 @@ export function createPublishGithubAction(options: { } | undefined; requiredCommitSigning?: boolean | undefined; + customProperties?: + | { + [key: string]: string; + } + | undefined; }, JsonObject >; From 74987245fef7f742f4fc95d11d6bf25a9a08dc70 Mon Sep 17 00:00:00 2001 From: zeshanziya Date: Fri, 30 Aug 2024 10:53:55 +0530 Subject: [PATCH 066/317] refactor relevant sections for better readability Signed-off-by: zeshanziya --- .../software-catalog/external-integrations.md | 39 +++---------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 4b0659fc50..f9c41e00b9 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1060,42 +1060,15 @@ backend.start(); ### Writing an Incremental Entity Provider -To create an Incremental Entity Provider, you need to know how to retrieve a single page of the data that you wish to ingest into the Backstage catalog. If the API has pagination and you know how to make a paginated request to that API, you'll be able to implement an Incremental Entity Provider for this API. For more information about compatibility, check out the [requirements](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend-module-incremental-ingestion#requirements) section. +To create an Incremental Entity Provider, you need to know how to retrieve a single page of data from an API with pagination. The `IncrementalEntityProvider` facilitates this by requiring: -Here is the type definition for an Incremental Entity Provider. +- **getProviderName:** A unique name to avoid conflicts with other providers. +- **next:** Fetches a specific page of entities, moving the cursor forward. +- **around:** Handles setup and teardown, wrapping the process that iterates through multiple pages. -```ts -interface IncrementalEntityProvider { - /** - * This name must be unique between all of the entity providers - * operating in the catalog. - */ - getProviderName(): string; - /** - * Return a single page of entities from a specific point in the - * ingestion. - * - * @param context - anything needed in order to fetch a single page. - * @param cursor - a unique value identifying the page to ingest. - * @returns The entities to be ingested, as well as the cursor of - * the next page after this one. - */ - next( - context: TContext, - cursor?: TCursor, - ): Promise>; - /** - * Do any setup and teardown necessary in order to provide the - * context for fetching pages. This should always invoke `burst` in - * order to fetch the individual pages. - * - * @param burst - a function which performs a series of iterations - */ - around(burst: (context: TContext) => Promise): Promise; -} -``` +For more information on compatibility, refer to the [requirements](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend-module-incremental-ingestion#requirements). -For this tutorial, we'll write an Incremental Entity Provider that will call an imaginary API. This imaginary API will return a list of imaginary services. The imaginary API has an imaginary API client with the following interface. +In this tutorial, we'll implement an Incremental Entity Provider that interacts with an imaginary API to fetch a list of imaginary services. ```ts interface MyApiClient { From 6ba96e3a2a8f8bd82a30a9ac62fbd1e7df21b122 Mon Sep 17 00:00:00 2001 From: zeshanziya Date: Fri, 30 Aug 2024 10:56:35 +0530 Subject: [PATCH 067/317] remove image Signed-off-by: zeshanziya --- ...re-catalog-incremental-provider-diagram.png | Bin 325253 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/assets/software-catalog/software-catalog-incremental-provider-diagram.png diff --git a/docs/assets/software-catalog/software-catalog-incremental-provider-diagram.png b/docs/assets/software-catalog/software-catalog-incremental-provider-diagram.png deleted file mode 100644 index 884210b815abc9e34536d4d86eda68e9c9f58045..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325253 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%0D(YyK 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
    lqjm`ID0{)R={}NP0NFByhR?=6Zc$Zd6 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�uRBwrVX1o9A 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 z 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
      t(0QbTnHu`dIExIR* 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

      |2Si+G6K%T2qOP_> z%FC;@(1ZdKjj_UwTh5ZPvLnjLaSGg~F*MP!Qv9UThf`5g8=$Rd*0+G;nrGr7*(des zfLDk9i}fJirMpc|yD?eL{`LM?a!yWlIlg1joyjsy{}(ubJQ0FUPR{V^H@A)_h6rf` zF@Iye?>z_s0pY>~?0sdhu&y5IhB32m|`&Wi3$DS#}>Fa)=%^UPV3*z{@D^dl-3YA#P505 z!S?HaL<*oYM7bkd)WQHU4S>x@AQhLD`FXq%=Bu?PwdI5qcp;*XLgkxb{KY7GNTkC? z9{Cbc1UQ1dE|iJ5HX^gXJcr*8|An4HZt&Xz^JlB5JT0vH4!>bv=mMFFYS1Ul)B5OA@ocI#$WjLfrd&42JxfXpWdjgSF>2C~Pic%SkF zNxoFuej%b$xOLOOiH+!Hp7lkv2^a8Lf5<}=2Djac z5fv0m*GQEJM4y0(XcWJFmMkc9YphQN*r|HdVZ;(3)}(y{s`3Y(s!=s8gZ94^;Dc@? zkaG|@G$bH42HXP(RgM@Tfi$~cV4(P;9Owt+BOTFRKnR-Ky-aq$8vyewTma0wQyFJFJij;v!ppx!8$X@QGp7^18mp^~^ng|23>+r;& zB84DkqGfmJ>((jcXaA#QfPP*Lu3xUp_vpCrWL*kfACfO2F`j(8wr8anU}Y7E7|ts- zqIEqFgF_>ADuyY<7lHv@z`*65zGQ0gODA(EulQa zl8gJLp=~G9t7H9qEmk;NLK_&qDA}0;erRc57ccGfNvdbbdU7>9BEVTA_%HJApnU4| z5;OGqw>lLwr25ExmJoVg+^_&Sj(_(LN9Nxis5NI4dyJJnsbc+n=w{Pnag&|J=zJB< z^WFMx+iSd$;Vqm61@1;WfeZ%lv*`AzUG;7c0^VA4-1ekQ7SKmopWAxDsin(1q?FeIP?zhw>EZhQrEGLSW}!Eze=J*UF?W{3g45Mf+VOu&G#8GLLK&LBzPdG?Gk%U{x>~@h|WQ+r{JZHvS(wYFa*{=tH0h}Lweu9WZ#}g8i z$@4k-_&H?j_nj~KiK2f%mPR*yNX(kVK%$)SaD=ru0JaVjW@}r6I9rpo3vuN;S}3Yj zPcuC+$13Fj=m5OL$O%0eLDXS)o*juFBM@uu48$rf z17TP~AGEAIRXPkLsUb-VU{7o&FqY=(J8RX-P66q3EjO5I=W;s-pjn@#>l)(B$*cuR z@p{7NKA*Nn{_OB@K}a$o%2DX~|IqyjbUHjRR&ONkwDbug(X#MzP}fk**bP1=@kCa= ztZ4cR2w^PAm+5omL~8Z*7tP4pJ^3PzaCr718p(7Zf2(9v1HCuaukj#*SfpHS6gFrX%Rg4cwnwLpMxBnH@{Tpi#MI%BWe35wAla~=Ukr{C2Awb}x1BZ@n!w06| zOWlqUhD5VM`f$ifl+1WL&!AtEW#TIpv}a_1_Xp-)I_2-G|7@>UUaj7wxC;jYCE<%_MV=DWMO(kV`( zHL^tx7RnS0v$e5a`%RLs%NKDSRzi0H$zhj)KXjKkCvf#XmPu+di?E!pSr~58Hh;0; z{*^vfz$h(GzA2I=(^GxNB9)S)$l~NQ?ztg2nY?PIo`U0*_qS&ON><@BMXGbdHQZH) zSuD90HvP;G(rQX>-)HLI#mCvOJ#h4E8z8#JIel*3=jr!(sg>8n>|OUY-!8?Y+N+^- zL6M-bbz;^DzRbBv!6j)FvuwUGxJ00@v6o%%)oY?K2#Wt$rMwkLm+?b8Sex&iXsc8{ zw}~G2An{{IxOFg}q)od0T0Y{{OdvO1Fa!K3SIc*^zOioI9NKc>3V~&`Vu|+Iq@;Li z^7HfGrZzeN)cUSYCOz#qsafDrntG{vFh(eAg?+8_b#EVim-yfpf_plnRr)*QdxYz$ zgf*kRRU6A*bxTjo~bf%nvYMhh3Gjs2b42Jb5vj43>?nD>Hb>(R#{ zap9^-PQVZf=V9~knZU|#_h{OmqT+v|oM+C{M;tgO&byz8**Qc#T@a<;jYIkAm(faL z!+7;>TtvNYyvQ+R-eJ7RE~xUQRp`6M5YCCE>j&@k{Qw>IjfsBv7NnR0#%6Mr+PZS_ z^oJYe>#_ApeMfq#tkhJzhQ2GyJMibQyf!^D^tAEwv+1*A7PAQwGfT_x@|PPETf5;8 zL4%Cqsf);YjKWxOuG7#q0trtBn@X zFRCrWK1T!NQqfhh;W8&-qag`Rt5@BRN(9yjFY^J$5lkGF=3I?8(_%Bfrpc}n_|hrx z*~-ZX%dWGEzq&*!Z4|b1>tkv-tCwz_docRQ@?7}pX)ogmNJ7p0xbECOLG_x3M+WrzUucfyFRbk4mI(AITH7?ZUocC3%GCvx=Zk4h4JK|q44wCSG(But zY15LX*XwvW++n-gjvF}vXJRv|&1zClGnR0PRcd?7!dOhNTxQ;L-wGlP22Cx{TZM(< z-@f@A6q`2TzR`(Az|Ln+=YTgn=JjT|HRcU1ehkX7Sgk)gR_38y9Zp{C)sXSPx0r@*#k8U-`U>q&lN7IeT0)Pm}x=CY+s#dr?Z7Wq=A zasBKFw*(G0x}t-<{akPuaSb70G*Gly;MkP_2FCDZ7-w9ZQ0rw}2-9*`aX)ht?*P22 zm)>rO1itgjMd3aF<5l=DK%HYw1TYG`N~jFs4rCE_`F<3RDzB7ID(4CAsE?5S&OCv1 zH;`9%60_R-wZ*(H_2IisijgI%0OPm)=0^b%u&h);?k)0oKUyfuSFZb+C62-A?r}v| zLd8JZi9jr*rrgEZ#^q~?WZ*lCmu+n~Z>?SKGgicAx2icxKLbq?v6KdA)%Hes4yP$m zgE%lvd2<}e-_oP%non~LT&SB~`js{Y0{m9|jnr%RpTEfWN|#^nAK8w21fE4}fmYq0(KTT+ z|0-7eLkv)SdsL4A?bsyUhw(NODR-RNvWoSs%>m9`^bxk<5_55NjSn`HlOv|ysFhnyN-@7;Vfeqhi#g&=bFJR-`^@X z$ZDqu%H~&S8BfUMzuM*S+M(kS<+Ccn@A1M^TYsP2HhMRFGDi-pUsYn@e5$5?d62Lki z7-9b$4`q4K(5U=Y_1w`)Q)|hUyJ57#GeJSHc%}J=g{9+?xxccThv8dU47zvn! zPNbz{i#`^#`sG(}HxO+{bc8g#`?uH*@x!C_K%a@b>{%&ie}a2RB?ejw-jk(iCW!V? z83gk#x{EKS3i4;qTWnr9r&pa@+8JK9p6rR$_4^v788oUcGN&^&tIe}MkV`*})_Jvo zMd%j(xz-e8Y*M-dDKLW?JS2nFdISsck63Ye2!_jZ!tZ}-p%{(eoOeAg<$Gak;SFTW zZy~#$eAE=qQZEG^a2?PkwS1^q!B|I%FU>F40PEZ8+eiZRN((~6fY}PEpzsglNONW> z4=Z@FAkWVxH{|%yMqg zsg3-{Qul0uuo0O)5u$AX=y)r1vo9hY7JDaNsJ=}Do0~)#zxg?^?0;KNQZ4xGS{vJN z0qtC4zAdJM+aSY9yF^qAcW1WX6fs9AZ5^E;!jmUPH~O-XXg9rZNPpej*hy}#ORD9T z^`l|*EWb&lrR%*TH6f=QxVBfoK*4|}C#h;oKH;ajHg#Fh3#r0aOtu;0%y_^HrSQu><6>#(y z#xhK4(`vPzrUkQ<$W)!xzA9-j`Xv9DJcqmgKY0kKCFJ^3zb(;#`(U{M*Xxryduz4_ z>tJRM#j1hfbB<+x8pRZ@>V;y^j9ga8^2KEO_Fd~l^ndaHv5-6qlHbXb}x`?}U{To!f})&Aljf*4oz_rxhWuHQzcb1G}qYFlp0zjMBj zi+^0j8dcC*uvCr1io>{Eq&;P5lBsRI&R@GSXWH=1TV0DX>c@BPn-61*U$Ece(1c~g zC(~R?KHn9?NlNr~YD_xcRCLY#@TPxq&TP+RmkHCEmu%xSC@NhFeC>mfgA_a>}{9X=hZyUZ{ib!iP(D~d!1pKyN_Fav_ru93tMXj;x zwx3PZ=&1Qc?VGu6C>)#Ru|Mld?}-L9zfbMP5329#cfl4}0JUi~7)~{OT6rpOeET%j zxMmLLWUDLOL6{{>0b6jM-TiQEn&>)7S4m`CR@lt;q=6hOUn!ooF@4m)+7D_^m~Z0~ zIO)o@*y#bQQUK%9uSa2^XMlm^E?_mDw960d(gmDrUDRxBqOVZw376z4fAOu=`&rd# zxF?U}ZhE=rEH$0k)!zOKSilifMmW*NGF-T*o5$VxUO2 ziD36v`t!=-Hp)!LCh3y4(13tqu(6PBrC52l^W9=U zStDMD1ysUuZxU4(KQzDFkSy-RRly82pDQOVz?ZwR?E{>7vex#TkF4>$L zhQzNnUW1ng#>EwNojQ&I0&!$^*t*=sH@xLjPoygutlo&M6w%Sez;9{l$0A)oWy)ldkLheE6u7w7<&;DKHhW_<#%^Xv<$jWok!tGfP0qe{l(ZKtNwk_y99 zX~vyUiN5`GL7hz<`v{=SgsynNp2OE^>9QJ@>8csb;-1;3V9oo~Iv7T)1iyXU#*yQd=4) z#M*f@f9&;HBGxr!Dln|SKChbe#JFeI#keU97N1t9^qrw`N@NCZx@IeQny;l{40~hc_6~|v6`jbgvacr>cHe&r65>Ev7D%6+ zklXTn|AS2%k{`G&kNrhtcXPqK2VB7H)L6undp0%@J3^uu3||LNb(#GFwo~yjb}lJZ z$u!lr>xjMNojW^Oy{Yn1@URFuB_ktxW4@c!I%|@PdXZP}OAWi*eI<28y_o=gRX$xG zbV9q}uX}Tx>tcE46U7QtX*XI;baPZWvDpU;d9f>%%hghlw3%3YkDQ2JT27PO4U1n{pcM?dS!>Om(^sIn29^zf=1&y!>`S>mJzhoWezPJg z&V^Bt&aEQYXtN;J64WMKax` zNBOCiZ68y^>HU_zFs-aMzJ)(()?1a$+^6~?IAS4eM*F~2e)?+Q+H`Ylr_s?)-0_qoyOg;h(E9ia{-g!S7aZ#N?(qQ`?)}CB zi;PKjuFBb70AO@TUoY-{EWLfeSf(R@Zb&BBR$5sxoZ6eI+KAw^EEw5|kLslBj z67iSWueGWIcfjU(++79RRU}~m4eDhkXhzF+%cN0~VcISFjaH>apMF#{lUO`lNl251 zvquTg><@8?oXplnn9ZerQ~}Rz%vy-uCz&0W)eT$>k?_l5oo^Gv;=piNQ^&bA?V!r@ z19d5dzpL<1Kprx>H#`?@QiKJw1s={mkL| zZjUDk*)QZum0sUoj2;eo$0N!MP+LS8@GZWrl~1MGAoS3kXmPkdaL((!Y_l1pDfptm zqvKigm4HVD6I&by(fva7ET=ic(gjStbKFHJ{+W{!jm`5%1B|MTvbSTn2N+ws>#uGz zz@tZ&gg)i0s2pzs;=Mt;G~o%g-ep5AqlUsD_mQH3qs5~!qz7EB(zGZJ4n@Q%HS00! zjF+#!>0Jb>vY3p$>iS|R-B;huXT7Lp#>W`!sk^KyUlNp}=DEHl@k*z(bXuRBqW1z% zS7UL$6G*zyHyy3wbQeE0azM4R&0ep?T>)mgiNCQflGT$oQ=aaW{a({*^Xi4%$I>QR z1t8kkfY5jiTJjqJI^*CUF0{}qFqHGXmnaCoXNyc5-+YG!Z6=d1huI$oA9G`#jF)|F<(C!DfLt2lJOj?J8^|#L0-=~oT;qH z4yidz`m*tp_iD4*)AlKw9B@?!Khk*jiKBM_Y85yKD7*fWNQW5$YltaaVn#?a+z&diAb4GT;B+i3Q*}I(E+0&jU zA(fOi@NNT`BB(L2Uo^rhZm#v#a7F+w0PxD-Jb=pX4w-J9ggv4BL2>SyK)LZ}Nr?UG zZxKhG;k`T-9hLFa0el3La&eh+0sMRX0qe=@XO)MJb6TW(V@~Mz>OTec+t{V@#Aks3 z`&CSSJd1JVbCa?Acul^Dl)3DH#B@0a-1sP56YAOfNzJ@W1BpSNg3d^eEN>^7q}6em zCI-`+WVGkSi}??mD1K1~ucgRJ`OZxqKIy7TyVNY&3Mh zRnHj6=~sNLmtNGma(urZwKJnLi&b|#&oOHsq6Ir*7pFS8t1>w{>(f=PvtZY>n=PH9 zw7@{OvG+pbompUi|3q^+J*>PvLJuQ|z#H9o*YTj!;0y#jHE?()e!HJ^jW<*lN z%zHFm>@@RTW()y6b=?C@0iHY!Q=k?Rq8Shnd`P15r|u>5&)Fqg1tC_`In%0W%Z0j~ zA>%)d5?_19SaNJ17Mi=^3)_fjq@kmrMgeBzDV0CJIgdZlMu6m6>N3GrU_vj4HiJNE z`~RBnAaT@Hx=)&3@9cmb&S;+V`oLhLQW1v2_&>Nk&@H&(*D#NPtXr z?mqfw-y!y(46@$yS5y+%J>*8;COs^9ZHp9;I44AY_VnD_@N2S%ylgS3lLG_Sqg9=z zW|x;3ys6I#i(FPk~sYDGdn!zLy?V; zCGmk$`FN$=mOz=oJmJ37C|Q?)6{K}h7xgGC2#wY)K&$B;hDBUaEe{MOJ1*xN5l72H zebw3<=eDs|gKwy2r$zh6EvI$K!9{d{a>V{pjunX)a-C+jfA8P-1(ELM69?Ii$$FDI zr=au>DPCN^Sn|2#$7d|o8?nU=TH*b!>9*c&ClpF7o$KJ#iEckD)8ffjKYLMY!4i@i=8!$Ip^#>Nr+8U{<^ARIk+ zW9zp#ig_;-fN~=p`a6njVNzv{8VLrEl@aWQBt&smJu1yBjkW~2 zflFoW3OZdP829G6QVgh{W&-o_f&AmkLnF_K=B90n8BKLk`qSs6>kBLuKT}?rV=)wj zM}2pYiF(*H>ps5Lh+W4bWf;DIMq9j!Yl!WtmfezYjABf;RfbK4yuBnVglrm5+U9%r z7bBzckqMN7Ze7^$Pmm?)Axd;4cPWr&&7xUKG*3E40Rge@+y7Bck)}dRBVEI%#wjbI=2VsPlO4`)f(UwjB`b3z1 zvKGfxePWdg=3aRbN9WF72#!*N`~i@}lxa}zD2*r)z5f=Q-L)Zl`6+?$w&pN1azQje zuz0$OR=8RJ(AssTUc4OF%}%sISsVwI8i`|M=j=fwIrQcJyz(q&O*p*Z*KGajaILe5 zRQusivQ<2kO%^3$IKmovpFCkTFq9$ZgNT?QzKn6vogLxSg)QnPeE@( z#>)~yFAq@7#Ge76TF>L&nDu&LkWbq$R}U3Y&Mvf62UA&_dG*{*soE?S^Xh#{rRv&U z?P9uf17hM*YfDP7g(pBfN_3SL+54`jN_m1V3+}(E_XGqjky)RqAjfa}#p8>iarnXG zja@3GUo3SA*5Ek1wEoQp8ZP_xm6Iql!k*x8EjE!&%XjpCyqBRx)wFLl8|W!i!l+ZM zyFNmbewNjiD<9|NNTdmfq(^+vj&ZX;?2y+!A0-o>9LOYl-#$P@E@8JGBN7$YTi}h( z@QTO#I~5QzD>g1&9zW{eC_Kx#-RI;|)K(L}|!hAD}eUJk}y_5inlK zbfE4qU7&0q!Gr*c5hIdd&6P6%$afD-(0V_57mc!8;@cM z*ZM5+yyPtG17o+pc4dsRks~lmHUp^Rs7y@s1g8R7e(qf5sKgUdEETo=n*FLiCN7-e zEDM?Y8+?qa()WjDC=*xZNIFc-9^Zp^B>ewy?Vou9o_u?PyTt)9NGE5fh zF(xfdP3l+FJ%5<}KNUF;nd`h=#41AIvwGNJ^9^TFQN{mZ>#O6U>Yo1Fy3$5fG5>MpC-FrMtVkyZd(+^?7_g-`~so2d{PS-gD;6nR(BgnfEP< z+fK^a)M-LH?cDOqGSP{pbalQO4<9C=i=iS;aOMkLeV$%CML&jhKJ?(A<%&+LDYWdH zM!d78xP#RbD5_{o?m~?vk{U1yMQ9rD|V+j zDXX)zWFM{EsQ{H!r`L`g+6XVUX~>jq{Vb+c_kI$jI7@1i6OxXWNN`om{|M@rXoC;r z<4$P42SE!XniB@f^@#C1JM2%8V2*|b&|FgoY^k*eaQn^}E{UajFUHb&h(qji{j7He zmGL3C)p7-rPIZF%K`&w7kWG&IB-nq4QPHaMIh-PB{g6ZV7nF`CN|a5WaM?U>_hTXY zpi~_mYht>fLnIF6p-BhI z?d%zx1B8QFz%ey8Go_B~Ei8n-^n-_!6u(t8#b>2l3QLoJBmJ$ss=@nuw6-qRCQe+r zg0J-bw)gsJg6Es|scs|fA*fe-|1@$ak(y&Ctp2iLrQ~2Yk2l;~kSlAbEW7CHVn{4I zbID9^7KKr}kkMAgQ>wFr?d5k@Pp{locP`tAt0@$>2-VcmWpriI2bM$l8P zJfVp7Oo@{Jdmdubq0-Uu7bzs5`)-bR2Tz&XNeE?Uk<9yo92uM$ds!~xm7IV8`%=hF zWo5qPX7B_ZW^YMQkITfiQ+RY#%+t%Go8#54*Pw-S>If6*3Oc=Yc)%a-XI`Lt|KW?lB(%Np%MC?aQL{u%x6mH3(-Y$8Id;c?M|Ln^_MXR zdWOsHBA`fbqpHLN{0ZMGP8B$-6yN{4xJrfTb1^7EbMR!zL+leFQ2s#^gKKL4<$Ra| zvz^CN2rVmxd+x_;fLh|AXM;E67r@z1idJWAeSYiN?&0ol&|&r57`FY~d9;eX+*km_ zGordBx;V~t1(Yj{HhM4AyG-pECUVEY)+$YQcI8_)@3x;hH*k=BYJi6fh7j4H!H5}3hr6#^T~N$ZoaBPqiXca*{!9Dqb+dlZ}ngB1Sb?{6;hj7 zEd>=k0(+)xfbrc!jssrPa@C!a>1JOrXnpMXJ5l5C?bu6e#Qg8$5Wvez&gp^UsAh3k zE0qq(S*ioBu48d{PNfy5lxJ#IXgLzqghop_Tgav-!`rPeXUQBm+RtBF^(Sh6Tz=Ce z;-G`Rf8E%;8QlPGh{bkJ4tv(5i>$Tp2yqc1J-vjHUo6L=m&twkpo{EM- zOdnB1JmaO+whq?%LTPAvF{m`^$^D)E_;3-eCwwx1XIg|rE`234_X(R;2-5`tZLg|z zmeD5!kQdBC&UGP45Y71kH3Kf{`&)RfqmBM z&3bTI%vvEurOwpn`~7iXPoOlDGolu98hRFst#xmjo4X&`_V(5Z}d!Ns>g#;>{0e)jEQgG zN9o%yvzO}K+DEgMNXJW%O&_^gn+)b=Q1^p@tWG;kYwSve%5uqKz$2~Y?@dM>EZ?|I z8J>RTtvkQJK2=GAzR+ewV2aE!y)kTmySpZRaOS{<6E9L-q0M5mEkDsfUtRGIfTV>= z+D>w#OT>DrXph;3R2RQfdBqaasoQ#?S7J?%QNHz{Lu|TKI$GA2dJNYM)bFYie5iI3 zs@z@d-Z%d^063!HxixsWeW1QM(TSTAut>7LrxU^eODi``B`MIMtm|}Q0AzPwqkKIr zT7#dKO?_($ojHsX#BrY!SRX%+HlnVYFv4=qcHVn~>bGxHW*R0hI-(`hLQ4bxG~c*n zB}gdcd*wtSZDoEx0$1_Hycf;#B)-+R;;m7-N)c6ux{WOE0d~^5jqQ{odAvtp1v&EH zoj|{$Bu`X(pty^1r&7b`Amw{Vs71#+!LPyVWml}))B~CMihv|^D#5V&ntFo{VrSv(z46+!qmwOAT&>%Y?Xg>yQjpL;auui~9 z`NQbb0?$XQZ2A=eRi7qucG9O>-&Nn>0F@KRB`v&h4wuE@UT6|@@*A0&38gn%Um^-6 z!7Ej-F0IexU(8kj$#{_?%cSa0W7sanuIr>q7XaO#mPs~P)oMhZN}=tFDle4B7bEIj zyuX@Rd4o)?<$<=6`SEZPcLaOt28$8D=nv(NI1=&1_YW)AE0#R^8NXGCsRcBopB zUTe?mY?5h5uZ-uX2LAKmNSO9oA`|#Uhfe#*vqJk##|$&$z>drsnSmsowhwywnp;v!A3p5${LItI zWGDliOZyd#kq&SM5bDBJ7Eh|fSiR~NXyJLF|1nGsAzt!*S4iF{O+?2=P0Lsn&?{oZ zQF#evJg9Gw!PUv%%Ywg^ti5QNVcB@|;G&&?;KpxQs#IsvkD-U0Ui>9+$IKD$9a0brT4rY?D8BN!Xl`33{jK@pf8u}T@lP=r5 z!=}kF(hOy|Eqa6$24D>X!<>R4DJQ=CDdfVel^>VDS}m8-W-2ZvfO=8XB9W5 zgQ-C{s%{gN75jXSH`fF4J30#NmWLapItV8QJlWk9tUx+^HzzaLH@CdUf*%}5uewut zh$MuaC3<2&MH!mv&SwR5X7sUzX+a3M6|K1=ae0iw^>KbQF+hQm2+wbVq3J-9{vy9I zW%`HoKu2HMFp-zA|9sp#Z1$~-3#0L~DXXNX_^~pZL~oZ+s!(`Z4l|lPrQ1%t(P*mL z=%_+WXRl55rxx6if~#3gQcN#T0-?T-=ZkKuzg| zolnNUUQ8vu;@F5Wd)4uZ(O~HPdcjJ+kWQnl>NU8 zd0)w26f^`?}O{WJDmnN4#HU9GNapzQTrIKQY&V5 z24%_z!4;Sq)GRox>?g^Thsq_v7t>X0%M-XZp}0p!gTY$%90g()*s)k~sbU%|0o}Kk z3E9=Rt~MKymFk~rj;`7zUuh3y*nN3rp}X~-u?NZE7dgqF>=VeBkJC>AJ;V7$umIB+ zvHZ3)!O5#D=T<0?^JTXU_7q*_c&HV^?4~CSq%6#Vlm)zL`zv(XzNf8yeSIkXJ-gUD zi>?|(M`ENvrUwwul%g_`4Rd1>DI}}uxQRE8J^UyXAc3g~U$cK>K>OX4ZI(JNPd|RX z;DC2vb+qmzTh!Gc16SLgl47I3u_VX8KtxloQEP`d0Du|gNK1r)5SPgW*UF_*nMA8P;NGNv8o{ z!fJH#NcZLQ0VBz61l&pC3}7kVEzyh zcq2D!PpnNHtfGoL2pJ_7dZQGDdysG|JnMyjpiQ6jb0riz{x#Cg?x*EBy@asO{&-r} z`IPwsota&kIh0~0wQ2Z{QSk4anevnWZU{v{fO0Mh zwXNKYMP>(b_SWnbRx_Qv9XGi;OB`4WB3^Q0Q=_9%DMmn08@dyZE1ZLaeTeuiW#WC% z0MLjuM-qk)5r59nG9zdC6Y=}&qU`UL%9g%9UB)s293CD(xuU>YFRn1Qys%cXz7-Qr z+FT@(3t$_b>&zwJEJ&>PajB~@{GUQKV6$KE2+KeI;uT`HdJ!bl4lRq>;D9;mV)?y-yuZ%F^N0S&x~aJh z?Et-6qy%-q6HJ-{>hxde<|E=#1=jB7%V#Yo;y?%OoE@zAKV1>f<`_J_ z*}3)GD*YM)^eH%S_W)(6`+=zEqNd~T55J&B)|m%sX(T|deiqDR%AB@#c(y#sT4B&Z z19RZSqGN1emp569!-%?|)FgAd)xCB()YasXmLn*A&oYSZ+1sOyK6_69P1a5E&shFv zvd)tA+eGOqQx>z5|Fq2V5(S2YEdKC5pe@vDg$PBhi4Lbv)~J^B@0&ADB19FLUVE+) zy*fYxON3G7-F`aQOa2rVmZcyx6m+-kp~673u=mm%B)D+_SO>S3iwmo!W-dxT3DA#B zit+fjh5IuQ5-+!l2K~ks2^o3$xWM~Uu2&uH%SIafe1m_ujG%9kIoiS+rcZAL@jq>F zjH_2P%8#IvlbfG%CTW>7S`26xiA^q+9$1do*2wTOX@9ypbkb=b&?zmHgDq*BEu85i z@WW^#w!%2#LoYrQM17ptxp|VWoxVMY0AtbI&Dy_gF~4=d7y+hh%WAy5NJ%k3F=F^Ypf%^j?ZCqlly36PO3>LnEPyFD30<~PJ;wGULaJ6^Wz zbGzX*Bzo`*RRL5@-~{XHt|;m+u>KbE#JH<$ey59 zWyjT_DDTRml{SQPt6=WrJDW+P15<}9fe$NN6$dL^yB^$ZC8ooo{y47RfSsqHkgHC- zEh?tngxK27p%=F!qEl8Qjs4V7V&f*?c$v~*Jn3EASBuS36VLd~6p5ITf!q38^{Tf3 z4_H27zE%f%2CI4!#n!+(Q3!hj|IZq7CBVtpZ3i!(P7%LYY-OY9KZ(>@a=YA(z^HL# z3A2EM5~A+8vOOKx>VuJ|{q|*mD!I8KBTy@qa<`0;DCdmMzNB(NTB8A=oQ!ptR_#o) zyEi5>_|xSm8Y)mN{h&9VNph!lm+1U(m*BAjH)7u6?}q9?8W4nc9dzEhC@EGlkJm&a z5p@zq-b+$JJHKu1ns0pw=yWjKF4=sg*6%VK!mpsv{^{MDy$G^ls&9RobafYkvaXGu zevF?QUMU$m(_QwTni){qakY2$TA}Z6%r7O}x^gb<4{Q}@t7U|~H)boc{4o1qX(Yxp z&>i{bEa@%@`(SBbof3XQY%%eik;fc;-r&e(k`tuG<6d; zZy8M-0f&|&Cb}aBit_TLc{S&yI(QzxZiN~XN(Cm+b#!_|GBYw5o#~lHU%V zpBKOm7r=@6Q6M)_q#tDLa@g&HeS-{j1K7(*K0NWP{O>gP5YRCyZv#_wOZ3}i&NpJB z;>p&4snRU@b3|G$XNRm{n>`BtZP~}g=R(nrpF)5N3X<97ZTsee3VeuecND>?rK&}{ zhecT`z@>Qa{%aJ)QfCk2iE1U47t7DyabaJU%UT|O&?%kP|I+WU-EG&My=c+u9|ySH z>+h_%9x#X+jUeZI6ttz3pGZOADpTTEV(cp2OW$n_w9|i7#Eg+p4d6lATpv&l;OgZFMDk7>n4KG_Rp}x(|yvxs^V8NN<6C{HV znnUUQ=#yCbo5Rm5CpbGTi=yJw@IWx>8WfcWCj3SJE|0zVqGaC9||7lFxSDOvI zA@b&B&bs8~MvRoP1&!w0AGtHjL_fr8IA92&OSW|p^(~T3cKUDLPoV!Af^YG8leq*q z1f1siPC7BSbT;3(ROs7l7x&<9`(Hs4HD-ASGUW?OqmKm@pXpd9g&1ze)){5gP6Z#B z9`ra7RC>P~Dl-`5d<7%;F$YWaeRofm8uod?=+uQZe)RK+zoo7YaFi=eW#Gu3K|>b- zq~6PImFk?zs4vfZb$Wk#93t>YhU*WS_UFYe-vKHY`qDyFtFahx!K10@QKhyY{8GPQ)%YKTXrk?yWE2$ZFtO=vQ@PRSPcy! zABdLQ>>HR49Fi^(Fi544kq| z9FJbDPKDZhGiEIDTjbYWxzTr^6H3yT^zKQ;UUd=A`c1Q(9UZu&qy#8SbBMXxOGaG4 z2nRd5S+RV>PM!<|g2NNkwG?0lcuixsVi_7yRbKJLGGnRlR=ddTN}x#hNn8nAoR>8><^z1 z;JGU8HYtUk@l07%4yJvg@eQZ838iSGGtYh>W*grNkOi&VeITfq=Xzh^f5)T=C462E z9Ai_AAf#*dZ2MB|2%Z8-d4NIFncnp?N9;&}-l?f&$5*rxZB(Gi_ah=YP%e$v^ZIq^ z&m@3!P~_D!2qhVo6EqEsE<1dpRqgSZY0}~gA192I6lvqzf8#D~misS4E{mnp*%=jU zEyJ8o&b5WG<=0P8hH_kIKEDGfpae%P9zS_vvArew`_492xQ?|Z8o%og)ycxuL^cX} zN>n(krj}b=)d#oJ<|g*xcSWw)@AzCaIJAz0w>czk9X*Znrsm0aPt-anQhGw^>VjE} zMmbIh)Es&unUe0xhVX%SXIL6vC{rP?bX$!p1>#a;LZJKZ9G)ODUAZ-PRD%vEnHvm# zhXCgaBk3VfxR3WArGjP?n=hwgR`e*fc6v{$*qGu=SvRhe?~+wiID1&DG5<0Tgjonr zar=60Y6&FH#BH~x45-pEWzvb#YAy=o*y8L?q8Qf#-L0F0VEJ;O;u%owOmGcY(gG=K z5d*1py()6zvL>a_Hr4VZD&4{0Gc`b+^(Jw6u0h6eKm>!O;GN2>BUS0%WUzn(d&YF7 zJU>3fSjZ<^-)8Omz#;gxH#G_T{RFf*gYUhV2*>{&eiun!h8BFI+5w_}h)TJ=w|LYF z?oP{bPE{pmD?{-!h(GT@0l}YC$GGr^51;JZ(VCmry;|*gVu8}-Vv|Y1+}f*PV^#bW zfXx}8aC{NGQm%De1Lu6)EiS}wI$N^!6jEwDy>h`r>o^D41fI&T+`ufsP)LME?l2=N zaQ5@vva}`-lXhIL{B+tbCLNau@JRq^p>BrM{}5O)!x2NcXKX|ld2fcKTe{u7r{78q zuYZdw(ivQ#@xRXM7|Bs?|B4o16?PYh!Yr~0sM2Uc!zBz_&s85K6nP zz;Ub9D*r4{ZpDHOKt84@hCu`~vTwk1q}e_1nRLAePg5t9^%h;KN)wzw1wSvwHuTMJlbDP zcwV6FQNgx#CF9z|Um*NXEjBk)QwX03kE_@&cC~n2!}mhEopyMb>ov&6k!m+tl5D~T zN7}U#^$8Ch9bM8l7>mo@{g()9Uxt-R9Q~Y4uJR4S7WU1PHm9}w8Xo9$VJGK!ralGy z5uf05cEdGTc!Qi%k+Caks}2ce5Yf^~)_QIG2#m%XvouCqDT5t@j;V!()$PFzihi5e z$g!%=l843CBLe{_4MMy^c%EQ(02v*c%K+dt*wf0DvyMjA3_xe_XQ1C7sz?_48*uy% z#u&rg{3Aj{LbkPhs~p%1nAkJM)F~3ghn!G0mT4Nq)xayir=L%psAZls;h(UVp@RuT z=i$rq9?zZyy>{Gq8LMhy&qf5ZW5AYAhH!{DbpXS)olXA9mCN)^CTTEx+)C#-x}?9^ z{~S=K#)5cJz#nr#kyhiWfgW@yl)s@w%Qh7KCz>oQ5P`ZWeJT+BW>lNsJ z9e@;@%&0!|{ue1Kp!OlkGZ`GnsgT*44M#oOdMd+vv&1CZv~Pd4r#Vog@?Hr0pM4!Wb*I>^~N&pJ>i~L%GgVWi7 zoe@Tjp`vu_iWB8d&c{S3AlUxU4D0*F3ifA9w<*HGpK$OYz3iK{a4lNt3i0vT4reqq zn;G|*!am(k55ERph{Y zv1hY*C4dKPqIM9F5nKvI1?x*#ET(lATA%x1DV6OiWxgqH061ODZkCFAwJt0v+3lXr zNW`8=!9!P2^dX9P(}A3}_>j_1CsA^FD(R`Nqnml7!2m{a1>#+AjrWi4lJmcH2gr$J z6D2M#FU3TSSbW;%j*Q&P-bi;n56f07n?Wz&q13{9@gVMZj?T@E2m13TKgY@%@%F=? zhAom`kLg#2=LEp9Jszw6K_@!fKLuA{#seTf#(b44*c)}8Spa9QAtf6ng0 zJR?!eNAznRN|JNsS^*;;^EDUAq8Lpazhna|0Hi=Vs@fl}`gvoy|DtUoHg5`S7)ZCH z%Q5(L+?|cZ5f{kW-$IJEXZpM`2o517hVRm<*XI5A0%^*mpBu0Q9`l4?dbC7L%2gwB zvMDNhAP-GJ$*_CsJ799PHwsIhU<(E$PF64wa#iP3I&jFvNpf#5hvA9b?2((LKl z?v6u4=>tmO9vZkxO`DEn0PG)@T$CNaOk9e6{k?Vjc@M`$!iV<>(E6^5&+H%l`0G?8 zkQq)k9TEnPx97*8RmEZwgg_wTQA&Z|IETw*>r9W^4L67KWYw&WB!ueLv6z4^hSkmG zDPQBWE4ix7XkojqOkQsJoiU=>iXBIau3q0W>MbA3%a=1P+;!Wfll&$_>@T=ZBAfkN zKXV5W+@7@(VP`ogg;8jhS(Z6g@ybm1&^e|U&KTCtQ#h->j-ZaT%f=&OBLtg{cEvj`?#h@|JD?jes*7fJUqCYPqi|++X1fltJrHNElS68o3?P&ML^Thi5ZR%z&O%v>16w`$6xy+(gZ z@8Hv2*nFt7f5ky0j>lyemh73Z^ffyzO_}%h>;cDd*CFomNUk!im9Bv2mW44Nz@vH( z$OxN{^=YLzB}QO7yX}pMMVHNycKBIGF3y2HjMIRb8nwwOvtozWU2g|O zOeWfhd7LUsRixxeM`<*6D82Y}QL(t~^HptzKEzKr4+_n?^4lEcJekZeDvP{`#XZflshI6JTALQ?97}PY#P}xhQCRjD z;6O1a%Wnr-aHfkUmDVOF@M|uZ+oR?!qaRa4ET7Jj^7vhrt4!pZmc$?&!{dC#GqG z`I==((7~0fs<=10WfGY8G7#6b$yGb4f%6;1%L%} z;(Y5F@eHg}cs7O&AlXWSGFI;DrtdI@+Vi1^|y?etU)b|l_=h0x9wzs;^YpYjl7J2Kjt&NY1sU0kGR zAJ?-jHG9tU_=(P!`S~Uqnw;G5{b)gYQx|Chak$>sx7F3ai?$l%X84VsLZ}m^{y029 zrcw2J1ls|4(cQR_T%dtB9|k>bzW1>~wCtWaMH;*eW^d2pJkBr9ek(*_GCCu{%v`$@ zppzT-Cx_I=2xr8DWn#No(JSHf_H7LnsSO>SX~}JlO4cJ4R+D_nv^&r^g7y`NWDLX? zKy<4r$Y8Z^frga^$3CgQ7}*g?01ur~g1TTIi7F~YYJv>bS-amd6FS~KDJ=rJP@-%Fbgdmv7HO112 zYGUi{wbaOubH?Az#(SYG1h&~2P*cXh^bPk4F{=sVIIhwG+6o}fKY#@+ zjHth0nQ7br-WpK>Co4#?Jz+s)}W-$}Yg3LEL&Xz!8CuFTstGJ)_@##G;@%A*%Tj9Q38!^1jV0ETRjOx1q%kE0}H02vw?J|9G_0pN-S zsyP$8v>983%^Lc~q;qnQNn#QPO^Q?RgQ+I|V|%vWWa9u*uMe-EV4*VEOrokhX6Jjk zfs^E`b-+nn!yUWP$&w+D7)i6+aA|p5SYDf^A;vc*|RpKP9zWT|Y z?fiD!3E5o4|6m6&*N3Gm92Z?|aTG9R!~0?;6Rh2Gx$fHDB;RA8_L{2UR0LdQ$Fl;XHmgKYa3KNB{KW^p|&Z zvu<2*a}e{-mb|O6R!uPgpEhW?+(!O*H_?XD_~E}JMiG7neNG*lvZA`kwe+IlbA$#zN1AT zyaxwd-H5njb?@cc!roQo>kd@SrO?w83JSJP=uiM}7Bh@UKj6{v1ojD>8fv+YtwNVv zmdUA38E0Ji5Qn<}R@Zn~PrI^Hr)|zbed!IM6g9@>BHy=UeY45*JS0r`2e60y zzWV(6xB$xWWwL@DiCdwe)Y-vGlp~EBhU-L2T%k-88xFP+9T}sT8Obz*LW$KztFQ=h z;jA48&W=SA)!J}Zr}DIzIi(cb#o3!Vwo4#E z++I0lC7tuuiL_Iu!-DPfvhh<{2c_khNo#D&5ZZuM#?QBH@0{Y1(oU>184GXXqe?T_ zBB+cKHw~y7)!DKj-5WXnjoK3_GqHM|2iE7Y5W3BZS2|ZLW(#_0wclxOuXjFY-y~z>Ov0t=JXx04 zavIc@U~5zJV_FK%^S2zIMO{|x=NexruF(l5!_l!0G11+jo%ADTu^Kns>3rhf$}b9t}_{t6TMs&Y8-AhE5w;s>XdIMYz3OlyDBe@}-v@(Jfpom%SdLirMRqou-X*bTIZxYGV%I_I16RDL$Gev{VX zjnx+Eo+J{;P1F;r=HsLIIw?lc)$5O_(2{F&WLd{IzS?Fho^yp88yNe>E(!C{^;oBv z)E3@!9mbd)mFqZ~Cu4D{*#wu)H2+vDyimSmp6bJ0-Cd zQ^$rWZQ3;&TB``=XVTA5cg~9?MqWXuv`yZNwNzr8&`O;b8*7y0F<;sGeX%UPs?hCi zDm6~A!6$zgEz;%TS;;P{-_{xrziQgqDsARe%@h|^7uBRX_a!vdWm=Inx$7Zhja)si z4c(txJMF4f_+xM5S7zYTC}#&Xxf#O{+Fs!`Rg;cp^4?gE&Jbf<=@y@&4-=F4X1Xm; z(Dh>q##P@xv1a#wMoDAFl-&K$r-()c@mukUiQpsj)Q=G+R-LWlA88fC=z*V9idbB5 zi$rfN&rj2|JY4_S9j<|@6`OlwJ!oC6X-^8gBff%^*I~q{d7@}me|hvM(%2?E!(jR= zW1dr-*dRAMl!ril1=Eg-4x;(cNtt*gsI13CiPLIWHNS9Zna$YnD?Ou~_o#z;?uxEe zgAb!vs>z5haVb$!;{mQm3~9T#YT=5#P_e2-Q~78oFerP<&hR$dEk6f#!{xCpMd@)G zu2aF3Wb%qvb~fX)6Z%M)tl zd~)kG=FpRexIa5uWn$ZuQ@MiNjI$E(xL{St-@Yu2%k^CL{uvYaiCL25F-4Gs_%?GtRLh#t_I&)Jdq{(0npLd`bVya^%Tjl}t{a1jeD}-`D2lPu#7z%AO$Q6b;~HcG zXU`waP3usqRGvT{D?PWcr&pjaI{7F)c0(quuf)ju`5wZTH*UJXSdVGYc`8!h{#l?Ds3;$WO&uHaCBHO9{58(fG40dqa z55e{x)?hD1l)ZrZD<38Sn%N0TM7s|IC&g0%p8b_|4iyWQ8lm1A5p_PPb}0_MUD zHAdrqCcEZVKh;!`#LPV2fIm$dr}m&h#BM1c6Tyw|C1O{0P0F@rfH)8BnNw##y}}QS z7Rf_ms-^pIgmY+6uCkhZ6UZuVWu3`Oi1|g3&}7C$vZB#Se#t5I@YP1VG4; z9^Q!qkO*)kTET6Dz!Su}s~lR$08)S;*ke1ebrJ76BQS;Z9ur||1Hb*t!R5!+?@PR= z3plWGvF(0nl9P05o)bO**60%=ckK?hg?Y72Os7F>Jj$?f?&{}sFR~`& zvbw@m=fF3G2*pL_a1osCoC!Z@e&Zt;O5*bZ5wfqNU0a zKLHzy=omhjS}*?c&5sa;=sumPjX=$e)w56LBXlRR@QnAM!jUQetcwVdx$~f}OWwxQ?3lA2bZo#Fq&i10?59)i` zNu=_^RTQdCE}c4S7RyjShn&Ts4I%tHpjV`QJ05HyZ`?&vzz81cKJ^Rwn=JwAnq9dK zZENb-A3y!xlY9Ihbhn#7S`57o0ta*@XgGvNBn3UX`<;4^`mTx9%BBmtk;CK~&?SagZye& zyt@DieJ0*J%*mPFu$Db2(!yzqn^Yg`&K*nFK#)^bq0cy7%cU@lBxLuV#D3%Qk0IeB zFF%Kb+vlVHs{?$5?XYN!G2c9RN%mVMII#X>KyueH4BIyKx3#kEddk{`Gu8+SMVc{c8S1juJC-`F>a zm~dMPtpQRL{<1>yyM7t2Lk}JPn5zlIE<61kZ3FFO6l~_zwCl=-eZbtDNhXe(rEpHi zT;~Tb+y)T|0piR9;j7JF&c&+q+GmU|1m?~n)~7=15;SoM{@wU)d`xgNAh7^0S~VnwKkjyb?lH~Ngx%vvBeqHAB0&6XIy~*_D5*;8zAH>GME2A1dxqa|`rjU7kjM{=3Ho0vL}2Z%31K zSB_7A+nW>sQROI#)K9&^WOml_af^*cFcUX4TsxNW8gWQ-KIOF}wg2P4Hg_Tqu)-5e z{Etz-`tappK;|IeOeQbOKlYX5K1Q7V6YK)!%3&3mlwY1_EwX-~jZ7L}b#UJ`>}TYh z3o0O?V9B}pXPCA&C}pwd2e~JW5Et=8q$gk(XIl&X-^z5S&N&G7&tri_r}_e5WGL#I zyg3|GFEs9@_R~9M2l6LqlL=P-&WZt2@l~kW`q>Han=jNuU!Wj_@Rcb*MBi=- z(6C|Y%1dMXQ3E%4xG8BR!^NN9A$SB{J!dw^6-0mog*jF_kfQlO21~~Ii}X_&k`o}w z^%WJWOh$#j-j~ypTcX#xh9DJIj-s#EkTQKzj_Wn*SpU=ey^JvYZ+QZeq9B}W{wX3j z_k!_Z1RN4W8mRI`EcLUzfOXXG%Kxm5q}qXA5_kE;3jvnb)V4gQ4{|3_5bu6t?lrV0 zJ60RydkI_U3$J?OO}e$U4=1^w*RqK}7>G=@0DD+-t&3Kw4v)tl{x9XKe*?gF0bo4t z*b01v;&6wH={EAJMUd=V#zx3LPp(B9b7?>R2n13L+$FL@2ks<%Q7DItdS68?Ja%=6 z-rns2=n80stOrA@b;m5ezBDzOpfE=h5OpE}0{7H+j}VC3-q8bMTz5=37CP?>vS%Q$ zixs5>;XltH?BfFT+)?igBIfGv$w$I}w2u&l7g`af_ay$e z`${72^Y#cCFNqL%FC7Ye2?ZcgGwMaPdZ6`D+h9cYM&8opE6_OvzUQ=NPzPsz_3r~e z5KGZUm>5Z50GkdGOTe9v9n|pvZ65uL%s5^*%Bcr=m%02MO_{oMb}bV5f!+)C`I>yDbH5>Hj$5IV#8CdX8f z7tb{Fj{O~;-NB^+p$`VuMxE}t%a58vdM|}Lna2QU7N`p6)lySd1#C*oUaLeFGo}wr zgSt+MHctqx5Fl`FKGgqsS3bfF7!@EDNjDXI%xRHVFT(BhJWx8?&Y zpX+shn>@gn3Zub%e^3LJc62TxuF)03&<}>iv$QO(Ohp5I6QT7WtiN>a*9=7y!wJwY zdv1v}?evt>O9Ml)deEw9FB3o5al-J}wbC&LjuKl;{AK%Jbqah1w`{4UXVW;JzU5Ide};M=j)922Y(upn z1gUXQ(&w1V4BewSnNze&B9;az{yb)d6XSms4FCXyd>CXztm|%=!JqjOP|kKLZe?** zE0nHo0&dF`3(S+&rMHz_1P4vV0(IfmElr*^h(zbv;NE5Ie~E?~hC>lS;LabKFF*@# zKDahVeLN-oL?#H~raV|yl71PD{zPkXL7HRe|BB*niJlA0LH&APd^(!Q^9gV{yv5km zi#Paj{wem9lYyl;lL#M`kWgmCHJ%opzr6Ne&ASIuehwrD+WXel0iz{xtCw+=a=s!Zz&JIl9m+PLcuiPq(SXxDN_VZsg!F`Pjxdn1 z0nzaZt{wr^Uy?S3IShw0GKo|9&&mIz(STO|zM$49?BySe&c?)EQ!4Zjpe3hci^$x@e^pxmPNm1^u2N z3JG+d`1EW4BJv|8fmes>4twB*AtzqSN0Pj8Z5PP{3?zMm=Fb~np30Gy53M5-*gpDq z#y79#x_=_7ZXAs&wf6rzh&!o&dGld!$+h6Pp)TxQnCFPk`3pUt=KdUJ6AU1E@bd>h zbq7|wBY#)k6uxieku2$>m3Y)+WA#wak>f$lc|~B@&aVP4DXRLxt_Prm#ZCE{J5-&zr40(_fA0dqAZ*77!exI`eXj8s`~zI|Zt z5dfkjI*s&aG)85R$MUHVQa;zyD-BKTos9~VmXa{aM{lq=Oi^=J?5^KFKege5PV1O^ z6|xLJFgw8$QpHp1U0!zw@p*?;V<^t8d} z(pIeRqsecGpY~6kp<)W!z7EiF$BAf{2M<`|p?|Pzr8_I(AQw2tcuS z%bOCwm!|N1({6@b=sUrzwavx7xv#%ACns6{!k%QiM~{Uxh5ukNKkn?&$NjC0I?97o zgN8WhR67fOnTC(#qZOpS1E?pX;x`$5MUkH&sM5syA|>>%GAoFb@q<62W26i{X4a{g z%*{Ok6T#DU?w1U;M`pY?2wR<+7v3+SG)dMW?wL~0YAeR(Sk)PRqm*uQK1 zu91PQ6tRQheH2;+qlCnY(#FwJ{z0FK<-#%#<~0Q(@yOvTey{(>)mw$dxh_qh34s6^ z+}&Y-3=Y8|xVyW%1a}D@+q9JOifoR?4S13J;E9;B6ESme5=?qjF{r9K~}Wgm;_;y-z0 zAB*S;vChxKm2)SLShlGaAlWWO@9jC8Xq)+0aJtJ(-li6-SdTReP3?s@4x*U zzUR>I{(5+v0{kz+?4ZztHjI<4QQD1zMxcu#>`5)HIs4JjlU^Nb>QT9 zJ}BD0K_D;*xs=2WyM7O$s3*o4Il}Qi?84!G| z1(e>se@CdMq)lbtm2+lMO&)iBEF;eGyurcT^le+3#b0ir_}*R23iF0Y z_Ww!tpA;K1cHNOF{{=W+#eZ^lG}e6V%IN%%s<9`Vk^d+(Bkxn^N!I({Ip=3&=TaNH zlGvPtRzw{&cLKsDT+4j1Vxb}(ySRT86#Tb6#Ot>Kk@{+Vbsp$k5v-L`wpg8iZmP4Q!SxRumXnhssdm9O!t3N`l%kqwcA)2Lxi3dgp+hhjK6Ga zU%iGEg2qScO>jH1w<1}ESX%l9L&f@HZ8`*Vv%`CebS+0xbi6;p@3Ie@P59NUL*(-C ziJ!wT*ubJ>eUm|XhJFVJ7O}kutvuLFOjwN@C{_mrGjL#F8dGpn@}m$Uk&Rm=`Ia z{I9|2&HBmS1rGxEzDwu+2UFdM{SZmNE6QLSgU9<;xp1*F-#4kGOA}?|Q#^n$6(7NE zU!5r8-Z{&I?p^jlQbZjEM8i>@F@Xnc*Vo9F_;*AF1E*75x!$8_t%okNdU$LfU6Ywe6c71m20rFh2@5{<(gB4cJ7GP(~F8+L3@>qW5l zU$0Hyg!ZtiRhBf#!)aK@=gc)KrXBLBWSd@pa@?-H)5%)9`MKTIs(Et91pa2G8?Bth zsaO6@H!l8_xpi#LP?_h4k?0(kR*Hd@mLA29TnD^$GhK z4EZ{8lOr*y8KZ{AZ*?A%=?gY^R|@ zS}{}>XFHAL_iVsPZB7=IMpQELolH4clN(_>8r&E|un^4>r3P3u^H^!^o7B-aq>f;S zjarzRn|oE+Y@4z}R;k%KktUpu{UfbSAe$q-xe>L^=p z2q~S&JLA#A35IHkUVIsRC+Bu8_{O8x9Af~`fOOf_WmMrZ2__$oVLwzUvjC(=q8m5#uPFK%VA_3+SM^1MKl>yL@Jc?LiZBmz`rd5$4ZG0h(OKo zj2dSfu8QqzBSJfGCEriKuH^hR#6j~m)*v7U*3lpL@#~QfMwu-)zgUbA(0n`x zijb|8XT4OOzql$EzR?Wv#|RVnwHKYN%iT;T=fd89%5DkSkbr6H&@F5t|C)SS|ND*J zMIkA#;waQT25IBo(!DCmM^7z&0+4fDqzTrNi>B7DwjBAMn9_2JS*mxxGNp3R#1M30BqERCSsoo=*ogNM_PW z@Yf=5WQg$k_DR~fQ?aN&MpN1T)anv6FEQ7v57j0D?|7Yjzt=#{b8gtSD`5poC|R*> z05A*7Y{i*(b~O%4X1b*_hs8IqYAiHd!{w5);w?i3$3K3|jg;+w-^v@dk3S&Llw#o@ z1}Fjo2q;nlmjJ64eziesxj~A)znLgq>5eT(7@2QV7G5siQI(GW zV1&r|+E^o{ijWehQ^fsw)uzwluu4u@dc$mz=H+A^AN2Kbe*`y_M44qOg~Vr{R~043`xV%+<3Jrrnn2Lx#G(0#3=e=VbEy0H;%*ma9ESc-!#Bo_WjM;Ny{|;+rN@uvbEEKA#hTHo9%KS(g= zxMwWPxX}aAQgN^G!mE?zZZbhImi=Hl%0ZgEp#9d2#^HO5F!WDOd{@8Cn1$K;!Q*wc zor?KZKk2GwXbops_!9_sAo)*mr|qU=E-oJ<@I74m052A*5W{~HIQ843G1M%y8(-Y+ zD%yA`?o-R=7iQ@{E!qh2l|fmqAn48WbFxrIcS~$_yYB9I%gkQu7sRj08ZSfkLe*!t zC*K~m*kD6(1eNGRMn>WpllLv%HN&>mQJ5KBvn?)9Y?I6LDsh5fn5Mn};3d_S4YQbA zb+;Q8r(4r(CJfQ}zR@(|t}TEps<#eZlL{c!Ic@A)6z2ctrzMmNDvnFOxN;f$&=nqz z7CGg?8wy`@(tkRk?^E!Vsh1ke2EI?L#1~%FD-+|9b1R*C`=5Owy{Y9v1Ko(5pW@qjJ+sXILlh9=ffFHfk+M3++|jnF{&;Lpb*B? z(|0%FVgsDC&$mbY<#W&KT53eiG8Grp%+T*}2!DCW`?Fqg+3Jbn5GyQaymTNe4 zs)s^ZLNQIo-+R_gqOO5XTu|d3MsUnPmyWKyceky{2*_L3iGirqQb8|xe2&J_`O?Ue zD{=YDRw%pD?XP$a*V-}#vJDHrBZr;Y>ENYB6Hmh&l_sml{9~+GH2XYBLzAt9sd0T# znfQs>0#*=&a0({Q(TS!%nNw03r7Ijh17MnCkwknR|~~#*I~;P%_wrh3pSK9 zO3}|aS(2boiA83;rpAduUP3D7SCSI-^? z#Vs6NDnEp9Now8CT=mW^?<)Pt!R!-NG9%{#hl(=!;&E70_gfLM;YoZ#q0wTV%}ulHZ{;GHui6$g5GYqh4q z(K9ULCu@^f(NkI`*ytNVZjV1HI*tk;YSW|Ojle+rz9x%imuf=;vQ^u~hc<}5XqooR zP)7JkY%dJ1VI{DxC+*p!<5 z_?Kms^u^88VC6ZCat=_C;CC&yoO}6eT>+_b?V~`&KfeH}zppe(JbE8az9Lp2mWf)= z8Y4HgLZYb(mf4E+EynDHhHui%$iU1bc$lz>IEjFF;bIZnkJ<+ouX&`-V^e3IyIHG6 z?tA2l*xDxuc>jrMjGS_)+P)Sg2Vd|Fgx_Y+$SoNl5;b*& zLC5_mpFEtFr-~Ar!B6M3GM}38_cKXb+(FY8_(13&`w9i@^Z_98KbS3h9X?@Xc*n7H z`|nNwyTo$I3w1*kUY3ykrKH&DHe3nzVoDy zjL-xO?xcuR$4&KEQP4AqQwf!$ov|N3Hu*Mb2|6A#8<16pPk=0(KD)nf30gk3;_$LE zP!aP|WqM4SDuf9Q;INI&UWYpzeHISJyOr(=!ze+ReEMPrrZJ`odofhOz>;L`woKZa zuI}-LRHR(lx6zr8ZF;Gw=LKX71BoA^J+@;=A-sFtN)7N;yxC68@rApUIIHm9;}As$ z^N8GtaavaShB(WqLjofp>;PA4ycwiXUZvG?I6&3@n7B-inqJvtb2c%bN3g5?6arMk zr-0M1Las?3seaT9wK~GJY%Y+fJoIS;iSW{9t}8VvXrzDMf9BV!-c*RSuIP|eq8p%k zv7jHle|Azm^cYeZ3gKaU+aCdn7+e;4b$pExuO1v>)#H0gJO0LQFS}!ij-^z#_Wp@U z$Um&B=~c3~m!LTPJD@lrJ2Z8=C95kP%la6s`=e$wlC@e$vc09G0`8V*WaOw}Z$5I&iPC6-dtm)irHG8=7RsF;zvCDjArGtnUH1Hb$=#@nqnZK+_ku{;2 z*&*@3C%aBf!7Ui#u0mqFc|8eQF~P9bb=H=QV=Jlkv*crX-GEpuYGiUIop;Z-WBf4Bi>~ ze{g2ZKPx+{iCL&;Ny3D^8MX6wpX1)D#bG_aq=}%?*+sMR`%eZ{@F5^)Nl4jurOLS4 zSeF+%2U8>dNhXYJ1vAk2_MNaVn98qxjF2glyoA?Si)r@{|9aTHOU2aeVN!@UK5Oip zQuS_sswyYQ1_3F38n3My3wPnSYg@&!# z_14T`E@O>~%ZkH3I%98M-LuPO#nL#a0>Bw@ewPA-I*58vGH zrH1xjqKh1**%Fe2=bcmF;~L1aMNHj~QaF8J;V*>Qo{t@h0Zj0(WJRBHUR=IA~(n>)E zXpf@6kJCexDizY3Z)O%w>Y&KT@V-lXkNrU<_k^hHLJt|fOE|ii@BYGQiH3F+uVH|Y z;s{(O$VydXNa^f70YkHCo?}J5J((R&VbHn(qb5cKkJ57UnTOqM`as8G^p}`M^+Xrn zkM+Yo!$Ha;5`8OXP)UZKX#_M|K>=jq# z@FU!IR#l9k$1wFOU)7Tq!@QJ*Hfq_n{XFBg<5ed*;pLYMHac*T?0(SI@T%R~SdF$$QY3wyt* zMFaL_jV*LzzyDRBy}ZPPe%h-Lu^p-kB(O}(Wjs?s@O#79I2^P~!v$;26d3S2pKTcT zn7nRF^=K3n@K{q{!BUs=xc)2==tv%#9b%4x-!b6$YwNYy9no_Es!Ia-0s;m94|4mS z`%mxic9RIJra=Dw@sZTYc$r~jvhow_QifQHCGn|kQ{|!anfx|ts|rz_6^MJ^vV~~2 z6z{{#F9WL`yC6KXrk+62-QlK5cR{jf0@vR$G?I%s-OESTaoTK+O|uexCWB-{wAJ(i z=#Qm|NmLQHmVVqd2STyjM-ffhg(fQOZ@=kW=(^#AOtV_kpQXVj(mM|=JXq$1ec)?XE#qv+aFN$>R-$v$S`py?wTFcQ_U95aF!S0v?qm zRAibr^(V1BxQe|Ox5Yu(>_1kUOv6M;i{)`WE*A7rQV7SAVT2il)2_hN>M%{NRQ;V( z<&Yh>Nae1-F}I{|)6pj#oiZ0l*irty4f;iS^ix-zlLX6Rim+AUvqv)6i#Ji}_z#md0$b7}`N0Loqqpl#&hSz5HYYs-~j_bIGA@`Aa;YWH{FFWtU! z+(*9tb1pAwOtf!TgQ}Us0anoIDry6JvQ9S-J%-bH?|(Sj5Bs(T>-8MuFyyWC8R;GQ zII98z84D+G`58)jU_<$}!Zz;t(njw2`_wYK0V3F#rH2G;`GQV*0Vd_$vZO zY?T*zJlZI`IXt}B*S)gv0uT2CKcZi<0;+4xK=Cl2>$~-vt>Qb3Zd+WU3-OkPsc4h? zJGkGx3nO@%Y}Kt>_o+2@6e?y)Pyw>c##~te7)?{HtWaI>dh7%Xr|=;jq8h&^=EEa; zvykA6#(32^HjgEBS&?xku(hACOE6%d~PM5dR98 z^~5fgfjtn?`;rD(6ERZ^vnB?I6f3x=;BAH!D+S;sANDYliy+J}^*E;OaQWJ$jjUMo z95yQb+}8LmU_@g5Y!Y!Z=FP#77%eSzSTi#oug4`pXkbk16DlfvEu070X^8b=B}FL_ zXr!z2Pyv}%7BAKSP9&27_|x}4E-uaDQB*wa(UY2 z=DODTort7AegPja&Pf& zcQfxa1@pYoM|~L(Qx17V#{K$es;48l*Mf`CZ8_xbp^o&4h)GIeL>8;G#}jvc#OfejoKqZ4&Oc?>1;x|C z%YH6QF48rAbHQ|_BCnK>`P8zp@u4fUcjGwA(&zh^Fa7sf36Ds`S{OW(lyhWz}3XwB*VnW94!yHM9&O>~LESguk?3c?EO>yvBDcFE2ZQt}HB9K7IbtWbhSX z*B%5#(rA;!SfI*$bS%JBLe4$K;kV$-tSLOK@nZZSvAS#R<|qTwI6B=h~_ zPH4cRwxMh26RrtQ3x}KyUdFm-HxlS(v(q+m;mhbD|1+Su-NA5wq<+XO9YaDvjIV_r zhvX|aPqM0u=}2ZT1qgbQ42`qUEX6AccbwHzo5fFgLAsI0r@(Q-kX3hXo|!=m^T9jh zQ-_kHev$<;2x8bss#nT0IlBHm_sr2^e|q$?wCjUc#-um>u7D;Z`R6^LN}x0s>P7x$ zZB^wY!b4L!wZGm(H<~|vM^&+~hh8pw-bQ$oJ$zpOCGoZk>z9))_Q4ojDkd0sSr=6I zq^`^O=yjB-H`UJa&{FfCYxajx3f}t{Kik^jY(8(AELqFp1=Q+|NO0>F{mcCQjtrgY z^JHO3E+*1CmxqZ`GL7xm#T=3zwdt@>dL zyW?Qc4@O}9X|935Ui~uUClCUzeV29A?+QhNEZ?Z?hfM8nIFaKg0!82O@@{WjZXY>> zN_qO|Vs7J}IGo$fdOH!EMK5{@(H%6=`W7jVeygJe!5sb0Rv6=FdY+_4|c%I|>+sObq3O{(zIgD&Zu zkiZpYCl#hez%u)m2%`qJfLa^2Q43>NQ*@p5odUzCRh0<}9hHkgYrK5kfh~+Qgwjve z`Pr3~;_>%FdsYG55$?iu^|sxn&!sRXmw^ls1kQU^&&ZsGCbsf>vCZ1L71AB~sjAz! z#He2H8~PoEZhs6IX-1$DtSegGmH-W@xqzZ!2i* zcgj=@J&scrNmI)wUL(1Ik3JHFBM$WE?jWL?vr=tTXSv@nSY`Im<20cNUgy7^-!ewV zs5#A;peT(>0l}1*^Jeh8V#ckP;LTmy)vphaKlqd)3|}(s+cveNK zt+uiUO5Dg$;Ws52O=IhL{V)^X@{aeOg>1$!C+Fm!(j5J>75v{(*ImVw7MDNE zi4IK8E{OT~M4qWp*LRfOZO`}}wot3XuE-pDe+5BBA{hYa2*OrZ*8+-EL05knXh?Yc zYtsmGkY-RJ6bioD=FuNbiNo62>tYgRK6j)VXEzp*i?E9yy_eH37K58e_?dg+C$qH1 z#YURpayPB|zZ4qd19S|EiY1eL66AyW$i2L;F_brA4+$0`RkZ!TJ@f0UGp2+%qYo5s zj+t0^hZ$Nm&CO>RiIusLWz)HsV*@}}TrSQ0_W(J}U+!e}OW8f$3*Xufp1oeje3`FT zT;I5hE;Dq-VzfGW*hr^5KfUMadTqCf(arC2$DjhIYuCn(B$}da_{E5Vo2O9C#*?9i zPQJ-kVlr4b2L@74>>Fi=M*r2`U+3tn^kc3D z34Ii7W#o0og^%fO!>;`4uk+PGpHw??O~^5kb2lm!MxNCo)<)us zB*HOQaN}^C$=E?hQ>SLDDzOCEA`ymy4QMRcokRW0c#=&DCFa@3tJ<6-W~B?}KCJCS zCu$o*ta2C|aXJDY)5^MP;s8^w3Z|TvBj|_fE4XSR5bNDClzr&&BeNW!IYiWV@gjSw z`RJYFZu41NGq~h;F6~0l2$Qu7#~2rzgzZnTpKpjy;{_w0eXa?@#zPa4+fz$W&tN@G zZayTIXL5c5CVECVBNI~jP35|6+zL_HeL3gr-g}cG=egt{`{75|cX2x!*~`Psr1mXo z$OAsS(pc|n{-%>Pr(gNFxD?xpuSKuDgm(Qm{PF!?qX$`!tGtGmBU}n&rzEaIE#`qg zBA|i6_>q2`CX;YyPDwfp5Hv?1ocxAsuMNFuH^B<+%xi~)o7{7}{)fYc=Px9{_VP{c z`Ss>u#_%Z7u9x8ea_?5y#o2s2vGfSSW7EQ7jROC%#^9iV%c+P1(OwqhzGiD^grBN@ z{nC~u+n@^okaf{%QvqvZw|izWMnfDw`#rJvLl%eOVd;KbxBU1CUwaU=s_CHb>Ipa8 z4ezLLW&gze#W3{j55f~|4EM2B$20gzUv69LuT$~#W?MTu)YyKQ6Yi{5uy-B1Z>eV2 z&~Vs5+&MH3+dF;Z_3|85mn`fwLG7t2mbY;}+pK=pxY!UP89?@KiZ}G6ehRILG2y<@ zDGRp!^MJK~ya&I=tSvdm5Z-y|-)v$iXyB_ImOaA153vH;w*ArcGHt$9v@!_2ona=Pa^w{|O1p{n;ebUqCE;a3+_rWO*HaY8>tuxtYv++I-`d&{2er^ zS-I#ln*zBm7PnwRzex75PJ+b-Y9=UTBInIs=5>cM=p5}nnemPg2BB($*^ZTz*g;4c z^5oUY{r)zCm)qS&_R;m(TVg(3(L-WULw#5BvO99L$2?Wlc7l2DoCshZvVFT^awZ(# z2qfvCU(hO%SUD%fRV$nyZP-Me`7|?PGxle)JS%_7gpfe8xtvF-4> z0U;B8X^4!g0EQo($pyDs)ZZv<>}W+DQIc;t=s7x}7mt%zezgnaVb{M$W^$9YHQ?%F0TsR@HnVa&qGS9A7IAE$6}PyA_td0L{!=tW^F z>$X3>g2!gekT5zH*i-!Xdm_ED{&u?F`{(`X51{^Nl3v?sQpE82?$q%0>DHh42J#oW z9Pe$yLYrqpN}6JZ!01)uW-xwlv-5H90RP*ise~c=IaRBDc(_cELag{dNTKIRV(w~p z=;SqyNQlpKNpMnbl3Lct;t^UXcLL0ZQx~#dl8_yZm`m_zg#vka4TZzvd+WtYAGE!+ z&#uLJjPbqLbtwp2-Y0RBGD}*9JtyYSxQ7mr{f#~fJD&}U!TrVvZ#0(&jQ$Cjz)>oB z7G;QTscDxR$Yh%oX{a<0N1J>8L)&S=mX(ZSWg>h=S_S~5uoSL@tE8kPgj}s2iLCEC zqVGe1B7a%{n#P2kg9@u5x=tOQv!?mSo|oZKF0NTeI_4qOQ!Lq&yu-Y%JQ0SM%)dF3 zhUU;x%G4skcE!`zl(EIdwo>h4`Md|RPFCN=e@Q1{)>*U776zY>T7D1oh@y?hFBU~x z>Z;MAai@z5(yhq|4n($Suv65h?mu=WC2rjalzT|i7C5O$AmPw(xs6_iM8$ZMLa3un zl{ESI&$*6xiB^V<^*`6X5Pmm>A2CGXddnGdu5UhT-)^(~?LQs9-eaO3YC1dnk8GoR z&U4@QXBdEbT9BOA7W&r9^*HtquWkHvN&y}?3jgn=>k+RXnC*&LgIcNB{n{`;*SGoEnfCy ztE!}+E(z7O1<=D z9F>@K(BYjIOvf6@`B8moJcp*ullz^O8Np34SYNDA<)ah z4Rcl`6oD|hK6(POzuWTO|+ydE!I3XtiHL7O3-u#`FIJ6I{pRZU+{Gb(=>ZG)37Y1(iyWnA6 z;B6d|$r7COvhEmGq1x1fP*p1{Vf^Di&jc(IAWGxqx}Lx&j2yAA1RD>9n{P80u7kj} zN%IuvH#zLL@Hl?vmGj*ybFyURrFZ1Lkf?{v*-hqG5v_Goun(s--aTad1$4>tbb5 zMQm-0`4Ik`?(oc5Zv_fRFXSN7Sq^=QPQ}BAb1h0)i=#M*6>3bY{0W%2m;UuDQ{k)6 z9KYD&YwQ9H(1XJBzKLx;J_z9-h=)Y1Vi5%dp&|WE#E1{V{ALi9NKs39>K_Ks#xv-G z_pd+c#4>j(Y;iF|rzE7~*yNm72sg+rQz96i%k6$43q`1t%Aj`sX#aCci15a_;Lj^j zwd6}hK@j@tHD-wvp*Lz_3;Fsf*>HSgM8Mmr)-XxK%&+UjpY_#{Z)?##PBr_dWG-lp zcf;@|yU+ayxLEPdUlFyBioRx?FXwm7I^?g3(K|Eme6CS}-2Y+{{x@Ad`2B)NexKG& zQATO8^$!g6xwc;u+P~h;1F$lk(CAxEm@nJTz2&qas(KYIgxoIDt>@n7j;q1qDB@rI z92R>Dk&4Hb+ZchnZl1+vvj^6!+-6EYHif-~X_Piax6|t1rT_<2&#GUB%N5#LzbYA% zaxh6Oax?vALrk~AXHkFyq^31fl?98`wKFoI2p(S238qCB9@z_7)t89o&0CYzA6UtD z=;aAivnJWv=PWl(7+y4Y6i-)u&VK(hKg~6N{(UMx%HUGQwOedRSSxdb%UnS^;Wkui zF;U>wR$ZN<6c+M0%;^+*gQQ(=C+0nd`K2gZn@JfbyU|Ofnz-wEPccvPatlkI+iKqE z%aHKq6bv?dfAq>+Pp>mt%EypW1qY#9(8#_E!y+Cx$&fzPl5!Ubcf%qUOKGTe7QV!% zA-1^EA{X5+W|wh@c7OJd7q85d|1WR+Ahol+VU4yO(xf);^%%wzLq@1A5eARgV;6>4 za&q#m<@LAvg-U|}tE)fWr&fj@9uN&!G652J=vH+Oi56}`Z=%}+7dj4u^9Bnf=UH9p z48~z;+X+xfe^HFrVx^s;Xh@2w?)WhimmtR8$czthq&bNZoKE9(h|p+}+7caDohl+| za85EzCPKP@cInqkP3) zTy#efIHAuY`MF@$n|7iaE#dK?CTTg%y0>31!HNL}8A}!;#6xzKg}E+`<^*nPD|22- z4e*hp4Xe*>wIORdF;aFPat(U~(GDU}H2B_aP*ci=gu*s?{uiap^y{WqI@QY*eEHJ} zUwDAYCdC%4g}E3}fd2{zEKlHiRmh29J;0v=Fw_4_rg5n*Frgxqgz~Xt61aC`dZ4Ww zCLMgX>1_gemh0cRQJb`nPe<>2)DHr!_2TAWiibm)^M=vlM+y3+Xj|_{=|-Ll0H+0rd{m3IJ^0ZsaKR`3m^j zV=)N-?ADfK))<$Z@iDdJXfQH%gb1 zhSy9r)`V5$2wKTdx26?3${hDcSKJ&%+_4*!{FxsGXTI4{Z#*$M=AES8!EfFbTJJVx z7LwZ7Gl?ZD-4)s{2RsvxQ!{YM>JbQItfSPdw=?v9qCC&`Dyn+5a;2u|8e)Qs2Yq*= zGD3k~9C15lUF(a@8FfT?A31sBWm1FCjX;Lu3o#%6yM+>#)L(D+-qKosO0pu;JS^)wJ z-~43+fsGLWz?IHH=TfX^E9j8DGw00WVq&;;<{R-DO)#FaHDX7l-4;*jomTp4^-mVe zIS*OGC}DKEv&5k))4H5W^u zS}1=u5mINh9a`cp!M1_SK+GMI_p^mIJx3K__4Kgvg#Y6!$QbetUi$1U_+jso;sko8 za^G(m3jKFMdu2!vTO{$WpnWk9Dsj%R>wnR;k)n@S@*Jd-Wx_D@<=~ylY4A z1bO}&^WKQgeg1cMh4Q0l3A0_kyQ*kTaxZnre7;8Y_7MHvNLu)O*7Aq6LZx>b;)VOR z_v20U<#qkNEhRnODpA?iNTFA2?F#F1x#M-&zYkd6?L4mov~9fJZVR{h=%omi81g2;7)~>I_w<#nyoF>p1ggy)$<339wtxfdTyV$9}D?D8>3}S^A?~snDp@ zrHAsI^eGi9AhzeuN<_;*VgT2|JHZ72WV6{6j&6lmg4H*pO#Gu|gqo`<4|QJ#vGRtB zG53F62BqQk7S6oHRflO3GJYmWxj%fEsrq3hxDKXOJ}fTk7~fPJSmWf=XZ~_W%aKHk zjt@a^{OK5Inra@3lL!-LXLC1y&$OBFLj0wkWbAU!S?N;s$^S3<8J(l->nEChBMcqd zCks_Q&RPtyKRl+>dua;6hMGi^SmLT>@^7D)ciSBIWiN--+7-c$W1L%#SE*+x6*QQo=fTFSzZ?b#2AH{9wiS`$?ZE9S-YVNnuGsD*8(vF1~}SF)w*6~ zG|;Q}I;%Hi6*f{n9da+{bA^7{$KW%`#MUyFoVesS_&;H$-NkoGL<%XFO-T`#bw2<>?a!38FLV(!5-3MdZ_u``D#BnZmBp z&?=hDSA@)E=^$z9dp5stpC$dt@n}nhCS2=y9?bNw!I*4BB0qS46a2sjUukEk z6ZX_rS@}IDRBy3#30e+41`a zYk#{OiUaP$vr~^>>VkA1eau$OAk6lJa351PlC*tuMsb`?j;5JKb9Ase5=##?Z@bBb zXFAa@^w#5v3l8HAMIg@tHT-|suP-E>Wd0ENZGr7;Jv5rIemKU-Q$hMg=O^S}$sI4d zQyEh1&)0k)afp9k2g9|26pn5X{c2L-y_DiAHa2$OvF<(B<>?P!EwzsA$II;j7_FKn z$fu%D@BLOU+;B3TJ2fZ>=JH6bL(>Z~F+jS%ifdTDi;tHJM$w_C%MGrze~3fd;ALBx zF7o)Q#*9*>2=R{bj*mX;G)ap_syaYUl0_9qqZg1y(AXBpl$wh0*_uchI@D2N(sHxo zi)rjb+w!V;pu0kynP-rNU2*!i3}+Fern*B>IP~bv`W~3N@}mIw#JROW=q@2$gr-kE zdqQ)eAE+tq(=L|GOH&!B!Rkm0t=vfcUSFae z(cKLXkM}tdCK7C*AhgcF+jGK3+Oda!9^u>w%W9!X?>xh9u(aBgqTj7PF~@!IDjOb_ zzab3$!iU>mbt~U5vCJ*Vci?K3mpL$)KSs68D_HmY)(*wHy0n$pjyn*+n?lqIf@p7C<{hW)AM$gGem#BTyY%dol+cr zd1>Y4irJ-@ZBwQV)qCmFJCU$940#A*@Diw0!DsO6qx$34_mvAPec}48(Dw;=ne6+R zd{-o&v`8z}G9jC>YWwc9SRwXnhZb-Yb7bLL{*5o*Sx6KP*sNIzFRQ=VOQFjmE$;Y< zFGPFj_0jwcWkzrmC+HkZOp^CeIh=S0W+rjR=#*`c1h-s(gU2~oDeK3q66=Yuw2y|m%O}H z;2%tT7u*y*^1$OrlbP;|I9W5-%YwXLh1JsC63c=<-hwaNzW{(b86d1vLv63oPSgWx~4rB;VO=z0wLSfOP$2W%h_x6U;FYE25 zu%b^+RyJ%joxR02oc5|F>$Ob36xY_Tyr6ZD?N98P-h#>4!9kJ!JpDgV75l1FeB<>P z_o@qe!@5rk279FlqG#h)*w@~fnFQQEdd&s@{;P|)A{GQoYv_x2qu_ONJ)P@y@a9x{}KiW--+IC$qQ-tU(i;s`vzVxQ1$^rI5TQTI2IR@26H%X|%!MN`b z`A%8}zmqk+j=WSU0mf?mCQS;*xvHghLrw`=Ivb( zSb|Tv#`vr@C)sAMUdWab-%@^5JyG_$x7Tp{w)GXnZ<)`kJ=_H>vVNM)&16zo02%oV zycu%IA*15n#>kGXy?%kPpqg`5RYy4MIzTH4-40GxUqozc^9R{sg+ztMc{#ml-p2_7n=2^u6Hwg>{HB4B!V795js6jD@Z+Pj+vxl_Pu}vd6|S?L z7iL@q+hSr5VDPyN??Yr?ArXPIU0%Pfa1DJv@6c;KQu%}Se1HB!yjxW_2_XAV&jdO9 zZ#{>U=rXn4ml-9w#0qzUSo*I9^Q-w^GV^R(PQP7-%6WCOz5atxFL=NK_>y4v?jav+ zy)e&($gz{M)$~G1ZwPiU;&=hx=P`Rcb9nV!f6jbp>3}#&*&NUR!S=l~^QH1RZt#%` z({>_riN5(~6}bMGv2H<2J7p1&>4^%iRWgIs8ED-3ga2n#)MvWiTiX)Hg_jIc#9WI% z0f2k(^W-A8aLg8)6kobRAsGhv+UT#+^Ax7Bh(SLS`56`x(%zDsSxftXjo#NSR+lUt1EVbAtywtUC3=Lcl^g@XUlt z&iHo`Emy5AB(~L|(tR$K2geqU&{btpb!b`&j?I`-YJgyF7FTLn4}uqsR^4YT3kB_s zp#Ihm?e(=Ay>HThyrq|~^R7g2=5$*;DE_9-l2pnLuDrN{2Ej zB+3`Ke7ZR3>5|um$Ze&Nm00uMBezyV&E0H^B6|4CQ}S6pp>Cl}H;>L$8U+}2=3gzC zY$K>qP%zka&7E)`a|bD!|E;mqIbQE6UoS2<8sghn2g(#dTYo)s?ORw-7vjPKL*vFN zyJ9UiHmP&{BXg|dy1GmIt*Yj^PVbp5)poW-<<7k6@1xI69L4pHLi*P{9{hQ*bJ&Z4f zptO1PN#XsozlkCpf>sAQoUAlf;>TILKRP5kl9Q|k#+D^S8tGDmjf2@$Q4p=}!4$&A zt~c4gfSwWm)5rWjEyA z>`Ie_=%L>+)hX*@lyK;Z{jvhSi5Y5e_-zR1l^_(mMnkpuF=txXH|j0Zfm8gIbPf~7 zzX~nE{6>>DhCX9o)kcGq3UL`N?*uLD@!I}`u7305;I5>&h!w~FXJNKKM87qmVhy$h^%hBJ85Wz1Oe%P@^D)9M2E57C)p|Wrk3CmF`>4(L*2^s={KBC1u5UDdXZCCT&MH;p&h#8o(FdK?u8+kkM~ zFS=myw_ssm`;dJuK6RaWY6clrQNP{&c>kt2DN$D<3a%{yg22>Vw`G&5wkb>vq6+o7 z^8?GwM@t5-a7%wXR@nqm#B}SlIQ*mt(d`3*;vIwu6(Ati>ShmS8CEOr@!x>Mo}e^? zJ)S76PbY{`Ph3YmN)xZ75(eox9FvjttNX#cI&d=%0j6bGswt>6y3~J@MUuEIhG$YM znhwltjMk3N5ObFg#a4H+ zvWzv-m-zeUHF_gQ9L9hRXqnlJ(en?A*Zm1;yqOyJJUOvC?q=!hm3{i3)AdqaV~{~$ z^~wjG-~g-*t&9;>JP?e?cVvf+b^j;cO3YAeC;rH6q}n{MeVgKI(l11d-9s_=(m4q*_{N zb+0*?K^uXv8t{+|B-kfdtuKLOG|pyS!CDWqdao)pWcJH*ptIXQzHM$@0NshoR1w_w z$E^27ly^v>YbT8&e>;eND=J^;Z6|M8HHekO=z<6u;Uw|q7U_Y9S^F^d285dRN{>aQ zf^Oiom9W^3G-eNoXWSveIe&G7vL&Tt+gEUhx{-MOzhTF2K)4^1GFs9oSh8iGRlR)- zXD2Mz#seqFqv9QRL$szjB2tj=fA!Fe+HMMm*L2fxuH|Fw&pOueXp?k>DyS`=v!yI) zwQV&AKzxSdV!vwT8zQ|#%#v5<)%JFh#aoL+j*NEZN4cX~+=D)97Dbt+&wTomVfAIEBlNdnR3=;ky7YCeELd{id(~xyjHKG^XYvI z9#vvk`-mFX{+E{m#((F?2bt_tJSQ)&mGfMW;qy2tdQ^~MWk@)E^I-dtMN_H>wj8%> zDm9JsI|KsQ_6Ypp`@F2Py)1I|*nB&ug&()-FhYlzB)$+fbk(UU1>pePgQ9Jl+6{U> zciq1JHRGFR)^_B z1|)d`^|8U`QbcY0X)go|-Tx0$Ul|nFwsji_1Pcik2<~az35~ml#$AKEYjAfD?ykXI zg9dkZcXxul&AIo!^Hp{6hoXyGd+oKx95N?D;01#!m{IhZ(hQd$KkeQ_5LUk={*J8h zVk5oWv4E75;rf`dv-eCxxRLO?<-Dm-{E)8m#9Yi9X_^lc_$jreClkSColAKIZ(%^m ze#zC_Uq0LmU$0P+JZcuO{K*> zMb95(u0S}=HJcf6M1Q!|c6JDs7DuYvwaW@;_&usd2uk2E3wxhYIo&bJ6ce9dU!>=; z3of6`#ShJCv+(CS=5{wvx;<9lz@0yM7MG#@7YF2`bUCmD|5>8pydk1L1QX`Ev)!wz`U)+9#mdXFAa2z&FQa_~~^_Xkc zQ2|qmBY_TZYLEn=*+tX~=+$qIkUeix_>%?t64BiN)`+Gm#l|27gP!~3Ba*w2C# zilf5xVAQWzK!-kXISwr%MHDXC2k+Iz)Sla692ih|!Nm7 z)b>@?Uh#9(_QxLxrkX??Z7%=o1rWMD&8=i-l)$m?@khRHL##z|?b5;z$|F5RZwo*< zLw07vvS?|gm29bZswx5(F&m&s3yYVQ#TkoL1zg5jZn5-46Wl zYb`(}6H-Nb*HAsA5mCm-rCuu{I53j)Q+=WOO^UeU~fN z5J}_#L5Z+b^$Dev><7&re{r^JYeYz?)2ZdfL4y0ys^H%TmJmH(e{^APqovpq*uTSv zk_x=iIbcQ;YWZrdn`4s~C23qVo)wqP%(ykOmfcaClr_JSv-eM5W^x}td6x@Ypq7<) z7wqtnZtALk^SO=c!)}(&8qutFdTt1AEKm+Ms~p~v;DW~ zBlN6qI0X~LVHGeQQ2PT^=rZey8CfUqmWy#^|=v5ag^GSPX|PR#wmpI?U3ObAP zA^O7T7lSJ681g}6rW$NoqvPYQn3LQy;*wm$EJ6dfgi>7Imv4TgJJ-$eTiBacJrpDL z9&i(w5M6+4#H*Clq-1qx73K$d7{mwuBq{_Kv=I`KoSxtrm`W}m&;4=TcjadZEL*3(F^|q*PpeXqfN*yHOFZD?i@$E9lwL#=|vV(;6+!KUVg8LKi`Ndx(8IAw>d5 zuLI7G6J)IY{_7tgBQpkibN}CdZ9pgOWL2=q7wA2Phu<-)j*Au>e-F#Cj?(>q8IVzS zSDw(ku|ZO=Gq(x7!DC}*-%q}otJKS1%S~bsLXy*9Hb(FFLB}Na$1Yant|}ESQhmx= zAdy1+3aqu#u3i(aTi=NCsm7x+mQKGMD7hLIWSAy=`bmp zic~(a=&bwEDKe1`^RqQgDB?W_o5*ErvMFPaiFl-tK}_rePHgaW*2x->yiN~8Dvx(i zHEe?WRMlD6T??Pn0@yD;-dZ+&yV~w^hf?!G&l>AGkpoGrO9EY*w)w9gLkj%So7^Ur zGp4Nje+Ew9Q+DlCPV#UYbv;Zl@%@t7*W<93YXS0Y<8M;`sEi(V zy8Lv_ z%%#5teiEdX3;Z30_YXHnpLin;=x2;yy_duyrE#=+gYEu;3zi}Nc z?PanqR5I=GQAWoIw1Pd0RNNSZdui{Rj=2q^da1U8k_w|L27OrHbhIU3n2F7_|i*wyw~!ZKw6+nkQtMATvET9nG`m6B1d(utFGo8h3YIsfNMFc`Q^WYCL+7{_0 zX-XwOE%$Q&a zxKz>zuzAV1hs_gPw!IC|)}uX9Brj;LHfDE!xC~vU?pJ z;?*xNh}&k<$U-BwXC$h^s9KGb|;!l>Cz6)8^R=_TrYyrzqo zQ?6{i>o*t>^k`W8u5$Bqbnx$D@)AQ_yX})j{N(MVD`!d`Wa{?^?}^JXOK#_=bt;C2=a=m(z|800=a!k`P zEx=C1hcYisGR*@}GOX4+&VQrL`SPsKiVPFQ#*FyN*(VG>cqX-f%g)s5C?dsgC9jsD9Bud-W16JJ z)&niQkkz8P>fdgX@mUK{ge~SAB{Bw{2s^!;ltgD}K7LU<&elAzPFB-^0o7q;);OIs zrZuCfg2y|K%qLJ2T}h$<606@<#(dom)`!bfDlGKno zH)%)vd0ShEIf4H4$87Vf8`Ea?Y3RK<($N=+p{Soti?BqFJg&DVpOm#+^0)^sncDC0 zftiWKPk?t*sIrhl$T??uA()#H3DSfRCn3yrx8}Qiv@pp7H1({oCc&O%mZ-O2y;CPV z=1%kTJ1H$CMtS=;aRNG!L4-!}(l_MuLNT5)gxiQa@F;C7LWsDO0txgNBkzGN+s5p2 zWuY_J(rtP%C-qSF;iyBY%!Wja&bxN(p`C1UIDJ|3Syg*@ms4JstXzO9;MUoj6Jb-usvGmHOJ z-(^S0$FoXU$6p%XXDAxx{9kL6*c)#^J@2iJzr4KMASJvHI;^uKJq4yVo)f;D6P7hs zBjMGbt-d_1T8{hle0dF%aED5PV*y7-^Ufy)eYt}+mrFxs7-^pST(g#hzEh9e=1wUK zxBa{X4#$ktRQuAyPjzJzQg{8*9wZTjyeq?H+{c^3uVYdWZI)41_M+!@#%9w;_-viZ z2Fnp~F14wJH{?K#TRufDv{tMu>`PuS@*0;KdH(1*bV_l_1=1au7VBh2YT)2lHHoxb z96$du^Zu7As&tEMy{b3xnGC>2UE{$0Qr8?)l<$VIjNg>dd z$UArH1OYn2f2|w(sNvRR{4IedW@cu>w{;o>C03`us&kz8zGVVS7a8@6K`GT2Um43S zm?wfq#awuZ0+l5JPwI zpBAj};lHW1?!!!b+zWrr z&t2(?URrrvrr_t@QrpxX@Q+7Ehy|;wgBdw{<(_R+m}xjE6F3ey5ObneH%@2EGkGtH zurVQ1L27@kBlmpMfciWmz)^IJ(65;{WWeh$%xJ>Z(+p1DihCK)G4{B_hWzo{(O$Y0 zCQ*cUTBd&y$8H^#m`m-f5Fv>6FJoyee|JpDk<5#9(VJkD#D0Et9!93TxY#8lu& z{ycFcM<|H8>(=O=u7wZ7s|I1HYY`g&?6eBlBzV9l~y^Zf_&7)Qedi5lE)i*UxJ&IBs;flMJ)ofxc6F89SoJ_u;wE>!NksxOE zwl;CT+%w0Y*?i1Y{o1o`o!Gskz*AUY7>Rp;tjm8)XdYxygWE#P#PH3TE`E9xnvkDWTpjJ7ugnNp-igA~SZ0tuch6K$9BQ}0D$4T(5dh>f{Cn<0`ikR))nP{`t7_tf! zMsy#IS?|3}HX+AH0v=mm@YuInop8`pPO%RK1;0x&CPX4&tow*7;+d!_U@);z#9Kr< z(*YUpgn7(m4oB&u`tUB*33dz#?`qX2R-Cxy*(7NtyDYdlS|>%=DO+JlopC1j46CVG z(zK7X;QkuMXnZnMNihj0^QB0al zv9JzSCct8`ST=C27YA?$LbeOu2#unYFp@ z3BUYdJOym_Y?1UVl+Gsv$!qi^t(uYNtzI4_stA|MOgz*^(SP`%$@rkjbP!(kUd9A% z4;Py7S7-YUIlW7;cW7E`fal)=Po`JTjV_zEh6YRfLvuTB^M`n8qM&_b0ByF43nqNG z*h)Kf+a2SI)nb=&pbzo)SGQ_q#q4-kxow-f`X20g>*Q%f#IcY3dSFPEvwr&Nz8TJT zV^}-b?67}j)_!AF37t2WCJnC4B+Tl%kwgN;L&i%MUFM+rXJQJ4(QDr@AIT+mb9_qu zrmJ0D6B%JI(d2c=)Etltt$A>zsg$+WtqSgsYY_Aow_7@!bLu2PfYpxjC38Fz`TY&N za5F4vH&yah-JHWFS^2!3+{eS)eub0k#qHZ2i}dmda8Xh2=y?_y`+kDWie5ZIsMfN9joCU znh!-HCoBYozT_laH)>?1{9;6-z@^&ZVKTIEotvsqlns>mruQz4$L;&sY zfxbncBABDGa_+*_o%B4%@?`vqk7@I~_VJm>QPXPU^l9ZZsr~t|{X`04f!&!To5j}= zEQ|pUnId`P+ME0K3rVSDKN`gHTVCYf_1qRMKu7H{oJxdC;P^XfoMRK+fhIE^tU2~> z7Ow&U2hUES08a;@c#Q^pAF7XGEl!nfsUNfOQgfut?rdG)o9S*i{=vNdOLRNc8wveg z`5O7Nz&)55Lj|{(ys?~mbDr%Eg=;Vq z6*a)$B0_e6*iDNGu0vi_X(c!M@L89JApw4KewX~%iDPz)q_+v zN{xk>R{>RTN7VC5bQn;>SB!0yTWG&5h#VUk804AZ(xh@Y3_KpTKbvX)U?KkK5IJL{ z@(4@s?9JV7;{wDYoD#L~{Ix$*;7nK$IIlsOA@2g^S~A8Eeao4^WiMMBqe_H`$I%}T z8YLA!u2-pJ*i8Fj!Fv{^TeRHs>2TQUX~s&}Dd|TlCZtzX3!Wx*&xi^)CdVh%*v+lD zd363p6ZhC-aDF9Q(-uFL!$%x9PL9_UHT845jpQ<2o>m|p=APnHgkGaXArS~``brq1 zC8Z}L+!)S^Xe4-aV9FiUdii5sQ#Eu=x#sZi-kG!rJh-L(hPAs~2kqN|=ITE1+den~ z0b|y#cFAJJLT`1lMjs`qfN{kV=wmsE#sLhzyPO#!<^u%(#476$waxzu3MU#x4Xk=? zSB0w`UF1+*>|NU>f!i7X zWzwiyFW2+kX63_}kon84=gV!r2EntKe;@gXZBLrhib?uSO#!w_o%8*mw&kxZoz=oW zuMz0W%1HO2-*KZ9o*osQ7waEA=MbrgcvQ7cZdYCV$rFrgn|DwoGLNXs8akVv8@J=t zhiYfFaJTvemwgAqQ14%jHbTbc7=>uTI>{$6emlS62TWUEINS z(4WbHyopiO2vm&LDkS;tO6+r_iakDH15BaTJpQ@!FK3xI)FKIVu! zwzWFcJhfH6>Bd}>6ZOR5AmOU)%s2k?%d}r1JTWxxu#V!C>>lCyqe%oVL10>Fp(MWJ z=MSW{?G6jXviWQJZ^T|a%JAXd+DcJ1N_B{PO`x;U#vLnlcZxgSo)*5LD2t$Tn4W(Lu;=*&H{}#283Ck2#eC^^KfnzwuXdr(qj;XCf||P3Lf> z_0v8{zS8q)h@#(JW^>*x8vc zl@NgZabF&G!-UO7?4wHY;sY$V6`e!CZNzaJX+0Sf8WHgE9)i!D#7ld4%?6ma0U#Oq z*B`-;-1^TK^e?u?>HO3gf2Ru682bPhI~#>$)UzG$t=&6;px5`4KUt&=;0}^zfjj!$ zHIL&_Vy%Mh&XA4;^U@Mu;TZ0k?d~rt_Y$dqX*77@>~)}ojih^Lqr!gQC(46HqsQxc z7wGw!W;g4E-=H_>Hwlskbw{z7hF2)=H+Uo@qQd7&rlLzxe*UWxv*G6uVjdd z!=oDu)olT0}>&Tf4hKSTVS>izNOj6Qj0J^%hCs>^n`CtzFBa z(w0lA|C+uaO)4;CZ+GqeK@Jl8PQ1c2kq2@%+w!;h#KaajI{!OW<8^F*pph%LTdh7*y^u6*zj0svl_Enk#E7ms&Um8BKykZ-=`=R=-@5wN;g)S$ zE9@k!)S1~?(GV#e*e@djQW{X!`&WQ9vcP-$X9HKx#9AGk!A(G=#>$mtP3U$d3`n^4 zdu1s8sLqU>ByH;5jQLbp*L#A)hQO_0e{^J%#;Nh>61zvuvcBe>g$5jZG1Is5%4Has zrT*wnjy@cDGUh9xdzh&@-{f;^sk;W?!rSEnjk&t0wu|?EXUF|gw+!T7K4)#GhY~al$O= zL!fD`HEJu@xzBpLzP*CtVr*QfILeu{SDW%*;W1$Hu6KNC4WK9dSdpEm?)uq%UHjsm z{lu?%pkw79ByNSHd zo4j8$o{o%$vjhVjYo!}x?|;Mbpfn865BH)#x9m$s&5k6U>Qh?!s9(QO~grRZ8DULhn<|yI~x9TH%7}3FsJKXP#26MI?yr!{7 z;oKrMa>^+*h5AD{>gOMncVsq}_8CSs>q6)|<||g~xs2#A90vjJedA-D{U~4!;r~86 z7t%M%6xQmOSO8jmOY0j}%q&FEexK5Aa{JbavKf_7=oUGY8gRiCINYL35n=?YY|N-z zvA&kFzf=qy9HtGQj)BZ}^oi!i&HV!q{cy!>)mOX)m(bXl*!+3GZ9m7|iQqh8U|V&w zTRO-x8FlVu9>QwB6aMF-Zd$=kW;fgpXIn8Id`@Y<$94Tc?1h6t*R)4+7y#$s?~eh` z3rXjiPj~_xT*s0r`p~qgVbaxeX6yPRydU_>Xv>OxqGAroEx-r^2Hj}z!)#`6W4==5 zA;ANu=ertfv??pbG%&S}+!5C2fX2zN{rQRBK9<1|Pt_Xhm=X4nm09Y=@%%OWfl+lz zj=Os8+2j%TWor6Os0=omq^W}1wUgyIsZY2@`-T)P+UlRVLzCVN$Yu$TLQ%4PwUsNW zm~4KjPmQHA)IdIzgLB9vGMY8Yp86&#R(Tk*t>e3ua56fB!~oB#`0QuP*4f0 z^@h*mq)tF1&O8lYrhk7)3zq%TtB^YlO(TIF3|Uiy)8=N!3yA5P{#-vvGUBb0Gl3Fr zwxBTbry>6}s8rrt)FPp3@}E0$8O@7t%LuT14&$zzy}JbJkx94b%Z&O-?%O3jNGkYy zfl`TqKiMm?+D&sxX9upr#{K-1QkAuRY0sulP)Bu34UftiHvY7L@9r`89XCDTK%DSey;a^7C4U?>jnI>r!6kt9h`gi)JBwb_o65uJ?`Q!QcXBhCWw9bfl%0JnXkhQ6a6t5gtfU80j@JJH*``+>q=ZsGTM1W>yV1 z(yE?Yo2$r9IaIk;FP(U%wnZH#+p)sNy;7%S?bX5J#Xt9#t@@q%6)g)(qRk`8f#O(% z`&nb}l)A)e=1y&!w{~uUv7&ICZu;9FPY=v&OtK44_~01^jP(K#SawiCiyR}F{7000 zm0jyc0kM)<3eAID=ZmY>agWoYF#&T40F%m=c70q z=3}s6z|X~bvc?|<`4$zk@Y6rlV#ak9pOi`HxzWp5S8?VJ{qJCJB=&+lnU%NBKwim3 z-yaTJJ%&a`e`x6FULl~?O;rN+H&Pj8`6{U}?BO*+KO_wwr9fEM!0`Q2q1vE6c$ zw@~ zxiXywfho7fI)fce3j)b+$%Q$cjSRaLKNOh-VizmTBjDoOsZ1*rPu&Q+_QXHP2Gn0N z#On*IZGUo!H)eK=A>Z~~HTBMF+J`yx-UEjgi{h5Mw4ZRkbwN-4ZJGG)*nZkzq!|I` zC4-?qynHnb_ZjE+Vibz=bNk4t)3o}&OpxRlP4sh>@4dw1(kQ%~4os0oZgd=>bQRb1u7rUfHTQ#6(iT$N1OlH#!l^Esf6ns19Y z?!@1uo8GxB@RkiHJ+91rZ6vO6U^t4Rk*^NX4wttpR+0VB89;`RnZqqrDIW8u(>+r9 zO}x~_=eF~3t8S&kP9*v;sSJe;FaRAfQdO47SMwmh+qwXXXpL;5jDSdjBs=`(JF^E` z-nU(z@7uW;ZcB!Og=3WcL6WQPVPBabppoDiHCPbGL6yu9FyfXjcECH)GCPoGnlm(_ zA$yIiUmlxBdS4E1#T+kEcMuEZz=}CsNgQ+Q>91BjM&rU5&8o84=c-~T#LgSbJ;DZ63aD3#r zgfnNB?*CgBu1Ci@X;YcEn+vqi&`e0i@e5Z2n*^4-X}UBrxInLa5uv8SY}CYa#q7v0 zz3KFY0mT;Z{)ptbVx{kr5t((qUaU?dN&Ww+PhWpl z;6n2P+OL&P4;cqOhkc*9<%tK`Fo@?oxGg&pk6kw%z%sXa(oEm+I)VWbt@va*FXDo} z;(Ct7%p@E}_8_{^@wafy4iApKrT$_y)Hs458&vrLPnWN3f`6*BSwsz6He+H|pjVSj ztUe;-$J{q-M$>f|I)@$Z!nIt01;UH2*d$UF(DKKG{Iu5VZ{1a>e{^-+O3!qeNEU^~ z7+?LSw!Pb$Do2X(wy4&d<`D0xwayefRJ#g>G1uJvl!9$`Tb+6V8LUrQ#`MFz9$5YM zNx;V6!obm%BAW|?DbY0@+^t}}aqlxOgqYNscHs08MwTjMqK1+yb-$#l4q2>OoCqB4 z`m1Rzt?liCXLvnH^H_Z8#peyLK11|Sx_j4!^i3;kjZms@ZzFV1iQ0q5AughNEt3Eo zykP%s^Y2PVIYIx4BxGA$T+~F@*nACsuP9Ep-w#_QM*BY1~48-pbASC^wk;I<_>TBodcDL$uV z*n7)YcmmbZ*Q%Ya&R`8zV(yRz0Zb2?1t_JkyM&X$KEEf_pazK?Ky*d|tuKmvUL-ub z`!$^{-CRS2F9;mWn}=}*ra;08D^=gbAXoGK@@KjCE1rr!akF&zIXxfj@)FS+%WF+B zHhQCHKNyz|_OOSR=zo}`S_GN@N=Dl4Yi@5=#%h(Zj_aZ-P|Kr30#P85D9Bh?yt126cb!bK8c0%=MouN zfF9c@U6^%G$tyVU*yqwIU_sn`4PyUAX~4=}Le>D}6)t=h;nbpMP7MO$U<~E1%ZJ~V z26updiWhTF(o8mZ9SE8SzhCU^Y5dZ?&$`6qcPv)a#zyV&T+{+E@SX5Kv0DuV;+Hq! zGDSlozTd~HDVsNOW3`BdO33+3^?Puyb~_g2-iD_FbM+7*`F;k;(=;_*@5I!=;t_5N zWTFWiXNIK1&{h_F94umpJW9hBbZs2d>QIL#{EEW9X zZy0MNIWV!Ha4a(uzaU&W4O0g0O6>LlTRYWoYtb-W^T1rB z6ys1`kzBY#JZlq2f{1vAjUCLMJi8Xb#r3Rf_H~QRzKFS8nI$sizr-WJ58v>w0bnJc z{l}H@D#1zHgXLu>R@<1{zNGendI19dKGz*U0~uL1tiQ7ZBJO6k4DGK-h_-$SjRy3I zvamMdJM35zh)dc!o~J$ zU%-Nd5h2i-?wAl;Ko(tCD@5+#&%;LGSo0S6#fu^d4MRf0-ZW+7Mr_abz|3JHq`8Ln z$Hi!IbDz@rIUIK})Wk*&jKKdjq07iL9uvZWiUfiH+kY&uHf>cUK{74i@Nqasga|rb z!Wn7&cALg5uDps8JimBfXu%Eulyf&12-rsF=l04C2d6r}b#{BZ9JA>tQIekDVtV$j z-_`*deNs%mRr$bMCHiX|AcuN2lJoqbbP>&=opqPaA=?SYy;%}4=Zu@pA!UB?ydEaP zTZvk!jZTOOF`f){Tg5Qy>Fob(nY{5XZD+gXsEzoMbxfubCLB zUjd_0`fq_M;15i*VU6?bgw*5Q^U7vU zGp1c4(~dtORC{uuH^}uIp#_hq36v2&$W&|pa9=K~DMslD*Y}9;w)5?*qG=<~II|S> z5>F%Nr-#1O4w7RtOrTNcr34e|<=Jph z_+mn!F}|V!6kC6N(#b_*?r0Xk;YqJF?cU-E!Gfr2=?a`*rfw~2u8LbHT>viOO!CT< zrq-}0Z)C4pq66V{?B8>j5853DP?2tvnopVr)u{K?DG@4keN5QaXFZ>0fr5=~@rdvRBWtU(S0+&b@gbN?}3dVSNdibnx$#>?4$% zy~Ry#)%Ocmv+x)*gGyfXf&XlQZ!RO0@_{^)l%X=?;%4t%(^EVf@}g5D;*DAA+Jj2v zui+E9_*(1iO}YYar78!%H6g*`Q%04L+ae!eK+&kp z_C@HPtVKOe8(GwSa0@tBzCoeC|*@EYleK}CP0#k5@ofyaUbf>sFUc=D2=xTr(GR6jN#*bo5 z$RpHyMMgc9i#$xVNPRjVx6^4=b;a)(3z5JBUp1m>b2MRBj-*d3`gd^|1YwG&i))oG ze0lGDA9#x8iu65pojm?P9#-ZPIWzrZ&-vnTqbpJVt`pp=;9Jo%CrWk`Y8mwK0wy`e ze$rzMH=EEzNyEJ_nb47?ngSkemwa&B+x&2c1@>m%WOOf1uiYN7g7~-0Xze!VGhZ*ffO@RWijP)e+oO zVV*!GVv#X?nIN4ZnU+*(&GdtBg+>*;9!uZwi$UPu_x=G?(b8hh1E}FCuN_EsfY#ft z3+u7zS7deohm!;}L`s{<8%cMp3JmW>O%1y9I1wCQ4EfH-kVe-b{yv*52Jsoh{?Pld zSImCwNa^CF;qEVrR-7_|K_#jlAfu3x|8`(uJC%i2SollMqHyQi3gig*<;f1j`TO?DW`?3mixsko}pE>*rD4sEJX|NDr;=AfoI zpEz_sIntJucUIiIW?VK!Im9OQ9I{9t`=XqwuEwV6aYj;$Rc^~f-^yHgn0TCDKz8y= z)J~}cnWAaiEiSf*VHHa19x(FtWcf$J0AwKbsqvuJS}xl^_DFhwz?83}pCHo){U6Bx z4@>%4WDT&f?QoyEM82lehF`<@c0i+LY!zN?Od^QO)1kK2^J^yRH7LFV*c;X>j)F^Z zC9MT|f>||7L45B!n+k7;4nx>eorE4yfeRjvuY<+ySt0cb?V||er>^n2R+kBtG7GS` zQVl6_xUHS0h|anYXT;?=BZ`IfRCHkDPU4Skf$!g^poI>m6R)A&kwC-#2Cli&QWntZ z*KY~k+>U=ED+BJWiRketasQLU6ii5VDzM+I5u%oB=OY@XfC=z>*l6fSQnQ+y(;Bx@ zEix^xDY*%VX=m)Git`wRf10P>$CY8EcB39hPrAw!>U2* z-tAO0nY-4lgMmRjoxN~2j5 zY=89Q8eqA{uk8pUxkzhcjZTut-tduqaxB;CQ8n^e(|@Qq6q3wrD4H`!@JAnY7!c7c z{`QwBd$!)LC})!nq&abQOplS;XJ--iZYwvU;_y*)Nrqf;h2^-uWfKWPb<#8p1QPeG zivPL2u5$&5O99Xf7I-o_SdpgH8t)Y6CC%Mq#yHQ_JotHNBpZfYW1UUb>0-PtBz$`j zt@86U^Xn?JfJA@yRa%exX+OhT?4LfaK?zy4b@~zZEv*7NRix1T)c}zH>O_>?OfR4v!F*Y;?RKLB|p zVGzfN*14bjXQG73rk1@T@m~WBO-9jkNTq?=H0e#yIDZ`EEalzAM18KE%mW=Pkm7?o zeh&+ZOOwA*8EXKV>A*_kpgu03=Yhi)z&L9t8?C!g3EpUs%S521Ut+1H3>L!aRb{n5)cKCy{$GFf1k zw+Mj+;R~b0GvNM?NGsbfk7aj8%26K(<#4W&#b?jBWD6vSAGu=Qj2o>BE%c2AL>*&| z(L~|9zvuWY4EI?_2C>^6_X0V2`RwQTHH0c~1wkp-i_8B_fVoP`+iaecyt zRGdTdBR*6%*1Bh4LTDm(I-vp$!Xk^$jX#bmo~5n{Nq3zsT~^n9Molf`y`_n0tFoKG zEC~-C|6w4T(el%THdV|(!CLnzAtBYZkmm67@o%L=G}Rf9r$L}oa<=Dbzc#*6rU(@n zl}!U40}$OdIM4?R~izMba?!T`b+J3F>OK zda(_*D7^7gDtHUBL2$UhcX6%Rs;)H|%06}lR4X&faEc3_5@MBJLhZsE`yVHDXxT(RFl zE&=-R_F=qq1266hU*}P?d}0S8uz;q2a@KOjqA=DMTmU;d;ZP}PEw^YrgLMX(W87!^ zbpUUmFw~ZCaG6_@f7vJlh@7=mL7bSmo9^lyN%iC(y6)%wbpM2}S<%ty%X?E&D8ix* zlN%+KYXru=4fqbDU>*>?hI6)}eTDPR(%}oaWhdu%{vUe8&fU#iG?XMKJL3)QS}-|E z3gK;nDpeJBMOoe7w+rNB5bRu*IFYb|Zl$$qja!O?Y8Zq|f9_RAa9Ry|u#f?yk99## zrz#$&AeJ{^aSk!Ep&bLZKu#`Z4YDL`iRrqK<{+7(rPghUK*}e#gh6S=_OAn!$f?i? za+BlQ>fi()^mSsVw>p(jbQ!JOa1H+Xp_Em3?XGTL zaD?*%iT1G^^p+92eZb-&i=gsR96(cYmnPBuyuVvZ3Q_2n2=mlxAEo4 z@9QwP$o81sZJ}h=YNecLh>)hfub3ZU2Vtqn`uHj|;WI85_VE-D6MTnigQ)e$ey#PryhfgKH9NQReAPX9&% z6OBNEftzr9E+;s_kZ^5z5^upH9+;TBH9AeAtz!F^@i4-)QXgkyrnFHbcxdol*$B=c zhtq1dvy%!>rA2)D+*c$ccdSymEHZq8Y(Otp`M(NK8Td2kFtiH$`IQ}#`K$un7X5j@Jz z-%7plc+~m?xA*Oni%*U%r+pWCOnC7HT}8>7kbgj<^D}JihDv4*ZEZ%i^+Id33y>_@sOgliaCBx z_u5U5c$Je~6^Y@o&t~y-4yGxQ3tjMKNS-W<9uXFp^|d~+cq(A1MaLqV9HqP>o3Mar zRW2;S$bKwDVrf|sbw)BlzKqI+j#?so!%G4+q?2^5c@efZFs50o+4^%=K_wL6{mvx~ zE;JgaM%DLnv5^g{2qWk;2n~lM_i_a}epM>a0zrsCOCqwtOgaag!TDgXYOZIS0lJVk z(B3|=HpY!ypRe67Hq?g(imZ~6+AlrlMkJU2HhDHk<~*dAD5ABDc9A2}{Nw|#?VQ^* z2v?leH%Q&v44s?T#|DAcmB>|QuKMt1LWsgBLuKA&Ck`kLLPY$?e4$ITGBrM>U+*lc zHd3HgA`Hj^rH`MNFjjyi^nqN#-UEH*EUM;#y`Y3~(S~|>(^>P2kut(d9_eYKYl8w~ zDohIh%zi$EjLzhOvs667+C9-qysiUZyN4FCCs&b;)SVN%!|(BpLVnXy^@Kn93$?m! zhDorqyFAU84M-(vdsoxeS??Wp+s@wQaF=KXxjH$a#D)(N)a|)E)B0x=0oQMqF6qx| zNFdv1lu~E89o6>s`GMTCK%ZC)qH6g6`>5>^!pOnryl*CcskTW>juRCso>`nkhV>~+ zO_n3$5U?sZK@!vALLC^R*}sX97T#a|!x3Yu1Di%PFxUMvP>JWRUx%3}CgjO3#6c1` zqN}|!LRR>;+L8f-Vxea95ex{!MtCHSFs>|`D4Naz8Y4FH4igfVSR?WWM#MyY_RT~v zOF3X9@PdUF5&VKfa{7b8K$Fi1MvyNP6#(;F0t^&fXx{4hK~nZpw))c=qJs`EPJkM2 z5Z2)+ut$?sND<@{+hGJ1{}tt6a4%hGms|5d1x4=(6*vh_$#uIOi@qpHe)Ze*N7Jqt z1Ts^+zf|I!MvJ{2I7<(M*5*pY0?QxuTTDg2xV%$VW3uXRAwE2`q>`MqF!^Jzf3l)^ zZQxiM@?i*O9ig;bC6t zN?GjcSn!ZXet5?uItRH%#HkqacQE`lh-NO^y~*rf2y>kWePLtw@TBF^&PoI{R^WwO zh&%a`t}X&#R{PJoPRYbC+U;B?jzaui*5dV-H;j=V2F++fkWvPqsv8_5RXK0pRk~fR zB2Pdx?y{eU5b}PeP=QtV(NfpAhRlrTdr_T+oi_+R@WUlavBi#$_Wy8qT)G~)WaV0( zr8N6L>>K(QhWIu8P6Np1NsDbaOu6I0Zv<9fw}wEx;>Ac1J=o;l2i+u7A09!B(;5Zs zH1WN}K?F_Nsu_CSM;>vGaKaj!Fo-^1S1=1lpJy0`FqbwaMB@(=&NKE81i$e7>hZg2 zSGRkW4>YMdLL1)2s$8{triaMY4vwt$eNr{Pp}_A z&_u|&E%Zho4s}UCsbV9vQ6?mSHqcQsYnGR2MK?&~s9haE3W7W-+Uc})zL*bz252r= zHTV-{@@kvCPP;RdLDUhQ#m6-wvvwVhYL>O34AGyPHEdh;(-<-5nCrAti`(3c>*nAtjwkw{&+1NFV8xuDg9opZnhX z{=eN%mvIJTjh%DPHP>8o&G;=sCG(Yq*An6a@>Qrc&vj6>IVH7Dd-dzL6>>D0BP}1! zxFbA?)eXsZ%ch#t9t-3WOh-M-qCb>xJ8vE9Jb?{xnFD_E~NRqSbX!=o_Qn1E}V z=YC!lsN-zf>-y-e^Y3ig*X(m>5kn1KF5ZLhYo-6lm-3 zK7AYWltrRfld+l_J~}&09M$Z$z?_wq6%yihGqd-}zNTN1s}wS{U8Kv-n&K`m%9zA3 zZPCJZ5@IhJ{(w{NgJErUTy~SSJL|kVW=&xndPjW?DI5z=X@vU}f>!5B-$KF_I;z9d z?&m!L{L6v2c|=YIpn`*rZb>V$-Of_hZ+b0k)Q{u0k?^e!AAZP$*|>KGm*;)+;z7d} zZ2BwlE~h+<$BY>* zek>B|!$4h|X74Bw!%bq!;l}8jUfYqHX=?`Tk(f_DhIyKdp>-&KBs;8RcDqY>{qY^pOfMFDpVDF z=6KR3z_B)1Ho?{3YUqL14%n61I`#&Fm?rP^NJPhU`Z#pq0|9(-?9%eK>{%k$KJ69? z+#qx?NVO)GrQy=l_OY?d$3&i(oDKJf2xY=i%UbR7$LFV*7Go5;AZD<)^X>av#_> zf8ue8_Hfb!;LE&MuFdd!vb1cri#PfE_EY;-XL}fN4h}&X2BCY|*0PzvG9;eyg}M#Y zZ^)c{A=ae14Yk-;n+P-4JpvQ8e?f)VQ%iBTn$L<1(SEb(?QD;pf;hN@0;A+3;?7Dp z@^$j}Hw&>1-x*p3OQS8*rmKfN_qcZ#+*6e&<_glrRTkS`$v3h%`YpqH8qNZJGRfq> zklNlggvEWRbZX-%{@`9@YPsibSiUTEbwu8%+@IB{S_k? zsjy?0?CHjUlFz`ch#QZ7s(YWgE)&1HIUL#6gF!{vveP`aQ8h4NBa#svg^seZL(C9z z_G&hWKQ8B{jUy)AbUV7aO(icD)bx}+eUYT%pl=6$Uz+r^?z1Vn$hwlctfD>3K$uMm zN9_?3=Zawh@VV-dF-lQ<)993thFWTB9{3_@jQU-Ngsgzu`MO)iwyxKSxuTEyD9REX zD(nM###0VP1joa(o$E%%PnirNrhTAZ=AYlCF4HpEsN0FWjd8>M3R`i0A-Lw9%biv4 zG#S&z!ds1lu}oy_SklM4mlAn@`w|Slh^n@J4)4h`O&2ZdIk10@I6!rwEPc|3El!?6 z;zbY~i4J<9V5U4+T<7Bk1SKPm_j2=3+}-Y_RHh&ANiC*-!e19*rG^7Blq}~urv@c( zj-0F|1AFd4Cx9*G@QWXDrC4=--V2l5!d-8ij8I!H`a+-)J8LCctf(9i+;FcV4(LOk zQ}Ti(31Ca15r!>Xzn%$kjZ-samc+qOyl`>*nV4@JHkxKzvIqCz=ku>RQBRjwKThh@ z9=Q6jV=fU>OW{+QiU+@Ta?rr$q}=y~Fu52P^`m+-(306YCfJ^gY!udYGPBRC7`d^0dgtNr&YBWsN|_Zf z$3APn(f0Dru;&YQ@3W8OO>mJUmfjiXXG-wUvC%9_Q|_DRSq%B%?jQowf|?;QP|Go1 zl9^zZ%v#gQWDJz6u98I|Osa2rC@oVxeK3=<5uS2FE2$`B@ItLUTth1X<{uNI2q34; z-N)^yFZf&Fk~f_Z3~m_veEjU>a{TUl>TrkWm(;o*#k$>|QMicnylf>$wpK8f72-hH zfX3X{=3oLgHj_ZmK3i%e^<9;#vmab7VHNrgRw!jICH}~=Rk_U?W{~__8uywJ%0^Oo zDSS}FEBY|S7iLE$8{w?g#}J$cD_$lAkZuL2=O&2@WN3C!*2YvZ|km^$(~e98@9jqGiGsy z5b^_MGC3yYI^#&oc0K|_%cAijz7!HWxe$ zCh&B16Y9(NPc>vuzi@zXCdEG;&O_2?ui58T8o$}#zBe5v^;y|}Lh2fsAN6Tc?cK?1Al3V^ z2;5euM&>cFoaOqms;M<}7C5kby=bml-&G=N`M8$zn9$=O*9)V|`@LujC{HnL9GglZ znM1lvn_0K7N)*OM80h;wo9`i1`aYuwmo2&E`=|y2%b|L0zM~-lm zMB%r3ja!d2Uv)AfNV8xT$r)kQv0N^Z`Y?I*KLG-WjG}=hBEQXnVnTr%8BZxWP=W|nMszQ7DSa|WY)#ir z+ATmOnE8aawLmB|?(MUIy)~&{oe$$I;%00Qk0D%qs>973`%=C~?usnbTlna+@(dPw z!n!T8TMKLET@9{22QtNdL;FEq<@P=abF5=3I1}>;?Ksvh{S4!-KycUITQ^jiMUx*@ zrTkLLV<_P|-J1dYRW3V0b&CaH(L@|Byr4>M$GLs5n&!p)#Q>6E>@M=aCS>A0S64h>1D+cqKOKaiV&l6ThyYkguu;4l*Zw-Mxz}sHRZBN=fTatdz`P4&{=#KH# zzGtv&$Efmyb7jORF3Db(&Zg3~sJnJo6cHGP@G)JrYp$sWdaaV-Lf@ozXR@HdT0t>J zXIGWD@g{B_&SJ_p_OKIAyCCCEY~tFTW!!Hg&mF_MkSkGkl3G=f`dvTkOTl(|SM?TlDImvU4&JaHc3@E{Q;Dd+ zg$dtYyec?H@FG8;F}hSg1N$0)ajMNcvRR>3{T}8nAKEtkf%yvcc-Ke5_*jE(a^o6q zB0TD$$OiT_;la03tpT1+%a@|7R#x|dp%pnBH2boLsqhb<%T~S z!|0~)*#P|BP2uSP!)Aqqb&!|F=XB6`#r{En$>kktBbq?MilV;;3~Q(PI9u3*w3X~V zRZcw*`V%JWk$^H??B^#WvSnnIADhd+88vIjlpC37Q8?4cvR`h~z_QDQiBXAipXZpr zMHA>)jP}=dMv&>S`zXNjvaLxFM(Aug)TG#Bj(I*q70bI36aT|wT}Y<5p)Mx3))hqm zYTPh1k{Ku3p=M1(Kxu`(!`*B@UHTwtN_l2;Zm>;i9}X*}ZnSdd!D?iSbCBykOiq&` zCVj;-c4_0h+$*vXSxVaq7l37T!u41p+DwEl=Wf==Logq5&`O%@Es#9Uzk0gFyNAS? zy!=SEM8~4ojERIxw_UC_t`mmyq1Q;_kpMQ-%ro`8DpIPZT~NkhM}6n}ZPUT+(%CJ- zNNX-a2{bDQKtov{>p&@S=@BaGtFON)CuZEsLb>mUYldTt~huAFm?&f zAa#Rz29aez+-tn7_v95^0#6R#oo7x?P&Ga@E8`If#=|mF8Djz!!oq9@yIYlSH1l1l zO*3J-9^Tpx1jWZ}bO{S5UoCY8i^JL;I#(8e@dv)*4IYWm06yTp>qM}#U^2gdkP*@V zM6z_NuQ8!1Kn8|s{C->T*-LM5>5OvyT%7A0`I20o1@Df^I+xtXtt>2Sw4++AJ!i6_H0@Vo^aBsKUrwS(r}A?>0C!(`bVYW934l-6(#^Q$DguT z{90D#xA|J$Ds|}k(_2XUI^#OZoS7P zqnZ7Igr1A)Y}QBYrOrVQssK8rPP~?%U^cNCepJz!G)oP~dV3876SaMF>+ZImv^hY3 z{0OnNl5i|`n4L6%j8&j5@y2lE`b!pfzNc}50o%(NW%mL1JGP87l5=`pF@2#RzDhymaVJ(m^KkkKq|*gyoM)r(A^JGlo}cyBWKq|e1qt+v4ddxCR>d zxuDHI)QPGI>HX*kj>^qc5<{L1a*GLPP>{*`!rSBZ;ka0A=ahc(dJ?6ndJ2(f<31_d{-11q?@DK(2|bknbt+&mq+9vJEJo{wIw7v znJ)~b!J`$gW8B?}1b65v)K*m;22Jawh)3ugIa0OGFtFrFlGHG3@zvL-77wJH@YQcr zf=%DX=zK?{Svo^$R!lYU1Yxjb;gaQ3ltFx=~ z0(DN_l%4{Ii$FyjwV^n6ZBE6`*f94KE)x1>WTdF85`C%&oGcTKm*!8c7+NOtHXH5R zy~RGxcE{U2-1Au7iZ!~+91K@<#2a8mZajH1soy7RKq>GlT#rp;=dEqvb_z$trhUD0aA$o} zj*>LmydxX13?8z)zYVB9AWL4j1n1u_@>X(<(d zl6<(}KHUMG;?Q{ZPcZU{hzzowVX@~WByvlw=-El#f(`V)wo~5pyWd#q5 zrBDk|>k|F+^eHgfaOxp(m|+!elhWB?r)9eP65@{Z^-{H8ZPMa0*^}Zde3gYp=XKcx z&9^Q(jt)c3_tO~s$?khqJBeC>*6789Oz~m_aK1}4buYcnjWF7&C7%Rp4Si-<7KY0d zy@0Xdfq6Hx8d=!lfy|IL9<0}I6Ldk%d)?JeN<#k%8VX|*GV-s;RSUn9eiOlyZPu^; z8VRJ7126M*riL7g84qLL*7?@Ia@(bYwDZSoKy~__-FM*up|f@EsIOB}GcQZ-!n}9; zrCskl4|rk?e4=#)Jv2H|FiFC~&*-I4s6}k}q{R@p_sm)hWn>!1#vLc!Dj6j(h{U61 z%HP$IeBBFM2qzy5OE)8>CshBBYvyW{XQH*TIOc2+@74NKVA;U9ZZ}9~M%1?PT$sf|hinoVd3s*`maP_Ol z7$*G^4N^Vfo#v~3VS+&)Oo+|iqfQr67IExXEpcbGrKg99@$84E5r>H`R8b_ySfzz{ zqrktbTCiU6S3*pGfraD>dfJ$jUQDW^&KNlrX{NZp#fP!#btZY(DCkKay+LP6IdPnA zYu56KCA&j1zwXqa(#f-vv~j0==*2C%9SXf-dtZiXKdC)P+(r^H%V{M+x*)&tH`o z;SU-sz)xPrDJoqVuH~s{K;%H-bu5=GlU;+UNY5)6j%~X8x`j>&^r1wO7=y}y(esfu zG^;CICp##q#m)FIdkcO9ucD`QDmp}0!0<`rM~}f{9DP`uB(==kQ*$kqfPsDIjrDX7 zbOJdHqw7c?mbYoj@5IQv=W6As^8lm#jX=$0&c77CYx zSF3OC$xFJb9v9VD9EqthEm%yeWSSEfQya1HL6_@;>y;7?DR2`LW98hdZG+^c=!nZH z7umk}nf<7B9LzI*HM?+`S;j+F862#ZWjW*vRhl-Vg%di!+;!~^cpHLO_WhGTd92y(Q@IJk-^nQDlit8o2SBIp)ph=vx;l+ZLwOSjTDHr2-3^|p zQeE}2L^PD=o~;KT*J1^r?-7FEV)NX5nR;mOT=T};*tggce|Wm|H0zsr&&!@-QV=5e zg+sv;iez~p;KySpn}3V>tnmJ8{gbfwiFnoSn-LoQ=2R21CZ@u;(6+CA?)RtucFM?R zbV{XPpH6R>xUOWm36zIj=c`2l1ZzQ7I~i4S$4QbcuVWYx;IW-%-dKNhmu5`*!NES$ z)tob^*k09b*icsN*mrGHn?!rI-aamFUT-FyzSEO5w5BaM;FeMMRRV}A>)0y|QUwA9 zS<;?I$z9l%`kl|6PPO!3&e}qYe5#a-6(OsUn!e7c{@kZiMTOUF1>JMj8OO_>v6vvc zl6uuj3A_vH{^t&V=Ah292XY#P<%0D1=h&?se+#G6#tFg$_}f4od1FF1sB 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 ZBb?{b Date: Fri, 30 Aug 2024 09:53:10 +0200 Subject: [PATCH 068/317] docs: update old page and create new one Signed-off-by: Camila Belo --- ...adding-a-resource-permission-check--old.md | 299 ++++++++++++++++++ .../03-adding-a-resource-permission-check.md | 17 +- 2 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 docs/permissions/plugin-authors/03-adding-a-resource-permission-check--old.md diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check--old.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check--old.md new file mode 100644 index 0000000000..ee06629e71 --- /dev/null +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check--old.md @@ -0,0 +1,299 @@ +--- +id: 03-adding-a-resource-permission-check--old +title: 3. Adding a resource permission check +description: Explains how to add a resource permission check to a Backstage plugin +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./03-adding-a-resource-permission-check.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + +When performing updates (or other operations) on specific [resources](../../references/glossary.md#resource-permission-plugin), the permissions framework allows for the decision to be based on characteristics of the resource itself. This means that it's possible to write policies that (for example) allow the operation for users that own a resource, and deny the operation otherwise. + +## Creating the update permission + +Let's add a new permission to the file `plugins/todo-list-common/src/permissions.ts` from [the previous section](./02-adding-a-basic-permission-check.md). + +```ts title="plugins/todo-list-common/src/permissions.ts" +import { createPermission } from '@backstage/plugin-permission-common'; + +/* highlight-add-next-line */ +export const TODO_LIST_RESOURCE_TYPE = 'todo-item'; + +export const todoListCreatePermission = createPermission({ + name: 'todo.list.create', + attributes: { action: 'create' }, +}); + +/* highlight-add-start */ +export const todoListUpdatePermission = createPermission({ + name: 'todo.list.update', + attributes: { action: 'update' }, + resourceType: TODO_LIST_RESOURCE_TYPE, +}); +/* highlight-add-end */ + +/* highlight-remove-next-line */ +export const todoListPermissions = [todoListCreatePermission]; +/* highlight-add-start */ +export const todoListPermissions = [ + todoListCreatePermission, + todoListUpdatePermission, +]; +/* highlight-add-end */ +``` + +Notice that unlike `todoListCreatePermission`, the `todoListUpdatePermission` permission contains a `resourceType` field. This field indicates to the permission framework that this permission is intended to be authorized in the context of a resource with type `'todo-item'`. You can use whatever string you like as the resource type, as long as you use the same value consistently for each type of resource. + +## Setting up authorization for the update permission + +To start, let's edit `plugins/todo-list-backend/src/service/router.ts` in the same manner as we did in the previous section: + +```ts title="plugins/todo-list-backend/src/service/router.ts" +/* highlight-remove-next-line */ +import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; +/* highlight-add-start */ +import { + todoListCreatePermission, + todoListUpdatePermission, +} from '@internal/plugin-todo-list-common'; +/* highlight-add-end */ + +// ... + +const permissionIntegrationRouter = createPermissionIntegrationRouter({ + /* highlight-remove-next-line */ + permissions: [todoListCreatePermission], + /* highlight-add-next-line */ + permissions: [todoListCreatePermission, todoListUpdatePermission], +}); + +// ... + +router.put('/todos', async (req, res) => { + /* highlight-add-start */ + const token = getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ); + /* highlight-add-end */ + + if (!isTodoUpdateRequest(req.body)) { + throw new InputError('Invalid payload'); + } + /* highlight-add-start */ + const decision = ( + await permissions.authorize( + [{ permission: todoListUpdatePermission, resourceRef: req.body.id }], + { + token, + }, + ) + )[0]; + + if (decision.result !== AuthorizeResult.ALLOW) { + throw new NotAllowedError('Unauthorized'); + } + /* highlight-add-end */ + + res.json(update(req.body)); +}); +``` + +**Important:** Notice that we are passing an extra `resourceRef` field, with the `id` of the todo item as the value. + +This enables decisions based on characteristics of the resource, but it's important to note that policy authors will not have access to the resource ref inside of their permission policies. Instead, the policies will return conditional decisions, which we need to now support in our plugin. + +## Adding support for conditional decisions + +Install the missing module: + +```bash +$ yarn workspace @internal/plugin-todo-list-backend add zod +``` + +Create a new `plugins/todo-list-backend/src/service/rules.ts` file and append the following code: + +```typescript title="plugins/todo-list-backend/src/service/rules.ts" +import { makeCreatePermissionRule } from '@backstage/plugin-permission-node'; +import { TODO_LIST_RESOURCE_TYPE } from '@internal/plugin-todo-list-common'; +import { z } from 'zod'; +import { Todo, TodoFilter } from './todos'; + +export const createTodoListPermissionRule = makeCreatePermissionRule< + Todo, + TodoFilter, + typeof TODO_LIST_RESOURCE_TYPE +>(); + +export const isOwner = createTodoListPermissionRule({ + name: 'IS_OWNER', + description: 'Should allow only if the todo belongs to the user', + resourceType: TODO_LIST_RESOURCE_TYPE, + paramsSchema: z.object({ + userId: z.string().describe('User ID to match on the resource'), + }), + apply: (resource: Todo, { userId }) => { + return resource.author === userId; + }, + toQuery: ({ userId }) => { + return { + property: 'author', + values: [userId], + }; + }, +}); + +export const rules = { isOwner }; +``` + +`makeCreatePermissionRule` is a helper used to ensure that rules created for this plugin use consistent types for the resource and query. + +:::note Note + +To support custom rules defined by Backstage integrators, you must export `createTodoListPermissionRule` from the backend package and provide some way for custom rules to be passed in before the backend starts, likely via `createRouter`. + +::: + +We have created a new `isOwner` rule, which is going to be automatically used by the permission framework whenever a conditional response is returned in response to an authorized request with an attached `resourceRef`. +Specifically, the `apply` function is used to understand whether the passed resource should be authorized or not. + +Let's skip the `toQuery` function for now, we'll come back to that in the next section. + +Now, let's create the new endpoint by editing `plugins/todo-list-backend/src/service/router.ts`. This uses the `createPermissionIntegrationRouter` helper to add the APIs needed by the permission framework to your plugin. You'll need to supply: + +- `getResources`: a function that accepts an array of `resourceRefs` in the same format you expect to be passed to `authorize`, and returns an array of the corresponding resources. +- `resourceType`: the same value used in the permission rule above. +- `permissions`: the list of permissions that your plugin accepts. +- `rules`: an array of all the permission rules you want to support in conditional decisions. + +```ts title="plugins/todo-list-backend/src/service/router.ts" +// ... +import { + /* highlight-add-next-line */ + TODO_LIST_RESOURCE_TYPE, + todoListCreatePermission, + todoListUpdatePermission, +} from '@internal/plugin-todo-list-common'; +/* highlight-remove-next-line */ +import { add, getAll, update } from './todos'; +/* highlight-add-start */ +import { add, getAll, getTodo, update } from './todos'; +import { rules } from './rules'; +/* highlight-add-end */ + +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger, identity, permissions } = options; + + const permissionIntegrationRouter = createPermissionIntegrationRouter({ + permissions: [todoListCreatePermission, todoListUpdatePermission], + /* highlight-add-start */ + getResources: async resourceRefs => { + return resourceRefs.map(getTodo); + }, + resourceType: TODO_LIST_RESOURCE_TYPE, + rules: Object.values(rules), + /* highlight-add-end */ + }); + + const router = Router(); + router.use(express.json()); + + // ... +} +``` + +## Provide utilities for policy authors + +Now that we have a new resource type and a corresponding rule, we need to export some utilities for policy authors to reference them. + +Create a new `plugins/todo-list-backend/src/conditionExports.ts` file and add the following code: + +```typescript title="plugins/todo-list-backend/src/conditionExports.ts" +import { TODO_LIST_RESOURCE_TYPE } from '@internal/plugin-todo-list-common'; +import { createConditionExports } from '@backstage/plugin-permission-node'; +import { rules } from './service/rules'; + +const { conditions, createConditionalDecision } = createConditionExports({ + pluginId: 'todolist', + resourceType: TODO_LIST_RESOURCE_TYPE, + rules, +}); + +export const todoListConditions = conditions; + +export const createTodoListConditionalDecision = createConditionalDecision; +``` + +Make sure `todoListConditions` and `createTodoListConditionalDecision` are exported from the `todo-list-backend` package by editing `plugins/todo-list-backend/src/index.ts`: + +```ts title="plugins/todo-list-backend/src/index.ts" +export * from './service/router'; +/* highlight-add-next-line */ +export * from './conditionExports'; +export { exampleTodoListPlugin } from './plugin'; +``` + +## Test the authorized update endpoint + +Let's go back to the permission policy's handle function and try to authorize our new permission with an `isOwner` condition. + +```ts title="packages/backend/src/plugins/permission.ts" +import { + IdentityClient +} from '@backstage/plugin-auth-node'; +import { + PermissionPolicy, + PolicyQuery, + PolicyQueryUser, +} from '@backstage/plugin-permission-node'; +import { isPermission } from '@backstage/plugin-permission-common'; +/* highlight-remove-next-line */ +import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; +/* highlight-add-start */ +import { + todoListCreatePermission, + todoListUpdatePermission, +} from '@internal/plugin-todo-list-common'; +import { + todoListConditions, + createTodoListConditionalDecision, +} from '@internal/plugin-todo-list-backend'; +/* highlight-add-end */ + + +async handle( + request: PolicyQuery, + /* highlight-remove-next-line */ + _user?: PolicyQueryUser, + /* highlight-add-next-line */ + user?: PolicyQueryUser, +): Promise { + if (isPermission(request.permission, todoListCreatePermission)) { + return { + result: AuthorizeResult.ALLOW, + }; + } + /* highlight-add-start */ + if (isPermission(request.permission, todoListUpdatePermission)) { + return createTodoListConditionalDecision( + request.permission, + todoListConditions.isOwner({ + userId: user?.info.userEntityRef ?? '', + }), + ); + } + /* highlight-add-end */ + + return { + result: AuthorizeResult.ALLOW, + }; +} +``` + +For any incoming update requests, we now return a _Conditional Decision_. We are saying: + +> Hey permission framework, I can't make a decision alone. Please go to the plugin with id `todolist` and ask it to apply these conditions. + +To check that everything works as expected, you should now see an error in the UI whenever you try to edit an item that wasn’t created by you. Success! diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index 7ad551e926..5cfa6329b4 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -4,6 +4,10 @@ title: 3. Adding a resource permission check description: Explains how to add a resource permission check to a Backstage plugin --- +:::info +This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./03-adding-a-resource-permission-check--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + When performing updates (or other operations) on specific [resources](../../references/glossary.md#resource-permission-plugin), the permissions framework allows for the decision to be based on characteristics of the resource itself. This means that it's possible to write policies that (for example) allow the operation for users that own a resource, and deny the operation otherwise. ## Creating the update permission @@ -68,9 +72,7 @@ const permissionIntegrationRouter = createPermissionIntegrationRouter({ router.put('/todos', async (req, res) => { /* highlight-add-start */ - const token = getBearerTokenFromAuthorizationHeader( - req.header('authorization'), - ); + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); /* highlight-add-end */ if (!isTodoUpdateRequest(req.body)) { @@ -80,9 +82,10 @@ router.put('/todos', async (req, res) => { const decision = ( await permissions.authorize( [{ permission: todoListUpdatePermission, resourceRef: req.body.id }], - { - token, - }, + await auth.getPluginRequestToken({ + onBehalfOf: credentials, + targetPluginId: 'permission', + }), ) )[0]; @@ -146,7 +149,7 @@ export const rules = { isOwner }; :::note Note -To support custom rules defined by Backstage integrators, you must export `createTodoListPermissionRule` from the backend package and provide some way for custom rules to be passed in before the backend starts, likely via `createRouter`. +To support custom rules defined by Backstage integrators, you must export `createTodoListPermissionRule` from the backend package and provide some way for custom rules to be passed in before the backend starts, likely via `extension point`. ::: From 2a27cb9be75a2fea8a7510826d87c165270ed986 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 29 Aug 2024 17:00:57 +0200 Subject: [PATCH 069/317] refactor: apply review suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Camila Belo --- ...02-adding-a-basic-permission-check--old.md | 2 +- .../02-adding-a-basic-permission-check.md | 38 +++++++------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md index 7c51214359..7c8dad3d87 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check--old.md @@ -1,5 +1,5 @@ --- -id: 02-adding-a-basic-permission-check +id: 02-adding-a-basic-permission-check--old title: 2. Adding a basic permission check description: Explains how to add a basic permission check to a Backstage plugin --- diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index a1873ea1d3..2d39d5cef3 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -61,23 +61,21 @@ Edit `plugins/todo-list-backend/src/service/router.ts`: ```ts title="plugins/todo-list-backend/src/service/router.ts" /* highlight-remove-start */ import { InputError } from '@backstage/errors'; -import { HttpAuthService, LoggerService } from '@backstage/backend-plugin-api'; +import { LoggerService, HttpAuthService } from '@backstage/backend-plugin-api'; /* highlight-remove-end */ /* highlight-add-start */ import { InputError, NotAllowedError } from '@backstage/errors'; -import { AuthService, HttpAuthService, LoggerService } from '@backstage/backend-plugin-api'; -import { AuthorizeResult, PermissionEvaluator } from '@backstage/plugin-permission-common'; +import { LoggerService, HttpAuthService, PermissionsService } from '@backstage/backend-plugin-api'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; import { todoListCreatePermission } from '@internal/plugin-todo-list-common'; /* highlight-add-end */ export interface RouterOptions { logger: LoggerService; + httpAuth: HttpAuthService; /* highlight-add-next-line */ - auth: AuthService; - identity: IdentityApi; - /* highlight-add-next-line */ - permissions: PermissionEvaluator; + permissions: PermissionsService; } export async function createRouter( @@ -86,7 +84,7 @@ export async function createRouter( /* highlight-remove-next-line */ const { logger, httpAuth } = options; /* highlight-add-next-line */ - const { logger, auth, httpAuth, permissions } = options; + const { logger, httpAuth, permissions } = options; /* highlight-add-start */ const permissionIntegrationRouter = createPermissionIntegrationRouter({ @@ -115,13 +113,11 @@ export async function createRouter( const user = await identity.getIdentity({ request: req }); author = user?.identity.userEntityRef; /* highlight-add-start */ + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); const decision = ( await permissions.authorize( [{ permission: todoListCreatePermission }], - await auth.getPluginRequestToken({ - onBehalfOf: credentials, - targetPluginId: 'permission', - }), + { credentials }, ) )[0]; @@ -141,7 +137,7 @@ export async function createRouter( // ... ``` -Pass the `auth` and `permissions` objects to the plugin in `plugins/todo-list-backend/src/plugin.ts`: +Pass the `permissions` object to the plugin in `plugins/todo-list-backend/src/plugin.ts`: ```ts title="plugins/todo-list-backend/src/plugin.ts" import { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'; @@ -153,25 +149,21 @@ export const exampleTodoListPlugin = createBackendPlugin({ env.registerInit({ deps: { logger: coreServices.logger, - /* highlight-add-start */ - auth: coreServices.auth, - permissions: coreServices.permissions, - /* highlight-add-end */ httpAuth: coreServices.httpAuth, httpRouter: coreServices.httpRouter, + /* highlight-add-next-line */ + permissions: coreServices.permissions, }, /* highlight-remove-next-line */ async init({ logger, httpAuth, httpRouter }) { /* highlight-add-next-line */ - async init({ logger, auth, httpAuth, httpRouter, permissions }) { + async init({ logger, httpAuth, httpRouter, permissions }) { httpRouter.use( await createRouter({ logger, - /* highlight-add-start */ - auth, - permissions, - /* highlight-add-end */ httpAuth, + /* highlight-add-next-line */ + permissions, }), ); httpRouter.addAuthPolicy({ @@ -279,8 +271,6 @@ describe('createRouter', () => { beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), - /* highlight-add-next-line */ - auth: mockServices.auth.mock(), httpAuth: mockServices.httpAuth.mock(), /* highlight-add-next-line */ permissions: mockServices.permissions.mock(), From 2727488e3c7880b5facaa1238bb5670c3f5a2176 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 30 Aug 2024 10:04:43 +0200 Subject: [PATCH 070/317] refactor: apply review suggestions Signed-off-by: Camila Belo Co-authored-by: Patrik Oldsberg --- .../plugin-authors/03-adding-a-resource-permission-check.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index 5cfa6329b4..3f5a9777b3 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -82,10 +82,7 @@ router.put('/todos', async (req, res) => { const decision = ( await permissions.authorize( [{ permission: todoListUpdatePermission, resourceRef: req.body.id }], - await auth.getPluginRequestToken({ - onBehalfOf: credentials, - targetPluginId: 'permission', - }), + { credentials }, ) )[0]; From ed81ab07e2a38588684fabadc7aaa19b3c192c59 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Fri, 30 Aug 2024 09:13:53 +0300 Subject: [PATCH 071/317] docs(debug): add information about profiling Add information to documentation how to profile and stress test Backstage application. Signed-off-by: Heikki Hellgren --- docs/tooling/local-dev/profiling.md | 65 +++++++++++++++++++++++++++++ microsite/sidebars.json | 3 +- mkdocs.yml | 1 + 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 docs/tooling/local-dev/profiling.md diff --git a/docs/tooling/local-dev/profiling.md b/docs/tooling/local-dev/profiling.md new file mode 100644 index 0000000000..bbc263c3d5 --- /dev/null +++ b/docs/tooling/local-dev/profiling.md @@ -0,0 +1,65 @@ +--- +id: profiling +title: Profiling Backstage +description: Finding performance bottlenecks in your Backstage application +--- + +Profiling can help you find performance bottlenecks in your code. This guide will show you how to profile +both the backend and frontend of your Backstage application. + +# Backend + +To profile the backend, start the backend with the `--inspect` flag: + +```shell +yarn workspace backend start --inspect +# or `yarn workspace example-backend start --inspect` in this repository +``` + +Next you can use the Chrome DevTools to profile the backend by navigating to `chrome://inspect` and +clicking the `Open dedicated DevTools for Node` link. + +In the `Performance` tab, you can start a new recording by clicking the `Start` button. After you +have recorded some data, you can stop the recording by clicking the `Stop` button. The recording +will show you a flame graph of the backend's execution, which can help you identify performance issues. + +You can also use the `Memory` tab to profile the backend's memory usage and find potential memory leaks. + +It's recommended to start profiling with short periods of time to avoid too much data being collected. + +## Stress testing + +To get more out of profiling, you might want to introduce additional load to your application with some tooling. +One such tool is called [AutoCannon](https://www.npmjs.com/package/autocannon) which can be used to stress test the +API endpoints of your application. You can install it globally with `npm install -g autocannon`. + +To be able to access the API endpoints, you must configure a static token for the backend. In your +`app-config.yaml` file, add the following configuration: + +```yaml +backend: + auth: + externalAccess: + - type: static + options: + token: autocannon12345 + subject: autocannon +``` + +See more information in the [Service to Service Auth](../../auth/service-to-service-auth.md) documentation. +To run the stress test, you can use the following command: + +```shell +autocannon -H "Authorization=Bearer autocannon12345" http://localhost:7007/api/catalog/entities +``` + +See more command options in the AutoCannon documentation. + +# Frontend + +Profiling the frontend can be done by using the `React DevTools` extension for Chrome or Firefox. +The extension is available for download from the Chrome Web Store or the Firefox Add-ons website. + +To start profiling, start the application with `yarn dev` and open inspector in the browser. In the +`Profiler` tab (far to the right), click the `Start profiling` button to start recording. After +you have recorded some data by navigating through the page, click the `Stop profiling` button to stop the recording. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index baca4f4db6..6038229bf0 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -366,7 +366,8 @@ "label": "Local Development", "items": [ "tooling/local-dev/linking-local-packages", - "tooling/local-dev/debugging" + "tooling/local-dev/debugging", + "tooling/local-dev/profiling" ] }, "tooling/package-metadata" diff --git a/mkdocs.yml b/mkdocs.yml index 7bc56695b8..4d4d039756 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -182,6 +182,7 @@ nav: - Local Development: - Linking in Local Packages: 'tooling/local-dev/linking-local-packages.md' - Debugging Backstage: 'tooling/local-dev/debugging.md' + - Profiling Backstage: 'tooling/local-dev/profiling.md' - Package Metadata: 'tooling/package-metadata.md' - Deployment: - Deploying Backstage: 'deployment/index.md' From d3534f4548cce09541ecee1f91aca1e3a30d3674 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 30 Aug 2024 12:19:28 +0200 Subject: [PATCH 072/317] docs: update old and new pages Signed-off-by: Camila Belo --- ...thorizing-access-to-paginated-data--old.md | 193 ++++++++++++++++++ ...04-authorizing-access-to-paginated-data.md | 13 +- 2 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data--old.md diff --git a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data--old.md b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data--old.md new file mode 100644 index 0000000000..cca6899ef6 --- /dev/null +++ b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data--old.md @@ -0,0 +1,193 @@ +--- +id: 04-authorizing-access-to-paginated-data--old +title: 4. Authorizing access to paginated data +description: Explains how to authorize access to paginated data in a Backstage plugin +--- + +:::info +This documentation is written for the old backend which has been replaced by [the new backend system](../../backend-system/index.md), being the default since Backstage [version 1.24](../../releases/v1.24.0.md). If have migrated to the new backend system, you may want to read [its own article](./04-authorizing-access-to-paginated-data.md) instead. Otherwise, [consider migrating](../../backend-system/building-backends/08-migrating.md)! +::: + +Authorizing `GET /todos` is similar to the update endpoint, in that it should be possible to authorize access based on the characteristics of each resource. However, we'll need to authorize a list of resources for this endpoint. + +One possible solution may leverage the batching functionality to authorize all of the todos, and then returning only the ones for which the decision was `ALLOW`: + +```ts +router.get('/todos', async (req, res) => { + /* highlight-add-next-line */ + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); + + /* highlight-remove-next-line */ + res.json(getAll()); + /* highlight-add-start */ + const items = getAll(); + const decisions = await permissions.authorize( + items.map(({ id }) => ({ + permission: todoListReadPermission, + resourceRef: id, + })), + { credentials }, + ); + + const filteredItems = decisions.filter( + decision => decision.result === AuthorizeResult.ALLOW, + ); + res.json(filteredItems); + /* highlight-add-end */ +}); +``` + +This approach will work for simple cases, but it has a downside: it forces us to retrieve all the elements upfront and authorize them one by one. This forces the plugin implementation to handle concerns like pagination, which is currently handled by the data source. + +To avoid this situation, the permissions framework has support for filtering items in the data source itself. In this part of the tutorial, we'll describe the steps required to use that behavior. + +:::note Note + +In order to perform authorization filtering in this way, the data source must allow filters to be logically combined with AND, OR, and NOT operators. The conditional decisions returned by the permissions framework use a [nested object](https://backstage.io/docs/reference/plugin-permission-common.permissioncriteria) to combine conditions. If you're implementing a filter API from scratch, we recommend using the same shape for ease of interoperability. If not, you'll need to implement a function which transforms the nested object into your own format. + +::: + +## Creating the read permission + +Let's add another permission to the plugin. + +```ts title="plugins/todo-list-backend/src/service/permissions.ts" +import { createPermission } from '@backstage/plugin-permission-common'; + +export const TODO_LIST_RESOURCE_TYPE = 'todo-item'; + +export const todoListCreatePermission = createPermission({ + name: 'todo.list.create', + attributes: { action: 'create' }, +}); + +export const todoListUpdatePermission = createPermission({ + name: 'todo.list.update', + attributes: { action: 'update' }, + resourceType: TODO_LIST_RESOURCE_TYPE, +}); + +/* highlight-add-start */ +export const todoListReadPermission = createPermission({ + name: 'todos.list.read', + attributes: { action: 'read' }, + resourceType: TODO_LIST_RESOURCE_TYPE, +}); +/* highlight-add-end */ + +export const todoListPermissions = [ + todoListCreatePermission, + todoListUpdatePermission, + /* highlight-add-start */ + todoListReadPermission, + /* highlight-add-end */ +]; +``` + +## Using conditional policy decisions + +So far we've only used the `PermissionsService.authorize` method, which will evaluate conditional decisions before returning a result. In this step, we want to evaluate conditional decisions within our plugin, so we'll use `PermissionsService.authorizeConditional` instead. + +```ts title="plugins/todo-list-backend/src/service/router.ts" +/* highlight-remove-next-line */ +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; +/* highlight-add-start */ +import { + createPermissionIntegrationRouter, + createConditionTransformer, + ConditionTransformer, +} from '@backstage/plugin-permission-node'; +/* highlight-add-end */ +/* highlight-remove-next-line */ +import { add, getAll, getTodo, update } from './todos'; +/* highlight-add-next-line */ +import { add, getAll, getTodo, TodoFilter, update } from './todos'; +import { + TODO_LIST_RESOURCE_TYPE, + todoListCreatePermission, + todoListUpdatePermission, + /* highlight-add-next-line */ + todoListReadPermission, +} from './permissions'; + +// ... + +const permissionIntegrationRouter = createPermissionIntegrationRouter({ + /* highlight-remove-next-line */ + permissions: [todoListCreatePermission, todoListUpdatePermission], + /* highlight-add-next-line */ + permissions: [todoListCreatePermission, todoListUpdatePermission, todoListReadPermission], + getResources: async resourceRefs => { + return resourceRefs.map(getTodo); + }, + resourceType: TODO_LIST_RESOURCE_TYPE, + rules: Object.values(rules), +}); + +// ... + +/* highlight-add-next-line */ +const transformConditions: ConditionTransformer = createConditionTransformer(Object.values(rules)); + +/* highlight-remove-next-line */ +router.get('/todos', async (_req, res) => { +/* highlight-add-start */ +router.get('/todos', async (req, res) => { + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); + + const decision = ( + await permissions.authorizeConditional([{ permission: todoListReadPermission }], { + credentials, + }) + )[0]; + + if (decision.result === AuthorizeResult.DENY) { + throw new NotAllowedError('Unauthorized'); + } + + if (decision.result === AuthorizeResult.CONDITIONAL) { + const filter = transformConditions(decision.conditions); + res.json(getAll(filter)); + } else { + res.json(getAll()); + } +/* highlight-add-end */ + /* highlight-remove-next-line */ + res.json(getAll()); +}); +``` + +To make the process of handling conditional decisions easier, the permission framework provides a `createConditionTransformer` helper. This function accepts an array of permission rules, and returns a transformer function which converts the conditions to the format needed by the plugin using the `toQuery` method defined on each rule. + +Since `TodoFilter` used in our plugin matches the structure of the conditions object, we can directly pass the output of our condition transformer. If the filters were structured differently, we'd need to transform it further before passing it to the api. + +## Test the authorized read endpoint + +Let's update our permission policy to return a conditional result whenever a `todoListReadPermission` permission is received. In this case, we can reuse the decision returned for the `todosListCreate` permission. + +```ts title="packages/backend/src/plugins/permission.ts" +import { + todoListCreatePermission, + todoListUpdatePermission, + /* highlight-add-next-line */ + todoListReadPermission, +} from '@internal/plugin-todo-list-common'; + +/* highlight-remove-next-line */ +if (isPermission(request.permission, todoListUpdatePermission)) { +/* highlight-add-start */ +if ( + isPermission(request.permission, todoListUpdatePermission) || + isPermission(request.permission, todoListReadPermission) +) { +/* highlight-add-end */ + return createTodoListConditionalDecision( + request.permission, + todoListConditions.isOwner({ + userId: user?.identity.userEntityRef + }), + ); +} +``` + +Once the changes to the permission policy are saved, the UI should show only the todo items you've created. diff --git a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md index 84e86140fc..883884b6e5 100644 --- a/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md +++ b/docs/permissions/plugin-authors/04-authorizing-access-to-paginated-data.md @@ -4,6 +4,8 @@ title: 4. Authorizing access to paginated data description: Explains how to authorize access to paginated data in a Backstage plugin --- +This documentation is written for [the new backend system](../../backend-system/index.md) which is the default since Backstage [version 1.24](../../releases/v1.24.0.md). If you are still on the old backend system, you may want to read [its own article](./04-authorizing-access-to-paginated-data--old.md) instead, and [consider migrating](../../backend-system/building-backends/08-migrating.md)! + Authorizing `GET /todos` is similar to the update endpoint, in that it should be possible to authorize access based on the characteristics of each resource. However, we'll need to authorize a list of resources for this endpoint. One possible solution may leverage the batching functionality to authorize all of the todos, and then returning only the ones for which the decision was `ALLOW`: @@ -11,7 +13,7 @@ One possible solution may leverage the batching functionality to authorize all o ```ts router.get('/todos', async (req, res) => { /* highlight-add-next-line */ - const token = IdentityClient.getBearerToken(req.header('authorization')); + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); /* highlight-remove-next-line */ res.json(getAll()); @@ -22,6 +24,7 @@ router.get('/todos', async (req, res) => { permission: todoListReadPermission, resourceRef: id, })), + { credentials }, ); const filteredItems = decisions.filter( @@ -81,7 +84,7 @@ export const todoListPermissions = [ ## Using conditional policy decisions -So far we've only used the `PermissionEvaluator.authorize` method, which will evaluate conditional decisions before returning a result. In this step, we want to evaluate conditional decisions within our plugin, so we'll use `PermissionEvaluator.authorizeConditional` instead. +So far we've only used the `PermissionsService.authorize` method, which will evaluate conditional decisions before returning a result. In this step, we want to evaluate conditional decisions within our plugin, so we'll use `PermissionsService.authorizeConditional` instead. ```ts title="plugins/todo-list-backend/src/service/router.ts" /* highlight-remove-next-line */ @@ -128,13 +131,11 @@ const transformConditions: ConditionTransformer = createConditionTra router.get('/todos', async (_req, res) => { /* highlight-add-start */ router.get('/todos', async (req, res) => { - const token = getBearerTokenFromAuthorizationHeader( - req.header('authorization'), - ); + const credentials = await httpAuth.credentials(req, { allow: ['user'] }); const decision = ( await permissions.authorizeConditional([{ permission: todoListReadPermission }], { - token, + credentials, }) )[0]; From 0bee00f386e1411a0101d570abb8c49d39693271 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 30 Aug 2024 12:20:33 +0200 Subject: [PATCH 073/317] docs: Update auth provider docs Signed-off-by: Johan Haals --- docs/auth/add-auth-provider.md | 299 +++++++++++++-------------------- 1 file changed, 116 insertions(+), 183 deletions(-) diff --git a/docs/auth/add-auth-provider.md b/docs/auth/add-auth-provider.md index d1dad0b183..c8ed0315ee 100644 --- a/docs/auth/add-auth-provider.md +++ b/docs/auth/add-auth-provider.md @@ -1,13 +1,12 @@ --- id: add-auth-provider -title: Contributing New Providers +title: Contributing New Provider Modules description: Documentation on adding new authentication providers --- :::note Note -The primary audience for this documentation are contributors to the main -Backstage project that want to add support for new authentication providers. +The primary audience for this documentation are contributors that want to add support for new authentication providers. While you can follow it to implement your own custom providers it is much more advanced than using our built-in providers. @@ -122,191 +121,128 @@ due to its comprehensive set of supported authentication ### Quick guide -[1.](#installing-the-dependencies) Install the passport-js based provider -package. +[1.](#create-new-auth-provider-module) Create a new auth provider module -[2.](#create-implementation) Create a new folder structure for the provider. +[3.](#adding-an-oauth-based-provider) or [adding a proxy auth based provider](#creating-proxy-auth-based-provider) depending on your needs. -[3.](#adding-an-oauth-based-provider) Implement the provider, extending the -suitable framework if needed. +[4.](#add-the-provider-to-the-backend) Add the provider to the backend. -[4.](#hook-it-up-to-the-backend) Add the provider to the backend. +### Create new auth provider module -### Installing the dependencies: +Create a new module using `yarn new`, pick `backend-module` and provide `auth-backend` as the plugin ID and `providername-provider` as the module ID. + +Make sure that the module has the appropriate passport provider as a dependency. ```bash -cd plugins/auth-backend +cd plugins/auth-backend-backend-module-providername-provider yarn add passport-provider-a yarn add @types/passport-provider-a ``` -### Create implementation - -Make a new folder with the name of the provider following the below file -structure: - -```bash -plugins/auth-backend/src/providers/providerA -├── index.ts -└── provider.ts -``` - -**`plugins/auth-backend/src/providers/providerA/provider.ts`** defines the -provider class which implements a handler for the chosen framework. - ### Adding an OAuth based provider -If we're adding an `OAuth` based provider we would implement the -`OAuthHandlers` interface. By implementing this -interface we can use the `OAuthProvider` class provided by `lib/oauth`, meaning -we don't need to implement the full -`AuthProviderRouteHandlers` interface that providers -otherwise need to implement. +We're then creating a new module that can extend the Auth backend using the `authProvidersExtensionPoint`. -The provider class takes the provider's options as a class parameter. It also -imports the `Strategy` from the passport package. +```ts title="plugins/auth-backend-providername-provider/src/module.ts" +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + commonSignInResolvers, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { providerAuthenticator } from './authenticator'; -```ts -import { Strategy as ProviderAStrategy } from 'passport-provider-a'; - -export type ProviderAProviderOptions = OAuthProviderOptions & { - // extra options here -} - -export class ProviderAAuthProvider implements OAuthHandlers { - private readonly _strategy: ProviderAStrategy; - - constructor(options: ProviderAProviderOptions) { - this._strategy = new ProviderAStrategy( - { - clientID: options.clientId, - clientSecret: options.clientSecret, - callbackURL: options.callbackUrl, - passReqToCallback: false, - response_type: 'code', - /// ... etc - } - verifyFunction, // See the "Verify Callback" section - ); - } - - async start() {} - async handler() {} -} -``` - -### Adding an non-OAuth based provider - -An non-`OAuth` based provider could implement -`AuthProviderRouteHandlers` instead. - -```ts -type ProviderAOptions = { - // ... -}; - -export class ProviderAAuthProvider implements AuthProviderRouteHandlers { - private readonly _strategy: ProviderAStrategy; - - constructor(options: ProviderAOptions) { - this._strategy = new ProviderAStrategy( - { - // ... +/** @public */ +export const authModuleOktaProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'providername', + register(reg) { + reg.registerInit({ + deps: { + providers: authProvidersExtensionPoint, }, - verifyFunction, // See the "Verify Callback" section - ); - } - - async start() {} - async frameHandler() {} - async logout() {} - async refresh() {} // If supported -} -``` - -#### Integration Wrapper - -Each provider exports an object that provides a way to create new instances -of the provider, along with related utilities like predefined sign-in resolvers. - -The object is created using `createAuthProviderIntegration`, with the most -important part being the `create` method that acts as the factory function -for our provider. - -The factory should return an implementation of `AuthProviderFactory`, which -passes in a object with utilities for configuration, logging, token issuing, -etc. The factory should return an implementation of -`AuthProviderRouteHandlers`. - -The factory is what decides the mapping from -[static configuration](../conf/index.md) to the creation of auth providers. For -example, OAuth providers use `OAuthEnvironmentHandler` to allow for multiple -different configurations, one for each environment, which looks like this; - -```ts -export const okta = createAuthProviderIntegration({ - create(options?: { - /** - * The profile transformation function used to verify and convert the auth response - * into the profile that will be presented to the user. - */ - authHandler?: AuthHandler; - - /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. - */ - signIn?: { - /** - * Maps an auth result to a Backstage identity for the user. - */ - resolver: SignInResolver; - }; - }) { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - // read options from config - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - - // Use provided auth handler, or create a default one - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile, params.id_token), - }); - - // instantiate our OAuthHandlers implementation - const provider = new OktaAuthProvider({ - audience, - clientId, - clientSecret, - callbackUrl, - authHandler, - signInResolver: options?.signIn?.resolver, - resolverContext, + async init({ providers }) { + providers.registerProvider({ + providerId: 'providername', + factory: createOAuthProviderFactory({ + authenticator: providerAuthenticator, + signInResolverFactories: { + ...commonSignInResolvers, + }, + }), }); - - // Wrap the OAuthHandlers with OAuthProvider, which implements AuthProviderRouteHandlers - return OAuthProvider.fromConfig(globalConfig, provider, { - providerId, - tokenIssuer, - }); - }); - }, - resolvers: { - /** - * Looks up the user by matching their email local part to the entity name. - */ - emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, - - // ... additional predefined resolvers + }, + }); }, }); ``` -The purpose of the different environments is to allow for a single auth-backend -to serve as the authentication service for multiple different frontend -environments, such as local development, staging, and production. +Now let's implement the actual authenticator for our provider using `Strategy` from a passport package. + +```ts title="plugins/auth-backend-providername-provider/src/authenticator.ts" +import { Strategy as ProviderStrategy } from 'passport-provider-a'; +import { + createOAuthAuthenticator, + PassportOAuthAuthenticatorHelper, + PassportOAuthDoneCallback, + PassportProfile, +} from '@backstage/plugin-auth-node'; + +/** @public */ +export const providerAuthenticator = createOAuthAuthenticator({ + defaultProfileTransform: + PassportOAuthAuthenticatorHelper.defaultProfileTransform, + scopes: { + // Scopes required by the provider + required: ['openid', 'email', 'profile', 'offline_access'], + }, + initialize({ callbackUrl, config }) { + const clientId = config.getString('clientId'); + const clientSecret = config.getString('clientSecret'); + + return PassportOAuthAuthenticatorHelper.from( + new ProviderStrategy( + { + clientID: clientId, + clientSecret: clientSecret, + // ... other options + }, + ( + accessToken: string, + refreshToken: string, + params: any, + fullProfile: PassportProfile, + done: PassportOAuthDoneCallback, + ) => { + done( + undefined, + { fullProfile, params, accessToken }, + { refreshToken }, + ); + }, + ), + ); + }, + + async start(input, helper) { + return helper.start(input); + }, + + async authenticate(input, helper) { + return helper.authenticate(input); + }, + + async refresh(input, helper) { + return helper.refresh(input); + }, +}); +``` + +### Creating proxy auth based provider + +A proxy auth provider is a provider that uses another provider to authenticate for example Google IAP or AWS ALB, please note that those providers are already supported by Backstage. + +The implementation is similar to the OAuth provider, but the `authenticator` function is different. There are already some examples on how to implement a proxy provider in the codebase, for example [auth-backend-module-gcp-iap-provider](https://github.com/backstage/backstage/tree/master/plugins/auth-backend-module-gcp-iap-provider) and [auth-backend-module-aws-alb-provider](https://github.com/backstage/backstage/tree/master/plugins/auth-backend-module-aws-alb-provider) #### Verify Callback @@ -323,27 +259,24 @@ environments, such as local development, staging, and production. > > http://www.passportjs.org/docs/configure/ -**`plugins/auth-backend/src/providers/providerA/index.ts`** is simply -re-exporting the factory function to be used for hooking the provider up to the -backend. +### Add the provider to the backend + +The process for adding the new module is the same as for any other type of module or backend plugin. + +If this provider is internal to your installation the import path that you add to `packages/backend/src/index.ts` would be something like: ```ts -export { createProviderAProvider } from './provider'; +backend.add( + import('@internal/plugin-auth-backend-module-providername-provider'), +); ``` -### Hook it up to the backend - -**`plugins/auth-backend/src/providers/factories.ts`** When the `auth-backend` -starts it sets up routing for all the available providers by calling -the factory function of each provider. You need to import the factory -function from the provider and add it to the factory: +But if this module is contributed directly to Backstage the module would be imported as ```ts -import { createProviderAProvider } from './providerA'; - -const factories: { [providerId: string]: AuthProviderFactory } = { - providerA: createProviderAProvider, -}; +backend.add( + import('@backstage/plugin-auth-backend-module-providername-provider'), +); ``` By doing this `auth-backend` automatically adds these endpoints: From f0c6b252cc62ff378a4c3e2503961465a054f18f Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Thu, 29 Aug 2024 11:20:02 +0300 Subject: [PATCH 074/317] feat: allow listing file contents with `debug:log` This further improves the debug functionality to be able to actually see what is the content of the files generated by the template Signed-off-by: Heikki Hellgren --- .changeset/fair-peaches-learn.md | 5 +++ plugins/scaffolder-backend/api-report.md | 2 +- .../actions/builtin/debug/log.examples.ts | 15 +++++++ .../actions/builtin/debug/log.test.ts | 33 +++++++++++++- .../scaffolder/actions/builtin/debug/log.ts | 45 ++++++++++--------- 5 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 .changeset/fair-peaches-learn.md diff --git a/.changeset/fair-peaches-learn.md b/.changeset/fair-peaches-learn.md new file mode 100644 index 0000000000..772980ee57 --- /dev/null +++ b/.changeset/fair-peaches-learn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Allow listing file contents with `debug:log` scaffolder action diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 00f690c646..3d84c30bcf 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -130,7 +130,7 @@ export function createCatalogWriteAction(): TemplateAction_2< export function createDebugLogAction(): TemplateAction_2< { message?: string | undefined; - listWorkspace?: boolean | undefined; + listWorkspace?: boolean | 'with-contents' | 'with-filenames' | undefined; }, JsonObject >; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.ts index 0e9173bfa6..c1ad6a2982 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.examples.ts @@ -48,4 +48,19 @@ export const examples: TemplateExample[] = [ ], }), }, + { + description: 'List the workspace directory with file contents', + example: yaml.stringify({ + steps: [ + { + action: 'debug:log', + id: 'write-workspace-directory', + name: 'List the workspace directory with file contents', + input: { + listWorkspace: 'with-contents', + }, + }, + ], + }), + }, ]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.test.ts index 9c626f1dfe..4606a01e0c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.test.ts @@ -35,8 +35,9 @@ describe('debug:log', () => { beforeEach(() => { mockDir.setContent({ - [`${mockContext.workspacePath}/README.md`]: '', - [`${mockContext.workspacePath}/a-directory/index.md`]: '', + [`${mockContext.workspacePath}/README.md`]: 'This is a README file', + [`${mockContext.workspacePath}/a-directory/index.md`]: + 'This is a markdown file', }); jest.resetAllMocks(); }); @@ -94,6 +95,34 @@ describe('debug:log', () => { ); }); + it('should log the workspace content with file contents from an example, if active', async () => { + const example = action.examples?.find( + sample => + sample.description === + 'List the workspace directory with file contents', + )?.example as string; + expect(typeof example).toEqual('string'); + const context = { + ...mockContext, + ...yaml.parse(example).steps[0], + }; + + await action.handler(context); + + expect(logger.info).toHaveBeenCalledWith( + expect.stringContaining('README.md'), + ); + expect(logger.info).toHaveBeenCalledWith( + expect.stringContaining(join('a-directory', 'index.md')), + ); + expect(logger.info).toHaveBeenCalledWith( + expect.stringContaining('This is a README file'), + ); + expect(logger.info).toHaveBeenCalledWith( + expect.stringContaining('This is a markdown file'), + ); + }); + it('should log message from an example', async () => { const example = action.examples?.find( sample => sample.description === 'Write a debug message', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts index 98ab02437e..2ff64e1025 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts @@ -15,9 +15,11 @@ */ import { readdir, stat } from 'fs-extra'; -import { relative, join } from 'path'; +import { join, relative } from 'path'; import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; import { examples } from './log.examples'; +import fs from 'fs'; +import { z } from 'zod'; const id = 'debug:log'; @@ -32,28 +34,24 @@ const id = 'debug:log'; * @public */ export function createDebugLogAction() { - return createTemplateAction<{ message?: string; listWorkspace?: boolean }>({ + return createTemplateAction<{ + message?: string; + listWorkspace?: boolean | 'with-filenames' | 'with-contents'; + }>({ id, description: - 'Writes a message into the log or lists all files in the workspace.', + 'Writes a message into the log and/or lists all files in the workspace.', examples, schema: { - input: { - type: 'object', - properties: { - message: { - title: 'Message to output.', - type: 'string', - }, - listWorkspace: { - title: 'List all files in the workspace, if true.', - type: 'boolean', - }, - extra: { - title: 'Extra info', - }, - }, - }, + input: z.object({ + message: z.string({ description: 'Message to output.' }).optional(), + listWorkspace: z + .union([z.boolean(), z.enum(['with-filenames', 'with-contents'])], { + description: + 'List all files in the workspace. If used with "with-contents", also the file contents are listed.', + }) + .optional(), + }), }, supportsDryRun: true, async handler(ctx) { @@ -67,7 +65,14 @@ export function createDebugLogAction() { const files = await recursiveReadDir(ctx.workspacePath); ctx.logger.info( `Workspace:\n${files - .map(f => ` - ${relative(ctx.workspacePath, f)}`) + .map(f => { + const relativePath = relative(ctx.workspacePath, f); + if (ctx.input?.listWorkspace === 'with-contents') { + const content = fs.readFileSync(f, 'utf-8'); + return ` - ${relativePath}:\n\n ${content}`; + } + return ` - ${relativePath}`; + }) .join('\n')}`, ); } From 055b75b39f60406d77b91c39eb05407d7214744b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 30 Aug 2024 13:20:18 +0200 Subject: [PATCH 075/317] remove deprecated exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/wise-forks-play.md | 10 ++++++ packages/backend-common/api-report.md | 12 +------ .../backend-common/src/deprecated/index.ts | 16 +--------- .../backend-defaults/api-report-database.md | 14 +------- .../entrypoints/database/DatabaseManager.ts | 32 ++----------------- .../src/entrypoints/database/index.ts | 2 -- 6 files changed, 15 insertions(+), 71 deletions(-) create mode 100644 .changeset/wise-forks-play.md diff --git a/.changeset/wise-forks-play.md b/.changeset/wise-forks-play.md new file mode 100644 index 0000000000..84a6da9003 --- /dev/null +++ b/.changeset/wise-forks-play.md @@ -0,0 +1,10 @@ +--- +'@backstage/backend-common': minor +'@backstage/backend-defaults': minor +--- + +**BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. + +- The deprecated `dropDatabase` function has now been removed, without replacement. +- The deprecated `LegacyRootDatabaseService` type has now been removed. The implication is that you now need to pass deps to a `DatabaseManager` when constructing one. +- The return type from `DatabaseManager.forPlugin` is now directly a `DatabaseService`, as arguably expected. diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 7de2731518..2555ab6f5b 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -143,7 +143,7 @@ export function createStatusCheckRouter(options: { }): Promise; // @public @deprecated (undocumented) -export class DatabaseManager implements LegacyRootDatabaseService { +export class DatabaseManager { // (undocumented) forPlugin( pluginId: string, @@ -173,11 +173,6 @@ export class DockerContainerRunner implements ContainerRunner { runContainer(options: RunContainerOptions): Promise; } -// Warning: (ae-forgotten-export) The symbol "dropDatabase_2" needs to be exported by the entry point index.d.ts -// -// @public @deprecated (undocumented) -export const dropDatabase: typeof dropDatabase_2; - // @public @deprecated export function errorHandler( options?: ErrorHandlerOptions, @@ -367,11 +362,6 @@ export const legacyPlugin: ( }>, ) => BackendFeature; -// Warning: (ae-forgotten-export) The symbol "LegacyRootDatabaseService_2" needs to be exported by the entry point index.d.ts -// -// @public @deprecated (undocumented) -export type LegacyRootDatabaseService = LegacyRootDatabaseService_2; - // @public @deprecated export function loadBackendConfig(options: { logger: LoggerService; diff --git a/packages/backend-common/src/deprecated/index.ts b/packages/backend-common/src/deprecated/index.ts index 5dfcd322ed..d34b81aa92 100644 --- a/packages/backend-common/src/deprecated/index.ts +++ b/packages/backend-common/src/deprecated/index.ts @@ -30,10 +30,8 @@ import { // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { - dropDatabase as _dropDatabase, DatabaseManager as _DatabaseManager, type DatabaseManagerOptions as _DatabaseManagerOptions, - type LegacyRootDatabaseService as _LegacyRootDatabaseService, } from '../../../backend-defaults/src/entrypoints/database/DatabaseManager'; import { @@ -169,7 +167,7 @@ export type CacheClientOptions = CacheServiceOptions; * @public * @deprecated Use `DatabaseManager` from the `@backstage/backend-defaults` package instead */ -export class DatabaseManager implements LegacyRootDatabaseService { +export class DatabaseManager { private constructor(private readonly _databaseManager: _DatabaseManager) {} static fromConfig( @@ -202,18 +200,6 @@ export type DatabaseManagerOptions = _DatabaseManagerOptions; */ export type PluginDatabaseManager = _PluginDatabaseManager; -/** - * @public - * @deprecated Use `LegacyRootDatabaseService` from the `@backstage/backend-defaults` package instead - */ -export type LegacyRootDatabaseService = _LegacyRootDatabaseService; - -/** - * @public - * @deprecated Use `dropDatabase` from the `@backstage/backend-defaults` package instead - */ -export const dropDatabase = _dropDatabase; - /** * @public * @deprecated This function is deprecated and will be removed in a future release, see https://github.com/backstage/backstage/issues/24493. diff --git a/packages/backend-defaults/api-report-database.md b/packages/backend-defaults/api-report-database.md index 49ffa75ca7..8f3857fe23 100644 --- a/packages/backend-defaults/api-report-database.md +++ b/packages/backend-defaults/api-report-database.md @@ -3,7 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Config } from '@backstage/config'; import { DatabaseService } from '@backstage/backend-plugin-api'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; @@ -12,7 +11,7 @@ import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public -export class DatabaseManager implements LegacyRootDatabaseService { +export class DatabaseManager { forPlugin( pluginId: string, deps?: { @@ -39,16 +38,5 @@ export const databaseServiceFactory: ServiceFactory< 'singleton' >; -// @public @deprecated -export function dropDatabase( - dbConfig: Config, - ...databaseNames: string[] -): Promise; - -// @public -export type LegacyRootDatabaseService = { - forPlugin(pluginId: string): DatabaseService; -}; - // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts index 88d95443bb..1230280e07 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts @@ -46,18 +46,10 @@ export type DatabaseManagerOptions = { logger?: LoggerService; }; -/** - * An interface that represents the legacy global DatabaseManager implementation. - * @public - */ -export type LegacyRootDatabaseService = { - forPlugin(pluginId: string): DatabaseService; -}; - /** * Testable implementation class for {@link DatabaseManager} below. */ -export class DatabaseManagerImpl implements LegacyRootDatabaseService { +export class DatabaseManagerImpl { constructor( private readonly config: Config, private readonly connectors: Record, @@ -193,7 +185,7 @@ export class DatabaseManagerImpl implements LegacyRootDatabaseService { * set `prefix` which is used to prefix generated database names if config is * not provided. */ -export class DatabaseManager implements LegacyRootDatabaseService { +export class DatabaseManager { /** * Creates a {@link DatabaseManager} from `backend.database` config. * @@ -241,23 +233,3 @@ export class DatabaseManager implements LegacyRootDatabaseService { return this.impl.forPlugin(pluginId, deps); } } - -/** - * Helper for deleting databases. - * - * @public - * @deprecated Will be removed in a future release. - */ -export async function dropDatabase( - dbConfig: Config, - ...databaseNames: string[] -): Promise { - const client = dbConfig.getString('client'); - const prefix = dbConfig.getOptionalString('prefix') || 'backstage_plugin_'; - - if (client === 'pg') { - await new PgConnector(dbConfig, prefix).dropDatabase(...databaseNames); - } else if (client === 'mysql' || client === 'mysql2') { - await new MysqlConnector(dbConfig, prefix).dropDatabase(...databaseNames); - } -} diff --git a/packages/backend-defaults/src/entrypoints/database/index.ts b/packages/backend-defaults/src/entrypoints/database/index.ts index 7d6221b856..e30d06cbdd 100644 --- a/packages/backend-defaults/src/entrypoints/database/index.ts +++ b/packages/backend-defaults/src/entrypoints/database/index.ts @@ -18,6 +18,4 @@ export { databaseServiceFactory } from './databaseServiceFactory'; export { DatabaseManager, type DatabaseManagerOptions, - type LegacyRootDatabaseService, - dropDatabase, } from './DatabaseManager'; From fd66330d4a375555b4891190cb83805297d12986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 30 Aug 2024 13:42:38 +0200 Subject: [PATCH 076/317] remove unused DatabaseConnector type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../src/entrypoints/database/types.ts | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/database/types.ts b/packages/backend-defaults/src/entrypoints/database/types.ts index a9cceaa1f9..74f41581f6 100644 --- a/packages/backend-defaults/src/entrypoints/database/types.ts +++ b/packages/backend-defaults/src/entrypoints/database/types.ts @@ -18,75 +18,10 @@ import { LifecycleService, PluginMetadataService, } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; import { Knex } from 'knex'; export type { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api'; -/** - * Manages an underlying Knex database driver. - */ -export interface DatabaseConnector { - /** - * Provides an instance of a knex database connector. - */ - createClient( - dbConfig: Config, - overrides?: Partial, - deps?: { - lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; - }, - ): Knex; - - /** - * Provides a partial knex config sufficient to override a database name. - */ - createNameOverride(name: string): Partial; - - /** - * Provides a partial knex config sufficient to override a PostgreSQL schema - * name within utilizing the `searchPath` knex configuration. - */ - createSchemaOverride?(name: string): Partial; - - /** - * Produces a knex connection config object representing a database connection - * string. - */ - parseConnectionString( - connectionString: string, - client?: string, - ): Knex.StaticConnectionConfig; - - /** - * Performs a side-effect to ensure database names passed in are present. - * - * Calling this function on databases which already exist should do nothing. - * Missing databases should be created if needed. - */ - ensureDatabaseExists?( - dbConfig: Config, - ...databases: Array - ): Promise; - - /** - * Performs a side-effect to ensure schema names passed in are present. - * - * Calling this function on schemas which already exist should do nothing. - * Missing schemas should be created if needed. - */ - ensureSchemaExists?( - dbConfig: Config, - ...schemas: Array - ): Promise; - - /** - * Deletes databases. - */ - dropDatabase?(dbConfig: Config, ...databases: Array): Promise; -} - export interface Connector { getClient( pluginId: string, From e46754a18b1bcd9e63e3ae6a56b80be3eaf52c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 30 Aug 2024 13:46:40 +0200 Subject: [PATCH 077/317] remove the unused dropDatabase in the connector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../src/entrypoints/database/DatabaseManager.test.ts | 6 ------ .../src/entrypoints/database/connectors/mysql.ts | 4 ---- .../src/entrypoints/database/connectors/postgres.ts | 4 ---- .../src/entrypoints/database/connectors/sqlite3.ts | 4 ---- packages/backend-defaults/src/entrypoints/database/types.ts | 2 -- 5 files changed, 20 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts index 0d52ff16ff..fca6080329 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts @@ -26,11 +26,9 @@ describe('DatabaseManagerImpl', () => { it('calls the right connector, only once per plugin id', async () => { const connector1 = { getClient: jest.fn(), - dropDatabase: jest.fn(), } satisfies Connector; const connector2 = { getClient: jest.fn(), - dropDatabase: jest.fn(), } satisfies Connector; const impl = new DatabaseManagerImpl( @@ -62,11 +60,9 @@ describe('DatabaseManagerImpl', () => { it('respects per-plugin overridden connectors', async () => { const connector1 = { getClient: jest.fn(), - dropDatabase: jest.fn(), } satisfies Connector; const connector2 = { getClient: jest.fn(), - dropDatabase: jest.fn(), } satisfies Connector; const impl = new DatabaseManagerImpl( @@ -99,7 +95,6 @@ describe('DatabaseManagerImpl', () => { it('migration skip options take precedence over config', async () => { const connector = { getClient: jest.fn(), - dropDatabase: jest.fn(), } satisfies Connector; const impl = new DatabaseManagerImpl( @@ -133,7 +128,6 @@ describe('DatabaseManagerImpl', () => { it('plugin can skip migrations using config', async () => { const connector = { getClient: jest.fn(), - dropDatabase: jest.fn(), } satisfies Connector; const impl = new DatabaseManagerImpl( diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts b/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts index b11b659a6d..f06db5999d 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts @@ -293,10 +293,6 @@ export class MysqlConnector implements Connector { return client; } - async dropDatabase(...databaseNames: string[]): Promise { - return await dropMysqlDatabase(this.config, ...databaseNames); - } - /** * Provides the canonical database name for a given plugin. * diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts index 46b958789d..18418a271a 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts @@ -312,10 +312,6 @@ export class PgConnector implements Connector { return client; } - async dropDatabase(...databaseNames: string[]): Promise { - return await dropPgDatabase(this.config, ...databaseNames); - } - /** * Provides the canonical database name for a given plugin. * diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts index 97ba6d46fd..77e8303390 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts @@ -210,10 +210,6 @@ export class Sqlite3Connector implements Connector { return client; } - async dropDatabase(..._databaseNames: string[]): Promise { - // do nothing - } - /** * Provides the canonical database name for a given plugin. * diff --git a/packages/backend-defaults/src/entrypoints/database/types.ts b/packages/backend-defaults/src/entrypoints/database/types.ts index 74f41581f6..57b85ff3c1 100644 --- a/packages/backend-defaults/src/entrypoints/database/types.ts +++ b/packages/backend-defaults/src/entrypoints/database/types.ts @@ -30,6 +30,4 @@ export interface Connector { pluginMetadata: PluginMetadataService; }, ): Promise; - - dropDatabase(...databaseNames: string[]): Promise; } From ec1b4be3203bf16fdb42eaf9d31f553639cb2bbe Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 30 Aug 2024 13:52:01 +0200 Subject: [PATCH 078/317] NBS: Promote backend packages to 1.0.0 Signed-off-by: Johan Haals --- .changeset/green-pumas-build.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changeset/green-pumas-build.md diff --git a/.changeset/green-pumas-build.md b/.changeset/green-pumas-build.md new file mode 100644 index 0000000000..6f0a4c9773 --- /dev/null +++ b/.changeset/green-pumas-build.md @@ -0,0 +1,11 @@ +--- +'@backstage/backend-plugin-api': major +'@backstage/backend-app-api': major +'@backstage/backend-test-utils': major +--- + +Release 1.0 of the new backend system! :tada: + +The backend system is finally getting promoted to 1.0.0. This means that the API is now stable and breaking changes should not occur until version 2.0.0, see our [package versioning policy](https://backstage.io/docs/overview/versioning-policy/#package-versioning-policy) for more information what this means. + +This release also marks the end of the old backend system based on `createRouter` exports. Going forward backend plugins packages will start to deprecate and later this year remove exports supporting the old backend system. If you would like to help out with this transition, see https://github.com/backstage/backstage/issues/26353 or consult the [migration guide](https://backstage.io/docs/backend-system/building-plugins-and-modules/migrating/#remove-support-for-the-old-backend-system). From 9be84e26c4c9d64c4fb693fc2b1bea096f8eba21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20Daba=C5=A1inskas?= Date: Fri, 30 Aug 2024 15:23:20 +0300 Subject: [PATCH 079/317] Update extending-the-model.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Tomas Dabašinskas --- docs/features/software-catalog/extending-the-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/extending-the-model.md b/docs/features/software-catalog/extending-the-model.md index 51acd44c1d..f9d20d03c2 100644 --- a/docs/features/software-catalog/extending-the-model.md +++ b/docs/features/software-catalog/extending-the-model.md @@ -597,7 +597,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { catalogModelExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; /* highlight-add-next-line */ import { FoobarEntitiesProcessor } from './providers'; From 85f50d43a183a8ef03749908ce3b7c2888e9ec67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 30 Aug 2024 14:29:52 +0200 Subject: [PATCH 080/317] force the passing of deps to the database manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/wise-forks-play.md | 3 +- packages/backend-common/api-report.md | 5 ++- .../backend-common/src/deprecated/index.ts | 29 ++++++++++++-- .../backend-defaults/api-report-database.md | 6 +-- .../database/DatabaseManager.test.ts | 40 +++++++++++-------- .../entrypoints/database/DatabaseManager.ts | 26 +++++++----- .../entrypoints/database/connectors/mysql.ts | 9 ++--- .../database/connectors/postgres.ts | 9 ++--- .../database/connectors/sqlite3.test.ts | 10 ++++- .../database/connectors/sqlite3.ts | 21 +++++----- .../database/databaseServiceFactory.ts | 5 ++- .../src/entrypoints/database/types.ts | 9 ++--- 12 files changed, 102 insertions(+), 70 deletions(-) diff --git a/.changeset/wise-forks-play.md b/.changeset/wise-forks-play.md index 84a6da9003..c5348a8add 100644 --- a/.changeset/wise-forks-play.md +++ b/.changeset/wise-forks-play.md @@ -6,5 +6,6 @@ **BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. - The deprecated `dropDatabase` function has now been removed, without replacement. -- The deprecated `LegacyRootDatabaseService` type has now been removed. The implication is that you now need to pass deps to a `DatabaseManager` when constructing one. +- The deprecated `LegacyRootDatabaseService` type has now been removed. - The return type from `DatabaseManager.forPlugin` is now directly a `DatabaseService`, as arguably expected. +- `DatabaseManager.forPlugin` now requires the `deps` argument, with the logger and lifecycle services. diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 2555ab6f5b..8a895dd037 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -157,7 +157,10 @@ export class DatabaseManager { // (undocumented) static fromConfig( config: Config, - options?: DatabaseManagerOptions, + options?: { + migrations?: DatabaseService['migrations']; + logger?: LoggerService; + }, ): DatabaseManager; } diff --git a/packages/backend-common/src/deprecated/index.ts b/packages/backend-common/src/deprecated/index.ts index d34b81aa92..3b4b518de1 100644 --- a/packages/backend-common/src/deprecated/index.ts +++ b/packages/backend-common/src/deprecated/index.ts @@ -46,6 +46,8 @@ import { isChildPath as _isChildPath, LifecycleService, PluginMetadataService, + DatabaseService, + LoggerService, } from '@backstage/backend-plugin-api'; export * from './hot'; @@ -168,14 +170,20 @@ export type CacheClientOptions = CacheServiceOptions; * @deprecated Use `DatabaseManager` from the `@backstage/backend-defaults` package instead */ export class DatabaseManager { - private constructor(private readonly _databaseManager: _DatabaseManager) {} + private constructor( + private readonly _databaseManager: _DatabaseManager, + private readonly logger?: LoggerService, + ) {} static fromConfig( config: Config, - options?: DatabaseManagerOptions, + options?: { + migrations?: DatabaseService['migrations']; + logger?: LoggerService; + }, ): DatabaseManager { const _databaseManager = _DatabaseManager.fromConfig(config, options); - return new DatabaseManager(_databaseManager); + return new DatabaseManager(_databaseManager, options?.logger); } forPlugin( @@ -184,7 +192,20 @@ export class DatabaseManager { | { lifecycle: LifecycleService; pluginMetadata: PluginMetadataService } | undefined, ): PluginDatabaseManager { - return this._databaseManager.forPlugin(pluginId, deps); + const logger: LoggerService = this.logger ?? { + debug() {}, + info() {}, + warn() {}, + error() {}, + child() { + return this; + }, + }; + const lifecycle: LifecycleService = deps?.lifecycle ?? { + addShutdownHook() {}, + addStartupHook() {}, + }; + return this._databaseManager.forPlugin(pluginId, { logger, lifecycle }); } } diff --git a/packages/backend-defaults/api-report-database.md b/packages/backend-defaults/api-report-database.md index 8f3857fe23..d9e9e9e287 100644 --- a/packages/backend-defaults/api-report-database.md +++ b/packages/backend-defaults/api-report-database.md @@ -6,7 +6,6 @@ import { DatabaseService } from '@backstage/backend-plugin-api'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; -import { PluginMetadataService } from '@backstage/backend-plugin-api'; import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; @@ -14,9 +13,9 @@ import { ServiceFactory } from '@backstage/backend-plugin-api'; export class DatabaseManager { forPlugin( pluginId: string, - deps?: { + deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): DatabaseService; static fromConfig( @@ -28,7 +27,6 @@ export class DatabaseManager { // @public export type DatabaseManagerOptions = { migrations?: DatabaseService['migrations']; - logger?: LoggerService; }; // @public diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts index fca6080329..26c2d54231 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.test.ts @@ -17,12 +17,18 @@ import { ConfigReader } from '@backstage/config'; import { DatabaseManagerImpl } from './DatabaseManager'; import { Connector } from './types'; +import { mockServices } from '@backstage/backend-test-utils'; describe('DatabaseManagerImpl', () => { afterEach(() => { jest.clearAllMocks(); }); + const deps = { + logger: mockServices.logger.mock(), + lifecycle: mockServices.lifecycle.mock(), + }; + it('calls the right connector, only once per plugin id', async () => { const connector1 = { getClient: jest.fn(), @@ -41,19 +47,19 @@ describe('DatabaseManagerImpl', () => { }, ); - await impl.forPlugin('plugin1').getClient(); + await impl.forPlugin('plugin1', deps).getClient(); expect(connector1.getClient).toHaveBeenCalledTimes(1); - expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', deps); expect(connector2.getClient).toHaveBeenCalledTimes(0); - await impl.forPlugin('plugin1').getClient(); + await impl.forPlugin('plugin1', deps).getClient(); expect(connector1.getClient).toHaveBeenCalledTimes(1); - expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', deps); expect(connector2.getClient).toHaveBeenCalledTimes(0); - await impl.forPlugin('plugin2').getClient(); + await impl.forPlugin('plugin2', deps).getClient(); expect(connector1.getClient).toHaveBeenCalledTimes(2); - expect(connector1.getClient).toHaveBeenLastCalledWith('plugin2', undefined); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin2', deps); expect(connector2.getClient).toHaveBeenCalledTimes(0); }); @@ -80,16 +86,16 @@ describe('DatabaseManagerImpl', () => { }, ); - await impl.forPlugin('plugin1').getClient(); + await impl.forPlugin('plugin1', deps).getClient(); expect(connector1.getClient).toHaveBeenCalledTimes(1); - expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', deps); expect(connector2.getClient).toHaveBeenCalledTimes(0); - await impl.forPlugin('plugin2').getClient(); + await impl.forPlugin('plugin2', deps).getClient(); expect(connector1.getClient).toHaveBeenCalledTimes(1); - expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', undefined); + expect(connector1.getClient).toHaveBeenLastCalledWith('plugin1', deps); expect(connector2.getClient).toHaveBeenCalledTimes(1); - expect(connector2.getClient).toHaveBeenLastCalledWith('plugin2', undefined); + expect(connector2.getClient).toHaveBeenLastCalledWith('plugin2', deps); }); it('migration skip options take precedence over config', async () => { @@ -112,7 +118,7 @@ describe('DatabaseManagerImpl', () => { }, { migrations: { skip: false } }, ); - expect((await impl.forPlugin('plugin1')).migrations).toEqual({ + expect((await impl.forPlugin('plugin1', deps)).migrations).toEqual({ skip: false, }); @@ -120,7 +126,7 @@ describe('DatabaseManagerImpl', () => { pg: connector, }); - expect((await impl1.forPlugin('plugin1')).migrations).toEqual({ + expect((await impl1.forPlugin('plugin1', deps)).migrations).toEqual({ skip: false, }); }); @@ -142,10 +148,10 @@ describe('DatabaseManagerImpl', () => { }, ); - expect((await impl.forPlugin('plugin1')).migrations).toEqual({ + expect((await impl.forPlugin('plugin1', deps)).migrations).toEqual({ skip: true, }); - expect((await impl.forPlugin('plugin2')).migrations).toEqual({ + expect((await impl.forPlugin('plugin2', deps)).migrations).toEqual({ skip: false, }); @@ -163,10 +169,10 @@ describe('DatabaseManagerImpl', () => { pg: connector, }, ); - expect((await impl2.forPlugin('plugin1')).migrations).toEqual({ + expect((await impl2.forPlugin('plugin1', deps)).migrations).toEqual({ skip: false, }); - expect((await impl2.forPlugin('plugin2')).migrations).toEqual({ + expect((await impl2.forPlugin('plugin2', deps)).migrations).toEqual({ skip: true, }); }); diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts index 1230280e07..46d4ee8cf7 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts @@ -18,7 +18,6 @@ import { DatabaseService, LifecycleService, LoggerService, - PluginMetadataService, RootConfigService, } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; @@ -43,7 +42,6 @@ function pluginPath(pluginId: string): string { */ export type DatabaseManagerOptions = { migrations?: DatabaseService['migrations']; - logger?: LoggerService; }; /** @@ -66,9 +64,9 @@ export class DatabaseManagerImpl { */ forPlugin( pluginId: string, - deps?: { + deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): PluginDatabaseManager { const client = this.getClientType(pluginId).client; @@ -128,9 +126,9 @@ export class DatabaseManagerImpl { private async getDatabase( pluginId: string, connector: Connector, - deps?: { + deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): Promise { if (this.databaseCache.has(pluginId)) { @@ -141,13 +139,19 @@ export class DatabaseManagerImpl { this.databaseCache.set(pluginId, clientPromise); if (process.env.NODE_ENV !== 'test') { - clientPromise.then(client => this.startKeepaliveLoop(pluginId, client)); + clientPromise.then(client => + this.startKeepaliveLoop(pluginId, client, deps.logger), + ); } return clientPromise; } - private startKeepaliveLoop(pluginId: string, client: Knex): void { + private startKeepaliveLoop( + pluginId: string, + client: Knex, + logger: LoggerService, + ): void { let lastKeepaliveFailed = false; setInterval(() => { @@ -160,7 +164,7 @@ export class DatabaseManagerImpl { (error: unknown) => { if (!lastKeepaliveFailed) { lastKeepaliveFailed = true; - this.options?.logger?.warn( + logger.warn( `Database keepalive failed for plugin ${pluginId}, ${stringifyError( error, )}`, @@ -225,9 +229,9 @@ export class DatabaseManager { */ forPlugin( pluginId: string, - deps?: { + deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): PluginDatabaseManager { return this.impl.forPlugin(pluginId, deps); diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts b/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts index f06db5999d..d5bd88deef 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts @@ -14,10 +14,7 @@ * limitations under the License. */ -import { - LifecycleService, - PluginMetadataService, -} from '@backstage/backend-plugin-api'; +import { LifecycleService, LoggerService } from '@backstage/backend-plugin-api'; import { Config, ConfigReader } from '@backstage/config'; import { InputError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; @@ -253,9 +250,9 @@ export class MysqlConnector implements Connector { async getClient( pluginId: string, - _deps?: { + _deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): Promise { const pluginConfig = new ConfigReader( diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts index 18418a271a..b5cfffd1f4 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts @@ -14,10 +14,7 @@ * limitations under the License. */ -import { - LifecycleService, - PluginMetadataService, -} from '@backstage/backend-plugin-api'; +import { LifecycleService, LoggerService } from '@backstage/backend-plugin-api'; import { Config, ConfigReader } from '@backstage/config'; import { ForwardedError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; @@ -261,9 +258,9 @@ export class PgConnector implements Connector { async getClient( pluginId: string, - _deps?: { + _deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): Promise { const pluginConfig = new ConfigReader( diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.test.ts b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.test.ts index 2bd517d373..769d402eda 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.test.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.test.ts @@ -20,11 +20,17 @@ import { buildSqliteDatabaseConfig, createSqliteDatabaseClient, } from './sqlite3'; +import { mockServices } from '@backstage/backend-test-utils'; describe('better-sqlite3', () => { const createConfig = (connection: any) => new ConfigReader({ client: 'better-sqlite3', connection }); + const deps = { + logger: mockServices.logger.mock(), + lifecycle: mockServices.lifecycle.mock(), + }; + describe('buildSqliteDatabaseConfig', () => { it('builds an in-memory connection', () => { expect(buildSqliteDatabaseConfig(createConfig(':memory:'))).toEqual({ @@ -90,7 +96,9 @@ describe('better-sqlite3', () => { describe('createSqliteDatabaseClient', () => { it('creates an in memory knex instance', () => { - expect(createSqliteDatabaseClient(createConfig(':memory:'))).toBeTruthy(); + expect( + createSqliteDatabaseClient('p', createConfig(':memory:'), deps), + ).toBeTruthy(); }); }); }); diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts index 77e8303390..67dd9d975f 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts @@ -15,10 +15,7 @@ */ import { DevDataStore } from '@backstage/backend-dev-utils'; -import { - LifecycleService, - PluginMetadataService, -} from '@backstage/backend-plugin-api'; +import { LifecycleService, LoggerService } from '@backstage/backend-plugin-api'; import { Config, ConfigReader } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ensureDirSync } from 'fs-extra'; @@ -35,12 +32,13 @@ import { mergeDatabaseConfig } from './mergeDatabaseConfig'; * @param overrides - Additional options to merge with the config */ export function createSqliteDatabaseClient( + pluginId: string, dbConfig: Config, - overrides?: Knex.Config, - deps?: { + deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, + overrides?: Knex.Config, ) { const knexConfig = buildSqliteDatabaseConfig(dbConfig, overrides); const connConfig = knexConfig.connection as Knex.Sqlite3ConnectionConfig; @@ -62,7 +60,7 @@ export function createSqliteDatabaseClient( const devStore = DevDataStore.get(); if (devStore) { - const dataKey = `sqlite3-db-${deps.pluginMetadata.getId()}`; + const dataKey = `sqlite3-db-${pluginId}`; const connectionLoader = async () => { // If seed data is available, use it tconnectionLoader restore the database @@ -180,9 +178,9 @@ export class Sqlite3Connector implements Connector { async getClient( pluginId: string, - deps?: { + deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): Promise { const pluginConfig = new ConfigReader( @@ -202,9 +200,10 @@ export class Sqlite3Connector implements Connector { ); const client = createSqliteDatabaseClient( + pluginId, pluginConfig, - databaseClientOverrides, deps, + databaseClientOverrides, ); return client; diff --git a/packages/backend-defaults/src/entrypoints/database/databaseServiceFactory.ts b/packages/backend-defaults/src/entrypoints/database/databaseServiceFactory.ts index 3bdf1790f3..167779e807 100644 --- a/packages/backend-defaults/src/entrypoints/database/databaseServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/database/databaseServiceFactory.ts @@ -35,6 +35,7 @@ export const databaseServiceFactory = createServiceFactory({ deps: { config: coreServices.rootConfig, lifecycle: coreServices.lifecycle, + logger: coreServices.logger, pluginMetadata: coreServices.pluginMetadata, }, async createRootContext({ config }) { @@ -48,10 +49,10 @@ export const databaseServiceFactory = createServiceFactory({ }), ); }, - async factory({ pluginMetadata, lifecycle }, databaseManager) { + async factory({ pluginMetadata, lifecycle, logger }, databaseManager) { return databaseManager.forPlugin(pluginMetadata.getId(), { - pluginMetadata, lifecycle, + logger, }); }, }); diff --git a/packages/backend-defaults/src/entrypoints/database/types.ts b/packages/backend-defaults/src/entrypoints/database/types.ts index 57b85ff3c1..beded85d65 100644 --- a/packages/backend-defaults/src/entrypoints/database/types.ts +++ b/packages/backend-defaults/src/entrypoints/database/types.ts @@ -14,10 +14,7 @@ * limitations under the License. */ -import { - LifecycleService, - PluginMetadataService, -} from '@backstage/backend-plugin-api'; +import { LifecycleService, LoggerService } from '@backstage/backend-plugin-api'; import { Knex } from 'knex'; export type { DatabaseService as PluginDatabaseManager } from '@backstage/backend-plugin-api'; @@ -25,9 +22,9 @@ export type { DatabaseService as PluginDatabaseManager } from '@backstage/backen export interface Connector { getClient( pluginId: string, - deps?: { + deps: { + logger: LoggerService; lifecycle: LifecycleService; - pluginMetadata: PluginMetadataService; }, ): Promise; } From 8bd210abc1da1cb39806c4f13e26b4979bff5340 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 30 Aug 2024 12:51:55 +0200 Subject: [PATCH 081/317] microsite: patch redirect plugin to ignore override errors Signed-off-by: Patrik Oldsberg --- microsite/docusaurus.config.ts | 186 +++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 81 deletions(-) diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index c6ceb21085..fcac2973dc 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -21,11 +21,35 @@ import { themes } from 'prism-react-renderer'; import type * as Preset from '@docusaurus/preset-classic'; import { Config } from '@docusaurus/types'; +import RedirectPlugin from '@docusaurus/plugin-client-redirects' + const backstageTheme = themes.vsDark; backstageTheme.plain.backgroundColor = '#232323'; const useVersionedDocs = require('fs').existsSync('versions.json'); +// This patches the redirect plugin to ignore the error when it tries to override existing fields. +// This lets us add redirects that only apply to the next docs, while the stable docs still contain the source path. +const PatchedRedirectPlugin: typeof RedirectPlugin = (ctx, opts) => { + const plugin = RedirectPlugin(ctx, opts); + + return { + ...plugin, + async postBuild(...args) { + try { + await plugin.postBuild(...args); + } catch (error) { + if (error.message === 'The redirect plugin is not supposed to override existing files.') { + // Bit of a hack to make sure all remaining redirects are written, since the write uses Promise.all + await new Promise(resolve => setTimeout(resolve, 1000)); + } else { + throw error + } + } + } + } +} + const config: Config = { title: 'Backstage Software Catalog and Developer Platform', tagline: 'An open source framework for building developer portals', @@ -141,89 +165,89 @@ const config: Config = { }; }, }), - [ - '@docusaurus/plugin-client-redirects', + ctx => PatchedRedirectPlugin(ctx, { + id: '@docusaurus/plugin-client-redirects', + toExtensions: [], + fromExtensions: [], + redirects: [ { - redirects: [ - { - from: '/docs', - to: '/docs/overview/what-is-backstage', - }, - { - from: '/docs/features/software-catalog/software-catalog-overview', - to: '/docs/features/software-catalog/', - }, - { - from: '/docs/features/software-templates/software-templates-index', - to: '/docs/features/software-templates/', - }, - { - from: '/docs/features/techdocs/techdocs-overview', - to: '/docs/features/techdocs/', - }, - { - from: '/docs/features/kubernetes/overview', - to: '/docs/features/kubernetes/', - }, - { - from: '/docs/features/search/search-overview', - to: '/docs/features/search/', - }, - { - from: '/docs/getting-started/running-backstage-locally', - to: '/docs/getting-started/', - }, - { - from: '/docs/features/software-templates/testing-scaffolder-alpha', - to: '/docs/features/software-templates/migrating-to-rjsf-v5', - }, - { - from: '/docs/auth/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/overview/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/getting-started/create-an-app', - to: '/docs/getting-started/', - }, - { - from: '/docs/getting-started/configuration', - to: '/docs/getting-started/#next-steps', - }, - { - from: '/docs/features/software-templates/authorizing-parameters-steps-and-actions', - to: '/docs/features/software-templates/authorizing-scaffolder-template-details', - }, - { - from: '/docs/local-dev/cli-commands/', - to: '/docs/tooling/cli/commands/', - }, - { - from: '/docs/local-dev/cli-build-system/', - to: '/docs/tooling/cli/build-system/', - }, - { - from: '/docs/local-dev/cli-overview/', - to: '/docs/tooling/cli/overview/', - }, - { - from: '/docs/local-dev/linking-local-packages/', - to: '/docs/tooling/local-dev/linking-local-packages', - }, - { - from: '/docs/local-dev/debugging/', - to: '/docs/tooling/local-dev/debugging', - }, - { - from: '/docs/plugins/url-reader/', - to: '/docs/backend-system/core-services/url-reader', - }, - ], + from: '/docs', + to: '/docs/overview/what-is-backstage', }, - ], + { + from: '/docs/features/software-catalog/software-catalog-overview', + to: '/docs/features/software-catalog/', + }, + { + from: '/docs/features/software-templates/software-templates-index', + to: '/docs/features/software-templates/', + }, + { + from: '/docs/features/techdocs/techdocs-overview', + to: '/docs/features/techdocs/', + }, + { + from: '/docs/features/kubernetes/overview', + to: '/docs/features/kubernetes/', + }, + { + from: '/docs/features/search/search-overview', + to: '/docs/features/search/', + }, + { + from: '/docs/getting-started/running-backstage-locally', + to: '/docs/getting-started/', + }, + { + from: '/docs/features/software-templates/testing-scaffolder-alpha', + to: '/docs/features/software-templates/migrating-to-rjsf-v5', + }, + { + from: '/docs/auth/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/overview/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/getting-started/create-an-app', + to: '/docs/getting-started/', + }, + { + from: '/docs/getting-started/configuration', + to: '/docs/getting-started/#next-steps', + }, + { + from: '/docs/features/software-templates/authorizing-parameters-steps-and-actions', + to: '/docs/features/software-templates/authorizing-scaffolder-template-details', + }, + { + from: '/docs/local-dev/cli-commands/', + to: '/docs/tooling/cli/commands/', + }, + { + from: '/docs/local-dev/cli-build-system/', + to: '/docs/tooling/cli/build-system/', + }, + { + from: '/docs/local-dev/cli-overview/', + to: '/docs/tooling/cli/overview/', + }, + { + from: '/docs/local-dev/linking-local-packages/', + to: '/docs/tooling/local-dev/linking-local-packages', + }, + { + from: '/docs/local-dev/debugging/', + to: '/docs/tooling/local-dev/debugging', + }, + { + from: '/docs/plugins/url-reader/', + to: '/docs/backend-system/core-services/url-reader', + }, + ] + }), [ 'docusaurus-pushfeedback', { From 8bbe3474b7c05a3d669168ebe98bfdd80a5490b8 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 29 Aug 2024 22:57:12 +0200 Subject: [PATCH 082/317] workflow/deploy_microsite: split build and add stable docs Signed-off-by: Patrik Oldsberg --- .github/workflows/deploy_microsite.yml | 207 +++++++++++++++++++++---- 1 file changed, 181 insertions(+), 26 deletions(-) diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index ac02794268..98a2910d39 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -1,5 +1,6 @@ name: Deploy Microsite on: + pull_request: # DEBUG ONLY push: branches: - master @@ -8,14 +9,148 @@ permissions: contents: read jobs: - deploy-microsite-and-storybook: - permissions: - contents: write # for JamesIves/github-pages-deploy-action to push changes in repo + stable: runs-on: ubuntu-latest + concurrency: + group: stable-reference-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true env: CI: true NODE_OPTIONS: --max-old-space-size=8192 + + outputs: + release: ${{ steps.find-release.outputs.result }} + + steps: + - name: Harden Runner + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + with: + egress-policy: audit + + - name: find latest release + uses: actions/github-script@v7 + id: find-release + with: + script: | + const { data } = await github.rest.repos.listTags({ + owner: context.repo.owner, + repo: context.repo.repo, + per_page: 100, + }) + + const [{tag}] = data + .map(i => i.name) + .filter(tag => tag.match(/^v\d+\.\d+\.\d+$/)) + .map(tag => ({ + tag, + val: tag + .slice(1) + .split('.') + .reduce((val, part) => Number(val) * 1000 + Number(part)) + })) + .sort((a, b) => b.val - a.val) + + return tag + result-encoding: string + + - name: checkout latest release + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + ref: refs/tags/${{ steps.find-release.outputs.result }} + + - name: use node.js 18.x + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version: 18.x + registry-url: https://registry.npmjs.org/ # Needed for auth + + - name: yarn install + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 + with: + cache-prefix: ${{ runner.os }}-v18.x + + - name: build API reference + run: yarn build:api-docs + + - name: upload API reference + uses: actions/upload-artifact@v4 + with: + name: stable-reference + path: docs/reference/ + if-no-files-found: error + retention-days: 1 + + next: + runs-on: ubuntu-latest + concurrency: + group: next-reference-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: + CI: true + NODE_OPTIONS: --max-old-space-size=8192 + + steps: + - name: Harden Runner + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + with: + egress-policy: audit + + - name: checkout master + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: use node.js 18.x + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version: 18.x + registry-url: https://registry.npmjs.org/ # Needed for auth + + - name: yarn install + uses: backstage/actions/yarn-install@3c138326f7fcbf253b88170c1f29bae8e975d47c # v0.6.14 + with: + cache-prefix: ${{ runner.os }}-v18.x + + - name: build API reference + run: yarn build:api-docs + + - name: upload API reference + uses: actions/upload-artifact@v4 + with: + name: next-reference + path: docs/reference/ + if-no-files-found: error + retention-days: 1 + + # Also build and upload storybook + - name: storybook yarn install + run: yarn install --immutable + working-directory: storybook + + - name: storybook build + run: yarn build-storybook + working-directory: storybook + + - name: storybook upload + uses: actions/upload-artifact@v4 + with: + name: storybook + path: storybook/dist/ + if-no-files-found: error + retention-days: 1 + + deploy-microsite-and-storybook: + permissions: + contents: write # for JamesIves/github-pages-deploy-action to push changes in repo + + runs-on: ubuntu-latest + + needs: + - stable + - next + + env: + CI: true + NODE_OPTIONS: --max-old-space-size=16384 DOCUSAURUS_SSR_CONCURRENCY: 5 concurrency: @@ -28,45 +163,65 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - name: use node.js 18.x uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth - # We avoid caching in this workflow, as we're running an install of both the top-level - # dependencies and the microsite. We leave it to the main master workflow to produce the - # cache, as that results in a smaller bundle. - - name: top-level yarn install - run: yarn install --immutable + # Stable docs + - name: checkout latest release + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + ref: refs/tags/${{ needs.stable.outputs.release }} + - name: microsite yarn install run: yarn install --immutable working-directory: microsite - - name: storybook yarn install - run: yarn install --immutable - working-directory: storybook - - name: build API reference - run: yarn build:api-docs + - name: download stable reference + uses: actions/download-artifact@v4 + with: + name: stable-reference + path: docs/reference + + - name: generate stable docs + run: yarn docusaurus docs:version stable + working-directory: microsite + + - name: clear API reference + run: rm -r docs/reference + + # Next docs + - name: checkout master + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + clean: false + + - name: microsite yarn install + run: yarn install --immutable + working-directory: microsite + + - name: download next reference + uses: actions/download-artifact@v4 + with: + name: next-reference + path: docs/reference - name: build microsite run: yarn build working-directory: microsite - - name: build storybook - run: yarn build-storybook - working-directory: storybook - - - name: move storybook dist into microsite - run: mv storybook/dist/ microsite/build/storybook + - uses: actions/download-artifact@v4 + with: + name: storybook + path: microsite/build/storybook - name: Check the build output run: ls microsite/build && ls microsite/build/storybook - - name: Deploy both microsite and storybook to gh-pages - uses: JamesIves/github-pages-deploy-action@94f3c658273cf92fb48ef99e5fbc02bd2dc642b2 # v4.6.3 - with: - branch: gh-pages - folder: microsite/build + # - name: Deploy both microsite and storybook to gh-pages + # uses: JamesIves/github-pages-deploy-action@94f3c658273cf92fb48ef99e5fbc02bd2dc642b2 # v4.6.3 + # with: + # branch: gh-pages + # folder: microsite/build From 0e1a817a26dba2cc7c0ba19bd64f433554c73edf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 30 Aug 2024 13:42:19 +0200 Subject: [PATCH 083/317] packages/cli: output index.html.tmpl Signed-off-by: Patrik Oldsberg --- .changeset/great-zebras-hunt.md | 5 +++++ packages/cli/package.json | 1 + packages/cli/src/lib/bundler/config.ts | 11 +++++++++++ packages/cli/src/lib/bundler/transforms.ts | 4 ++-- yarn.lock | 17 +++++++++++++++-- 5 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 .changeset/great-zebras-hunt.md diff --git a/.changeset/great-zebras-hunt.md b/.changeset/great-zebras-hunt.md new file mode 100644 index 0000000000..cbb0beb1b6 --- /dev/null +++ b/.changeset/great-zebras-hunt.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +The app build process now outputs an additional `index.html.tmpl` file. This is an non-templated version of the `index.html` file, which can be used to delay templating until runtime. diff --git a/packages/cli/package.json b/packages/cli/package.json index 89230c13b2..947e79c87f 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -130,6 +130,7 @@ "pirates": "^4.0.6", "postcss": "^8.1.0", "process": "^0.11.10", + "raw-loader": "^4.0.2", "react-dev-utils": "^12.0.0-next.60", "react-refresh": "^0.14.0", "recursive-readdir": "^2.2.2", diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index da7b378fbd..40ed82ef54 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -198,6 +198,17 @@ export async function createConfig( }, }), ); + plugins.push( + new HtmlWebpackPlugin({ + meta: { + 'backstage-app-mode': options?.appMode ?? 'public', + }, + minify: false, + publicPath: '<%= publicPath %>', + filename: 'index.html.tmpl', + template: `raw-loader!${paths.targetHtml}`, + }), + ); } if (options.moduleFederation) { diff --git a/packages/cli/src/lib/bundler/transforms.ts b/packages/cli/src/lib/bundler/transforms.ts index 302b2fbe68..17a6a0c191 100644 --- a/packages/cli/src/lib/bundler/transforms.ts +++ b/packages/cli/src/lib/bundler/transforms.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { ModuleOptions, WebpackPluginInstance } from 'webpack'; +import { RuleSetRule, WebpackPluginInstance } from 'webpack'; import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import { svgrTemplate } from '../svgrTemplate'; type Transforms = { - loaders: ModuleOptions['rules']; + loaders: RuleSetRule[]; plugins: WebpackPluginInstance[]; }; diff --git a/yarn.lock b/yarn.lock index c36b6da828..04ebf90c5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3996,6 +3996,7 @@ __metadata: pirates: ^4.0.6 postcss: ^8.1.0 process: ^0.11.10 + raw-loader: ^4.0.2 react-dev-utils: ^12.0.0-next.60 react-refresh: ^0.14.0 recursive-readdir: ^2.2.2 @@ -32573,7 +32574,7 @@ __metadata: languageName: node linkType: hard -"loader-utils@npm:^2.0.4": +"loader-utils@npm:^2.0.0, loader-utils@npm:^2.0.4": version: 2.0.4 resolution: "loader-utils@npm:2.0.4" dependencies: @@ -38143,6 +38144,18 @@ __metadata: languageName: node linkType: hard +"raw-loader@npm:^4.0.2": + version: 4.0.2 + resolution: "raw-loader@npm:4.0.2" + dependencies: + loader-utils: ^2.0.0 + schema-utils: ^3.0.0 + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + checksum: 51cc1b0d0e8c37c4336b5318f3b2c9c51d6998ad6f56ea09612afcfefc9c1f596341309e934a744ae907177f28efc9f1654eacd62151e82853fcc6d37450e795 + languageName: node + linkType: hard + "rc-progress@npm:3.5.1": version: 3.5.1 resolution: "rc-progress@npm:3.5.1" @@ -40166,7 +40179,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1, schema-utils@npm:^3.2.0": version: 3.3.0 resolution: "schema-utils@npm:3.3.0" dependencies: From 06b8206edd1ac07a4256da47ab53bb828a565c45 Mon Sep 17 00:00:00 2001 From: Diana Farkas-Deregi Date: Fri, 30 Aug 2024 15:10:17 +0200 Subject: [PATCH 084/317] core-components: Add titleComponent prop to SignInPage to allow title customization with ReactNode Signed-off-by: Diana Farkas-Deregi --- .changeset/odd-goats-kiss.md | 5 +++++ .../src/layout/SignInPage/SignInPage.tsx | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .changeset/odd-goats-kiss.md diff --git a/.changeset/odd-goats-kiss.md b/.changeset/odd-goats-kiss.md new file mode 100644 index 0000000000..0f199c1a1e --- /dev/null +++ b/.changeset/odd-goats-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Added `titleComponent` prop to `SignInPage` component to allow further customization of the title using `ReactNode` diff --git a/packages/core-components/src/layout/SignInPage/SignInPage.tsx b/packages/core-components/src/layout/SignInPage/SignInPage.tsx index 95d96b701a..fb8f804d5e 100644 --- a/packages/core-components/src/layout/SignInPage/SignInPage.tsx +++ b/packages/core-components/src/layout/SignInPage/SignInPage.tsx @@ -24,7 +24,7 @@ import { UserIdentity } from './UserIdentity'; import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; import Typography from '@material-ui/core/Typography'; -import React, { useState } from 'react'; +import React, { ReactNode, useState } from 'react'; import { useMountEffect } from '@react-hookz/web'; import { Progress } from '../../components/Progress'; import { Content } from '../Content/Content'; @@ -41,6 +41,7 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; type MultiSignInPageProps = SignInPageProps & { providers: IdentityProviders; title?: string; + titleComponent?: ReactNode; align?: 'center' | 'left'; }; @@ -55,6 +56,7 @@ export const MultiSignInPage = ({ onSignInSuccess, providers = [], title, + titleComponent, align = 'left', }: MultiSignInPageProps) => { const configApi = useApi(configApiRef); @@ -74,7 +76,13 @@ export const MultiSignInPage = ({

      - {title && } + {(title || titleComponent) && ( + + )} Date: Fri, 30 Aug 2024 15:16:12 +0200 Subject: [PATCH 085/317] put back the types in common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/chilly-birds-shout.md | 7 +++++++ .changeset/wise-forks-play.md | 1 - packages/backend-common/api-report.md | 7 ++++++- packages/backend-common/src/deprecated/index.ts | 10 +++++++++- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 .changeset/chilly-birds-shout.md diff --git a/.changeset/chilly-birds-shout.md b/.changeset/chilly-birds-shout.md new file mode 100644 index 0000000000..dfa99b6f5d --- /dev/null +++ b/.changeset/chilly-birds-shout.md @@ -0,0 +1,7 @@ +--- +'@backstage/backend-common': minor +--- + +**BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. + +- The deprecated `dropDatabase` function has now been removed, without replacement. diff --git a/.changeset/wise-forks-play.md b/.changeset/wise-forks-play.md index c5348a8add..3846b3a6f0 100644 --- a/.changeset/wise-forks-play.md +++ b/.changeset/wise-forks-play.md @@ -1,5 +1,4 @@ --- -'@backstage/backend-common': minor '@backstage/backend-defaults': minor --- diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 8a895dd037..450bee9063 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -143,7 +143,7 @@ export function createStatusCheckRouter(options: { }): Promise; // @public @deprecated (undocumented) -export class DatabaseManager { +export class DatabaseManager implements LegacyRootDatabaseService { // (undocumented) forPlugin( pluginId: string, @@ -365,6 +365,11 @@ export const legacyPlugin: ( }>, ) => BackendFeature; +// @public @deprecated (undocumented) +export type LegacyRootDatabaseService = { + forPlugin(pluginId: string): DatabaseService; +}; + // @public @deprecated export function loadBackendConfig(options: { logger: LoggerService; diff --git a/packages/backend-common/src/deprecated/index.ts b/packages/backend-common/src/deprecated/index.ts index 3b4b518de1..7f18bd188e 100644 --- a/packages/backend-common/src/deprecated/index.ts +++ b/packages/backend-common/src/deprecated/index.ts @@ -169,7 +169,7 @@ export type CacheClientOptions = CacheServiceOptions; * @public * @deprecated Use `DatabaseManager` from the `@backstage/backend-defaults` package instead */ -export class DatabaseManager { +export class DatabaseManager implements LegacyRootDatabaseService { private constructor( private readonly _databaseManager: _DatabaseManager, private readonly logger?: LoggerService, @@ -221,6 +221,14 @@ export type DatabaseManagerOptions = _DatabaseManagerOptions; */ export type PluginDatabaseManager = _PluginDatabaseManager; +/** + * @public + * @deprecated Use `DatabaseManager` from `@backstage/backend-defaults/database` instead, or migrate to the new backend system and use `coreServices.database` + */ +export type LegacyRootDatabaseService = { + forPlugin(pluginId: string): DatabaseService; +}; + /** * @public * @deprecated This function is deprecated and will be removed in a future release, see https://github.com/backstage/backstage/issues/24493. From 1c8d7571c5cfc830d0dc1f31b91300263b45f8ee Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 30 Aug 2024 15:31:29 +0200 Subject: [PATCH 086/317] packages/cli: add support for runtime configuration of the webpack public path Signed-off-by: Patrik Oldsberg --- .changeset/great-zebras-hunt.md | 2 ++ packages/cli/config/webpack-public-path.js | 31 ++++++++++++++++++++++ packages/cli/src/lib/bundler/config.ts | 8 +++++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 packages/cli/config/webpack-public-path.js diff --git a/.changeset/great-zebras-hunt.md b/.changeset/great-zebras-hunt.md index cbb0beb1b6..252f270092 100644 --- a/.changeset/great-zebras-hunt.md +++ b/.changeset/great-zebras-hunt.md @@ -3,3 +3,5 @@ --- The app build process now outputs an additional `index.html.tmpl` file. This is an non-templated version of the `index.html` file, which can be used to delay templating until runtime. + +The new `index.html.tmpl` file also sets a `backstage-public-path` meta tag to be templated at runtime. The meta tag is in turn picked up by the new `@backstage/cli/config/webpack-public-path.js` entry point script, which uses it to set the runtime public path of the Webpack bundle. diff --git a/packages/cli/config/webpack-public-path.js b/packages/cli/config/webpack-public-path.js new file mode 100644 index 0000000000..3e14e96eb9 --- /dev/null +++ b/packages/cli/config/webpack-public-path.js @@ -0,0 +1,31 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This script is used to pick up and set the public path of the Webpack bundle +// at runtime. The meta tag is injected by the app build, but only present in +// the `index.html.tmpl` file. The runtime value of the meta tag is populated by +// the app backend, when it templates the final `index.html` file. +// +// This is needed for additional chunks to use the correct public path, and it +// is not possible to set the `__webpack_public_path__` variable outside of the +// build itself. The Webpack output also does not read any tags or +// similar, this seems to be the only way to dynamically configure the public +// path at runtime. +const el = document.querySelector('meta[name="backstage-public-path"]'); +const path = el?.getAttribute('content'); +if (path) { + __webpack_public_path__ = path; +} diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 40ed82ef54..a64bc0fcd8 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -202,6 +202,8 @@ export async function createConfig( new HtmlWebpackPlugin({ meta: { 'backstage-app-mode': options?.appMode ?? 'public', + // This is added to be written in the later step, and finally read by the extra entry point + 'backstage-public-path': '<%= publicPath %>/', }, minify: false, publicPath: '<%= publicPath %>', @@ -352,7 +354,11 @@ export async function createConfig( }, devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map', context: paths.targetPath, - entry: [...(options.additionalEntryPoints ?? []), paths.targetEntry], + entry: [ + require.resolve('@backstage/cli/config/webpack-public-path'), + ...(options.additionalEntryPoints ?? []), + paths.targetEntry, + ], resolve: { extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx', '.json', '.wasm'], mainFields: ['browser', 'module', 'main'], From aa1c607a9a3955a11587c85159525c0a731b0681 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:47:27 +0000 Subject: [PATCH 087/317] chore(deps): update actions/setup-python digest to f677139 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_microsite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index a695eb1d51..a290b221b2 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -34,7 +34,7 @@ jobs: uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version: 18.x - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5 + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5 with: python-version: '3.9' From 15e3e073d6b97ebec6047f6ff4ac43ca43d34fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 30 Aug 2024 15:47:11 +0200 Subject: [PATCH 088/317] make CacheManager.forPlugin return a CacheService directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/wise-forks-play.md | 7 ++++ .../backend-common/src/deprecated/index.ts | 36 +++++++++++++++---- packages/backend-defaults/api-report-cache.md | 9 +---- .../entrypoints/cache/CacheManager.test.ts | 22 ++++++------ .../src/entrypoints/cache/CacheManager.ts | 33 ++++++----------- .../entrypoints/cache/cacheServiceFactory.ts | 2 +- .../src/entrypoints/cache/index.ts | 2 +- .../src/entrypoints/cache/types.ts | 11 ------ 8 files changed, 63 insertions(+), 59 deletions(-) diff --git a/.changeset/wise-forks-play.md b/.changeset/wise-forks-play.md index 3846b3a6f0..1c8be76d19 100644 --- a/.changeset/wise-forks-play.md +++ b/.changeset/wise-forks-play.md @@ -4,7 +4,14 @@ **BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. +For the `/database` subpath exports: + - The deprecated `dropDatabase` function has now been removed, without replacement. - The deprecated `LegacyRootDatabaseService` type has now been removed. - The return type from `DatabaseManager.forPlugin` is now directly a `DatabaseService`, as arguably expected. - `DatabaseManager.forPlugin` now requires the `deps` argument, with the logger and lifecycle services. + +For the `/cache` subpath exports: + +- The `PluginCacheManager` type has been removed. You can still import it from `@backstage/backend-common`, but it's deprecated there, and you should move off of that package by migrating fully to the new backend system. +- Accordingly, `CacheManager.forPlugin` immediately returns a `CacheService` instead of a `PluginCacheManager`. The outcome of this is that you no longer need to make the extra `.getClient()` call. The old `CacheManager` with the old behavior still exists on `@backstage/backend-common`, but the above recommendations apply. diff --git a/packages/backend-common/src/deprecated/index.ts b/packages/backend-common/src/deprecated/index.ts index 7f18bd188e..c7ba186df4 100644 --- a/packages/backend-common/src/deprecated/index.ts +++ b/packages/backend-common/src/deprecated/index.ts @@ -23,10 +23,7 @@ import { HostDiscovery as _HostDiscovery } from '../../../backend-defaults/src/e import { CacheManager as _CacheManager } from '../../../backend-defaults/src/entrypoints/cache/CacheManager'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { - type PluginCacheManager as _PluginCacheManager, - type CacheManagerOptions as _CacheManagerOptions, -} from '../../../backend-defaults/src/entrypoints/cache/types'; +import { type CacheManagerOptions as _CacheManagerOptions } from '../../../backend-defaults/src/entrypoints/cache/types'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { @@ -48,6 +45,7 @@ import { PluginMetadataService, DatabaseService, LoggerService, + RootConfigService, } from '@backstage/backend-plugin-api'; export * from './hot'; @@ -133,7 +131,31 @@ export { HostDiscovery as SingleHostDiscovery }; * @public * @deprecated Use `CacheManager` from the `@backstage/backend-defaults` package instead */ -export class CacheManager extends _CacheManager {} +export class CacheManager { + /** + * Creates a new {@link CacheManager} instance by reading from the `backend` + * config section, specifically the `.cache` key. + * + * @param config - The loaded application configuration. + */ + static fromConfig( + config: RootConfigService, + options: CacheManagerOptions = {}, + ): CacheManager { + return new CacheManager(_CacheManager.fromConfig(config, options)); + } + + private constructor(private readonly _impl: _CacheManager) {} + + forPlugin(pluginId: string): PluginCacheManager { + return { + getClient: options => { + const result = this._impl.forPlugin(pluginId); + return options ? result.withOptions(options) : result; + }, + }; + } +} /** * @public @@ -145,7 +167,9 @@ export type CacheManagerOptions = _CacheManagerOptions; * @public * @deprecated Use `PluginCacheManager` from the `@backstage/backend-defaults` package instead */ -export type PluginCacheManager = _PluginCacheManager; +export type PluginCacheManager = { + getClient(options?: CacheServiceOptions): CacheService; +}; /** * @public diff --git a/packages/backend-defaults/api-report-cache.md b/packages/backend-defaults/api-report-cache.md index f5ea85ad24..ecda42f983 100644 --- a/packages/backend-defaults/api-report-cache.md +++ b/packages/backend-defaults/api-report-cache.md @@ -4,14 +4,13 @@ ```ts import { CacheService } from '@backstage/backend-plugin-api'; -import { CacheServiceOptions } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public export class CacheManager { - forPlugin(pluginId: string): PluginCacheManager; + forPlugin(pluginId: string): CacheService; static fromConfig( config: RootConfigService, options?: CacheManagerOptions, @@ -31,11 +30,5 @@ export const cacheServiceFactory: ServiceFactory< 'singleton' >; -// @public (undocumented) -export interface PluginCacheManager { - // (undocumented) - getClient(options?: CacheServiceOptions): CacheService; -} - // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts index 6a1dad2c91..997739faa2 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts @@ -52,10 +52,10 @@ describe('CacheManager integration', () => { }), ); - manager.forPlugin('p1').getClient(); - manager.forPlugin('p1').getClient({ defaultTtl: 200 }); - manager.forPlugin('p2').getClient(); - manager.forPlugin('p3').getClient({}); + manager.forPlugin('p1'); + manager.forPlugin('p1').withOptions({ defaultTtl: 200 }); + manager.forPlugin('p2'); + manager.forPlugin('p3').withOptions({}); if (store === 'redis') { // eslint-disable-next-line jest/no-conditional-expect @@ -80,9 +80,11 @@ describe('CacheManager integration', () => { }), ); - const plugin1 = manager.forPlugin('p1').getClient(); - const plugin2a = manager.forPlugin('p2').getClient(); - const plugin2b = manager.forPlugin('p2').getClient({ defaultTtl: 2000 }); + const plugin1 = manager.forPlugin('p1'); + const plugin2a = manager.forPlugin('p2'); + const plugin2b = manager + .forPlugin('p2') + .withOptions({ defaultTtl: 2000 }); await plugin1.set('a', 'plugin1'); await plugin2a.set('a', 'plugin2a'); @@ -114,9 +116,9 @@ describe('CacheManager integration', () => { }), ).forPlugin('p'); - const defaultClient = manager.getClient(); - const numberOverrideClient = manager.getClient({ defaultTtl: 400 }); - const durationOverrideClient = manager.getClient({ + const defaultClient = manager; + const numberOverrideClient = manager.withOptions({ defaultTtl: 400 }); + const durationOverrideClient = manager.withOptions({ defaultTtl: { milliseconds: 400 }, }); diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts index fa4487a50f..41fe595123 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts @@ -15,17 +15,14 @@ */ import { + CacheService, CacheServiceOptions, LoggerService, RootConfigService, } from '@backstage/backend-plugin-api'; import Keyv from 'keyv'; import { DefaultCacheClient } from './CacheClient'; -import { - CacheManagerOptions, - PluginCacheManager, - ttlToMilliseconds, -} from './types'; +import { CacheManagerOptions, ttlToMilliseconds } from './types'; import { durationToMilliseconds } from '@backstage/types'; type StoreFactory = (pluginId: string, defaultTtl: number | undefined) => Keyv; @@ -129,24 +126,16 @@ export class CacheManager { * @param pluginId - The plugin that the cache manager should be created for. * Plugin names should be unique. */ - forPlugin(pluginId: string): PluginCacheManager { - return { - getClient: (defaultOptions = {}) => { - const clientFactory = (options: CacheServiceOptions) => { - const ttl = options.defaultTtl ?? this.defaultTtl; - return this.getClientWithTtl( - pluginId, - ttl !== undefined ? ttlToMilliseconds(ttl) : undefined, - ); - }; - - return new DefaultCacheClient( - clientFactory(defaultOptions), - clientFactory, - defaultOptions, - ); - }, + forPlugin(pluginId: string): CacheService { + const clientFactory = (options: CacheServiceOptions) => { + const ttl = options.defaultTtl ?? this.defaultTtl; + return this.getClientWithTtl( + pluginId, + ttl !== undefined ? ttlToMilliseconds(ttl) : undefined, + ); }; + + return new DefaultCacheClient(clientFactory({}), clientFactory, {}); } private getClientWithTtl(pluginId: string, ttl: number | undefined): Keyv { diff --git a/packages/backend-defaults/src/entrypoints/cache/cacheServiceFactory.ts b/packages/backend-defaults/src/entrypoints/cache/cacheServiceFactory.ts index f16b306d81..352cde44d7 100644 --- a/packages/backend-defaults/src/entrypoints/cache/cacheServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/cache/cacheServiceFactory.ts @@ -40,6 +40,6 @@ export const cacheServiceFactory = createServiceFactory({ return CacheManager.fromConfig(config, { logger }); }, async factory({ plugin }, manager) { - return manager.forPlugin(plugin.getId()).getClient(); + return manager.forPlugin(plugin.getId()); }, }); diff --git a/packages/backend-defaults/src/entrypoints/cache/index.ts b/packages/backend-defaults/src/entrypoints/cache/index.ts index b16fa56bd2..958fa36091 100644 --- a/packages/backend-defaults/src/entrypoints/cache/index.ts +++ b/packages/backend-defaults/src/entrypoints/cache/index.ts @@ -16,4 +16,4 @@ export { cacheServiceFactory } from './cacheServiceFactory'; export { CacheManager } from './CacheManager'; -export type { CacheManagerOptions, PluginCacheManager } from './types'; +export type { CacheManagerOptions } from './types'; diff --git a/packages/backend-defaults/src/entrypoints/cache/types.ts b/packages/backend-defaults/src/entrypoints/cache/types.ts index 23c8bcaf3d..7260099a0a 100644 --- a/packages/backend-defaults/src/entrypoints/cache/types.ts +++ b/packages/backend-defaults/src/entrypoints/cache/types.ts @@ -15,10 +15,6 @@ */ import { LoggerService } from '@backstage/backend-plugin-api'; -import { - CacheService, - CacheServiceOptions, -} from '@backstage/backend-plugin-api'; import { HumanDuration, durationToMilliseconds } from '@backstage/types'; /** @@ -39,13 +35,6 @@ export type CacheManagerOptions = { onError?: (err: Error) => void; }; -/** - * @public - */ -export interface PluginCacheManager { - getClient(options?: CacheServiceOptions): CacheService; -} - export function ttlToMilliseconds(ttl: number | HumanDuration): number { return typeof ttl === 'number' ? ttl : durationToMilliseconds(ttl); } From 53b24d9a5926859e89c799ca812227cfffae63a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 30 Aug 2024 16:02:40 +0200 Subject: [PATCH 089/317] update usage elsewhere MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/chilled-cameras-change.md | 5 +++++ packages/backend-common/api-report.md | 17 +++++++++++------ .../src/GitLabDiscoveryProcessor.ts | 9 +++------ .../techdocs-backend/src/service/router.test.ts | 6 ++++-- 4 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 .changeset/chilled-cameras-change.md diff --git a/.changeset/chilled-cameras-change.md b/.changeset/chilled-cameras-change.md new file mode 100644 index 0000000000..8491761594 --- /dev/null +++ b/.changeset/chilled-cameras-change.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +Internal update to use the new cache manager diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 450bee9063..4a59277e37 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -65,10 +65,15 @@ export type CacheClientOptions = CacheServiceOptions; // @public @deprecated (undocumented) export type CacheClientSetOptions = CacheServiceSetOptions; -// Warning: (ae-forgotten-export) The symbol "CacheManager_2" needs to be exported by the entry point index.d.ts -// // @public @deprecated (undocumented) -export class CacheManager extends CacheManager_2 {} +export class CacheManager { + // (undocumented) + forPlugin(pluginId: string): PluginCacheManager; + static fromConfig( + config: RootConfigService, + options?: CacheManagerOptions, + ): CacheManager; +} // Warning: (ae-forgotten-export) The symbol "CacheManagerOptions_2" needs to be exported by the entry point index.d.ts // @@ -411,10 +416,10 @@ export function makeLegacyPlugin< // @public @deprecated export function notFoundHandler(): RequestHandler; -// Warning: (ae-forgotten-export) The symbol "PluginCacheManager_2" needs to be exported by the entry point index.d.ts -// // @public @deprecated (undocumented) -export type PluginCacheManager = PluginCacheManager_2; +export type PluginCacheManager = { + getClient(options?: CacheServiceOptions): CacheService; +}; // @public @deprecated (undocumented) export type PluginDatabaseManager = DatabaseService; diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts index 29914db705..01e8018845 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts @@ -27,10 +27,7 @@ import { } from '@backstage/plugin-catalog-node'; import { GitLabClient, GitLabProject, paginated } from './lib'; import { CacheService, LoggerService } from '@backstage/backend-plugin-api'; -import { - CacheManager, - PluginCacheManager, -} from '@backstage/backend-defaults/cache'; +import { CacheManager } from '@backstage/backend-defaults/cache'; /** * Extracts repositories out of an GitLab instance. @@ -64,13 +61,13 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { private constructor(options: { integrations: ScmIntegrationRegistry; - pluginCache: PluginCacheManager; + pluginCache: CacheService; logger: LoggerService; skipReposWithoutExactFileMatch?: boolean; skipForkedRepos?: boolean; }) { this.integrations = options.integrations; - this.cache = options.pluginCache.getClient(); + this.cache = options.pluginCache; this.logger = options.logger; this.skipReposWithoutExactFileMatch = options.skipReposWithoutExactFileMatch || false; diff --git a/plugins/techdocs-backend/src/service/router.test.ts b/plugins/techdocs-backend/src/service/router.test.ts index 5b5fc52741..631efe06ea 100644 --- a/plugins/techdocs-backend/src/service/router.test.ts +++ b/plugins/techdocs-backend/src/service/router.test.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + PluginCacheManager, + loggerToWinstonLogger, +} from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { DocsBuildStrategy, @@ -30,7 +33,6 @@ import { createEventStream, createRouter, RouterOptions } from './router'; import { TechDocsCache } from '../cache'; import { mockErrorHandler, mockServices } from '@backstage/backend-test-utils'; import { DiscoveryService } from '@backstage/backend-plugin-api'; -import { PluginCacheManager } from '@backstage/backend-defaults/cache'; jest.mock('@backstage/catalog-client'); jest.mock('@backstage/config'); From 2693b3113c4dc357f42dce0d5e9e41595ac8cffd Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 30 Aug 2024 16:25:36 +0200 Subject: [PATCH 090/317] workflow/deploy_microsite: re-enable publish Signed-off-by: Patrik Oldsberg --- .github/workflows/deploy_microsite.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 98a2910d39..c5d8cc26c2 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -1,6 +1,5 @@ name: Deploy Microsite on: - pull_request: # DEBUG ONLY push: branches: - master @@ -220,8 +219,8 @@ jobs: - name: Check the build output run: ls microsite/build && ls microsite/build/storybook - # - name: Deploy both microsite and storybook to gh-pages - # uses: JamesIves/github-pages-deploy-action@94f3c658273cf92fb48ef99e5fbc02bd2dc642b2 # v4.6.3 - # with: - # branch: gh-pages - # folder: microsite/build + - name: Deploy both microsite and storybook to gh-pages + uses: JamesIves/github-pages-deploy-action@94f3c658273cf92fb48ef99e5fbc02bd2dc642b2 # v4.6.3 + with: + branch: gh-pages + folder: microsite/build From 590fb2dfd7c2506a9b15d77a538a7f169016301b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 30 Aug 2024 15:34:22 +0200 Subject: [PATCH 091/317] app-backend: add support for templating the new index.html.tmpl Signed-off-by: Patrik Oldsberg --- .changeset/khaki-crews-rule.md | 11 +++ plugins/app-backend/src/lib/config/index.ts | 18 +++++ .../src/lib/config/injectConfig.ts | 36 +++++++++ .../lib/config/injectConfigIntoHtml.test.ts | 73 +++++++++++++++++ .../src/lib/config/injectConfigIntoHtml.ts | 78 +++++++++++++++++++ .../injectConfigIntoStatic.test.ts} | 19 ++--- .../src/lib/config/injectConfigIntoStatic.ts | 61 +++++++++++++++ .../readFrontendConfig.ts} | 64 ++------------- plugins/app-backend/src/lib/config/types.ts | 27 +++++++ .../app-backend/src/service/router.test.ts | 12 +-- plugins/app-backend/src/service/router.ts | 7 +- 11 files changed, 331 insertions(+), 75 deletions(-) create mode 100644 .changeset/khaki-crews-rule.md create mode 100644 plugins/app-backend/src/lib/config/index.ts create mode 100644 plugins/app-backend/src/lib/config/injectConfig.ts create mode 100644 plugins/app-backend/src/lib/config/injectConfigIntoHtml.test.ts create mode 100644 plugins/app-backend/src/lib/config/injectConfigIntoHtml.ts rename plugins/app-backend/src/lib/{config.test.ts => config/injectConfigIntoStatic.test.ts} (91%) create mode 100644 plugins/app-backend/src/lib/config/injectConfigIntoStatic.ts rename plugins/app-backend/src/lib/{config.ts => config/readFrontendConfig.ts} (55%) create mode 100644 plugins/app-backend/src/lib/config/types.ts diff --git a/.changeset/khaki-crews-rule.md b/.changeset/khaki-crews-rule.md new file mode 100644 index 0000000000..4d8eb32083 --- /dev/null +++ b/.changeset/khaki-crews-rule.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-app-backend': patch +--- + +**BREAKING**: The app backend now supports the new `index.html.tmpl` output from `@backstage/cli`. If available, the `index.html` will be templated at runtime with the current configuration of the app backend. + +This is marked as a breaking change because you must now supply the app build-time configuration to the backend. This change also affects the public path behavior, where it is no longer necessary to build the app with the correct public path upfront. You now only need to supply a correct `app.baseUrl` to the app backend plugin at runtime. + +An effect that this change has is that the `index.html` will now contain and present the frontend configuration in an easily readable way, which can aid in debugging. This data was always available in the frontend, but it was injected and hidden in the static bundle. + +This templating behavior is enabled by default, but it can be disabled by setting the `app.disableConfigInjection` configuration option to `true`. diff --git a/plugins/app-backend/src/lib/config/index.ts b/plugins/app-backend/src/lib/config/index.ts new file mode 100644 index 0000000000..2e685578d0 --- /dev/null +++ b/plugins/app-backend/src/lib/config/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { injectConfig } from './injectConfig'; +export { readFrontendConfig } from './readFrontendConfig'; diff --git a/plugins/app-backend/src/lib/config/injectConfig.ts b/plugins/app-backend/src/lib/config/injectConfig.ts new file mode 100644 index 0000000000..9d63d219f4 --- /dev/null +++ b/plugins/app-backend/src/lib/config/injectConfig.ts @@ -0,0 +1,36 @@ +/* + * 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 { injectConfigIntoHtml } from './injectConfigIntoHtml'; +import { injectConfigIntoStatic } from './injectConfigIntoStatic'; +import { InjectOptions } from './types'; + +/** + * Injects configs into the app bundle, replacing any existing injected config. + * @internal + */ +export async function injectConfig( + options: InjectOptions, +): Promise { + // In order to minimize the potential impact when rolling out the new config + // injection, we use both methods for a few releases. This allows the frontend + // app to be behind the backend by a version or two, but temporarily increases + // config injection overhead. + // TODO(Rugvip): After the 1.32 release we can stop calling the static injection if the HTML one is successful + await injectConfigIntoHtml(options); + + return injectConfigIntoStatic(options); +} diff --git a/plugins/app-backend/src/lib/config/injectConfigIntoHtml.test.ts b/plugins/app-backend/src/lib/config/injectConfigIntoHtml.test.ts new file mode 100644 index 0000000000..f4f2655f1c --- /dev/null +++ b/plugins/app-backend/src/lib/config/injectConfigIntoHtml.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createMockDirectory, + mockServices, +} from '@backstage/backend-test-utils'; +import { injectConfigIntoHtml } from './injectConfigIntoHtml'; + +describe('injectConfigIntoHtml', () => { + const mockDir = createMockDirectory(); + + const baseOptions = { + appConfigs: [], + rootDir: mockDir.path, + staticDir: 'ignored', + logger: mockServices.logger.mock(), + }; + + beforeEach(() => { + mockDir.clear(); + }); + + it('should template html', async () => { + mockDir.setContent({ + 'index.html.tmpl': "<%= config.getNumber('x') %>", + }); + await injectConfigIntoHtml({ + ...baseOptions, + appConfigs: [{ context: 'mock', data: { x: 1 } }], + }); + expect(mockDir.content()).toMatchObject({ + 'index.html': '1', + }); + }); + + it('should inject config', async () => { + mockDir.setContent({ + 'index.html.tmpl': '', + }); + await injectConfigIntoHtml({ + ...baseOptions, + appConfigs: [{ context: 'mock', data: { x: 1 } }], + }); + expect(mockDir.content()).toMatchObject({ + 'index.html': ` + +`, + }); + }); +}); diff --git a/plugins/app-backend/src/lib/config/injectConfigIntoHtml.ts b/plugins/app-backend/src/lib/config/injectConfigIntoHtml.ts new file mode 100644 index 0000000000..604b6a8711 --- /dev/null +++ b/plugins/app-backend/src/lib/config/injectConfigIntoHtml.ts @@ -0,0 +1,78 @@ +/* + * 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 { resolve as resolvePath } from 'path'; +import { InjectOptions } from './types'; +import compileTemplate from 'lodash/template'; +import { Config, ConfigReader } from '@backstage/config'; + +const HTML_TEMPLATE_NAME = 'index.html.tmpl'; + +/** @internal */ +export async function injectConfigIntoHtml( + options: InjectOptions, +): Promise { + const { rootDir, appConfigs } = options; + + const templatePath = resolvePath(rootDir, HTML_TEMPLATE_NAME); + + if (!(await fs.exists(templatePath))) { + return false; + } + + const templateContent = await fs.readFile( + resolvePath(rootDir, HTML_TEMPLATE_NAME), + 'utf8', + ); + + const config = ConfigReader.fromConfigs(appConfigs); + + const templateSource = compileTemplate(templateContent, { + interpolate: /<%=([\s\S]+?)%>/g, + }); + + const publicPath = resolvePublicPath(config); + const indexHtmlContent = templateSource({ + config, + publicPath, + }); + + const indexHtmlContentWithConfig = indexHtmlContent.replace( + '', + ` + +`, + ); + + await fs.writeFile( + resolvePath(rootDir, 'index.html'), + indexHtmlContentWithConfig, + 'utf8', + ); + + return true; +} + +export function resolvePublicPath(config: Config) { + const baseUrl = new URL( + config.getOptionalString('app.baseUrl') ?? '/', + 'http://localhost:7007', + ); + return baseUrl.pathname.replace(/\/+$/, ''); +} diff --git a/plugins/app-backend/src/lib/config.test.ts b/plugins/app-backend/src/lib/config/injectConfigIntoStatic.test.ts similarity index 91% rename from plugins/app-backend/src/lib/config.test.ts rename to plugins/app-backend/src/lib/config/injectConfigIntoStatic.test.ts index 34f744d5b8..cd5b4dcae2 100644 --- a/plugins/app-backend/src/lib/config.test.ts +++ b/plugins/app-backend/src/lib/config/injectConfigIntoStatic.test.ts @@ -18,13 +18,14 @@ import { createMockDirectory, mockServices, } from '@backstage/backend-test-utils'; -import { injectConfig } from './config'; +import { injectConfigIntoStatic } from './injectConfigIntoStatic'; -describe('injectConfig', () => { +describe('injectConfigIntoStatic', () => { const mockDir = createMockDirectory(); const baseOptions = { appConfigs: [], + rootDir: 'ignored', staticDir: mockDir.path, logger: mockServices.logger.mock(), }; @@ -42,7 +43,7 @@ describe('injectConfig', () => { 'main.js': '"__APP_INJECTED_RUNTIME_CONFIG__"', }); - await injectConfig(baseOptions); + await injectConfigIntoStatic(baseOptions); expect(mockDir.content()).toEqual({ 'main.js': '/*__APP_INJECTED_CONFIG_MARKER__*/"[]"/*__INJECTED_END__*/', @@ -55,7 +56,7 @@ describe('injectConfig', () => { '({a:"__APP_INJECTED_RUNTIME_CONFIG__",b:"__APP_INJECTED_RUNTIME_CONFIG__"})', }); - await injectConfig(baseOptions); + await injectConfigIntoStatic(baseOptions); expect(mockDir.content()).toEqual({ 'main.js': @@ -71,7 +72,7 @@ describe('injectConfig', () => { 'after.js': 'NO_PLACEHOLDER_HERE', }); - await injectConfig({ + await injectConfigIntoStatic({ ...baseOptions, appConfigs: [{ data: { x: 0 }, context: 'test' }], }); @@ -90,7 +91,7 @@ describe('injectConfig', () => { 'main.js': 'JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__")', }); - await injectConfig({ + await injectConfigIntoStatic({ ...baseOptions, appConfigs: [{ data: { x: 0 }, context: 'test' }], }); @@ -100,7 +101,7 @@ describe('injectConfig', () => { 'JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/)', }); - await injectConfig({ + await injectConfigIntoStatic({ ...baseOptions, appConfigs: [{ data: { x: 1, y: 2 }, context: 'test' }], }); @@ -117,7 +118,7 @@ describe('injectConfig', () => { '({ a: JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__"), b: JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__") })', }); - await injectConfig({ + await injectConfigIntoStatic({ ...baseOptions, appConfigs: [{ data: { x: 0 }, context: 'test' }], }); @@ -127,7 +128,7 @@ describe('injectConfig', () => { '({ a: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/), b: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/) })', }); - await injectConfig({ + await injectConfigIntoStatic({ ...baseOptions, appConfigs: [{ data: { x: 1, y: 2 }, context: 'test' }], }); diff --git a/plugins/app-backend/src/lib/config/injectConfigIntoStatic.ts b/plugins/app-backend/src/lib/config/injectConfigIntoStatic.ts new file mode 100644 index 0000000000..29975b0c2a --- /dev/null +++ b/plugins/app-backend/src/lib/config/injectConfigIntoStatic.ts @@ -0,0 +1,61 @@ +/* + * 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 { resolve as resolvePath } from 'path'; +import { InjectOptions } from './types'; + +/** + * Injects configs into the app bundle, replacing any existing injected config. + */ +export async function injectConfigIntoStatic( + options: InjectOptions, +): Promise { + const { staticDir, logger, appConfigs } = options; + + const files = await fs.readdir(staticDir); + const jsFiles = files.filter(file => file.endsWith('.js')); + + const escapedData = JSON.stringify(appConfigs).replace(/("|'|\\)/g, '\\$1'); + const injected = `/*__APP_INJECTED_CONFIG_MARKER__*/"${escapedData}"/*__INJECTED_END__*/`; + + for (const jsFile of jsFiles) { + const path = resolvePath(staticDir, jsFile); + + const content = await fs.readFile(path, 'utf8'); + if (content.includes('__APP_INJECTED_RUNTIME_CONFIG__')) { + logger.info(`Injecting env config into ${jsFile}`); + + const newContent = content.replaceAll( + '"__APP_INJECTED_RUNTIME_CONFIG__"', + injected, + ); + await fs.writeFile(path, newContent, 'utf8'); + return path; + } else if (content.includes('__APP_INJECTED_CONFIG_MARKER__')) { + logger.info(`Replacing injected env config in ${jsFile}`); + + const newContent = content.replaceAll( + /\/\*__APP_INJECTED_CONFIG_MARKER__\*\/.*?\/\*__INJECTED_END__\*\//g, + injected, + ); + await fs.writeFile(path, newContent, 'utf8'); + return path; + } + } + logger.info('Env config not injected'); + return undefined; +} diff --git a/plugins/app-backend/src/lib/config.ts b/plugins/app-backend/src/lib/config/readFrontendConfig.ts similarity index 55% rename from plugins/app-backend/src/lib/config.ts rename to plugins/app-backend/src/lib/config/readFrontendConfig.ts index df1146e746..becd1c2f52 100644 --- a/plugins/app-backend/src/lib/config.ts +++ b/plugins/app-backend/src/lib/config/readFrontendConfig.ts @@ -23,69 +23,17 @@ import { loadConfigSchema, readEnvConfig, } from '@backstage/config-loader'; -import { LoggerService } from '@backstage/backend-plugin-api'; - -type InjectOptions = { - appConfigs: AppConfig[]; - // Directory of the static JS files to search for file to inject - staticDir: string; - logger: LoggerService; -}; - -/** - * Injects configs into the app bundle, replacing any existing injected config. - */ -export async function injectConfig( - options: InjectOptions, -): Promise { - const { staticDir, logger, appConfigs } = options; - - const files = await fs.readdir(staticDir); - const jsFiles = files.filter(file => file.endsWith('.js')); - - const escapedData = JSON.stringify(appConfigs).replace(/("|'|\\)/g, '\\$1'); - const injected = `/*__APP_INJECTED_CONFIG_MARKER__*/"${escapedData}"/*__INJECTED_END__*/`; - - for (const jsFile of jsFiles) { - const path = resolvePath(staticDir, jsFile); - - const content = await fs.readFile(path, 'utf8'); - if (content.includes('__APP_INJECTED_RUNTIME_CONFIG__')) { - logger.info(`Injecting env config into ${jsFile}`); - - const newContent = content.replaceAll( - '"__APP_INJECTED_RUNTIME_CONFIG__"', - injected, - ); - await fs.writeFile(path, newContent, 'utf8'); - return path; - } else if (content.includes('__APP_INJECTED_CONFIG_MARKER__')) { - logger.info(`Replacing injected env config in ${jsFile}`); - - const newContent = content.replaceAll( - /\/\*__APP_INJECTED_CONFIG_MARKER__\*\/.*?\/\*__INJECTED_END__\*\//g, - injected, - ); - await fs.writeFile(path, newContent, 'utf8'); - return path; - } - } - logger.info('Env config not injected'); - return undefined; -} - -type ReadOptions = { - env: { [name: string]: string | undefined }; - appDistDir: string; - config: Config; - schema?: ConfigSchema; -}; /** * Read config from environment and process the backend config using the * schema that is embedded in the frontend build. */ -export async function readConfigs(options: ReadOptions): Promise { +export async function readFrontendConfig(options: { + env: { [name: string]: string | undefined }; + appDistDir: string; + config: Config; + schema?: ConfigSchema; +}): Promise { const { env, appDistDir, config } = options; const appConfigs = readEnvConfig(env); diff --git a/plugins/app-backend/src/lib/config/types.ts b/plugins/app-backend/src/lib/config/types.ts new file mode 100644 index 0000000000..e6a0e9141b --- /dev/null +++ b/plugins/app-backend/src/lib/config/types.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { LoggerService } from '@backstage/backend-plugin-api'; +import { AppConfig } from '@backstage/config'; + +export interface InjectOptions { + appConfigs: AppConfig[]; + /** Directory of the public web content */ + rootDir: string; + /** Directory of the static JS files to search for file to inject */ + staticDir: string; + logger: LoggerService; +} diff --git a/plugins/app-backend/src/service/router.test.ts b/plugins/app-backend/src/service/router.test.ts index b7ad65706c..cdb150e06c 100644 --- a/plugins/app-backend/src/service/router.test.ts +++ b/plugins/app-backend/src/service/router.test.ts @@ -25,7 +25,7 @@ import { mockServices } from '@backstage/backend-test-utils'; jest.mock('../lib/config', () => ({ injectConfig: jest.fn(), - readConfigs: jest.fn(), + readFrontendConfig: jest.fn(), })); global.__non_webpack_require__ = { @@ -120,11 +120,13 @@ describe('createRouter with static fallback handler', () => { describe('createRouter config schema test', () => { const libConfigs = require('../lib/config'); const libConfigsActual = jest.requireActual('../lib/config'); - const readConfigsMock: jest.Mock = libConfigs.readConfigs; + const readFrontendConfigMock: jest.Mock = libConfigs.readFrontendConfig; beforeEach(() => { jest.resetAllMocks(); - readConfigsMock.mockImplementation(libConfigsActual.readConfigs); + readFrontendConfigMock.mockImplementation( + libConfigsActual.readFrontendConfig, + ); }); it('uses an external schema', async () => { @@ -155,7 +157,7 @@ describe('createRouter config schema test', () => { }), }); - const results = readConfigsMock.mock.results; + const results = readFrontendConfigMock.mock.results; expect(results.length).toBe(1); const mockedResult = results[0]; @@ -177,7 +179,7 @@ describe('createRouter config schema test', () => { appPackageName: 'example-app', }); - const results = readConfigsMock.mock.results; + const results = readFrontendConfigMock.mock.results; expect(results.length).toBe(1); const mockedResult = results[0]; diff --git a/plugins/app-backend/src/service/router.ts b/plugins/app-backend/src/service/router.ts index b205f16203..cd1c0e4ee2 100644 --- a/plugins/app-backend/src/service/router.ts +++ b/plugins/app-backend/src/service/router.ts @@ -26,7 +26,6 @@ import express from 'express'; import Router from 'express-promise-router'; import fs from 'fs-extra'; import { resolve as resolvePath } from 'path'; -import { injectConfig, readConfigs } from '../lib/config'; import { createStaticAssetMiddleware, findStaticAssets, @@ -44,6 +43,7 @@ import { LoggerService, } from '@backstage/backend-plugin-api'; import { AuthenticationError } from '@backstage/errors'; +import { injectConfig, readFrontendConfig } from '../lib/config'; // express uses mime v1 while we only have types for mime v2 type Mime = { lookup(arg0: string): string }; @@ -147,7 +147,7 @@ export async function createRouter( const appConfigs = disableConfigInjection ? undefined - : await readConfigs({ + : await readFrontendConfig({ config, appDistDir, env: process.env, @@ -266,7 +266,8 @@ async function createEntryPointRouter({ const staticDir = resolvePath(rootDir, 'static'); const injectedConfigPath = - appConfigs && (await injectConfig({ appConfigs, logger, staticDir })); + appConfigs && + (await injectConfig({ appConfigs, logger, rootDir, staticDir })); const router = Router(); From ea69e4671c0c806f5c2cf45e6b37392ee5977194 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 30 Aug 2024 15:44:40 +0200 Subject: [PATCH 092/317] core-app-api: add support for backstage.io/config tags Signed-off-by: Patrik Oldsberg --- .changeset/calm-toes-melt.md | 5 +++ ...r.test.ts => defaultConfigLoader.test.tsx} | 42 +++++++++++++++++++ .../src/app/defaultConfigLoader.ts | 31 +++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 .changeset/calm-toes-melt.md rename packages/core-app-api/src/app/{defaultConfigLoader.test.ts => defaultConfigLoader.test.tsx} (67%) diff --git a/.changeset/calm-toes-melt.md b/.changeset/calm-toes-melt.md new file mode 100644 index 0000000000..b8a7b12c46 --- /dev/null +++ b/.changeset/calm-toes-melt.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +The `defaultConfigLoader` now also reads configuration from scripts tags with `type="backstage.io/config"`. The tag is expected to contain a JSON-serialized array of `AppConfig` objects. If any of these script tags are present, the injected runtime configuration in the static assets will no longer be used. diff --git a/packages/core-app-api/src/app/defaultConfigLoader.test.ts b/packages/core-app-api/src/app/defaultConfigLoader.test.tsx similarity index 67% rename from packages/core-app-api/src/app/defaultConfigLoader.test.ts rename to packages/core-app-api/src/app/defaultConfigLoader.test.tsx index c70ec3fbff..44d93aa625 100644 --- a/packages/core-app-api/src/app/defaultConfigLoader.test.ts +++ b/packages/core-app-api/src/app/defaultConfigLoader.test.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ +import React from 'react'; +import { render } from '@testing-library/react'; import { defaultConfigLoaderSync } from './defaultConfigLoader'; +import { ConfigReader } from '@backstage/config'; (process as any).env = { NODE_ENV: 'test' }; const anyEnv = process.env as any; @@ -53,6 +56,45 @@ describe('defaultConfigLoaderSync', () => { ]); }); + it('loads config from script tags and ignore static config', () => { + anyEnv.APP_CONFIG = []; + + render( + , + ); + + const configs = (defaultConfigLoaderSync as any)('{"my":"runtime-config"}'); + expect(configs).toEqual([ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]); + expect(ConfigReader.fromConfigs(configs).get('my')).toBe('override-config'); + }); + + it('loads config from all script tags in order', () => { + anyEnv.APP_CONFIG = []; + + render( + <> + + + , + ); + + const configs = (defaultConfigLoaderSync as any)('{"my":"runtime-config"}'); + expect(configs).toEqual([ + { data: { my: 'config' }, context: 'a' }, + { data: { my: 'override-config' }, context: 'b' }, + ]); + expect(ConfigReader.fromConfigs(configs).get('my')).toBe('override-config'); + }); + it('fails to load invalid missing config', () => { expect(() => defaultConfigLoaderSync()).toThrow( 'No static configuration provided', diff --git a/packages/core-app-api/src/app/defaultConfigLoader.ts b/packages/core-app-api/src/app/defaultConfigLoader.ts index 301b64a865..bfcef390ec 100644 --- a/packages/core-app-api/src/app/defaultConfigLoader.ts +++ b/packages/core-app-api/src/app/defaultConfigLoader.ts @@ -49,9 +49,36 @@ export function defaultConfigLoaderSync( } const configs = appConfig.slice() as unknown as AppConfig[]; - // Avoiding this string also being replaced at runtime - if ( + // Check if we have any config script tags, otherwise fall back to injected config + const configScripts = document.querySelectorAll( + 'script[type="backstage.io/config"]', + ); + if (configScripts.length > 0) { + for (const el of configScripts) { + try { + const content = el.textContent; + if (!content) { + throw new Error('tag is empty'); + } + let data; + try { + data = JSON.parse(content); + } catch (error) { + throw new Error(`failed to parse config; ${error}`); + } + if (!Array.isArray(data)) { + throw new Error('data is not an array'); + } + configs.push(...data); + } catch (error) { + throw new Error( + `Failed to load config from script tag, ${error.message}`, + ); + } + } + } else if ( runtimeConfigJson !== + // Avoiding this string also being replaced at runtime '__app_injected_runtime_config__'.toLocaleUpperCase('en-US') ) { try { From 767db0bd625667c47e43d9ae7998ba8af16c9aca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 07:59:38 +0000 Subject: [PATCH 093/317] chore(deps): update dependency @types/react to v18.3.5 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index cb7d02c59d..93752a1fb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18437,12 +18437,12 @@ __metadata: linkType: hard "@types/react@npm:^18": - version: 18.3.4 - resolution: "@types/react@npm:18.3.4" + version: 18.3.5 + resolution: "@types/react@npm:18.3.5" dependencies: "@types/prop-types": "*" csstype: ^3.0.2 - checksum: 555ccd1af86a23c781dea0360de64b2f7a0708cdcbf9e6496744b77630065868526fd55147c727dc5ef11b7fd712b04f7898757a84c67e2eb9dfd4c4ead10d95 + checksum: 63d2ff473b348c902b68c20be55d2c5124d078c4336c2d1778f316c27789ed596657e8e714022ce14fb24994b0960fc64c913e629bb0bf85815355b0c31eb46b languageName: node linkType: hard From aee6faefe077f241b99b1a7eb09c332e393b3c66 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 08:18:46 +0000 Subject: [PATCH 094/317] chore(deps): update dependency knip to v5.27.5 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index cb7d02c59d..dbfbe22139 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32193,8 +32193,8 @@ __metadata: linkType: hard "knip@npm:^5.0.0": - version: 5.27.4 - resolution: "knip@npm:5.27.4" + version: 5.27.5 + resolution: "knip@npm:5.27.5" dependencies: "@nodelib/fs.walk": 1.2.8 "@snyk/github-codeowners": 1.1.0 @@ -32218,7 +32218,7 @@ __metadata: bin: knip: bin/knip.js knip-bun: bin/knip-bun.js - checksum: 1c83404f5511c17903142995dea305d57ab6830ecd107c5fb1a6a1c7244547f05cf1d3a65bce75d28f4e5566b9251f57ec44429aabe1c3195abd8999fd40b011 + checksum: a348806f33c211246a28f7f353f48c6dca74e483b7b3883132a4bc166516805ad3089f66e32d3a15e3085516d145fcd58a051948c9853ba5e4052ab0630cf957 languageName: node linkType: hard From 44d237d5c083a0a5534f15f86b1adf8900d1e54f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 31 Aug 2024 11:01:17 +0200 Subject: [PATCH 095/317] backend-test-utils: added throw test for register methods Signed-off-by: Patrik Oldsberg --- .../src/next/wiring/TestBackend.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts index b0ef9802f9..812e234828 100644 --- a/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts +++ b/packages/backend-test-utils/src/next/wiring/TestBackend.test.ts @@ -377,4 +377,35 @@ describe('TestBackend', () => { "Module 'tester' for plugin 'test' startup failed; caused by Error: nah", ); }); + + it('should forward errors from plugin register', async () => { + await expect( + startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'test', + register() { + throw new Error('nah'); + }, + }), + ], + }), + ).rejects.toThrow('nah'); + }); + + it('should forward errors from module register', async () => { + await expect( + startTestBackend({ + features: [ + createBackendModule({ + pluginId: 'test', + moduleId: 'tester', + register() { + throw new Error('nah'); + }, + }), + ], + }), + ).rejects.toThrow('nah'); + }); }); From 0c6b61dfb2727c994d20b604bf5eff03758656a1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 09:07:25 +0000 Subject: [PATCH 096/317] chore(deps): update dependency msw to v2.4.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index dbfbe22139..3a3ee74ab5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34553,8 +34553,8 @@ __metadata: linkType: hard "msw@npm:^2.0.0, msw@npm:^2.0.8": - version: 2.4.0 - resolution: "msw@npm:2.4.0" + version: 2.4.1 + resolution: "msw@npm:2.4.1" dependencies: "@bundled-es-modules/cookie": ^2.0.0 "@bundled-es-modules/statuses": ^1.0.1 @@ -34573,6 +34573,7 @@ __metadata: type-fest: ^4.9.0 yargs: ^17.7.2 peerDependencies: + graphql: ">= 16.8.x" typescript: ">= 4.7.x" peerDependenciesMeta: graphql: @@ -34581,7 +34582,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 0be990c56dc1f071bab37785807be99d8429abeb165f122ed47954a2513a5bcd2a0b1866390faed211cf8cd9232e93d9c5551075c59b3a1aa555cafa3a3233f6 + checksum: dc4d20bf856dfaed88c5ac438d4ee003ad8b56af2ca6e16ccd5e22b4f7fecb60a8df47d575bfd5d34c9bfc676263caa0426e1a9b7a539664b1a3427624ee1ffe languageName: node linkType: hard From d26def9a69deccd589e4f83b3231124777463fac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 09:49:50 +0000 Subject: [PATCH 097/317] fix(deps): update dependency json-schema-to-ts to v3.1.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3a3ee74ab5..98acc4787b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31713,12 +31713,12 @@ __metadata: linkType: hard "json-schema-to-ts@npm:^3.0.0": - version: 3.1.0 - resolution: "json-schema-to-ts@npm:3.1.0" + version: 3.1.1 + resolution: "json-schema-to-ts@npm:3.1.1" dependencies: "@babel/runtime": ^7.18.3 ts-algebra: ^2.0.0 - checksum: 0902ff0ad29d0c6d859f7a7f0d8c37bb67aee8a01dd340fe8bf301b9ad9c5422a886834c4aa11f2c81dcadef29c998d646641df9d797876e593053167a5a82bb + checksum: b616f1c2d7492502e11eec4f8e4539ee1e897543a679d929494afdc164d9557275cead8372747b73f239b1e68056ffbf551b03ae82d0047bba0dfe2bbd6b64f4 languageName: node linkType: hard From 3c8d0237a22e010a59cd2aada24ff00ea32cac97 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:46:56 +0000 Subject: [PATCH 098/317] fix(deps): update dependency rollup to v4.21.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 134 +++++++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/yarn.lock b/yarn.lock index 98acc4787b..bd8e1c8d1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14735,114 +14735,114 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.21.1" +"@rollup/rollup-android-arm-eabi@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.21.2" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-android-arm64@npm:4.21.1" +"@rollup/rollup-android-arm64@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-android-arm64@npm:4.21.2" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-darwin-arm64@npm:4.21.1" +"@rollup/rollup-darwin-arm64@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-darwin-arm64@npm:4.21.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-darwin-x64@npm:4.21.1" +"@rollup/rollup-darwin-x64@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-darwin-x64@npm:4.21.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.21.1" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.21.2" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.21.1" +"@rollup/rollup-linux-arm-musleabihf@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.21.2" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.21.1" +"@rollup/rollup-linux-arm64-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.21.2" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.21.1" +"@rollup/rollup-linux-arm64-musl@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.21.2" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.1" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.2" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.21.1" +"@rollup/rollup-linux-riscv64-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.21.2" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.21.1" +"@rollup/rollup-linux-s390x-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.21.2" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.21.1" +"@rollup/rollup-linux-x64-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.21.2" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.21.1" +"@rollup/rollup-linux-x64-musl@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.21.2" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.21.1" +"@rollup/rollup-win32-arm64-msvc@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.21.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.21.1" +"@rollup/rollup-win32-ia32-msvc@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.21.2" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.21.1": - version: 4.21.1 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.21.1" +"@rollup/rollup-win32-x64-msvc@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.21.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -39835,25 +39835,25 @@ __metadata: linkType: hard "rollup@npm:^4.0.0": - version: 4.21.1 - resolution: "rollup@npm:4.21.1" + version: 4.21.2 + resolution: "rollup@npm:4.21.2" dependencies: - "@rollup/rollup-android-arm-eabi": 4.21.1 - "@rollup/rollup-android-arm64": 4.21.1 - "@rollup/rollup-darwin-arm64": 4.21.1 - "@rollup/rollup-darwin-x64": 4.21.1 - "@rollup/rollup-linux-arm-gnueabihf": 4.21.1 - "@rollup/rollup-linux-arm-musleabihf": 4.21.1 - "@rollup/rollup-linux-arm64-gnu": 4.21.1 - "@rollup/rollup-linux-arm64-musl": 4.21.1 - "@rollup/rollup-linux-powerpc64le-gnu": 4.21.1 - "@rollup/rollup-linux-riscv64-gnu": 4.21.1 - "@rollup/rollup-linux-s390x-gnu": 4.21.1 - "@rollup/rollup-linux-x64-gnu": 4.21.1 - "@rollup/rollup-linux-x64-musl": 4.21.1 - "@rollup/rollup-win32-arm64-msvc": 4.21.1 - "@rollup/rollup-win32-ia32-msvc": 4.21.1 - "@rollup/rollup-win32-x64-msvc": 4.21.1 + "@rollup/rollup-android-arm-eabi": 4.21.2 + "@rollup/rollup-android-arm64": 4.21.2 + "@rollup/rollup-darwin-arm64": 4.21.2 + "@rollup/rollup-darwin-x64": 4.21.2 + "@rollup/rollup-linux-arm-gnueabihf": 4.21.2 + "@rollup/rollup-linux-arm-musleabihf": 4.21.2 + "@rollup/rollup-linux-arm64-gnu": 4.21.2 + "@rollup/rollup-linux-arm64-musl": 4.21.2 + "@rollup/rollup-linux-powerpc64le-gnu": 4.21.2 + "@rollup/rollup-linux-riscv64-gnu": 4.21.2 + "@rollup/rollup-linux-s390x-gnu": 4.21.2 + "@rollup/rollup-linux-x64-gnu": 4.21.2 + "@rollup/rollup-linux-x64-musl": 4.21.2 + "@rollup/rollup-win32-arm64-msvc": 4.21.2 + "@rollup/rollup-win32-ia32-msvc": 4.21.2 + "@rollup/rollup-win32-x64-msvc": 4.21.2 "@types/estree": 1.0.5 fsevents: ~2.3.2 dependenciesMeta: @@ -39893,7 +39893,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: d275e1ab9c860a0be97f527a5dfa9bc1234b7cd1e547c057ed28d5697af2f537a210268bcd84bcf884ff23e61f6ceaf8552397279cd3ab627e1b364622aaa053 + checksum: ac83ca15495dd00d6b910e87e7f5f2c480d36977471ec11e3d207089e0aa207d34fc9bd0270f7804b268a7f7473d3bc6b65bccf474fbd1d1826a70bde2741894 languageName: node linkType: hard From 569c3f016faab0b4bcb1b39fb436c5526e24fe93 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 31 Aug 2024 12:47:23 +0200 Subject: [PATCH 099/317] cli: fix css + svg import conflict Signed-off-by: Patrik Oldsberg --- .changeset/swift-fishes-rush.md | 5 +++++ packages/cli/src/lib/builder/config.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/swift-fishes-rush.md diff --git a/.changeset/swift-fishes-rush.md b/.changeset/swift-fishes-rush.md new file mode 100644 index 0000000000..ccfa3a0e40 --- /dev/null +++ b/.changeset/swift-fishes-rush.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fixed an issue where published frontend packages would end up with an invalid import structure if a single module imported both `.css` and `.svg` files. diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts index 8beb2cbb1e..a514158e9b 100644 --- a/packages/cli/src/lib/builder/config.ts +++ b/packages/cli/src/lib/builder/config.ts @@ -118,6 +118,7 @@ export async function makeRollupConfigs( ), output, onwarn, + makeAbsoluteExternalsRelative: false, preserveEntrySignatures: 'strict', // All module imports are always marked as external external: (source, importer, isResolved) => From 78e5de123e42ead0f26bf0e5f3aa6b9fba31a736 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 11:14:22 +0000 Subject: [PATCH 100/317] chore(deps): update actions/setup-python action to v5.2.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_e2e-techdocs.yml | 2 +- .github/workflows/verify_e2e-windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index b59c8c0dd1..e2788e2d47 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -35,7 +35,7 @@ jobs: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index ec58ba3dae..8663e420eb 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -56,7 +56,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: setup python - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: '3.10' From 90317eb583fe050cce93ba1e885dc84cf66e9b09 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 31 Aug 2024 12:51:09 +0000 Subject: [PATCH 101/317] fix(deps): update dependency testcontainers to v10.13.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 303 +++++++++++++++++++++++++++--------------------------- 1 file changed, 151 insertions(+), 152 deletions(-) diff --git a/yarn.lock b/yarn.lock index bd8e1c8d1c..36a3f979de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9269,6 +9269,13 @@ __metadata: languageName: node linkType: hard +"@fastify/busboy@npm:^2.0.0": + version: 2.1.1 + resolution: "@fastify/busboy@npm:2.1.1" + checksum: 42c32ef75e906c9a4809c1e1930a5ca6d4ddc8d138e1a8c8ba5ea07f997db32210617d23b2e4a85fe376316a41a1a0439fc6ff2dedf5126d96f45a9d80754fb2 + languageName: node + linkType: hard + "@floating-ui/core@npm:^1.0.0": version: 1.6.0 resolution: "@floating-ui/core@npm:1.6.0" @@ -17485,14 +17492,14 @@ __metadata: languageName: node linkType: hard -"@types/dockerode@npm:^3.3.0, @types/dockerode@npm:^3.3.24": - version: 3.3.28 - resolution: "@types/dockerode@npm:3.3.28" +"@types/dockerode@npm:^3.3.0, @types/dockerode@npm:^3.3.29": + version: 3.3.31 + resolution: "@types/dockerode@npm:3.3.31" dependencies: "@types/docker-modem": "*" "@types/node": "*" "@types/ssh2": "*" - checksum: d354d790a970b20f7e6b65554eaf851a931adb6fdce831408797b660ff58bf46e87c625ae56c8f7eb902437515cc2286a7cb171ca4e54fbc1262f2d08ab93b78 + checksum: f634f18dc0633f8324faefcde53bcd3d8f3c4bd74d31078cbeb65d2e1597f9abcf12c2158abfaea13dc816bae0f5fa08d0bb570d4214ab0df1ded90db5ebabfe languageName: node linkType: hard @@ -20679,24 +20686,6 @@ __metadata: languageName: node linkType: hard -"archiver-utils@npm:^2.1.0": - version: 2.1.0 - resolution: "archiver-utils@npm:2.1.0" - dependencies: - glob: ^7.1.4 - graceful-fs: ^4.2.0 - lazystream: ^1.0.0 - lodash.defaults: ^4.2.0 - lodash.difference: ^4.5.0 - lodash.flatten: ^4.4.0 - lodash.isplainobject: ^4.0.6 - lodash.union: ^4.6.0 - normalize-path: ^3.0.0 - readable-stream: ^2.0.0 - checksum: 5665f40bde87ee82cb638177bdccca8cc6e55edea1b94338f7e6b56a1d9367b0d9a39e42b47866eaf84b8c67669a7d250900a226207ecc30fa163b52aae859a5 - languageName: node - linkType: hard - "archiver-utils@npm:^4.0.1": version: 4.0.1 resolution: "archiver-utils@npm:4.0.1" @@ -20711,18 +20700,18 @@ __metadata: languageName: node linkType: hard -"archiver@npm:^5.3.2": - version: 5.3.2 - resolution: "archiver@npm:5.3.2" +"archiver-utils@npm:^5.0.0, archiver-utils@npm:^5.0.2": + version: 5.0.2 + resolution: "archiver-utils@npm:5.0.2" dependencies: - archiver-utils: ^2.1.0 - async: ^3.2.4 - buffer-crc32: ^0.2.1 - readable-stream: ^3.6.0 - readdir-glob: ^1.1.2 - tar-stream: ^2.2.0 - zip-stream: ^4.1.0 - checksum: 7d3b9b9b51cf54d88c89fbca9b0847c120bfcf9776c7025c52dd0b62f6603dc63dc0f3f1a09582f936f67e3906b46d58954cc762a255be45e8d3e14e3cb0b0b1 + glob: ^10.0.0 + graceful-fs: ^4.2.0 + is-stream: ^2.0.1 + lazystream: ^1.0.0 + lodash: ^4.17.15 + normalize-path: ^3.0.0 + readable-stream: ^4.0.0 + checksum: 7dc4f3001dc373bd0fa7671ebf08edf6f815cbc539c78b5478a2eaa67e52e3fc0e92f562cdef2ba016c4dcb5468d3d069eb89535c6844da4a5bb0baf08ad5720 languageName: node linkType: hard @@ -20741,6 +20730,21 @@ __metadata: languageName: node linkType: hard +"archiver@npm:^7.0.1": + version: 7.0.1 + resolution: "archiver@npm:7.0.1" + dependencies: + archiver-utils: ^5.0.2 + async: ^3.2.4 + buffer-crc32: ^1.0.0 + readable-stream: ^4.0.0 + readdir-glob: ^1.1.2 + tar-stream: ^3.0.0 + zip-stream: ^6.0.1 + checksum: f93bcc00f919e0bbb6bf38fddf111d6e4d1ed34721b73cc073edd37278303a7a9f67aa4abd6fd2beb80f6c88af77f2eb4f60276343f67605e3aea404e5ad93ea + languageName: node + linkType: hard + "are-we-there-yet@npm:^2.0.0": version: 2.0.0 resolution: "are-we-there-yet@npm:2.0.0" @@ -21992,13 +21996,20 @@ __metadata: languageName: node linkType: hard -"buffer-crc32@npm:^0.2.1, buffer-crc32@npm:^0.2.13, buffer-crc32@npm:~0.2.3": +"buffer-crc32@npm:^0.2.1, buffer-crc32@npm:~0.2.3": version: 0.2.13 resolution: "buffer-crc32@npm:0.2.13" checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c languageName: node linkType: hard +"buffer-crc32@npm:^1.0.0": + version: 1.0.0 + resolution: "buffer-crc32@npm:1.0.0" + checksum: bc114c0e02fe621249e0b5093c70e6f12d4c2b1d8ddaf3b1b7bbe3333466700100e6b1ebdc12c050d0db845bc582c4fce8c293da487cc483f97eea027c480b23 + languageName: node + linkType: hard + "buffer-equal-constant-time@npm:1.0.1": version: 1.0.1 resolution: "buffer-equal-constant-time@npm:1.0.1" @@ -23155,18 +23166,6 @@ __metadata: languageName: node linkType: hard -"compress-commons@npm:^4.1.0": - version: 4.1.0 - resolution: "compress-commons@npm:4.1.0" - dependencies: - buffer-crc32: ^0.2.13 - crc32-stream: ^4.0.1 - normalize-path: ^3.0.0 - readable-stream: ^3.6.0 - checksum: 3348bea7a1cce04b430f6ec3125eebbc0e23bd8dd14d30ec2fb113d3733f95ae6b12d5b33ad794e7ceab70f57e38a6c2642d96a64bc83e490a3ddae8db469949 - languageName: node - linkType: hard - "compress-commons@npm:^5.0.1": version: 5.0.1 resolution: "compress-commons@npm:5.0.1" @@ -23179,6 +23178,19 @@ __metadata: languageName: node linkType: hard +"compress-commons@npm:^6.0.2": + version: 6.0.2 + resolution: "compress-commons@npm:6.0.2" + dependencies: + crc-32: ^1.2.0 + crc32-stream: ^6.0.0 + is-stream: ^2.0.1 + normalize-path: ^3.0.0 + readable-stream: ^4.0.0 + checksum: 37d79a54f91344ecde352588e0a128f28ce619b085acd4f887defd76978a0640e3454a42c7dcadb0191bb3f971724ae4b1f9d6ef9620034aa0427382099ac946 + languageName: node + linkType: hard + "compressible@npm:~2.0.16": version: 2.0.18 resolution: "compressible@npm:2.0.18" @@ -23648,16 +23660,6 @@ __metadata: languageName: node linkType: hard -"crc32-stream@npm:^4.0.1": - version: 4.0.1 - resolution: "crc32-stream@npm:4.0.1" - dependencies: - crc-32: ^1.2.0 - readable-stream: ^3.4.0 - checksum: 39c2e0d5d7a3388e85ccf27ef0b30475f98902d34e7a048decc1665ff67f98f17f850059b28942fdf5582cb836eaa5184453ec35a6ea2442ce3bc10c8f1ee8a4 - languageName: node - linkType: hard - "crc32-stream@npm:^5.0.0": version: 5.0.0 resolution: "crc32-stream@npm:5.0.0" @@ -23668,6 +23670,16 @@ __metadata: languageName: node linkType: hard +"crc32-stream@npm:^6.0.0": + version: 6.0.0 + resolution: "crc32-stream@npm:6.0.0" + dependencies: + crc-32: ^1.2.0 + readable-stream: ^4.0.0 + checksum: e6edc2f81bc387daef6d18b2ac18c2ffcb01b554d3b5c7d8d29b177505aafffba574658fdd23922767e8dab1183d1962026c98c17e17fb272794c33293ef607c + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.0": version: 4.0.3 resolution: "create-ecdh@npm:4.0.3" @@ -24407,7 +24419,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.3.6": +"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.6": version: 4.3.6 resolution: "debug@npm:4.3.6" dependencies: @@ -24909,12 +24921,12 @@ __metadata: languageName: node linkType: hard -"docker-compose@npm:^0.24.6": - version: 0.24.7 - resolution: "docker-compose@npm:0.24.7" +"docker-compose@npm:^0.24.8": + version: 0.24.8 + resolution: "docker-compose@npm:0.24.8" dependencies: yaml: ^2.2.2 - checksum: 54daa1985fd7a57a1881dfdb7d294a189b9ba4e097de81127bfb4ff2f7727d249cc1514ee6a5a06c0085f9f6b5bcafe55b3e463f30b87c4d682bd8b0dac0450e + checksum: 48f3564c46490f1f51899a144deb546b61450a76bffddb378379ac7702aa34b055e0237e0dc77507df94d7ad6f1f7daeeac27730230bce9aafe2e35efeda6b45 languageName: node linkType: hard @@ -28297,18 +28309,19 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": - version: 10.3.10 - resolution: "glob@npm:10.3.10" +"glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": + version: 10.4.5 + resolution: "glob@npm:10.4.5" dependencies: foreground-child: ^3.1.0 - jackspeak: ^2.3.5 - minimatch: ^9.0.1 - minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 - path-scurry: ^1.10.1 + jackspeak: ^3.1.2 + minimatch: ^9.0.4 + minipass: ^7.1.2 + package-json-from-dist: ^1.0.0 + path-scurry: ^1.11.1 bin: glob: dist/esm/bin.mjs - checksum: 4f2fe2511e157b5a3f525a54092169a5f92405f24d2aed3142f4411df328baca13059f4182f1db1bf933e2c69c0bd89e57ae87edd8950cba8c7ccbe84f721cf3 + checksum: 0bc725de5e4862f9f387fd0f2b274baf16850dcd2714502ccf471ee401803997983e2c05590cb65f9675a3c6f2a58e7a53f9e365704108c6ad3cbf1d60934c4a languageName: node linkType: hard @@ -30441,7 +30454,7 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^2.0.0": +"is-stream@npm:^2.0.0, is-stream@npm:^2.0.1": version: 2.0.1 resolution: "is-stream@npm:2.0.1" checksum: b8e05ccdf96ac330ea83c12450304d4a591f9958c11fd17bed240af8d5ffe08aedafa4c0f4cfccd4d28dc9d4d129daca1023633d5c11601a6cbc77521f6fae66 @@ -30808,16 +30821,16 @@ __metadata: languageName: node linkType: hard -"jackspeak@npm:^2.3.5": - version: 2.3.6 - resolution: "jackspeak@npm:2.3.6" +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" dependencies: "@isaacs/cliui": ^8.0.2 "@pkgjs/parseargs": ^0.11.0 dependenciesMeta: "@pkgjs/parseargs": optional: true - checksum: 57d43ad11eadc98cdfe7496612f6bbb5255ea69fe51ea431162db302c2a11011642f50cfad57288bd0aea78384a0612b16e131944ad8ecd09d619041c8531b54 + checksum: be31027fc72e7cc726206b9f560395604b82e0fddb46c4cbf9f97d049bcef607491a5afc0699612eaa4213ca5be8fd3e1e7cd187b3040988b65c9489838a7c00 languageName: node linkType: hard @@ -32663,20 +32676,6 @@ __metadata: languageName: node linkType: hard -"lodash.difference@npm:^4.5.0": - version: 4.5.0 - resolution: "lodash.difference@npm:4.5.0" - checksum: ecee276aa578f300e79350805a14a51be8d1f12b3c1389a19996d8ab516f814211a5f65c68331571ecdad96522b863ccc484b55504ce8c9947212a29f8857d5a - languageName: node - linkType: hard - -"lodash.flatten@npm:^4.4.0": - version: 4.4.0 - resolution: "lodash.flatten@npm:4.4.0" - checksum: 0ac34a393d4b795d4b7421153d27c13ae67e08786c9cbb60ff5b732210d46f833598eee3fb3844bb10070e8488efe390ea53bb567377e0cb47e9e630bf0811cb - languageName: node - linkType: hard - "lodash.flattendeep@npm:^4.0.0": version: 4.4.0 resolution: "lodash.flattendeep@npm:4.4.0" @@ -32761,13 +32760,6 @@ __metadata: languageName: node linkType: hard -"lodash.isplainobject@npm:^4.0.6": - version: 4.0.6 - resolution: "lodash.isplainobject@npm:4.0.6" - checksum: 29c6351f281e0d9a1d58f1a4c8f4400924b4c79f18dfc4613624d7d54784df07efaff97c1ff2659f3e085ecf4fff493300adc4837553104cef2634110b0d5337 - languageName: node - linkType: hard - "lodash.isstring@npm:^4.0.1": version: 4.0.1 resolution: "lodash.isstring@npm:4.0.1" @@ -32824,13 +32816,6 @@ __metadata: languageName: node linkType: hard -"lodash.union@npm:^4.6.0": - version: 4.6.0 - resolution: "lodash.union@npm:4.6.0" - checksum: 1514dc6508b2614ec071a6470f36eb7a70f69bf1abb6d55bdfdc21069635a4517783654b28504c0f025059a7598d37529766888e6d5902b8ab28b712228f7b2a - languageName: node - linkType: hard - "lodash.uniq@npm:^4.5.0": version: 4.5.0 resolution: "lodash.uniq@npm:4.5.0" @@ -33000,10 +32985,10 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.0, lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.2.0 - resolution: "lru-cache@npm:10.2.0" - checksum: eee7ddda4a7475deac51ac81d7dd78709095c6fa46e8350dc2d22462559a1faa3b81ed931d5464b13d48cbd7e08b46100b6f768c76833912bc444b99c37e25db +"lru-cache@npm:^10.0.0, lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 6476138d2125387a6d20f100608c2583d415a4f64a0fecf30c9e2dda976614f09cad4baa0842447bd37dd459a7bd27f57d9d8f8ce558805abd487c583f3d774a languageName: node linkType: hard @@ -34195,7 +34180,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": +"minimatch@npm:^9.0.0, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" dependencies: @@ -34336,10 +34321,10 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3": - version: 7.0.4 - resolution: "minipass@npm:7.0.4" - checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21 +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 2bfd325b95c555f2b4d2814d49325691c7bee937d753814861b0b49d5edcda55cbbf22b6b6a60bb91eddac8668771f03c5ff647dcd9d0f798e9548b9cdc46ee3 languageName: node linkType: hard @@ -36053,6 +36038,13 @@ __metadata: languageName: node linkType: hard +"package-json-from-dist@npm:^1.0.0": + version: 1.0.0 + resolution: "package-json-from-dist@npm:1.0.0" + checksum: ac706ec856a5a03f5261e4e48fa974f24feb044d51f84f8332e2af0af04fbdbdd5bbbfb9cbbe354190409bc8307c83a9e38c6672c3c8855f709afb0006a009ea + languageName: node + linkType: hard + "package-json@npm:^6.3.0": version: 6.5.0 resolution: "package-json@npm:6.5.0" @@ -36480,13 +36472,13 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.10.1": - version: 1.10.1 - resolution: "path-scurry@npm:1.10.1" +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" dependencies: - lru-cache: ^9.1.1 || ^10.0.0 + lru-cache: ^10.2.0 minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 - checksum: e2557cff3a8fb8bc07afdd6ab163a92587884f9969b05bbbaf6fe7379348bfb09af9ed292af12ed32398b15fb443e81692047b786d1eeb6d898a51eb17ed7d90 + checksum: 890d5abcd593a7912dcce7cf7c6bf7a0b5648e3dee6caf0712c126ca0a65c7f3d7b9d769072a4d1baf370f61ce493ab5b038d59988688e0c5f3f646ee3c69023 languageName: node linkType: hard @@ -38948,16 +38940,16 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^4.3.0": - version: 4.4.2 - resolution: "readable-stream@npm:4.4.2" +"readable-stream@npm:^4.0.0, readable-stream@npm:^4.3.0": + version: 4.5.2 + resolution: "readable-stream@npm:4.5.2" dependencies: abort-controller: ^3.0.0 buffer: ^6.0.3 events: ^3.3.0 process: ^0.11.10 string_decoder: ^1.3.0 - checksum: 6f4063763dbdb52658d22d3f49ca976420e1fbe16bbd241f744383715845350b196a2f08b8d6330f8e219153dff34b140aeefd6296da828e1041a7eab1f20d5e + checksum: c4030ccff010b83e4f33289c535f7830190773e274b3fcb6e2541475070bdfd69c98001c3b0cb78763fc00c8b62f514d96c2b10a8bd35d5ce45203a25fa1d33a languageName: node linkType: hard @@ -42031,9 +42023,9 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:^3.0.5": - version: 3.0.5 - resolution: "tar-fs@npm:3.0.5" +"tar-fs@npm:^3.0.6": + version: 3.0.6 + resolution: "tar-fs@npm:3.0.6" dependencies: bare-fs: ^2.1.1 bare-path: ^2.1.0 @@ -42044,7 +42036,7 @@ __metadata: optional: true bare-path: optional: true - checksum: e31c7e3e525fec0afecdec1cac58071809e396187725f2eba442f08a4c5649c8cd6b7ce25982f9a91bb0f055628df47c08177dd2ea4f5dafd3c22f42f8da8f00 + checksum: b4fa09c70f75caf05bf5cf87369cd2862f1ac5fb75c4ddf9d25d55999f7736a94b58ad679d384196cba837c5f5ff14086e060fafccef5474a16e2d3058ffa488 languageName: node linkType: hard @@ -42060,7 +42052,7 @@ __metadata: languageName: node linkType: hard -"tar-stream@npm:^2.0.0, tar-stream@npm:^2.0.1, tar-stream@npm:^2.1.4, tar-stream@npm:^2.2.0": +"tar-stream@npm:^2.0.0, tar-stream@npm:^2.0.1, tar-stream@npm:^2.1.4": version: 2.2.0 resolution: "tar-stream@npm:2.2.0" dependencies: @@ -42242,25 +42234,25 @@ __metadata: linkType: hard "testcontainers@npm:^10.0.0": - version: 10.8.2 - resolution: "testcontainers@npm:10.8.2" + version: 10.13.0 + resolution: "testcontainers@npm:10.13.0" dependencies: "@balena/dockerignore": ^1.0.2 - "@types/dockerode": ^3.3.24 - archiver: ^5.3.2 + "@types/dockerode": ^3.3.29 + archiver: ^7.0.1 async-lock: ^1.4.1 byline: ^5.0.0 - debug: ^4.3.4 - docker-compose: ^0.24.6 + debug: ^4.3.5 + docker-compose: ^0.24.8 dockerode: ^3.3.5 get-port: ^5.1.1 - node-fetch: ^2.7.0 proper-lockfile: ^4.1.2 properties-reader: ^2.3.0 ssh-remote-port-forward: ^1.0.4 - tar-fs: ^3.0.5 - tmp: ^0.2.1 - checksum: 3a97ad705c98abc5066577699be5e791090ef2a5c45fee3a203196fa09d0cb66c7709f6ae8168c025ed97a07f1f5f0676a34e748680514e732e90671720d34c6 + tar-fs: ^3.0.6 + tmp: ^0.2.3 + undici: ^5.28.4 + checksum: cbc2e9503944ad51ff71e743803d14da91905590086f53acc53822f5664d606479c22129361af36e82d99027be0f6a395f7fb388a76a3b09e6692befb40cd8b5 languageName: node linkType: hard @@ -42420,12 +42412,10 @@ __metadata: languageName: node linkType: hard -"tmp@npm:^0.2.1": - version: 0.2.1 - resolution: "tmp@npm:0.2.1" - dependencies: - rimraf: ^3.0.0 - checksum: 8b1214654182575124498c87ca986ac53dc76ff36e8f0e0b67139a8d221eaecfdec108c0e6ec54d76f49f1f72ab9325500b246f562b926f85bcdfca8bf35df9e +"tmp@npm:^0.2.3": + version: 0.2.3 + resolution: "tmp@npm:0.2.3" + checksum: 73b5c96b6e52da7e104d9d44afb5d106bb1e16d9fa7d00dbeb9e6522e61b571fbdb165c756c62164be9a3bbe192b9b268c236d370a2a0955c7689cd2ae377b95 languageName: node linkType: hard @@ -43230,6 +43220,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.28.4": + version: 5.28.4 + resolution: "undici@npm:5.28.4" + dependencies: + "@fastify/busboy": ^2.0.0 + checksum: a8193132d84540e4dc1895ecc8dbaa176e8a49d26084d6fbe48a292e28397cd19ec5d13bc13e604484e76f94f6e334b2bdc740d5f06a6e50c44072818d0c19f9 + languageName: node + linkType: hard + "uni-global@npm:^1.0.0": version: 1.0.0 resolution: "uni-global@npm:1.0.0" @@ -45132,17 +45131,6 @@ __metadata: languageName: node linkType: hard -"zip-stream@npm:^4.1.0": - version: 4.1.0 - resolution: "zip-stream@npm:4.1.0" - dependencies: - archiver-utils: ^2.1.0 - compress-commons: ^4.1.0 - readable-stream: ^3.6.0 - checksum: 4a73da856738b0634700b52f4ab3fe0bf0a532bea6820ad962d0bda0163d2d5525df4859f89a7238e204a378384e12551985049790c1894c3ac191866e85887f - languageName: node - linkType: hard - "zip-stream@npm:^5.0.1": version: 5.0.1 resolution: "zip-stream@npm:5.0.1" @@ -45154,6 +45142,17 @@ __metadata: languageName: node linkType: hard +"zip-stream@npm:^6.0.1": + version: 6.0.1 + resolution: "zip-stream@npm:6.0.1" + dependencies: + archiver-utils: ^5.0.0 + compress-commons: ^6.0.2 + readable-stream: ^4.0.0 + checksum: aa5abd6a89590eadeba040afbc375f53337f12637e5e98330012a12d9886cde7a3ccc28bd91aafab50576035bbb1de39a9a316eecf2411c8b9009c9f94f0db27 + languageName: node + linkType: hard + "zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4": version: 3.23.2 resolution: "zod-to-json-schema@npm:3.23.2" From 10b1452d92876b4ba0206dbc3194682c7a7e823b Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Mon, 26 Aug 2024 15:16:32 +0300 Subject: [PATCH 102/317] feat: allow using languages in `createDevApp` This allows plugin developers to verify their translations on the fly in dev app. Relates also to #26127 Closes #26161 Signed-off-by: Heikki Hellgren --- .changeset/strange-snakes-agree.md | 6 + packages/dev-utils/api-report.md | 7 ++ .../SidebarLanguageSwitcher.test.tsx | 85 ++++++++++++++ .../SidebarLanguageSwitcher.tsx | 109 ++++++++++++++++++ .../SidebarLanguageSwitcher/index.ts | 16 +++ packages/dev-utils/src/components/index.ts | 1 + packages/dev-utils/src/devApp/render.tsx | 36 +++++- 7 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 .changeset/strange-snakes-agree.md create mode 100644 packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.test.tsx create mode 100644 packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.tsx create mode 100644 packages/dev-utils/src/components/SidebarLanguageSwitcher/index.ts diff --git a/.changeset/strange-snakes-agree.md b/.changeset/strange-snakes-agree.md new file mode 100644 index 0000000000..b3f2c220a7 --- /dev/null +++ b/.changeset/strange-snakes-agree.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-plugin-api': patch +'@backstage/dev-utils': patch +--- + +Allow using translations in `createDevApp` diff --git a/packages/dev-utils/api-report.md b/packages/dev-utils/api-report.md index 0a5b3d10e8..1a7b765fab 100644 --- a/packages/dev-utils/api-report.md +++ b/packages/dev-utils/api-report.md @@ -16,6 +16,7 @@ import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { SignInProviderConfig } from '@backstage/core-components'; +import { TranslationResource } from '@backstage/core-plugin-api/alpha'; // @public export function createDevApp(): DevAppBuilder; @@ -27,6 +28,7 @@ export class DevAppBuilder { addSidebarItem(sidebarItem: JSX.Element): DevAppBuilder; addSignInProvider(provider: SignInProviderConfig): this; addThemes(themes: AppTheme[]): this; + addTranslationResource(resource: TranslationResource): this; build(): ComponentType>; registerApi< Api, @@ -37,6 +39,8 @@ export class DevAppBuilder { >(factory: ApiFactory): DevAppBuilder; registerPlugin(...plugins: BackstagePlugin[]): DevAppBuilder; render(): void; + setAvailableLanguages(languages: string[]): this; + setDefaultLanguage(language: string): this; } // @public (undocumented) @@ -55,6 +59,9 @@ export const EntityGridItem: ( }, ) => JSX.Element; +// @public (undocumented) +export const SidebarLanguageSwitcher: () => React_2.JSX.Element | null; + // @public export const SidebarSignOutButton: (props: { icon?: IconComponent; diff --git a/packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.test.tsx b/packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.test.tsx new file mode 100644 index 0000000000..fc620b846b --- /dev/null +++ b/packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.test.tsx @@ -0,0 +1,85 @@ +/* + * 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 { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import ObservableImpl from 'zen-observable'; +import { + AppLanguageApi, + appLanguageApiRef, +} from '@backstage/core-plugin-api/alpha'; +import { SidebarLanguageSwitcher } from './SidebarLanguageSwitcher'; + +describe('SidebarLanguageSwitcher', () => { + let languageApi: jest.Mocked; + + beforeEach(() => { + languageApi = { + getAvailableLanguages: jest.fn(), + getLanguage: jest.fn(), + language$: jest.fn(), + setLanguage: jest.fn(), + }; + + languageApi.language$.mockReturnValue( + ObservableImpl.of<{ language?: string }>({ language: 'en' }), + ); + languageApi.getLanguage.mockReturnValue({ language: 'en' }); + languageApi.getAvailableLanguages.mockReturnValue({ + languages: ['en', 'fi'], + }); + }); + + it('should display current language', async () => { + const { getByLabelText, getByRole, getByText } = await renderInTestApp( + + + , + ); + + const button = getByLabelText('Language'); + expect(button).toBeInTheDocument(); + + await userEvent.click(button); + + expect(getByRole('listbox')).toBeInTheDocument(); + expect(getByText('English')).toBeInTheDocument(); + expect(getByText('English').parentElement?.parentElement).toHaveAttribute( + 'aria-selected', + 'true', + ); + }); + + it('should select different language', async () => { + const { getByLabelText, getByRole, getByText } = await renderInTestApp( + + + , + ); + + const button = getByLabelText('Language'); + expect(button).toBeInTheDocument(); + + await userEvent.click(button); + + expect(getByRole('listbox')).toBeInTheDocument(); + + await userEvent.click(getByText('suomi')); + + expect(languageApi.setLanguage).toHaveBeenCalledWith('fi'); + }); +}); diff --git a/packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.tsx b/packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.tsx new file mode 100644 index 0000000000..71fd9a6c94 --- /dev/null +++ b/packages/dev-utils/src/components/SidebarLanguageSwitcher/SidebarLanguageSwitcher.tsx @@ -0,0 +1,109 @@ +/* + * 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 React, { useState } from 'react'; +import { appLanguageApiRef } from '@backstage/core-plugin-api/alpha'; +import TranslateIcon from '@material-ui/icons/Translate'; +import ListItemText from '@material-ui/core/ListItemText'; +import { useApi } from '@backstage/core-plugin-api'; +import useObservable from 'react-use/esm/useObservable'; +import { SidebarItem } from '@backstage/core-components'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; + +/** @public */ +export const SidebarLanguageSwitcher = () => { + const languageApi = useApi(appLanguageApiRef); + + const [languageObservable] = useState(() => languageApi.language$()); + const { language: currentLanguage } = useObservable( + languageObservable, + languageApi.getLanguage(), + ); + const [anchorEl, setAnchorEl] = useState(); + + const { languages } = languageApi.getAvailableLanguages(); + + if (languages.length <= 1) { + return null; + } + + const open = Boolean(anchorEl); + + const handleClose = () => { + setAnchorEl(undefined); + }; + + const handleOpen = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleSetLanguage = (newLanguage: string | undefined) => { + languageApi.setLanguage(newLanguage); + setAnchorEl(undefined); + }; + + const getLanguageDisplayName = (language: string) => { + try { + const names = new Intl.DisplayNames([language], { + type: 'language', + }); + return names.of(language) || language; + } catch (err) { + return language; + } + }; + + return ( + <> + + + Choose language + {languages.map(lang => { + const active = currentLanguage === lang; + return ( + handleSetLanguage(lang)} + > + {getLanguageDisplayName(lang)} + + ); + })} + + + ); +}; diff --git a/packages/dev-utils/src/components/SidebarLanguageSwitcher/index.ts b/packages/dev-utils/src/components/SidebarLanguageSwitcher/index.ts new file mode 100644 index 0000000000..6726e0bb9d --- /dev/null +++ b/packages/dev-utils/src/components/SidebarLanguageSwitcher/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { SidebarLanguageSwitcher } from './SidebarLanguageSwitcher'; diff --git a/packages/dev-utils/src/components/index.ts b/packages/dev-utils/src/components/index.ts index 8959b6e7b1..7a27a97e73 100644 --- a/packages/dev-utils/src/components/index.ts +++ b/packages/dev-utils/src/components/index.ts @@ -16,3 +16,4 @@ export * from './EntityGridItem'; export * from './SidebarSignOutButton'; +export * from './SidebarLanguageSwitcher'; diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index 3b3c993a6b..a22c4ffb71 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -40,6 +40,7 @@ import { IconComponent, RouteRef, } from '@backstage/core-plugin-api'; +import { TranslationResource } from '@backstage/core-plugin-api/alpha'; import { ScmIntegrationsApi, scmIntegrationsApiRef, @@ -50,7 +51,7 @@ import React, { ComponentType, PropsWithChildren, ReactNode } from 'react'; import { createRoutesFromChildren, Route } from 'react-router-dom'; import { SidebarThemeSwitcher } from './SidebarThemeSwitcher'; import 'react-dom'; -import { SidebarSignOutButton } from '../components'; +import { SidebarLanguageSwitcher, SidebarSignOutButton } from '../components'; let ReactDOMPromise: Promise< typeof import('react-dom') | typeof import('react-dom/client') @@ -98,9 +99,12 @@ export class DevAppBuilder { private readonly routes = new Array(); private readonly sidebarItems = new Array(); private readonly signInProviders = new Array(); + private readonly translationResources = new Array(); private defaultPage?: string; private themes?: Array; + private languages?: string[]; + private defaultLanguage?: string; /** * Register one or more plugins to render in the dev app @@ -193,6 +197,30 @@ export class DevAppBuilder { return this; } + /** + * Set available languages to be shown in the dev app + */ + setAvailableLanguages(languages: string[]) { + this.languages = languages; + return this; + } + + /** + * Add translation resource to the dev app + */ + addTranslationResource(resource: TranslationResource) { + this.translationResources.push(resource); + return this; + } + + /** + * Set default language for the dev app + */ + setDefaultLanguage(language: string) { + this.defaultLanguage = language; + return this; + } + /** * Build a DevApp component using the resources registered so far */ @@ -237,6 +265,11 @@ export class DevAppBuilder { bind(plugin.externalRoutes, targets); } }, + __experimentalTranslations: { + defaultLanguage: this.defaultLanguage, + availableLanguages: this.languages, + resources: this.translationResources, + }, }); const DevApp = ( @@ -252,6 +285,7 @@ export class DevAppBuilder { + From 909b613598a3cce6df65e3aa68be488f7fc124b6 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Mon, 2 Sep 2024 08:10:58 +0200 Subject: [PATCH 103/317] Added changed api-report file Signed-off-by: Jasper Boeijenga --- plugins/scaffolder-react/api-report.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-react/api-report.md b/plugins/scaffolder-react/api-report.md index a19a656d17..f73365f0ac 100644 --- a/plugins/scaffolder-react/api-report.md +++ b/plugins/scaffolder-react/api-report.md @@ -128,7 +128,12 @@ export interface FieldExtensionUiSchema // @public export type FormProps = Pick< FormProps_2, - 'transformErrors' | 'noHtml5Validate' | 'uiSchema' | 'formContext' + | 'transformErrors' + | 'noHtml5Validate' + | 'uiSchema' + | 'formContext' + | 'omitExtraData' + | 'liveOmit' >; // @public From f11bc722f266c9298bb4a1593a63b357159c7ca5 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Mon, 2 Sep 2024 08:35:07 +0200 Subject: [PATCH 104/317] Fix failing test Signed-off-by: Jasper Boeijenga --- .../src/next/TemplateWizardPage/TemplateWizardPage.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx index 2639e8149c..2b1522cac2 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx @@ -140,7 +140,7 @@ describe('TemplateWizardPage', () => { // And the "Create" button should have fired an event expect(analyticsMock.getEvents()[1]).toMatchObject({ action: 'create', - subject: 'expected-name', + subject: 'test', context: { entityRef: 'template:default/test' }, value: 120, }); From 8d1fb8d257d62554cb57a6147ef64ca815c5f351 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Mon, 2 Sep 2024 09:49:37 +0300 Subject: [PATCH 105/317] fix: throw proper error when missing email closes #26404 Signed-off-by: Heikki Hellgren --- .changeset/cool-actors-sin.md | 5 ++++ .../src/authenticator.test.ts | 28 +++++++++++++++++++ .../src/authenticator.ts | 8 ++++-- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 .changeset/cool-actors-sin.md diff --git a/.changeset/cool-actors-sin.md b/.changeset/cool-actors-sin.md new file mode 100644 index 0000000000..4d5b2589c5 --- /dev/null +++ b/.changeset/cool-actors-sin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-aws-alb-provider': patch +--- + +Throw correct error when email is missing from the claims diff --git a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts index 61c80c4b3c..73f859df52 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts @@ -146,6 +146,34 @@ describe('AwsAlbProvider', () => { ); }); + it('Email is missing', async () => { + const jwt = await new SignJWT({ ...mockClaims, email: undefined }) + .setProtectedHeader({ alg: 'HS256', signer: 'SIGNER_ARN' }) + .sign(signingKey); + const req = { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return jwt; + } else if (name === ALB_ACCESS_TOKEN_HEADER) { + return mockAccessToken; + } + return undefined; + }), + } as unknown as express.Request; + await expect( + awsAlbAuthenticator.authenticate( + { req }, + { + issuer: 'ISSUER_URL', + signer: undefined, + getKey: jest.fn().mockResolvedValue(signingKey), + }, + ), + ).rejects.toThrow( + 'Exception occurred during JWT processing: AuthenticationError: Missing email in the JWT token', + ); + }); + it('issuer is missing', async () => { const jwt = await new SignJWT({}) .setProtectedHeader({ alg: 'HS256' }) diff --git a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts index 8a32b05a92..2e7fe1cd26 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.ts @@ -15,11 +15,11 @@ */ import { AuthenticationError } from '@backstage/errors'; -import { AwsAlbClaims, AwsAlbResult, AwsAlbProtectedHeader } from './types'; +import { AwsAlbClaims, AwsAlbProtectedHeader, AwsAlbResult } from './types'; import { jwtVerify } from 'jose'; import { - PassportProfile, createProxyAuthenticator, + PassportProfile, } from '@backstage/plugin-auth-node'; import NodeCache from 'node-cache'; import { makeProfileInfo, provisionKeyCache } from './helpers'; @@ -69,6 +69,10 @@ export const awsAlbAuthenticator = createProxyAuthenticator({ throw new AuthenticationError('Signer mismatch on JWT token'); } + if (!claims.email) { + throw new AuthenticationError(`Missing email in the JWT token`); + } + const fullProfile: PassportProfile = { provider: 'unknown', id: claims.sub, From 51273aa2f025bc7539f811824c68a49b36ad3b46 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 09:13:43 +0200 Subject: [PATCH 106/317] chore: fix prettier Signed-off-by: blam --- microsite/docusaurus.config.ts | 182 +++++++++++++++++---------------- 1 file changed, 93 insertions(+), 89 deletions(-) diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index fcac2973dc..96edb57dea 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -21,7 +21,7 @@ import { themes } from 'prism-react-renderer'; import type * as Preset from '@docusaurus/preset-classic'; import { Config } from '@docusaurus/types'; -import RedirectPlugin from '@docusaurus/plugin-client-redirects' +import RedirectPlugin from '@docusaurus/plugin-client-redirects'; const backstageTheme = themes.vsDark; backstageTheme.plain.backgroundColor = '#232323'; @@ -39,16 +39,19 @@ const PatchedRedirectPlugin: typeof RedirectPlugin = (ctx, opts) => { try { await plugin.postBuild(...args); } catch (error) { - if (error.message === 'The redirect plugin is not supposed to override existing files.') { + if ( + error.message === + 'The redirect plugin is not supposed to override existing files.' + ) { // Bit of a hack to make sure all remaining redirects are written, since the write uses Promise.all await new Promise(resolve => setTimeout(resolve, 1000)); } else { - throw error + throw error; } } - } - } -} + }, + }; +}; const config: Config = { title: 'Backstage Software Catalog and Developer Platform', @@ -165,89 +168,90 @@ const config: Config = { }; }, }), - ctx => PatchedRedirectPlugin(ctx, { - id: '@docusaurus/plugin-client-redirects', - toExtensions: [], - fromExtensions: [], - redirects: [ - { - from: '/docs', - to: '/docs/overview/what-is-backstage', - }, - { - from: '/docs/features/software-catalog/software-catalog-overview', - to: '/docs/features/software-catalog/', - }, - { - from: '/docs/features/software-templates/software-templates-index', - to: '/docs/features/software-templates/', - }, - { - from: '/docs/features/techdocs/techdocs-overview', - to: '/docs/features/techdocs/', - }, - { - from: '/docs/features/kubernetes/overview', - to: '/docs/features/kubernetes/', - }, - { - from: '/docs/features/search/search-overview', - to: '/docs/features/search/', - }, - { - from: '/docs/getting-started/running-backstage-locally', - to: '/docs/getting-started/', - }, - { - from: '/docs/features/software-templates/testing-scaffolder-alpha', - to: '/docs/features/software-templates/migrating-to-rjsf-v5', - }, - { - from: '/docs/auth/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/overview/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/getting-started/create-an-app', - to: '/docs/getting-started/', - }, - { - from: '/docs/getting-started/configuration', - to: '/docs/getting-started/#next-steps', - }, - { - from: '/docs/features/software-templates/authorizing-parameters-steps-and-actions', - to: '/docs/features/software-templates/authorizing-scaffolder-template-details', - }, - { - from: '/docs/local-dev/cli-commands/', - to: '/docs/tooling/cli/commands/', - }, - { - from: '/docs/local-dev/cli-build-system/', - to: '/docs/tooling/cli/build-system/', - }, - { - from: '/docs/local-dev/cli-overview/', - to: '/docs/tooling/cli/overview/', - }, - { - from: '/docs/local-dev/linking-local-packages/', - to: '/docs/tooling/local-dev/linking-local-packages', - }, - { - from: '/docs/local-dev/debugging/', - to: '/docs/tooling/local-dev/debugging', - }, - { - from: '/docs/plugins/url-reader/', - to: '/docs/backend-system/core-services/url-reader', - }, - ] - }), + ctx => + PatchedRedirectPlugin(ctx, { + id: '@docusaurus/plugin-client-redirects', + toExtensions: [], + fromExtensions: [], + redirects: [ + { + from: '/docs', + to: '/docs/overview/what-is-backstage', + }, + { + from: '/docs/features/software-catalog/software-catalog-overview', + to: '/docs/features/software-catalog/', + }, + { + from: '/docs/features/software-templates/software-templates-index', + to: '/docs/features/software-templates/', + }, + { + from: '/docs/features/techdocs/techdocs-overview', + to: '/docs/features/techdocs/', + }, + { + from: '/docs/features/kubernetes/overview', + to: '/docs/features/kubernetes/', + }, + { + from: '/docs/features/search/search-overview', + to: '/docs/features/search/', + }, + { + from: '/docs/getting-started/running-backstage-locally', + to: '/docs/getting-started/', + }, + { + from: '/docs/features/software-templates/testing-scaffolder-alpha', + to: '/docs/features/software-templates/migrating-to-rjsf-v5', + }, + { + from: '/docs/auth/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/overview/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/getting-started/create-an-app', + to: '/docs/getting-started/', + }, + { + from: '/docs/getting-started/configuration', + to: '/docs/getting-started/#next-steps', + }, + { + from: '/docs/features/software-templates/authorizing-parameters-steps-and-actions', + to: '/docs/features/software-templates/authorizing-scaffolder-template-details', + }, + { + from: '/docs/local-dev/cli-commands/', + to: '/docs/tooling/cli/commands/', + }, + { + from: '/docs/local-dev/cli-build-system/', + to: '/docs/tooling/cli/build-system/', + }, + { + from: '/docs/local-dev/cli-overview/', + to: '/docs/tooling/cli/overview/', + }, + { + from: '/docs/local-dev/linking-local-packages/', + to: '/docs/tooling/local-dev/linking-local-packages', + }, + { + from: '/docs/local-dev/debugging/', + to: '/docs/tooling/local-dev/debugging', + }, + { + from: '/docs/plugins/url-reader/', + to: '/docs/backend-system/core-services/url-reader', + }, + ], + }), [ 'docusaurus-pushfeedback', { From 5e2d399f0599191900cc2d1432c80b9fde77fee1 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Mon, 2 Sep 2024 09:39:33 +0200 Subject: [PATCH 107/317] Fix handleCreate and initialState cases Signed-off-by: Jasper Boeijenga --- .../src/next/components/Stepper/Stepper.tsx | 18 ++++++++----- .../src/next/hooks/useFormDataFromQuery.ts | 26 ++++++++----------- .../TemplateWizardPage.test.tsx | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 16a3bb0761..394fe517ce 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -115,7 +115,8 @@ export const Stepper = (stepperProps: StepperProps) => { const apiHolder = useApiHolder(); const [activeStep, setActiveStep] = useState(0); const [isValidating, setIsValidating] = useState(false); - const [formState, setFormState] = useFormDataFromQuery(props.initialState); + const initialState = useFormDataFromQuery(props.initialState); + const [formState, setFormState] = useState<{ [step: string]: any }>(); const [errors, setErrors] = useState(); const styles = useStyles(); @@ -164,11 +165,6 @@ export const Stepper = (stepperProps: StepperProps) => { [setFormState, activeStep], ); - const handleCreate = useCallback(() => { - props.onCreate(formState); - analytics.captureEvent('click', `${createLabel}`); - }, [props, formState, analytics, createLabel]); - const currentStep = useTransformSchemaToProps(steps[activeStep], { layouts }); const handleNext = async ({ @@ -207,6 +203,9 @@ export const Stepper = (stepperProps: StepperProps) => { const mergedUiSchema = merge({}, propUiSchema, currentStep?.uiSchema); const mergedState = useMemo(() => { + if (!formState) { + return initialState; + } const { [`step${activeStep}`]: activeState, ...historicalState } = formState; const chronologicalState = { @@ -214,7 +213,12 @@ export const Stepper = (stepperProps: StepperProps) => { [`step${activeStep}`]: activeState, }; return merge({}, ...Object.values(chronologicalState)); - }, [formState, activeStep]); + }, [formState, activeStep, initialState]); + + const handleCreate = useCallback(() => { + props.onCreate(mergedState); + analytics.captureEvent('click', `${createLabel}`); + }, [props, mergedState, analytics, createLabel]); return ( <> diff --git a/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts b/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts index 966a8fe8f4..9f9f4ed9a2 100644 --- a/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts +++ b/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { JsonValue } from '@backstage/types'; import qs from 'qs'; -import { useState } from 'react'; /** * This hook is used to get the formData from the query string. @@ -25,19 +23,17 @@ import { useState } from 'react'; export const useFormDataFromQuery = ( initialState?: Record, ) => { - return useState>(() => { - if (initialState) { - return initialState; - } + if (initialState) { + return initialState; + } - const query = qs.parse(window.location.search, { - ignoreQueryPrefix: true, - }); - - try { - return JSON.parse(query.formData as string); - } catch (e) { - return {}; - } + const query = qs.parse(window.location.search, { + ignoreQueryPrefix: true, }); + + try { + return JSON.parse(query.formData as string); + } catch (e) { + return {}; + } }; diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx index 2b1522cac2..2639e8149c 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.test.tsx @@ -140,7 +140,7 @@ describe('TemplateWizardPage', () => { // And the "Create" button should have fired an event expect(analyticsMock.getEvents()[1]).toMatchObject({ action: 'create', - subject: 'test', + subject: 'expected-name', context: { entityRef: 'template:default/test' }, value: 120, }); From 0dfe21016fedc018be73cb0ad73c1cb66066e352 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Mon, 2 Sep 2024 09:56:59 +0200 Subject: [PATCH 108/317] Updated api reports Signed-off-by: Jasper Boeijenga --- plugins/scaffolder-react/api-report-alpha.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/scaffolder-react/api-report-alpha.md b/plugins/scaffolder-react/api-report-alpha.md index 545cc02e9e..5b5a37d6b3 100644 --- a/plugins/scaffolder-react/api-report-alpha.md +++ b/plugins/scaffolder-react/api-report-alpha.md @@ -3,12 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -/// - import { ApiHolder } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { CustomFieldValidator } from '@backstage/plugin-scaffolder-react'; -import { Dispatch } from 'react'; import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react'; import { FieldValidation } from '@rjsf/utils'; import { FormProps } from '@backstage/plugin-scaffolder-react'; @@ -25,7 +22,6 @@ import { ReviewStepProps } from '@backstage/plugin-scaffolder-react'; import { ScaffolderRJSFFormProps } from '@backstage/plugin-scaffolder-react'; import { ScaffolderStep } from '@backstage/plugin-scaffolder-react'; import { ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react'; -import { SetStateAction } from 'react'; import { StyleRules } from '@material-ui/core/styles/withStyles'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; @@ -280,7 +276,7 @@ export const useFilteredSchemaProperties: ( // @alpha export const useFormDataFromQuery: ( initialState?: Record, -) => [Record, Dispatch>>]; +) => any; // @alpha (undocumented) export const useTemplateParameterSchema: (templateRef: string) => { From 011ec433ec8dee7fb123b6bf4f3a0584904f9950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 2 Sep 2024 09:57:24 +0200 Subject: [PATCH 109/317] Update .changeset/strange-snakes-agree.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/strange-snakes-agree.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/strange-snakes-agree.md b/.changeset/strange-snakes-agree.md index b3f2c220a7..fc32ec8a6c 100644 --- a/.changeset/strange-snakes-agree.md +++ b/.changeset/strange-snakes-agree.md @@ -1,5 +1,4 @@ --- -'@backstage/core-plugin-api': patch '@backstage/dev-utils': patch --- From 8be7cbf8a999e47964a528a46bca2a4b3a308488 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Mon, 2 Sep 2024 10:22:44 +0200 Subject: [PATCH 110/317] Improve typings Signed-off-by: Jasper Boeijenga --- plugins/scaffolder-react/api-report-alpha.md | 2 +- .../scaffolder-react/src/next/components/Stepper/Stepper.tsx | 4 +++- .../scaffolder-react/src/next/hooks/useFormDataFromQuery.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-react/api-report-alpha.md b/plugins/scaffolder-react/api-report-alpha.md index 5b5a37d6b3..57f97f2012 100644 --- a/plugins/scaffolder-react/api-report-alpha.md +++ b/plugins/scaffolder-react/api-report-alpha.md @@ -276,7 +276,7 @@ export const useFilteredSchemaProperties: ( // @alpha export const useFormDataFromQuery: ( initialState?: Record, -) => any; +) => Record; // @alpha (undocumented) export const useTemplateParameterSchema: (templateRef: string) => { diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 394fe517ce..069512da5a 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -116,7 +116,9 @@ export const Stepper = (stepperProps: StepperProps) => { const [activeStep, setActiveStep] = useState(0); const [isValidating, setIsValidating] = useState(false); const initialState = useFormDataFromQuery(props.initialState); - const [formState, setFormState] = useState<{ [step: string]: any }>(); + const [formState, setFormState] = useState<{ + [step: string]: Record; + }>(); const [errors, setErrors] = useState(); const styles = useStyles(); diff --git a/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts b/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts index 9f9f4ed9a2..777c615cd7 100644 --- a/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts +++ b/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts @@ -22,7 +22,7 @@ import qs from 'qs'; */ export const useFormDataFromQuery = ( initialState?: Record, -) => { +): Record => { if (initialState) { return initialState; } From b552a9429e425fcf768ba867f7e5f3f41eb0715d Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 2 Sep 2024 10:26:10 +0200 Subject: [PATCH 111/317] Rename example module, extend docs Signed-off-by: Johan Haals --- docs/auth/add-auth-provider.md | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/docs/auth/add-auth-provider.md b/docs/auth/add-auth-provider.md index c8ed0315ee..8ea8a4ffc0 100644 --- a/docs/auth/add-auth-provider.md +++ b/docs/auth/add-auth-provider.md @@ -129,12 +129,14 @@ due to its comprehensive set of supported authentication ### Create new auth provider module -Create a new module using `yarn new`, pick `backend-module` and provide `auth-backend` as the plugin ID and `providername-provider` as the module ID. +In this example we will create auth module for a made up service named foobar. + +Create a new module using `yarn new`, pick `backend-module` and provide `auth-backend` as the plugin ID and `foobar-provider` as the module ID. Make sure that the module has the appropriate passport provider as a dependency. ```bash -cd plugins/auth-backend-backend-module-providername-provider +cd plugins/auth-backend-backend-module-foobar-provider yarn add passport-provider-a yarn add @types/passport-provider-a ``` @@ -143,7 +145,7 @@ yarn add @types/passport-provider-a We're then creating a new module that can extend the Auth backend using the `authProvidersExtensionPoint`. -```ts title="plugins/auth-backend-providername-provider/src/module.ts" +```ts title="plugins/auth-backend-foobar-provider/src/module.ts" import { createBackendModule } from '@backstage/backend-plugin-api'; import { authProvidersExtensionPoint, @@ -153,9 +155,9 @@ import { import { providerAuthenticator } from './authenticator'; /** @public */ -export const authModuleOktaProvider = createBackendModule({ +export const authModuleFoobarProvider = createBackendModule({ pluginId: 'auth', - moduleId: 'providername', + moduleId: 'foobar', register(reg) { reg.registerInit({ deps: { @@ -163,7 +165,7 @@ export const authModuleOktaProvider = createBackendModule({ }, async init({ providers }) { providers.registerProvider({ - providerId: 'providername', + providerId: 'foobar', factory: createOAuthProviderFactory({ authenticator: providerAuthenticator, signInResolverFactories: { @@ -178,8 +180,9 @@ export const authModuleOktaProvider = createBackendModule({ ``` Now let's implement the actual authenticator for our provider using `Strategy` from a passport package. +The authenticator is responsible for creating the passport strategy and handling the authentication flow using secrets from the config file. -```ts title="plugins/auth-backend-providername-provider/src/authenticator.ts" +```ts title="plugins/auth-backend-foobar-provider/src/authenticator.ts" import { Strategy as ProviderStrategy } from 'passport-provider-a'; import { createOAuthAuthenticator, @@ -238,6 +241,12 @@ export const providerAuthenticator = createOAuthAuthenticator({ }); ``` +Here are some examples of authenticators that are already implemented in the codebase: + +- [Google](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-google-provider/src/authenticator.ts) +- [Github](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-github-provider/src/authenticator.ts) +- [Okta](https://github.com/backstage/backstage/blob/master/plugins/auth-backend-module-okta-provider/src/authenticator.ts) + ### Creating proxy auth based provider A proxy auth provider is a provider that uses another provider to authenticate for example Google IAP or AWS ALB, please note that those providers are already supported by Backstage. @@ -266,17 +275,13 @@ The process for adding the new module is the same as for any other type of modul If this provider is internal to your installation the import path that you add to `packages/backend/src/index.ts` would be something like: ```ts -backend.add( - import('@internal/plugin-auth-backend-module-providername-provider'), -); +backend.add(import('@internal/plugin-auth-backend-module-foobar-provider')); ``` But if this module is contributed directly to Backstage the module would be imported as ```ts -backend.add( - import('@backstage/plugin-auth-backend-module-providername-provider'), -); +backend.add(import('@backstage/plugin-auth-backend-module-foobar-provider')); ``` By doing this `auth-backend` automatically adds these endpoints: From c46eb0fed24cafbad3324d072d8f6a95d0a82c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 2 Sep 2024 10:43:50 +0200 Subject: [PATCH 112/317] Extend the "unable to resolve user identity" message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/dry-glasses-push.md | 5 +++++ .../auth-node/src/sign-in/readDeclarativeSignInResolver.ts | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/dry-glasses-push.md diff --git a/.changeset/dry-glasses-push.md b/.changeset/dry-glasses-push.md new file mode 100644 index 0000000000..31a04db24a --- /dev/null +++ b/.changeset/dry-glasses-push.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-node': patch +--- + +Extend the "unable to resolve user identity" message diff --git a/plugins/auth-node/src/sign-in/readDeclarativeSignInResolver.ts b/plugins/auth-node/src/sign-in/readDeclarativeSignInResolver.ts index 82b4918326..ed8f4baeeb 100644 --- a/plugins/auth-node/src/sign-in/readDeclarativeSignInResolver.ts +++ b/plugins/auth-node/src/sign-in/readDeclarativeSignInResolver.ts @@ -66,6 +66,8 @@ export function readDeclarativeSignInResolver( } } - throw new Error('Failed to sign-in, unable to resolve user identity'); + throw new Error( + 'Failed to sign-in, unable to resolve user identity. Please verify that your catalog contains the expected User entities that would match your configured sign-in resolver.', + ); }; } From 92108d029baca4411c3c00dbadbbb76b6bde42bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Jerna=C5=9B?= Date: Mon, 2 Sep 2024 11:01:02 +0200 Subject: [PATCH 113/317] fix(docs): Fix imports for blueprints in sample plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Jernaś --- docs/frontend-system/building-plugins/01-index.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/frontend-system/building-plugins/01-index.md b/docs/frontend-system/building-plugins/01-index.md index 57a783d406..633afd6ce8 100644 --- a/docs/frontend-system/building-plugins/01-index.md +++ b/docs/frontend-system/building-plugins/01-index.md @@ -64,8 +64,8 @@ export const rootRouteRef = createRouteRef(); ```tsx title="in src/plugin.ts" import { createFrontendPlugin, - createPageExtension, - createNavItemExtension, + PageBlueprint, + NavItemBlueprint, } from '@backstage/frontend-plugin-api'; import { rootRouteRef } from './routes'; @@ -155,10 +155,7 @@ export function ExamplePage() { ``` ```tsx title="in src/plugin.ts - Registering a factory for our API" -import { - createApiFactory, - createApiExtension, -} from '@backstage/frontend-plugin-api'; +import { createApiFactory, ApiBlueprint } from '@backstage/frontend-plugin-api'; import { exampleApiRef, DefaultExampleApi } from './api'; // highlight-add-start From 0c70f435b2d64c845f0f8a0df2e4dd61139e0f4b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 31 Aug 2024 17:45:54 +0200 Subject: [PATCH 114/317] cli-node: add backstage.inline Signed-off-by: Patrik Oldsberg --- .changeset/hip-fans-decide.md | 5 +++++ packages/cli-node/api-report.md | 1 + packages/cli-node/src/monorepo/PackageGraph.ts | 9 +++++++++ 3 files changed, 15 insertions(+) create mode 100644 .changeset/hip-fans-decide.md diff --git a/.changeset/hip-fans-decide.md b/.changeset/hip-fans-decide.md new file mode 100644 index 0000000000..ecdf6d8a4d --- /dev/null +++ b/.changeset/hip-fans-decide.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli-node': patch +--- + +Add definition for the new `backstage.inline` field in `package.json`. diff --git a/packages/cli-node/api-report.md b/packages/cli-node/api-report.md index 9f7e525048..f22583dd25 100644 --- a/packages/cli-node/api-report.md +++ b/packages/cli-node/api-report.md @@ -18,6 +18,7 @@ export interface BackstagePackageJson { backstage?: { role?: PackageRole; moved?: string; + inline?: boolean; pluginId?: string | null; pluginPackage?: string; pluginPackages?: string[]; diff --git a/packages/cli-node/src/monorepo/PackageGraph.ts b/packages/cli-node/src/monorepo/PackageGraph.ts index c8877803d5..69ec99c2dd 100644 --- a/packages/cli-node/src/monorepo/PackageGraph.ts +++ b/packages/cli-node/src/monorepo/PackageGraph.ts @@ -47,6 +47,15 @@ export interface BackstagePackageJson { role?: PackageRole; moved?: string; + /** + * If set to `true`, the package will be treated as an internal package + * where any imports will be inlined into the consuming package. + * + * When set to `true`, the top-level `private` field must be set to `true` + * as well. + */ + inline?: boolean; + /** * The ID of the plugin if this is a plugin package. Must always be set for plugin and module packages, and may be set for library packages. A `null` value means that the package is explicitly not a plugin package. */ From 2d3caafc0fa84fbb7023f42c5dce87b1cb5f8a5e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 31 Aug 2024 17:47:04 +0200 Subject: [PATCH 115/317] cli: add support for backstage.inline Signed-off-by: Patrik Oldsberg --- .changeset/stupid-toes-complain.md | 5 ++ .../cli/src/commands/build/buildBackend.ts | 2 + packages/cli/src/commands/build/command.ts | 3 +- packages/cli/src/commands/repo/build.ts | 1 + packages/cli/src/lib/builder/config.test.ts | 1 + packages/cli/src/lib/builder/config.ts | 46 +++++++++++++------ packages/cli/src/lib/builder/types.ts | 3 +- .../src/lib/packager/createDistWorkspace.ts | 1 + 8 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 .changeset/stupid-toes-complain.md diff --git a/.changeset/stupid-toes-complain.md b/.changeset/stupid-toes-complain.md new file mode 100644 index 0000000000..246dce3af1 --- /dev/null +++ b/.changeset/stupid-toes-complain.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +The build commands now support the new `backstage.inline` flag in `package.json`, which causes the contents of private packages to be inlined into the consuming package, rather than be treated as an external dependency. diff --git a/packages/cli/src/commands/build/buildBackend.ts b/packages/cli/src/commands/build/buildBackend.ts index 3609e40eb5..88d322d482 100644 --- a/packages/cli/src/commands/build/buildBackend.ts +++ b/packages/cli/src/commands/build/buildBackend.ts @@ -21,6 +21,7 @@ import tar, { CreateOptions } from 'tar'; import { createDistWorkspace } from '../../lib/packager'; import { getEnvironmentParallelism } from '../../lib/parallel'; import { buildPackage, Output } from '../../lib/builder'; +import { PackageGraph } from '@backstage/cli-node'; const BUNDLE_FILE = 'bundle.tar.gz'; const SKELETON_FILE = 'skeleton.tar.gz'; @@ -42,6 +43,7 @@ export async function buildBackend(options: BuildBackendOptions) { packageJson: pkg, outputs: new Set([Output.cjs]), minify, + workspacePackages: await PackageGraph.listTargetPackages(), }); const tmpDir = await fs.mkdtemp(resolvePath(os.tmpdir(), 'backstage-bundle')); diff --git a/packages/cli/src/commands/build/command.ts b/packages/cli/src/commands/build/command.ts index c0ad3c912b..a5f7f60971 100644 --- a/packages/cli/src/commands/build/command.ts +++ b/packages/cli/src/commands/build/command.ts @@ -17,7 +17,7 @@ import { OptionValues } from 'commander'; import { buildPackage, Output } from '../../lib/builder'; import { findRoleFromCommand } from '../../lib/role'; -import { PackageRoles } from '@backstage/cli-node'; +import { PackageGraph, PackageRoles } from '@backstage/cli-node'; import { paths } from '../../lib/paths'; import { buildFrontend } from './buildFrontend'; import { buildBackend } from './buildBackend'; @@ -82,5 +82,6 @@ export async function command(opts: OptionValues): Promise { return buildPackage({ outputs, minify: Boolean(opts.minify), + workspacePackages: await PackageGraph.listTargetPackages(), }); } diff --git a/packages/cli/src/commands/repo/build.ts b/packages/cli/src/commands/repo/build.ts index 6d362c7d98..6a2b9be06d 100644 --- a/packages/cli/src/commands/repo/build.ts +++ b/packages/cli/src/commands/repo/build.ts @@ -137,6 +137,7 @@ export async function command(opts: OptionValues, cmd: Command): Promise { outputs, logPrefix: `${chalk.cyan(relativePath(paths.targetRoot, pkg.dir))}: `, minify: buildOptions.minify, + workspacePackages: packages, }; }); diff --git a/packages/cli/src/lib/builder/config.test.ts b/packages/cli/src/lib/builder/config.test.ts index 733c35fb55..e426e1ffa5 100644 --- a/packages/cli/src/lib/builder/config.test.ts +++ b/packages/cli/src/lib/builder/config.test.ts @@ -29,6 +29,7 @@ describe('makeRollupConfigs', () => { version: '0.0.0', main: './src/index.ts', }, + workspacePackages: [], }); const external = config.external as Exclude< ExternalOption, diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts index 8beb2cbb1e..b568b402bd 100644 --- a/packages/cli/src/lib/builder/config.ts +++ b/packages/cli/src/lib/builder/config.ts @@ -53,6 +53,21 @@ function isFileImport(source: string) { return false; } +function buildInternalImportPattern(options: BuildOptions) { + const inlinedPackages = options.workspacePackages.filter( + pkg => pkg.packageJson.backstage?.inline, + ); + for (const { packageJson } of inlinedPackages) { + if (!packageJson.private) { + throw new Error( + `Inlined package ${packageJson.name} must be marked as private`, + ); + } + } + const names = inlinedPackages.map(pkg => pkg.packageJson.name); + return new RegExp(`^(?:${names.join('|')})(?:$|/)`); +} + export async function makeRollupConfigs( options: BuildOptions, ): Promise { @@ -83,6 +98,19 @@ export async function makeRollupConfigs( SCRIPT_EXTS.includes(e.ext), ); + const internalImportPattern = buildInternalImportPattern(options); + const external = ( + source: string, + importer: string | undefined, + isResolved: boolean, + ) => + Boolean( + importer && + !isResolved && + !internalImportPattern.test(source) && + !isFileImport(source), + ); + if (options.outputs.has(Output.cjs) || options.outputs.has(Output.esm)) { const output = new Array(); const mainFields = ['module', 'main']; @@ -120,8 +148,7 @@ export async function makeRollupConfigs( onwarn, preserveEntrySignatures: 'strict', // All module imports are always marked as external - external: (source, importer, isResolved) => - Boolean(importer && !isResolved && !isFileImport(source)), + external, plugins: [ resolve({ mainFields }), commonjs({ @@ -190,18 +217,11 @@ export async function makeRollupConfigs( chunkFileNames: `types/[name]-[hash].d.ts`, format: 'es', }, - external: [ - /\.css$/, - /\.scss$/, - /\.sass$/, - /\.svg$/, - /\.eot$/, - /\.woff$/, - /\.woff2$/, - /\.ttf$/, - ], + external: (source, importer, isResolved) => + /\.css|scss|sass|svg|eot|woff|woff2|ttf$/.test(source) || + external(source, importer, isResolved), onwarn, - plugins: [dts()], + plugins: [dts({ respectExternal: true })], }); } diff --git a/packages/cli/src/lib/builder/types.ts b/packages/cli/src/lib/builder/types.ts index 7d9c34fe29..937bd157c9 100644 --- a/packages/cli/src/lib/builder/types.ts +++ b/packages/cli/src/lib/builder/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { BackstagePackageJson } from '@backstage/cli-node'; +import { BackstagePackage, BackstagePackageJson } from '@backstage/cli-node'; export enum Output { esm, @@ -28,4 +28,5 @@ export type BuildOptions = { packageJson?: BackstagePackageJson; outputs: Set; minify?: boolean; + workspacePackages: BackstagePackage[]; }; diff --git a/packages/cli/src/lib/packager/createDistWorkspace.ts b/packages/cli/src/lib/packager/createDistWorkspace.ts index b966e23a85..54ce9c073f 100644 --- a/packages/cli/src/lib/packager/createDistWorkspace.ts +++ b/packages/cli/src/lib/packager/createDistWorkspace.ts @@ -211,6 +211,7 @@ export async function createDistWorkspace( outputs: outputs, logPrefix: `${chalk.cyan(relativePath(paths.targetRoot, pkg.dir))}: `, minify: options.minify, + workspacePackages: packages, }); } } From 1a8837e074e178891b6eadf0b908895952897aec Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 31 Aug 2024 18:04:00 +0200 Subject: [PATCH 116/317] repo-tools: avoid API reports for inline packages Signed-off-by: Patrik Oldsberg --- .changeset/yellow-panthers-decide.md | 5 +++++ .../repo-tools/src/commands/api-reports/api-extractor.ts | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 .changeset/yellow-panthers-decide.md diff --git a/.changeset/yellow-panthers-decide.md b/.changeset/yellow-panthers-decide.md new file mode 100644 index 0000000000..a8bf76a17d --- /dev/null +++ b/.changeset/yellow-panthers-decide.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Avoid generating API reports for packages with `backstage.inline` set. diff --git a/packages/repo-tools/src/commands/api-reports/api-extractor.ts b/packages/repo-tools/src/commands/api-reports/api-extractor.ts index 122f04cc7e..7bd026c773 100644 --- a/packages/repo-tools/src/commands/api-reports/api-extractor.ts +++ b/packages/repo-tools/src/commands/api-reports/api-extractor.ts @@ -1232,6 +1232,9 @@ export async function categorizePackageDirs(packageDirs: string[]) { if (!role) { return; // Ignore packages without roles } + if (pkgJson?.backstage?.inline) { + return; // Ignore inline packages + } if (role === 'cli') { cliPackageDirs.push(dir); } else if (role !== 'frontend' && role !== 'backend') { From b99b158dfc148ec54422ff625ebcbbd442c295a9 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 30 Aug 2024 08:39:47 +0200 Subject: [PATCH 117/317] chore: deprecate the `namespace` option in blueprints and extensions Signed-off-by: blam --- .../frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts | 1 - .../frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts | 2 +- packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts | 1 - packages/frontend-plugin-api/src/wiring/createExtension.ts | 1 + .../frontend-plugin-api/src/wiring/createExtensionBlueprint.ts | 3 +++ plugins/app/src/extensions/App.tsx | 1 - plugins/app/src/extensions/AppLayout.tsx | 1 - plugins/app/src/extensions/AppNav.tsx | 1 - plugins/app/src/extensions/AppRoot.tsx | 1 - plugins/app/src/extensions/AppRoutes.tsx | 1 - plugins/app/src/extensions/elements.tsx | 2 -- 11 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts index 369ac052f5..51b3b6d633 100644 --- a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts @@ -24,7 +24,6 @@ const iconsDataRef = createExtensionDataRef<{ /** @public */ export const IconBundleBlueprint = createExtensionBlueprint({ kind: 'icon-bundle', - namespace: 'app', attachTo: { id: 'api:app/icons', input: 'icons' }, output: [iconsDataRef], config: { diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts index 6d3f6280a3..89c2229fab 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts @@ -42,7 +42,7 @@ describe('ThemeBlueprint', () => { "inputs": {}, "kind": "theme", "name": "light", - "namespace": "app", + "namespace": undefined, "output": [ [Function], ], diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts index ca33970125..323078bc4e 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts +++ b/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts @@ -28,7 +28,6 @@ const themeDataRef = createExtensionDataRef().with({ */ export const ThemeBlueprint = createExtensionBlueprint({ kind: 'theme', - namespace: 'app', attachTo: { id: 'api:app/app-theme', input: 'themes' }, output: [themeDataRef], dataRefs: { diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 445bbd8a68..ab999a2d0c 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -122,6 +122,7 @@ export type CreateExtensionOptions< UFactoryOutput extends ExtensionDataValue, > = { kind?: TKind; + /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ namespace?: TNamespace; name?: TName; attachTo: { id: string; input: string }; diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 77d078f257..de788473b5 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -57,6 +57,7 @@ export type CreateExtensionBlueprintOptions< TDataRefs extends { [name in string]: AnyExtensionDataRef }, > = { kind: TKind; + /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ namespace?: TNamespace; attachTo: { id: string; input: string }; disabled?: boolean; @@ -111,6 +112,7 @@ export interface ExtensionBlueprint< TNewNamespace extends string | undefined, TNewName extends string | undefined, >(args: { + /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ namespace?: TNewNamespace; name?: TNewName; attachTo?: { id: string; input: string }; @@ -149,6 +151,7 @@ export interface ExtensionBlueprint< >; }, >(args: { + /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ namespace?: TNewNamespace; name?: TNewName; attachTo?: { id: string; input: string }; diff --git a/plugins/app/src/extensions/App.tsx b/plugins/app/src/extensions/App.tsx index 5c6d95bfbc..7f4b3d5ee1 100644 --- a/plugins/app/src/extensions/App.tsx +++ b/plugins/app/src/extensions/App.tsx @@ -27,7 +27,6 @@ import { ApiProvider } from '../../../../packages/core-app-api/src'; import { AppThemeProvider } from '../../../../packages/core-app-api/src/app/AppThemeProvider'; export const App = createExtension({ - namespace: 'app', attachTo: { id: 'root', input: 'app' }, inputs: { root: createExtensionInput([coreExtensionData.reactElement], { diff --git a/plugins/app/src/extensions/AppLayout.tsx b/plugins/app/src/extensions/AppLayout.tsx index 9fadba8a93..3a0560a807 100644 --- a/plugins/app/src/extensions/AppLayout.tsx +++ b/plugins/app/src/extensions/AppLayout.tsx @@ -23,7 +23,6 @@ import { import { SidebarPage } from '@backstage/core-components'; export const AppLayout = createExtension({ - namespace: 'app', name: 'layout', attachTo: { id: 'app/root', input: 'children' }, inputs: { diff --git a/plugins/app/src/extensions/AppNav.tsx b/plugins/app/src/extensions/AppNav.tsx index 3659f474b2..3e4fcebf8c 100644 --- a/plugins/app/src/extensions/AppNav.tsx +++ b/plugins/app/src/extensions/AppNav.tsx @@ -82,7 +82,6 @@ const SidebarNavItem = ( }; export const AppNav = createExtension({ - namespace: 'app', name: 'nav', attachTo: { id: 'app/layout', input: 'nav' }, inputs: { diff --git a/plugins/app/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx index bd786765f9..014f9a1354 100644 --- a/plugins/app/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -53,7 +53,6 @@ import { RouteTracker } from '../../../../packages/frontend-app-api/src/routing/ import { getBasePath } from '../../../../packages/frontend-app-api/src/routing/getBasePath'; export const AppRoot = createExtension({ - namespace: 'app', name: 'root', attachTo: { id: 'app', input: 'root' }, inputs: { diff --git a/plugins/app/src/extensions/AppRoutes.tsx b/plugins/app/src/extensions/AppRoutes.tsx index 6ef7e2a271..5880d6182e 100644 --- a/plugins/app/src/extensions/AppRoutes.tsx +++ b/plugins/app/src/extensions/AppRoutes.tsx @@ -25,7 +25,6 @@ import { import { useRoutes } from 'react-router-dom'; export const AppRoutes = createExtension({ - namespace: 'app', name: 'routes', attachTo: { id: 'app/layout', input: 'content' }, inputs: { diff --git a/plugins/app/src/extensions/elements.tsx b/plugins/app/src/extensions/elements.tsx index 1ab7f97ba2..bbb6374ebe 100644 --- a/plugins/app/src/extensions/elements.tsx +++ b/plugins/app/src/extensions/elements.tsx @@ -19,7 +19,6 @@ import { AppRootElementBlueprint } from '@backstage/frontend-plugin-api'; import React from 'react'; export const oauthRequestDialogAppRootElement = AppRootElementBlueprint.make({ - namespace: 'app', name: 'oauth-request-dialog', params: { element: , @@ -28,7 +27,6 @@ export const oauthRequestDialogAppRootElement = AppRootElementBlueprint.make({ export const alertDisplayAppRootElement = AppRootElementBlueprint.makeWithOverrides({ - namespace: 'app', name: 'alert-display', config: { schema: { From 6860490a88e3f2f6173a9b05238922b0570ff8b9 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 30 Aug 2024 09:25:16 +0200 Subject: [PATCH 118/317] chore: api-reports Signed-off-by: blam --- packages/frontend-plugin-api/api-report.md | 4 ++-- plugins/app/api-report.md | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index b42efddffa..9419efc05e 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -1289,7 +1289,7 @@ export { googleAuthApiRef }; // @public (undocumented) export const IconBundleBlueprint: ExtensionBlueprint<{ kind: 'icon-bundle'; - namespace: 'app'; + namespace: undefined; name: undefined; params: { icons: { @@ -1701,7 +1701,7 @@ export interface SubRouteRef< // @public export const ThemeBlueprint: ExtensionBlueprint<{ kind: 'theme'; - namespace: 'app'; + namespace: undefined; name: undefined; params: { theme: AppTheme; diff --git a/plugins/app/api-report.md b/plugins/app/api-report.md index ca76ecb91b..fbf77debb4 100644 --- a/plugins/app/api-report.md +++ b/plugins/app/api-report.md @@ -71,7 +71,7 @@ const appPlugin: FrontendPlugin< >; }; kind: undefined; - namespace: 'app'; + namespace: undefined; name: undefined; }>; 'api:app/app-language': ExtensionDefinition<{ @@ -112,7 +112,7 @@ const appPlugin: FrontendPlugin< >; }; kind: undefined; - namespace: 'app'; + namespace: undefined; name: 'layout'; }>; 'app/nav': ExtensionDefinition<{ @@ -155,7 +155,7 @@ const appPlugin: FrontendPlugin< >; }; kind: undefined; - namespace: 'app'; + namespace: undefined; name: 'nav'; }>; 'app/root': ExtensionDefinition<{ @@ -220,7 +220,7 @@ const appPlugin: FrontendPlugin< >; }; kind: undefined; - namespace: 'app'; + namespace: undefined; name: 'root'; }>; 'app/routes': ExtensionDefinition<{ @@ -249,7 +249,7 @@ const appPlugin: FrontendPlugin< >; }; kind: undefined; - namespace: 'app'; + namespace: undefined; name: 'routes'; }>; 'api:app/app-theme': ExtensionDefinition<{ @@ -275,7 +275,7 @@ const appPlugin: FrontendPlugin< }>; 'theme:app/light': ExtensionDefinition<{ kind: 'theme'; - namespace: 'app'; + namespace: undefined; name: 'light'; config: {}; configInput: {}; @@ -284,7 +284,7 @@ const appPlugin: FrontendPlugin< }>; 'theme:app/dark': ExtensionDefinition<{ kind: 'theme'; - namespace: 'app'; + namespace: undefined; name: 'dark'; config: {}; configInput: {}; @@ -393,7 +393,7 @@ const appPlugin: FrontendPlugin< }>; 'app-root-element:app/oauth-request-dialog': ExtensionDefinition<{ kind: 'app-root-element'; - namespace: 'app'; + namespace: undefined; name: 'oauth-request-dialog'; config: {}; configInput: {}; @@ -436,7 +436,7 @@ const appPlugin: FrontendPlugin< >; }; kind: 'app-root-element'; - namespace: 'app'; + namespace: undefined; name: 'alert-display'; }>; 'api:app/discovery': ExtensionDefinition<{ From 52f9c5a0124b832458728d9464e335fa9990f486 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 30 Aug 2024 09:26:15 +0200 Subject: [PATCH 119/317] chore: changeset Signed-off-by: blam --- .changeset/fuzzy-feet-exist.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fuzzy-feet-exist.md diff --git a/.changeset/fuzzy-feet-exist.md b/.changeset/fuzzy-feet-exist.md new file mode 100644 index 0000000000..2197da2f1f --- /dev/null +++ b/.changeset/fuzzy-feet-exist.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/plugin-app': patch +--- + +Deprecated the `namespace` option for `createExtensionBlueprint` and `createExtension`, these are no longer required and will default to the `pluginId` instead From 86ec8e170b595d2c756e206873e74af67bf10b2d Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 30 Aug 2024 13:11:43 +0200 Subject: [PATCH 120/317] chore: added a console warning for deprecation as it's a little tricky to do in typescript Signed-off-by: blam --- .../src/wiring/resolveExtensionDefinition.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index 870fb258f5..a6be1ded97 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -149,6 +149,7 @@ export function resolveExtensionDefinition< override: _skip2, ...rest } = internalDefinition; + const namespace = internalDefinition.namespace ?? context?.namespace; const namePart = @@ -161,6 +162,13 @@ export function resolveExtensionDefinition< const id = kind ? `${kind}:${namePart}` : namePart; + if (internalDefinition.namespace) { + // eslint-disable-next-line no-console + console.warn( + `Extension ${id} has a deprecated namespace set, this option is no longer required, and should be removed from createExtensionBlueprint or createExtension. It will be automatically set from the pluginId when installing extensions using createFrontendModule and createFrontendPlugin`, + ); + } + return { ...rest, $$type: '@backstage/Extension', From a3b48cc65ac0a147350d3f42836235ff1aaa9dc1 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 30 Aug 2024 15:12:50 +0200 Subject: [PATCH 121/317] chore: rework how we do the deprecations Signed-off-by: blam --- packages/frontend-plugin-api/api-report.md | 166 +++++++++++++++++- .../src/wiring/createExtension.ts | 89 +++++++++- .../src/wiring/createExtensionBlueprint.ts | 155 +++++++++++++++- .../src/wiring/resolveExtensionDefinition.ts | 7 - 4 files changed, 401 insertions(+), 16 deletions(-) diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 9419efc05e..fbcd3a9caa 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -477,6 +477,55 @@ export function createComponentRef(options: { }): ComponentRef; // @public (undocumented) +export function createExtension< + UOutput extends AnyExtensionDataRef, + TInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }, + TConfigSchema extends { + [key: string]: (zImpl: typeof z) => z.ZodType; + }, + UFactoryOutput extends ExtensionDataValue, + const TKind extends string | undefined = undefined, + const TNamespace extends string | undefined = undefined, + const TName extends string | undefined = undefined, +>( + options: CreateExtensionOptions< + TKind, + undefined, + TName, + UOutput, + TInputs, + TConfigSchema, + UFactoryOutput + >, +): ExtensionDefinition<{ + config: string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + output: UOutput; + inputs: TInputs; + kind: string | undefined extends TKind ? undefined : TKind; + namespace: string | undefined extends TNamespace ? undefined : TNamespace; + name: string | undefined extends TName ? undefined : TName; +}>; + +// @public @deprecated (undocumented) export function createExtension< UOutput extends AnyExtensionDataRef, TInputs extends { @@ -543,7 +592,7 @@ export function createExtensionBlueprint< }, UFactoryOutput extends ExtensionDataValue, TKind extends string, - TNamespace extends string | undefined = undefined, + TNamespace extends undefined = undefined, TName extends string | undefined = undefined, TDataRefs extends { [name in string]: AnyExtensionDataRef; @@ -551,7 +600,7 @@ export function createExtensionBlueprint< >( options: CreateExtensionBlueprintOptions< TKind, - TNamespace, + undefined, TName, TParams, UOutput, @@ -562,7 +611,7 @@ export function createExtensionBlueprint< >, ): ExtensionBlueprint<{ kind: TKind; - namespace: TNamespace; + namespace: undefined; name: TName; params: TParams; output: UOutput; @@ -857,6 +906,28 @@ export interface ExtensionBlueprint< // (undocumented) dataRefs: T['dataRefs']; // (undocumented) + make< + TNewNamespace extends string | undefined, + TNewName extends string | undefined, + >(args: { + namespace?: undefined; + name?: TNewName; + attachTo?: { + id: string; + input: string; + }; + disabled?: boolean; + params: T['params']; + }): ExtensionDefinition<{ + kind: T['kind']; + namespace: undefined; + name: string | undefined extends TNewName ? T['name'] : TNewName; + config: T['config']; + configInput: T['configInput']; + output: T['output']; + inputs: T['inputs']; + }>; + // @deprecated (undocumented) make< TNewNamespace extends string | undefined, TNewName extends string | undefined, @@ -898,7 +969,94 @@ export interface ExtensionBlueprint< >; }, >(args: { - namespace?: TNewNamespace; + namespace?: undefined; + name?: TNewName; + attachTo?: { + id: string; + input: string; + }; + disabled?: boolean; + inputs?: TExtraInputs & { + [KName in keyof T['inputs']]?: `Error: Input '${KName & + string}' is already defined in parent definition`; + }; + output?: Array; + config?: { + schema: TExtensionConfigSchema & { + [KName in keyof T['config']]?: `Error: Config key '${KName & + string}' is already defined in parent schema`; + }; + }; + factory( + originalFactory: ( + params: T['params'], + context?: { + config?: T['config']; + inputs?: ResolveInputValueOverrides>; + }, + ) => ExtensionDataContainer>, + context: { + node: AppNode; + apis: ApiHolder; + config: T['config'] & { + [key in keyof TExtensionConfigSchema]: z.infer< + ReturnType + >; + }; + inputs: Expand>; + }, + ): Iterable & + VerifyExtensionFactoryOutput< + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UFactoryOutput + >; + }): ExtensionDefinition<{ + config: (string extends keyof TExtensionConfigSchema + ? {} + : { + [key in keyof TExtensionConfigSchema]: z.infer< + ReturnType + >; + }) & + T['config']; + configInput: (string extends keyof TExtensionConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + >) & + T['configInput']; + output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput; + inputs: T['inputs'] & TExtraInputs; + kind: T['kind']; + namespace: undefined; + name: string | undefined extends TNewName ? T['name'] : TNewName; + }>; + // @deprecated (undocumented) + makeWithOverrides< + TNewNamespace extends string | undefined, + TNewName extends string | undefined, + TExtensionConfigSchema extends { + [key in string]: (zImpl: typeof z) => z.ZodType; + }, + UFactoryOutput extends ExtensionDataValue, + UNewOutput extends AnyExtensionDataRef, + TExtraInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }, + >(args: { + namespace: TNewNamespace; name?: TNewName; attachTo?: { id: string; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index ab999a2d0c..8c79758395 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -122,7 +122,6 @@ export type CreateExtensionOptions< UFactoryOutput extends ExtensionDataValue, > = { kind?: TKind; - /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ namespace?: TNamespace; name?: TName; attachTo: { id: string; input: string }; @@ -315,6 +314,94 @@ export function toInternalExtensionDefinition< } /** @public */ +export function createExtension< + UOutput extends AnyExtensionDataRef, + TInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, + TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, + UFactoryOutput extends ExtensionDataValue, + const TKind extends string | undefined = undefined, + const TNamespace extends string | undefined = undefined, + const TName extends string | undefined = undefined, +>( + options: CreateExtensionOptions< + TKind, + undefined, + TName, + UOutput, + TInputs, + TConfigSchema, + UFactoryOutput + >, +): ExtensionDefinition<{ + config: string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + output: UOutput; + inputs: TInputs; + kind: string | undefined extends TKind ? undefined : TKind; + namespace: string | undefined extends TNamespace ? undefined : TNamespace; + name: string | undefined extends TName ? undefined : TName; +}>; +/** + * @public + * @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. + */ +export function createExtension< + UOutput extends AnyExtensionDataRef, + TInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, + TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, + UFactoryOutput extends ExtensionDataValue, + const TKind extends string | undefined = undefined, + const TNamespace extends string | undefined = undefined, + const TName extends string | undefined = undefined, +>( + options: CreateExtensionOptions< + TKind, + TNamespace, + TName, + UOutput, + TInputs, + TConfigSchema, + UFactoryOutput + >, +): ExtensionDefinition<{ + config: string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + output: UOutput; + inputs: TInputs; + kind: string | undefined extends TKind ? undefined : TKind; + namespace: string | undefined extends TNamespace ? undefined : TNamespace; + name: string | undefined extends TName ? undefined : TName; +}>; export function createExtension< UOutput extends AnyExtensionDataRef, TInputs extends { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index de788473b5..f67b30c8df 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -57,7 +57,6 @@ export type CreateExtensionBlueprintOptions< TDataRefs extends { [name in string]: AnyExtensionDataRef }, > = { kind: TKind; - /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ namespace?: TNamespace; attachTo: { id: string; input: string }; disabled?: boolean; @@ -112,7 +111,25 @@ export interface ExtensionBlueprint< TNewNamespace extends string | undefined, TNewName extends string | undefined, >(args: { - /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ + namespace?: undefined; + name?: TNewName; + attachTo?: { id: string; input: string }; + disabled?: boolean; + params: T['params']; + }): ExtensionDefinition<{ + kind: T['kind']; + namespace: undefined; + name: string | undefined extends TNewName ? T['name'] : TNewName; + config: T['config']; + configInput: T['configInput']; + output: T['output']; + inputs: T['inputs']; + }>; + /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ + make< + TNewNamespace extends string | undefined, + TNewName extends string | undefined, + >(args: { namespace?: TNewNamespace; name?: TNewName; attachTo?: { id: string; input: string }; @@ -151,8 +168,88 @@ export interface ExtensionBlueprint< >; }, >(args: { - /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ - namespace?: TNewNamespace; + namespace?: undefined; + name?: TNewName; + attachTo?: { id: string; input: string }; + disabled?: boolean; + inputs?: TExtraInputs & { + [KName in keyof T['inputs']]?: `Error: Input '${KName & + string}' is already defined in parent definition`; + }; + output?: Array; + config?: { + schema: TExtensionConfigSchema & { + [KName in keyof T['config']]?: `Error: Config key '${KName & + string}' is already defined in parent schema`; + }; + }; + factory( + originalFactory: ( + params: T['params'], + context?: { + config?: T['config']; + inputs?: ResolveInputValueOverrides>; + }, + ) => ExtensionDataContainer>, + context: { + node: AppNode; + apis: ApiHolder; + config: T['config'] & { + [key in keyof TExtensionConfigSchema]: z.infer< + ReturnType + >; + }; + inputs: Expand>; + }, + ): Iterable & + VerifyExtensionFactoryOutput< + AnyExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UFactoryOutput + >; + }): ExtensionDefinition<{ + config: (string extends keyof TExtensionConfigSchema + ? {} + : { + [key in keyof TExtensionConfigSchema]: z.infer< + ReturnType + >; + }) & + T['config']; + configInput: (string extends keyof TExtensionConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + >) & + T['configInput']; + output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput; + inputs: T['inputs'] & TExtraInputs; + kind: T['kind']; + namespace: undefined; + name: string | undefined extends TNewName ? T['name'] : TNewName; + }>; + /** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ + makeWithOverrides< + TNewNamespace extends string | undefined, + TNewName extends string | undefined, + TExtensionConfigSchema extends { + [key in string]: (zImpl: typeof z) => z.ZodType; + }, + UFactoryOutput extends ExtensionDataValue, + UNewOutput extends AnyExtensionDataRef, + TExtraInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, + >(args: { + namespace: TNewNamespace; name?: TNewName; attachTo?: { id: string; input: string }; disabled?: boolean; @@ -227,6 +324,56 @@ export interface ExtensionBlueprint< * * @public */ +export function createExtensionBlueprint< + TParams extends object, + UOutput extends AnyExtensionDataRef, + TInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, + TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, + UFactoryOutput extends ExtensionDataValue, + TKind extends string, + TNamespace extends undefined = undefined, + TName extends string | undefined = undefined, + TDataRefs extends { [name in string]: AnyExtensionDataRef } = never, +>( + options: CreateExtensionBlueprintOptions< + TKind, + undefined, + TName, + TParams, + UOutput, + TInputs, + TConfigSchema, + UFactoryOutput, + TDataRefs + >, +): ExtensionBlueprint<{ + kind: TKind; + namespace: undefined; + name: TName; + params: TParams; + output: UOutput; + inputs: string extends keyof TInputs ? {} : TInputs; + config: string extends keyof TConfigSchema + ? {} + : { [key in keyof TConfigSchema]: z.infer> }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + dataRefs: TDataRefs; +}>; +/** + * @public + * @deprcated the namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. + */ export function createExtensionBlueprint< TParams extends object, UOutput extends AnyExtensionDataRef, diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index a6be1ded97..5a631819d7 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -162,13 +162,6 @@ export function resolveExtensionDefinition< const id = kind ? `${kind}:${namePart}` : namePart; - if (internalDefinition.namespace) { - // eslint-disable-next-line no-console - console.warn( - `Extension ${id} has a deprecated namespace set, this option is no longer required, and should be removed from createExtensionBlueprint or createExtension. It will be automatically set from the pluginId when installing extensions using createFrontendModule and createFrontendPlugin`, - ); - } - return { ...rest, $$type: '@backstage/Extension', From 8c27b82078f4738d2d76231108c1066e69bf8e93 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 30 Aug 2024 17:22:00 +0200 Subject: [PATCH 122/317] chore: need a separate type for the overload and for the implementation Signed-off-by: blam --- .../src/wiring/createExtensionBlueprint.ts | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index f67b30c8df..40d1284b15 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -372,8 +372,54 @@ export function createExtensionBlueprint< }>; /** * @public - * @deprcated the namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. + * @deprecated the namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */ +export function createExtensionBlueprint< + TParams extends object, + UOutput extends AnyExtensionDataRef, + TInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }, + TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, + UFactoryOutput extends ExtensionDataValue, + TKind extends string, + TNamespace extends string | undefined = undefined, + TName extends string | undefined = undefined, + TDataRefs extends { [name in string]: AnyExtensionDataRef } = never, +>( + options: CreateExtensionBlueprintOptions< + TKind, + TNamespace, + TName, + TParams, + UOutput, + TInputs, + TConfigSchema, + UFactoryOutput, + TDataRefs + >, +): ExtensionBlueprint<{ + kind: TKind; + namespace: TNamespace; + name: TName; + params: TParams; + output: UOutput; + inputs: string extends keyof TInputs ? {} : TInputs; + config: string extends keyof TConfigSchema + ? {} + : { [key in keyof TConfigSchema]: z.infer> }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + dataRefs: TDataRefs; +}>; export function createExtensionBlueprint< TParams extends object, UOutput extends AnyExtensionDataRef, From 79310e935c54628073b03b037b2ed1e6e4299f33 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 30 Aug 2024 17:26:51 +0200 Subject: [PATCH 123/317] chore: derprecation Signed-off-by: blam --- packages/frontend-plugin-api/api-report.md | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index fbcd3a9caa..276ca190a6 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -631,6 +631,63 @@ export function createExtensionBlueprint< dataRefs: TDataRefs; }>; +// @public @deprecated (undocumented) +export function createExtensionBlueprint< + TParams extends object, + UOutput extends AnyExtensionDataRef, + TInputs extends { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }, + TConfigSchema extends { + [key in string]: (zImpl: typeof z) => z.ZodType; + }, + UFactoryOutput extends ExtensionDataValue, + TKind extends string, + TNamespace extends string | undefined = undefined, + TName extends string | undefined = undefined, + TDataRefs extends { + [name in string]: AnyExtensionDataRef; + } = never, +>( + options: CreateExtensionBlueprintOptions< + TKind, + TNamespace, + TName, + TParams, + UOutput, + TInputs, + TConfigSchema, + UFactoryOutput, + TDataRefs + >, +): ExtensionBlueprint<{ + kind: TKind; + namespace: TNamespace; + name: TName; + params: TParams; + output: UOutput; + inputs: string extends keyof TInputs ? {} : TInputs; + config: string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }; + configInput: string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >; + dataRefs: TDataRefs; +}>; + // @public (undocumented) export type CreateExtensionBlueprintOptions< TKind extends string, From da5dd9361827df0fcb01be2bd720338db227771a Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 2 Sep 2024 15:12:21 +0200 Subject: [PATCH 124/317] backend-defaults: deprecate createLifecycleMiddleware Reduce API surface by deprecating export with no external usage. Fixes #26359 Signed-off-by: Johan Haals --- packages/backend-defaults/api-report-httpRouter.md | 2 +- .../src/entrypoints/httpRouter/createLifecycleMiddleware.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend-defaults/api-report-httpRouter.md b/packages/backend-defaults/api-report-httpRouter.md index dc612377ae..bc5f069559 100644 --- a/packages/backend-defaults/api-report-httpRouter.md +++ b/packages/backend-defaults/api-report-httpRouter.md @@ -9,7 +9,7 @@ import { LifecycleService } from '@backstage/backend-plugin-api'; import { RequestHandler } from 'express'; import { ServiceFactory } from '@backstage/backend-plugin-api'; -// @public +// @public @deprecated export function createLifecycleMiddleware( options: LifecycleMiddlewareOptions, ): RequestHandler; diff --git a/packages/backend-defaults/src/entrypoints/httpRouter/createLifecycleMiddleware.ts b/packages/backend-defaults/src/entrypoints/httpRouter/createLifecycleMiddleware.ts index 3f8ee3ea69..2a18f5046e 100644 --- a/packages/backend-defaults/src/entrypoints/httpRouter/createLifecycleMiddleware.ts +++ b/packages/backend-defaults/src/entrypoints/httpRouter/createLifecycleMiddleware.ts @@ -48,6 +48,8 @@ export interface LifecycleMiddlewareOptions { * {@link @backstage/errors#ServiceUnavailableError}. * * @public + * @deprecated This function export will be removed in a future release. + * If rely on this function then please reach out to the Backstage maintainers. */ export function createLifecycleMiddleware( options: LifecycleMiddlewareOptions, From aeea1adaa5eaf01dfd9cc25636f2090c92f56b6f Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 15:35:36 +0200 Subject: [PATCH 125/317] chore: added some more info in changeset Signed-off-by: blam --- .changeset/fuzzy-feet-exist.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/.changeset/fuzzy-feet-exist.md b/.changeset/fuzzy-feet-exist.md index 2197da2f1f..35ef1e58fd 100644 --- a/.changeset/fuzzy-feet-exist.md +++ b/.changeset/fuzzy-feet-exist.md @@ -3,4 +3,32 @@ '@backstage/plugin-app': patch --- -Deprecated the `namespace` option for `createExtensionBlueprint` and `createExtension`, these are no longer required and will default to the `pluginId` instead +Deprecated the `namespace` option for `createExtensionBlueprint` and `createExtension`, these are no longer required and will default to the `pluginId` instead. + +You can migrate some of your extensions that use `createExtensionOverrides` to using `createFrontendModule` instead and providing a `pluginId` there. + +```ts +// Before +createExtensionOverrides({ + extensions: [ + createExtension({ + name: 'my-extension', + namespace: 'my-namespace', + kind: 'test', + ... + }) + ], +}); + +// After +createFrontendModule({ + pluginId: 'my-namespace', + extensions: [ + createExtension({ + name: 'my-extension', + kind: 'test', + ... + }) + ], +}); +``` From baeef13ec0a7bb3adbb96cc8a21f99e19c433136 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 2 Sep 2024 15:45:42 +0200 Subject: [PATCH 126/317] add changeset Signed-off-by: Johan Haals --- .changeset/silent-worms-admire.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silent-worms-admire.md diff --git a/.changeset/silent-worms-admire.md b/.changeset/silent-worms-admire.md new file mode 100644 index 0000000000..4038382962 --- /dev/null +++ b/.changeset/silent-worms-admire.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': minor +--- + +Deprecated `createLifecycleMiddleware` to clean up API surface, it has no external references, let us know if you use this directly. From e632a09b9d3d340abf95d09906156208b0fec7ba Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 16:18:24 +0200 Subject: [PATCH 127/317] chore: update core-compat-api Signed-off-by: blam --- .../src/collectLegacyRoutes.test.tsx | 14 +++++++------- .../core-compat-api/src/collectLegacyRoutes.tsx | 2 +- .../core-compat-api/src/convertLegacyApp.test.tsx | 6 +++--- packages/core-compat-api/src/convertLegacyApp.ts | 10 +++++----- .../src/convertLegacyPlugin.test.tsx | 2 +- .../core-compat-api/src/convertLegacyPlugin.ts | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx index 4c2acd35a6..0319628b14 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx @@ -73,7 +73,7 @@ describe('collectLegacyRoutes', () => { defaultConfig: {}, }, { - id: 'api:plugin.scoringdata.service', + id: 'api:score-card/plugin.scoringdata.service', attachTo: { id: 'root', input: 'apis' }, disabled: false, }, @@ -89,7 +89,7 @@ describe('collectLegacyRoutes', () => { defaultConfig: {}, }, { - id: 'api:plugin.stackstorm.service', + id: 'api:stackstorm/plugin.stackstorm.service', attachTo: { id: 'root', input: 'apis' }, disabled: false, }, @@ -111,7 +111,7 @@ describe('collectLegacyRoutes', () => { defaultConfig: {}, }, { - id: 'api:plugin.puppetdb.service', + id: 'api:puppetDb/plugin.puppetdb.service', attachTo: { id: 'root', input: 'apis' }, disabled: false, }, @@ -209,7 +209,7 @@ describe('collectLegacyRoutes', () => { disabled: false, }, { - id: 'api:plugin.catalog.service', + id: 'api:catalog/plugin.catalog.service', attachTo: { id: 'root', input: 'apis', @@ -218,7 +218,7 @@ describe('collectLegacyRoutes', () => { disabled: false, }, { - id: 'api:catalog-react.starred-entities', + id: 'api:catalog/catalog-react.starred-entities', attachTo: { id: 'root', input: 'apis', @@ -227,7 +227,7 @@ describe('collectLegacyRoutes', () => { disabled: false, }, { - id: 'api:plugin.catalog.entity-presentation', + id: 'api:catalog/plugin.catalog.entity-presentation', attachTo: { id: 'root', input: 'apis', @@ -241,7 +241,7 @@ describe('collectLegacyRoutes', () => { id: 'score-card', extensions: [ { - id: 'api:plugin.scoringdata.service', + id: 'api:score-card/plugin.scoringdata.service', attachTo: { id: 'root', input: 'apis' }, disabled: false, }, diff --git a/packages/core-compat-api/src/collectLegacyRoutes.tsx b/packages/core-compat-api/src/collectLegacyRoutes.tsx index 197c245278..43e66ce859 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.tsx @@ -269,7 +269,7 @@ export function collectLegacyRoutes( ...extensions, ...Array.from(plugin.getApis()).map(factory => ApiBlueprint.make({ - namespace: factory.api.id, + name: factory.api.id, params: { factory }, }), ), diff --git a/packages/core-compat-api/src/convertLegacyApp.test.tsx b/packages/core-compat-api/src/convertLegacyApp.test.tsx index b76a6c2d71..224ab46221 100644 --- a/packages/core-compat-api/src/convertLegacyApp.test.tsx +++ b/packages/core-compat-api/src/convertLegacyApp.test.tsx @@ -65,7 +65,7 @@ describe('convertLegacyApp', () => { defaultConfig: {}, }, { - id: 'api:plugin.scoringdata.service', + id: 'api:score-card/plugin.scoringdata.service', attachTo: { id: 'root', input: 'apis' }, disabled: false, }, @@ -81,7 +81,7 @@ describe('convertLegacyApp', () => { defaultConfig: {}, }, { - id: 'api:plugin.stackstorm.service', + id: 'api:stackstorm/plugin.stackstorm.service', attachTo: { id: 'root', input: 'apis' }, disabled: false, }, @@ -103,7 +103,7 @@ describe('convertLegacyApp', () => { defaultConfig: {}, }, { - id: 'api:plugin.puppetdb.service', + id: 'api:puppetDb/plugin.puppetdb.service', attachTo: { id: 'root', input: 'apis' }, disabled: false, }, diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index 2729664941..c3e569e85f 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -23,11 +23,12 @@ import React, { } from 'react'; import { ExtensionOverrides, + FrontendModule, FrontendPlugin, coreExtensionData, createExtension, createExtensionInput, - createExtensionOverrides, + createFrontendModule, } from '@backstage/frontend-plugin-api'; import { getComponentData } from '@backstage/core-plugin-api'; import { collectLegacyRoutes } from './collectLegacyRoutes'; @@ -61,7 +62,7 @@ function selectChildren( /** @public */ export function convertLegacyApp( rootElement: React.JSX.Element, -): (FrontendPlugin | ExtensionOverrides)[] { +): (FrontendPlugin | FrontendModule)[] { if (getComponentData(rootElement, 'core.type') === 'FlatRoutes') { return collectLegacyRoutes(rootElement); } @@ -104,7 +105,6 @@ export function convertLegacyApp( const [routesEl] = routesEls; const CoreLayoutOverride = createExtension({ - namespace: 'app', name: 'layout', attachTo: { id: 'app', input: 'root' }, inputs: { @@ -127,7 +127,6 @@ export function convertLegacyApp( }, }); const CoreNavOverride = createExtension({ - namespace: 'app', name: 'nav', attachTo: { id: 'app/layout', input: 'nav' }, output: [], @@ -139,7 +138,8 @@ export function convertLegacyApp( return [ ...collectedRoutes, - createExtensionOverrides({ + createFrontendModule({ + pluginId: 'app', extensions: [CoreLayoutOverride, CoreNavOverride], }), ]; diff --git a/packages/core-compat-api/src/convertLegacyPlugin.test.tsx b/packages/core-compat-api/src/convertLegacyPlugin.test.tsx index 3d5b001c61..01266a35b7 100644 --- a/packages/core-compat-api/src/convertLegacyPlugin.test.tsx +++ b/packages/core-compat-api/src/convertLegacyPlugin.test.tsx @@ -84,7 +84,7 @@ describe('convertLegacyPlugin', () => { }); expect(internalConverted.featureFlags).toEqual([{ name: 'test-flag' }]); expect(internalConverted.extensions.map(e => e.id)).toEqual([ - 'api:plugin.test.client', + 'api:test/plugin.test.client', 'page:test', ]); }); diff --git a/packages/core-compat-api/src/convertLegacyPlugin.ts b/packages/core-compat-api/src/convertLegacyPlugin.ts index bf5759f214..5fd5e585e6 100644 --- a/packages/core-compat-api/src/convertLegacyPlugin.ts +++ b/packages/core-compat-api/src/convertLegacyPlugin.ts @@ -18,7 +18,7 @@ import { BackstagePlugin as LegacyBackstagePlugin } from '@backstage/core-plugin import { ApiBlueprint, ExtensionDefinition, - BackstagePlugin as NewBackstagePlugin, + FrontendPlugin as NewBackstagePlugin, createFrontendPlugin, } from '@backstage/frontend-plugin-api'; import { convertLegacyRouteRefs } from './convertLegacyRouteRef'; @@ -29,7 +29,7 @@ export function convertLegacyPlugin( options: { extensions: ExtensionDefinition[] }, ): NewBackstagePlugin { const apiExtensions = Array.from(legacyPlugin.getApis()).map(factory => - ApiBlueprint.make({ namespace: factory.api.id, params: { factory } }), + ApiBlueprint.make({ name: factory.api.id, params: { factory } }), ); return createFrontendPlugin({ id: legacyPlugin.getId(), From 5f29b079164a986fd1ea8cc26f606f6584a8febf Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 16:37:55 +0200 Subject: [PATCH 128/317] chore: default namespace to test in createExtensionTester Signed-off-by: blam --- packages/frontend-test-utils/src/app/createExtensionTester.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.tsx index f9c1b3cb89..b520ee35bb 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.tsx @@ -110,6 +110,8 @@ export class ExtensionTester { const definition = { ...extension, + // If no namespace is set, we default to "test" + namespace: namespace ?? 'test', // setting name "test" as fallback name: !namespace && !name ? 'test' : name, }; From ff60b8cd7013b5dbbfecc89f2bd9dd70b51945f1 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 16:38:18 +0200 Subject: [PATCH 129/317] chore: work on frontend-app-api Signed-off-by: blam --- .../frontend-app-api/src/extensions/Root.ts | 1 - .../extractRouteInfoFromAppNode.test.ts | 4 ++- .../src/tree/instantiateAppNodeTree.test.ts | 28 +++++++++---------- .../src/wiring/createSpecializedApp.tsx | 4 ++- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/frontend-app-api/src/extensions/Root.ts b/packages/frontend-app-api/src/extensions/Root.ts index 3b1c237e7a..66c4db3140 100644 --- a/packages/frontend-app-api/src/extensions/Root.ts +++ b/packages/frontend-app-api/src/extensions/Root.ts @@ -22,7 +22,6 @@ import { } from '@backstage/frontend-plugin-api'; export const Root = createExtension({ - namespace: 'root', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { app: createExtensionInput([coreExtensionData.reactElement], { diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts index 4e1c37e9a0..7ffe64d6d7 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts @@ -89,7 +89,9 @@ function routeInfoFromExtensions(extensions: ExtensionDefinition[]) { 'root', resolveAppNodeSpecs({ features: [appPlugin, plugin], - builtinExtensions: [resolveExtensionDefinition(Root)], + builtinExtensions: [ + resolveExtensionDefinition(Root, { namespace: 'root' }), + ], parameters: readAppExtensionsConfig(new MockConfigApi({})), forbidden: new Set(['root']), }), diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index 06d412b30e..f24a9e17f3 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -667,7 +667,6 @@ describe('instantiateAppNodeTree', () => { describe('v2', () => { const simpleExtension = resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, output: [testDataRef, otherDataRef.optional()], @@ -684,6 +683,7 @@ describe('instantiateAppNodeTree', () => { ]; }, }), + { namespace: 'app' }, ); function mirrorInputs(ctx: { @@ -750,7 +750,6 @@ describe('instantiateAppNodeTree', () => { makeSpec( resolveExtensionDefinition( createExtension({ - namespace: 'root-node', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { test: createExtensionInput([testDataRef]), @@ -758,6 +757,7 @@ describe('instantiateAppNodeTree', () => { output: [inputMirrorDataRef], factory: mirrorInputs, }), + { namespace: 'root-node' }, ), ), makeSpec(simpleExtension, { @@ -790,7 +790,6 @@ describe('instantiateAppNodeTree', () => { ...makeSpec( resolveExtensionDefinition( createExtension({ - namespace: 'root-node', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { test: createExtensionInput([testDataRef]), @@ -798,6 +797,7 @@ describe('instantiateAppNodeTree', () => { output: [inputMirrorDataRef], factory: mirrorInputs, }), + { namespace: 'root-node' }, ), ), }, @@ -873,7 +873,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { @@ -897,6 +896,7 @@ describe('instantiateAppNodeTree', () => { output: [inputMirrorDataRef], factory: mirrorInputs, }), + { namespace: 'app' }, ), ), }); @@ -946,7 +946,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, output: [testDataRef], @@ -956,6 +955,7 @@ describe('instantiateAppNodeTree', () => { throw error; }, }), + { namespace: 'app' }, ), ), attachments: new Map(), @@ -972,7 +972,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, output: [testDataRef, testDataRef], @@ -980,6 +979,7 @@ describe('instantiateAppNodeTree', () => { return [testDataRef('test'), testDataRef('test2')]; }, }), + { namespace: 'app' }, ), ), attachments: new Map(), @@ -996,7 +996,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, output: [testDataRef], @@ -1004,6 +1003,7 @@ describe('instantiateAppNodeTree', () => { return [] as any; }, }), + { namespace: 'app' }, ), ), attachments: new Map(), @@ -1021,7 +1021,6 @@ describe('instantiateAppNodeTree', () => { resolveExtensionDefinition( // @ts-expect-error createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, output: [], // Output not declared @@ -1029,6 +1028,7 @@ describe('instantiateAppNodeTree', () => { return [testDataRef('test')] as any; }, }), + { namespace: 'app' }, ), ), attachments: new Map(), @@ -1045,7 +1045,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { @@ -1056,6 +1055,7 @@ describe('instantiateAppNodeTree', () => { output: [], factory: () => [], }), + { namespace: 'app' }, ), ), attachments: new Map(), @@ -1090,7 +1090,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'parent', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { @@ -1099,6 +1098,7 @@ describe('instantiateAppNodeTree', () => { output: [], factory: () => [], }), + { namespace: 'app' }, ), ), }), @@ -1129,12 +1129,12 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'parent', attachTo: { id: 'ignored', input: 'ignored' }, output: [], factory: () => [], }), + { namespace: 'app' }, ), ), }), @@ -1162,7 +1162,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { @@ -1173,6 +1172,7 @@ describe('instantiateAppNodeTree', () => { output: [], factory: () => [], }), + { namespace: 'app' }, ), ), }), @@ -1197,7 +1197,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { @@ -1209,6 +1208,7 @@ describe('instantiateAppNodeTree', () => { output: [], factory: () => [], }), + { namespace: 'app' }, ), ), }), @@ -1227,7 +1227,6 @@ describe('instantiateAppNodeTree', () => { node: makeNode( resolveExtensionDefinition( createExtension({ - namespace: 'app', name: 'test', attachTo: { id: 'ignored', input: 'ignored' }, inputs: { @@ -1238,6 +1237,7 @@ describe('instantiateAppNodeTree', () => { output: [], factory: () => [], }), + { namespace: 'app' }, ), ), }), diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx index 27f1d52822..612befc597 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -206,7 +206,9 @@ export function createSpecializedApp(options?: { 'root', resolveAppNodeSpecs({ features, - builtinExtensions: [resolveExtensionDefinition(Root)], + builtinExtensions: [ + resolveExtensionDefinition(Root, { namespace: 'root' }), + ], parameters: readAppExtensionsConfig(config), forbidden: new Set(['root']), }), From ba263b350c1ca9e4dae9a9159da933a81c158300 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 16:38:32 +0200 Subject: [PATCH 130/317] chore: updating frontend-plugin-api Signed-off-by: blam --- .../src/blueprints/ApiBlueprint.test.ts | 12 ++-- .../src/blueprints/RouterBlueprint.test.tsx | 3 - .../extensions/createComponentExtension.tsx | 3 +- .../src/wiring/createExtension.test.ts | 68 +++++++------------ 4 files changed, 30 insertions(+), 56 deletions(-) diff --git a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts index 800eb5d7b1..519b7a4d1a 100644 --- a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts @@ -31,7 +31,7 @@ describe('ApiBlueprint', () => { params: { factory, }, - namespace: 'test', + name: 'test', }); expect(extension).toMatchInlineSnapshot(` @@ -47,8 +47,8 @@ describe('ApiBlueprint', () => { "factory": [Function], "inputs": {}, "kind": "api", - "name": undefined, - "namespace": "test", + "name": "test", + "namespace": undefined, "output": [ [Function], ], @@ -72,7 +72,7 @@ describe('ApiBlueprint', () => { inputs: { test: createExtensionInput([ApiBlueprint.dataRefs.factory]), }, - namespace: api.id, + name: api.id, factory(originalFactory, { config: _config, inputs: _inputs }) { return originalFactory({ factory: createApiFactory({ @@ -122,8 +122,8 @@ describe('ApiBlueprint', () => { }, }, "kind": "api", - "name": undefined, - "namespace": "test", + "name": "test", + "namespace": undefined, "output": [ [Function], ], diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx index 3062d6d9de..99ceb2a7fa 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx @@ -59,7 +59,6 @@ describe('RouterBlueprint', () => { it('should work with simple options', async () => { const extension = RouterBlueprint.make({ - namespace: 'test', params: { Component: ({ children }) => ( @@ -86,7 +85,6 @@ describe('RouterBlueprint', () => { it('should work with complex options and props', async () => { const extension = RouterBlueprint.makeWithOverrides({ - namespace: 'test', name: 'test', config: { schema: { @@ -115,7 +113,6 @@ describe('RouterBlueprint', () => { config: { name: 'Robin' }, }).add( createExtension({ - namespace: 'test', attachTo: { id: 'app-router-component:test/test', input: 'children', diff --git a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx index c8fa907bc8..ca8f1f807b 100644 --- a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx @@ -33,8 +33,7 @@ export function createComponentExtension(options: { }) { return createExtension({ kind: 'component', - namespace: options.ref.id, - name: options.name, + name: options.ref.id, attachTo: { id: 'api:app/components', input: 'components' }, disabled: options.disabled, output: [createComponentExtension.componentDataRef], diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 68478a756e..d22effa513 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -31,7 +31,6 @@ function unused(..._any: any[]) {} describe('createExtension', () => { it('should create an extension with a simple output', () => { const baseConfig = { - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef], }; @@ -41,7 +40,7 @@ describe('createExtension', () => { return [stringDataRef('bar')]; }, }); - expect(extension).toMatchObject({ version: 'v2', namespace: 'test' }); + expect(extension).toMatchObject({ version: 'v2' }); // Member arrow function declaration createExtension({ @@ -161,7 +160,6 @@ describe('createExtension', () => { it('should create an extension with a some optional output', () => { const baseConfig = { - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef.optional()], }; @@ -169,7 +167,7 @@ describe('createExtension', () => { ...baseConfig, factory: () => [stringDataRef('bar')], }); - expect(extension).toMatchObject({ version: 'v2', namespace: 'test' }); + expect(extension).toMatchObject({ version: 'v2' }); createExtension({ ...baseConfig, @@ -206,7 +204,6 @@ describe('createExtension', () => { it('should create an extension with input', () => { const extension = createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, inputs: { mixed: createExtensionInput([stringDataRef, numberDataRef.optional()]), @@ -250,15 +247,14 @@ describe('createExtension', () => { return [stringDataRef('bar')]; }, }); - expect(extension).toMatchObject({ version: 'v2', namespace: 'test' }); + expect(extension).toMatchObject({ version: 'v2' }); expect(String(extension)).toBe( - 'ExtensionDefinition{namespace=test,attachTo=root@default}', + 'ExtensionDefinition{attachTo=root@default}', ); }); it('should create an extension with config', () => { const extension = createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, config: { schema: { @@ -284,9 +280,9 @@ describe('createExtension', () => { return [stringDataRef('bar')]; }, }); - expect(extension).toMatchObject({ version: 'v2', namespace: 'test' }); + expect(extension).toMatchObject({ version: 'v2' }); expect(String(extension)).toBe( - 'ExtensionDefinition{namespace=test,attachTo=root@default}', + 'ExtensionDefinition{attachTo=root@default}', ); expect( @@ -330,7 +326,6 @@ describe('createExtension', () => { expect( // @ts-expect-error createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], factory() { @@ -342,7 +337,6 @@ describe('createExtension', () => { expect( // @ts-expect-error createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], factory() { @@ -354,7 +348,6 @@ describe('createExtension', () => { // Duplicate output, we won't attempt to handle this a compile time and instead error out at runtime expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef], factory() { @@ -366,7 +359,6 @@ describe('createExtension', () => { expect( // @ts-expect-error createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef], factory() { @@ -377,7 +369,6 @@ describe('createExtension', () => { expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], factory() { @@ -388,7 +379,6 @@ describe('createExtension', () => { expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef.optional()], factory() { @@ -399,7 +389,6 @@ describe('createExtension', () => { expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef.optional()], factory() { @@ -413,7 +402,6 @@ describe('createExtension', () => { expect( // @ts-expect-error createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], *factory() { @@ -425,7 +413,6 @@ describe('createExtension', () => { expect( // @ts-expect-error createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], *factory() { @@ -437,7 +424,6 @@ describe('createExtension', () => { // Duplicate output, we won't attempt to handle this a compile time and instead error out at runtime expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef], *factory() { @@ -450,7 +436,6 @@ describe('createExtension', () => { expect( // @ts-expect-error createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef], *factory() { @@ -462,7 +447,6 @@ describe('createExtension', () => { expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], *factory() { @@ -474,7 +458,6 @@ describe('createExtension', () => { expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef.optional()], *factory() { @@ -486,7 +469,6 @@ describe('createExtension', () => { expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef.optional()], *factory() { @@ -499,7 +481,6 @@ describe('createExtension', () => { it('should support new form of inputs', () => { expect( createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'default' }, inputs: { header: createExtensionInput([stringDataRef.optional()], { @@ -534,7 +515,6 @@ describe('createExtension', () => { describe('overrides', () => { it('should allow overriding of config and merging', () => { const testExtension = createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'blob' }, output: [stringDataRef], config: { @@ -563,7 +543,6 @@ describe('createExtension', () => { it('should allow overriding of outputs', () => { const testExtension = createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'blob' }, output: [stringDataRef], inputs: { @@ -603,7 +582,6 @@ describe('createExtension', () => { it('should allow overriding the factory function and calling the original factory', () => { const testExtension = createExtension({ - namespace: 'test', attachTo: { id: 'root', input: 'blob' }, output: [stringDataRef], config: { @@ -635,7 +613,7 @@ describe('createExtension', () => { it('should allow overriding the returned values from the parent factory', () => { const testExtension = createExtension({ kind: 'thing', - namespace: 'test', + attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], config: { @@ -666,7 +644,7 @@ describe('createExtension', () => { it('should work functionally with overrides', () => { const testExtension = createExtension({ kind: 'thing', - namespace: 'test', + attachTo: { id: 'root', input: 'default' }, output: [stringDataRef], config: { @@ -749,28 +727,28 @@ describe('createExtension', () => { const optExt = createExtension({ name: 'o', - attachTo: { id: 'subject', input: 'opt' }, + attachTo: { id: 'test/subject', input: 'opt' }, output: [testDataRef1], factory: () => [testDataRef1('orig-opt')], }); const singleExt = createExtension({ name: 's', - attachTo: { id: 'subject', input: 'single' }, + attachTo: { id: 'test/subject', input: 'single' }, output: [testDataRef1, testDataRef2.optional()], factory: () => [testDataRef1('orig-single')], }); const multi1Ext = createExtension({ name: 'm1', - attachTo: { id: 'subject', input: 'multi' }, + attachTo: { id: 'test/subject', input: 'multi' }, output: [testDataRef1], factory: () => [testDataRef1('orig-multi1')], }); const multi2Ext = createExtension({ name: 'm2', - attachTo: { id: 'subject', input: 'multi' }, + attachTo: { id: 'test/subject', input: 'multi' }, output: [testDataRef1], factory: () => [testDataRef1('orig-multi2')], }); @@ -786,7 +764,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'm1=orig-multi1,m2=orig-multi2', + multi: 'test/m1=orig-multi1,test/m2=orig-multi2', }); // All values provided @@ -813,7 +791,7 @@ describe('createExtension', () => { opt: 'opt', single: 'single', singleOpt: 'singleOpt', - multi: 'm1=multi1,m2=multi2', + multi: 'test/m1=multi1,test/m2=multi2', }); // Minimal values provided @@ -862,7 +840,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'm1=orig-multi1,m2=orig-multi2', + multi: 'test/m1=orig-multi1,test/m2=orig-multi2', }); // Forward inputs separately @@ -889,7 +867,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'm1=orig-multi1,m2=orig-multi2', + multi: 'test/m1=orig-multi1,test/m2=orig-multi2', }); // Reordering inputs @@ -916,7 +894,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'm2=orig-multi2,m1=orig-multi1', + multi: 'test/m2=orig-multi2,test/m1=orig-multi1', }); // Filter out inputs @@ -943,7 +921,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'm2=orig-multi2', + multi: 'test/m2=orig-multi2', }); // Overriding based on original input @@ -974,7 +952,7 @@ describe('createExtension', () => { opt: 'none', single: 'override-orig-single', singleOpt: 'new-singleOpt', - multi: 'm1=override-orig-multi1,m2=override-orig-multi2', + multi: 'test/m1=override-orig-multi1,test/m2=override-orig-multi2', }); // Mismatched input override length @@ -997,7 +975,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'subject', override data provided for input 'multi' must match the length of the original inputs"`, + `"Failed to instantiate extension 'test/subject', override data provided for input 'multi' must match the length of the original inputs"`, ); // Mix forward and data override @@ -1020,7 +998,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'subject', override data for input 'multi' may not mix forwarded inputs with data overrides"`, + `"Failed to instantiate extension 'test/subject', override data for input 'multi' may not mix forwarded inputs with data overrides"`, ); // Required input not provided @@ -1043,7 +1021,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'subject', missing required extension data value(s) 'test1'"`, + `"Failed to instantiate extension 'test/subject', missing required extension data value(s) 'test1'"`, ); // Wrong value provided @@ -1072,7 +1050,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'subject', extension data 'test2' was provided but not declared"`, + `"Failed to instantiate extension 'test/subject', extension data 'test2' was provided but not declared"`, ); }); }); From 84d89bb5f3f4c28984a466fd10a274a26f6a6fc8 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 16:57:30 +0200 Subject: [PATCH 131/317] chore: fix the router test a different way, don't set namespace as test by default Signed-off-by: blam --- .../core-compat-api/src/convertLegacyApp.ts | 1 - .../src/blueprints/RouterBlueprint.test.tsx | 2 +- .../src/wiring/createExtension.test.ts | 30 +++++++++---------- .../src/app/createExtensionTester.tsx | 2 -- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index c3e569e85f..610c9e8842 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -22,7 +22,6 @@ import React, { isValidElement, } from 'react'; import { - ExtensionOverrides, FrontendModule, FrontendPlugin, coreExtensionData, diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx index 99ceb2a7fa..ab6bb1eb1f 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx @@ -114,7 +114,7 @@ describe('RouterBlueprint', () => { }).add( createExtension({ attachTo: { - id: 'app-router-component:test/test', + id: 'app-router-component:test', input: 'children', }, output: [coreExtensionData.reactElement], diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index d22effa513..3c68c910f7 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -727,28 +727,28 @@ describe('createExtension', () => { const optExt = createExtension({ name: 'o', - attachTo: { id: 'test/subject', input: 'opt' }, + attachTo: { id: 'subject', input: 'opt' }, output: [testDataRef1], factory: () => [testDataRef1('orig-opt')], }); const singleExt = createExtension({ name: 's', - attachTo: { id: 'test/subject', input: 'single' }, + attachTo: { id: 'subject', input: 'single' }, output: [testDataRef1, testDataRef2.optional()], factory: () => [testDataRef1('orig-single')], }); const multi1Ext = createExtension({ name: 'm1', - attachTo: { id: 'test/subject', input: 'multi' }, + attachTo: { id: 'subject', input: 'multi' }, output: [testDataRef1], factory: () => [testDataRef1('orig-multi1')], }); const multi2Ext = createExtension({ name: 'm2', - attachTo: { id: 'test/subject', input: 'multi' }, + attachTo: { id: 'subject', input: 'multi' }, output: [testDataRef1], factory: () => [testDataRef1('orig-multi2')], }); @@ -764,7 +764,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'test/m1=orig-multi1,test/m2=orig-multi2', + multi: 'm1=orig-multi1,m2=orig-multi2', }); // All values provided @@ -791,7 +791,7 @@ describe('createExtension', () => { opt: 'opt', single: 'single', singleOpt: 'singleOpt', - multi: 'test/m1=multi1,test/m2=multi2', + multi: 'm1=multi1,m2=multi2', }); // Minimal values provided @@ -840,7 +840,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'test/m1=orig-multi1,test/m2=orig-multi2', + multi: 'm1=orig-multi1,m2=orig-multi2', }); // Forward inputs separately @@ -867,7 +867,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'test/m1=orig-multi1,test/m2=orig-multi2', + multi: 'm1=orig-multi1,m2=orig-multi2', }); // Reordering inputs @@ -894,7 +894,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'test/m2=orig-multi2,test/m1=orig-multi1', + multi: 'm2=orig-multi2,m1=orig-multi1', }); // Filter out inputs @@ -921,7 +921,7 @@ describe('createExtension', () => { opt: 'orig-opt', single: 'orig-single', singleOpt: 'none', - multi: 'test/m2=orig-multi2', + multi: 'm2=orig-multi2', }); // Overriding based on original input @@ -952,7 +952,7 @@ describe('createExtension', () => { opt: 'none', single: 'override-orig-single', singleOpt: 'new-singleOpt', - multi: 'test/m1=override-orig-multi1,test/m2=override-orig-multi2', + multi: 'm1=override-orig-multi1,m2=override-orig-multi2', }); // Mismatched input override length @@ -975,7 +975,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'test/subject', override data provided for input 'multi' must match the length of the original inputs"`, + `"Failed to instantiate extension 'subject', override data provided for input 'multi' must match the length of the original inputs"`, ); // Mix forward and data override @@ -998,7 +998,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'test/subject', override data for input 'multi' may not mix forwarded inputs with data overrides"`, + `"Failed to instantiate extension 'subject', override data for input 'multi' may not mix forwarded inputs with data overrides"`, ); // Required input not provided @@ -1021,7 +1021,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'test/subject', missing required extension data value(s) 'test1'"`, + `"Failed to instantiate extension 'subject', missing required extension data value(s) 'test1'"`, ); // Wrong value provided @@ -1050,7 +1050,7 @@ describe('createExtension', () => { .add(multi2Ext) .get(outputRef), ).toThrowErrorMatchingInlineSnapshot( - `"Failed to instantiate extension 'test/subject', extension data 'test2' was provided but not declared"`, + `"Failed to instantiate extension 'subject', extension data 'test2' was provided but not declared"`, ); }); }); diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.tsx index b520ee35bb..f9c1b3cb89 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.tsx @@ -110,8 +110,6 @@ export class ExtensionTester { const definition = { ...extension, - // If no namespace is set, we default to "test" - namespace: namespace ?? 'test', // setting name "test" as fallback name: !namespace && !name ? 'test' : name, }; From d7f25e1d43e7dae5832f1da2ed609bb850388983 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 2 Sep 2024 17:00:08 +0200 Subject: [PATCH 132/317] chore: fixing formatting Signed-off-by: blam --- packages/frontend-plugin-api/src/wiring/createExtension.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 3c68c910f7..5e8aa1c57a 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -613,7 +613,6 @@ describe('createExtension', () => { it('should allow overriding the returned values from the parent factory', () => { const testExtension = createExtension({ kind: 'thing', - attachTo: { id: 'root', input: 'default' }, output: [stringDataRef, numberDataRef], config: { @@ -644,7 +643,6 @@ describe('createExtension', () => { it('should work functionally with overrides', () => { const testExtension = createExtension({ kind: 'thing', - attachTo: { id: 'root', input: 'default' }, output: [stringDataRef], config: { From 5edd344c9820be5ff0c32bcd82c3bdd8d79d20fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 2 Sep 2024 17:13:58 +0200 Subject: [PATCH 133/317] inject catalog client where possible in the new backend system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/slimy-chefs-think.md | 8 ++++++++ .../catalog-backend-module-github/api-report.md | 2 ++ .../src/analyzers/GithubLocationAnalyzer.ts | 4 +++- .../src/module/githubCatalogModule.ts | 16 ++++++++++------ .../package.json | 1 + .../src/module.ts | 12 ++++-------- .../src/processor/NotificationsEmailProcessor.ts | 7 ++----- plugins/notifications-backend/package.json | 1 + plugins/notifications-backend/src/plugin.ts | 7 ++++--- .../src/service/router.test.ts | 8 ++++++-- .../notifications-backend/src/service/router.ts | 12 ++++-------- plugins/techdocs-backend/api-report.md | 5 ++--- plugins/techdocs-backend/package.json | 1 + plugins/techdocs-backend/src/plugin.ts | 5 ++++- .../src/service/CachedEntityLoader.test.ts | 4 ++-- .../src/service/CachedEntityLoader.ts | 7 ++++--- plugins/techdocs-backend/src/service/router.ts | 10 +++++----- yarn.lock | 3 +++ 18 files changed, 66 insertions(+), 47 deletions(-) create mode 100644 .changeset/slimy-chefs-think.md diff --git a/.changeset/slimy-chefs-think.md b/.changeset/slimy-chefs-think.md new file mode 100644 index 0000000000..7fa546a399 --- /dev/null +++ b/.changeset/slimy-chefs-think.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-notifications-backend': patch +'@backstage/plugin-techdocs-backend': patch +--- + +Refactor to use injected catalog client in the new backend system diff --git a/plugins/catalog-backend-module-github/api-report.md b/plugins/catalog-backend-module-github/api-report.md index dad81f8681..025d845644 100644 --- a/plugins/catalog-backend-module-github/api-report.md +++ b/plugins/catalog-backend-module-github/api-report.md @@ -5,6 +5,7 @@ ```ts import { AnalyzeOptions } from '@backstage/plugin-catalog-node'; import { AuthService } from '@backstage/backend-plugin-api'; +import { CatalogApi } from '@backstage/catalog-client'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node'; import { Config } from '@backstage/config'; @@ -129,6 +130,7 @@ export type GithubLocationAnalyzerOptions = { tokenManager?: TokenManager; auth?: AuthService; githubCredentialsProvider?: GithubCredentialsProvider; + catalog?: CatalogApi; }; // @public diff --git a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts index 7e6ac73054..b635878f92 100644 --- a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts +++ b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts @@ -44,6 +44,7 @@ export type GithubLocationAnalyzerOptions = { tokenManager?: TokenManager; auth?: AuthService; githubCredentialsProvider?: GithubCredentialsProvider; + catalog?: CatalogApi; }; /** @public */ @@ -54,7 +55,8 @@ export class GithubLocationAnalyzer implements ScmLocationAnalyzer { private readonly auth: AuthService; constructor(options: GithubLocationAnalyzerOptions) { - this.catalogClient = new CatalogClient({ discoveryApi: options.discovery }); + this.catalogClient = + options.catalog ?? new CatalogClient({ discoveryApi: options.discovery }); this.integrations = ScmIntegrations.fromConfig(options.config); this.githubCredentialsProvider = options.githubCredentialsProvider || diff --git a/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts b/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts index 809a1a5094..15d36a3011 100644 --- a/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts +++ b/plugins/catalog-backend-module-github/src/module/githubCatalogModule.ts @@ -21,6 +21,7 @@ import { import { catalogAnalysisExtensionPoint, catalogProcessingExtensionPoint, + catalogServiceRef, } from '@backstage/plugin-catalog-node/alpha'; import { eventsServiceRef } from '@backstage/plugin-events-node'; import { GithubEntityProvider } from '../providers/GithubEntityProvider'; @@ -37,34 +38,37 @@ export const githubCatalogModule = createBackendModule({ register(env) { env.registerInit({ deps: { - analyzers: catalogAnalysisExtensionPoint, + catalogAnalyzers: catalogAnalysisExtensionPoint, auth: coreServices.auth, - catalog: catalogProcessingExtensionPoint, + catalogProcessing: catalogProcessingExtensionPoint, config: coreServices.rootConfig, discovery: coreServices.discovery, events: eventsServiceRef, logger: coreServices.logger, scheduler: coreServices.scheduler, + catalog: catalogServiceRef, }, async init({ - catalog, + catalogProcessing, config, events, logger, scheduler, - analyzers, + catalogAnalyzers, discovery, auth, + catalog, }) { - analyzers.addScmLocationAnalyzer( + catalogAnalyzers.addScmLocationAnalyzer( new GithubLocationAnalyzer({ discovery, config, auth, + catalog, }), ); - catalog.addEntityProvider( + catalogProcessing.addEntityProvider( GithubEntityProvider.fromConfig(config, { events, logger, diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index 20e076cdd2..228946c016 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -42,6 +42,7 @@ "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/integration-aws-node": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-notifications-common": "workspace:^", "@backstage/plugin-notifications-node": "workspace:^", "@backstage/types": "workspace:^", diff --git a/plugins/notifications-backend-module-email/src/module.ts b/plugins/notifications-backend-module-email/src/module.ts index 6b74bf70e7..c83d27b6a3 100644 --- a/plugins/notifications-backend-module-email/src/module.ts +++ b/plugins/notifications-backend-module-email/src/module.ts @@ -17,7 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { CatalogClient } from '@backstage/catalog-client'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; import { notificationsProcessingExtensionPoint } from '@backstage/plugin-notifications-node'; import { NotificationsEmailProcessor } from './processor'; import { @@ -46,21 +46,17 @@ export const notificationsModuleEmail = createBackendModule({ deps: { config: coreServices.rootConfig, notifications: notificationsProcessingExtensionPoint, - discovery: coreServices.discovery, logger: coreServices.logger, auth: coreServices.auth, cache: coreServices.cache, + catalog: catalogServiceRef, }, - async init({ config, notifications, discovery, logger, auth, cache }) { - const catalogClient = new CatalogClient({ - discoveryApi: discovery, - }); - + async init({ config, notifications, logger, auth, cache, catalog }) { notifications.addProcessor( new NotificationsEmailProcessor( logger, config, - catalogClient, + catalog, auth, cache, templateRenderer, diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts index a876a548d2..9337915ecc 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts @@ -24,10 +24,7 @@ import { } from '@backstage/backend-plugin-api'; import { Config, readDurationFromConfig } from '@backstage/config'; import { durationToMilliseconds } from '@backstage/types'; -import { - CATALOG_FILTER_EXISTS, - CatalogClient, -} from '@backstage/catalog-client'; +import { CATALOG_FILTER_EXISTS, CatalogApi } from '@backstage/catalog-client'; import { getProcessorFiltersFromConfig, Notification, @@ -63,7 +60,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { constructor( private readonly logger: LoggerService, private readonly config: Config, - private readonly catalog: CatalogClient, + private readonly catalog: CatalogApi, private readonly auth: AuthService, private readonly cache?: CacheService, private readonly templateRenderer?: NotificationTemplateRenderer, diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index a0273d628b..fecd053bec 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -45,6 +45,7 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-events-node": "workspace:^", "@backstage/plugin-notifications-common": "workspace:^", "@backstage/plugin-notifications-node": "workspace:^", diff --git a/plugins/notifications-backend/src/plugin.ts b/plugins/notifications-backend/src/plugin.ts index 288a0f8507..84a200bae1 100644 --- a/plugins/notifications-backend/src/plugin.ts +++ b/plugins/notifications-backend/src/plugin.ts @@ -25,6 +25,7 @@ import { notificationsProcessingExtensionPoint, NotificationsProcessingExtensionPoint, } from '@backstage/plugin-notifications-node'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; class NotificationsProcessingExtensionPointImpl implements NotificationsProcessingExtensionPoint @@ -65,9 +66,9 @@ export const notificationsPlugin = createBackendPlugin({ httpRouter: coreServices.httpRouter, logger: coreServices.logger, database: coreServices.database, - discovery: coreServices.discovery, signals: signalsServiceRef, config: coreServices.rootConfig, + catalog: catalogServiceRef, }, async init({ auth, @@ -76,9 +77,9 @@ export const notificationsPlugin = createBackendPlugin({ httpRouter, logger, database, - discovery, signals, config, + catalog, }) { httpRouter.use( await createRouter({ @@ -88,7 +89,7 @@ export const notificationsPlugin = createBackendPlugin({ logger, config, database, - discovery, + catalog, signals, processors: processingExtensions.processors, }), diff --git a/plugins/notifications-backend/src/service/router.test.ts b/plugins/notifications-backend/src/service/router.test.ts index a3a66636ca..b90ebcaf50 100644 --- a/plugins/notifications-backend/src/service/router.test.ts +++ b/plugins/notifications-backend/src/service/router.test.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { DatabaseManager, PluginDatabaseManager, @@ -25,6 +26,7 @@ import { ConfigReader } from '@backstage/config'; import { SignalsService } from '@backstage/plugin-signals-node'; import { mockCredentials, mockServices } from '@backstage/backend-test-utils'; import { NotificationSendOptions } from '@backstage/plugin-notifications-node'; +import { CatalogClient } from '@backstage/catalog-client'; function createDatabase(): PluginDatabaseManager { return DatabaseManager.fromConfig( @@ -46,7 +48,6 @@ describe('createRouter', () => { publish: jest.fn(), }; - const discovery = mockServices.discovery(); const userInfo = mockServices.userInfo(); const httpAuth = mockServices.httpAuth({ defaultCredentials: mockCredentials.service(), @@ -55,17 +56,20 @@ describe('createRouter', () => { const config = mockServices.rootConfig({ data: { app: { baseUrl: 'http://localhost' } }, }); + const catalog = new CatalogClient({ + discoveryApi: mockServices.discovery.mock(), + }); beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), database: createDatabase(), - discovery, signals: signalService, userInfo, config, httpAuth, auth, + catalog, }); app = express().use(router); }); diff --git a/plugins/notifications-backend/src/service/router.ts b/plugins/notifications-backend/src/service/router.ts index 95b9467f4a..fb5aacb5f4 100644 --- a/plugins/notifications-backend/src/service/router.ts +++ b/plugins/notifications-backend/src/service/router.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { errorHandler, PluginDatabaseManager } from '@backstage/backend-common'; import express, { Request } from 'express'; import Router from 'express-promise-router'; @@ -22,7 +23,7 @@ import { NotificationGetOptions, } from '../database'; import { v4 as uuid } from 'uuid'; -import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; +import { CatalogApi } from '@backstage/catalog-client'; import { NotificationProcessor, NotificationSendOptions, @@ -30,7 +31,6 @@ import { import { InputError } from '@backstage/errors'; import { AuthService, - DiscoveryService, HttpAuthService, LoggerService, UserInfoService, @@ -53,12 +53,11 @@ export interface RouterOptions { logger: LoggerService; config: Config; database: PluginDatabaseManager; - discovery: DiscoveryService; auth: AuthService; httpAuth: HttpAuthService; userInfo: UserInfoService; signals?: SignalsService; - catalog?: CatalogApi; + catalog: CatalogApi; processors?: NotificationProcessor[]; } @@ -73,14 +72,11 @@ export async function createRouter( auth, httpAuth, userInfo, - discovery, catalog, processors = [], signals, } = options; - const catalogClient = - catalog ?? new CatalogClient({ discoveryApi: discovery }); const store = await DatabaseNotificationsStore.create({ database }); const frontendBaseUrl = config.getString('app.baseUrl'); @@ -474,7 +470,7 @@ export async function createRouter( users = await getUsersForEntityRef( entityRef, recipients.excludeEntityRef ?? [], - { auth, catalogClient }, + { auth, catalogClient: catalog }, ); } catch (e) { logger.error(`Failed to resolve notification receivers: ${e}`); diff --git a/plugins/techdocs-backend/api-report.md b/plugins/techdocs-backend/api-report.md index a73647c40a..dceb0a4d9a 100644 --- a/plugins/techdocs-backend/api-report.md +++ b/plugins/techdocs-backend/api-report.md @@ -5,7 +5,6 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; -import { CatalogClient } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { DefaultTechDocsCollatorFactory as DefaultTechDocsCollatorFactory_2 } from '@backstage/plugin-search-backend-module-techdocs'; import { DiscoveryService } from '@backstage/backend-plugin-api'; @@ -66,7 +65,7 @@ export type OutOfTheBoxDeploymentOptions = { cache: PluginCacheManager; docsBuildStrategy?: DocsBuildStrategy_2; buildLogTransport?: winston.transport; - catalogClient?: CatalogClient; + catalogClient?: CatalogApi; httpAuth?: HttpAuthService; auth?: AuthService; }; @@ -80,7 +79,7 @@ export type RecommendedDeploymentOptions = { cache: PluginCacheManager; docsBuildStrategy?: DocsBuildStrategy_2; buildLogTransport?: winston.transport; - catalogClient?: CatalogClient; + catalogClient?: CatalogApi; httpAuth?: HttpAuthService; auth?: AuthService; }; diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index f9a790b1db..0c8a22c7ef 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -66,6 +66,7 @@ "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-search-backend-module-techdocs": "workspace:^", "@backstage/plugin-techdocs-common": "workspace:^", diff --git a/plugins/techdocs-backend/src/plugin.ts b/plugins/techdocs-backend/src/plugin.ts index fd0574fedc..0f3def79b1 100644 --- a/plugins/techdocs-backend/src/plugin.ts +++ b/plugins/techdocs-backend/src/plugin.ts @@ -22,7 +22,6 @@ import { coreServices, createBackendPlugin, } from '@backstage/backend-plugin-api'; - import { DocsBuildStrategy, Generators, @@ -39,6 +38,7 @@ import { techdocsPublisherExtensionPoint, } from '@backstage/plugin-techdocs-node'; import { createRouter } from '@backstage/plugin-techdocs-backend'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; import * as winston from 'winston'; /** @@ -108,6 +108,7 @@ export const techdocsPlugin = createBackendPlugin({ cache: coreServices.cache, httpAuth: coreServices.httpAuth, auth: coreServices.auth, + catalog: catalogServiceRef, }, async init({ config, @@ -118,6 +119,7 @@ export const techdocsPlugin = createBackendPlugin({ cache, httpAuth, auth, + catalog, }) { const winstonLogger = loggerToWinstonLogger(logger); // Preparers are responsible for fetching source files for documentation. @@ -161,6 +163,7 @@ export const techdocsPlugin = createBackendPlugin({ discovery, httpAuth, auth, + catalogClient: catalog, }), ); diff --git a/plugins/techdocs-backend/src/service/CachedEntityLoader.test.ts b/plugins/techdocs-backend/src/service/CachedEntityLoader.test.ts index 492690611c..c4673c6fa4 100644 --- a/plugins/techdocs-backend/src/service/CachedEntityLoader.test.ts +++ b/plugins/techdocs-backend/src/service/CachedEntityLoader.test.ts @@ -15,11 +15,11 @@ */ import { CacheService } from '@backstage/backend-plugin-api'; import { CachedEntityLoader } from './CachedEntityLoader'; -import { CatalogClient } from '@backstage/catalog-client'; +import { CatalogApi } from '@backstage/catalog-client'; import { CompoundEntityRef } from '@backstage/catalog-model'; describe('CachedEntityLoader', () => { - const catalog: jest.Mocked = { + const catalog: jest.Mocked = { getEntityByRef: jest.fn(), } as any; diff --git a/plugins/techdocs-backend/src/service/CachedEntityLoader.ts b/plugins/techdocs-backend/src/service/CachedEntityLoader.ts index d7d1fbbe2b..7541509907 100644 --- a/plugins/techdocs-backend/src/service/CachedEntityLoader.ts +++ b/plugins/techdocs-backend/src/service/CachedEntityLoader.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { CacheService } from '@backstage/backend-plugin-api'; -import { CatalogClient } from '@backstage/catalog-client'; +import { CatalogApi } from '@backstage/catalog-client'; import { Entity, CompoundEntityRef, @@ -22,12 +23,12 @@ import { } from '@backstage/catalog-model'; export type CachedEntityLoaderOptions = { - catalog: CatalogClient; + catalog: CatalogApi; cache: CacheService; }; export class CachedEntityLoader { - private readonly catalog: CatalogClient; + private readonly catalog: CatalogApi; private readonly cache: CacheService; private readonly readTimeout = 1000; diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index 0123ee89f2..b99579e0eb 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { createLegacyAuthAdapters, PluginCacheManager, } from '@backstage/backend-common'; -import { CatalogClient } from '@backstage/catalog-client'; +import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { NotFoundError } from '@backstage/errors'; @@ -48,7 +49,6 @@ import { * deployment configuration (prepare/generate/publish all in the Backend). * * @public - * */ export type OutOfTheBoxDeploymentOptions = { preparers: PreparerBuilder; @@ -61,7 +61,7 @@ export type OutOfTheBoxDeploymentOptions = { cache: PluginCacheManager; docsBuildStrategy?: DocsBuildStrategy; buildLogTransport?: winston.transport; - catalogClient?: CatalogClient; + catalogClient?: CatalogApi; httpAuth?: HttpAuthService; auth?: AuthService; }; @@ -81,7 +81,7 @@ export type RecommendedDeploymentOptions = { cache: PluginCacheManager; docsBuildStrategy?: DocsBuildStrategy; buildLogTransport?: winston.transport; - catalogClient?: CatalogClient; + catalogClient?: CatalogApi; httpAuth?: HttpAuthService; auth?: AuthService; }; @@ -112,7 +112,7 @@ function isOutOfTheBoxOption( * Creates a techdocs router. * * @public - *@deprecated This function is only exported for legacy reasons and will be removed in the future. + * @deprecated This function is only exported for legacy reasons and will be removed in the future. * Please {@link https://backstage.io/docs/backend-system/building-backends/migrating | migrate } to use the new backend system and follow these {@link https://backstage.io/docs/features/techdocs/getting-started#new-backend-system | instructions } to install the user settings backend plugin. */ export async function createRouter( diff --git a/yarn.lock b/yarn.lock index bd8e1c8d1c..b6a31aae8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6438,6 +6438,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/integration-aws-node": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-notifications-common": "workspace:^" "@backstage/plugin-notifications-node": "workspace:^" "@backstage/types": "workspace:^" @@ -6464,6 +6465,7 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-backend": "workspace:^" "@backstage/plugin-events-node": "workspace:^" "@backstage/plugin-notifications-common": "workspace:^" @@ -7683,6 +7685,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-search-backend-module-techdocs": "workspace:^" "@backstage/plugin-techdocs-common": "workspace:^" From e010b71e9088b436e9a909cba7392130b8b76f44 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 02:17:37 +0000 Subject: [PATCH 134/317] fix(deps): update dependency @swc/core to v1.7.23 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 96 ++++++++++++++++++++++----------------------- storybook/yarn.lock | 96 ++++++++++++++++++++++----------------------- yarn.lock | 96 ++++++++++++++++++++++----------------------- 3 files changed, 144 insertions(+), 144 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index d5d78e9371..2343441aff 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2715,92 +2715,92 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-darwin-arm64@npm:1.5.29" +"@swc/core-darwin-arm64@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-darwin-arm64@npm:1.7.23" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-darwin-x64@npm:1.5.29" +"@swc/core-darwin-x64@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-darwin-x64@npm:1.7.23" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.29" +"@swc/core-linux-arm-gnueabihf@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.23" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm64-gnu@npm:1.5.29" +"@swc/core-linux-arm64-gnu@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm64-gnu@npm:1.7.23" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm64-musl@npm:1.5.29" +"@swc/core-linux-arm64-musl@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm64-musl@npm:1.7.23" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-x64-gnu@npm:1.5.29" +"@swc/core-linux-x64-gnu@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-x64-gnu@npm:1.7.23" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-x64-musl@npm:1.5.29" +"@swc/core-linux-x64-musl@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-x64-musl@npm:1.7.23" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-arm64-msvc@npm:1.5.29" +"@swc/core-win32-arm64-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-arm64-msvc@npm:1.7.23" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-ia32-msvc@npm:1.5.29" +"@swc/core-win32-ia32-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-ia32-msvc@npm:1.7.23" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-x64-msvc@npm:1.5.29" +"@swc/core-win32-x64-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-x64-msvc@npm:1.7.23" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.5.29 - resolution: "@swc/core@npm:1.5.29" + version: 1.7.23 + resolution: "@swc/core@npm:1.7.23" dependencies: - "@swc/core-darwin-arm64": 1.5.29 - "@swc/core-darwin-x64": 1.5.29 - "@swc/core-linux-arm-gnueabihf": 1.5.29 - "@swc/core-linux-arm64-gnu": 1.5.29 - "@swc/core-linux-arm64-musl": 1.5.29 - "@swc/core-linux-x64-gnu": 1.5.29 - "@swc/core-linux-x64-musl": 1.5.29 - "@swc/core-win32-arm64-msvc": 1.5.29 - "@swc/core-win32-ia32-msvc": 1.5.29 - "@swc/core-win32-x64-msvc": 1.5.29 + "@swc/core-darwin-arm64": 1.7.23 + "@swc/core-darwin-x64": 1.7.23 + "@swc/core-linux-arm-gnueabihf": 1.7.23 + "@swc/core-linux-arm64-gnu": 1.7.23 + "@swc/core-linux-arm64-musl": 1.7.23 + "@swc/core-linux-x64-gnu": 1.7.23 + "@swc/core-linux-x64-musl": 1.7.23 + "@swc/core-win32-arm64-msvc": 1.7.23 + "@swc/core-win32-ia32-msvc": 1.7.23 + "@swc/core-win32-x64-msvc": 1.7.23 "@swc/counter": ^0.1.3 - "@swc/types": ^0.1.8 + "@swc/types": ^0.1.12 peerDependencies: "@swc/helpers": "*" dependenciesMeta: @@ -2827,7 +2827,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 19e3f8525ce4f4d9f6f3f62825b1f13e1e76146390930b6d5e663c0bb2c5e95ef3bf30268110d1444ea92b99dd95bfe5a3c6e2a3c0dd79291505dc7da1ed3dab + checksum: a21e9a67d305ad9b49b6def9f9698b374db6c15b2ded5e5cf29390221181fc6b91bfa317b53fd41d9cca989d85b79bbbca5ab27635df1e0232cad4681f618fa5 languageName: node linkType: hard @@ -2838,12 +2838,12 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.8": - version: 0.1.8 - resolution: "@swc/types@npm:0.1.8" +"@swc/types@npm:^0.1.12": + version: 0.1.12 + resolution: "@swc/types@npm:0.1.12" dependencies: "@swc/counter": ^0.1.3 - checksum: e564d0e37b0e28546973c6d50c7a179395912a97168d695cfe9cf1051199c8b828680cdafcb8d43948f76d3703873bafb88dfb5bc2dfe0596b4ad18fcaf90c80 + checksum: cf7f89e46f859864075d7965582baea9c5f98830f45b1046251568c9bdf1ca484b1bf37f6d3c32b7c82ecf8cd5df89d22f05268c391819c44e49911bb1a8e71a languageName: node linkType: hard diff --git a/storybook/yarn.lock b/storybook/yarn.lock index c5e39ebea7..0bb6696bae 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -2849,92 +2849,92 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-darwin-arm64@npm:1.5.29" +"@swc/core-darwin-arm64@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-darwin-arm64@npm:1.7.23" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-darwin-x64@npm:1.5.29" +"@swc/core-darwin-x64@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-darwin-x64@npm:1.7.23" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.29" +"@swc/core-linux-arm-gnueabihf@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.23" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm64-gnu@npm:1.5.29" +"@swc/core-linux-arm64-gnu@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm64-gnu@npm:1.7.23" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm64-musl@npm:1.5.29" +"@swc/core-linux-arm64-musl@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm64-musl@npm:1.7.23" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-x64-gnu@npm:1.5.29" +"@swc/core-linux-x64-gnu@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-x64-gnu@npm:1.7.23" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-x64-musl@npm:1.5.29" +"@swc/core-linux-x64-musl@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-x64-musl@npm:1.7.23" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-arm64-msvc@npm:1.5.29" +"@swc/core-win32-arm64-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-arm64-msvc@npm:1.7.23" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-ia32-msvc@npm:1.5.29" +"@swc/core-win32-ia32-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-ia32-msvc@npm:1.7.23" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-x64-msvc@npm:1.5.29" +"@swc/core-win32-x64-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-x64-msvc@npm:1.7.23" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.5.29 - resolution: "@swc/core@npm:1.5.29" + version: 1.7.23 + resolution: "@swc/core@npm:1.7.23" dependencies: - "@swc/core-darwin-arm64": 1.5.29 - "@swc/core-darwin-x64": 1.5.29 - "@swc/core-linux-arm-gnueabihf": 1.5.29 - "@swc/core-linux-arm64-gnu": 1.5.29 - "@swc/core-linux-arm64-musl": 1.5.29 - "@swc/core-linux-x64-gnu": 1.5.29 - "@swc/core-linux-x64-musl": 1.5.29 - "@swc/core-win32-arm64-msvc": 1.5.29 - "@swc/core-win32-ia32-msvc": 1.5.29 - "@swc/core-win32-x64-msvc": 1.5.29 + "@swc/core-darwin-arm64": 1.7.23 + "@swc/core-darwin-x64": 1.7.23 + "@swc/core-linux-arm-gnueabihf": 1.7.23 + "@swc/core-linux-arm64-gnu": 1.7.23 + "@swc/core-linux-arm64-musl": 1.7.23 + "@swc/core-linux-x64-gnu": 1.7.23 + "@swc/core-linux-x64-musl": 1.7.23 + "@swc/core-win32-arm64-msvc": 1.7.23 + "@swc/core-win32-ia32-msvc": 1.7.23 + "@swc/core-win32-x64-msvc": 1.7.23 "@swc/counter": ^0.1.3 - "@swc/types": ^0.1.8 + "@swc/types": ^0.1.12 peerDependencies: "@swc/helpers": "*" dependenciesMeta: @@ -2961,7 +2961,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 19e3f8525ce4f4d9f6f3f62825b1f13e1e76146390930b6d5e663c0bb2c5e95ef3bf30268110d1444ea92b99dd95bfe5a3c6e2a3c0dd79291505dc7da1ed3dab + checksum: a21e9a67d305ad9b49b6def9f9698b374db6c15b2ded5e5cf29390221181fc6b91bfa317b53fd41d9cca989d85b79bbbca5ab27635df1e0232cad4681f618fa5 languageName: node linkType: hard @@ -2972,12 +2972,12 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.8": - version: 0.1.8 - resolution: "@swc/types@npm:0.1.8" +"@swc/types@npm:^0.1.12": + version: 0.1.12 + resolution: "@swc/types@npm:0.1.12" dependencies: "@swc/counter": ^0.1.3 - checksum: e564d0e37b0e28546973c6d50c7a179395912a97168d695cfe9cf1051199c8b828680cdafcb8d43948f76d3703873bafb88dfb5bc2dfe0596b4ad18fcaf90c80 + checksum: cf7f89e46f859864075d7965582baea9c5f98830f45b1046251568c9bdf1ca484b1bf37f6d3c32b7c82ecf8cd5df89d22f05268c391819c44e49911bb1a8e71a languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index bd8e1c8d1c..76fbef0085 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16734,92 +16734,92 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-darwin-arm64@npm:1.5.29" +"@swc/core-darwin-arm64@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-darwin-arm64@npm:1.7.23" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-darwin-x64@npm:1.5.29" +"@swc/core-darwin-x64@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-darwin-x64@npm:1.7.23" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.5.29" +"@swc/core-linux-arm-gnueabihf@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.23" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm64-gnu@npm:1.5.29" +"@swc/core-linux-arm64-gnu@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm64-gnu@npm:1.7.23" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-arm64-musl@npm:1.5.29" +"@swc/core-linux-arm64-musl@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-arm64-musl@npm:1.7.23" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-x64-gnu@npm:1.5.29" +"@swc/core-linux-x64-gnu@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-x64-gnu@npm:1.7.23" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-linux-x64-musl@npm:1.5.29" +"@swc/core-linux-x64-musl@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-linux-x64-musl@npm:1.7.23" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-arm64-msvc@npm:1.5.29" +"@swc/core-win32-arm64-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-arm64-msvc@npm:1.7.23" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-ia32-msvc@npm:1.5.29" +"@swc/core-win32-ia32-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-ia32-msvc@npm:1.7.23" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.5.29": - version: 1.5.29 - resolution: "@swc/core-win32-x64-msvc@npm:1.5.29" +"@swc/core-win32-x64-msvc@npm:1.7.23": + version: 1.7.23 + resolution: "@swc/core-win32-x64-msvc@npm:1.7.23" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.5.29 - resolution: "@swc/core@npm:1.5.29" + version: 1.7.23 + resolution: "@swc/core@npm:1.7.23" dependencies: - "@swc/core-darwin-arm64": 1.5.29 - "@swc/core-darwin-x64": 1.5.29 - "@swc/core-linux-arm-gnueabihf": 1.5.29 - "@swc/core-linux-arm64-gnu": 1.5.29 - "@swc/core-linux-arm64-musl": 1.5.29 - "@swc/core-linux-x64-gnu": 1.5.29 - "@swc/core-linux-x64-musl": 1.5.29 - "@swc/core-win32-arm64-msvc": 1.5.29 - "@swc/core-win32-ia32-msvc": 1.5.29 - "@swc/core-win32-x64-msvc": 1.5.29 + "@swc/core-darwin-arm64": 1.7.23 + "@swc/core-darwin-x64": 1.7.23 + "@swc/core-linux-arm-gnueabihf": 1.7.23 + "@swc/core-linux-arm64-gnu": 1.7.23 + "@swc/core-linux-arm64-musl": 1.7.23 + "@swc/core-linux-x64-gnu": 1.7.23 + "@swc/core-linux-x64-musl": 1.7.23 + "@swc/core-win32-arm64-msvc": 1.7.23 + "@swc/core-win32-ia32-msvc": 1.7.23 + "@swc/core-win32-x64-msvc": 1.7.23 "@swc/counter": ^0.1.3 - "@swc/types": ^0.1.8 + "@swc/types": ^0.1.12 peerDependencies: "@swc/helpers": "*" dependenciesMeta: @@ -16846,7 +16846,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 19e3f8525ce4f4d9f6f3f62825b1f13e1e76146390930b6d5e663c0bb2c5e95ef3bf30268110d1444ea92b99dd95bfe5a3c6e2a3c0dd79291505dc7da1ed3dab + checksum: a21e9a67d305ad9b49b6def9f9698b374db6c15b2ded5e5cf29390221181fc6b91bfa317b53fd41d9cca989d85b79bbbca5ab27635df1e0232cad4681f618fa5 languageName: node linkType: hard @@ -16879,12 +16879,12 @@ __metadata: languageName: node linkType: hard -"@swc/types@npm:^0.1.8": - version: 0.1.8 - resolution: "@swc/types@npm:0.1.8" +"@swc/types@npm:^0.1.12": + version: 0.1.12 + resolution: "@swc/types@npm:0.1.12" dependencies: "@swc/counter": ^0.1.3 - checksum: e564d0e37b0e28546973c6d50c7a179395912a97168d695cfe9cf1051199c8b828680cdafcb8d43948f76d3703873bafb88dfb5bc2dfe0596b4ad18fcaf90c80 + checksum: cf7f89e46f859864075d7965582baea9c5f98830f45b1046251568c9bdf1ca484b1bf37f6d3c32b7c82ecf8cd5df89d22f05268c391819c44e49911bb1a8e71a languageName: node linkType: hard From 1ac573b4f3e4f8ffa34ff0d81cb3bfd9160d74f7 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 07:59:02 +0200 Subject: [PATCH 135/317] chore: smol refactor Signed-off-by: blam --- .../src/app/createExtensionTester.test.tsx | 14 +++----------- .../src/app/renderInTestApp.tsx | 4 ++-- .../SearchResultListItemBlueprint.test.tsx | 2 +- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx index 582bbeed00..a6265ae3ac 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx @@ -30,7 +30,6 @@ const stringDataRef = createExtensionDataRef().with({ describe('createExtensionTester', () => { it('should return the correct dataRef when called', () => { const extension = createExtension({ - namespace: 'test', attachTo: { id: 'ignored', input: 'ignored' }, output: [stringDataRef], factory: () => [stringDataRef('test-text')], @@ -43,7 +42,6 @@ describe('createExtensionTester', () => { it('should throw an error if trying to access an instance not provided to the tester', () => { const extension = createExtension({ - namespace: 'test', name: 'e1', attachTo: { id: 'ignored', input: 'ignored' }, output: [stringDataRef], @@ -51,7 +49,6 @@ describe('createExtensionTester', () => { }); const extension2 = createExtension({ - namespace: 'test', name: 'e2', attachTo: { id: 'ignored', input: 'ignored' }, output: [stringDataRef], @@ -61,13 +58,12 @@ describe('createExtensionTester', () => { const tester = createExtensionTester(extension); expect(() => tester.query(extension2)).toThrow( - "Extension with ID 'test/e2' not found, please make sure it's added to the tester", + "Extension with ID 'e2' not found, please make sure it's added to the tester", ); }); it('should throw an error if trying to access an instance which is not part of the tree', () => { const extension = createExtension({ - namespace: 'test', name: 'e1', attachTo: { id: 'ignored', input: 'ignored' }, output: [stringDataRef], @@ -75,7 +71,6 @@ describe('createExtensionTester', () => { }); const extension2 = createExtension({ - namespace: 'test', name: 'e2', attachTo: { id: 'ignored', input: 'ignored' }, output: [stringDataRef], @@ -85,7 +80,7 @@ describe('createExtensionTester', () => { const tester = createExtensionTester(extension).add(extension2); expect(() => tester.query(extension2)).toThrow( - "Extension with ID 'test/e2' has not been instantiated, because it is not part of the test subject's extension tree", + "Extension with ID 'e2' has not been instantiated, because it is not part of the test subject's extension tree", ); }); @@ -99,7 +94,6 @@ describe('createExtensionTester', () => { }); const extension = createExtension({ - namespace: 'test', name: 'e1', attachTo: { id: 'ignored', input: 'ignored' }, output: [stringDataRef, internalRef.optional()], @@ -129,7 +123,6 @@ describe('createExtensionTester', () => { }); const extension = createExtension({ - namespace: 'test', name: 'e1', inputs: { ignored: createExtensionInput([stringDataRef]), @@ -140,9 +133,8 @@ describe('createExtensionTester', () => { }); const extraExtension = createExtension({ - namespace: 'test', name: 'e2', - attachTo: { id: 'test/e1', input: 'ignored' }, + attachTo: { id: 'e1', input: 'ignored' }, output: [stringDataRef, internalRef.optional()], factory: () => [stringDataRef('test-text')], }); diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.tsx index fae67a260c..80897e942a 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.tsx @@ -25,7 +25,6 @@ import { ConfigReader } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { createExtension, - createExtensionOverrides, ExtensionDefinition, coreExtensionData, RouteRef, @@ -33,6 +32,7 @@ import { IconComponent, RouterBlueprint, NavItemBlueprint, + createExtensionOverrides, } from '@backstage/frontend-plugin-api'; import appPlugin from '@backstage/plugin-app'; @@ -182,10 +182,10 @@ export function renderInTestApp( } const features: FrontendFeature[] = [ - appPluginOverride, createExtensionOverrides({ extensions, }), + appPluginOverride, ]; if (options?.features) { diff --git a/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx b/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx index b50ca9e55a..6b8f75d70c 100644 --- a/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx +++ b/plugins/search-react/src/alpha/blueprints/SearchResultListItemBlueprint.test.tsx @@ -87,7 +87,7 @@ describe('SearchResultListItemBlueprint', () => { }); const mockSearchPage = PageBlueprint.makeWithOverrides({ - namespace: 'search', + name: 'search', inputs: { items: createExtensionInput([searchResultListItemDataRef]), }, From bbb018213c61273e3073ed3acf2811467fa9668f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 3 Sep 2024 09:51:29 +0200 Subject: [PATCH 136/317] Update docs/features/software-catalog/external-integrations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/external-integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index f9c41e00b9..0f64790087 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1064,7 +1064,7 @@ To create an Incremental Entity Provider, you need to know how to retrieve a sin - **getProviderName:** A unique name to avoid conflicts with other providers. - **next:** Fetches a specific page of entities, moving the cursor forward. -- **around:** Handles setup and teardown, wrapping the process that iterates through multiple pages. +- **around:** Handles setup and tear-down, wrapping the process that iterates through multiple pages. For more information on compatibility, refer to the [requirements](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend-module-incremental-ingestion#requirements). From dbd3349a4321adf63e6dd0c2a4821a58aa39de62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 3 Sep 2024 09:51:36 +0200 Subject: [PATCH 137/317] Update docs/features/software-catalog/external-integrations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/external-integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 0f64790087..69ff245169 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1136,7 +1136,7 @@ If you need to pass a token to your API, then you can create a constructor that export class MyIncrementalEntityProvider implements IncrementalEntityProvider { - token: string; + private readonly token: string; constructor(token: string) { this.token = token; From 9da6a9457e80d22baad231f0da98613021ffb25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 3 Sep 2024 09:51:41 +0200 Subject: [PATCH 138/317] Update docs/features/software-catalog/external-integrations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/external-integrations.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 69ff245169..a326c89370 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1158,8 +1158,7 @@ The last step is to implement the actual `next` method that will accept the curs ```ts export class MyIncrementalEntityProvider implements IncrementalEntityProvider { - - token: string; + private readonly token: string; constructor(token: string) { this.token = token; From 2d3a7629883cf277038cf3f645f9a520155daf4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 3 Sep 2024 09:51:46 +0200 Subject: [PATCH 139/317] Update docs/features/software-catalog/external-integrations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/external-integrations.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index a326c89370..1dbd118668 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1170,7 +1170,6 @@ export class MyIncrementalEntityProvider implements IncrementalEntityProvider Promise): Promise { - const apiClient = new MyApiClient(this.token) await burst({ apiClient }) From db6ad39298f8e48b49ebbc9eaef5523615483e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 3 Sep 2024 09:51:52 +0200 Subject: [PATCH 140/317] Update docs/features/software-catalog/external-integrations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/external-integrations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 1dbd118668..4a49585e1a 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1199,7 +1199,7 @@ export class MyIncrementalEntityProvider implements IncrementalEntityProvider Date: Tue, 3 Sep 2024 09:51:58 +0200 Subject: [PATCH 141/317] Update docs/features/software-catalog/external-integrations.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/features/software-catalog/external-integrations.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 4a49585e1a..50fccd7f59 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1201,7 +1201,9 @@ export class MyIncrementalEntityProvider implements IncrementalEntityProvider Date: Tue, 3 Sep 2024 10:19:04 +0200 Subject: [PATCH 142/317] chore: fix namespace snapshot Signed-off-by: blam --- packages/frontend-defaults/src/createApp.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend-defaults/src/createApp.test.tsx b/packages/frontend-defaults/src/createApp.test.tsx index 1954554816..2890700653 100644 --- a/packages/frontend-defaults/src/createApp.test.tsx +++ b/packages/frontend-defaults/src/createApp.test.tsx @@ -284,9 +284,9 @@ describe('createApp', () => { components [ - - - + + + ] From 4f354ce750d69782e0e01e0c1e756027572f8449 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:40:36 +0000 Subject: [PATCH 143/317] chore(deps): update actions/upload-artifact digest to 5076954 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/api-breaking-changes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index 72f7ee9fb4..12d33b450e 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -46,7 +46,7 @@ jobs: cat ${{ github.event_path }} > event.json - name: Upload Artifacts - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4 with: name: preview-spec path: | From 220fc7e8b80e023c28baee766b2bb1a32507c8c9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:54:55 +0000 Subject: [PATCH 144/317] fix(deps): update dependency luxon to v3.5.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index d5d78e9371..d8f8ccc44f 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -3064,9 +3064,9 @@ __metadata: linkType: hard "@types/luxon@npm:^3.0.0": - version: 3.3.8 - resolution: "@types/luxon@npm:3.3.8" - checksum: 026711b4aefc01c6e233e359c693b6d4c0873acc3f52a08661ea3751b2cf3e34d1ce1d5b0b99b0e40a059a2a3755acc20498d37db5f9a5a4f0f330cdf72db3db + version: 3.4.2 + resolution: "@types/luxon@npm:3.4.2" + checksum: 6f92d5bd02e89f310395753506bcd9cef3a56f5940f7a50db2a2b9822bce753553ac767d143cb5b4f9ed5ddd4a84e64f89ff538082ceb4d18739af7781b56925 languageName: node linkType: hard @@ -7516,9 +7516,9 @@ __metadata: linkType: hard "luxon@npm:^3.0.0": - version: 3.4.4 - resolution: "luxon@npm:3.4.4" - checksum: 36c1f99c4796ee4bfddf7dc94fa87815add43ebc44c8934c924946260a58512f0fd2743a629302885df7f35ccbd2d13f178c15df046d0e3b6eb71db178f1c60c + version: 3.5.0 + resolution: "luxon@npm:3.5.0" + checksum: f290fe5788c8e51e748744f05092160d4be12150dca70f9fadc0d233e53d60ce86acd82e7d909a114730a136a77e56f0d3ebac6141bbb82fd310969a4704825b languageName: node linkType: hard From 0c97c99f5cc460d0a6d189e60d112108f55ef73a Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 10:56:46 +0200 Subject: [PATCH 145/317] chore: make beautiful :nail_care: Signed-off-by: blam --- .../software-catalog/external-integrations.md | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 50fccd7f59..4d5b283c4e 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -1157,7 +1157,9 @@ export class MyIncrementalEntityProvider The last step is to implement the actual `next` method that will accept the cursor, call the API, process the result and return the result. ```ts -export class MyIncrementalEntityProvider implements IncrementalEntityProvider { +export class MyIncrementalEntityProvider + implements IncrementalEntityProvider +{ private readonly token: string; constructor(token: string) { @@ -1168,14 +1170,16 @@ export class MyIncrementalEntityProvider implements IncrementalEntityProvider Promise): Promise { - const apiClient = new MyApiClient(this.token) + const apiClient = new MyApiClient(this.token); - await burst({ apiClient }) + await burst({ apiClient }); } - async next(context: Context, cursor?: Cursor = { page: 1 }): Promise> { + async next( + context: Context, + cursor?: Cursor = { page: 1 }, + ): Promise> { const { apiClient } = context; // call your API with the current cursor @@ -1198,26 +1202,26 @@ export class MyIncrementalEntityProvider implements IncrementalEntityProvider Date: Tue, 3 Sep 2024 09:10:15 +0000 Subject: [PATCH 146/317] chore(deps): update dependency lint-staged to v15.2.10 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 33d63250ef..f99cdc620e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32495,8 +32495,8 @@ __metadata: linkType: hard "lint-staged@npm:^15.0.0": - version: 15.2.9 - resolution: "lint-staged@npm:15.2.9" + version: 15.2.10 + resolution: "lint-staged@npm:15.2.10" dependencies: chalk: ~5.3.0 commander: ~12.1.0 @@ -32504,13 +32504,13 @@ __metadata: execa: ~8.0.1 lilconfig: ~3.1.2 listr2: ~8.2.4 - micromatch: ~4.0.7 + micromatch: ~4.0.8 pidtree: ~0.6.0 string-argv: ~0.3.2 yaml: ~2.5.0 bin: lint-staged: bin/lint-staged.js - checksum: 7f804c24d0374b48d26f11a051342141b84c9954f77225bab5ae221b1e4d47a72f722f39b13169267592c28b2614ad39f9722fd86bc4cdfdf93e8601ff66b0e1 + checksum: 7ab255b848478ca47c6b94aad0e7a3cfe5ba48ae1fb353cfa86635741333b83b1fd793d7cac6d44bf0388ad087d7e0250c7ec0a8ebece63fbcf7a8d175279809 languageName: node linkType: hard @@ -33989,7 +33989,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.7, micromatch@npm:~4.0.7": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.7, micromatch@npm:~4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: From b831235ba9818d34f08890a3e4bc400166ecb28d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:16:22 +0000 Subject: [PATCH 147/317] chore(deps): update jamesives/github-pages-deploy-action action to v4.6.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/deploy_microsite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index ac02794268..2b8dd8365a 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -66,7 +66,7 @@ jobs: run: ls microsite/build && ls microsite/build/storybook - name: Deploy both microsite and storybook to gh-pages - uses: JamesIves/github-pages-deploy-action@94f3c658273cf92fb48ef99e5fbc02bd2dc642b2 # v4.6.3 + uses: JamesIves/github-pages-deploy-action@920cbb300dcd3f0568dbc42700c61e2fd9e6139c # v4.6.4 with: branch: gh-pages folder: microsite/build From 8a9d7972eeec0e05c166dabcbdc1a2a8c6435d8e Mon Sep 17 00:00:00 2001 From: Severin Wischmann Date: Tue, 3 Sep 2024 11:27:19 +0200 Subject: [PATCH 148/317] Removed broad Gitlab permission from ScmAuth To write repositories the `api` permission is not needed anymore. Signed-off-by: Severin Wischmann --- .changeset/cuddly-glasses-battle.md | 7 +++++++ packages/integration-react/src/api/ScmAuth.test.ts | 2 +- packages/integration-react/src/api/ScmAuth.ts | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 .changeset/cuddly-glasses-battle.md diff --git a/.changeset/cuddly-glasses-battle.md b/.changeset/cuddly-glasses-battle.md new file mode 100644 index 0000000000..b9cfbbcb99 --- /dev/null +++ b/.changeset/cuddly-glasses-battle.md @@ -0,0 +1,7 @@ +--- +'@backstage/integration-react': patch +--- + +Remove unnecessary broad permissions from Gitlab SCMAuth + +Newer versions of Gitlab (after 2019) do not require the broad api permissions to write to repos. diff --git a/packages/integration-react/src/api/ScmAuth.test.ts b/packages/integration-react/src/api/ScmAuth.test.ts index d252b95b85..e63ce2844c 100644 --- a/packages/integration-react/src/api/ScmAuth.test.ts +++ b/packages/integration-react/src/api/ScmAuth.test.ts @@ -105,7 +105,7 @@ describe('ScmAuth', () => { additionalScope: { repoWrite: true }, }), ).resolves.toMatchObject({ - token: 'read_user read_api read_repository write_repository api', + token: 'read_user read_api read_repository write_repository', }); const azureAuth = ScmAuth.forAzure(mockAuthApi); diff --git a/packages/integration-react/src/api/ScmAuth.ts b/packages/integration-react/src/api/ScmAuth.ts index a22b15e400..6257915c8c 100644 --- a/packages/integration-react/src/api/ScmAuth.ts +++ b/packages/integration-react/src/api/ScmAuth.ts @@ -162,7 +162,7 @@ export class ScmAuth implements ScmAuthApi { * * If the additional `repoWrite` permission is requested, these scopes are added: * - * `write_repository api` + * `write_repository` */ static forGitlab( gitlabAuthApi: OAuthApi, @@ -173,7 +173,7 @@ export class ScmAuth implements ScmAuthApi { const host = options?.host ?? 'gitlab.com'; return new ScmAuth('gitlab', gitlabAuthApi, host, { default: ['read_user', 'read_api', 'read_repository'], - repoWrite: ['write_repository', 'api'], + repoWrite: ['write_repository'], }); } From 40b8a7a280f9361b078a66c9b8b562eeef8a5a0b Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 12:02:17 +0200 Subject: [PATCH 149/317] chore: should be wrapped up in a plugin instead of a module Signed-off-by: blam --- .../src/blueprints/SignInPageBlueprint.test.tsx | 1 - packages/frontend-test-utils/src/app/renderInTestApp.tsx | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx index 2637873930..a85b5e6f39 100644 --- a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx @@ -57,7 +57,6 @@ describe('SignInPageBlueprint', () => { const MockSignInPage = () =>
      ; const extension = SignInPageBlueprint.make({ - name: 'test', params: { loader: async () => () => }, }); diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.tsx index 80897e942a..e53566a118 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.tsx @@ -32,7 +32,7 @@ import { IconComponent, RouterBlueprint, NavItemBlueprint, - createExtensionOverrides, + createFrontendPlugin, } from '@backstage/frontend-plugin-api'; import appPlugin from '@backstage/plugin-app'; @@ -136,7 +136,6 @@ export function renderInTestApp( ): RenderResult { const extensions: Array = [ createExtension({ - namespace: 'test', attachTo: { id: 'app/routes', input: 'routes' }, output: [coreExtensionData.reactElement, coreExtensionData.routePath], factory: () => { @@ -147,7 +146,6 @@ export function renderInTestApp( }, }), RouterBlueprint.make({ - namespace: 'test', params: { Component: ({ children }) => {children}, }, @@ -182,7 +180,8 @@ export function renderInTestApp( } const features: FrontendFeature[] = [ - createExtensionOverrides({ + createFrontendPlugin({ + id: 'test', extensions, }), appPluginOverride, From e6a0d4f7f09bab7a98979e0fe250945c0a841d3c Mon Sep 17 00:00:00 2001 From: martina-equinix Date: Tue, 3 Sep 2024 11:35:10 +0200 Subject: [PATCH 150/317] Support mapping custom tags in mkdocs yaml parser (techdocs) Signed-off-by: martina-equinix --- .../generate/__fixtures__/mkdocs_with_extensions.yml | 7 ++++++- plugins/techdocs-node/src/stages/generate/helpers.test.ts | 4 ++++ plugins/techdocs-node/src/stages/generate/helpers.ts | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/plugins/techdocs-node/src/stages/generate/__fixtures__/mkdocs_with_extensions.yml b/plugins/techdocs-node/src/stages/generate/__fixtures__/mkdocs_with_extensions.yml index d6c6858722..d1209d0c21 100644 --- a/plugins/techdocs-node/src/stages/generate/__fixtures__/mkdocs_with_extensions.yml +++ b/plugins/techdocs-node/src/stages/generate/__fixtures__/mkdocs_with_extensions.yml @@ -4,4 +4,9 @@ site_description: Test site description markdown_extensions: - pymdownx.emoji: emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_generator: !!python/object.apply:materialx.emoji.to_svg + - pymdownx.tabbed: + alternate_style: true + slugify: !!python/object/apply:pymdownx.slugs.slugify + kwds: + case: lower diff --git a/plugins/techdocs-node/src/stages/generate/helpers.test.ts b/plugins/techdocs-node/src/stages/generate/helpers.test.ts index 755cd9d1a6..af61dae936 100644 --- a/plugins/techdocs-node/src/stages/generate/helpers.test.ts +++ b/plugins/techdocs-node/src/stages/generate/helpers.test.ts @@ -247,6 +247,10 @@ describe('helpers', () => { expect(updatedMkdocsYml.toString()).toContain( "emoji_index: !!python/name:materialx.emoji.twemoji ''", ); + expect(updatedMkdocsYml.toString()).toContain( + 'slugify: !!python/object/apply:pymdownx.slugs.slugify', + ); + expect(updatedMkdocsYml.toString()).toContain('case: lower'); }); it('should not override existing repo_url in mkdocs.yml', async () => { diff --git a/plugins/techdocs-node/src/stages/generate/helpers.ts b/plugins/techdocs-node/src/stages/generate/helpers.ts index d62b6dcccd..0cba56cfa5 100644 --- a/plugins/techdocs-node/src/stages/generate/helpers.ts +++ b/plugins/techdocs-node/src/stages/generate/helpers.ts @@ -141,6 +141,14 @@ export const MKDOCS_SCHEMA = DEFAULT_SCHEMA.extend([ instanceOf: UnknownTag, construct: (data: string, type?: string) => new UnknownTag(data, type), }), + new Type('tag:', { + kind: 'mapping', + multi: true, + representName: o => (o as UnknownTag).type, + represent: o => (o as UnknownTag).data ?? '', + instanceOf: UnknownTag, + construct: (data: string, type?: string) => new UnknownTag(data, type), + }), new Type('', { kind: 'sequence', multi: true, From 57da51fa0fe2c1b2df0d90abe7d7a11f35ae9d92 Mon Sep 17 00:00:00 2001 From: martina-equinix Date: Tue, 3 Sep 2024 11:38:56 +0200 Subject: [PATCH 151/317] Add changeset Signed-off-by: martina-equinix --- .changeset/long-peas-repeat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/long-peas-repeat.md diff --git a/.changeset/long-peas-repeat.md b/.changeset/long-peas-repeat.md new file mode 100644 index 0000000000..1ea65056b4 --- /dev/null +++ b/.changeset/long-peas-repeat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +Add support for mapping custom tags in the techdocs yaml parser that validates the mkdocs.yaml file From 5cbb4c6fd10ca656bc20779960590ee5f0b555a3 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 13:16:14 +0200 Subject: [PATCH 152/317] chore: fix remaining tests Signed-off-by: blam --- .../core-compat-api/src/compatWrapper/compatWrapper.test.tsx | 2 +- .../src/blueprints/AppRootWrapperBlueprint.test.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx index bee379a792..fe4770a08c 100644 --- a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx +++ b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx @@ -74,7 +74,7 @@ describe('BackwardsCompatProvider', () => { ); expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(` - "plugins: app + "plugins: test, app components: NotFoundErrorPage, BootErrorPage, Progress, Router, ErrorBoundaryFallback icons: brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, user, warning" `); diff --git a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx index cda0085fec..f9214121ce 100644 --- a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx @@ -72,7 +72,6 @@ describe('AppRootWrapperBlueprint', () => { it('should render the complex component wrapper', async () => { const extension = AppRootWrapperBlueprint.makeWithOverrides({ - name: 'test', config: { schema: { name: z => z.string(), From 948d4319836ebbcc731eff0c3a79213f9af8908e Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 13:19:43 +0200 Subject: [PATCH 153/317] chore: add changeset Signed-off-by: blam --- .changeset/shiny-carpets-worry.md | 9 ++++++++ packages/core-compat-api/api-report.md | 7 +++---- packages/frontend-plugin-api/api-report.md | 2 +- plugins/app/api-report.md | 24 ---------------------- 4 files changed, 13 insertions(+), 29 deletions(-) create mode 100644 .changeset/shiny-carpets-worry.md diff --git a/.changeset/shiny-carpets-worry.md b/.changeset/shiny-carpets-worry.md new file mode 100644 index 0000000000..f1c3bbe292 --- /dev/null +++ b/.changeset/shiny-carpets-worry.md @@ -0,0 +1,9 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-test-utils': patch +'@backstage/frontend-app-api': patch +'@backstage/core-compat-api': patch +'@backstage/plugin-search-react': patch +--- + +Removing superfluous `namespace` parameter in favour of `pluginId` instead diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index e61ee451d9..aaf840df8c 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -9,12 +9,11 @@ import { AnalyticsEvent } from '@backstage/core-plugin-api'; import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { BackstagePlugin as BackstagePlugin_2 } from '@backstage/frontend-plugin-api'; import { ComponentType } from 'react'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; -import { ExtensionOverrides } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; +import { FrontendModule } from '@backstage/frontend-plugin-api'; import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; @@ -29,7 +28,7 @@ export function compatWrapper(element: ReactNode): React_2.JSX.Element; // @public (undocumented) export function convertLegacyApp( rootElement: React_2.JSX.Element, -): (FrontendPlugin | ExtensionOverrides)[]; +): (FrontendPlugin | FrontendModule)[]; // @public (undocumented) export function convertLegacyPageExtension( @@ -46,7 +45,7 @@ export function convertLegacyPlugin( options: { extensions: ExtensionDefinition[]; }, -): BackstagePlugin_2; +): FrontendPlugin; // @public export function convertLegacyRouteRef( diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 276ca190a6..75d91d28f0 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -454,7 +454,7 @@ export function createComponentExtension(options: { >; }; kind: 'component'; - namespace: string; + namespace: undefined; name: string; }>; diff --git a/plugins/app/api-report.md b/plugins/app/api-report.md index fbf77debb4..6f2e6e0463 100644 --- a/plugins/app/api-report.md +++ b/plugins/app/api-report.md @@ -29,30 +29,6 @@ const appPlugin: FrontendPlugin< {}, {}, { - [x: `component:app/${string}`]: ExtensionDefinition<{ - config: {}; - configInput: {}; - output: ConfigurableExtensionDataRef< - { - ref: ComponentRef; - impl: ComponentType; - }, - 'core.component.component', - {} - >; - inputs: { - [x: string]: ExtensionInput< - AnyExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; - }; - kind: 'component'; - namespace: string; - name: string; - }>; app: ExtensionDefinition<{ config: {}; configInput: {}; From 8d9a54c7d50f9130823c0aec79e47232424ea251 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Tue, 3 Sep 2024 13:35:43 +0200 Subject: [PATCH 154/317] processed feedback Signed-off-by: Jasper Boeijenga --- plugins/scaffolder-react/api-report-alpha.md | 6 +++- .../src/next/components/Stepper/Stepper.tsx | 15 ++++++---- .../src/next/hooks/useFormDataFromQuery.ts | 28 +++++++++++-------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/plugins/scaffolder-react/api-report-alpha.md b/plugins/scaffolder-react/api-report-alpha.md index 57f97f2012..545cc02e9e 100644 --- a/plugins/scaffolder-react/api-report-alpha.md +++ b/plugins/scaffolder-react/api-report-alpha.md @@ -3,9 +3,12 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +/// + import { ApiHolder } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { CustomFieldValidator } from '@backstage/plugin-scaffolder-react'; +import { Dispatch } from 'react'; import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react'; import { FieldValidation } from '@rjsf/utils'; import { FormProps } from '@backstage/plugin-scaffolder-react'; @@ -22,6 +25,7 @@ import { ReviewStepProps } from '@backstage/plugin-scaffolder-react'; import { ScaffolderRJSFFormProps } from '@backstage/plugin-scaffolder-react'; import { ScaffolderStep } from '@backstage/plugin-scaffolder-react'; import { ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react'; +import { SetStateAction } from 'react'; import { StyleRules } from '@material-ui/core/styles/withStyles'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; @@ -276,7 +280,7 @@ export const useFilteredSchemaProperties: ( // @alpha export const useFormDataFromQuery: ( initialState?: Record, -) => Record; +) => [Record, Dispatch>>]; // @alpha (undocumented) export const useTemplateParameterSchema: (templateRef: string) => { diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 069512da5a..a1b31d0089 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -115,7 +115,7 @@ export const Stepper = (stepperProps: StepperProps) => { const apiHolder = useApiHolder(); const [activeStep, setActiveStep] = useState(0); const [isValidating, setIsValidating] = useState(false); - const initialState = useFormDataFromQuery(props.initialState); + const [initialState] = useFormDataFromQuery(props.initialState); const [formState, setFormState] = useState<{ [step: string]: Record; }>(); @@ -123,6 +123,8 @@ export const Stepper = (stepperProps: StepperProps) => { const [errors, setErrors] = useState(); const styles = useStyles(); + const makeStepKey = (step: string | number) => `step-${step}`; + const backLabel = presentation?.buttonLabels?.backButtonText ?? backButtonText; const createLabel = @@ -161,7 +163,7 @@ export const Stepper = (stepperProps: StepperProps) => { (e: IChangeEvent) => { setFormState(current => ({ ...current, - [`step${activeStep}`]: e.formData, + [makeStepKey(activeStep)]: e.formData, })); }, [setFormState, activeStep], @@ -193,7 +195,10 @@ export const Stepper = (stepperProps: StepperProps) => { return stepNum; }); } - setFormState(current => ({ ...current, [`step${activeStep}`]: formData })); + setFormState(current => ({ + ...current, + [makeStepKey(activeStep)]: formData, + })); }; const { @@ -208,11 +213,11 @@ export const Stepper = (stepperProps: StepperProps) => { if (!formState) { return initialState; } - const { [`step${activeStep}`]: activeState, ...historicalState } = + const { [makeStepKey(activeStep)]: activeState, ...historicalState } = formState; const chronologicalState = { ...historicalState, - [`step${activeStep}`]: activeState, + [makeStepKey(activeStep)]: activeState, }; return merge({}, ...Object.values(chronologicalState)); }, [formState, activeStep, initialState]); diff --git a/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts b/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts index 777c615cd7..966a8fe8f4 100644 --- a/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts +++ b/plugins/scaffolder-react/src/next/hooks/useFormDataFromQuery.ts @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { JsonValue } from '@backstage/types'; import qs from 'qs'; +import { useState } from 'react'; /** * This hook is used to get the formData from the query string. @@ -22,18 +24,20 @@ import qs from 'qs'; */ export const useFormDataFromQuery = ( initialState?: Record, -): Record => { - if (initialState) { - return initialState; - } +) => { + return useState>(() => { + if (initialState) { + return initialState; + } - const query = qs.parse(window.location.search, { - ignoreQueryPrefix: true, + const query = qs.parse(window.location.search, { + ignoreQueryPrefix: true, + }); + + try { + return JSON.parse(query.formData as string); + } catch (e) { + return {}; + } }); - - try { - return JSON.parse(query.formData as string); - } catch (e) { - return {}; - } }; From a4bac3c25b354f8b0dda2065756c1e51827ba96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 3 Sep 2024 13:14:00 +0200 Subject: [PATCH 155/317] removed the basePath of discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/angry-hotels-warn.md | 6 ++++++ packages/backend-common/api-report.md | 7 +------ .../backend-common/src/deprecated/index.ts | 6 +++--- .../backend-defaults/api-report-discovery.md | 7 +------ .../discovery/HostDiscovery.test.ts | 19 ------------------- .../entrypoints/discovery/HostDiscovery.ts | 10 ++++------ 6 files changed, 15 insertions(+), 40 deletions(-) create mode 100644 .changeset/angry-hotels-warn.md diff --git a/.changeset/angry-hotels-warn.md b/.changeset/angry-hotels-warn.md new file mode 100644 index 0000000000..07caaba574 --- /dev/null +++ b/.changeset/angry-hotels-warn.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-defaults': minor +'@backstage/backend-common': minor +--- + +**BREAKING**: You can no longer supply a `basePath` option to the host discovery implementation. In the new backend system, the ability to choose this path has been removed anyway at the plugin router level. diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 4a59277e37..1a67d4877f 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -277,12 +277,7 @@ export class Git { // @public @deprecated class HostDiscovery implements DiscoveryService { - static fromConfig( - config: Config, - options?: { - basePath?: string; - }, - ): HostDiscovery; + static fromConfig(config: Config): HostDiscovery; // (undocumented) getBaseUrl(pluginId: string): Promise; // (undocumented) diff --git a/packages/backend-common/src/deprecated/index.ts b/packages/backend-common/src/deprecated/index.ts index c7ba186df4..f769d4db09 100644 --- a/packages/backend-common/src/deprecated/index.ts +++ b/packages/backend-common/src/deprecated/index.ts @@ -96,11 +96,11 @@ export class HostDiscovery implements DiscoveryService { * plugins: [search] * ``` * - * The basePath defaults to `/api`, meaning the default full internal + * The fixed base path is `/api`, meaning the default full internal * path for the `catalog` plugin will be `http://localhost:7007/api/catalog`. */ - static fromConfig(config: Config, options?: { basePath?: string }) { - return new HostDiscovery(_HostDiscovery.fromConfig(config, options)); + static fromConfig(config: Config) { + return new HostDiscovery(_HostDiscovery.fromConfig(config)); } private constructor(private readonly impl: _HostDiscovery) {} diff --git a/packages/backend-defaults/api-report-discovery.md b/packages/backend-defaults/api-report-discovery.md index f12daf49c1..d168d33a71 100644 --- a/packages/backend-defaults/api-report-discovery.md +++ b/packages/backend-defaults/api-report-discovery.md @@ -16,12 +16,7 @@ export const discoveryServiceFactory: ServiceFactory< // @public export class HostDiscovery implements DiscoveryService { - static fromConfig( - config: RootConfigService, - options?: { - basePath?: string; - }, - ): HostDiscovery; + static fromConfig(config: RootConfigService): HostDiscovery; // (undocumented) getBaseUrl(pluginId: string): Promise; // (undocumented) diff --git a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts index 4e6aff5853..e4e30044dd 100644 --- a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts +++ b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.test.ts @@ -54,25 +54,6 @@ describe('HostDiscovery', () => { ); }); - it('can configure the base path', async () => { - const discovery = HostDiscovery.fromConfig( - new ConfigReader({ - backend: { - baseUrl: 'http://localhost:40', - listen: { port: 80, host: 'localhost' }, - }, - }), - { basePath: '/service' }, - ); - - await expect(discovery.getBaseUrl('catalog')).resolves.toBe( - 'http://localhost:80/service/catalog', - ); - await expect(discovery.getExternalBaseUrl('catalog')).resolves.toBe( - 'http://localhost:40/service/catalog', - ); - }); - it.each([ [{ listen: ':80' }, 'http://localhost:80'], [{ listen: ':40', https: true }, 'https://localhost:40'], diff --git a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts index 26e2a01a23..47793c82f7 100644 --- a/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts +++ b/packages/backend-defaults/src/entrypoints/discovery/HostDiscovery.ts @@ -42,6 +42,7 @@ export class HostDiscovery implements DiscoveryService { * * Can be overridden in config by providing a target and corresponding plugins in `discovery.endpoints`. * eg. + * * ```yaml * discovery: * endpoints: @@ -55,14 +56,11 @@ export class HostDiscovery implements DiscoveryService { * plugins: [search] * ``` * - * The basePath defaults to `/api`, meaning the default full internal + * The fixed base path is `/api`, meaning the default full internal * path for the `catalog` plugin will be `http://localhost:7007/api/catalog`. */ - static fromConfig( - config: RootConfigService, - options?: { basePath?: string }, - ) { - const basePath = options?.basePath ?? '/api'; + static fromConfig(config: RootConfigService) { + const basePath = '/api'; const externalBaseUrl = config .getString('backend.baseUrl') .replace(/\/+$/, ''); From 4d39e3b4ae431de662bfbb405bdb151a3471ab03 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 13:55:36 +0200 Subject: [PATCH 156/317] chore: revert small change Signed-off-by: blam --- packages/core-compat-api/api-report.md | 3 ++- packages/core-compat-api/src/convertLegacyApp.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index aaf840df8c..ddb3bc6bf4 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -11,6 +11,7 @@ import { AnyRouteRefParams } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { ExtensionOverrides } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; import { FrontendModule } from '@backstage/frontend-plugin-api'; @@ -28,7 +29,7 @@ export function compatWrapper(element: ReactNode): React_2.JSX.Element; // @public (undocumented) export function convertLegacyApp( rootElement: React_2.JSX.Element, -): (FrontendPlugin | FrontendModule)[]; +): (FrontendPlugin | FrontendModule | ExtensionOverrides)[]; // @public (undocumented) export function convertLegacyPageExtension( diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index 610c9e8842..3f35c78408 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -26,6 +26,7 @@ import { FrontendPlugin, coreExtensionData, createExtension, + ExtensionOverrides, createExtensionInput, createFrontendModule, } from '@backstage/frontend-plugin-api'; @@ -61,7 +62,7 @@ function selectChildren( /** @public */ export function convertLegacyApp( rootElement: React.JSX.Element, -): (FrontendPlugin | FrontendModule)[] { +): (FrontendPlugin | FrontendModule | ExtensionOverrides)[] { if (getComponentData(rootElement, 'core.type') === 'FlatRoutes') { return collectLegacyRoutes(rootElement); } From 2f74b736113fc0ca14129ee657d98ca06242315b Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 14:47:52 +0200 Subject: [PATCH 157/317] chore: fix changeset Signed-off-by: blam --- .changeset/shiny-carpets-worry.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/shiny-carpets-worry.md b/.changeset/shiny-carpets-worry.md index f1c3bbe292..2a111c7334 100644 --- a/.changeset/shiny-carpets-worry.md +++ b/.changeset/shiny-carpets-worry.md @@ -3,7 +3,6 @@ '@backstage/frontend-test-utils': patch '@backstage/frontend-app-api': patch '@backstage/core-compat-api': patch -'@backstage/plugin-search-react': patch --- -Removing superfluous `namespace` parameter in favour of `pluginId` instead +Removing deprecated `namespace` parameter in favour of `pluginId` instead From 6db849e650901cf11e7d5b2758b518381e118dc4 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 3 Sep 2024 15:33:48 +0200 Subject: [PATCH 158/317] chore: fix up some small things Signed-off-by: blam --- .changeset/shiny-carpets-worried.md | 5 +++++ .changeset/shiny-carpets-worry.md | 1 - .../src/extensions/createComponentExtension.tsx | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/shiny-carpets-worried.md diff --git a/.changeset/shiny-carpets-worried.md b/.changeset/shiny-carpets-worried.md new file mode 100644 index 0000000000..c0093b49aa --- /dev/null +++ b/.changeset/shiny-carpets-worried.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': minor +--- + +**BREAKING**: The `namespace` parameter for API's is now defaulted to the `pluginId` which was discovered. This means that if you're overriding API's by using ID's directly, they might have changed to include the plugin ID too. diff --git a/.changeset/shiny-carpets-worry.md b/.changeset/shiny-carpets-worry.md index 2a111c7334..53688df920 100644 --- a/.changeset/shiny-carpets-worry.md +++ b/.changeset/shiny-carpets-worry.md @@ -2,7 +2,6 @@ '@backstage/frontend-plugin-api': patch '@backstage/frontend-test-utils': patch '@backstage/frontend-app-api': patch -'@backstage/core-compat-api': patch --- Removing deprecated `namespace` parameter in favour of `pluginId` instead diff --git a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx index ca8f1f807b..f561a73dc6 100644 --- a/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createComponentExtension.tsx @@ -33,7 +33,7 @@ export function createComponentExtension(options: { }) { return createExtension({ kind: 'component', - name: options.ref.id, + name: options.name ?? options.ref.id, attachTo: { id: 'api:app/components', input: 'components' }, disabled: options.disabled, output: [createComponentExtension.componentDataRef], From 582092fb23f1053eb41de6d0f823624c4e082480 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 3 Sep 2024 14:38:01 +0000 Subject: [PATCH 159/317] Version Packages (next) --- .changeset/pre.json | 41 +- docs/releases/v1.31.0-next.1-changelog.md | 2176 +++++++++++++++++ package.json | 2 +- packages/app-defaults/CHANGELOG.md | 11 + packages/app-defaults/package.json | 2 +- packages/app-next-example-plugin/CHANGELOG.md | 8 + packages/app-next-example-plugin/package.json | 2 +- packages/app-next/CHANGELOG.md | 45 + packages/app-next/package.json | 2 +- packages/app/CHANGELOG.md | 41 + packages/app/package.json | 2 +- packages/backend-app-api/CHANGELOG.md | 16 + packages/backend-app-api/package.json | 2 +- packages/backend-common/CHANGELOG.md | 23 + packages/backend-common/package.json | 2 +- packages/backend-defaults/CHANGELOG.md | 42 + packages/backend-defaults/package.json | 2 +- .../CHANGELOG.md | 26 + .../package.json | 2 +- packages/backend-legacy/CHANGELOG.md | 42 + packages/backend-legacy/package.json | 2 +- packages/backend-openapi-utils/CHANGELOG.md | 8 + packages/backend-openapi-utils/package.json | 2 +- packages/backend-plugin-api/CHANGELOG.md | 12 + packages/backend-plugin-api/package.json | 2 +- packages/backend-test-utils/CHANGELOG.md | 15 + packages/backend-test-utils/package.json | 2 +- packages/backend/CHANGELOG.md | 36 + packages/backend/package.json | 2 +- packages/catalog-client/CHANGELOG.md | 12 + packages/catalog-client/package.json | 2 +- packages/cli/CHANGELOG.md | 23 + packages/cli/package.json | 2 +- packages/codemods/CHANGELOG.md | 8 + packages/codemods/package.json | 2 +- packages/core-compat-api/CHANGELOG.md | 14 + packages/core-compat-api/package.json | 2 +- packages/core-components/CHANGELOG.md | 12 + packages/core-components/package.json | 2 +- packages/create-app/CHANGELOG.md | 13 + packages/create-app/package.json | 2 +- packages/dev-utils/CHANGELOG.md | 14 + packages/dev-utils/package.json | 2 +- packages/e2e-test/CHANGELOG.md | 9 + packages/e2e-test/package.json | 2 +- packages/frontend-app-api/CHANGELOG.md | 22 + packages/frontend-app-api/package.json | 2 +- packages/frontend-defaults/CHANGELOG.md | 18 + packages/frontend-defaults/package.json | 2 +- packages/frontend-plugin-api/CHANGELOG.md | 45 + packages/frontend-plugin-api/package.json | 2 +- packages/frontend-test-utils/CHANGELOG.md | 13 + packages/frontend-test-utils/package.json | 2 +- packages/repo-tools/CHANGELOG.md | 13 + packages/repo-tools/package.json | 2 +- .../techdocs-cli-embedded-app/CHANGELOG.md | 19 + .../techdocs-cli-embedded-app/package.json | 2 +- packages/techdocs-cli/CHANGELOG.md | 11 + packages/techdocs-cli/package.json | 2 +- plugins/api-docs/CHANGELOG.md | 15 + plugins/api-docs/package.json | 2 +- plugins/app-backend/CHANGELOG.md | 14 + plugins/app-backend/package.json | 2 +- plugins/app-node/CHANGELOG.md | 8 + plugins/app-node/package.json | 2 +- plugins/app-visualizer/CHANGELOG.md | 9 + plugins/app-visualizer/package.json | 2 +- plugins/app/CHANGELOG.md | 41 + plugins/app/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- plugins/auth-backend/CHANGELOG.md | 30 + plugins/auth-backend/package.json | 2 +- plugins/auth-node/CHANGELOG.md | 14 + plugins/auth-node/package.json | 2 +- plugins/auth-react/CHANGELOG.md | 9 + plugins/auth-react/package.json | 2 +- .../catalog-backend-module-aws/CHANGELOG.md | 16 + .../catalog-backend-module-aws/package.json | 2 +- .../catalog-backend-module-azure/CHANGELOG.md | 11 + .../catalog-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../catalog-backend-module-gcp/CHANGELOG.md | 11 + .../catalog-backend-module-gcp/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 17 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../catalog-backend-module-ldap/CHANGELOG.md | 13 + .../catalog-backend-module-ldap/package.json | 2 +- .../catalog-backend-module-logs/CHANGELOG.md | 9 + .../catalog-backend-module-logs/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- plugins/catalog-backend/CHANGELOG.md | 25 + plugins/catalog-backend/package.json | 2 +- plugins/catalog-graph/CHANGELOG.md | 15 + plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/CHANGELOG.md | 18 + plugins/catalog-import/package.json | 2 +- plugins/catalog-node/CHANGELOG.md | 14 + plugins/catalog-node/package.json | 2 +- plugins/catalog-react/CHANGELOG.md | 20 + plugins/catalog-react/package.json | 2 +- .../catalog-unprocessed-entities/CHANGELOG.md | 15 + .../catalog-unprocessed-entities/package.json | 2 +- plugins/catalog/CHANGELOG.md | 21 + plugins/catalog/package.json | 2 +- plugins/config-schema/CHANGELOG.md | 10 + plugins/config-schema/package.json | 2 +- plugins/devtools-backend/CHANGELOG.md | 16 + plugins/devtools-backend/package.json | 2 +- plugins/devtools/CHANGELOG.md | 13 + plugins/devtools/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../events-backend-module-azure/CHANGELOG.md | 8 + .../events-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../events-backend-module-gerrit/CHANGELOG.md | 8 + .../events-backend-module-gerrit/package.json | 2 +- .../events-backend-module-github/CHANGELOG.md | 9 + .../events-backend-module-github/package.json | 2 +- .../events-backend-module-gitlab/CHANGELOG.md | 9 + .../events-backend-module-gitlab/package.json | 2 +- .../events-backend-test-utils/CHANGELOG.md | 7 + .../events-backend-test-utils/package.json | 2 +- plugins/events-backend/CHANGELOG.md | 10 + plugins/events-backend/package.json | 2 +- plugins/events-node/CHANGELOG.md | 7 + plugins/events-node/package.json | 2 +- .../example-todo-list-backend/CHANGELOG.md | 10 + .../example-todo-list-backend/package.json | 2 +- plugins/example-todo-list/CHANGELOG.md | 8 + plugins/example-todo-list/package.json | 2 +- plugins/home-react/CHANGELOG.md | 8 + plugins/home-react/package.json | 2 +- plugins/home/CHANGELOG.md | 17 + plugins/home/package.json | 2 +- plugins/kubernetes-backend/CHANGELOG.md | 21 + plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes-cluster/CHANGELOG.md | 12 + plugins/kubernetes-cluster/package.json | 2 +- plugins/kubernetes-node/CHANGELOG.md | 10 + plugins/kubernetes-node/package.json | 2 +- plugins/kubernetes-react/CHANGELOG.md | 12 + plugins/kubernetes-react/package.json | 2 +- plugins/kubernetes/CHANGELOG.md | 14 + plugins/kubernetes/package.json | 2 +- .../CHANGELOG.md | 17 + .../package.json | 2 +- plugins/notifications-backend/CHANGELOG.md | 20 + plugins/notifications-backend/package.json | 2 +- plugins/notifications-node/CHANGELOG.md | 12 + plugins/notifications-node/package.json | 2 +- plugins/notifications/CHANGELOG.md | 13 + plugins/notifications/package.json | 2 +- plugins/org-react/CHANGELOG.md | 11 + plugins/org-react/package.json | 2 +- plugins/org/CHANGELOG.md | 13 + plugins/org/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/permission-backend/CHANGELOG.md | 13 + plugins/permission-backend/package.json | 2 +- plugins/permission-node/CHANGELOG.md | 12 + plugins/permission-node/package.json | 2 +- plugins/proxy-backend/CHANGELOG.md | 10 + plugins/proxy-backend/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/scaffolder-backend/CHANGELOG.md | 32 + plugins/scaffolder-backend/package.json | 2 +- .../scaffolder-node-test-utils/CHANGELOG.md | 10 + .../scaffolder-node-test-utils/package.json | 2 +- plugins/scaffolder-node/CHANGELOG.md | 13 + plugins/scaffolder-node/package.json | 2 +- plugins/scaffolder-react/CHANGELOG.md | 17 + plugins/scaffolder-react/package.json | 2 +- plugins/scaffolder/CHANGELOG.md | 21 + plugins/scaffolder/package.json | 2 +- .../CHANGELOG.md | 17 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- plugins/search-backend-module-pg/CHANGELOG.md | 11 + plugins/search-backend-module-pg/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 17 + .../package.json | 2 +- plugins/search-backend-node/CHANGELOG.md | 12 + plugins/search-backend-node/package.json | 2 +- plugins/search-backend/CHANGELOG.md | 17 + plugins/search-backend/package.json | 2 +- plugins/search-react/CHANGELOG.md | 13 + plugins/search-react/package.json | 2 +- plugins/search/CHANGELOG.md | 16 + plugins/search/package.json | 2 +- plugins/signals-backend/CHANGELOG.md | 13 + plugins/signals-backend/package.json | 2 +- plugins/signals-node/CHANGELOG.md | 12 + plugins/signals-node/package.json | 2 +- plugins/signals/CHANGELOG.md | 12 + plugins/signals/package.json | 2 +- .../techdocs-addons-test-utils/CHANGELOG.md | 15 + .../techdocs-addons-test-utils/package.json | 2 +- plugins/techdocs-backend/CHANGELOG.md | 20 + plugins/techdocs-backend/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- plugins/techdocs-node/CHANGELOG.md | 14 + plugins/techdocs-node/package.json | 2 +- plugins/techdocs-react/CHANGELOG.md | 11 + plugins/techdocs-react/package.json | 2 +- plugins/techdocs/CHANGELOG.md | 22 + plugins/techdocs/package.json | 2 +- plugins/user-settings-backend/CHANGELOG.md | 14 + plugins/user-settings-backend/package.json | 2 +- plugins/user-settings/CHANGELOG.md | 17 + plugins/user-settings/package.json | 2 +- yarn.lock | 171 +- 308 files changed, 4653 insertions(+), 209 deletions(-) create mode 100644 docs/releases/v1.31.0-next.1-changelog.md create mode 100644 packages/frontend-defaults/CHANGELOG.md diff --git a/.changeset/pre.json b/.changeset/pre.json index 492877ea8a..0266c977a1 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -187,9 +187,12 @@ "@backstage/plugin-user-settings": "0.8.11", "@backstage/plugin-user-settings-backend": "0.2.22", "@backstage/plugin-user-settings-common": "0.0.1", - "@backstage/plugin-app": "0.0.0" + "@backstage/plugin-app": "0.0.0", + "@backstage/frontend-defaults": "0.0.0" }, "changesets": [ + "afraid-fans-cross", + "angry-hotels-warn", "big-spies-stare", "blue-forks-cry", "breezy-flowers-dance", @@ -197,24 +200,37 @@ "brown-boxes-arrive", "brown-worms-sneeze", "calm-brooms-fail", + "chilled-cameras-change", + "chilly-birds-shout", "cold-ways-protect", + "cool-actors-sin", "cool-melons-check", "cool-poems-hammer", "cuddly-rocks-invent", "curly-brooms-raise", + "dry-beers-shake", + "dry-glasses-push", "dry-monkeys-mate", "eighty-tables-hope", "empty-coats-sparkle", + "famous-badgers-drop", "five-tigers-share", "flat-kangaroos-push", "forty-toes-float", + "four-parents-buy", "four-ties-raise", "fresh-apes-dress", + "fresh-bears-prove", "fresh-pumas-clean", + "friendly-days-march", + "friendly-months-march", "funny-houses-rest", + "fuzzy-feet-exist", "fuzzy-mails-walk", "fuzzy-spies-share", + "gentle-hats-act", "giant-jars-mix", + "gorgeous-scissors-wave", "green-worms-rescue", "healthy-moons-drum", "heavy-suits-judge", @@ -222,48 +238,71 @@ "kind-moose-learn", "kind-walls-speak", "large-poets-check", + "loud-brooms-pull", "lucky-sheep-cover", "metal-garlics-fetch", "metal-rivers-grin", + "mighty-cheetahs-call", "mighty-days-kiss", "nasty-tigers-knock", "new-poets-unite", "ninety-mayflies-raise", + "odd-goats-kiss", "olive-phones-sniff", + "plenty-dragons-know", "popular-cooks-camp", "purple-cows-sing", "purple-glasses-tease", + "quick-suns-swim", + "quiet-spies-clean", "rare-feet-melt", "real-pants-rule", + "renovate-53b7d25", + "renovate-546c524", + "renovate-93d032c", + "renovate-b8911c2", + "rich-bees-tickle", + "rotten-crabs-hear", "serious-cheetahs-help", + "serious-spies-knock", "sharp-fans-tan", "sharp-items-study", "sharp-mayflies-beg", + "shiny-carpets-worried", + "shiny-carpets-worry", "shiny-zoos-film", "short-moles-brush", "silent-eyes-lie", "six-goats-sort", "six-humans-guess", "sixty-rabbits-cheat", + "slimy-chefs-think", + "smart-gifts-report", + "smart-owls-sell", "spicy-poems-hammer", "spicy-vans-eat", "sweet-cows-clean", + "swift-fishes-rush", "swift-garlics-mix", "swift-radios-enjoy", "swift-seahorses-share", + "tall-camels-march", "tame-hornets-shake", "tasty-pigs-vanish", "thick-walls-share", + "tiny-icons-sit", "tiny-waves-provide", "tough-peaches-kneel", "tricky-apricots-film", "twenty-clouds-melt", "twenty-queens-grow", "unlucky-cycles-clean", + "violet-apricots-smoke", "violet-jokes-tap", "warm-boxes-grab-2", "warm-boxes-grab", "wild-buses-notice", + "wise-forks-play", "wise-scissors-help", "witty-years-cry", "yellow-bees-hope" diff --git a/docs/releases/v1.31.0-next.1-changelog.md b/docs/releases/v1.31.0-next.1-changelog.md new file mode 100644 index 0000000000..eb9b11125e --- /dev/null +++ b/docs/releases/v1.31.0-next.1-changelog.md @@ -0,0 +1,2176 @@ +# Release v1.31.0-next.1 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.31.0-next.1](https://backstage.github.io/upgrade-helper/?to=1.31.0-next.1) + +## @backstage/backend-common@0.25.0-next.1 + +### Minor Changes + +- a4bac3c: **BREAKING**: You can no longer supply a `basePath` option to the host discovery implementation. In the new backend system, the ability to choose this path has been removed anyway at the plugin router level. +- 988c145: **BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. + + - The deprecated `dropDatabase` function has now been removed, without replacement. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + +## @backstage/backend-defaults@0.5.0-next.1 + +### Minor Changes + +- a4bac3c: **BREAKING**: You can no longer supply a `basePath` option to the host discovery implementation. In the new backend system, the ability to choose this path has been removed anyway at the plugin router level. +- 055b75b: **BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. + + For the `/database` subpath exports: + + - The deprecated `dropDatabase` function has now been removed, without replacement. + - The deprecated `LegacyRootDatabaseService` type has now been removed. + - The return type from `DatabaseManager.forPlugin` is now directly a `DatabaseService`, as arguably expected. + - `DatabaseManager.forPlugin` now requires the `deps` argument, with the logger and lifecycle services. + + For the `/cache` subpath exports: + + - The `PluginCacheManager` type has been removed. You can still import it from `@backstage/backend-common`, but it's deprecated there, and you should move off of that package by migrating fully to the new backend system. + - Accordingly, `CacheManager.forPlugin` immediately returns a `CacheService` instead of a `PluginCacheManager`. The outcome of this is that you no longer need to make the extra `.getClient()` call. The old `CacheManager` with the old behavior still exists on `@backstage/backend-common`, but the above recommendations apply. + +### Patch Changes + +- 622360e: Move down the discovery config to be in the root +- fe6fd8c: Accept `ConfigService` instead of `Config` in constructors/factories +- 5705424: Wrap scheduled tasks from the scheduler core service now in OpenTelemetry spans +- b2a329d: Properly indent the config schema +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-app-api@0.10.0-next.1 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + +## @backstage/core-compat-api@0.3.0-next.1 + +### Minor Changes + +- 6db849e: **BREAKING**: The `namespace` parameter for API's is now defaulted to the `pluginId` which was discovered. This means that if you're overriding API's by using ID's directly, they might have changed to include the plugin ID too. + +### Patch Changes + +- c816e2d: Added support for new `FrontendPlugin` and `FrontendModule` types. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-plugin-api@1.9.3 + - @backstage/version-bridge@1.0.8 + +## @backstage/frontend-app-api@0.9.0-next.1 + +### Minor Changes + +- 7c80650: **BREAKING**: The `createSpecializedApp` function now creates a bare-bones app without any of the default app structure or APIs. To re-introduce this functionality if you need to use `createSpecializedApp` you can install the `app` plugin from `@backstage/plugin-app`. + + In addition, the `createApp` and `CreateAppFeatureLoader` exports are now deprecated as they are being moved to `@backstage/frontend-defaults`, which should be used instead. + +### Patch Changes + +- c816e2d: Added support for new `FrontendPlugin` and `FrontendModule` types. +- 948d431: Removing deprecated `namespace` parameter in favour of `pluginId` instead +- Updated dependencies + - @backstage/frontend-defaults@0.1.0-next.0 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + +## @backstage/frontend-defaults@0.1.0-next.0 + +### Minor Changes + +- 7c80650: Initial release of this package, which provides a default app setup through the `createApp` function. This replaces the existing `createApp` method from `@backstage/frontend-app-api`. + +### Patch Changes + +- 7d19cd5: Added a new `CreateAppOptions` type for the `createApp` options. +- 7d19cd5: Added `createPublicSignInApp`, used to creating apps for the public entry point. +- Updated dependencies + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/plugin-app@0.1.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/app-defaults@1.5.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/backend-app-api@0.10.0-next.1 + +### Patch Changes + +- c246372: Updated the error message for missing service dependencies to include the plugin and module IDs. +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + +## @backstage/backend-dynamic-feature-service@0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-app-api@0.10.0-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.25-next.1 + - @backstage/plugin-events-backend@0.3.12-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/backend-openapi-utils@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + +## @backstage/backend-plugin-api@0.9.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/backend-test-utils@0.6.0-next.1 + +### Patch Changes + +- 710f621: Added missing service mock for `mockServices.rootConfig.mock`, and fixed the definition of `mockServices.rootHttpRouter.factory` to not have a duplicate callback. +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-app-api@0.10.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/catalog-client@1.6.7-next.0 + +### Patch Changes + +- 1882cfe: Moved `getEntities` ordering to utilize database instead of having it inside catalog client + + Please note that the latest version of `@backstage/catalog-client` will not order the entities in the same way as before. This is because the ordering is now done in the database query instead of in the client. If you rely on the ordering of the entities, you may need to update your backend plugin or code to handle this change. + +- Updated dependencies + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + +## @backstage/cli@0.27.1-next.1 + +### Patch Changes + +- d2d2313: Add `config.d.ts` files to the list of included file in `tsconfig.json`. + + This allows ESLint to detect issues or deprecations in those files. + +- 97422b0: Update templates to not refer to backend-common + +- f865103: Updated dependency `esbuild` to `^0.23.0`. + +- 569c3f0: Fixed an issue where published frontend packages would end up with an invalid import structure if a single module imported both `.css` and `.svg` files. + +- Updated dependencies + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/eslint-plugin@0.1.8 + - @backstage/integration@1.14.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + +## @backstage/codemods@0.1.50-next.0 + +### Patch Changes + +- 0894166: Updated dependency `jscodeshift` to `^0.16.0`. +- Updated dependencies + - @backstage/cli-common@0.1.14 + +## @backstage/core-components@0.14.11-next.0 + +### Patch Changes + +- 06b8206: Added `titleComponent` prop to `SignInPage` component to allow further customization of the title using `ReactNode` +- Updated dependencies + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/version-bridge@1.0.8 + +## @backstage/create-app@0.5.19-next.1 + +### Patch Changes + +- d2d2313: Add `config.d.ts` files to the list of included file in `tsconfig.json`. + + This allows ESLint to detect issues or deprecations in those files. + +- 97422b0: Update templates to not refer to backend-common + +- bf370c2: Remove references to the `@backstage/backend-tasks` in versions of the `create-app` package, as it has been deprecated. + +- Updated dependencies + - @backstage/cli-common@0.1.14 + +## @backstage/dev-utils@1.0.38-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + +## @backstage/frontend-plugin-api@0.8.0-next.1 + +### Patch Changes + +- c816e2d: Added `createFrontendModule` as a replacement for `createExtensionOverrides`, which is now deprecated. + + Deprecated the `BackstagePlugin` and `FrontendFeature` type in favor of `FrontendPlugin` and `FrontendFeature` from `@backstage/frontend-app-api` respectively. + +- 52f9c5a: Deprecated the `namespace` option for `createExtensionBlueprint` and `createExtension`, these are no longer required and will default to the `pluginId` instead. + + You can migrate some of your extensions that use `createExtensionOverrides` to using `createFrontendModule` instead and providing a `pluginId` there. + + ```ts + // Before + createExtensionOverrides({ + extensions: [ + createExtension({ + name: 'my-extension', + namespace: 'my-namespace', + kind: 'test', + ... + }) + ], + }); + + // After + createFrontendModule({ + pluginId: 'my-namespace', + extensions: [ + createExtension({ + name: 'my-extension', + kind: 'test', + ... + }) + ], + }); + ``` + +- 948d431: Removing deprecated `namespace` parameter in favour of `pluginId` instead + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + +## @backstage/frontend-test-utils@0.2.0-next.1 + +### Patch Changes + +- 948d431: Removing deprecated `namespace` parameter in favour of `pluginId` instead +- Updated dependencies + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/plugin-app@0.1.0-next.1 + - @backstage/config@1.2.0 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/types@1.1.1 + +## @backstage/repo-tools@0.9.7-next.1 + +### Patch Changes + +- 5c4aa2f: Updated dependency `@useoptic/openapi-utilities` to `^0.55.0`. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + +## @techdocs/cli@1.8.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.11-next.1 + +## @backstage/plugin-api-docs@0.11.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-app@0.1.0-next.1 + +### Patch Changes + +- 52f9c5a: Deprecated the `namespace` option for `createExtensionBlueprint` and `createExtension`, these are no longer required and will default to the `pluginId` instead. + + You can migrate some of your extensions that use `createExtensionOverrides` to using `createFrontendModule` instead and providing a `pluginId` there. + + ```ts + // Before + createExtensionOverrides({ + extensions: [ + createExtension({ + name: 'my-extension', + namespace: 'my-namespace', + kind: 'test', + ... + }) + ], + }); + + // After + createFrontendModule({ + pluginId: 'my-namespace', + extensions: [ + createExtension({ + name: 'my-extension', + kind: 'test', + ... + }) + ], + }); + ``` + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-app-backend@0.3.74-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.25-next.1 + +## @backstage/plugin-app-node@0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config-loader@1.9.0 + +## @backstage/plugin-app-visualizer@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + +## @backstage/plugin-auth-backend@0.23.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-oidc-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.1.0-next.1 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.2.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.0-next.1 + +### Patch Changes + +- 8d1fb8d: Throw correct error when email is missing from the claims +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + - @backstage/plugin-auth-backend@0.23.0-next.1 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-google-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-auth-backend@0.23.0-next.1 + +## @backstage/plugin-auth-backend-module-okta-provider@0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + +## @backstage/plugin-auth-node@0.5.2-next.1 + +### Patch Changes + +- c46eb0f: Extend the "unable to resolve user identity" message +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-auth-react@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + +## @backstage/plugin-catalog@1.22.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + +## @backstage/plugin-catalog-backend@1.25.3-next.1 + +### Patch Changes + +- 1882cfe: Moved `getEntities` ordering to utilize database instead of having it inside catalog client + + Please note that the latest version of `@backstage/catalog-client` will not order the entities in the same way as before. This is because the ordering is now done in the database query instead of in the client. If you rely on the ordering of the entities, you may need to update your backend plugin or code to handle this change. + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.1 + +## @backstage/plugin-catalog-backend-module-aws@0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-kubernetes-common@0.8.2 + +## @backstage/plugin-catalog-backend-module-azure@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.18-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-kubernetes-common@0.8.2 + +## @backstage/plugin-catalog-backend-module-gerrit@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-github@0.7.3-next.1 + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.7.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.4.2-next.1 + +### Patch Changes + +- 53b24d9: Internal update to use the new cache manager +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.4.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-catalog-backend-module-ldap@0.9.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-logs@0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.6.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.4 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-catalog-graph@0.4.9-next.1 + +### Patch Changes + +- da91078: Fixed a bug in the `CatalogGraphPage` component where, after clicking on some nodes, clicking the back button would break the navigation. This issue caused the entire navigation to fail and behaved differently across various browsers. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-import@0.12.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-catalog-node@1.12.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + +## @backstage/plugin-catalog-react@1.12.4-next.1 + +### Patch Changes + +- ae9b6cb: Small internal fix to better work with recent `lodash` versions +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.8-next.0 + +### Patch Changes + +- 4f08c85: Show additional info on DevTools unprocessed entities table + + - Location path (so that it's easier to search the failed entity from the YAML URL) + - Time info of last discovery and next refresh time so that users can be aware of it and can sort the errors based on the time. + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + +## @backstage/plugin-config-schema@0.1.59-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## @backstage/plugin-devtools@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-react@0.4.25 + +## @backstage/plugin-devtools-backend@0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + +## @backstage/plugin-events-backend@0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-backend-module-azure@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-backend-module-gerrit@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-backend-module-github@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-backend-module-gitlab@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-backend-test-utils@0.1.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-events-node@0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + +## @backstage/plugin-home@0.7.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-home-react@0.1.17-next.0 + +## @backstage/plugin-home-react@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + +## @backstage/plugin-kubernetes@0.11.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.3-next.0 + +## @backstage/plugin-kubernetes-backend@0.18.6-next.1 + +### Patch Changes + +- ca96b66: Skip start without proper config +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-node@0.1.19-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + +## @backstage/plugin-kubernetes-cluster@0.0.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.3-next.0 + +## @backstage/plugin-kubernetes-node@0.1.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + +## @backstage/plugin-kubernetes-react@0.4.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + +## @backstage/plugin-notifications@0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-signals-react@0.0.5-next.0 + +## @backstage/plugin-notifications-backend@0.4.0-next.1 + +### Patch Changes + +- f195972: Validate notification link when new notification is created +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + +## @backstage/plugin-notifications-backend-module-email@0.3.0-next.1 + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.1 + +## @backstage/plugin-notifications-node@0.2.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-signals-node@0.1.11-next.1 + +## @backstage/plugin-org@0.6.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog-common@1.0.26 + +## @backstage/plugin-org-react@0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + +## @backstage/plugin-permission-backend@0.5.49-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + +## @backstage/plugin-permission-node@0.8.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + +## @backstage/plugin-proxy-backend@0.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder@1.25.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.1 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-scaffolder-backend@1.25.0-next.1 + +### Patch Changes + +- f865103: Updated dependency `esbuild` to `^0.23.0`. +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.1 + - @backstage/plugin-scaffolder-common@1.5.5 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.12-next.1 + +## @backstage/plugin-scaffolder-node@0.4.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-scaffolder-node-test-utils@0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-test-utils@0.6.0-next.1 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + +## @backstage/plugin-scaffolder-react@1.12.0-next.1 + +### Patch Changes + +- c2cbe1e: Updated dependency `use-immer` to `^0.10.0`. +- Updated dependencies + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + +## @backstage/plugin-search@1.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + +## @backstage/plugin-search-backend@1.5.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-openapi-utils@0.1.18-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-catalog@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-elasticsearch@1.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-explore@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-pg@0.5.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-backend-module-techdocs@0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-node@1.12.11-next.1 + +## @backstage/plugin-search-backend-node@1.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-search-react@1.8.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.14 + +## @backstage/plugin-signals@0.0.10-next.1 + +### Patch Changes + +- 3e9b1a4: Put a name on the `SignalsDisplay` component extension +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.5-next.0 + +## @backstage/plugin-signals-backend@0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + +## @backstage/plugin-signals-node@0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + +## @backstage/plugin-techdocs@1.10.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.6-next.0 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.38-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + +## @backstage/plugin-techdocs-backend@1.10.13-next.1 + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-node@1.12.11-next.1 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + +## @backstage/plugin-techdocs-node@1.12.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## @backstage/plugin-techdocs-react@1.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/version-bridge@1.0.8 + +## @backstage/plugin-user-settings@0.8.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.5-next.0 + - @backstage/plugin-user-settings-common@0.0.1 + +## @backstage/plugin-user-settings-backend@0.2.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + - @backstage/plugin-user-settings-common@0.0.1 + +## example-app@0.2.101-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.27.1-next.1 + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.1 + - @backstage/plugin-scaffolder-react@1.12.0-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.8-next.0 + - @backstage/plugin-signals@0.0.10-next.1 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-api-docs@0.11.9-next.1 + - @backstage/plugin-auth-react@0.1.6-next.0 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-import@0.12.3-next.1 + - @backstage/plugin-devtools@0.1.18-next.1 + - @backstage/plugin-home@0.7.10-next.1 + - @backstage/plugin-kubernetes@0.11.14-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.1 + - @backstage/plugin-notifications@0.3.1-next.1 + - @backstage/plugin-org@0.6.29-next.1 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder@1.25.0-next.1 + - @backstage/plugin-search@1.4.16-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + - @backstage/plugin-user-settings@0.8.12-next.1 + +## example-app-next@0.0.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.27.1-next.1 + - @backstage/frontend-defaults@0.1.0-next.0 + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/plugin-app@0.1.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.1 + - @backstage/plugin-scaffolder-react@1.12.0-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.8-next.0 + - @backstage/plugin-signals@0.0.10-next.1 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-api-docs@0.11.9-next.1 + - @backstage/plugin-app-visualizer@0.1.10-next.1 + - @backstage/plugin-auth-react@0.1.6-next.0 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-import@0.12.3-next.1 + - @backstage/plugin-home@0.7.10-next.1 + - @backstage/plugin-kubernetes@0.11.14-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.1 + - @backstage/plugin-notifications@0.3.1-next.1 + - @backstage/plugin-org@0.6.29-next.1 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder@1.25.0-next.1 + - @backstage/plugin-search@1.4.16-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + - @backstage/plugin-user-settings@0.8.12-next.1 + +## app-next-example-plugin@0.0.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + +## example-backend@0.0.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/plugin-scaffolder-backend@1.25.0-next.1 + - @backstage/plugin-notifications-backend@0.4.0-next.1 + - @backstage/plugin-kubernetes-backend@0.18.6-next.1 + - @backstage/plugin-techdocs-backend@1.10.13-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-app-backend@0.3.74-next.1 + - @backstage/plugin-auth-backend@0.23.0-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.0-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.1 + - @backstage/plugin-devtools-backend@0.4.0-next.1 + - @backstage/plugin-permission-backend@0.5.49-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-proxy-backend@0.5.6-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.1 + - @backstage/plugin-search-backend@1.5.17-next.1 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-signals-backend@0.2.0-next.1 + +## example-backend-legacy@0.2.102-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/plugin-scaffolder-backend@1.25.0-next.1 + - @backstage/plugin-kubernetes-backend@0.18.6-next.1 + - @backstage/plugin-techdocs-backend@1.10.13-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-app-backend@0.3.74-next.1 + - @backstage/plugin-auth-backend@0.23.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-devtools-backend@0.4.0-next.1 + - @backstage/plugin-events-backend@0.3.12-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-backend@0.5.49-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-proxy-backend@0.5.6-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.0-next.1 + - @backstage/plugin-search-backend@1.5.17-next.1 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.5.6-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.35-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-signals-backend@0.2.0-next.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + +## e2e-test@0.2.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.19-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/errors@1.2.4 + +## techdocs-cli-embedded-app@0.2.100-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.27.1-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/theme@0.5.6 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + +## @internal/plugin-todo-list@1.0.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + +## @internal/plugin-todo-list-backend@1.0.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 diff --git a/package.json b/package.json index fe87d18027..4028f7bacd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.31.0-next.0", + "version": "1.31.0-next.1", "private": true, "repository": { "type": "git", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index 72c172e395..449f249236 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/app-defaults +## 1.5.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-permission-react@0.4.25 + ## 1.5.10 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index ee21affccf..0e0cb11cbe 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/app-defaults", - "version": "1.5.10", + "version": "1.5.11-next.0", "description": "Provides the default wiring of a Backstage App", "backstage": { "role": "web-library" diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 2893155e97..68879cd7aa 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.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + ## 0.0.15-next.0 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index a923676370..89e2d353f6 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.15-next.0", + "version": "0.0.15-next.1", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 2ea7f13f2a..8ddcaad4b3 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,50 @@ # example-app-next +## 0.0.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.27.1-next.1 + - @backstage/frontend-defaults@0.1.0-next.0 + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/plugin-app@0.1.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.1 + - @backstage/plugin-scaffolder-react@1.12.0-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.8-next.0 + - @backstage/plugin-signals@0.0.10-next.1 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-api-docs@0.11.9-next.1 + - @backstage/plugin-app-visualizer@0.1.10-next.1 + - @backstage/plugin-auth-react@0.1.6-next.0 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-import@0.12.3-next.1 + - @backstage/plugin-home@0.7.10-next.1 + - @backstage/plugin-kubernetes@0.11.14-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.1 + - @backstage/plugin-notifications@0.3.1-next.1 + - @backstage/plugin-org@0.6.29-next.1 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder@1.25.0-next.1 + - @backstage/plugin-search@1.4.16-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + - @backstage/plugin-user-settings@0.8.12-next.1 + ## 0.0.15-next.0 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index e77c1f4f5f..397be2197a 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.15-next.0", + "version": "0.0.15-next.1", "private": true, "repository": { "type": "git", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 2cde44ff46..4cfd9ca1a8 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,46 @@ # example-app +## 0.2.101-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.27.1-next.1 + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-graph@0.4.9-next.1 + - @backstage/plugin-scaffolder-react@1.12.0-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.8-next.0 + - @backstage/plugin-signals@0.0.10-next.1 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-api-docs@0.11.9-next.1 + - @backstage/plugin-auth-react@0.1.6-next.0 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-import@0.12.3-next.1 + - @backstage/plugin-devtools@0.1.18-next.1 + - @backstage/plugin-home@0.7.10-next.1 + - @backstage/plugin-kubernetes@0.11.14-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.15-next.1 + - @backstage/plugin-notifications@0.3.1-next.1 + - @backstage/plugin-org@0.6.29-next.1 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder@1.25.0-next.1 + - @backstage/plugin-search@1.4.16-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.14-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + - @backstage/plugin-user-settings@0.8.12-next.1 + ## 0.2.101-next.0 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index cae6409f10..9d9431b6c1 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.101-next.0", + "version": "0.2.101-next.1", "backstage": { "role": "frontend" }, diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 662b85702f..9605e30ba3 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/backend-app-api +## 0.10.0-next.1 + +### Patch Changes + +- c246372: Updated the error message for missing service dependencies to include the plugin and module IDs. +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + ## 0.10.0-next.0 ### Minor Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 8168bae97f..6582a60515 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": "0.10.0-next.0", + "version": "0.10.0-next.1", "description": "Core API used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 3795ae2730..2a488efd04 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/backend-common +## 0.25.0-next.1 + +### Minor Changes + +- a4bac3c: **BREAKING**: You can no longer supply a `basePath` option to the host discovery implementation. In the new backend system, the ability to choose this path has been removed anyway at the plugin router level. +- 988c145: **BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. + + - The deprecated `dropDatabase` function has now been removed, without replacement. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + ## 0.25.0-next.0 ### Minor Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 74fa058cc0..1e052c8eba 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-common", - "version": "0.25.0-next.0", + "version": "0.25.0-next.1", "description": "Common functionality library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index dfa2373019..d9fa0e99f6 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/backend-defaults +## 0.5.0-next.1 + +### Minor Changes + +- a4bac3c: **BREAKING**: You can no longer supply a `basePath` option to the host discovery implementation. In the new backend system, the ability to choose this path has been removed anyway at the plugin router level. +- 055b75b: **BREAKING**: Simplifications and cleanup as part of the Backend System 1.0 work. + + For the `/database` subpath exports: + + - The deprecated `dropDatabase` function has now been removed, without replacement. + - The deprecated `LegacyRootDatabaseService` type has now been removed. + - The return type from `DatabaseManager.forPlugin` is now directly a `DatabaseService`, as arguably expected. + - `DatabaseManager.forPlugin` now requires the `deps` argument, with the logger and lifecycle services. + + For the `/cache` subpath exports: + + - The `PluginCacheManager` type has been removed. You can still import it from `@backstage/backend-common`, but it's deprecated there, and you should move off of that package by migrating fully to the new backend system. + - Accordingly, `CacheManager.forPlugin` immediately returns a `CacheService` instead of a `PluginCacheManager`. The outcome of this is that you no longer need to make the extra `.getClient()` call. The old `CacheManager` with the old behavior still exists on `@backstage/backend-common`, but the above recommendations apply. + +### Patch Changes + +- 622360e: Move down the discovery config to be in the root +- fe6fd8c: Accept `ConfigService` instead of `Config` in constructors/factories +- 5705424: Wrap scheduled tasks from the scheduler core service now in OpenTelemetry spans +- b2a329d: Properly indent the config schema +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-app-api@0.10.0-next.1 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + ## 0.5.0-next.0 ### Minor Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 60a7ce155c..2f157580bb 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.5.0-next.0", + "version": "0.5.0-next.1", "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 902f805fbd..e76ed0b504 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/backend-dynamic-feature-service +## 0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-app-api@0.10.0-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.25-next.1 + - @backstage/plugin-events-backend@0.3.12-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + ## 0.4.0-next.0 ### Minor Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 20ef7790f6..451a373650 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.4.0-next.0", + "version": "0.4.0-next.1", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-legacy/CHANGELOG.md b/packages/backend-legacy/CHANGELOG.md index df3dce4cf6..6a3068a37b 100644 --- a/packages/backend-legacy/CHANGELOG.md +++ b/packages/backend-legacy/CHANGELOG.md @@ -1,5 +1,47 @@ # example-backend-legacy +## 0.2.102-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/plugin-scaffolder-backend@1.25.0-next.1 + - @backstage/plugin-kubernetes-backend@0.18.6-next.1 + - @backstage/plugin-techdocs-backend@1.10.13-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-app-backend@0.3.74-next.1 + - @backstage/plugin-auth-backend@0.23.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-devtools-backend@0.4.0-next.1 + - @backstage/plugin-events-backend@0.3.12-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-backend@0.5.49-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-proxy-backend@0.5.6-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.5.0-next.1 + - @backstage/plugin-search-backend@1.5.17-next.1 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.5.6-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.35-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-signals-backend@0.2.0-next.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + ## 0.2.102-next.0 ### Patch Changes diff --git a/packages/backend-legacy/package.json b/packages/backend-legacy/package.json index 8d2eb61566..65e5a46f28 100644 --- a/packages/backend-legacy/package.json +++ b/packages/backend-legacy/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-legacy", - "version": "0.2.102-next.0", + "version": "0.2.102-next.1", "backstage": { "role": "backend" }, diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 1393b93790..3d62a9d121 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/backend-openapi-utils +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + ## 0.1.18-next.0 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 2ae545af9f..37ae412be6 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-openapi-utils", "description": "OpenAPI typescript support.", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 2356b1714d..5fdf43aaa6 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/backend-plugin-api +## 0.9.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + ## 0.9.0-next.0 ### Minor Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 6a3392529d..322c936b39 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": "0.9.0-next.0", + "version": "0.9.0-next.1", "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 49a2c44ddf..28ee376101 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/backend-test-utils +## 0.6.0-next.1 + +### Patch Changes + +- 710f621: Added missing service mock for `mockServices.rootConfig.mock`, and fixed the definition of `mockServices.rootHttpRouter.factory` to not have a duplicate callback. +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-app-api@0.10.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.6.0-next.0 ### Minor Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 680a7b9c2c..a84aca9b52 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "0.6.0-next.0", + "version": "0.6.0-next.1", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index ccb3825cd2..e5555f42a0 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,41 @@ # example-backend +## 0.0.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/plugin-scaffolder-backend@1.25.0-next.1 + - @backstage/plugin-notifications-backend@0.4.0-next.1 + - @backstage/plugin-kubernetes-backend@0.18.6-next.1 + - @backstage/plugin-techdocs-backend@1.10.13-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-app-backend@0.3.74-next.1 + - @backstage/plugin-auth-backend@0.23.0-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.0-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.5.0-next.1 + - @backstage/plugin-devtools-backend@0.4.0-next.1 + - @backstage/plugin-permission-backend@0.5.49-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-proxy-backend@0.5.6-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.1 + - @backstage/plugin-search-backend@1.5.17-next.1 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.1 + - @backstage/plugin-search-backend-module-explore@0.2.2-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-signals-backend@0.2.0-next.1 + ## 0.0.30-next.0 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 630a7b98e3..7f81417b31 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.30-next.0", + "version": "0.0.30-next.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 9ed8a50b91..cf175a3d23 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/catalog-client +## 1.6.7-next.0 + +### Patch Changes + +- 1882cfe: Moved `getEntities` ordering to utilize database instead of having it inside catalog client + + Please note that the latest version of `@backstage/catalog-client` will not order the entities in the same way as before. This is because the ordering is now done in the database query instead of in the client. If you rely on the ordering of the entities, you may need to update your backend plugin or code to handle this change. + +- Updated dependencies + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + ## 1.6.6 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 29c1d3049c..b0568e0b0d 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "1.6.6", + "version": "1.6.7-next.0", "description": "An isomorphic client for the catalog backend", "backstage": { "role": "common-library" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index c2a17a5b93..05a9574f3a 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/cli +## 0.27.1-next.1 + +### Patch Changes + +- d2d2313: Add `config.d.ts` files to the list of included file in `tsconfig.json`. + + This allows ESLint to detect issues or deprecations in those files. + +- 97422b0: Update templates to not refer to backend-common +- f865103: Updated dependency `esbuild` to `^0.23.0`. +- 569c3f0: Fixed an issue where published frontend packages would end up with an invalid import structure if a single module imported both `.css` and `.svg` files. +- Updated dependencies + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/eslint-plugin@0.1.8 + - @backstage/integration@1.14.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + ## 0.27.1-next.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 89230c13b2..31cfd3747e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.27.1-next.0", + "version": "0.27.1-next.1", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index 2a47e34b54..c93a71fe38 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/codemods +## 0.1.50-next.0 + +### Patch Changes + +- 0894166: Updated dependency `jscodeshift` to `^0.16.0`. +- Updated dependencies + - @backstage/cli-common@0.1.14 + ## 0.1.49 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index d8c759c34e..38d32dfc74 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/codemods", - "version": "0.1.49", + "version": "0.1.50-next.0", "description": "A collection of codemods for Backstage projects", "backstage": { "role": "cli" diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index 4e4047863c..62a77898bd 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/core-compat-api +## 0.3.0-next.1 + +### Minor Changes + +- 6db849e: **BREAKING**: The `namespace` parameter for API's is now defaulted to the `pluginId` which was discovered. This means that if you're overriding API's by using ID's directly, they might have changed to include the plugin ID too. + +### Patch Changes + +- c816e2d: Added support for new `FrontendPlugin` and `FrontendModule` types. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-plugin-api@1.9.3 + - @backstage/version-bridge@1.0.8 + ## 0.2.9-next.0 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 7cb411855f..a54cb8f20f 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.2.9-next.0", + "version": "0.3.0-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index f50b830702..72fe6e6692 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/core-components +## 0.14.11-next.0 + +### Patch Changes + +- 06b8206: Added `titleComponent` prop to `SignInPage` component to allow further customization of the title using `ReactNode` +- Updated dependencies + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/version-bridge@1.0.8 + ## 0.14.10 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 154331d70d..ba0634bfe9 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.14.10", + "version": "0.14.11-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 2b8b261e4d..1ab0f13fd1 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/create-app +## 0.5.19-next.1 + +### Patch Changes + +- d2d2313: Add `config.d.ts` files to the list of included file in `tsconfig.json`. + + This allows ESLint to detect issues or deprecations in those files. + +- 97422b0: Update templates to not refer to backend-common +- bf370c2: Remove references to the `@backstage/backend-tasks` in versions of the `create-app` package, as it has been deprecated. +- Updated dependencies + - @backstage/cli-common@0.1.14 + ## 0.5.19-next.0 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 3fb0d4ffa8..0fad58446e 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.5.19-next.0", + "version": "0.5.19-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 4b2e3fbced..e71ef37738 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.0.38-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + ## 1.0.38-next.0 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 45b32babf4..71a4de45ca 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.0.38-next.0", + "version": "1.0.38-next.1", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index bec3eb9e36..f553c6e450 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.19-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/errors@1.2.4 + ## 0.2.20-next.0 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index d9dae4270c..db0b649123 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.2.20-next.0", + "version": "0.2.20-next.1", "private": true, "backstage": { "role": "cli" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 878cebea2a..9596e51ee3 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/frontend-app-api +## 0.9.0-next.1 + +### Minor Changes + +- 7c80650: **BREAKING**: The `createSpecializedApp` function now creates a bare-bones app without any of the default app structure or APIs. To re-introduce this functionality if you need to use `createSpecializedApp` you can install the `app` plugin from `@backstage/plugin-app`. + + In addition, the `createApp` and `CreateAppFeatureLoader` exports are now deprecated as they are being moved to `@backstage/frontend-defaults`, which should be used instead. + +### Patch Changes + +- c816e2d: Added support for new `FrontendPlugin` and `FrontendModule` types. +- 948d431: Removing deprecated `namespace` parameter in favour of `pluginId` instead +- Updated dependencies + - @backstage/frontend-defaults@0.1.0-next.0 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + ## 0.9.0-next.0 ### Minor Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 52078a4ff7..140266eae6 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.9.0-next.0", + "version": "0.9.0-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md new file mode 100644 index 0000000000..79ea0d79eb --- /dev/null +++ b/packages/frontend-defaults/CHANGELOG.md @@ -0,0 +1,18 @@ +# @backstage/frontend-defaults + +## 0.1.0-next.0 + +### Minor Changes + +- 7c80650: Initial release of this package, which provides a default app setup through the `createApp` function. This replaces the existing `createApp` method from `@backstage/frontend-app-api`. + +### Patch Changes + +- 7d19cd5: Added a new `CreateAppOptions` type for the `createApp` options. +- 7d19cd5: Added `createPublicSignInApp`, used to creating apps for the public entry point. +- Updated dependencies + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/plugin-app@0.1.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index 745c19e298..7f00371525 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.0.0", + "version": "0.1.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index ff8776070b..e16c08ac7a 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,50 @@ # @backstage/frontend-plugin-api +## 0.8.0-next.1 + +### Patch Changes + +- c816e2d: Added `createFrontendModule` as a replacement for `createExtensionOverrides`, which is now deprecated. + + Deprecated the `BackstagePlugin` and `FrontendFeature` type in favor of `FrontendPlugin` and `FrontendFeature` from `@backstage/frontend-app-api` respectively. + +- 52f9c5a: Deprecated the `namespace` option for `createExtensionBlueprint` and `createExtension`, these are no longer required and will default to the `pluginId` instead. + + You can migrate some of your extensions that use `createExtensionOverrides` to using `createFrontendModule` instead and providing a `pluginId` there. + + ```ts + // Before + createExtensionOverrides({ + extensions: [ + createExtension({ + name: 'my-extension', + namespace: 'my-namespace', + kind: 'test', + ... + }) + ], + }); + + // After + createFrontendModule({ + pluginId: 'my-namespace', + extensions: [ + createExtension({ + name: 'my-extension', + kind: 'test', + ... + }) + ], + }); + ``` + +- 948d431: Removing deprecated `namespace` parameter in favour of `pluginId` instead +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + ## 0.8.0-next.0 ### Minor Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 540a8b54fe..55168aed65 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.8.0-next.0", + "version": "0.8.0-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 04cc025a74..fdc40b60a8 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.0-next.1 + +### Patch Changes + +- 948d431: Removing deprecated `namespace` parameter in favour of `pluginId` instead +- Updated dependencies + - @backstage/frontend-app-api@0.9.0-next.1 + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/plugin-app@0.1.0-next.1 + - @backstage/config@1.2.0 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/types@1.1.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index a84d296c55..988ce42822 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.0-next.0", + "version": "0.2.0-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 228886efc1..7ffae42333 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/repo-tools +## 0.9.7-next.1 + +### Patch Changes + +- 5c4aa2f: Updated dependency `@useoptic/openapi-utilities` to `^0.55.0`. +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/cli-node@0.2.7 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + ## 0.9.7-next.0 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index cff6c7b7dc..89b2f0f124 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/repo-tools", "description": "CLI for Backstage repo tooling ", - "version": "0.9.7-next.0", + "version": "0.9.7-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index ea42ae4cc0..77a6e403bd 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.100-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.27.1-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/app-defaults@1.5.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/theme@0.5.6 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + ## 0.2.100-next.0 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 54844b882d..83bc610762 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.100-next.0", + "version": "0.2.100-next.1", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index c3b4be91f8..a7c62bd94b 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.8.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.11-next.1 + ## 1.8.19-next.0 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index c0af6ceaed..64a6ebc772 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.8.19-next.0", + "version": "1.8.19-next.1", "publishConfig": { "access": "public" }, diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 7c67bea51e..f75835f0a6 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-api-docs +## 0.11.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + ## 0.11.9-next.0 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 7f27a4c6a2..3a102a70e5 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.11.9-next.0", + "version": "0.11.9-next.1", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index c7e709c1ec..a350fb994e 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-app-backend +## 0.3.74-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.25-next.1 + ## 0.3.74-next.0 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 90c85f8b63..4f438a7d9b 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.3.74-next.0", + "version": "0.3.74-next.1", "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 53a3d3967e..feae31a8ea 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-node +## 0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config-loader@1.9.0 + ## 0.1.25-next.0 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 5cab91886f..33af5bfba1 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.25-next.0", + "version": "0.1.25-next.1", "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 44ec74ddd1..2afc4a1651 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-visualizer +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 54a8adfbd7..3e24482eda 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.10-next.0", + "version": "0.1.10-next.1", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index c7523808e5..2ff8607a13 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-app +## 0.1.0-next.1 + +### Patch Changes + +- 52f9c5a: Deprecated the `namespace` option for `createExtensionBlueprint` and `createExtension`, these are no longer required and will default to the `pluginId` instead. + + You can migrate some of your extensions that use `createExtensionOverrides` to using `createFrontendModule` instead and providing a `pluginId` there. + + ```ts + // Before + createExtensionOverrides({ + extensions: [ + createExtension({ + name: 'my-extension', + namespace: 'my-namespace', + kind: 'test', + ... + }) + ], + }); + + // After + createFrontendModule({ + pluginId: 'my-namespace', + extensions: [ + createExtension({ + name: 'my-extension', + kind: 'test', + ... + }) + ], + }); + ``` + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-permission-react@0.4.25 + ## 0.1.0-next.0 ### Minor Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index b6289ca5a2..5912cc3781 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.1.0-next.0", + "version": "0.1.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 1f053979f2..294ab9204d 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.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index cbdf2da557..105e69150b 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.3.0-next.0", + "version": "0.3.0-next.1", "description": "The atlassian-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 fc3193783b..ef6008aa90 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.2.0-next.1 + +### Patch Changes + +- 8d1fb8d: Throw correct error when email is missing from the claims +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + - @backstage/plugin-auth-backend@0.23.0-next.1 + ## 0.2.0-next.0 ### 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 d4df833840..de5fb31298 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.2.0-next.0", + "version": "0.2.0-next.1", "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 f9f72069bb..cb1f2da1fb 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-azure-easyauth-provider/package.json b/plugins/auth-backend-module-azure-easyauth-provider/package.json index 920efa816f..9688d88308 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.0-next.0", + "version": "0.2.0-next.1", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index 16f7b29526..92484e03fe 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.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index 17869169d1..e2385ca6ed 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.2.0-next.0", + "version": "0.2.0-next.1", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index 28f18baf79..1ed55e9d69 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.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index 7f5c8b97f4..b40928a4bf 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.3.0-next.0", + "version": "0.3.0-next.1", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index ea27e5f2dc..32de67cf73 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.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.3.0-next.0 ### 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 9ae551c9ce..94e52df683 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.3.0-next.0", + "version": "0.3.0-next.1", "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 cab470870e..b898b8b23f 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.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 75b6b39f0a..08e3acaf8f 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.2.0-next.0", + "version": "0.2.0-next.1", "description": "The github-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index cc50ac6618..8cf6bbc41e 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.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index 6fa019c9d2..dc562c16e8 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.2.0-next.0", + "version": "0.2.0-next.1", "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 3bfd7f2feb..72c422b3bc 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.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index f81459cf1f..3f4305d8e5 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.2.0-next.0", + "version": "0.2.0-next.1", "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 9c01cc51b4..69c7e94a3e 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json index 70ee3926da..d46324f4de 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.0-next.0", + "version": "0.2.0-next.1", "description": "The guest-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index 0a50d8c7a6..094eee92a8 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.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index bd956fca14..1a81038994 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.2.0-next.0", + "version": "0.2.0-next.1", "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 590aa37742..1e91fac858 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.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index ce8cab68c3..f5ad94f1b8 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.3.0-next.0", + "version": "0.3.0-next.1", "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 47ad278b68..240a3853f2 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json index 63f13f5844..9dce1fec17 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.0-next.0", + "version": "0.2.0-next.1", "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 51ac80e309..3b087761d0 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-auth-backend@0.23.0-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index bf7091547b..1a1d6c9c8a 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.3.0-next.0", + "version": "0.3.0-next.1", "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 c588e5b98d..08d2ef1f2f 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.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.1.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 1659bc3824..0965e1548d 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.1.0-next.0", + "version": "0.1.0-next.1", "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 8ac77804b1..796813e0a3 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.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index bd3e332633..53e8e646ca 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.2.0-next.0", + "version": "0.2.0-next.1", "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 5fb39c0962..ebbff3416d 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index 88612c827b..932ad93b38 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.2.0-next.0", + "version": "0.2.0-next.1", "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 3083f74262..8b6540ddf3 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.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + ## 0.3.0-next.0 ### 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 62330f1bed..2f198ebacb 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.3.0-next.0", + "version": "0.3.0-next.1", "description": "The vmware-cloud-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 2e28c0c410..4244638abd 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-auth-backend +## 0.23.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.0-next.1 + - @backstage/plugin-auth-backend-module-oidc-provider@0.3.0-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.1.0-next.1 + - @backstage/plugin-auth-backend-module-onelogin-provider@0.2.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.23.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 177478fdb9..f090b86f4e 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.23.0-next.0", + "version": "0.23.0-next.1", "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 c3773c052e..4565a93cf7 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-auth-node +## 0.5.2-next.1 + +### Patch Changes + +- c46eb0f: Extend the "unable to resolve user identity" message +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.5.2-next.0 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 82ffbc4c7a..23818fcc31 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.5.2-next.0", + "version": "0.5.2-next.1", "backstage": { "role": "node-library", "pluginId": "auth", diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index f88d6210a8..2aac588abe 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-react +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + ## 0.1.5 ### Patch Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index ae534e89d9..2f76c289b3 100644 --- a/plugins/auth-react/package.json +++ b/plugins/auth-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-react", - "version": "0.1.5", + "version": "0.1.6-next.0", "description": "Web library for the auth plugin", "backstage": { "role": "web-library", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 5c09b5789a..aea8617e9b 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.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-kubernetes-common@0.8.2 + ## 0.4.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 66afec1f99..1d9891ade3 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.2-next.0", + "version": "0.4.2-next.1", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 2f750f48e8..4215a5a156 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.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 8d27517cf5..ce62913fe5 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.2.2-next.0", + "version": "0.2.2-next.1", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index 65d193523b..c04f478566 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.18-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.4.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index 97517b0b43..54de9a7b3b 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.0-next.0", + "version": "0.4.0-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 27ee19cb99..8def9570bf 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.3.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 59afec4970..04de284c56 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.3.2-next.0", + "version": "0.3.2-next.1", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 1260480994..e45ac208c4 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.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 58719409ce..ea2c30f112 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.2.2-next.0", + "version": "0.2.2-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 606ead08be..6af3647bd9 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-kubernetes-common@0.8.2 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index d48a43ad9a..4441a7ca8b 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.0-next.0", + "version": "0.3.0-next.1", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index d19ce42b01..abbb586446 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.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index fa395efa22..2937b47db0 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.2-next.0", + "version": "0.2.2-next.1", "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 db1b68c801..5ade859371 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.7.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index b738010b4a..37d2804e60 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.0-next.0", + "version": "0.3.0-next.1", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 502bc5879e..b956c2e9e6 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-github +## 0.7.3-next.1 + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.7.3-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index adc448cf27..16af5a72c5 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.3-next.0", + "version": "0.7.3-next.1", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 321c7e1732..a96c08f602 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.4.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index a97b1bffb1..563ae32df9 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.0-next.0", + "version": "0.2.0-next.1", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 6740abb6a6..2022e3e511 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.4.2-next.1 + +### Patch Changes + +- 53b24d9: Internal update to use the new cache manager +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.4.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 4a890cf3bd..c99443a5d9 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.4.2-next.0", + "version": "0.4.2-next.1", "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 80bbdc5e7d..2b01b76534 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + ## 0.5.3-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 644a533b6b..fe7370ac8f 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.5.3-next.0", + "version": "0.5.3-next.1", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 7255cb6b68..f48ab34ba4 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.9.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.9.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 653104dfd1..c3e0f39e9f 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.9.0-next.0", + "version": "0.9.0-next.1", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index f8d48f5f68..05d129c042 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.1.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index 412ffe60d3..0d4bbf6e5d 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.0-next.0", + "version": "0.1.0-next.1", "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 5424f8e00e..9cc7e6207b 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.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.6.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 49a0813462..cc8fc5abc8 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.2-next.0", + "version": "0.6.2-next.1", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 95bd5f212e..b1e1c3308c 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-catalog-backend@1.25.3-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index cf5dd7a52f..f93f1e8119 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.0-next.0", + "version": "0.2.0-next.1", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 49a82b1575..cfcf1acec3 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.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 83913a20b7..e9f322e4c1 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.2-next.0", + "version": "0.2.2-next.1", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index 4fbe07a9a4..d6e8c54549 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index 444a9ab2af..3855e64192 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.0-next.0", + "version": "0.2.0-next.1", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index a606984ef1..401f93077d 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.4 + - @backstage/plugin-permission-common@0.8.1 + ## 0.5.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index f6f1154917..78a9c345ce 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.0-next.0", + "version": "0.5.0-next.1", "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 6128d697e3..d2987d1182 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend +## 1.25.3-next.1 + +### Patch Changes + +- 1882cfe: Moved `getEntities` ordering to utilize database instead of having it inside catalog client + + Please note that the latest version of `@backstage/catalog-client` will not order the entities in the same way as before. This is because the ordering is now done in the database query instead of in the client. If you rely on the ordering of the entities, you may need to update your backend plugin or code to handle this change. + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-openapi-utils@0.1.18-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-search-backend-module-catalog@0.2.2-next.1 + ## 1.25.3-next.0 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 6261cacf20..e3a09b65ec 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "1.25.3-next.0", + "version": "1.25.3-next.1", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 8a7784877e..dd6cfdaf24 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-graph +## 0.4.9-next.1 + +### Patch Changes + +- da91078: Fixed a bug in the `CatalogGraphPage` component where, after clicking on some nodes, clicking the back button would break the navigation. This issue caused the entire navigation to fail and behaved differently across various browsers. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/types@1.1.1 + ## 0.4.9-next.0 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 9c59eb1b08..74b9054492 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.9-next.0", + "version": "0.4.9-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index c5dcfd4e11..0456c5ee24 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.12.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.12.3-next.0 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index e0e7056e47..32e183e7d9 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.3-next.0", + "version": "0.12.3-next.1", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 89ed287403..b4472333fd 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-node +## 1.12.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + ## 1.12.7-next.0 ### Patch Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index 20124ceca5..25f8d699ab 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "1.12.7-next.0", + "version": "1.12.7-next.1", "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 7f01541057..5027678cbe 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-react +## 1.12.4-next.1 + +### Patch Changes + +- ae9b6cb: Small internal fix to better work with recent `lodash` versions +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-react@0.4.25 + ## 1.12.4-next.0 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 33f7772e81..ba4aff5c42 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.12.4-next.0", + "version": "1.12.4-next.1", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index 5b43a660d5..74cab9fbfb 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.2.8-next.0 + +### Patch Changes + +- 4f08c85: Show additional info on DevTools unprocessed entities table + + - Location path (so that it's easier to search the failed entity from the YAML URL) + - Time info of last discovery and next refresh time so that users can be aware of it and can sort the errors based on the time. + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + ## 0.2.7 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index a823129f9d..ed3c14655e 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities", - "version": "0.2.7", + "version": "0.2.8-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-unprocessed-entities", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 3d5de77a6b..5aa877ad13 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog +## 1.22.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + ## 1.22.1-next.0 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 2212eb037d..50e897be8d 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.22.1-next.0", + "version": "1.22.1-next.1", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 28d63f63d7..d97d6b764d 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-config-schema +## 0.1.59-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + ## 0.1.58 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 7fd576a85f..614b80a190 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-config-schema", - "version": "0.1.58", + "version": "0.1.59-next.0", "description": "A Backstage plugin that lets you browse the configuration schema of your app", "backstage": { "role": "frontend-plugin", diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index 2774ae1b34..1f7e3996bb 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-devtools-backend +## 0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/config-loader@1.9.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + ## 0.4.0-next.0 ### Minor Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index cfe0e1d1f5..4bde07d5f7 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.4.0-next.0", + "version": "0.4.0-next.1", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index 0d9dfcc1b2..4fa8e6b609 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-devtools +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/plugin-devtools-common@0.1.12 + - @backstage/plugin-permission-react@0.4.25 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 22c0b3eddb..19ec9d93c6 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "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 52cf45ae2c..46182e1880 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.4.2-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 2354c1fb79..d84cb7d717 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.2-next.0", + "version": "0.4.2-next.1", "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 ad86906d1c..73e6a9c135 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.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 11202a486e..7fe1c40108 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.11-next.0", + "version": "0.2.11-next.1", "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 bd3a5dd34c..594e9a85dd 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.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 28a917c763..25df31c5fc 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.11-next.0", + "version": "0.2.11-next.1", "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 b2b9aa139f..f8b842415c 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.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 7bd829e02e..3d1358f055 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.11-next.0", + "version": "0.2.11-next.1", "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 9ad3204050..9f7b6ae04b 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.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index 3c08b10c89..877840e806 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.11-next.0", + "version": "0.2.11-next.1", "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 c4faca4da4..4231baf2a8 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.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index 5d287fb864..86a988074a 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.11-next.0", + "version": "0.2.11-next.1", "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 b9c612b329..5dafcc9f52 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.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.1.35-next.0 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index aae188ef7a..b174bc989e 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.35-next.0", + "version": "0.1.35-next.1", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", "backstage": { "role": "node-library", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index f8fbf82dc7..14fa485178 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend +## 0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.3.12-next.0 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index b1b586ecfa..ee057c91fd 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.3.12-next.0", + "version": "0.3.12-next.1", "backstage": { "role": "backend-plugin", "pluginId": "events", diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 3dfcf3c9e9..15934678d6 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-node +## 0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + ## 0.4.0-next.0 ### Minor Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 435f2f6667..ce31730b78 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.0-next.0", + "version": "0.4.0-next.1", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index ea9a0bca3a..3362c91a79 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @internal/plugin-todo-list-backend +## 1.0.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/errors@1.2.4 + ## 1.0.31-next.0 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index dd6f2f74ff..cf6e61f6ab 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.31-next.0", + "version": "1.0.31-next.1", "backstage": { "role": "backend-plugin", "pluginId": "todo-list", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index c5ccfbcd54..b31862e9b3 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-todo-list +## 1.0.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + ## 1.0.30 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index eb13f52c6b..df2fe42a29 100644 --- a/plugins/example-todo-list/package.json +++ b/plugins/example-todo-list/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list", - "version": "1.0.30", + "version": "1.0.31-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "todo-list", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index 87ff61daf0..41a71ed633 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-home-react +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + ## 0.1.16 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index e720402766..1af7a32d86 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home-react", - "version": "0.1.16", + "version": "0.1.17-next.0", "description": "A Backstage plugin that contains react components helps you build a home page", "backstage": { "role": "web-library", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 4d412eae76..51c5abe7dd 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-home +## 0.7.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-home-react@0.1.17-next.0 + ## 0.7.10-next.0 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index c7ef7b3862..7204942d52 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.7.10-next.0", + "version": "0.7.10-next.1", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 3e81a64b08..33447a6be2 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-kubernetes-backend +## 0.18.6-next.1 + +### Patch Changes + +- ca96b66: Skip start without proper config +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-node@0.1.19-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + ## 0.18.6-next.0 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 1401870767..dc42a2b7b2 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.18.6-next.0", + "version": "0.18.6-next.1", "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 8fedd3d637..e5f1848841 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.3-next.0 + ## 0.0.15-next.0 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 65e475675b..29c933be61 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.15-next.0", + "version": "0.0.15-next.1", "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 7b687e0fa7..8259430ff5 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-node +## 0.1.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 2c998e9802..cc6aa3dd65 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.1.19-next.0", + "version": "0.1.19-next.1", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index d76b454a4b..747360d3e4 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-react +## 0.4.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.8.2 + ## 0.4.2 ### Patch Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 936231da7c..8d60446945 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-react", - "version": "0.4.2", + "version": "0.4.3-next.0", "description": "Web library for the kubernetes-react plugin", "backstage": { "role": "web-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 4ff750f743..07a229d934 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-kubernetes +## 0.11.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-kubernetes-common@0.8.2 + - @backstage/plugin-kubernetes-react@0.4.3-next.0 + ## 0.11.14-next.0 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 238155d940..9020737fbd 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.11.14-next.0", + "version": "0.11.14-next.1", "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 33d1df94c1..113c7b06b4 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.0-next.1 + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index 228946c016..9b910b0041 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.0-next.0", + "version": "0.3.0-next.1", "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 081048900f..b74489313d 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-notifications-backend +## 0.4.0-next.1 + +### Patch Changes + +- f195972: Validate notification link when new notification is created +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + ## 0.4.0-next.0 ### Minor Changes diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index fecd053bec..d91f0f7aea 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.4.0-next.0", + "version": "0.4.0-next.1", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index e1d48a7d88..7066316a04 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-notifications-node +## 0.2.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-signals-node@0.1.11-next.1 + ## 0.2.6-next.0 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index af3fdcde9b..09008239ae 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.6-next.0", + "version": "0.2.6-next.1", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index 19cd62732a..945000c468 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-notifications +## 0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-signals-react@0.0.5-next.0 + ## 0.3.1-next.0 ### Patch Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index ba211d21ea..7dd72d0d97 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.3.1-next.0", + "version": "0.3.1-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index f2b8ca2f81..034384a4be 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + ## 0.1.28-next.0 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 3120392573..b0e983b324 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.28-next.0", + "version": "0.1.28-next.1", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 8b98dd83e2..a273d086a1 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-org +## 0.6.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-catalog-common@1.0.26 + ## 0.6.29-next.0 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 94c3a7fdc3..93957e54e1 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.29-next.0", + "version": "0.6.29-next.1", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 32c768972b..1534a2d605 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index 7ee62b6e91..866e2fe744 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.0-next.0", + "version": "0.2.0-next.1", "description": "Allow all policy backend module for the permission plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 2733e1a634..a1008cf37c 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-permission-backend +## 0.5.49-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + ## 0.5.49-next.0 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 42d1ce3dae..282af7cc0b 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.49-next.0", + "version": "0.5.49-next.1", "backstage": { "role": "backend-plugin", "pluginId": "permission", diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index 9b99387c7f..729f32104f 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-node +## 0.8.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + ## 0.8.3-next.0 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 2922888b7e..0323950376 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.3-next.0", + "version": "0.8.3-next.1", "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 2038b4a7ea..07cd16bbdd 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-proxy-backend +## 0.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + ## 0.5.6-next.0 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 4638be55b1..c23ea0fca4 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.6-next.0", + "version": "0.5.6-next.1", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index 1c82a5bba1..f3bb21a181 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index eb4fb7e935..d76df5f0eb 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.0-next.0", + "version": "0.2.0-next.1", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index a305262a6d..391eaed947 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index dbbb2082e6..316dcc867c 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.0-next.0", + "version": "0.2.0-next.1", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index e2067f2b96..5bbe594290 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 6305efa7c1..a6650512e0 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.0-next.0", + "version": "0.2.0-next.1", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index b628eb3671..e68474476c 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index 81d34dcf50..56596b59bc 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.0-next.0", + "version": "0.3.0-next.1", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index d0861cf549..9c945e4faf 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index 8766ae63e3..05383eb557 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.0-next.0", + "version": "0.3.0-next.1", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 2097f650ab..e41bf64fdc 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 2c41ca4e8d..48a106d5db 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.0-next.0", + "version": "0.3.0-next.1", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index b255527bdd..4eaf48a537 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 8151f984c6..f0a5114dc0 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.0-next.0", + "version": "0.2.0-next.1", "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 33b0cbdfe6..1dd8dff942 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index c00e285e83..b09b9101b9 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.0-next.0", + "version": "0.2.0-next.1", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index 117b3ea78e..f3474b406e 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 279a596d79..3269b54a8d 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.0-next.0", + "version": "0.2.0-next.1", "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 cb52e9e482..5c93531f01 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.5.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index ab4f129594..2664a1a667 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.0-next.0", + "version": "0.5.0-next.1", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index cbe0e36df6..c8875fe3ea 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.5.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 51d7903cb1..6b711c0f5f 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.5.0-next.0", + "version": "0.5.0-next.1", "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 c6361b7e69..4a3d53c896 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.1.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index 8ec318432f..65b8da39dd 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.0-next.0", + "version": "0.1.0-next.1", "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 ddb4825d82..651ad36d61 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.5.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 64241de844..09e0f0dc93 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.0-next.0", + "version": "0.5.0-next.1", "description": "A module for the scaffolder backend that lets you template projects using Rails", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 17979b55b1..0a98cd391f 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 4f9da15018..f0935fbb05 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.0-next.0", + "version": "0.2.0-next.1", "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 0924d440b5..35f01d25c6 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.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.1.12-next.1 + ## 0.4.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 910af87192..6a5599f6a4 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.0-next.0", + "version": "0.4.0-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index a780a9421e..36f4facf93 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-scaffolder-backend +## 1.25.0-next.1 + +### Patch Changes + +- f865103: Updated dependency `esbuild` to `^0.23.0`. +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.22 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.0-next.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.0-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.5.0-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.5.0-next.1 + - @backstage/plugin-scaffolder-common@1.5.5 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 1.25.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 783f1b7496..4017dc760b 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "1.25.0-next.0", + "version": "1.25.0-next.1", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index a376071bdb..fef8549682 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-test-utils@0.6.0-next.1 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-node@0.4.11-next.1 + ## 0.1.12-next.0 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index b9102e03fc..4b32bf3d95 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.12-next.0", + "version": "0.1.12-next.1", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index aa235c19ec..887fac33d1 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-node +## 0.4.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 0.4.11-next.0 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 32c3f042a9..42291dd081 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.4.11-next.0", + "version": "0.4.11-next.1", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 501c8417ff..991cc8d10e 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-scaffolder-react +## 1.12.0-next.1 + +### Patch Changes + +- c2cbe1e: Updated dependency `use-immer` to `^0.10.0`. +- Updated dependencies + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 1.12.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 4be94c4634..d952986e6e 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.12.0-next.0", + "version": "1.12.0-next.1", "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 8f2aaf80bc..d3080d6edc 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-scaffolder +## 1.25.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-scaffolder-react@1.12.0-next.1 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-permission-react@0.4.25 + - @backstage/plugin-scaffolder-common@1.5.5 + ## 1.25.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 5b558ee232..2e75d776d2 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.25.0-next.0", + "version": "1.25.0-next.1", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index da431dadcb..89de3f2ba5 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-search-backend-module-catalog +## 0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index ea216c5033..6bc3cfd753 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.2.2-next.0", + "version": "0.2.2-next.1", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index 65cf4bb263..ef70474a2e 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + ## 1.5.6-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 969152c536..670f10a5f3 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.5.6-next.0", + "version": "1.5.6-next.1", "description": "A module for the search backend that implements search using ElasticSearch", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 05c68e9d00..9e22c2cdec 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-explore +## 0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 7aa0b449ae..1c267d5cc0 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.2-next.0", + "version": "0.2.2-next.1", "description": "A module for the search backend that exports explore modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index cac5550c8a..f2cc0b4ca4 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.35-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + ## 0.5.35-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 01b20fb454..a37fcf98d5 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.35-next.0", + "version": "0.5.35-next.1", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index 92c8c81331..46f03fc870 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index 84363f010b..d6614573db 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.0-next.0", + "version": "0.3.0-next.1", "description": "A module for the search backend that exports stack overflow modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 47e94e2937..699d069a2c 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-node@1.12.11-next.1 + ## 0.2.2-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 9d5f3b238a..32295f9d91 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.2.2-next.0", + "version": "0.2.2-next.1", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index bcbee26057..82ab3b006c 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-backend-node +## 1.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-common@1.2.14 + ## 1.3.2-next.0 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index a900da8917..8833f15c6c 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.2-next.0", + "version": "1.3.2-next.1", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", "backstage": { "role": "node-library", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 4581b5ac91..6325f6bb73 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-search-backend +## 1.5.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/backend-common@0.25.0-next.1 + - @backstage/backend-openapi-utils@0.1.18-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-permission-node@0.8.3-next.1 + - @backstage/plugin-search-backend-node@1.3.2-next.1 + - @backstage/plugin-search-common@1.2.14 + ## 1.5.17-next.0 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 2c635fa4fe..c3689b9478 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "1.5.17-next.0", + "version": "1.5.17-next.1", "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 b86490a172..407c207559 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-react +## 1.8.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.14 + ## 1.8.0-next.0 ### Minor Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 5c132446eb..749a4f62c1 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.0-next.0", + "version": "1.8.0-next.1", "backstage": { "role": "web-library", "pluginId": "search", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 10dbdfa82f..63397834bf 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search +## 1.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.8 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + ## 1.4.16-next.0 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 1c1bf897d8..b8a76d61b8 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.16-next.0", + "version": "1.4.16-next.1", "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 d563916a49..9f67f6bcb0 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-signals-backend +## 0.2.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + ## 0.2.0-next.0 ### Minor Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index ba691cf3e5..be39008ad4 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-backend", - "version": "0.2.0-next.0", + "version": "0.2.0-next.1", "backstage": { "role": "backend-plugin", "pluginId": "signals", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index 1a52f705fd..6737179922 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-signals-node +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.4.0-next.1 + ## 0.1.11-next.0 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index 4268ffdba9..705e29eb90 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.11-next.0", + "version": "0.1.11-next.1", "description": "Node.js library for the signals plugin", "backstage": { "role": "node-library", diff --git a/plugins/signals/CHANGELOG.md b/plugins/signals/CHANGELOG.md index 3f247a89b7..f06315faab 100644 --- a/plugins/signals/CHANGELOG.md +++ b/plugins/signals/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-signals +## 0.0.10-next.1 + +### Patch Changes + +- 3e9b1a4: Put a name on the `SignalsDisplay` component extension +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.5-next.0 + ## 0.0.10-next.0 ### Patch Changes diff --git a/plugins/signals/package.json b/plugins/signals/package.json index f6489cc24d..95cab2df40 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals", - "version": "0.0.10-next.0", + "version": "0.0.10-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "signals", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 322031a769..05c53b5302 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.38-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration-react@1.1.30 + - @backstage/test-utils@1.6.0-next.0 + - @backstage/plugin-catalog@1.22.1-next.1 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs@1.10.9-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + ## 1.0.38-next.0 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 6a48c12a24..8ecfdfe90e 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.38-next.0", + "version": "1.0.38-next.1", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index ad0af5f33a..703f151823 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-techdocs-backend +## 1.10.13-next.1 + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/plugin-catalog-common@1.0.26 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-permission-common@0.8.1 + - @backstage/plugin-search-backend-module-techdocs@0.2.2-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-node@1.12.11-next.1 + ## 1.10.13-next.0 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 0c8a22c7ef..468b349f11 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "1.10.13-next.0", + "version": "1.10.13-next.1", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 2dc0782cc5..80a556378d 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + ## 1.1.14-next.0 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 60f499eb48..db2fc00ed9 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", - "version": "1.1.14-next.0", + "version": "1.1.14-next.1", "description": "Plugin module for contributed TechDocs Addons", "backstage": { "role": "frontend-plugin-module", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index c157eeaa9a..5df6fd23cc 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-techdocs-node +## 1.12.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + ## 1.12.11-next.0 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index daf36a34a7..8fa90e66d8 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.12.11-next.0", + "version": "1.12.11-next.1", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library", diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 72db11b0f2..93ad0932d8 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-react +## 1.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.11-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/version-bridge@1.0.8 + ## 1.2.8-next.0 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index d527baa8a4..df95b38138 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-react", - "version": "1.2.8-next.0", + "version": "1.2.8-next.1", "description": "Shared frontend utilities for TechDocs and Addons", "backstage": { "role": "web-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 496bfed377..56fdbed8eb 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-techdocs +## 1.10.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.6-next.0 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + ## 1.10.9-next.0 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index f551f387ae..cefc822ebf 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.10.9-next.0", + "version": "1.10.9-next.1", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index cdfc83f10c..5b6283c103 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.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/plugin-auth-node@0.5.2-next.1 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-node@0.1.11-next.1 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.2.24-next.0 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index 379b665f9f..dcbd3dac2d 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.24-next.0", + "version": "0.2.24-next.1", "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 4c835489d3..7716030af1 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-user-settings +## 0.8.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/core-app-api@1.14.2 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.5-next.0 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.8.12-next.0 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 0afe259a00..404024730c 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.12-next.0", + "version": "0.8.12-next.1", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", diff --git a/yarn.lock b/yarn.lock index 33d63250ef..7dcbde7cc0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3821,7 +3821,19 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-client@^1.6.6, @backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": +"@backstage/catalog-client@npm:^1.6.6": + version: 1.6.6 + resolution: "@backstage/catalog-client@npm:1.6.6" + dependencies: + "@backstage/catalog-model": ^1.6.0 + "@backstage/errors": ^1.2.4 + cross-fetch: ^4.0.0 + uri-template: ^2.0.0 + checksum: 10a859979a6ec3d9bcca519ace01ca371517e56bd54a90e0d667b4ef90bee54b09495238974b4040d7dc75f8cacf0d212900b40fe6738d484ff57a739e3eec4b + languageName: node + linkType: hard + +"@backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": version: 0.0.0-use.local resolution: "@backstage/catalog-client@workspace:packages/catalog-client" dependencies: @@ -4087,7 +4099,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/config@^1.1.1, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": +"@backstage/config@^1.1.1, @backstage/config@^1.2.0, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": version: 0.0.0-use.local resolution: "@backstage/config@workspace:packages/config" dependencies: @@ -4177,7 +4189,107 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@^0.14.10, @backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": +"@backstage/core-components@npm:^0.13.10": + version: 0.13.10 + resolution: "@backstage/core-components@npm:0.13.10" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.8.2 + "@backstage/errors": ^1.2.3 + "@backstage/theme": ^0.5.0 + "@backstage/version-bridge": ^1.0.7 + "@date-io/core": ^1.3.13 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^23.0.0 + "@types/react": ^16.13.1 || ^17.0.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-text-truncate": ^0.14.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + linkify-react: 4.1.3 + linkifyjs: 4.1.3 + lodash: ^4.17.21 + pluralize: ^8.0.0 + qs: ^6.9.4 + rc-progress: 3.5.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-idle-timer: 5.6.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-text-truncate: ^0.19.0 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.11 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ^3.22.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: ec2a0d0a27bc4d6b9d4da97f0b4df600148529ee51bf2c8e7d3adc45c3950adac662535d3beabc451db346f2c7e3c412ceaa756fc774012b43dff5e2695f2271 + languageName: node + linkType: hard + +"@backstage/core-components@npm:^0.14.10": + version: 0.14.10 + resolution: "@backstage/core-components@npm:0.14.10" + dependencies: + "@backstage/config": ^1.2.0 + "@backstage/core-plugin-api": ^1.9.3 + "@backstage/errors": ^1.2.4 + "@backstage/theme": ^0.5.6 + "@backstage/version-bridge": ^1.0.8 + "@date-io/core": ^1.3.13 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^24.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 + "@types/react-sparklines": ^1.7.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + linkify-react: 4.1.3 + linkifyjs: 4.1.3 + lodash: ^4.17.21 + pluralize: ^8.0.0 + qs: ^6.9.4 + rc-progress: 3.5.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-idle-timer: 5.7.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.11 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ^3.22.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 303682d411b846a4892b27c1064d499dbd4eccb5a3cdc5acbb1d3247e1141f4704efc7bd286744c19818487e52a61891943156817f59e3bd6555926f7331f575 + languageName: node + linkType: hard + +"@backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": version: 0.0.0-use.local resolution: "@backstage/core-components@workspace:packages/core-components" dependencies: @@ -4248,57 +4360,6 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@npm:^0.13.10": - version: 0.13.10 - resolution: "@backstage/core-components@npm:0.13.10" - dependencies: - "@backstage/config": ^1.1.1 - "@backstage/core-plugin-api": ^1.8.2 - "@backstage/errors": ^1.2.3 - "@backstage/theme": ^0.5.0 - "@backstage/version-bridge": ^1.0.7 - "@date-io/core": ^1.3.13 - "@material-table/core": ^3.1.0 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - "@react-hookz/web": ^23.0.0 - "@types/react": ^16.13.1 || ^17.0.0 - "@types/react-sparklines": ^1.7.0 - "@types/react-text-truncate": ^0.14.0 - ansi-regex: ^6.0.1 - classnames: ^2.2.6 - d3-selection: ^3.0.0 - d3-shape: ^3.0.0 - d3-zoom: ^3.0.0 - dagre: ^0.8.5 - linkify-react: 4.1.3 - linkifyjs: 4.1.3 - lodash: ^4.17.21 - pluralize: ^8.0.0 - qs: ^6.9.4 - rc-progress: 3.5.1 - react-helmet: 6.1.0 - react-hook-form: ^7.12.2 - react-idle-timer: 5.6.2 - react-markdown: ^8.0.0 - react-sparklines: ^1.7.0 - react-syntax-highlighter: ^15.4.5 - react-text-truncate: ^0.19.0 - react-use: ^17.3.2 - react-virtualized-auto-sizer: ^1.0.11 - react-window: ^1.8.6 - remark-gfm: ^3.0.1 - zen-observable: ^0.10.0 - zod: ^3.22.4 - peerDependencies: - react: ^16.13.1 || ^17.0.0 || ^18.0.0 - react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: ec2a0d0a27bc4d6b9d4da97f0b4df600148529ee51bf2c8e7d3adc45c3950adac662535d3beabc451db346f2c7e3c412ceaa756fc774012b43dff5e2695f2271 - languageName: node - linkType: hard - "@backstage/core-plugin-api@^1.8.2, @backstage/core-plugin-api@^1.9.3, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": version: 0.0.0-use.local resolution: "@backstage/core-plugin-api@workspace:packages/core-plugin-api" @@ -8031,7 +8092,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/theme@^0.5.0, @backstage/theme@workspace:^, @backstage/theme@workspace:packages/theme": +"@backstage/theme@^0.5.0, @backstage/theme@^0.5.6, @backstage/theme@workspace:^, @backstage/theme@workspace:packages/theme": version: 0.0.0-use.local resolution: "@backstage/theme@workspace:packages/theme" dependencies: From 0d2dfea229a6b39133269aa12cccbd613c6f052e Mon Sep 17 00:00:00 2001 From: aperigault Date: Tue, 3 Sep 2024 16:41:18 +0200 Subject: [PATCH 160/317] chore(microsite): Update grafana plugin metadata Signed-off-by: aperigault --- microsite/data/plugins/grafana.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsite/data/plugins/grafana.yaml b/microsite/data/plugins/grafana.yaml index c26d10b751..0637717466 100644 --- a/microsite/data/plugins/grafana.yaml +++ b/microsite/data/plugins/grafana.yaml @@ -4,9 +4,9 @@ author: K-Phoen authorUrl: https://github.com/K-Phoen category: Monitoring description: Associate alerts and dashboards to components. -documentation: https://github.com/K-Phoen/backstage-plugin-grafana/ +documentation: https://github.com/backstage/community-plugins/blob/main/workspaces/grafana/plugins/grafana/docs/index.md iconUrl: https://avatars.githubusercontent.com/u/7195757?s=200&v=4 -npmPackageName: '@k-phoen/backstage-plugin-grafana' +npmPackageName: '@backstage-community/plugin-grafana' tags: - dashboards - monitoring From 4a53dd03845256e91d36c3445f38b21989797dff Mon Sep 17 00:00:00 2001 From: David Weber Date: Tue, 3 Sep 2024 00:13:51 +0200 Subject: [PATCH 161/317] fix: implement icon in backend and show icon in table if available Signed-off-by: David Weber Signed-off-by: David Weber --- .changeset/orange-shrimps-pull.md | 6 ++++ .../migrations/20240902_addIcon.js | 33 ++++++++++++++++++ .../DatabaseNotificationsStore.test.ts | 2 ++ .../database/DatabaseNotificationsStore.ts | 3 ++ .../NotificationsTable/NotificationIcon.tsx | 34 +++++++++++++++++++ .../NotificationsTable/NotificationsTable.tsx | 6 ++-- 6 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 .changeset/orange-shrimps-pull.md create mode 100644 plugins/notifications-backend/migrations/20240902_addIcon.js create mode 100644 plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx diff --git a/.changeset/orange-shrimps-pull.md b/.changeset/orange-shrimps-pull.md new file mode 100644 index 0000000000..7d509a0e5d --- /dev/null +++ b/.changeset/orange-shrimps-pull.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-notifications-backend': patch +'@backstage/plugin-notifications': patch +--- + +Implement icon in backend and show icon in table if available. diff --git a/plugins/notifications-backend/migrations/20240902_addIcon.js b/plugins/notifications-backend/migrations/20240902_addIcon.js new file mode 100644 index 0000000000..a5327f8d73 --- /dev/null +++ b/plugins/notifications-backend/migrations/20240902_addIcon.js @@ -0,0 +1,33 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.up = async function up(knex) { + await knex.schema.alterTable('notification', table => { + table.string('icon', 255).nullable(); + }); + await knex.schema.alterTable('broadcast', table => { + table.string('icon', 255).nullable(); + }); +}; + +exports.down = async function down(knex) { + await knex.schema.alterTable('notification', table => { + table.dropColumn('icon'); + }); + await knex.schema.alterTable('broadcast', table => { + table.dropColumn('icon'); + }); +}; diff --git a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts index d4e678c8f1..0c7193c1fc 100644 --- a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts +++ b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts @@ -68,6 +68,7 @@ const testNotification1: Notification = { topic: 'efgh-topic', link: '/catalog', severity: 'critical', + icon: 'docs', }, }; const testNotification2: Notification = { @@ -183,6 +184,7 @@ describe.each(databases.eachSupportedId())( expect(notification?.payload?.topic).toBe('efgh-topic'); expect(notification?.payload?.link).toBe('/catalog'); expect(notification?.payload?.severity).toBe('critical'); + expect(notification?.payload?.icon).toBe('docs'); }); }); diff --git a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts index b23f5254f4..40a76c7486 100644 --- a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts +++ b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts @@ -41,6 +41,7 @@ const NOTIFICATION_COLUMNS = [ 'origin', 'scope', 'topic', + 'icon', 'created', 'updated', 'user', @@ -119,6 +120,7 @@ export class DatabaseNotificationsStore implements NotificationsStore { description: notification.payload?.description, severity: normalizeSeverity(notification.payload?.severity), scope: notification.payload?.scope, + icon: notification.payload.icon, saved: notification.saved, read: notification.read, }; @@ -134,6 +136,7 @@ export class DatabaseNotificationsStore implements NotificationsStore { title: notification.payload?.title, description: notification.payload?.description, severity: normalizeSeverity(notification.payload?.severity), + icon: notification.payload.icon, scope: notification.payload?.scope, }; }; diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx new file mode 100644 index 0000000000..c89f920249 --- /dev/null +++ b/plugins/notifications/src/components/NotificationsTable/NotificationIcon.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { Notification } from '@backstage/plugin-notifications-common'; +import SvgIcon from '@material-ui/core/SvgIcon'; +import { useApp } from '@backstage/core-plugin-api'; +import { SeverityIcon } from './SeverityIcon'; + +export const NotificationIcon = ({ + notification, +}: { + notification: Notification; +}) => { + const app = useApp(); + + if (notification.payload.icon) { + const Icon = app.getSystemIcon(notification.payload.icon) ?? SvgIcon; + return ; + } + return ; +}; diff --git a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx index 8a6cbd17d7..ee3e4e164d 100644 --- a/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx +++ b/plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx @@ -34,10 +34,9 @@ import { } from '@backstage/core-components'; import { notificationsApiRef } from '../../api'; - -import { SeverityIcon } from './SeverityIcon'; import { SelectAll } from './SelectAll'; import { BulkActions } from './BulkActions'; +import { NotificationIcon } from './NotificationIcon'; const ThrottleDelayMs = 1000; @@ -184,7 +183,6 @@ export const NotificationsTable = ({ const compactColumns = React.useMemo((): TableColumn[] => { const showToolbar = notifications.length > 0; - return [ { /* selection column */ @@ -220,7 +218,7 @@ export const NotificationsTable = ({ return ( - + From f40a443621e1776844575a531821622874d29100 Mon Sep 17 00:00:00 2001 From: Alex Lorenzi Date: Tue, 3 Sep 2024 13:10:41 -0400 Subject: [PATCH 162/317] fix: updated the hover effect on the table to a lighter colour and used the theme border radius so they match the rest of the Backstage styling a bit more Signed-off-by: Alex Lorenzi --- .../techdocs/src/reader/transformers/styles/rules/typeset.ts | 1 + .../techdocs/src/reader/transformers/styles/rules/variables.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts index c682008a4d..680e58e182 100644 --- a/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts +++ b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts @@ -113,6 +113,7 @@ ${headings.reduce((style, heading) => { border: 1px solid var(--md-default-fg-color); border-bottom: none; border-collapse: collapse; + border-radius: ${theme.shape.borderRadius}px; } .md-typeset table:not([class]) th { font-weight: bold; diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts b/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts index 3999430efb..7ffe76c5fb 100644 --- a/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts +++ b/plugins/techdocs/src/reader/transformers/styles/rules/variables.ts @@ -139,7 +139,7 @@ export default ({ theme }: RuleOptions) => ` --md-typeset-color: var(--md-default-fg-color); --md-typeset-a-color: ${theme.palette.link}; --md-typeset-table-color: ${theme.palette.text.primary}; - --md-typeset-table-color--light: ${alpha(theme.palette.text.primary, 0.5)}; + --md-typeset-table-color--light: ${alpha(theme.palette.text.primary, 0.05)}; --md-typeset-del-color: ${ theme.palette.type === 'dark' ? alpha(theme.palette.error.dark, 0.5) From 97db53e2d47548fec62be97226695668bb8b5c7f Mon Sep 17 00:00:00 2001 From: Alex Lorenzi Date: Tue, 3 Sep 2024 13:12:36 -0400 Subject: [PATCH 163/317] chore: changeset Signed-off-by: Alex Lorenzi --- .changeset/twenty-jobs-repeat.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/twenty-jobs-repeat.md diff --git a/.changeset/twenty-jobs-repeat.md b/.changeset/twenty-jobs-repeat.md new file mode 100644 index 0000000000..1ea98319d7 --- /dev/null +++ b/.changeset/twenty-jobs-repeat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Enhanced the table hover effect with a lighter color and updated the border radius to align with Backstage's theme styling From c81b8c10cecfac3c90164862a90378b70542fcd6 Mon Sep 17 00:00:00 2001 From: ElaineDeMattosSilvaB Date: Tue, 3 Sep 2024 20:11:37 +0200 Subject: [PATCH 164/317] feat: add deprecation note to changeset Signed-off-by: ElaineDeMattosSilvaB --- .changeset/loud-singers-happen.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.changeset/loud-singers-happen.md b/.changeset/loud-singers-happen.md index 32dbdb8481..04207d6428 100644 --- a/.changeset/loud-singers-happen.md +++ b/.changeset/loud-singers-happen.md @@ -3,3 +3,14 @@ --- Add the `relations` array to allow Backstage to mirror GitLab's membership behavior, including descendant, inherited, and shared-from-group memberships. + +The previous `allowInherited` config option will be deprecated in future versions. Use the `relations` array with the `INHERITED` option instead. + +```yaml +catalog: + providers: + gitlab: + development: + relations: + - INHERITED +``` From 817cbc0f4394366291c942288c0dc50439efdb65 Mon Sep 17 00:00:00 2001 From: darylgraham Date: Wed, 4 Sep 2024 06:03:01 +0000 Subject: [PATCH 165/317] Update wording in user-settings README to clarify example location Signed-off-by: darylgraham --- plugins/user-settings/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/user-settings/README.md b/plugins/user-settings/README.md index 41a6930ae3..ee26a5f332 100644 --- a/plugins/user-settings/README.md +++ b/plugins/user-settings/README.md @@ -91,7 +91,7 @@ To standardize the UI of all setting tabs, make sure you use a similar component structure as the other tabs. You can take a look at [the example extra tab](https://github.com/backstage/backstage/blob/master/packages/app/src/components/advancedSettings/AdvancedSettings.tsx) -we have created in Backstage's demo app. +we have created in Backstage's example app. To change the layout altogether, create a custom page in `packages/app/src/components/user-settings/SettingsPage.tsx`: From 9d04f492517622a373ed41b1fd74855bbd257a48 Mon Sep 17 00:00:00 2001 From: AmbrishRamachandiran Date: Wed, 4 Sep 2024 11:59:03 +0530 Subject: [PATCH 166/317] Updated notifications docs-grammer Signed-off-by: AmbrishRamachandiran --- docs/notifications/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/notifications/index.md b/docs/notifications/index.md index 86a67ce211..d4234b494e 100644 --- a/docs/notifications/index.md +++ b/docs/notifications/index.md @@ -33,7 +33,7 @@ Example of use-cases: ## Installation in Older Environments -Newer versions of instances created by the create-app have both the notifications and signals plugins included by default, this section can be skipped right to the Configuration. +Newer versions of instances created by the create-app have both the notifications and signals plugins included by default; this section can be skipped right to the Configuration. Following installation instructions are valid for enabling the plugins in older environments. @@ -136,15 +136,15 @@ If the signals plugin is properly configured, it will be automatically discovere The Notifications backend plugin provides an API to create notifications, list notifications per logged-in user, and search based on parameters. -The plugin uses a relational [database](https://backstage.io/docs/getting-started/config/database) for persistence, no specifics are introduced in this context. +The plugin uses a relational [database](https://backstage.io/docs/getting-started/config/database) for persistence; no specifics are introduced in this context. No additional configuration in the app-config is needed, except for optional additional modules for `processors`. ### Notifications Frontend -The recipients of notifications have to be entities in the catalog, e.g. of the User or Group kind. +The recipients of notifications have to be entities in the catalog, e.g., of the User or Group kind. -Otherwise no specific configuration is needed for the front-end notifications plugin. +Otherwise, no specific configuration is needed for the front-end notifications plugin. All parametrization is done through component properties, such as the `NotificationsSidebarItem`, which can be used as an active left-side menu item in the front-end. From 3a2551cb96261aa3d22d0c7de0fed74068a961c1 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Sat, 31 Aug 2024 08:30:25 +0200 Subject: [PATCH 167/317] docs: update old and new pages Signed-off-by: Camila Belo --- docs/auth/oidc--old.md | 289 +++++++++++++++++++++++++++++++++++++++++ docs/auth/oidc.md | 152 ++++++++++++---------- 2 files changed, 369 insertions(+), 72 deletions(-) create mode 100644 docs/auth/oidc--old.md diff --git a/docs/auth/oidc--old.md b/docs/auth/oidc--old.md new file mode 100644 index 0000000000..904e48c9a3 --- /dev/null +++ b/docs/auth/oidc--old.md @@ -0,0 +1,289 @@ +--- +id: oidc--old +title: OIDC provider from scratch +description: This section shows how to use an OIDC provider from scratch, same steps apply for custom providers. +--- + +:::info +This documentation is written for the old backend which has been replaced by +[the new backend system](../backend-system/index.md), being the default since +Backstage [version 1.24](../releases/v1.24.0.md). If have migrated to the new +backend system, you may want to read [its own article](./oidc.md) +instead. Otherwise, [consider migrating](../backend-system/building-backends/08-migrating.md)! +::: + +This section shows how to use an OIDC provider from scratch, same steps apply for custom +providers. Please note these steps are for using a provider, not how to implement one, +and Backstage recommends creating custom providers specific to the IDP, so we'll use a +`azureOIDC` provider throughout this example, feel free to change any of those refs +to your provider name. + +## Summary + +To add providers not enabled by default like OIDC, we need to follow some steps, we +assume you already have a sign in page to which we'll add the provider so users can +sign in through the provider. In simple steps here's how you enable the provider: + +- Create an API reference to identify the provider. +- Create the API factory that will handle the authentication. +- Add or reuse an auth provider so you can authenticate. +- Add or reuse a resolver to handle the result from the authentication. +- Configure the provider to access your 3rd party auth solution. +- Add the provider to sign in page so users can login with it. + +We'll explain each step more in detail next. + +### The API reference + +An API reference exist for the sake of **Dependency Injection**, check [Utility APIs][4] +for extended explanation. + +In this OIDC example, we'll create the API reference directly in the +`packages/app/src/apis.ts` file, it is not a requirement to put the reference in this +file. Any location will do as long as it's available to be imported to where the API +factory is, as well as easily accessible to the rest of the application so any package +and plugin can inject the API instance when necessary. + +An example of such would be when you use an auth provider from a library installed with +NPM, or any other library repository, you would import the API ref from the library. + +```ts +export const azureOIDCAuthApiRef: ApiRef< + OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +> = createApiRef({ + id: 'auth.my-custom-provider', +}); +``` + +Please note a few things, the ID can be anything you want as long as it doesn't conflict +with other refs, backstage recommends to use a custom name that references your custom +provider, for example we are using OIDC protocol with Azure, so we could use something +like `auth.azure.oidc` as well. + +Also we're exporting this reference, as well as the `typings`, we need to +be able to import this reference anywhere in the app, and the `typings` will tell typescript +what instance we're getting from DI when injecting the API. In this case we are defining +an API for authentication, so we tell TS that this instance complies with 4 API +interfaces: + +- The OICD API that will handle authentication. +- Profile API for requesting user profile info from the auth provider in question. +- Backstage identity API to handle and associate the user profile with backstage identity. +- Session API, to handle the session the user will have while logged in. + +### The API Factory + +A factory is a function that can take some parameters or dependencies and return an +instance of something, in our case it will be a function that requests some backstage +APIs and use them to create an instance of an OIDC API provider. + +Please note that this function only runs (creates the instance) when somewhere else in +the app you request the DI to give you an instance of the OIDC provider using the API ref +defined above, and the DI will only run this function the first time, from then on any +other DI injection will just receive the same instance created the first time, basically +the instance is cached by the DI library, a singleton. + +Let's add our OIDC API factory to the APIs array in the `packages/app/src/apis.ts` file: + +```ts title="packages/app/src/apis.ts" +/* highlight-add-next-line */ +import { OAuth2 } from '@backstage/core-app-api'; + +export const apis: AnyApiFactory[] = [ + /* highlight-add-start */ + createApiFactory({ + api: azureOIDCAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + configApi, + discoveryApi, + oauthRequestApi, + provider: { + id: 'my-auth-provider', + title: 'My custom auth provider', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), + defaultScopes: ['openid', 'profile', 'email'], + popupOptions: { + // optional, used to customize login in popup size + size: { + fullscreen: true, + }, + /** + * or specify popup width and height + * size: { + width: 1000, + height: 1000, + } + */ + }, + }), + }), + /* highlight-add-end */ + // .. +]; +``` + +Please note we're importing the `OAuth2` class from `@backstage/core-app-api` effectively +delegating the authentication to it. Also we're using the `my-auth-provider` ID to tell +`OAuth2` to use the auth provider we'll define in the next section, and added the default +scopes to request ID, profile, email and user read permissions. + +## The Auth Provider + +The Auth Provider is responsible for authenticating with the 3rd party service, and give +us back the credentials, here's where you pick which protocol to use, be it Auth0, OAuth2, +OIDC, SAML or any other that your 3rd party IDP provider supports. + +For this example we'll use OIDC, we pass a factory to the `providerFactories` object with +the ID you picked to represent the Auth provider, this ID has to match with the provider's +`id` inside the API factory, the yaml config provider key under `auth.providers`, and the +callback URI provider segment (you'll have to configure your IDP to handle the callback +URI properly). + +```ts +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + database: env.database, + discovery: env.discovery, + tokenManager: env.tokenManager, + providerFactories: { + ...defaultAuthProviderFactories, + /* highlight-add-next-line */ + 'my-auth-provider': providers.oidc.create({}), + }, + // .. +}) +``` + +### The Resolver + +Resolvers exist to map user identity from the 3rd party (in this case an azure IDP +provider) to the backstage user identity, for a detailed explanation check the +[Identity Resolver][1] page, it explains how to write a custom resolver as well as +linking the built in resolvers of backstage. + +The default OIDC provider does not support SignIn, we need to add such support by +adding a resolver for a SignIn request. + +The OIDC provider doesn't provide any build-in resolvers, so we'll need to define our own: + +```ts +import { + DEFAULT_NAMESPACE, + /* highlight-add-next-line */ + stringifyEntityRef, +} from '@backstage/catalog-model'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + database: env.database, + discovery: env.discovery, + tokenManager: env.tokenManager, + providerFactories: { + ...defaultAuthProviderFactories, + 'my-auth-provider': providers.oidc.create({ + /* highlight-add-start */ + signIn: { + resolver(info, ctx) { + const userRef = stringifyEntityRef({ + kind: 'User', + name: info.result.userinfo.sub, + namespace: DEFAULT_NAMESPACE, + }); + return ctx.issueToken({ + claims: { + sub: userRef, // The user's own identity + ent: [userRef], // A list of identities that the user claims ownership through + }, + }); + }, + }, + /* highlight-add-end */ + }), + }, + // .. + }) +``` + +### The configuration + +Since we are using our custom OIDC Auth Provider, we need to add a configuration based +on the provider used, in this case based on OIDC protocol (remember the 3rd party has to +support the protocol). + +In this example we'll configure OIDC with `my-auth-provider`, to do so we need to +[Create app registration][2] in the Azure console, the only difference is that the +`http://localhost:7007/api/auth/microsoft/handler/frame` URL needs to change to +`http://localhost:7007/api/auth/my-auth-provider/handler/frame`. + +Then we need to configure the env variables for the provider, based on the provider's code +in `plugins/auth-backend/src/providers/oidc/provider.ts` we need the following variables +in the `app-config.yaml`: + +```yaml title="app-config.yaml" +auth: + environment: development + ### Providing an auth.session.secret will enable session support in the auth-backend + session: + secret: ${SESSION_SECRET} + providers: + my-auth-provider: + development: + metadataUrl: https://example.com/.well-known/openid-configuration + clientId: ${AUTH_MY_CLIENT_ID} + clientSecret: ${AUTH_MY_CLIENT_SECRET} +``` + +Anything enclosed in `${}` can be replaced directly in the yaml, or provided as +environment variables, the way you obtain all these except `scope` and `prompt` is to +check the App Registration you created: + +- `clientId`: Grab from the Overview page. +- `clientSecret`: Can only be seen when creating the secret, if you lose it you'll need a + new secret. +- `metadataUrl`: In Overview > Endpoints tab, grab OpenID Connect metadata document URL. +- `authorizationUrl` and `tokenUrl`: Open the `metadataUrl` in a browser, that json will + hold these 2 urls somewhere in there. +- `tokenEndpointAuthMethod`: Don't define it, use the default unless you know what it does. +- `tokenSignedResponseAlg`: Don't define it, use the default unless you know what it does. +- `scope`: Only used if we didn't specify `defaultScopes` in the provider's factory, + basically the same thing. +- `prompt`: Recommended to use `auto` so the browser will request login to the IDP if the + user has no active session. + +Note that for the time being, any change in this yaml file requires a restart of the app, +also you need to have the `session.secret` part to use OIDC (some other providers might +need this as well) to support user sessions. + +### The Sign In provider + +The last step is to add the provider to the `SignInPage` so users can sign in with your +new provider, please follow the [Sign In Configuration][3] docs, here's where you import +and use the API reference we defined earlier. + +## Note + +These steps apply to most if not all the providers, including custom providers, the main +difference between different providers will be the contents of the API factory, the code +in the Auth Provider Factory, the resolver, and the different variables each provider +needs in the YAML config or env variables. + +[1]: https://backstage.io/docs/auth/identity-resolver +[2]: https://backstage.io/docs/auth/microsoft/provider#create-an-app-registration-on-azure +[3]: https://backstage.io/docs/auth/#sign-in-configuration +[4]: https://backstage.io/docs/api/utility-apis diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 98d0e63255..64da189fae 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -4,6 +4,13 @@ title: OIDC provider from scratch description: This section shows how to use an OIDC provider from scratch, same steps apply for custom providers. --- +:::info +This documentation is written for [the new backend system](../backend-system/index.md) which is the default since Backstage +[version 1.24](../releases/v1.24.0.md). If you are still on the old backend +system, you may want to read [its own article](./oidc--old.md) +instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)! +::: + This section shows how to use an OIDC provider from scratch, same steps apply for custom providers. Please note these steps are for using a provider, not how to implement one, and Backstage recommends creating custom providers specific to the IDP, so we'll use a @@ -133,85 +140,86 @@ The Auth Provider is responsible for authenticating with the 3rd party service, us back the credentials, here's where you pick which protocol to use, be it Auth0, OAuth2, OIDC, SAML or any other that your 3rd party IDP provider supports. -For this example we'll use OIDC, we pass a factory to the `providerFactories` object with -the ID you picked to represent the Auth provider, this ID has to match with the provider's -`id` inside the API factory, the yaml config provider key under `auth.providers`, and the -callback URI provider segment (you'll have to configure your IDP to handle the callback -URI properly). - -```ts -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - return await createRouter({ - logger: env.logger, - config: env.config, - database: env.database, - discovery: env.discovery, - tokenManager: env.tokenManager, - providerFactories: { - ...defaultAuthProviderFactories, - /* highlight-add-next-line */ - 'my-auth-provider': providers.oidc.create({}), - }, - // .. -}) -``` - ### The Resolver Resolvers exist to map user identity from the 3rd party (in this case an azure IDP -provider) to the backstage user identity, for a detailed explanation check the -[Identity Resolver][1] page, it explains how to write a custom resolver as well as -linking the built in resolvers of backstage. +provider) to the backstage user identity. -The default OIDC provider does not support SignIn, we need to add such support by -adding a resolver for a SignIn request. +The default OIDC provider has built-in resolvers, here is how you configure them: -The OIDC provider doesn't provide any build-in resolvers, so we'll need to define our own: - -```ts -import { - DEFAULT_NAMESPACE, - /* highlight-add-next-line */ - stringifyEntityRef, -} from '@backstage/catalog-model'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - return await createRouter({ - logger: env.logger, - config: env.config, - database: env.database, - discovery: env.discovery, - tokenManager: env.tokenManager, - providerFactories: { - ...defaultAuthProviderFactories, - 'my-auth-provider': providers.oidc.create({ - /* highlight-add-start */ - signIn: { - resolver(info, ctx) { - const userRef = stringifyEntityRef({ - kind: 'User', - name: info.result.userinfo.sub, - namespace: DEFAULT_NAMESPACE, - }); - return ctx.issueToken({ - claims: { - sub: userRef, // The user's own identity - ent: [userRef], // A list of identities that the user claims ownership through - }, - }); - }, - }, - /* highlight-add-end */ - }), - }, - // .. - }) +```yaml title="app-config.yaml" +auth: + environment: development + providers: + oidc: + development: + # ... + signIn: + resolvers: + # typically you would pick one of these + - resolver: emailLocalPartMatchingUserEntityName + - resolver: emailMatchingUserEntityProfileEmail ``` +But you can also write a custom resolver as well, see an example below: + +```ts title="in packages/backend/src/index.ts" +/* highlight-add-start */ +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider'; + +const myAuthProviderModule = createBackendModule({ + // This ID must be exactly "auth" because that's the plugin it targets + pluginId: 'auth', + // This ID must be unique, but can be anything + moduleId: 'my-auth-provider', + register(reg) { + reg.registerInit({ + deps: { providers: authProvidersExtensionPoint }, + async init({ providers }) { + providers.registerProvider({ + // This ID must match the actual provider config, e.g. addressing + // auth.providers.azure means that this must be "azure". + providerId: 'my-auth-provider', + // Use createProxyAuthProviderFactory instead if it's one of the proxy + // based providers rather than an OAuth based one + factory: createOAuthProviderFactory({ + // For more info about authenticators please see https://backstage.io/docs/auth/add-auth-provider/#adding-an-oauth-based-provider + authenticator: oidcAuthenticator, + async signInResolver(info, ctx) { + const userRef = stringifyEntityRef({ + kind: 'User', + name: info.result.userinfo.sub, + namespace: DEFAULT_NAMESPACE, + }); + return ctx.issueToken({ + claims: { + sub: userRef, // The user's own identity + ent: [userRef], // A list of identities that the user claims ownership through + }, + }); + }, + }), + }); + }, + }); + }, +}); +/* highlight-add-end */ +//... +backend.add(import('@backstage/plugin-auth-backend')); +/* highlight-add-next-line */ +backend.add(myAuthProviderModule); +//... +``` + +For a more a detailed explanation about resolvers check the +[Identity Resolver][1] page. + ### The configuration Since we are using our custom OIDC Auth Provider, we need to add a configuration based From 5d1670f95909d8f8ca6af4af44865abfb649166a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 4 Sep 2024 11:15:41 +0200 Subject: [PATCH 168/317] update installation instructions in readme files for new backend system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/early-swans-grab.md | 12 +++++ plugins/devtools-backend/README.md | 43 +++++++++-------- .../README.md | 47 ++++--------------- .../README.md | 29 +++--------- .../README.md | 47 +++---------------- .../scaffolder-backend-module-rails/README.md | 37 +++------------ .../README.md | 26 +++------- .../README.md | 30 +++--------- plugins/user-settings-backend/README.md | 40 ++-------------- 9 files changed, 79 insertions(+), 232 deletions(-) create mode 100644 .changeset/early-swans-grab.md diff --git a/.changeset/early-swans-grab.md b/.changeset/early-swans-grab.md new file mode 100644 index 0000000000..4526067275 --- /dev/null +++ b/.changeset/early-swans-grab.md @@ -0,0 +1,12 @@ +--- +'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch +'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-sentry': patch +'@backstage/plugin-scaffolder-backend-module-yeoman': patch +'@backstage/plugin-scaffolder-backend-module-rails': patch +'@backstage/plugin-user-settings-backend': patch +'@backstage/plugin-devtools-backend': patch +--- + +Update README installation instructions diff --git a/plugins/devtools-backend/README.md b/plugins/devtools-backend/README.md index bf0eb64a98..d9ffb3e076 100644 --- a/plugins/devtools-backend/README.md +++ b/plugins/devtools-backend/README.md @@ -4,7 +4,30 @@ Welcome to the DevTools backend plugin! This plugin provides data for the [DevTo ## Setup -Here's how to get the DevTools Backend up and running: +Here's how to get the DevTools Backend up and running in the new backend system: + +1. First we need to add the `@backstage/plugin-devtools-backend` package to your backend: + + ```sh + # From the Backstage root directory + yarn --cwd packages/backend add @backstage/plugin-devtools-backend + ``` + +2. Then add the plugin to your backend index file: + + ```ts + // In packages/backend/src/index.ts + const backend = createBackend(); + // ... other feature additions + backend.add(import('@backstage/plugin-devtools-backend')); + ``` + +3. Now run `yarn start-backend` from the repo root +4. Finally open `http://localhost:7007/api/devtools/health` in a browser and it should return `{"status":"ok"}` + +## Old Backend System + +If you are still on the old backend system, please consider migrating to the new backend system. But to install this plugin in an old backend, see below. 1. First we need to add the `@backstage/plugin-devtools-backend` package to your backend: @@ -50,24 +73,6 @@ Here's how to get the DevTools Backend up and running: 4. Now run `yarn start-backend` from the repo root 5. Finally open `http://localhost:7007/api/devtools/health` in a browser and it should return `{"status":"ok"}` -### New Backend System - -The DevTools backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up: - -In your `packages/backend/src/index.ts` make the following changes: - -```diff - import { createBackend } from '@backstage/backend-defaults'; - - const backend = createBackend(); - - // ... other feature additions - -+ backend.add(import('@backstage/plugin-devtools-backend')); - - backend.start(); -``` - ## Links - [Frontend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/devtools) diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/README.md b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md index 1e64004041..aa7b9fee79 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/README.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md @@ -15,47 +15,16 @@ From your Backstage root directory run: yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-confluence-to-markdown ``` -Then configure the action: -(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): +Then ensure that both the scaffolder and this module are added to your backend: ```typescript -// packages/backend/src/plugins/scaffolder.ts - -import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; -import { ScmIntegrations } from '@backstage/integration'; -import { createConfluenceToMarkdownAction } from '@backstage/plugin-scaffolder-backend-module-confluence-to-markdown'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const catalogClient = new CatalogClient({ discoveryApi: env.discovery }); - const integrations = ScmIntegrations.fromConfig(env.config); - - const builtInActions = createBuiltinActions({ - integrations, - catalogClient, - config: env.config, - reader: env.reader, - }); - - const actions = [ - ...builtInActions, - createConfluenceToMarkdownAction({ - integrations, - config: env.config, - reader: env.reader, - }), - ]; - - return createRouter({ - actions, - catalogClient: catalogClient, - logger: env.logger, - config: env.config, - database: env.database, - reader: env.reader, - }); -} +// In packages/backend/src/index.ts +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); +backend.add( + import('@backstage/plugin-scaffolder-backend-module-confluence-to-markdown'), +); ``` ### Configuration diff --git a/plugins/scaffolder-backend-module-cookiecutter/README.md b/plugins/scaffolder-backend-module-cookiecutter/README.md index 643154c826..ba2cd14cb2 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/README.md +++ b/plugins/scaffolder-backend-module-cookiecutter/README.md @@ -13,31 +13,14 @@ You need to configure the action in your backend: yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-cookiecutter ``` -Configure the action: -(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): +Then ensure that both the scaffolder and this module are added to your backend: ```typescript -// packages/backend/src/plugins/scaffolder.ts - -const actions = [ - createFetchCookiecutterAction({ - integrations, - reader: env.reader, - }), - ...createBuiltInActions({ - ... - }) -]; - -return await createRouter({ - containerRunner, - catalogClient, - actions, - logger: env.logger, - config: env.config, - database: env.database, - reader: env.reader, -}); +// In packages/backend/src/index.ts +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); +backend.add(import('@backstage/plugin-scaffolder-backend-module-cookiecutter')); ``` After that you can use the action in your template: diff --git a/plugins/scaffolder-backend-module-gitlab/README.md b/plugins/scaffolder-backend-module-gitlab/README.md index 3aee2b6681..3f991bf308 100644 --- a/plugins/scaffolder-backend-module-gitlab/README.md +++ b/plugins/scaffolder-backend-module-gitlab/README.md @@ -13,50 +13,17 @@ Here you can find all Gitlab related features to improve your scaffolder: yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-gitlab ``` -Configure the action: -(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): +Then ensure that both the scaffolder and this module are added to your backend: ```typescript -// packages/backend/src/plugins/scaffolder.ts - -import { - createGitlabProjectAccessTokenAction, - createGitlabProjectDeployTokenAction, - createGitlabProjectVariableAction, - createGitlabGroupEnsureExistsAction, - createGitlabIssueAction, -} from '@backstage/plugin-scaffolder-backend-module-gitlab'; - -// Create BuiltIn Actions -const builtInActions = createBuiltinActions({ - integrations, - catalogClient, - config: env.config, - reader: env.reader, -}); - -// Add Gitlab Actions -const actions = [ - ...builtInActions, - createGitlabProjectAccessTokenAction({ integrations: integrations }), - createGitlabProjectDeployTokenAction({ integrations: integrations }), - createGitlabProjectVariableAction({ integrations: integrations }), - createGitlabGroupEnsureExistsAction({ integrations: integrations }), - createGitlabIssueAction({ integrations: integrations }), -]; - -// Create Scaffolder Router -return await createRouter({ - catalogClient, - actions, - logger: env.logger, - config: env.config, - database: env.database, - reader: env.reader, -}); +// In packages/backend/src/index.ts +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); +backend.add(import('@backstage/plugin-scaffolder-backend-module-gitlab')); ``` -After that you can use the action in your template: +After that you can use the actions in your template: ```yaml apiVersion: scaffolder.backstage.io/v1beta3 diff --git a/plugins/scaffolder-backend-module-rails/README.md b/plugins/scaffolder-backend-module-rails/README.md index 18e16a7567..27db5656c5 100644 --- a/plugins/scaffolder-backend-module-rails/README.md +++ b/plugins/scaffolder-backend-module-rails/README.md @@ -18,26 +18,14 @@ You need to configure the action in your backend: yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-rails ``` -Configure the action (you can check -the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to -see all options): +Then ensure that both the scaffolder and this module are added to your backend: ```typescript -const actions = [ - createFetchRailsAction({ - integrations, - reader: env.reader, - }), -]; - -return await createRouter({ - catalogClient, - actions, - logger: env.logger, - config: env.config, - database: env.database, - reader: env.reader, -}); +// In packages/backend/src/index.ts +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); +backend.add(import('@backstage/plugin-scaffolder-backend-module-rails')); ``` After that you can use the action in your template: @@ -244,16 +232,3 @@ steps: system: ${{ parameters.system }} railsArguments: ${{ parameters.railsArguments }} ``` - -You also need to configure the list of allowed images as part of the creating the action for the scaffolder backend: - -```typescript -const actions = [ - createFetchRailsAction({ - integrations, - reader: env.reader, - containerRunner, - allowedImageNames: ['repository/rails:tag'], - }), -]; -``` diff --git a/plugins/scaffolder-backend-module-sentry/README.md b/plugins/scaffolder-backend-module-sentry/README.md index 5cb1f1b810..d13f923a2a 100644 --- a/plugins/scaffolder-backend-module-sentry/README.md +++ b/plugins/scaffolder-backend-module-sentry/README.md @@ -15,28 +15,14 @@ You need to configure the action in your backend: yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-sentry ``` -Configure the action (you can check -the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to -see all options): +Then ensure that both the scaffolder and this module are added to your backend: ```typescript -const actions = [ - createSentryCreateProjectAction({ - integrations, - reader: env.reader, - containerRunner, - }), -]; - -return await createRouter({ - containerRunner, - catalogClient, - actions, - logger: env.logger, - config: env.config, - database: env.database, - reader: env.reader, -}); +// In packages/backend/src/index.ts +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); +backend.add(import('@backstage/plugin-scaffolder-backend-module-sentry')); ``` You need to define your Sentry API Token in your `app-config.yaml`: diff --git a/plugins/scaffolder-backend-module-yeoman/README.md b/plugins/scaffolder-backend-module-yeoman/README.md index 05bbf6064b..3a32dd2124 100644 --- a/plugins/scaffolder-backend-module-yeoman/README.md +++ b/plugins/scaffolder-backend-module-yeoman/README.md @@ -13,32 +13,14 @@ You need to configure the action in your backend: yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-yeoman ``` -Configure the action: -(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): +Then ensure that both the scaffolder and this module are added to your backend: ```typescript -// packages/backend/src/plugins/scaffolder.ts - -const actions = [ - createRunYeomanAction(), - ...createBuiltInActions({ - containerRunner, - catalogClient, - integrations, - config: env.config, - reader: env.reader, - }), -]; - -return await createRouter({ - containerRunner, - catalogClient, - actions, - logger: env.logger, - config: env.config, - database: env.database, - reader: env.reader, -}); +// In packages/backend/src/index.ts +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); +backend.add(import('@backstage/plugin-scaffolder-backend-module-yeoman')); ``` After that you can use the action in your template: diff --git a/plugins/user-settings-backend/README.md b/plugins/user-settings-backend/README.md index a1fb504415..249edec592 100644 --- a/plugins/user-settings-backend/README.md +++ b/plugins/user-settings-backend/README.md @@ -10,48 +10,16 @@ Install the backend plugin ```bash # From your Backstage root directory -yarn --cwd packages/backend add @backstage/plugin-user-settings-backend +yarn --cwd packages/backend add @backstage/plugin-user-settings-backend @backstage/plugin-signals-backend ``` -### New backend - Add the plugin to your backend in `packages/backend/src/index.ts`: ```ts backend.add(import('@backstage/plugin-user-settings-backend/alpha')); -``` - -To get real-time updates of the user settings across different user sessions, you must also install -the `@backstage/plugin-signals-backend` plugin. - -### Old backend - -1. Configure the routes by adding a new `userSettings.ts` file in - `packages/backend/src/plugins/`: - -```ts -// packages/backend/src/plugins/userSettings.ts -import { createRouter } from '@backstage/plugin-user-settings-backend'; -import { PluginEnvironment } from '../types'; - -export default async function createPlugin(env: PluginEnvironment) { - return await createRouter({ - database: env.database, - identity: env.identity, - }); -} -``` - -2. Add the new routes to your backend by modifying `packages/backend/src/index.ts`: - -```diff - // packages/backend/src/index.ts -+import userSettings from './plugins/userSettings'; - async function main() { -+ const userSettingsEnv = useHotMemoize(module, () => createEnv('user-settings')); - const apiRouter = Router(); -+ apiRouter.use('/user-settings', await userSettings(userSettingsEnv)); -} +// The signals backend is technically optional but enables real-time update of user +// settings across different sessions +backend.add(import('@backstage/plugin-signals-backend')); ``` ## Setup app From 30944adff85df9c8411f250a689f527e8b987b64 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 11:34:13 +0000 Subject: [PATCH 169/317] chore(deps): pin dependencies Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/deploy_microsite.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index c5d8cc26c2..8f3820ae62 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -28,7 +28,7 @@ jobs: egress-policy: audit - name: find latest release - uses: actions/github-script@v7 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 id: find-release with: script: | @@ -73,7 +73,7 @@ jobs: run: yarn build:api-docs - name: upload API reference - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4 with: name: stable-reference path: docs/reference/ @@ -113,7 +113,7 @@ jobs: run: yarn build:api-docs - name: upload API reference - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4 with: name: next-reference path: docs/reference/ @@ -130,7 +130,7 @@ jobs: working-directory: storybook - name: storybook upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4 with: name: storybook path: storybook/dist/ @@ -179,7 +179,7 @@ jobs: working-directory: microsite - name: download stable reference - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 with: name: stable-reference path: docs/reference @@ -202,7 +202,7 @@ jobs: working-directory: microsite - name: download next reference - uses: actions/download-artifact@v4 + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 with: name: next-reference path: docs/reference @@ -211,7 +211,7 @@ jobs: run: yarn build working-directory: microsite - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 with: name: storybook path: microsite/build/storybook From e4db1496f423d4f93ecc9362ec6e5d7b3f1050f7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:24:40 +0000 Subject: [PATCH 170/317] chore(deps): update dependency @changesets/cli to v2.27.8 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 185 ++++++++++++++++++++++++++---------------------------- 1 file changed, 90 insertions(+), 95 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4479624fd3..4689c43a11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3342,7 +3342,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": version: 7.24.4 resolution: "@babel/runtime@npm:7.24.4" dependencies: @@ -8188,15 +8188,14 @@ __metadata: languageName: node linkType: hard -"@changesets/apply-release-plan@npm:^7.0.4": - version: 7.0.4 - resolution: "@changesets/apply-release-plan@npm:7.0.4" +"@changesets/apply-release-plan@npm:^7.0.5": + version: 7.0.5 + resolution: "@changesets/apply-release-plan@npm:7.0.5" dependencies: - "@babel/runtime": ^7.20.1 - "@changesets/config": ^3.0.2 + "@changesets/config": ^3.0.3 "@changesets/get-version-range-type": ^0.4.0 - "@changesets/git": ^3.0.0 - "@changesets/should-skip-package": ^0.1.0 + "@changesets/git": ^3.0.1 + "@changesets/should-skip-package": ^0.1.1 "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 detect-indent: ^6.0.0 @@ -8206,22 +8205,21 @@ __metadata: prettier: ^2.7.1 resolve-from: ^5.0.0 semver: ^7.5.3 - checksum: 5a61a8794fd4762e9270ffec474dc7c5d420c1d0479c5316a12e5264589882ccfb3ec08af6c0ebd07de2bf26ae880ad2f65e8f16d5886745aecf48cb11f625f5 + checksum: f6a1b90d89fd08b46c11fad05d5fee510ff8a1888c163fd6221ccfb045eab013fa57c0c32c5697d6406852a39cf4df01b44f3ecca4746f30bd610bec54aa9abf languageName: node linkType: hard -"@changesets/assemble-release-plan@npm:^6.0.3": - version: 6.0.3 - resolution: "@changesets/assemble-release-plan@npm:6.0.3" +"@changesets/assemble-release-plan@npm:^6.0.4": + version: 6.0.4 + resolution: "@changesets/assemble-release-plan@npm:6.0.4" dependencies: - "@babel/runtime": ^7.20.1 "@changesets/errors": ^0.2.0 - "@changesets/get-dependents-graph": ^2.1.1 - "@changesets/should-skip-package": ^0.1.0 + "@changesets/get-dependents-graph": ^2.1.2 + "@changesets/should-skip-package": ^0.1.1 "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 semver: ^7.5.3 - checksum: ae0ac29a44640fc6c0d3782c134adad3f0af80903ecb8bf663f0bf5f569586c9620980100737a05639f1a7252f145942ae62fa25d0757e89b5d6311abf899cd5 + checksum: 948066a8ca8e12390599f641a0439b6a4d6c1c2a9958f58596aa50cf68d7d594b28acc1eb6bd0ad17df2025f0614006e44728a2614fad2a3d54c669568bf6d65 languageName: node linkType: hard @@ -8235,59 +8233,57 @@ __metadata: linkType: hard "@changesets/cli@npm:^2.14.0": - version: 2.27.7 - resolution: "@changesets/cli@npm:2.27.7" + version: 2.27.8 + resolution: "@changesets/cli@npm:2.27.8" dependencies: - "@babel/runtime": ^7.20.1 - "@changesets/apply-release-plan": ^7.0.4 - "@changesets/assemble-release-plan": ^6.0.3 + "@changesets/apply-release-plan": ^7.0.5 + "@changesets/assemble-release-plan": ^6.0.4 "@changesets/changelog-git": ^0.2.0 - "@changesets/config": ^3.0.2 + "@changesets/config": ^3.0.3 "@changesets/errors": ^0.2.0 - "@changesets/get-dependents-graph": ^2.1.1 - "@changesets/get-release-plan": ^4.0.3 - "@changesets/git": ^3.0.0 - "@changesets/logger": ^0.1.0 - "@changesets/pre": ^2.0.0 - "@changesets/read": ^0.6.0 - "@changesets/should-skip-package": ^0.1.0 + "@changesets/get-dependents-graph": ^2.1.2 + "@changesets/get-release-plan": ^4.0.4 + "@changesets/git": ^3.0.1 + "@changesets/logger": ^0.1.1 + "@changesets/pre": ^2.0.1 + "@changesets/read": ^0.6.1 + "@changesets/should-skip-package": ^0.1.1 "@changesets/types": ^6.0.0 - "@changesets/write": ^0.3.1 + "@changesets/write": ^0.3.2 "@manypkg/get-packages": ^1.1.3 "@types/semver": ^7.5.0 ansi-colors: ^4.1.3 - chalk: ^2.1.0 ci-info: ^3.7.0 enquirer: ^2.3.0 external-editor: ^3.1.0 fs-extra: ^7.0.1 - human-id: ^1.0.2 mri: ^1.2.0 outdent: ^0.5.0 p-limit: ^2.2.0 - preferred-pm: ^3.0.0 + package-manager-detector: ^0.2.0 + picocolors: ^1.1.0 resolve-from: ^5.0.0 semver: ^7.5.3 spawndamnit: ^2.0.0 term-size: ^2.1.0 bin: changeset: bin.js - checksum: f518121a516ce8186dfc71dcca2f4be5fea28fe29a54b038aa787964bb4d4f1e866840d6d765776db33f7723d1a850ee4a38ed726f302a26e2e56a5e2b9422e8 + checksum: b58386716b337976d5797debd4a418fb257bec1e6c9932b99eac7725dd5a76fceff32691c625f897fd4baa78aa2bfba9747fbacf7d8193197f9246b36d31c013 languageName: node linkType: hard -"@changesets/config@npm:^3.0.2": - version: 3.0.2 - resolution: "@changesets/config@npm:3.0.2" +"@changesets/config@npm:^3.0.3": + version: 3.0.3 + resolution: "@changesets/config@npm:3.0.3" dependencies: "@changesets/errors": ^0.2.0 - "@changesets/get-dependents-graph": ^2.1.1 - "@changesets/logger": ^0.1.0 + "@changesets/get-dependents-graph": ^2.1.2 + "@changesets/logger": ^0.1.1 "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 fs-extra: ^7.0.1 micromatch: ^4.0.2 - checksum: 4160e59f74c1f36a2d3d011868f83c46410553f9fc164a83dde46ec7e37f13ba983526b9c299de3cd3f317e5e42808a991959931c9d34f2913f3e8d4c515cd12 + checksum: f216f497e09c0fcdd4c397fc3998d1651a171b89981d2bed2a6c23c0f55ffa4e240cadbd13902bf91c218686165689a5183674a5b4682d80d3d5b8b9c569f5f1 languageName: node linkType: hard @@ -8300,31 +8296,29 @@ __metadata: languageName: node linkType: hard -"@changesets/get-dependents-graph@npm:^2.1.1": - version: 2.1.1 - resolution: "@changesets/get-dependents-graph@npm:2.1.1" +"@changesets/get-dependents-graph@npm:^2.1.2": + version: 2.1.2 + resolution: "@changesets/get-dependents-graph@npm:2.1.2" dependencies: "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 - chalk: ^2.1.0 - fs-extra: ^7.0.1 + picocolors: ^1.1.0 semver: ^7.5.3 - checksum: e427603a3cad3690deec86a5831f15766dc32dc0819532e3ad70fbda027317bb46bb207c6a3bc947daf00e5e8229a01dd67aa3ec7248aa11f87611a7ecd74097 + checksum: 38446343e43f9b8731098e3b42d2525d5399d59cfccc09bdb62c9a48de60c7a893882050202badca3b5cab8405e6deb82e88258a56a318e42749fa60d96d874a languageName: node linkType: hard -"@changesets/get-release-plan@npm:^4.0.3": - version: 4.0.3 - resolution: "@changesets/get-release-plan@npm:4.0.3" +"@changesets/get-release-plan@npm:^4.0.4": + version: 4.0.4 + resolution: "@changesets/get-release-plan@npm:4.0.4" dependencies: - "@babel/runtime": ^7.20.1 - "@changesets/assemble-release-plan": ^6.0.3 - "@changesets/config": ^3.0.2 - "@changesets/pre": ^2.0.0 - "@changesets/read": ^0.6.0 + "@changesets/assemble-release-plan": ^6.0.4 + "@changesets/config": ^3.0.3 + "@changesets/pre": ^2.0.1 + "@changesets/read": ^0.6.1 "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 - checksum: baf01eb124f95df729e1798c23e4ac1996acd74e0d22336492e4a0fd68f3a19c97197337fdcb58d47d0b580ca38e0b0888cbacaa1c99d7c173c3cecb859ab4cc + checksum: 7217347f5bfaa56f97d3964e28e23a109d60c42b7c879c0cab6934feb30bdbdebb6dd0e81b4ecb5ec414be442d566b6af90d9224f6a48a52b6c5269c337f54a6 languageName: node linkType: hard @@ -8335,27 +8329,25 @@ __metadata: languageName: node linkType: hard -"@changesets/git@npm:^3.0.0": - version: 3.0.0 - resolution: "@changesets/git@npm:3.0.0" +"@changesets/git@npm:^3.0.1": + version: 3.0.1 + resolution: "@changesets/git@npm:3.0.1" dependencies: - "@babel/runtime": ^7.20.1 "@changesets/errors": ^0.2.0 - "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 is-subdir: ^1.1.1 micromatch: ^4.0.2 spawndamnit: ^2.0.0 - checksum: a8fa66d77302b50d5e604aca898ee813247537d23a05004637ecee4aa1579d6a2859283c099bdcf3e2b232258c93ff81dd57aa867858788e457df40118c64c2b + checksum: 46d780fecd3dbdafde7c96dde7fe35a8461bc6edbff1de92d490971a99f021d60c5b4606a1d4fb778567146810090ede6610cf89407c14bde88edaa246801539 languageName: node linkType: hard -"@changesets/logger@npm:^0.1.0": - version: 0.1.0 - resolution: "@changesets/logger@npm:0.1.0" +"@changesets/logger@npm:^0.1.1": + version: 0.1.1 + resolution: "@changesets/logger@npm:0.1.1" dependencies: - chalk: ^2.1.0 - checksum: d8ef1b7caf3d2c15a9e7743b7a9462e0c2e61c76d9a5bbed5eff805afa8226117505309c6e9095001136b4f6d9ae0aba61377e53af8aa0809f1febd1b5f787f1 + picocolors: ^1.1.0 + checksum: acca50ef6bf6e446b46eb576b32f1955bf4579dbf4bbc316768ed2c1d4ba4066c9c73b114eedefaa1b3e360b1060a020e6bd3dbdbc44b74da732df92307beab0 languageName: node linkType: hard @@ -8369,43 +8361,40 @@ __metadata: languageName: node linkType: hard -"@changesets/pre@npm:^2.0.0": - version: 2.0.0 - resolution: "@changesets/pre@npm:2.0.0" +"@changesets/pre@npm:^2.0.1": + version: 2.0.1 + resolution: "@changesets/pre@npm:2.0.1" dependencies: - "@babel/runtime": ^7.20.1 "@changesets/errors": ^0.2.0 "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 fs-extra: ^7.0.1 - checksum: 6a01086405f4e4ce63abb8f222de39b69a5762c9c8c8f19c0d3c72f7798248d7a152937028f1be24be1f8a4a5e47e4cb23c54bc36f979539b24a728c893caf4e + checksum: fbe94283dce0223ee79c12fa221105752ac89eb885b77e300ec755682cb06cc0145e10335f4bc6cb26d63473e549556c2b1c8c866242419aee5e41986379652a languageName: node linkType: hard -"@changesets/read@npm:^0.6.0": - version: 0.6.0 - resolution: "@changesets/read@npm:0.6.0" +"@changesets/read@npm:^0.6.1": + version: 0.6.1 + resolution: "@changesets/read@npm:0.6.1" dependencies: - "@babel/runtime": ^7.20.1 - "@changesets/git": ^3.0.0 - "@changesets/logger": ^0.1.0 + "@changesets/git": ^3.0.1 + "@changesets/logger": ^0.1.1 "@changesets/parse": ^0.4.0 "@changesets/types": ^6.0.0 - chalk: ^2.1.0 fs-extra: ^7.0.1 p-filter: ^2.1.0 - checksum: 3da6428124b4983f6ccbdae324c73044cd6a84269bfdbaff545331042e3d6845c647613b5d8f4ffdd48bad5b791623eca2be1b507652ea47b77e136cd2e26c70 + picocolors: ^1.1.0 + checksum: d00a18a3d04af5c76e7b763096650ebe16589864ab04eaf9e99c88aa77542f64de547b585037fc204d2055f9dd47fae94c789e2f173d3507a4e29dbe6609dd5b languageName: node linkType: hard -"@changesets/should-skip-package@npm:^0.1.0": - version: 0.1.0 - resolution: "@changesets/should-skip-package@npm:0.1.0" +"@changesets/should-skip-package@npm:^0.1.1": + version: 0.1.1 + resolution: "@changesets/should-skip-package@npm:0.1.1" dependencies: - "@babel/runtime": ^7.20.1 "@changesets/types": ^6.0.0 "@manypkg/get-packages": ^1.1.3 - checksum: 3c966feae085a41d4ce10a46a1b525f980cec59951f7cf23ea805866b370de5c75fdd45af326e1a5ef502fa4846f821d9df15550c2b606b96e99ed4b967f01c0 + checksum: d187ef22495deb63e678d0ff65e8627701e2b52c25bd59dde10ce8646be8d605c0ed0a6af020dd825b137c2fc748fdc6cef52e7774bad4c7a4f404bf182a85cf languageName: node linkType: hard @@ -8423,16 +8412,15 @@ __metadata: languageName: node linkType: hard -"@changesets/write@npm:^0.3.1": - version: 0.3.1 - resolution: "@changesets/write@npm:0.3.1" +"@changesets/write@npm:^0.3.2": + version: 0.3.2 + resolution: "@changesets/write@npm:0.3.2" dependencies: - "@babel/runtime": ^7.20.1 "@changesets/types": ^6.0.0 fs-extra: ^7.0.1 human-id: ^1.0.2 prettier: ^2.7.1 - checksum: 6df0447e05ededbab71f36e6ad23aa77cf06eb6adda7a8b8e7fb9d6bd5bc93acceb916d55b2a37cb7e93fb05d39a236a0dd7ade5243aae4772885081101d4784 + checksum: 553ed0ba6bd6397784f5e0e2921794bd7417a3c4fb810f1abb15e7072bf9d312af74308ff743161c6ea01478884cebcaf9cee02e5c70e2c7552b2774960ee07c languageName: node linkType: hard @@ -22430,7 +22418,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:2.4.2, chalk@npm:^2.1.0, chalk@npm:^2.3.2, chalk@npm:^2.4.2": +"chalk@npm:2.4.2, chalk@npm:^2.3.2, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -36138,6 +36126,13 @@ __metadata: languageName: node linkType: hard +"package-manager-detector@npm:^0.2.0": + version: 0.2.0 + resolution: "package-manager-detector@npm:0.2.0" + checksum: 3ba12d366aef0045d8341670eea71a3c9ef4efb00a411f45bf970bd526dbfc41b6baac4fb18a2585fe2d5f93dbb7245fbce4b4fcb89baa175ecf221c05f47db1 + languageName: node + linkType: hard + "pacote@npm:^12.0.0, pacote@npm:^12.0.2": version: 12.0.3 resolution: "pacote@npm:12.0.3" @@ -36800,10 +36795,10 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1": - version: 1.0.1 - resolution: "picocolors@npm:1.0.1" - checksum: fa68166d1f56009fc02a34cdfd112b0dd3cf1ef57667ac57281f714065558c01828cdf4f18600ad6851cbe0093952ed0660b1e0156bddf2184b6aaf5817553a5 +"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.0": + version: 1.1.0 + resolution: "picocolors@npm:1.1.0" + checksum: a64d653d3a188119ff45781dfcdaeedd7625583f45280aea33fcb032c7a0d3959f2368f9b192ad5e8aade75b74dbd954ffe3106c158509a45e4c18ab379a2acd languageName: node linkType: hard @@ -37586,7 +37581,7 @@ __metadata: languageName: node linkType: hard -"preferred-pm@npm:^3.0.0, preferred-pm@npm:^3.0.3": +"preferred-pm@npm:^3.0.3": version: 3.0.3 resolution: "preferred-pm@npm:3.0.3" dependencies: From 95442fba23fa2cb8e087bc6e618b701c2f77e4ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:25:22 +0000 Subject: [PATCH 171/317] chore(deps): update dependency msw to v2.4.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4479624fd3..042f895197 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34626,8 +34626,8 @@ __metadata: linkType: hard "msw@npm:^2.0.0, msw@npm:^2.0.8": - version: 2.4.1 - resolution: "msw@npm:2.4.1" + version: 2.4.2 + resolution: "msw@npm:2.4.2" dependencies: "@bundled-es-modules/cookie": ^2.0.0 "@bundled-es-modules/statuses": ^1.0.1 @@ -34647,7 +34647,7 @@ __metadata: yargs: ^17.7.2 peerDependencies: graphql: ">= 16.8.x" - typescript: ">= 4.7.x" + typescript: ">= 4.8.x" peerDependenciesMeta: graphql: optional: true @@ -34655,7 +34655,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: dc4d20bf856dfaed88c5ac438d4ee003ad8b56af2ca6e16ccd5e22b4f7fecb60a8df47d575bfd5d34c9bfc676263caa0426e1a9b7a539664b1a3427624ee1ffe + checksum: 13ea882de674ba4a4eafe261b1fa4ed0e885bc021286730a9d6454bff3688e79265d752c826b4b4536ece728eda3cae19a5ea30495bf51bdfa1caff43893bedf languageName: node linkType: hard From fecff816267e8dc905fb56e71c145006922596f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Jerna=C5=9B?= Date: Wed, 4 Sep 2024 15:18:27 +0200 Subject: [PATCH 172/317] fix(docs): Fix some more typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Jernaś --- .../frontend-system/architecture/23-extension-blueprints.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/frontend-system/architecture/23-extension-blueprints.md b/docs/frontend-system/architecture/23-extension-blueprints.md index ee09e7fec6..fb10749845 100644 --- a/docs/frontend-system/architecture/23-extension-blueprints.md +++ b/docs/frontend-system/architecture/23-extension-blueprints.md @@ -20,7 +20,7 @@ The following is a simple example of how one might use the blueprint `make` meth const myPageExtension = PageBlueprint.make({ params: { defaultPath: '/my-page', - loader: () => import('./components/MyPage').them(m => ), + loader: () => import('./components/MyPage').then(m => ), }, }); ``` @@ -47,7 +47,7 @@ const myPageExtension = PageBlueprint.makeWithOverrides({ return originalFactory({ defaultPath: '/my-page', loader: () => - import('./components/MyPage').them(m => ( + import('./components/MyPage').then(m => ( // We can now access values from the factory context when providing // the blueprint parameters, such as config values. @@ -104,7 +104,7 @@ Most of the options provided to `createExtensionBlueprint` can be overridden whe In some cases you may want to define and provide [extension data reference](./20-extensions.md#extension-data-references) that are specific to your blueprint. In the above example we might want to forward the `title` as data for example, rather than encapsulating it into the `MyWidgetContainer` component. This gives the parent extension more flexibility in the rendering for our example widget extensions. -To do that, we create a new extension data reference for our widget title. This references is provided via the `dataRefs` options when we create the blueprint, which makes it available for use via `MyWidgetBlueprint.dataRefs.widgetTitle`. +To do that, we create a new extension data reference for our widget title. This reference is provided via the `dataRefs` options when we create the blueprint, which makes it available for use via `MyWidgetBlueprint.dataRefs.widgetTitle`. ```tsx export interface MyWidgetBlueprintParams { From 384b6424f30d08440fd495a1548207a763454a79 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:20:29 +0000 Subject: [PATCH 173/317] fix(deps): update dependency express-openapi-validator to v5.3.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 709e5b2227..3e33d52c50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27019,8 +27019,8 @@ __metadata: linkType: hard "express-openapi-validator@npm:^5.0.4": - version: 5.3.3 - resolution: "express-openapi-validator@npm:5.3.3" + version: 5.3.4 + resolution: "express-openapi-validator@npm:5.3.4" dependencies: "@apidevtools/json-schema-ref-parser": ^11.7.0 "@types/multer": ^1.4.11 @@ -27037,7 +27037,7 @@ __metadata: path-to-regexp: ^6.2.2 peerDependencies: express: "*" - checksum: 75de6151475cce7b7222789656b774a48f2e8000be5eb7a3f0bf992652d98db1f429df60d2cd1a366a1dcd1f6058cd933d8bbbb8e98e30d6017d3a72b4d6b018 + checksum: 0c699e8f12d499d759e11c08009cc479d8748bb1adf9e31b72e7f1c0d56d6c6c2333e72124517724cb1d6c7bd428a1a6dfe241b15fbe16f513f373222baf2d77 languageName: node linkType: hard From 66e31cfc04641c267116aa49c878ab99616320a5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:21:59 +0000 Subject: [PATCH 174/317] fix(deps): update dependency google-auth-library to v9.14.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 709e5b2227..fc4b019e07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28522,8 +28522,8 @@ __metadata: linkType: hard "google-auth-library@npm:^9.0.0, google-auth-library@npm:^9.3.0, google-auth-library@npm:^9.6.3": - version: 9.14.0 - resolution: "google-auth-library@npm:9.14.0" + version: 9.14.1 + resolution: "google-auth-library@npm:9.14.1" dependencies: base64-js: ^1.3.0 ecdsa-sig-formatter: ^1.0.11 @@ -28531,7 +28531,7 @@ __metadata: gcp-metadata: ^6.1.0 gtoken: ^7.0.0 jws: ^4.0.0 - checksum: ff2a66e84726b3a25711a64583d86cf9e0c0df857d538ce8127822d220e81e1e7d0defcedecb2be7fc407c104bb7d5ca26a4f201bde871c8d09ef72cc83603f8 + checksum: 98c7ffb6ef8d811a54d728a94c31aa60c777f035306f0ded70654ce0aa1f4dcf393bb505b262aa48438f5ead8941248f3759f24f0e22b4465b8537b1d90415ac languageName: node linkType: hard From bffd11fb40872dd73293f28d9562844df5af173a Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Tue, 3 Sep 2024 16:28:19 +0200 Subject: [PATCH 175/317] feat: started porting the publish:bitbucketServer:pull-request action to Bitbucket Cloud Signed-off-by: Benjamin Janssens --- .../src/actions/bitbucketCloudPullRequest.ts | 468 ++++++++++++++++++ .../src/actions/index.ts | 1 + .../src/module.ts | 5 + .../actions/builtin/createBuiltinActions.ts | 2 + 4 files changed, 476 insertions(+) create mode 100644 plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts new file mode 100644 index 0000000000..b5cce9d0a4 --- /dev/null +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -0,0 +1,468 @@ +/* + * 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 { InputError } from '@backstage/errors'; +import { + getBitbucketServerRequestOptions, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { + createTemplateAction, + getRepoSourceDirectory, + commitAndPushBranch, + addFiles, + createBranch as createGitBranch, + cloneRepo, + parseRepoUrl, +} from '@backstage/plugin-scaffolder-node'; +import fetch, { RequestInit, Response } from 'node-fetch'; +import { Config } from '@backstage/config'; +import fs from 'fs-extra'; +import { getAuthorizationHeader } from './helpers'; + +const createPullRequest = async (opts: { + workspace: string; + repo: string; + title: string; + description?: string; + targetBranch: string; + sourceBranch: string; + authorization: string; + apiBaseUrl: string; +}) => { + const { + workspace, + repo, + title, + description, + targetBranch, + sourceBranch, + authorization, + apiBaseUrl, + } = opts; + + let response: Response; + const data: RequestInit = { + method: 'POST', + body: JSON.stringify({ + title: title, + summary: { + raw: description, + }, + state: 'OPEN', + source: { + branch: { + name: sourceBranch, + }, + }, + destination: { + branch: { + name: targetBranch, + }, + }, + }), + headers: { + Authorization: authorization, + 'Content-Type': 'application/json', + }, + }; + + try { + response = await fetch( + `${apiBaseUrl}/repositories/${workspace}/${repo}/pullrequests`, + data, + ); + } catch (e) { + throw new Error(`Unable to create pull-reqeusts, ${e}`); + } + + if (response.status !== 201) { + throw new Error( + `Unable to create pull requests, ${response.status} ${ + response.statusText + }, ${await response.text()}`, + ); + } + + const r = await response.json(); + return r.links.self.href; +}; + +const findBranches = async (opts: { + project: string; + repo: string; + branchName: string; + authorization: string; + apiBaseUrl: string; +}) => { + const { project, repo, branchName, authorization, apiBaseUrl } = opts; + + let response: Response; + const options: RequestInit = { + method: 'GET', + headers: { + Authorization: authorization, + 'Content-Type': 'application/json', + }, + }; + + try { + response = await fetch( + `${apiBaseUrl}/projects/${encodeURIComponent( + project, + )}/repos/${encodeURIComponent( + repo, + )}/branches?boostMatches=true&filterText=${encodeURIComponent( + branchName, + )}`, + options, + ); + } catch (e) { + throw new Error(`Unable to get branches, ${e}`); + } + + if (response.status !== 200) { + throw new Error( + `Unable to get branches, ${response.status} ${ + response.statusText + }, ${await response.text()}`, + ); + } + + const r = await response.json(); + for (const object of r.values) { + if (object.displayId === branchName) { + return object; + } + } + + return undefined; +}; +const createBranch = async (opts: { + project: string; + repo: string; + branchName: string; + authorization: string; + apiBaseUrl: string; + startPoint: string; +}) => { + const { project, repo, branchName, authorization, apiBaseUrl, startPoint } = + opts; + + let response: Response; + const options: RequestInit = { + method: 'POST', + body: JSON.stringify({ + name: branchName, + startPoint, + }), + headers: { + Authorization: authorization, + 'Content-Type': 'application/json', + }, + }; + + try { + response = await fetch( + `${apiBaseUrl}/projects/${encodeURIComponent( + project, + )}/repos/${encodeURIComponent(repo)}/branches`, + options, + ); + } catch (e) { + throw new Error(`Unable to create branch, ${e}`); + } + + if (response.status !== 200) { + throw new Error( + `Unable to create branch, ${response.status} ${ + response.statusText + }, ${await response.text()}`, + ); + } + + return await response.json(); +}; +const getDefaultBranch = async (opts: { + workspace: string; + repo: string; + authorization: string; + apiBaseUrl: string; +}): Promise => { + const { workspace, repo, authorization, apiBaseUrl } = opts; + let response: Response; + + const options: RequestInit = { + method: 'GET', + headers: { + Authorization: authorization, + 'Content-Type': 'application/json', + }, + }; + + try { + response = await fetch( + `${apiBaseUrl}/repositories/${workspace}/${repo}`, + options, + ); + } catch (error) { + throw error; + } + + const { mainbranch } = await response.json(); + console.log(authorization); + const defaultBranch = mainbranch.name; + if (!defaultBranch) { + throw new Error(`Could not fetch default branch for ${workspace}/${repo}`); + } + return defaultBranch; +}; +/** + * Creates a Bitbucket Cloud Pull Request action. + * @public + */ +export function createPublishBitbucketCloudPullRequestAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; +}) { + const { integrations, config } = options; + + return createTemplateAction<{ + repoUrl: string; + title: string; + description?: string; + targetBranch?: string; + sourceBranch: string; + token?: string; + gitAuthorName?: string; + gitAuthorEmail?: string; + }>({ + id: 'publish:bitbucketCloud:pull-request', + // examples, + schema: { + input: { + type: 'object', + required: ['repoUrl', 'title', 'sourceBranch'], + properties: { + repoUrl: { + title: 'Repository Location', + type: 'string', + }, + title: { + title: 'Pull Request title', + type: 'string', + description: 'The title for the pull request', + }, + description: { + title: 'Pull Request Description', + type: 'string', + description: 'The description of the pull request', + }, + targetBranch: { + title: 'Target Branch', + type: 'string', + description: `Branch of repository to apply changes to. The default value is 'master'`, + }, + sourceBranch: { + title: 'Source Branch', + type: 'string', + description: 'Branch of repository to copy changes from', + }, + token: { + title: 'Authorization Token', + type: 'string', + description: + 'The token to use for authorization to BitBucket Cloud', + }, + gitAuthorName: { + title: 'Author Name', + type: 'string', + description: `Sets the author name for the commit. The default value is 'Scaffolder'`, + }, + gitAuthorEmail: { + title: 'Author Email', + type: 'string', + description: `Sets the author email for the commit.`, + }, + }, + }, + output: { + type: 'object', + properties: { + pullRequestUrl: { + title: 'A URL to the pull request with the provider', + type: 'string', + }, + }, + }, + }, + async handler(ctx) { + const { + repoUrl, + title, + description, + targetBranch, + sourceBranch, + gitAuthorName, + gitAuthorEmail, + } = ctx.input; + + const { project, repo, host, workspace } = parseRepoUrl( + repoUrl, + integrations, + ); + + if (!project) { + throw new InputError( + `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`, + ); + } + + const integrationConfig = integrations.bitbucketCloud.byHost(host); + if (!integrationConfig) { + throw new InputError( + `No matching integration configuration for host ${host}, please check your integrations config`, + ); + } + + const authorization = getAuthorizationHeader( + ctx.input.token ? { token: ctx.input.token } : integrationConfig.config, + ); + + const apiBaseUrl = integrationConfig.config.apiBaseUrl; + + let finalTargetBranch = targetBranch; + if (!finalTargetBranch) { + finalTargetBranch = await getDefaultBranch({ + workspace: workspace!, + repo, + authorization, + apiBaseUrl, + }); + } + + // const toRef = await findBranches({ + // project, + // repo, + // branchName: finalTargetBranch!, + // authorization, + // apiBaseUrl, + // }); + + // let fromRef = await findBranches({ + // project, + // repo, + // branchName: sourceBranch, + // authorization, + // apiBaseUrl, + // }); + + // if (!fromRef) { + // // create branch + // ctx.logger.info( + // `source branch not found -> creating branch named: ${sourceBranch} lastCommit: ${toRef.latestCommit}`, + // ); + // const latestCommit = toRef.latestCommit; + + // fromRef = await createBranch({ + // project, + // repo, + // branchName: sourceBranch, + // authorization, + // apiBaseUrl, + // startPoint: latestCommit, + // }); + + // const remoteUrl = `https://${host}/scm/${project}/${repo}.git`; + + // const auth = authConfig.token + // ? { + // token: token!, + // } + // : { + // username: authConfig.username!, + // password: authConfig.password!, + // }; + + // const gitAuthorInfo = { + // name: + // gitAuthorName || + // config.getOptionalString('scaffolder.defaultAuthor.name'), + // email: + // gitAuthorEmail || + // config.getOptionalString('scaffolder.defaultAuthor.email'), + // }; + + // const tempDir = await ctx.createTemporaryDirectory(); + // const sourceDir = getRepoSourceDirectory(ctx.workspacePath, undefined); + // await cloneRepo({ + // url: remoteUrl, + // dir: tempDir, + // auth, + // logger: ctx.logger, + // ref: sourceBranch, + // }); + + // await createGitBranch({ + // dir: tempDir, + // auth, + // logger: ctx.logger, + // ref: sourceBranch, + // }); + + // // copy files + // fs.cpSync(sourceDir, tempDir, { + // recursive: true, + // filter: path => { + // return !(path.indexOf('.git') > -1); + // }, + // }); + + // await addFiles({ + // dir: tempDir, + // auth, + // logger: ctx.logger, + // filepath: '.', + // }); + + // await commitAndPushBranch({ + // dir: tempDir, + // auth, + // logger: ctx.logger, + // commitMessage: + // description ?? + // config.getOptionalString('scaffolder.defaultCommitMessage') ?? + // '', + // gitAuthorInfo, + // branch: sourceBranch, + // }); + // } + + const pullRequestUrl = await createPullRequest({ + workspace: workspace!, + repo, + title, + description, + targetBranch: finalTargetBranch, + sourceBranch, + authorization, + apiBaseUrl, + }); + + ctx.output('pullRequestUrl', pullRequestUrl); + }, + }); +} diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/index.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/index.ts index a9826a268a..c4030c0b52 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/index.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/index.ts @@ -15,3 +15,4 @@ */ export * from './bitbucketCloud'; export { createBitbucketPipelinesRunAction } from './bitbucketCloudPipelinesRun'; +export * from './bitbucketCloudPullRequest'; diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts index 5bf49f7cd9..c0f922ca08 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts @@ -24,6 +24,7 @@ import { import { createBitbucketPipelinesRunAction, createPublishBitbucketCloudAction, + createPublishBitbucketCloudPullRequestAction, } from './actions'; import { ScmIntegrations } from '@backstage/integration'; import { handleAutocompleteRequest } from './autocomplete/autocomplete'; @@ -48,6 +49,10 @@ export const bitbucketCloudModule = createBackendModule({ scaffolder.addActions( createPublishBitbucketCloudAction({ integrations, config }), createBitbucketPipelinesRunAction({ integrations }), + createPublishBitbucketCloudPullRequestAction({ + integrations, + config, + }), ); autocomplete.addAutocompleteProvider({ diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index dba867c96b..e2392150cc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -62,6 +62,7 @@ import { createPublishBitbucketAction } from '@backstage/plugin-scaffolder-backe import { createPublishBitbucketCloudAction, createBitbucketPipelinesRunAction, + createPublishBitbucketCloudPullRequestAction, } from '@backstage/plugin-scaffolder-backend-module-bitbucket-cloud'; import { @@ -197,6 +198,7 @@ export const createBuiltinActions = ( integrations, config, }), + createPublishBitbucketCloudPullRequestAction({ integrations, config }), createPublishBitbucketServerAction({ integrations, config, From 7f65deff34dda856ae9c0845981af1bc3616c8cd Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Tue, 3 Sep 2024 17:17:54 +0200 Subject: [PATCH 176/317] feat: make the publish:bitbucketCloud:pull-request action work end-to-end Signed-off-by: Benjamin Janssens --- .../src/actions/bitbucketCloudPullRequest.ts | 191 +++++++++--------- 1 file changed, 92 insertions(+), 99 deletions(-) diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts index b5cce9d0a4..47261021b6 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -15,10 +15,7 @@ */ import { InputError } from '@backstage/errors'; -import { - getBitbucketServerRequestOptions, - ScmIntegrationRegistry, -} from '@backstage/integration'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import { createTemplateAction, getRepoSourceDirectory, @@ -152,22 +149,30 @@ const findBranches = async (opts: { return undefined; }; const createBranch = async (opts: { - project: string; + workspace: string; repo: string; branchName: string; authorization: string; apiBaseUrl: string; - startPoint: string; + startBranch: string; }) => { - const { project, repo, branchName, authorization, apiBaseUrl, startPoint } = - opts; + const { + workspace, + repo, + branchName, + authorization, + apiBaseUrl, + startBranch, + } = opts; let response: Response; const options: RequestInit = { method: 'POST', body: JSON.stringify({ name: branchName, - startPoint, + target: { + hash: startBranch, + }, }), headers: { Authorization: authorization, @@ -177,16 +182,14 @@ const createBranch = async (opts: { try { response = await fetch( - `${apiBaseUrl}/projects/${encodeURIComponent( - project, - )}/repos/${encodeURIComponent(repo)}/branches`, + `${apiBaseUrl}/repositories/${workspace}/${repo}/refs/branches`, options, ); } catch (e) { throw new Error(`Unable to create branch, ${e}`); } - if (response.status !== 200) { + if (response.status !== 201) { throw new Error( `Unable to create branch, ${response.status} ${ response.statusText @@ -354,102 +357,92 @@ export function createPublishBitbucketCloudPullRequestAction(options: { }); } - // const toRef = await findBranches({ - // project, - // repo, - // branchName: finalTargetBranch!, - // authorization, - // apiBaseUrl, - // }); + await createBranch({ + workspace: workspace!, + repo, + branchName: sourceBranch, + authorization, + apiBaseUrl, + startBranch: finalTargetBranch, + }); - // let fromRef = await findBranches({ - // project, - // repo, - // branchName: sourceBranch, - // authorization, - // apiBaseUrl, - // }); + const remoteUrl = `https://${host}/${workspace}/${repo}.git`; - // if (!fromRef) { - // // create branch - // ctx.logger.info( - // `source branch not found -> creating branch named: ${sourceBranch} lastCommit: ${toRef.latestCommit}`, - // ); - // const latestCommit = toRef.latestCommit; + let auth; - // fromRef = await createBranch({ - // project, - // repo, - // branchName: sourceBranch, - // authorization, - // apiBaseUrl, - // startPoint: latestCommit, - // }); + if (ctx.input.token) { + auth = { + username: 'x-token-auth', + password: ctx.input.token, + }; + } else { + if ( + !integrationConfig.config.username || + !integrationConfig.config.appPassword + ) { + throw new Error( + 'Credentials for Bitbucket Cloud integration required for this action.', + ); + } - // const remoteUrl = `https://${host}/scm/${project}/${repo}.git`; + auth = { + username: integrationConfig.config.username, + password: integrationConfig.config.appPassword, + }; + } - // const auth = authConfig.token - // ? { - // token: token!, - // } - // : { - // username: authConfig.username!, - // password: authConfig.password!, - // }; + const gitAuthorInfo = { + name: + gitAuthorName || + config.getOptionalString('scaffolder.defaultAuthor.name'), + email: + gitAuthorEmail || + config.getOptionalString('scaffolder.defaultAuthor.email'), + }; - // const gitAuthorInfo = { - // name: - // gitAuthorName || - // config.getOptionalString('scaffolder.defaultAuthor.name'), - // email: - // gitAuthorEmail || - // config.getOptionalString('scaffolder.defaultAuthor.email'), - // }; + const tempDir = await ctx.createTemporaryDirectory(); + const sourceDir = getRepoSourceDirectory(ctx.workspacePath, undefined); + await cloneRepo({ + url: remoteUrl, + dir: tempDir, + auth, + logger: ctx.logger, + ref: sourceBranch, + }); - // const tempDir = await ctx.createTemporaryDirectory(); - // const sourceDir = getRepoSourceDirectory(ctx.workspacePath, undefined); - // await cloneRepo({ - // url: remoteUrl, - // dir: tempDir, - // auth, - // logger: ctx.logger, - // ref: sourceBranch, - // }); + await createGitBranch({ + dir: tempDir, + auth, + logger: ctx.logger, + ref: sourceBranch, + }); - // await createGitBranch({ - // dir: tempDir, - // auth, - // logger: ctx.logger, - // ref: sourceBranch, - // }); + // copy files + fs.cpSync(sourceDir, tempDir, { + recursive: true, + filter: path => { + return !(path.indexOf('.git') > -1); + }, + }); - // // copy files - // fs.cpSync(sourceDir, tempDir, { - // recursive: true, - // filter: path => { - // return !(path.indexOf('.git') > -1); - // }, - // }); + await addFiles({ + dir: tempDir, + auth, + logger: ctx.logger, + filepath: '.', + }); - // await addFiles({ - // dir: tempDir, - // auth, - // logger: ctx.logger, - // filepath: '.', - // }); - - // await commitAndPushBranch({ - // dir: tempDir, - // auth, - // logger: ctx.logger, - // commitMessage: - // description ?? - // config.getOptionalString('scaffolder.defaultCommitMessage') ?? - // '', - // gitAuthorInfo, - // branch: sourceBranch, - // }); - // } + await commitAndPushBranch({ + dir: tempDir, + auth, + logger: ctx.logger, + commitMessage: + description ?? + config.getOptionalString('scaffolder.defaultCommitMessage') ?? + '', + gitAuthorInfo, + branch: sourceBranch, + }); const pullRequestUrl = await createPullRequest({ workspace: workspace!, From 8a36e52d428805555de59bf29e45ef44e4b17a0f Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 09:40:56 +0200 Subject: [PATCH 177/317] fix: validate workspace instead of project Signed-off-by: Benjamin Janssens --- .../src/actions/bitbucketCloudPullRequest.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts index 47261021b6..033eb59b6c 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -323,12 +323,9 @@ export function createPublishBitbucketCloudPullRequestAction(options: { gitAuthorEmail, } = ctx.input; - const { project, repo, host, workspace } = parseRepoUrl( - repoUrl, - integrations, - ); + const { workspace, repo, host } = parseRepoUrl(repoUrl, integrations); - if (!project) { + if (!workspace) { throw new InputError( `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`, ); @@ -350,15 +347,17 @@ export function createPublishBitbucketCloudPullRequestAction(options: { let finalTargetBranch = targetBranch; if (!finalTargetBranch) { finalTargetBranch = await getDefaultBranch({ - workspace: workspace!, + workspace, repo, authorization, apiBaseUrl, }); } + // TODO: check if sourceBranch already exists and just create a pull request for it if so + await createBranch({ - workspace: workspace!, + workspace, repo, branchName: sourceBranch, authorization, @@ -445,7 +444,7 @@ export function createPublishBitbucketCloudPullRequestAction(options: { }); const pullRequestUrl = await createPullRequest({ - workspace: workspace!, + workspace, repo, title, description, From f582198055986e01a1ed30686f330da67cbefc41 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 09:48:02 +0200 Subject: [PATCH 178/317] fix: remove debugging console.log Signed-off-by: Benjamin Janssens --- .../src/actions/bitbucketCloudPullRequest.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts index 033eb59b6c..faec1b6da8 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -226,7 +226,6 @@ const getDefaultBranch = async (opts: { } const { mainbranch } = await response.json(); - console.log(authorization); const defaultBranch = mainbranch.name; if (!defaultBranch) { throw new Error(`Could not fetch default branch for ${workspace}/${repo}`); From c53e1769f2929c8baa9d389aebf7d4e7597c8159 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 11:24:03 +0200 Subject: [PATCH 179/317] feat: implement source branch check Signed-off-by: Benjamin Janssens --- .../src/actions/bitbucketCloudPullRequest.ts | 182 +++++++++--------- 1 file changed, 93 insertions(+), 89 deletions(-) diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts index faec1b6da8..71944c18ae 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -99,13 +99,13 @@ const createPullRequest = async (opts: { }; const findBranches = async (opts: { - project: string; + workspace: string; repo: string; branchName: string; authorization: string; apiBaseUrl: string; }) => { - const { project, repo, branchName, authorization, apiBaseUrl } = opts; + const { workspace, repo, branchName, authorization, apiBaseUrl } = opts; let response: Response; const options: RequestInit = { @@ -118,12 +118,8 @@ const findBranches = async (opts: { try { response = await fetch( - `${apiBaseUrl}/projects/${encodeURIComponent( - project, - )}/repos/${encodeURIComponent( - repo, - )}/branches?boostMatches=true&filterText=${encodeURIComponent( - branchName, + `${apiBaseUrl}/repositories/${workspace}/${repo}/refs/branches?q=${encodeURIComponent( + `name = "${branchName}"`, )}`, options, ); @@ -140,13 +136,8 @@ const findBranches = async (opts: { } const r = await response.json(); - for (const object of r.values) { - if (object.displayId === branchName) { - return object; - } - } - return undefined; + return r.values[0]; }; const createBranch = async (opts: { workspace: string; @@ -353,95 +344,108 @@ export function createPublishBitbucketCloudPullRequestAction(options: { }); } - // TODO: check if sourceBranch already exists and just create a pull request for it if so - - await createBranch({ + const sourceBranchRef = await findBranches({ workspace, repo, branchName: sourceBranch, authorization, apiBaseUrl, - startBranch: finalTargetBranch, }); - const remoteUrl = `https://${host}/${workspace}/${repo}.git`; + if (!sourceBranchRef) { + // create branch + ctx.logger.info( + `source branch not found -> creating branch named: ${sourceBranch}`, + ); - let auth; + await createBranch({ + workspace, + repo, + branchName: sourceBranch, + authorization, + apiBaseUrl, + startBranch: finalTargetBranch, + }); - if (ctx.input.token) { - auth = { - username: 'x-token-auth', - password: ctx.input.token, - }; - } else { - if ( - !integrationConfig.config.username || - !integrationConfig.config.appPassword - ) { - throw new Error( - 'Credentials for Bitbucket Cloud integration required for this action.', - ); + const remoteUrl = `https://${host}/${workspace}/${repo}.git`; + + let auth; + + if (ctx.input.token) { + auth = { + username: 'x-token-auth', + password: ctx.input.token, + }; + } else { + if ( + !integrationConfig.config.username || + !integrationConfig.config.appPassword + ) { + throw new Error( + 'Credentials for Bitbucket Cloud integration required for this action.', + ); + } + + auth = { + username: integrationConfig.config.username, + password: integrationConfig.config.appPassword, + }; } - auth = { - username: integrationConfig.config.username, - password: integrationConfig.config.appPassword, + const gitAuthorInfo = { + name: + gitAuthorName || + config.getOptionalString('scaffolder.defaultAuthor.name'), + email: + gitAuthorEmail || + config.getOptionalString('scaffolder.defaultAuthor.email'), }; + + const tempDir = await ctx.createTemporaryDirectory(); + const sourceDir = getRepoSourceDirectory(ctx.workspacePath, undefined); + await cloneRepo({ + url: remoteUrl, + dir: tempDir, + auth, + logger: ctx.logger, + ref: sourceBranch, + }); + + await createGitBranch({ + dir: tempDir, + auth, + logger: ctx.logger, + ref: sourceBranch, + }); + + // copy files + fs.cpSync(sourceDir, tempDir, { + recursive: true, + filter: path => { + return !(path.indexOf('.git') > -1); + }, + }); + + await addFiles({ + dir: tempDir, + auth, + logger: ctx.logger, + filepath: '.', + }); + + await commitAndPushBranch({ + dir: tempDir, + auth, + logger: ctx.logger, + commitMessage: + description ?? + config.getOptionalString('scaffolder.defaultCommitMessage') ?? + '', + gitAuthorInfo, + branch: sourceBranch, + }); } - const gitAuthorInfo = { - name: - gitAuthorName || - config.getOptionalString('scaffolder.defaultAuthor.name'), - email: - gitAuthorEmail || - config.getOptionalString('scaffolder.defaultAuthor.email'), - }; - - const tempDir = await ctx.createTemporaryDirectory(); - const sourceDir = getRepoSourceDirectory(ctx.workspacePath, undefined); - await cloneRepo({ - url: remoteUrl, - dir: tempDir, - auth, - logger: ctx.logger, - ref: sourceBranch, - }); - - await createGitBranch({ - dir: tempDir, - auth, - logger: ctx.logger, - ref: sourceBranch, - }); - - // copy files - fs.cpSync(sourceDir, tempDir, { - recursive: true, - filter: path => { - return !(path.indexOf('.git') > -1); - }, - }); - - await addFiles({ - dir: tempDir, - auth, - logger: ctx.logger, - filepath: '.', - }); - - await commitAndPushBranch({ - dir: tempDir, - auth, - logger: ctx.logger, - commitMessage: - description ?? - config.getOptionalString('scaffolder.defaultCommitMessage') ?? - '', - gitAuthorInfo, - branch: sourceBranch, - }); - const pullRequestUrl = await createPullRequest({ workspace, repo, From aa5c1f08a1ccad1b6a1d980c572a66eda67a65a6 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 12:16:04 +0200 Subject: [PATCH 180/317] docs: add examples Signed-off-by: Benjamin Janssens --- .../bitbucketCloudPullRequest.examples.ts | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.ts diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.ts new file mode 100644 index 0000000000..75d99c30c2 --- /dev/null +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.ts @@ -0,0 +1,123 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: + 'Creating pull request on bitbucket cloud with required fields', + example: yaml.stringify({ + steps: [ + { + action: 'publish:bitbucketCloud:pull-request', + id: 'publish-bitbucket-cloud-pull-request-minimal', + name: 'Creating pull request on bitbucket cloud', + input: { + repoUrl: + 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'My pull request', + sourceBranch: 'my-feature-branch', + }, + }, + ], + }), + }, + { + description: + 'Creating pull request on bitbucket cloud with custom descriptions', + example: yaml.stringify({ + steps: [ + { + action: 'publish:bitbucketCloud:pull-request', + id: 'publish-bitbucket-cloud-pull-request-minimal', + name: 'Creating pull request on bitbucket cloud', + input: { + repoUrl: + 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'My pull request', + sourceBranch: 'my-feature-branch', + description: 'This is a detailed description of my pull request', + }, + }, + ], + }), + }, + { + description: + 'Creating pull request on bitbucket cloud with different target branch', + example: yaml.stringify({ + steps: [ + { + action: 'publish:bitbucketCloud:pull-request', + id: 'publish-bitbucket-cloud-pull-request-target-branch', + name: 'Creating pull request on bitbucket cloud', + input: { + repoUrl: + 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'My pull request', + sourceBranch: 'my-feature-branch', + targetBranch: 'development', + }, + }, + ], + }), + }, + { + description: + 'Creating pull request on bitbucket cloud with authorization token', + example: yaml.stringify({ + steps: [ + { + action: 'publish:bitbucketCloud:pull-request', + id: 'publish-bitbucket-cloud-pull-request-minimal', + name: 'Creating pull request on bitbucket cloud', + input: { + repoUrl: + 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'My pull request', + sourceBranch: 'my-feature-branch', + token: 'my-auth-token', + }, + }, + ], + }), + }, + { + description: 'Creating pull request on bitbucket cloud with all fields', + example: yaml.stringify({ + steps: [ + { + action: 'publish:bitbucketCloud:pull-request', + id: 'publish-bitbucket-cloud-pull-request-minimal', + name: 'Creating pull request on bitbucket cloud', + input: { + repoUrl: + 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'My pull request', + sourceBranch: 'my-feature-branch', + targetBranch: 'development', + description: 'This is a detailed description of my pull request', + token: 'my-auth-token', + gitAuthorName: 'test-user', + gitAuthorEmail: 'test-user@sample.com', + }, + }, + ], + }), + }, +]; From c7c7b2f19078b4638b6ef938d94f31498adfea17 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 13:55:04 +0200 Subject: [PATCH 181/317] test: add tests for examples Signed-off-by: Benjamin Janssens --- ...bitbucketCloudPullRequest.examples.test.ts | 431 ++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts new file mode 100644 index 0000000000..6d5aaaeab1 --- /dev/null +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts @@ -0,0 +1,431 @@ +/* + * 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. + */ + +jest.mock('@backstage/plugin-scaffolder-node', () => { + return { + ...jest.requireActual('@backstage/plugin-scaffolder-node'), + initRepoAndPush: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + commitAndPushRepo: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + }; +}); + +import { createPublishBitbucketCloudPullRequestAction } from './bitbucketCloudPullRequest'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { registerMswTestHooks } from '@backstage/backend-test-utils'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import yaml from 'yaml'; +import { examples } from './bitbucketCloudPullRequest.examples'; +import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; + +describe('publish:bitbucketCloud:pull-request', () => { + const config = new ConfigReader({ + integrations: { + bitbucketCloud: [ + { + username: 'test-user', + appPassword: 'test-password', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishBitbucketCloudPullRequestAction({ + integrations, + config, + }); + const mockContext = createMockActionContext({ + input: { + repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'Add Scaffolder actions for Bitbucket Cloud', + description: + 'I just made a Pull Request that Add Scaffolder actions for Bitbucket Cloud', + targetBranch: 'master', + sourceBranch: 'develop', + }, + }); + const responseOfBranches = { + pagelen: 1, + size: 187, + values: [ + { + name: 'issue-9.3/AUI-5343-assistive-class', + links: { + commits: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commits/issue-9.3/AUI-5343-assistive-class', + }, + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches/issue-9.3/AUI-5343-assistive-class', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/branch/issue-9.3/AUI-5343-assistive-class', + }, + }, + default_merge_strategy: 'squash', + merge_strategies: ['merge_commit', 'squash', 'fast_forward'], + type: 'branch', + target: { + hash: 'e5d1cde9069fcb9f0af90403a4de2150c125a148', + repository: { + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui', + }, + html: { href: 'https://bitbucket.org/atlassian/aui' }, + avatar: { + href: 'https://bytebucket.org/ravatar/%7B585074de-7b60-4fd1-81ed-e0bc7fafbda5%7D?ts=86317', + }, + }, + type: 'repository', + name: 'aui', + full_name: 'atlassian/aui', + uuid: '{585074de-7b60-4fd1-81ed-e0bc7fafbda5}', + }, + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + comments: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/comments', + }, + patch: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/patch/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/commits/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + diff: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/diff/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + approve: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/approve', + }, + statuses: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/statuses', + }, + }, + author: { + raw: 'Marcin Konopka ', + type: 'author', + user: { + display_name: 'Marcin Konopka', + uuid: '{47cc24f4-2a05-4420-88fe-0417535a110a}', + links: { + self: { + href: 'https://api.bitbucket.org/2.0/users/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D', + }, + html: { + href: 'https://bitbucket.org/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D/', + }, + avatar: { + href: 'https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/initials/MK-1.png', + }, + }, + nickname: 'Marcin Konopka', + type: 'user', + account_id: '60113d2b47a9540069f4de03', + }, + }, + parents: [ + { + hash: '87f7fc92b00464ae47b13ef65c91884e4ac9be51', + type: 'commit', + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/87f7fc92b00464ae47b13ef65c91884e4ac9be51', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/commits/87f7fc92b00464ae47b13ef65c91884e4ac9be51', + }, + }, + }, + ], + date: '2021-04-13T13:44:49+00:00', + message: 'wip\n', + type: 'commit', + }, + }, + ], + page: 1, + next: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches?pagelen=1&page=2', + }; + const responseOfPullRequests = { + type: '', + links: { + self: { href: '', name: '' }, + html: { href: '', name: '' }, + commits: { href: '', name: '' }, + approve: { href: '', name: '' }, + diff: { href: '', name: '' }, + diffstat: { href: '', name: '' }, + comments: { href: '', name: '' }, + activity: { href: '', name: '' }, + merge: { href: '', name: '' }, + decline: { href: '', name: '' }, + }, + id: 108, + title: '', + rendered: { + title: { raw: '', markup: 'markdown', html: '' }, + description: { raw: '', markup: 'markdown', html: '' }, + reason: { raw: '', markup: 'markdown', html: '' }, + }, + summary: { raw: '', markup: 'markdown', html: '' }, + state: 'OPEN', + author: { type: '' }, + source: { + repository: { type: '' }, + branch: { + name: '', + merge_strategies: ['merge_commit'], + default_merge_strategy: '', + }, + commit: { hash: '' }, + }, + destination: { + repository: { type: '' }, + branch: { + name: '', + merge_strategies: ['merge_commit'], + default_merge_strategy: '', + }, + commit: { hash: '' }, + }, + merge_commit: { hash: '' }, + comment_count: 51, + task_count: 53, + close_source_branch: true, + closed_by: { type: '' }, + reason: '', + created_on: '', + updated_on: '', + reviewers: [{ type: '' }], + participants: [{ type: '' }], + }; + + const server = setupServer(); + registerMswTestHooks(server); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it(`should ${examples[0].description}`, async () => { + expect.assertions(2); + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Basic ${btoa('test-user:test-password')}`, + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Basic ${btoa('test-user:test-password')}`, + ); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...yaml.parse(examples[0].example).steps[0].input, + }, + }); + }); + + it(`should ${examples[1].description}`, async () => { + expect.assertions(2); + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Basic ${btoa('test-user:test-password')}`, + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Basic ${btoa('test-user:test-password')}`, + ); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...yaml.parse(examples[1].example).steps[0].input, + }, + }); + }); + + it(`should ${examples[2].description}`, async () => { + expect.assertions(2); + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Basic ${btoa('test-user:test-password')}`, + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Basic ${btoa('test-user:test-password')}`, + ); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...yaml.parse(examples[2].example).steps[0].input, + }, + }); + }); + + it(`should ${examples[3].description}`, async () => { + expect.assertions(2); + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Bearer ${yaml.parse(examples[3].example).steps[0].input.token}`, + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Bearer ${yaml.parse(examples[3].example).steps[0].input.token}`, + ); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...yaml.parse(examples[3].example).steps[0].input, + }, + }); + }); + + it(`should ${examples[4].description}`, async () => { + expect.assertions(2); + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Bearer ${yaml.parse(examples[4].example).steps[0].input.token}`, + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + `Bearer ${yaml.parse(examples[4].example).steps[0].input.token}`, + ); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + ...yaml.parse(examples[4].example).steps[0].input, + }, + }); + }); +}); From 55f685fd9f137972065e9889d31be87468d2e4c1 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 14:35:26 +0200 Subject: [PATCH 182/317] test: add more tests Signed-off-by: Benjamin Janssens --- ...bitbucketCloudPullRequest.examples.test.ts | 7 +- .../actions/bitbucketCloudPullRequest.test.ts | 418 ++++++++++++++++++ .../src/actions/bitbucketCloudPullRequest.ts | 2 +- 3 files changed, 424 insertions(+), 3 deletions(-) create mode 100644 plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts index 6d5aaaeab1..14d33b8dba 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -171,7 +171,10 @@ describe('publish:bitbucketCloud:pull-request', () => { const responseOfPullRequests = { type: '', links: { - self: { href: '', name: '' }, + self: { + href: 'https://bitbucket.org/workspace/repo/pull-requests/1', + name: '', + }, html: { href: '', name: '' }, commits: { href: '', name: '' }, approve: { href: '', name: '' }, diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts new file mode 100644 index 0000000000..ef900a57c7 --- /dev/null +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.test.ts @@ -0,0 +1,418 @@ +/* + * 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. + */ + +jest.mock('@backstage/plugin-scaffolder-node', () => { + return { + ...jest.requireActual('@backstage/plugin-scaffolder-node'), + initRepoAndPush: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + commitAndPushRepo: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + }; +}); + +import { createPublishBitbucketCloudPullRequestAction } from './bitbucketCloudPullRequest'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { registerMswTestHooks } from '@backstage/backend-test-utils'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; + +describe('publish:bitbucketCloud:pull-request', () => { + const config = new ConfigReader({ + integrations: { + bitbucketCloud: [ + { + username: 'test-user', + appPassword: 'test-password', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishBitbucketCloudPullRequestAction({ + integrations, + config, + }); + const mockContext = createMockActionContext({ + input: { + repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', + title: 'Add Scaffolder actions for Bitbucket Cloud', + description: + 'I just made a Pull Request that Add Scaffolder actions for Bitbucket Cloud', + targetBranch: 'master', + sourceBranch: 'develop', + }, + }); + const responseOfBranches = { + pagelen: 1, + size: 187, + values: [ + { + name: 'issue-9.3/AUI-5343-assistive-class', + links: { + commits: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commits/issue-9.3/AUI-5343-assistive-class', + }, + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches/issue-9.3/AUI-5343-assistive-class', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/branch/issue-9.3/AUI-5343-assistive-class', + }, + }, + default_merge_strategy: 'squash', + merge_strategies: ['merge_commit', 'squash', 'fast_forward'], + type: 'branch', + target: { + hash: 'e5d1cde9069fcb9f0af90403a4de2150c125a148', + repository: { + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui', + }, + html: { href: 'https://bitbucket.org/atlassian/aui' }, + avatar: { + href: 'https://bytebucket.org/ravatar/%7B585074de-7b60-4fd1-81ed-e0bc7fafbda5%7D?ts=86317', + }, + }, + type: 'repository', + name: 'aui', + full_name: 'atlassian/aui', + uuid: '{585074de-7b60-4fd1-81ed-e0bc7fafbda5}', + }, + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + comments: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/comments', + }, + patch: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/patch/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/commits/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + diff: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/diff/e5d1cde9069fcb9f0af90403a4de2150c125a148', + }, + approve: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/approve', + }, + statuses: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/e5d1cde9069fcb9f0af90403a4de2150c125a148/statuses', + }, + }, + author: { + raw: 'Marcin Konopka ', + type: 'author', + user: { + display_name: 'Marcin Konopka', + uuid: '{47cc24f4-2a05-4420-88fe-0417535a110a}', + links: { + self: { + href: 'https://api.bitbucket.org/2.0/users/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D', + }, + html: { + href: 'https://bitbucket.org/%7B47cc24f4-2a05-4420-88fe-0417535a110a%7D/', + }, + avatar: { + href: 'https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/initials/MK-1.png', + }, + }, + nickname: 'Marcin Konopka', + type: 'user', + account_id: '60113d2b47a9540069f4de03', + }, + }, + parents: [ + { + hash: '87f7fc92b00464ae47b13ef65c91884e4ac9be51', + type: 'commit', + links: { + self: { + href: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/commit/87f7fc92b00464ae47b13ef65c91884e4ac9be51', + }, + html: { + href: 'https://bitbucket.org/atlassian/aui/commits/87f7fc92b00464ae47b13ef65c91884e4ac9be51', + }, + }, + }, + ], + date: '2021-04-13T13:44:49+00:00', + message: 'wip\n', + type: 'commit', + }, + }, + ], + page: 1, + next: 'https://api.bitbucket.org/2.0/repositories/atlassian/aui/refs/branches?pagelen=1&page=2', + }; + const responseOfDefaultBranch = { + type: '', + links: { + self: { href: '', name: '' }, + html: { href: '', name: '' }, + avatar: { href: '', name: '' }, + pullrequests: { href: '', name: '' }, + commits: { href: '', name: '' }, + forks: { href: '', name: '' }, + watchers: { href: '', name: '' }, + downloads: { href: '', name: '' }, + clone: [{ href: '', name: '' }], + hooks: { href: '', name: '' }, + }, + uuid: '', + full_name: '', + is_private: true, + scm: 'git', + owner: { type: '' }, + name: '', + description: '', + created_on: '', + updated_on: '', + size: 2154, + language: '', + has_issues: true, + has_wiki: true, + fork_policy: 'allow_forks', + project: { type: '' }, + mainbranch: { type: '' }, + }; + const responseOfPullRequests = { + type: '', + links: { + self: { + href: 'https://bitbucket.org/workspace/repo/pull-requests/1', + name: '', + }, + html: { href: '', name: '' }, + commits: { href: '', name: '' }, + approve: { href: '', name: '' }, + diff: { href: '', name: '' }, + diffstat: { href: '', name: '' }, + comments: { href: '', name: '' }, + activity: { href: '', name: '' }, + merge: { href: '', name: '' }, + decline: { href: '', name: '' }, + }, + id: 108, + title: '', + rendered: { + title: { raw: '', markup: 'markdown', html: '' }, + description: { raw: '', markup: 'markdown', html: '' }, + reason: { raw: '', markup: 'markdown', html: '' }, + }, + summary: { raw: '', markup: 'markdown', html: '' }, + state: 'OPEN', + author: { type: '' }, + source: { + repository: { type: '' }, + branch: { + name: '', + merge_strategies: ['merge_commit'], + default_merge_strategy: '', + }, + commit: { hash: '' }, + }, + destination: { + repository: { type: '' }, + branch: { + name: '', + merge_strategies: ['merge_commit'], + default_merge_strategy: '', + }, + commit: { hash: '' }, + }, + merge_commit: { hash: '' }, + comment_count: 51, + task_count: 53, + close_source_branch: true, + closed_by: { type: '' }, + reason: '', + created_on: '', + updated_on: '', + reviewers: [{ type: '' }], + participants: [{ type: '' }], + }; + const handlers = [ + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (_, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo', + (_, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfDefaultBranch), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (_, res, ctx) => { + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ]; + + const server = setupServer(); + registerMswTestHooks(server); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should throw an error when the repoUrl is not well formed', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?project=project&repo=repo', + }, + }), + ).rejects.toThrow(/missing workspace/); + + await expect( + action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?workspace=workspace&project=project', + }, + }), + ).rejects.toThrow(/missing repo/); + }); + + it('should throw if there is no integration config provided', async () => { + await expect( + action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'missing.com?workspace=workspace&project=project&repo=repo', + }, + }), + ).rejects.toThrow(/No matching integration configuration/); + }); + + it('should call the correct APIs with basic auth', async () => { + expect.assertions(2); + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', + ); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', + }, + }); + }); + + it('should work if the token is provided through ctx.input', async () => { + expect.assertions(2); + const token = 'user-token'; + server.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe(`Bearer ${token}`); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfBranches), + ); + }, + ), + rest.post( + 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', + (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe(`Bearer ${token}`); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json(responseOfPullRequests), + ); + }, + ), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'bitbucket.org?workspace=workspace&project=project&repo=repo', + token: token, + }, + }); + }); + + it('should call outputs with the correct urls', async () => { + server.use(...handlers); + + await action.handler(mockContext); + + expect(mockContext.output).toHaveBeenCalledWith( + 'pullRequestUrl', + 'https://bitbucket.org/workspace/repo/pull-requests/1', + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts index 71944c18ae..d3593dc6f5 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -317,7 +317,7 @@ export function createPublishBitbucketCloudPullRequestAction(options: { if (!workspace) { throw new InputError( - `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`, + `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`, ); } From df9ae9efee5c0101b6320b564a5198de6c263db8 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 14:42:04 +0200 Subject: [PATCH 183/317] chore: add changeset Signed-off-by: Benjamin Janssens --- .changeset/fuzzy-fireants-crash.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fuzzy-fireants-crash.md diff --git a/.changeset/fuzzy-fireants-crash.md b/.changeset/fuzzy-fireants-crash.md new file mode 100644 index 0000000000..1feec6232c --- /dev/null +++ b/.changeset/fuzzy-fireants-crash.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch +--- + +Added scaffolder action publish:bitbucketCloud:pull-request From 69f7d0f3e27236c2baba2ae0411775dfadd32dba Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:10:13 +0000 Subject: [PATCH 184/317] fix(deps): update dependency eslint-plugin-react to v7.35.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 709e5b2227..072772eb3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26330,8 +26330,8 @@ __metadata: linkType: hard "eslint-plugin-react@npm:^7.28.0": - version: 7.35.0 - resolution: "eslint-plugin-react@npm:7.35.0" + version: 7.35.2 + resolution: "eslint-plugin-react@npm:7.35.2" dependencies: array-includes: ^3.1.8 array.prototype.findlast: ^1.2.5 @@ -26353,7 +26353,7 @@ __metadata: string.prototype.repeat: ^1.0.0 peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - checksum: cd4d3c0567e947964643dda5fc80147e058d75f06bac47c3f086ff0cd6156286c669d98e685e3834997c4043f3922b90e6374b6c3658f22abd025dbd41acc23f + checksum: 88dfb91d52a10fa0333199fa4abaa8d1112066d8fdc5df88c136d1cc24145b8d92bb7292f82a3781aef9c3ec91990b4461dbd1fa5eee471df5517014c6e843f9 languageName: node linkType: hard From 5794d5c1f86bdde98d712550205954a5ea30a531 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 16:23:14 +0200 Subject: [PATCH 185/317] docs: add action to readme Signed-off-by: Benjamin Janssens --- plugins/scaffolder-backend-module-bitbucket-cloud/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/README.md b/plugins/scaffolder-backend-module-bitbucket-cloud/README.md index 0d616f2670..d5ba306fa2 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/README.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/README.md @@ -7,5 +7,6 @@ _This plugin was created through the Backstage CLI_ ## Actions -- `publish:bitbucketCloud` -- `bitbucket:pipelines:run` +* `publish:bitbucketCloud` +* `bitbucket:pipelines:run` +* `publish:bitbucketCloud:pull-request` From 6253cf5f5941d289656ff50904b2db6843009ba1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:26:10 +0000 Subject: [PATCH 186/317] fix(deps): update dependency nodemailer to v6.9.15 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3655ef2ca3..db656c9978 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35196,9 +35196,9 @@ __metadata: linkType: hard "nodemailer@npm:^6.9.13": - version: 6.9.14 - resolution: "nodemailer@npm:6.9.14" - checksum: 4037a45e24dbca47c0ea9a962d2c316deef7afe6ebf73c7da3a84942a8d501d85aae7f36dc1c5dac58dec7275f7cb1c92fd4f9c7c19f037225019ad2f402a9dc + version: 6.9.15 + resolution: "nodemailer@npm:6.9.15" + checksum: 2d975f5c60f84acde52d79d43287c70197fe94bde4a731682f306e9636a148c49de29a5dd92ce7dc5aac068a148cd91da3401ede80f8f52b9f2bc00439f36ced languageName: node linkType: hard From 3b0e1d4c580edf165544638e711ce028cb8ec30d Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Wed, 4 Sep 2024 16:29:47 +0200 Subject: [PATCH 187/317] Implemented test case for omitExtraData Signed-off-by: Jasper Boeijenga --- .../next/components/Stepper/Stepper.test.tsx | 192 +++++++++++++----- 1 file changed, 139 insertions(+), 53 deletions(-) diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx index 78aef3428f..d6882c7403 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { renderInTestApp } from '@backstage/test-utils'; +import { JsonValue } from '@backstage/types'; +import { act, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; + +import { FieldExtensionComponentProps } from '../../../extensions'; +import { LayoutTemplate } from '../../../layouts'; +import { SecretsContextProvider } from '../../../secrets'; import { TemplateParameterSchema } from '../../../types'; import { Stepper } from './Stepper'; -import { renderInTestApp } from '@backstage/test-utils'; -import { act, fireEvent } from '@testing-library/react'; + import type { RJSFValidationError } from '@rjsf/utils'; -import { JsonValue } from '@backstage/types'; -import { FieldExtensionComponentProps } from '../../../extensions'; -import { SecretsContextProvider } from '../../../secrets'; -import { LayoutTemplate } from '../../../layouts'; describe('Stepper', () => { it('should render the step titles for each step of the manifest', async () => { @@ -63,7 +65,7 @@ describe('Stepper', () => { expect(getByRole('button', { name: 'Next' })).toBeInTheDocument(); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Next' })); + fireEvent.click(getByRole('button', { name: 'Next' })); }); expect(getByRole('button', { name: 'Review' })).toBeInTheDocument(); @@ -102,16 +104,15 @@ describe('Stepper', () => { , ); - await fireEvent.change(getByRole('textbox', { name: 'name' }), { - target: { value: 'im a test value' }, + await act(async () => { + fireEvent.change(getByRole('textbox', { name: 'name' }), { + target: { value: 'im a test value' }, + }); + fireEvent.click(getByRole('button', { name: 'Next' })); }); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Next' })); - }); - - await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Back' })); + fireEvent.click(getByRole('button', { name: 'Back' })); }); expect(getByRole('textbox', { name: 'name' })).toHaveValue( @@ -152,16 +153,15 @@ describe('Stepper', () => { , ); - await fireEvent.change(getByRole('textbox', { name: 'name' }), { - target: { value: 'im a test value' }, + await act(async () => { + fireEvent.change(getByRole('textbox', { name: 'name' }), { + target: { value: 'im a test value' }, + }); + fireEvent.click(getByRole('button', { name: 'Next' })); }); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Next' })); - }); - - await act(async () => { - await fireEvent.click(getByLabelText('Step 1')); + fireEvent.click(getByLabelText('Step 1')); }); expect(getByRole('textbox', { name: 'name' })).toHaveValue( @@ -169,6 +169,97 @@ describe('Stepper', () => { ); }); + it('should omit properties that are no longer pertinent to the current step', async () => { + const manifest: TemplateParameterSchema = { + title: 'Conditional Input Form', + steps: [ + { + title: 'Conditional Input step', + schema: { + type: 'object', + properties: { + moreInfo: { + type: 'boolean', + title: 'More info', + }, + }, + dependencies: { + moreInfo: { + oneOf: [ + { + properties: { + moreInfo: { + const: true, + }, + description: { + type: 'string', + title: 'Description', + }, + }, + required: ['description'], + }, + { + properties: { + moreInfo: { + not: { + const: true, + }, + }, + }, + }, + ], + }, + }, + }, + }, + ], + }; + + const onCreate = jest.fn(); + + const { getByRole, queryByRole } = await renderInTestApp( + + + , + ); + + await act(async () => { + fireEvent.click(getByRole('checkbox', { name: 'More info' })); + fireEvent.change(getByRole('textbox', { name: 'Description' }), { + target: { value: 'My Test Description' }, + }); + fireEvent.click(getByRole('button', { name: 'Review' })); + }); + + expect( + getByRole('cell', { name: 'My Test Description' }), + ).toBeInTheDocument(); + + await act(async () => { + fireEvent.click(getByRole('button', { name: 'Back' })); + }); + + await act(async () => { + fireEvent.click(getByRole('checkbox', { name: 'More info' })); + fireEvent.click(getByRole('button', { name: 'Review' })); + }); + + expect( + queryByRole('cell', { name: 'My Test Description' }), + ).not.toBeInTheDocument(); + + await act(async () => { + fireEvent.click(getByRole('button', { name: 'Create' })); + }); + + expect(onCreate).toHaveBeenCalledWith({ moreInfo: false }); + }); + it('should merge nested formData correctly in multiple steps', async () => { const Repo = ({ onChange, @@ -240,24 +331,22 @@ describe('Stepper', () => { , ); - await fireEvent.change(getByRole('textbox', { name: 'repo' }), { - target: { value: 'Repo' }, + await act(async () => { + fireEvent.change(getByRole('textbox', { name: 'repo' }), { + target: { value: 'Repo' }, + }); + fireEvent.click(getByRole('button', { name: 'Next' })); }); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Next' })); - }); - - await fireEvent.change(getByRole('textbox', { name: 'owner' }), { - target: { value: 'Owner' }, + fireEvent.change(getByRole('textbox', { name: 'owner' }), { + target: { value: 'Owner' }, + }); + fireEvent.click(getByRole('button', { name: 'Review' })); }); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Review' })); - }); - - await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Create' })); + fireEvent.click(getByRole('button', { name: 'Create' })); }); expect(onCreate).toHaveBeenCalled(); @@ -335,13 +424,11 @@ describe('Stepper', () => { /> , ); - - act(() => { - fireEvent.click(getByRole('button', { name: 'Review' })); + fireEvent.click(getByRole('button', { name: 'Review' })); + await waitFor(() => { + expect(getByRole('progressbar')).toBeInTheDocument(); // Check if progress bar is rendered + expect(getByRole('button', { name: 'Review' })).toBeDisabled(); // Check if the button is disabled }); - - expect(getByRole('progressbar')).toBeInTheDocument(); - expect(getByRole('button', { name: 'Review' })).toBeDisabled(); }); it('should transform default error message', async () => { @@ -381,12 +468,11 @@ describe('Stepper', () => { , ); - await fireEvent.change(getByRole('textbox', { name: 'postcode' }), { - target: { value: 'invalid' }, - }); - await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Review' })); + fireEvent.change(getByRole('textbox', { name: 'postcode' }), { + target: { value: 'invalid' }, + }); + fireEvent.click(getByRole('button', { name: 'Review' })); }); expect(getByText('invalid postcode')).toBeInTheDocument(); @@ -421,12 +507,12 @@ describe('Stepper', () => { , ); - await fireEvent.change(getByRole('textbox', { name: 'postcode' }), { - target: { value: 'invalid' }, - }); - await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Review' })); + fireEvent.change(getByRole('textbox', { name: 'postcode' }), { + target: { value: 'invalid' }, + }); + + fireEvent.click(getByRole('button', { name: 'Review' })); }); expect(getByText('invalid postcode')).toBeInTheDocument(); @@ -494,13 +580,13 @@ describe('Stepper', () => { ); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Review' })); + fireEvent.click(getByRole('button', { name: 'Review' })); }); expect(getByRole('button', { name: 'Create' })).toBeInTheDocument(); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Create' })); + fireEvent.click(getByRole('button', { name: 'Create' })); }); // flush promises @@ -539,13 +625,13 @@ describe('Stepper', () => { ); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Inspect' })); + fireEvent.click(getByRole('button', { name: 'Inspect' })); }); expect(getByRole('button', { name: 'Make' })).toBeInTheDocument(); await act(async () => { - await fireEvent.click(getByRole('button', { name: 'Make' })); + fireEvent.click(getByRole('button', { name: 'Make' })); }); }); From 09229c135a1b01772ec1b27f503959fded3aef68 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 16:37:51 +0200 Subject: [PATCH 188/317] fix: add examples; improve consistency of tests Signed-off-by: Benjamin Janssens --- .../bitbucketCloudPullRequest.examples.test.ts | 12 ++++++------ .../src/actions/bitbucketCloudPullRequest.ts | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts index 14d33b8dba..6b30efaa0b 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.examples.test.ts @@ -239,7 +239,7 @@ describe('publish:bitbucketCloud:pull-request', () => { 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', (req, res, ctx) => { expect(req.headers.get('Authorization')).toBe( - `Basic ${btoa('test-user:test-password')}`, + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', ); return res( ctx.status(200), @@ -252,7 +252,7 @@ describe('publish:bitbucketCloud:pull-request', () => { 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', (req, res, ctx) => { expect(req.headers.get('Authorization')).toBe( - `Basic ${btoa('test-user:test-password')}`, + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', ); return res( ctx.status(201), @@ -279,7 +279,7 @@ describe('publish:bitbucketCloud:pull-request', () => { 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', (req, res, ctx) => { expect(req.headers.get('Authorization')).toBe( - `Basic ${btoa('test-user:test-password')}`, + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', ); return res( ctx.status(200), @@ -292,7 +292,7 @@ describe('publish:bitbucketCloud:pull-request', () => { 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', (req, res, ctx) => { expect(req.headers.get('Authorization')).toBe( - `Basic ${btoa('test-user:test-password')}`, + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', ); return res( ctx.status(201), @@ -319,7 +319,7 @@ describe('publish:bitbucketCloud:pull-request', () => { 'https://api.bitbucket.org/2.0/repositories/workspace/repo/refs/branches', (req, res, ctx) => { expect(req.headers.get('Authorization')).toBe( - `Basic ${btoa('test-user:test-password')}`, + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', ); return res( ctx.status(200), @@ -332,7 +332,7 @@ describe('publish:bitbucketCloud:pull-request', () => { 'https://api.bitbucket.org/2.0/repositories/workspace/repo/pullrequests', (req, res, ctx) => { expect(req.headers.get('Authorization')).toBe( - `Basic ${btoa('test-user:test-password')}`, + 'Basic dGVzdC11c2VyOnRlc3QtcGFzc3dvcmQ=', ); return res( ctx.status(201), diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts index d3593dc6f5..f0c352bd68 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloudPullRequest.ts @@ -29,6 +29,7 @@ import fetch, { RequestInit, Response } from 'node-fetch'; import { Config } from '@backstage/config'; import fs from 'fs-extra'; import { getAuthorizationHeader } from './helpers'; +import { examples } from './bitbucketCloudPullRequest.examples'; const createPullRequest = async (opts: { workspace: string; @@ -244,7 +245,7 @@ export function createPublishBitbucketCloudPullRequestAction(options: { gitAuthorEmail?: string; }>({ id: 'publish:bitbucketCloud:pull-request', - // examples, + examples, schema: { input: { type: 'object', From c4e37e611c8a0e1b2e2b15d6d19547adb51113a8 Mon Sep 17 00:00:00 2001 From: Benjamin Janssens Date: Wed, 4 Sep 2024 16:42:13 +0200 Subject: [PATCH 189/317] style: fix linting issues; add api reports Signed-off-by: Benjamin Janssens --- .../README.md | 6 +++--- .../api-report.md | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/README.md b/plugins/scaffolder-backend-module-bitbucket-cloud/README.md index d5ba306fa2..462b97e74f 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/README.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/README.md @@ -7,6 +7,6 @@ _This plugin was created through the Backstage CLI_ ## Actions -* `publish:bitbucketCloud` -* `bitbucket:pipelines:run` -* `publish:bitbucketCloud:pull-request` +- `publish:bitbucketCloud` +- `bitbucket:pipelines:run` +- `publish:bitbucketCloud:pull-request` diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md b/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md index 90a8cbc29f..b853023b07 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/api-report.md @@ -42,4 +42,22 @@ export function createPublishBitbucketCloudAction(options: { }, JsonObject >; + +// @public +export function createPublishBitbucketCloudPullRequestAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; +}): TemplateAction< + { + repoUrl: string; + title: string; + description?: string | undefined; + targetBranch?: string | undefined; + sourceBranch: string; + token?: string | undefined; + gitAuthorName?: string | undefined; + gitAuthorEmail?: string | undefined; + }, + JsonObject +>; ``` From 94d80975057b21c4edd067f0011edf45d93a9524 Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Wed, 4 Sep 2024 16:54:37 +0200 Subject: [PATCH 190/317] Added extra case to test overwriting formdata in different steps Signed-off-by: Jasper Boeijenga --- .../next/components/Stepper/Stepper.test.tsx | 72 +++++++------------ 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx index d6882c7403..9d661aeb7e 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx @@ -260,38 +260,16 @@ describe('Stepper', () => { expect(onCreate).toHaveBeenCalledWith({ moreInfo: false }); }); - it('should merge nested formData correctly in multiple steps', async () => { - const Repo = ({ - onChange, - }: FieldExtensionComponentProps<{ repository: string }, any>) => ( - onChange({ repository: e.target.value })} - defaultValue="" - /> - ); - - const Owner = ({ - onChange, - }: FieldExtensionComponentProps<{ owner: string }, any>) => ( - onChange({ owner: e.target.value })} - defaultValue="" - /> - ); - + it('should merge and overwrite nested formData correctly', async () => { const manifest: TemplateParameterSchema = { steps: [ { title: 'Step 1', schema: { properties: { - first: { - type: 'object', - 'ui:field': 'Repo', + foo: { + type: 'string', + title: 'Foo - 1', }, }, }, @@ -300,9 +278,13 @@ describe('Stepper', () => { title: 'Step 2', schema: { properties: { - second: { - type: 'object', - 'ui:field': 'Owner', + foo: { + type: 'string', + title: 'Foo - 2', + }, + bar: { + type: 'string', + title: 'Bar - 2', }, }, }, @@ -311,36 +293,27 @@ describe('Stepper', () => { title: 'React JSON Schema Form Test', }; - const onCreate = jest.fn(async (values: Record) => { - expect(values).toEqual({ - first: { repository: 'Repo' }, - second: { owner: 'Owner' }, - }); - }); + const onCreate = jest.fn(); const { getByRole } = await renderInTestApp( - + , ); await act(async () => { - fireEvent.change(getByRole('textbox', { name: 'repo' }), { - target: { value: 'Repo' }, + fireEvent.change(getByRole('textbox', { name: 'Foo - 1' }), { + target: { value: 'value 1' }, }); fireEvent.click(getByRole('button', { name: 'Next' })); }); await act(async () => { - fireEvent.change(getByRole('textbox', { name: 'owner' }), { - target: { value: 'Owner' }, + fireEvent.change(getByRole('textbox', { name: 'Foo - 2' }), { + target: { value: 'value 2' }, + }); + fireEvent.change(getByRole('textbox', { name: 'Bar - 2' }), { + target: { value: 'value 2' }, }); fireEvent.click(getByRole('button', { name: 'Review' })); }); @@ -349,7 +322,10 @@ describe('Stepper', () => { fireEvent.click(getByRole('button', { name: 'Create' })); }); - expect(onCreate).toHaveBeenCalled(); + expect(onCreate).toHaveBeenCalledWith({ + foo: 'value 2', + bar: 'value 2', + }); }); it('should render custom field extensions properly', async () => { From 7899da1528d55dd04ec73f2429f31a9a5a424084 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:13:55 +0000 Subject: [PATCH 191/317] fix(deps): update dependency postcss to v8.4.45 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index db656c9978..9aa629b622 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37447,13 +37447,13 @@ __metadata: linkType: hard "postcss@npm:^8.1.0, postcss@npm:^8.4.27, postcss@npm:^8.4.33": - version: 8.4.41 - resolution: "postcss@npm:8.4.41" + version: 8.4.45 + resolution: "postcss@npm:8.4.45" dependencies: nanoid: ^3.3.7 picocolors: ^1.0.1 source-map-js: ^1.2.0 - checksum: f865894929eb0f7fc2263811cc853c13b1c75103028b3f4f26df777e27b201f1abe21cb4aa4c2e901c80a04f6fb325ee22979688fe55a70e2ea82b0a517d3b6f + checksum: 3223cdad4a9392c0b334ee3ee7e4e8041c631cb6160609cef83c18d2b2580e931dd8068ab13cc6000c1a254d57492ac6c38717efc397c5dcc9756d06bc9c44f3 languageName: node linkType: hard From 4880a195f2594e79ad4d3460a38c367b06b79d5a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:14:40 +0000 Subject: [PATCH 192/317] fix(deps): update dependency yaml to v2.5.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index db656c9978..b13970c6c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44995,11 +44995,11 @@ __metadata: linkType: hard "yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2, yaml@npm:^2.3.3, yaml@npm:~2.5.0": - version: 2.5.0 - resolution: "yaml@npm:2.5.0" + version: 2.5.1 + resolution: "yaml@npm:2.5.1" bin: yaml: bin.mjs - checksum: a116dca5c61641d9bf1f1016c6e71daeb1ed4915f5930ed237d45ab7a605aa5d92c332ff64879a6cd088cabede008c778774e3060ffeb4cd617d28088e4b2d83 + checksum: 31275223863fbd0b47ba9d2b248fbdf085db8d899e4ca43fff8a3a009497c5741084da6871d11f40e555d61360951c4c910b98216c1325d2c94753c0036d8172 languageName: node linkType: hard From ee53dec014d77754232182dedcf3a7816e868efd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:03:40 +0000 Subject: [PATCH 193/317] chore(deps): update actions/upload-artifact action to v4.4.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/pr-review-comment-trigger.yaml | 2 +- .github/workflows/scorecard.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml index 27a5a78052..62bf70187b 100644 --- a/.github/workflows/pr-review-comment-trigger.yaml +++ b/.github/workflows/pr-review-comment-trigger.yaml @@ -30,7 +30,7 @@ jobs: run: | mkdir -p ./pr echo $PR_NUMBER > ./pr/pr_number - - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: pr_number-${{ github.event.pull_request.number }} path: pr/ diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 0d22deeca1..112464e966 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -58,7 +58,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: 'Upload artifact' - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: SARIF file path: results.sarif From a6ce155b8dea5f6b4ceda05ba3c396dcaedb40af Mon Sep 17 00:00:00 2001 From: Jasper Boeijenga Date: Wed, 4 Sep 2024 18:10:49 +0200 Subject: [PATCH 194/317] Remove unused import Signed-off-by: Jasper Boeijenga --- .../src/next/components/Stepper/Stepper.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx index 9d661aeb7e..b2ec74c41a 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx @@ -18,7 +18,6 @@ import { JsonValue } from '@backstage/types'; import { act, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; -import { FieldExtensionComponentProps } from '../../../extensions'; import { LayoutTemplate } from '../../../layouts'; import { SecretsContextProvider } from '../../../secrets'; import { TemplateParameterSchema } from '../../../types'; From 74c972143f7ebf2e91ea7b7c5a41de6c3e691443 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:13:44 +0000 Subject: [PATCH 195/317] chore(deps): update dependency knip to v5.29.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 655594a9ff..a180df059c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32247,8 +32247,8 @@ __metadata: linkType: hard "knip@npm:^5.0.0": - version: 5.27.5 - resolution: "knip@npm:5.27.5" + version: 5.29.2 + resolution: "knip@npm:5.29.2" dependencies: "@nodelib/fs.walk": 1.2.8 "@snyk/github-codeowners": 1.1.0 @@ -32272,7 +32272,7 @@ __metadata: bin: knip: bin/knip.js knip-bun: bin/knip-bun.js - checksum: a348806f33c211246a28f7f353f48c6dca74e483b7b3883132a4bc166516805ad3089f66e32d3a15e3085516d145fcd58a051948c9853ba5e4052ab0630cf957 + checksum: c1c9abf9f6438c17f854c52adc7f294d07991632a7a45675c0213815546556b74fc7ac9cd3b7fd3eca2bd6c816fa973e212a4b38c0dc87e0826ea63635d5e2b6 languageName: node linkType: hard From 043d7cd963f07d72e7cdba14f467689da9e9b672 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 2 Sep 2024 14:50:09 +0300 Subject: [PATCH 196/317] frontend-*: initial refactor to create and use -internal Signed-off-by: Patrik Oldsberg --- .changeset/smart-beers-give.md | 6 + packages/frontend-internal/.eslintrc.js | 5 + packages/frontend-internal/README.md | 3 + packages/frontend-internal/catalog-info.yaml | 9 ++ packages/frontend-internal/package.json | 39 +++++++ packages/frontend-internal/src/index.ts | 17 +++ .../src/wiring/InternalExtensionDefinition.ts | 104 ++++++++++++++++++ .../frontend-internal/src/wiring/index.ts | 20 ++++ .../src/wiring/createExtension.ts | 80 +------------- .../wiring/createExtensionBlueprint.test.tsx | 6 +- .../src/wiring/createFrontendModule.ts | 4 +- .../src/wiring/createFrontendPlugin.ts | 4 +- .../src/wiring/resolveExtensionDefinition.ts | 2 +- packages/frontend-test-utils/package.json | 4 +- .../src/app/createExtensionTester.tsx | 3 +- yarn.lock | 19 ++++ 16 files changed, 234 insertions(+), 91 deletions(-) create mode 100644 .changeset/smart-beers-give.md create mode 100644 packages/frontend-internal/.eslintrc.js create mode 100644 packages/frontend-internal/README.md create mode 100644 packages/frontend-internal/catalog-info.yaml create mode 100644 packages/frontend-internal/package.json create mode 100644 packages/frontend-internal/src/index.ts create mode 100644 packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts create mode 100644 packages/frontend-internal/src/wiring/index.ts diff --git a/.changeset/smart-beers-give.md b/.changeset/smart-beers-give.md new file mode 100644 index 0000000000..5b9d9888ba --- /dev/null +++ b/.changeset/smart-beers-give.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-test-utils': patch +--- + +Internal refactor diff --git a/packages/frontend-internal/.eslintrc.js b/packages/frontend-internal/.eslintrc.js new file mode 100644 index 0000000000..e487f765b2 --- /dev/null +++ b/packages/frontend-internal/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { + rules: { + '@backstage/no-top-level-material-ui-4-imports': 'error', + }, +}); diff --git a/packages/frontend-internal/README.md b/packages/frontend-internal/README.md new file mode 100644 index 0000000000..72e54f21cf --- /dev/null +++ b/packages/frontend-internal/README.md @@ -0,0 +1,3 @@ +# @internal/frontend + +This is an internal package used by the other frontend packages. It does not get published to NPM, but instead inlined into consuming packages due to the `backstage.inline` flag in `package.json`. diff --git a/packages/frontend-internal/catalog-info.yaml b/packages/frontend-internal/catalog-info.yaml new file mode 100644 index 0000000000..630f53275a --- /dev/null +++ b/packages/frontend-internal/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: internal-frontend + title: '@internal/frontend' +spec: + lifecycle: experimental + type: backstage-web-library + owner: maintainers diff --git a/packages/frontend-internal/package.json b/packages/frontend-internal/package.json new file mode 100644 index 0000000000..1e637b9d78 --- /dev/null +++ b/packages/frontend-internal/package.json @@ -0,0 +1,39 @@ +{ + "name": "@internal/frontend", + "version": "0.0.0", + "backstage": { + "role": "web-library", + "inline": true + }, + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/frontend-internal" + }, + "license": "Apache-2.0", + "sideEffects": false, + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist" + ], + "scripts": { + "lint": "backstage-cli package lint", + "test": "backstage-cli package test" + }, + "dependencies": { + "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/types": "workspace:^", + "@backstage/version-bridge": "workspace:^", + "zod": "^3.22.4" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/frontend-app-api": "workspace:^", + "@backstage/frontend-test-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^15.0.0" + } +} diff --git a/packages/frontend-internal/src/index.ts b/packages/frontend-internal/src/index.ts new file mode 100644 index 0000000000..a5728f2ff6 --- /dev/null +++ b/packages/frontend-internal/src/index.ts @@ -0,0 +1,17 @@ +/* + * 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 * from './wiring'; diff --git a/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts b/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts new file mode 100644 index 0000000000..840e45ff4a --- /dev/null +++ b/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts @@ -0,0 +1,104 @@ +/* + * 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 { + AnyExtensionDataRef, + ApiHolder, + AppNode, + ExtensionDataValue, + ExtensionDefinition, + ExtensionDefinitionParameters, + ExtensionInput, + PortableSchema, + ResolvedExtensionInputs, +} from '@backstage/frontend-plugin-api'; + +export type InternalExtensionDefinition< + T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters, +> = ExtensionDefinition & { + readonly kind?: string; + readonly namespace?: string; + readonly name?: string; + readonly attachTo: { id: string; input: string }; + readonly disabled: boolean; + readonly configSchema?: PortableSchema; +} & ( + | { + readonly version: 'v1'; + readonly inputs: { + [inputName in string]: { + $$type: '@backstage/ExtensionInput'; + extensionData: { + [name in string]: AnyExtensionDataRef; + }; + config: { optional: boolean; singleton: boolean }; + }; + }; + readonly output: { + [name in string]: AnyExtensionDataRef; + }; + factory(context: { + node: AppNode; + apis: ApiHolder; + config: object; + inputs: { + [inputName in string]: unknown; + }; + }): { + [inputName in string]: unknown; + }; + } + | { + readonly version: 'v2'; + readonly inputs: { + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }; + readonly output: Array; + factory(context: { + node: AppNode; + apis: ApiHolder; + config: object; + inputs: ResolvedExtensionInputs<{ + [inputName in string]: ExtensionInput< + AnyExtensionDataRef, + { optional: boolean; singleton: boolean } + >; + }>; + }): Iterable>; + } + ); + +/** @internal */ +export function toInternalExtensionDefinition< + T extends ExtensionDefinitionParameters, +>(overrides: ExtensionDefinition): InternalExtensionDefinition { + const internal = overrides as InternalExtensionDefinition; + if (internal.$$type !== '@backstage/ExtensionDefinition') { + throw new Error( + `Invalid extension definition instance, bad type '${internal.$$type}'`, + ); + } + const version = internal.version; + if (version !== 'v1' && version !== 'v2') { + throw new Error( + `Invalid extension definition instance, bad version '${version}'`, + ); + } + return internal; +} diff --git a/packages/frontend-internal/src/wiring/index.ts b/packages/frontend-internal/src/wiring/index.ts new file mode 100644 index 0000000000..d0aefbed64 --- /dev/null +++ b/packages/frontend-internal/src/wiring/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { + toInternalExtensionDefinition, + type InternalExtensionDefinition, +} from './InternalExtensionDefinition'; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 445bbd8a68..4d7ebee521 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -15,7 +15,6 @@ */ import { ApiHolder, AppNode } from '../apis'; -import { PortableSchema } from '../schema'; import { Expand } from '../types'; import { ResolveInputValueOverrides, @@ -32,6 +31,7 @@ import { import { ExtensionInput } from './createExtensionInput'; import { z } from 'zod'; import { createSchemaFromZod } from '../schema/createSchemaFromZod'; +import { InternalExtensionDefinition } from '@internal/frontend'; /** * Convert a single extension input into a matching resolved input. @@ -235,84 +235,6 @@ export type ExtensionDefinition< }>; }; -/** @internal */ -export type InternalExtensionDefinition< - T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters, -> = ExtensionDefinition & { - readonly kind?: string; - readonly namespace?: string; - readonly name?: string; - readonly attachTo: { id: string; input: string }; - readonly disabled: boolean; - readonly configSchema?: PortableSchema; -} & ( - | { - readonly version: 'v1'; - readonly inputs: { - [inputName in string]: { - $$type: '@backstage/ExtensionInput'; - extensionData: { - [name in string]: AnyExtensionDataRef; - }; - config: { optional: boolean; singleton: boolean }; - }; - }; - readonly output: { - [name in string]: AnyExtensionDataRef; - }; - factory(context: { - node: AppNode; - apis: ApiHolder; - config: object; - inputs: { - [inputName in string]: unknown; - }; - }): { - [inputName in string]: unknown; - }; - } - | { - readonly version: 'v2'; - readonly inputs: { - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }; - readonly output: Array; - factory(context: { - node: AppNode; - apis: ApiHolder; - config: object; - inputs: ResolvedExtensionInputs<{ - [inputName in string]: ExtensionInput< - AnyExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }>; - }): Iterable>; - } - ); - -/** @internal */ -export function toInternalExtensionDefinition< - T extends ExtensionDefinitionParameters, ->(overrides: ExtensionDefinition): InternalExtensionDefinition { - const internal = overrides as InternalExtensionDefinition; - if (internal.$$type !== '@backstage/ExtensionDefinition') { - throw new Error( - `Invalid extension definition instance, bad type '${internal.$$type}'`, - ); - } - const version = internal.version; - if (version !== 'v1' && version !== 'v2') { - throw new Error( - `Invalid extension definition instance, bad version '${version}'`, - ); - } - return internal; -} - /** @public */ export function createExtension< UOutput extends AnyExtensionDataRef, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index fc77c63e97..47ef7a75b4 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -27,11 +27,9 @@ import { } from './createExtensionDataRef'; import { createExtensionInput } from './createExtensionInput'; import { RouteRef } from '../routing'; -import { - ExtensionDefinition, - toInternalExtensionDefinition, -} from './createExtension'; +import { ExtensionDefinition } from './createExtension'; import { createExtensionDataContainer } from './createExtensionDataContainer'; +import { toInternalExtensionDefinition } from '@internal/frontend'; function unused(..._any: any[]) {} diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts b/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts index aacc34ccf0..e3fa8b6682 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts @@ -15,10 +15,10 @@ */ import { - ExtensionDefinition, InternalExtensionDefinition, toInternalExtensionDefinition, -} from './createExtension'; +} from '@internal/frontend'; +import { ExtensionDefinition } from './createExtension'; import { Extension, resolveExtensionDefinition, diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts index 03c843543d..adcd2b0867 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts @@ -15,10 +15,10 @@ */ import { - ExtensionDefinition, InternalExtensionDefinition, toInternalExtensionDefinition, -} from './createExtension'; +} from '@internal/frontend'; +import { ExtensionDefinition } from './createExtension'; import { Extension, ResolveExtensionId, diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index 870fb258f5..ce275406e7 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -19,7 +19,6 @@ import { ExtensionDefinition, ExtensionDefinitionParameters, ResolvedExtensionInputs, - toInternalExtensionDefinition, } from './createExtension'; import { PortableSchema } from '../schema'; import { ExtensionInput } from './createExtensionInput'; @@ -27,6 +26,7 @@ import { AnyExtensionDataRef, ExtensionDataValue, } from './createExtensionDataRef'; +import { toInternalExtensionDefinition } from '@internal/frontend'; /** @public */ export interface Extension { diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index a84d296c55..8ada3151bf 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -36,7 +36,9 @@ "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-app": "workspace:^", "@backstage/test-utils": "workspace:^", - "@backstage/types": "workspace:^" + "@backstage/types": "workspace:^", + "@backstage/version-bridge": "workspace:^", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.tsx index f9c1b3cb89..31ca9c86ce 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.tsx @@ -29,8 +29,6 @@ import { JsonArray, JsonObject, JsonValue } from '@backstage/types'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { toInternalExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/createExtension'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports import { resolveAppTree } from '../../../frontend-app-api/src/tree/resolveAppTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveAppNodeSpecs'; @@ -39,6 +37,7 @@ import { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/insta // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig'; import { TestApiRegistry } from '@backstage/test-utils'; +import { toInternalExtensionDefinition } from '@internal/frontend'; /** @public */ export class ExtensionQuery { diff --git a/yarn.lock b/yarn.lock index dfc8cc25cd..bba0d759b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4522,8 +4522,10 @@ __metadata: "@backstage/plugin-app": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" + "@backstage/version-bridge": "workspace:^" "@testing-library/jest-dom": ^6.0.0 "@types/react": "*" + zod: ^3.22.4 peerDependencies: "@testing-library/react": ^15.0.0 react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -10002,6 +10004,23 @@ __metadata: languageName: node linkType: hard +"@internal/frontend@workspace:packages/frontend-internal": + version: 0.0.0-use.local + resolution: "@internal/frontend@workspace:packages/frontend-internal" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/frontend-app-api": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/frontend-test-utils": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/types": "workspace:^" + "@backstage/version-bridge": "workspace:^" + "@testing-library/jest-dom": ^6.0.0 + "@testing-library/react": ^15.0.0 + zod: ^3.22.4 + languageName: unknown + linkType: soft + "@internal/plugin-todo-list-backend@workspace:plugins/example-todo-list-backend": version: 0.0.0-use.local resolution: "@internal/plugin-todo-list-backend@workspace:plugins/example-todo-list-backend" From 08895e3f84aec81f6391e990ef7a53032fbe45b3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 4 Sep 2024 09:56:39 +0300 Subject: [PATCH 197/317] eslint-plugin: add support for inline packages Signed-off-by: Patrik Oldsberg --- .changeset/thirty-bottles-rest.md | 5 + packages/eslint-plugin/lib/getPackages.js | 2 +- .../rules/no-undeclared-imports.js | 304 ++++++++++++++---- .../monorepo/packages/bar/package.json | 7 +- .../inline-dep-invalid-direct/package.json | 12 + .../inline-dep-invalid-missing/package.json | 7 + .../packages/inline-dep-valid/package.json | 10 + .../monorepo/packages/inline/package.json | 16 + .../src/no-undeclared-imports.test.ts | 49 +++ 9 files changed, 350 insertions(+), 62 deletions(-) create mode 100644 .changeset/thirty-bottles-rest.md create mode 100644 packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-direct/package.json create mode 100644 packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-missing/package.json create mode 100644 packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-valid/package.json create mode 100644 packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline/package.json diff --git a/.changeset/thirty-bottles-rest.md b/.changeset/thirty-bottles-rest.md new file mode 100644 index 0000000000..51b572d507 --- /dev/null +++ b/.changeset/thirty-bottles-rest.md @@ -0,0 +1,5 @@ +--- +'@backstage/eslint-plugin': patch +--- + +Added support for linting dependencies on workspace packages with the `backstage.inline` flag. diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js index 8f4ccfacf3..3b46f41a54 100644 --- a/packages/eslint-plugin/lib/getPackages.js +++ b/packages/eslint-plugin/lib/getPackages.js @@ -21,7 +21,7 @@ const manypkg = require('@manypkg/get-packages'); /** * @typedef ExtendedPackage - * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array }}} packageJson + * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array, backstage?: { inline?: boolean } }}} packageJson */ /** diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index d2c92ccb8a..0b6a4289b5 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -22,11 +22,11 @@ const visitImports = require('../lib/visitImports'); const minimatch = require('minimatch'); const { execFileSync } = require('child_process'); -const depFields = { +const depFields = /** @type {const} */ ({ dep: 'dependencies', dev: 'devDependencies', peer: 'peerDependencies', -}; +}); const devModulePatterns = [ new minimatch.Minimatch('!src/**'), @@ -154,6 +154,124 @@ function addVersionQuery(name, flag, packages) { return `${name}@${mostCommonRange}`; } +/** + * Add missing package imports + * @param {Array<{name: string, flag: string, node: import('estree').Node}>} toAdd + * @param {import('../lib/getPackages').PackageMap} packages + * @param {import('../lib/getPackages').ExtendedPackage} localPkg + */ +function addMissingImports(toAdd, packages, localPkg) { + /** @type Record> */ + const byFlag = {}; + + for (const { name, flag } of toAdd) { + byFlag[flag] = byFlag[flag] ?? new Set(); + byFlag[flag].add(name); + } + + for (const name of byFlag[''] ?? []) { + byFlag['--dev']?.delete(name); + } + for (const name of byFlag['--peer'] ?? []) { + byFlag['']?.delete(name); + byFlag['--dev']?.delete(name); + } + + for (const [flag, names] of Object.entries(byFlag)) { + // Look up existing version queries in the repo for the same dependency + const namesWithQuery = [...names].map(name => + addVersionQuery(name, flag, packages), + ); + + // The security implication of this is a bit interesting, as crafted add-import + // directives could be used to install malicious packages. However, the same is true + // for adding malicious packages to package.json, so there's no significant difference. + execFileSync('yarn', ['add', ...(flag ? [flag] : []), ...namesWithQuery], { + cwd: localPkg.dir, + stdio: 'inherit', + }); + } +} + +/** + * Removes dependency entries pointing to inlined workspace packages. + * @param {Array<{pkg: import('../lib/getPackages').ExtendedPackage, node: import('estree').Node}>} toInline + * @param {import('../lib/getPackages').ExtendedPackage} localPkg + */ +function removeInlineImports(toInline, localPkg) { + /** @type Set */ + const toRemove = new Set(); + + for (const { pkg } of toInline) { + const name = pkg.packageJson.name; + for (const depType of Object.values(depFields)) { + if (localPkg.packageJson[depType]?.[name]) { + toRemove.add(name); + } + } + } + if (toRemove.size > 0) { + execFileSync('yarn', ['remove', ...toRemove], { + cwd: localPkg.dir, + stdio: 'inherit', + }); + } +} + +/** + * Adds dependencies that are not properly forwarded from inline dependencies. + * @param {Array<{pkg: import('../lib/getPackages').ExtendedPackage, node: import('estree').Node}>} toInline + * @param {import('../lib/getPackages').ExtendedPackage} localPkg + */ +function addForwardedInlineImports(toInline, localPkg) { + const declaredProdDeps = new Set([ + ...Object.keys(localPkg.packageJson.dependencies ?? {}), + ...Object.keys(localPkg.packageJson.peerDependencies ?? {}), + localPkg.packageJson.name, // include self + ]); + + /** @type Map> */ + const byFlagByName = new Map(); + + for (const { pkg } of toInline) { + for (const depType of /** @type {const} */ ([ + 'dependencies', + 'peerDependencies', + ])) { + for (const [depName, depQuery] of Object.entries( + pkg.packageJson[depType] ?? {}, + )) { + if (!declaredProdDeps.has(depName)) { + const flag = getAddFlagForDepsField(depType); + const byName = byFlagByName.get(flag); + if (byName) { + const query = byName.get(depName); + if (query && query !== depQuery) { + throw new Error( + `Conflicting dependency queries for inlined package dep ${depName}, got ${query} and ${depQuery}`, + ); + } else { + byName.set(depName, depQuery); + } + } else { + byFlagByName.set(flag, new Map([[depName, depQuery]])); + } + } + } + } + } + + for (const [flag, byName] of byFlagByName) { + const namesWithQuery = [...byName.entries()].map( + ([name, query]) => `${name}@${query}`, + ); + execFileSync('yarn', ['add', ...(flag ? [flag] : []), ...namesWithQuery], { + cwd: localPkg.dir, + stdio: 'inherit', + }); + } +} + /** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { @@ -165,6 +283,8 @@ module.exports = { switch: '{{ packageName }} is declared in {{ oldDepsField }}, but should be moved to {{ depsField }} in {{ packageJsonPath }}.', switchBack: 'Switch back to import declaration', + inlineDirect: `The dependency on the inline package {{ packageName }} must not be declared in package dependencies.`, + inlineMissing: `Each production dependency from the inline package {{ packageName }} must be re-declared by this package, the following dependencies are missing: {{ missingDeps }}`, }, docs: { description: @@ -189,57 +309,48 @@ module.exports = { /** @type Array<{name: string, flag: string, node: import('estree').Node}> */ const importsToAdd = []; + /** @type Array<{pkg: import('../lib/getPackages').ExtendedPackage, node: import('estree').Node}> */ + const importsToInline = []; + return { // All missing imports that we detect are collected as we traverse, and then we use // the program exit to execute all install directives that have been found. ['Program:exit']() { - /** @type Record> */ - const byFlag = {}; + if (importsToAdd.length > 0) { + addMissingImports(importsToAdd, packages, localPkg); - for (const { name, flag } of importsToAdd) { - byFlag[flag] = byFlag[flag] ?? new Set(); - byFlag[flag].add(name); + // This switches all import directives back to the original import. + for (const added of importsToAdd) { + context.report({ + node: added.node, + messageId: 'switchBack', + fix(fixer) { + return fixer.replaceText(added.node, `'${added.name}'`); + }, + }); + } + importsToAdd.length = 0; } - for (const name of byFlag[''] ?? []) { - byFlag['--dev']?.delete(name); - } - for (const name of byFlag['--peer'] ?? []) { - byFlag['']?.delete(name); - byFlag['--dev']?.delete(name); + if (importsToInline.length > 0) { + removeInlineImports(importsToInline, localPkg); + addForwardedInlineImports(importsToInline, localPkg); + + for (const inlined of importsToInline) { + context.report({ + node: inlined.node, + messageId: 'switchBack', + fix(fixer) { + return fixer.replaceText( + inlined.node, + `'${inlined.pkg.packageJson.name}'`, + ); + }, + }); + } + importsToInline.length = 0; } - for (const [flag, names] of Object.entries(byFlag)) { - // Look up existing version queries in the repo for the same dependency - const namesWithQuery = [...names].map(name => - addVersionQuery(name, flag, packages), - ); - - // The security implication of this is a bit interesting, as crafted add-import - // directives could be used to install malicious packages. However, the same is true - // for adding malicious packages to package.json, so there's no significant difference. - execFileSync( - 'yarn', - ['add', ...(flag ? [flag] : []), ...namesWithQuery], - { - cwd: localPkg.dir, - stdio: 'inherit', - }, - ); - } - - // This switches all import directives back to the original import. - for (const added of importsToAdd) { - context.report({ - node: added.node, - messageId: 'switchBack', - fix(fixer) { - return fixer.replaceText(added.node, `'${added.name}'`); - }, - }); - } - - importsToAdd.length = 0; packages.clearCache(); }, ...visitImports(context, (node, imp) => { @@ -255,22 +366,99 @@ module.exports = { // Any import directive that is found is collected for processing later if (imp.type === 'directive') { - const parts = imp.path.split(':'); - if (parts[1] !== 'add-import') { - return; - } - const [type, name] = parts.slice(2); - if (!name.match(/^(@[-\w\.~]+\/)?[-\w\.~]*$/i)) { - throw new Error( - `Invalid package name to add as dependency: '${name}'`, - ); + const [, directive, ...args] = imp.path.split(':'); + + if (directive === 'add-import') { + const [type, name] = args; + if (!name.match(/^(@[-\w\.~]+\/)?[-\w\.~]*$/i)) { + throw new Error( + `Invalid package name to add as dependency: '${name}'`, + ); + } + + importsToAdd.push({ + flag: getAddFlagForDepsField(type).trim(), + name, + node: imp.node, + }); + } + + if (directive === 'inline-imports') { + const [name] = args; + const pkg = packages.map.get(name); + if (!pkg) { + throw new Error(`Unexpectedly missing inline package: ${name}`); + } + + importsToInline.push({ + pkg: pkg, + node: imp.node, + }); + } + + return; + } + + // Importing an internal inlined package, which that imports are inlined too + if ( + imp.type === 'internal' && + imp.package.packageJson.backstage?.inline + ) { + for (const depType of Object.values(depFields)) { + if (localPkg.packageJson[depType]?.[imp.packageName]) { + context.report({ + node, + messageId: 'inlineDirect', + data: { + packageName: imp.packageName, + }, + fix: fixer => { + return fixer.replaceText( + imp.node, + `'directive:inline-imports:${imp.packageName}'`, + ); + }, + }); + return; + } + } + + const missingDeps = []; + const declaredProdDeps = new Set([ + ...Object.keys(localPkg.packageJson.dependencies ?? {}), + ...Object.keys(localPkg.packageJson.peerDependencies ?? {}), + localPkg.packageJson.name, // include self + ]); + for (const depType of /** @type {const} */ ([ + 'dependencies', + 'peerDependencies', + ])) { + for (const depName of Object.keys( + imp.package.packageJson[depType] ?? {}, + )) { + if (!declaredProdDeps.has(depName)) { + missingDeps.push(depName); + } + } + } + + if (missingDeps.length > 0) { + context.report({ + node, + messageId: 'inlineMissing', + data: { + packageName: imp.packageName, + missingDeps: missingDeps.join(', '), + }, + fix: fixer => { + return fixer.replaceText( + imp.node, + `'directive:inline-imports:${imp.packageName}'`, + ); + }, + }); } - importsToAdd.push({ - flag: getAddFlagForDepsField(type).trim(), - name, - node: imp.node, - }); return; } diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json index efef27047b..ba55598dc2 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json @@ -1,14 +1,15 @@ { "name": "@internal/bar", + "backstage": { + "role": "frontend-plugin" + }, "exports": { ".": "./src/index.ts", "./BarPage": "./src/components/Bar.tsx", "./package.json": "./package.json" }, - "backstage": { - "role": "frontend-plugin" - }, "dependencies": { + "inline-dep": "*", "react-router": "*" }, "devDependencies": { diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-direct/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-direct/package.json new file mode 100644 index 0000000000..352a02d66c --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-direct/package.json @@ -0,0 +1,12 @@ +{ + "name": "@internal/inline-dep-direct", + "files": [ + "dist", + "type-utils" + ], + "dependencies": { + "@internal/inline": "workspace:^", + "@internal/inline-dep-valid": "workspace:^", + "react": "*" + } +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-missing/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-missing/package.json new file mode 100644 index 0000000000..595ee4a86b --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-invalid-missing/package.json @@ -0,0 +1,7 @@ +{ + "name": "@internal/inline-dep-missing", + "files": [ + "dist", + "type-utils" + ] +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-valid/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-valid/package.json new file mode 100644 index 0000000000..cbb42d0080 --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline-dep-valid/package.json @@ -0,0 +1,10 @@ +{ + "name": "@internal/inline-dep-valid", + "files": [ + "dist", + "type-utils" + ], + "peerDependencies": { + "react": "*" + } +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline/package.json new file mode 100644 index 0000000000..fb9345f140 --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/inline/package.json @@ -0,0 +1,16 @@ +{ + "name": "@internal/inline", + "backstage": { + "inline": true + }, + "files": [ + "dist", + "type-utils" + ], + "dependencies": { + "@internal/inline-dep-valid": "workspace:^" + }, + "peerDependencies": { + "react": "*" + } +} diff --git a/packages/eslint-plugin/src/no-undeclared-imports.test.ts b/packages/eslint-plugin/src/no-undeclared-imports.test.ts index 29e2c3a87c..ee56c34926 100644 --- a/packages/eslint-plugin/src/no-undeclared-imports.test.ts +++ b/packages/eslint-plugin/src/no-undeclared-imports.test.ts @@ -52,6 +52,12 @@ const ERR_SWITCHED = ( const ERR_SWITCH_BACK = () => ({ message: 'Switch back to import declaration', }); +const ERR_INLINE_DIRECT = (name: string) => ({ + message: `The dependency on the inline package ${name} must not be declared in package dependencies.`, +}); +const ERR_INLINE_MISSING = (name: string, missing: string) => ({ + message: `Each production dependency from the inline package ${name} must be re-declared by this package, the following dependencies are missing: ${missing}`, +}); // cwd must be restored const origDir = process.cwd(); @@ -102,6 +108,17 @@ ruleTester.run(RULE, rule, { code: `require('lod' + 'ash')`, filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), }, + { + code: `import '@internal/inline'`, + filename: joinPath(FIXTURE, 'packages/inline-dep-valid/src/index.ts'), + }, + { + code: `import '@internal/inline'`, + filename: joinPath( + FIXTURE, + 'packages/inline-dep-valid/src/index.test.ts', + ), + }, ], invalid: [ { @@ -264,6 +281,29 @@ ruleTester.run(RULE, rule, { ), ], }, + { + code: `import '@internal/inline'`, + output: `import 'directive:inline-imports:@internal/inline'`, + filename: joinPath( + FIXTURE, + 'packages/inline-dep-invalid-direct/src/index.ts', + ), + errors: [ERR_INLINE_DIRECT('@internal/inline')], + }, + { + code: `import '@internal/inline'`, + output: `import 'directive:inline-imports:@internal/inline'`, + filename: joinPath( + FIXTURE, + 'packages/inline-dep-invalid-missing/src/index.ts', + ), + errors: [ + ERR_INLINE_MISSING( + '@internal/inline', + '@internal/inline-dep-valid, react', + ), + ], + }, // Switching back to original import declarations { @@ -302,5 +342,14 @@ ruleTester.run(RULE, rule, { filename: joinPath(FIXTURE, 'packages/bar/src/index.ts'), errors: [ERR_SWITCH_BACK()], }, + { + code: `import 'directive:inline-imports:@internal/inline'`, + output: `import '@internal/inline'`, + filename: joinPath( + FIXTURE, + 'packages/inline-dep-invalid-direct/src/index.ts', + ), + errors: [ERR_SWITCH_BACK()], + }, ], }); From 14dc624001591e569e45c8ee0121fafabf0d7a92 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 4 Sep 2024 18:46:28 +0300 Subject: [PATCH 198/317] docs/tooling: document backstage.inline field Signed-off-by: Patrik Oldsberg --- docs/tooling/package-metadata.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/tooling/package-metadata.md b/docs/tooling/package-metadata.md index 63ce6d1085..ad9d5ec5a0 100644 --- a/docs/tooling/package-metadata.md +++ b/docs/tooling/package-metadata.md @@ -156,6 +156,26 @@ This field indicates that a package has been renamed and moved to a new location } ``` +### `backstage.inline` + +If set to `true` this field indicates that monorepo package is private and should be inlined into dependent packages rather than being treated as a dependency. This effectively means that all imported code from the inlined package will be copied into the consuming package, once for each package. + +This flag affects various parts of the Backstage tooling, for example the way the Backstage CLI builds work, the way that `@backstage/eslint-plugin` lints dependencies on the package, and how it's treated by `@backstage/repo-tools`. + +The `backstage.inline` field is primarily intended to aid in the implementation of the Backstage core framework in the main Backstage repository, but it can be used in other projects as well. + +Setting this flag also requires the top-level `private` field to be set as well, since inline packages should not be published. + +```js title="Example usage of the backstage.moved field" +{ + "name": "@internal/utils", + "backstage": { + "inline": true + } + ... +} +``` + ## Metadata for Published Packages When publishing a package with the help of the Backstage CLI, there are a number of metadata checks that are performed to ensure that the package is correctly set up for the Backstage ecosystem. These checks are performed by the `backstage-cli package prepack` command, which is used to prepare a package for publishing. These checks can all also be verified separately using the `backstage-cli repo fix --publish` command, and in many cases the required metadata can be generated automatically. It is therefore important to make running the `fix` command part of your workflow in any project that is publishing Backstage packages. From 483c62da77965cc2fe468110bd2fecb52d644d3c Mon Sep 17 00:00:00 2001 From: Valber Junior <84424883+ValberJunior@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:19:53 -0300 Subject: [PATCH 199/317] Update github-workflows.yaml Signed-off-by: Valber Junior <84424883+ValberJunior@users.noreply.github.com> --- microsite/data/plugins/github-workflows.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/github-workflows.yaml b/microsite/data/plugins/github-workflows.yaml index 743e7a5b1b..f80e6f3f29 100644 --- a/microsite/data/plugins/github-workflows.yaml +++ b/microsite/data/plugins/github-workflows.yaml @@ -4,7 +4,7 @@ author: VeeCode Platform authorUrl: https://platform.vee.codes/ category: CI/CD description: The Github Workflows plugin provides an alternative for manually triggering GitHub workflows from within your Backstage component. -documentation: https://platform.vee.codes/plugin/Github%20workflows/ +documentation: https://platform.vee.codes/plugin/github-workflows/ iconUrl: https://veecode-platform.github.io/support/imgs/logo_2.svg npmPackageName: '@veecode-platform/backstage-plugin-github-workflows' tags: From 0f225d2b5acd7a2ee9ba821920008761797b107a Mon Sep 17 00:00:00 2001 From: Valber Junior <84424883+ValberJunior@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:21:30 -0300 Subject: [PATCH 200/317] Update gitlab-pipelines.yaml Signed-off-by: Valber Junior <84424883+ValberJunior@users.noreply.github.com> --- microsite/data/plugins/gitlab-pipelines.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/gitlab-pipelines.yaml b/microsite/data/plugins/gitlab-pipelines.yaml index d40419e464..4b77d67e07 100644 --- a/microsite/data/plugins/gitlab-pipelines.yaml +++ b/microsite/data/plugins/gitlab-pipelines.yaml @@ -4,7 +4,7 @@ author: VeeCode Platform authorUrl: https://platform.vee.codes category: CI/CD description: The Gitlab pipelines plugin integrates GitlabCi with its backstage component in a simple way. -documentation: https://platform.vee.codes/plugin/Gitlab%20Pipelines/ +documentation: https://platform.vee.codes/plugin/gitlab-pipelines/ iconUrl: https://veecode-platform.github.io/support/imgs/logo_1.svg npmPackageName: '@veecode-platform/backstage-plugin-gitlab-pipelines' tags: From ce617cf4d89abbb71794ebd3e18269b800b885f6 Mon Sep 17 00:00:00 2001 From: Valber Junior <84424883+ValberJunior@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:22:52 -0300 Subject: [PATCH 201/317] Update kong-service-manager.yaml Signed-off-by: Valber Junior <84424883+ValberJunior@users.noreply.github.com> --- microsite/data/plugins/kong-service-manager.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/kong-service-manager.yaml b/microsite/data/plugins/kong-service-manager.yaml index 78ff37e36e..2b5f444479 100644 --- a/microsite/data/plugins/kong-service-manager.yaml +++ b/microsite/data/plugins/kong-service-manager.yaml @@ -4,7 +4,7 @@ author: VeeCode Platform authorUrl: https://platform.vee.codes/ category: api-manager description: The Kong Service Manager plugin provides information about the kong service, a list of the routes it has and also offers the possibility of manipulating plugins without leaving the backstage. -documentation: https://platform.vee.codes/plugin/Kong%20Service%20Manager/ +documentation: https://platform.vee.codes/plugin/kong-service-manager/ iconUrl: https://veecode-platform.github.io/support/imgs/logo_3.svg npmPackageName: '@veecode-platform/plugin-kong-service-manager' tags: From 3e84c93e26e6f431bbfd74e28b164ad9a7536f12 Mon Sep 17 00:00:00 2001 From: Valber Junior <84424883+ValberJunior@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:23:52 -0300 Subject: [PATCH 202/317] Update kubernetes-gpt-analyzer.yaml Signed-off-by: Valber Junior <84424883+ValberJunior@users.noreply.github.com> --- microsite/data/plugins/kubernetes-gpt-analyzer.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/kubernetes-gpt-analyzer.yaml b/microsite/data/plugins/kubernetes-gpt-analyzer.yaml index 78bd0fe4c4..e5de961869 100644 --- a/microsite/data/plugins/kubernetes-gpt-analyzer.yaml +++ b/microsite/data/plugins/kubernetes-gpt-analyzer.yaml @@ -4,7 +4,7 @@ author: VeeCode Platform authorUrl: https://platform.vee.codes/ category: Monitoring description: The Kubernetes GPT Analyzer plug-in uses artificial intelligence with the help of k8s-operator to analyze and optimize your Kubernetes entities, improving the management and performance of your cluster. It makes it easier to detect anomalies and suggest best practices. -documentation: https://platform.vee.codes/plugin/Kubernetes%20GPT%20Analyzer/ +documentation: https://platform.vee.codes/plugin/kubernetes-gpt-analyzer/ iconUrl: https://veecode-platform.github.io/support/imgs/logo_4.svg npmPackageName: '@veecode-platform/backstage-plugin-kubernetes-gpt-analyzer' tags: From 8626aad138761e7f1f8bb9ab5723e1ff19a15b54 Mon Sep 17 00:00:00 2001 From: Rob Lindley Date: Wed, 4 Sep 2024 22:15:39 -0500 Subject: [PATCH 203/317] Add Webex scaffolder backend module to plugins listing Listing the scaffolder-backend-module-webex entity definition for @coderrob/backstage-plugin-scaffolder-backend-module-webex plugin. Signed-off-by: Rob Lindley --- .../data/plugins/scaffolder-backend-module-webex.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 microsite/data/plugins/scaffolder-backend-module-webex.yaml diff --git a/microsite/data/plugins/scaffolder-backend-module-webex.yaml b/microsite/data/plugins/scaffolder-backend-module-webex.yaml new file mode 100644 index 0000000000..f2113b2a8d --- /dev/null +++ b/microsite/data/plugins/scaffolder-backend-module-webex.yaml @@ -0,0 +1,11 @@ +--- +title: Cisco Webex Scaffolder Actions +author: Robert Lindley +authorUrl: https://github.com/Coderrob +category: Scaffolder +description: >- + A Backstage.io plugin that enables sending Webex messages via + Incoming Webhooks within scaffolder templates. +documentation: https://github.com/Coderrob/backstage-plugin-scaffolder-backend-module-webex +npmPackageName: '@coderrob/backstage-plugin-scaffolder-backend-module-webex' +addedDate: '2024-09-04' From b537bd7a6bc95c29d7e91091cee5eaa845801b01 Mon Sep 17 00:00:00 2001 From: Stephen Glass Date: Wed, 4 Sep 2024 23:21:58 -0400 Subject: [PATCH 204/317] custom theme options for star icon Signed-off-by: Stephen Glass --- .changeset/happy-bags-invite.md | 12 ++++++++++++ packages/app-defaults/src/defaults/icons.tsx | 4 ++++ packages/core-components/api-report.md | 6 ++++++ packages/core-components/src/icons/icons.tsx | 8 ++++++++ packages/theme/api-report.md | 9 +++++++++ packages/theme/src/base/palettes.ts | 6 ++++++ packages/theme/src/base/types.ts | 3 +++ .../src/components/FavoriteEntity/FavoriteEntity.tsx | 12 +++++------- .../src/components/UserListPicker/UserListPicker.tsx | 2 +- .../src/components/CatalogTable/CatalogTable.tsx | 12 ++++++------ .../StarredEntityListItem/StarredEntityListItem.tsx | 11 +++++++++-- .../techdocs/src/home/components/Tables/actions.tsx | 11 +++++------ 12 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 .changeset/happy-bags-invite.md diff --git a/.changeset/happy-bags-invite.md b/.changeset/happy-bags-invite.md new file mode 100644 index 0000000000..02dc05f5fd --- /dev/null +++ b/.changeset/happy-bags-invite.md @@ -0,0 +1,12 @@ +--- +'@backstage/core-components': patch +'@backstage/app-defaults': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-catalog': patch +'@backstage/theme': patch +'@backstage/plugin-home': patch +--- + +- Allow custom star icons to be provided via the `star` and `unstarred` icon overides. See how to override existing icons in the [Backstage documentation](https://backstage.io/docs/getting-started/app-custom-theme/#custom-icons). +- Add `entityStarButton.color` theme option to allow custom themes to specify color for filled star icons. diff --git a/packages/app-defaults/src/defaults/icons.tsx b/packages/app-defaults/src/defaults/icons.tsx index 1ed5bf03c6..ebda36c131 100644 --- a/packages/app-defaults/src/defaults/icons.tsx +++ b/packages/app-defaults/src/defaults/icons.tsx @@ -36,6 +36,8 @@ import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; import MuiStorageIcon from '@material-ui/icons/Storage'; import MuiFeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList'; +import Star from '@material-ui/icons/Star'; +import StarBorder from '@material-ui/icons/StarBorder'; export const icons = { brokenImage: MuiBrokenImageIcon as IconComponent, @@ -62,4 +64,6 @@ export const icons = { 'kind:template': MuiFeaturedPlayListIcon as IconComponent, user: MuiPersonIcon as IconComponent, warning: MuiWarningIcon as IconComponent, + star: Star as IconComponent, + unstarred: StarBorder as IconComponent, }; diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 597b1a9abf..e2c1e47f78 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -1186,6 +1186,9 @@ export function SimpleStepperStep( // @public (undocumented) export type SimpleStepperStepClassKey = 'end'; +// @public (undocumented) +export function StarIcon(props: IconComponentProps): React_2.JSX.Element; + // Warning: (ae-missing-release-tag) "StatusAborted" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1455,6 +1458,9 @@ export function TrendLine( }, ): React_2.JSX.Element | null; +// @public (undocumented) +export function UnstarredIcon(props: IconComponentProps): React_2.JSX.Element; + // @public export function useContent(): { focusContent: () => void; diff --git a/packages/core-components/src/icons/icons.tsx b/packages/core-components/src/icons/icons.tsx index ca3b6fa76a..bd1bf9e86e 100644 --- a/packages/core-components/src/icons/icons.tsx +++ b/packages/core-components/src/icons/icons.tsx @@ -95,3 +95,11 @@ export function UserIcon(props: IconComponentProps) { export function WarningIcon(props: IconComponentProps) { return ; } +/** @public */ +export function StarIcon(props: IconComponentProps) { + return ; +} +/** @public */ +export function UnstarredIcon(props: IconComponentProps) { + return ; +} diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index 988569ece7..4c6a8c1b5d 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -81,6 +81,9 @@ export type BackstagePaletteAdditions = { closeButtonColor?: string; warning?: string; }; + entityStarButton: { + color: string; + }; }; // @public @deprecated @@ -310,6 +313,9 @@ export const palettes: { tabbar: { indicator: string; }; + entityStarButton: { + color: string; + }; }; dark: { type: 'dark'; @@ -384,6 +390,9 @@ export const palettes: { tabbar: { indicator: string; }; + entityStarButton: { + color: string; + }; }; }; diff --git a/packages/theme/src/base/palettes.ts b/packages/theme/src/base/palettes.ts index 5e70afd7b8..55d21a060d 100644 --- a/packages/theme/src/base/palettes.ts +++ b/packages/theme/src/base/palettes.ts @@ -89,6 +89,9 @@ export const palettes = { tabbar: { indicator: '#9BF0E1', }, + entityStarButton: { + color: '#F3BA37', + }, }, dark: { type: 'dark' as const, @@ -163,5 +166,8 @@ export const palettes = { tabbar: { indicator: '#9BF0E1', }, + entityStarButton: { + color: '#F3BA37', + }, }, }; diff --git a/packages/theme/src/base/types.ts b/packages/theme/src/base/types.ts index d62d944741..4872ee8042 100644 --- a/packages/theme/src/base/types.ts +++ b/packages/theme/src/base/types.ts @@ -82,6 +82,9 @@ export type BackstagePaletteAdditions = { closeButtonColor?: string; warning?: string; }; + entityStarButton: { + color: string; + }; }; /** diff --git a/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx index ab674ff98e..9ddba4a2cc 100644 --- a/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx +++ b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx @@ -18,8 +18,7 @@ import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import IconButton from '@material-ui/core/IconButton'; import Tooltip from '@material-ui/core/Tooltip'; import { withStyles } from '@material-ui/core/styles'; -import Star from '@material-ui/icons/Star'; -import StarBorder from '@material-ui/icons/StarBorder'; +import { StarIcon, UnstarredIcon } from '@backstage/core-components'; import React, { ComponentProps } from 'react'; import { useStarredEntity } from '../../hooks/useStarredEntity'; import { catalogReactTranslationRef } from '../../translation'; @@ -30,12 +29,11 @@ export type FavoriteEntityProps = ComponentProps & { entity: Entity; }; -const YellowStar = withStyles({ +const FilledStar = withStyles(theme => ({ root: { - color: '#f3ba37', + color: theme.palette.entityStarButton.color, }, -})(Star); - +}))(StarIcon); /** * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities * @param props - MaterialUI IconButton props extended by required `entity` prop @@ -64,7 +62,7 @@ export const FavoriteEntity = (props: FavoriteEntityProps) => { onClick={() => toggleStarredEntity()} > - {isStarredEntity ? : } + {isStarredEntity ? : } ); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx index c6b515aa67..f572cec814 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx @@ -28,7 +28,7 @@ import MenuItem from '@material-ui/core/MenuItem'; import Typography from '@material-ui/core/Typography'; import { makeStyles } from '@material-ui/core/styles'; import SettingsIcon from '@material-ui/icons/Settings'; -import StarIcon from '@material-ui/icons/Star'; +import { StarIcon } from '@backstage/core-components'; import React, { Fragment, useEffect, useMemo, useState } from 'react'; import { EntityUserFilter } from '../../filters'; import { useEntityList } from '../../hooks'; diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 1026dc5d26..b136d98a82 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -23,6 +23,8 @@ import { } from '@backstage/catalog-model'; import { CodeSnippet, + StarIcon, + UnstarredIcon, Table, TableColumn, TableProps, @@ -39,8 +41,6 @@ import { withStyles } from '@material-ui/core/styles'; import { visuallyHidden } from '@mui/utils'; import Edit from '@material-ui/icons/Edit'; import OpenInNew from '@material-ui/icons/OpenInNew'; -import Star from '@material-ui/icons/Star'; -import StarBorder from '@material-ui/icons/StarBorder'; import { capitalize } from 'lodash'; import pluralize from 'pluralize'; import React, { ReactNode, useMemo } from 'react'; @@ -64,11 +64,11 @@ export interface CatalogTableProps { subtitle?: string; } -const YellowStar = withStyles({ +const FilledStar = withStyles(theme => ({ root: { - color: '#f3ba37', + color: theme.palette.entityStarButton.color, }, -})(Star); +}))(StarIcon); const refCompare = (a: Entity, b: Entity) => { const toRef = (entity: Entity) => @@ -163,7 +163,7 @@ export const CatalogTable = (props: CatalogTableProps) => { icon: () => ( <> {title} - {isStarred ? : } + {isStarred ? : } ), tooltip: title, diff --git a/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx index 579116b9a9..234ae699ec 100644 --- a/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx +++ b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx @@ -24,7 +24,8 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { useRouteRef } from '@backstage/core-plugin-api'; -import StarIcon from '@material-ui/icons/Star'; +import { StarIcon } from '@backstage/core-components'; +import { withStyles } from '@material-ui/core/styles'; type EntityListItemProps = { entity: Entity; @@ -37,6 +38,12 @@ export const StarredEntityListItem = ({ }: EntityListItemProps) => { const catalogEntityRoute = useRouteRef(entityRouteRef); + const FilledStar = withStyles(theme => ({ + root: { + color: theme.palette.entityStarButton.color, + }, + }))(StarIcon); + return ( @@ -46,7 +53,7 @@ export const StarredEntityListItem = ({ aria-label="unstar" onClick={() => onToggleStarredEntity(entity)} > - + diff --git a/plugins/techdocs/src/home/components/Tables/actions.tsx b/plugins/techdocs/src/home/components/Tables/actions.tsx index c95dd8b94c..c3eaa038c8 100644 --- a/plugins/techdocs/src/home/components/Tables/actions.tsx +++ b/plugins/techdocs/src/home/components/Tables/actions.tsx @@ -18,14 +18,13 @@ import React from 'react'; import ShareIcon from '@material-ui/icons/Share'; import { DocsTableRow } from './types'; import { withStyles } from '@material-ui/core/styles'; -import Star from '@material-ui/icons/Star'; -import StarBorder from '@material-ui/icons/StarBorder'; +import { StarIcon, UnstarredIcon } from '@backstage/core-components'; -const YellowStar = withStyles({ +const FilledStar = withStyles(theme => ({ root: { - color: '#f3ba37', + color: theme.palette.entityStarButton.color, }, -})(Star); +}))(StarIcon); /** * Not directly exported, but through DocsTable.actions and EntityListDocsTable.actions @@ -52,7 +51,7 @@ export const actionFactories = { const isStarred = isStarredEntity(entity); return { cellStyle: { paddingLeft: '1em' }, - icon: () => (isStarred ? : ), + icon: () => (isStarred ? : ), tooltip: isStarred ? 'Remove from favorites' : 'Add to favorites', onClick: () => toggleStarredEntity(entity), }; From decb4bf1f6a7a66d9e8d2c3c35aad56a8ab66b33 Mon Sep 17 00:00:00 2001 From: Stephen Glass Date: Thu, 5 Sep 2024 00:49:16 -0400 Subject: [PATCH 205/317] fix tests for star icon Signed-off-by: Stephen Glass --- packages/core-app-api/api-report.md | 2 ++ packages/core-app-api/src/app/types.ts | 2 ++ .../core-compat-api/src/compatWrapper/compatWrapper.test.tsx | 4 ++-- packages/test-utils/src/testUtils/appWrappers.tsx | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 8b8cc991c8..3d2c44a148 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -196,6 +196,8 @@ export type AppIcons = { techdocs: IconComponent; user: IconComponent; warning: IconComponent; + star: IconComponent; + unstarred: IconComponent; }; // @public diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 9d2c77292c..513063e974 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -115,6 +115,8 @@ export type AppIcons = { techdocs: IconComponent; user: IconComponent; warning: IconComponent; + star: IconComponent; + unstarred: IconComponent; }; /** diff --git a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx index 6357f13906..2e6b127070 100644 --- a/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx +++ b/packages/core-compat-api/src/compatWrapper/compatWrapper.test.tsx @@ -72,7 +72,7 @@ describe('BackwardsCompatProvider', () => { expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(` "plugins: components: NotFoundErrorPage, BootErrorPage, Progress, Router, ErrorBoundaryFallback - icons: brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, user, warning" + icons: brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, user, warning, star, unstarred" `); }); @@ -116,7 +116,7 @@ describe('ForwardsCompatProvider', () => { expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(` "components: progress=true, notFoundErrorPage=true, errorBoundaryFallback=true - icons: kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, user, warning" + icons: kind:api, kind:component, kind:domain, kind:group, kind:location, kind:system, kind:user, kind:resource, kind:template, brokenImage, catalog, scaffolder, techdocs, search, chat, dashboard, docs, email, github, group, help, user, warning, star, unstarred" `); }); diff --git a/packages/test-utils/src/testUtils/appWrappers.tsx b/packages/test-utils/src/testUtils/appWrappers.tsx index 3a55389b74..6f9361f39b 100644 --- a/packages/test-utils/src/testUtils/appWrappers.tsx +++ b/packages/test-utils/src/testUtils/appWrappers.tsx @@ -63,6 +63,8 @@ const mockIcons = { help: MockIcon, user: MockIcon, warning: MockIcon, + star: MockIcon, + unstarred: MockIcon, }; const ErrorBoundaryFallback = ({ error }: { error: Error }) => { From b0a5c9fc8fda352140064547c93fc6b2921c97f4 Mon Sep 17 00:00:00 2001 From: Stephen Glass Date: Thu, 5 Sep 2024 01:22:02 -0400 Subject: [PATCH 206/317] pass ownedentitypicker ui options to entitypicker Signed-off-by: Stephen Glass --- .changeset/proud-forks-buy.md | 5 +++++ .../fields/OwnedEntityPicker/OwnedEntityPicker.tsx | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/proud-forks-buy.md diff --git a/.changeset/proud-forks-buy.md b/.changeset/proud-forks-buy.md new file mode 100644 index 0000000000..558d5d9078 --- /dev/null +++ b/.changeset/proud-forks-buy.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +The `ui:options` for `OwnedEntityPicker` field are now passed to `EntityPicker`. This allows you to use any `ui:options` which `EntityPicker` accepts in the `OwnedEntityPicker` field including `allowArbitraryValues` and `defaultNamespace`. diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx index 06fdf17824..63e21f5dc6 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx @@ -94,7 +94,7 @@ function buildEntityPickerUISchema( // Note: This is typed to avoid es-lint rule TS2698 const uiOptions: EntityPickerProps['uiSchema']['ui:options'] = uiSchema?.['ui:options'] || {}; - const allowedKinds = uiOptions.allowedKinds; + const { allowedKinds, ...extraOptions } = uiOptions; const catalogFilter = asArray(uiOptions.catalogFilter).map(e => ({ ...e, @@ -104,6 +104,7 @@ function buildEntityPickerUISchema( return { 'ui:options': { + ...extraOptions, catalogFilter, }, }; From 4d5c115b9f18651467523f0a219ef0483465749f Mon Sep 17 00:00:00 2001 From: Stephen Glass Date: Thu, 5 Sep 2024 01:30:16 -0400 Subject: [PATCH 207/317] update changeset for star icon Signed-off-by: Stephen Glass --- .changeset/happy-bags-invite.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/happy-bags-invite.md b/.changeset/happy-bags-invite.md index 02dc05f5fd..ba8f3e846e 100644 --- a/.changeset/happy-bags-invite.md +++ b/.changeset/happy-bags-invite.md @@ -6,6 +6,8 @@ '@backstage/plugin-catalog': patch '@backstage/theme': patch '@backstage/plugin-home': patch +'@backstage/core-app-api': patch +'@backstage/test-utils': patch --- - Allow custom star icons to be provided via the `star` and `unstarred` icon overides. See how to override existing icons in the [Backstage documentation](https://backstage.io/docs/getting-started/app-custom-theme/#custom-icons). From 0e999448878d888090a7a757dab43c061bcf8979 Mon Sep 17 00:00:00 2001 From: swnia <119884634+swnia@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:48:00 +0200 Subject: [PATCH 208/317] Update .changeset/cuddly-glasses-battle.md Co-authored-by: Camila Belo Signed-off-by: swnia <119884634+swnia@users.noreply.github.com> --- .changeset/cuddly-glasses-battle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/cuddly-glasses-battle.md b/.changeset/cuddly-glasses-battle.md index b9cfbbcb99..be1d1a27e3 100644 --- a/.changeset/cuddly-glasses-battle.md +++ b/.changeset/cuddly-glasses-battle.md @@ -2,6 +2,6 @@ '@backstage/integration-react': patch --- -Remove unnecessary broad permissions from Gitlab SCMAuth +Remove unnecessary broad permissions from Gitlab `SCMAuth` Newer versions of Gitlab (after 2019) do not require the broad api permissions to write to repos. From fa9d8da1b55b2756d95a68e1483ba2825fbe8d2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 08:07:57 +0000 Subject: [PATCH 209/317] fix(deps): update rjsf monorepo to v5.20.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-a674163.md | 11 +++++ plugins/home-react/package.json | 2 +- plugins/home/package.json | 8 ++-- plugins/scaffolder-react/package.json | 8 ++-- plugins/scaffolder/package.json | 8 ++-- yarn.lock | 66 +++++++++++++-------------- 6 files changed, 57 insertions(+), 46 deletions(-) create mode 100644 .changeset/renovate-a674163.md diff --git a/.changeset/renovate-a674163.md b/.changeset/renovate-a674163.md new file mode 100644 index 0000000000..396139042a --- /dev/null +++ b/.changeset/renovate-a674163.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-home-react': patch +'@backstage/plugin-home': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +--- + +Updated dependency `@rjsf/utils` to `5.20.1`. +Updated dependency `@rjsf/core` to `5.20.1`. +Updated dependency `@rjsf/material-ui` to `5.20.1`. +Updated dependency `@rjsf/validator-ajv8` to `5.20.1`. diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index 1af7a32d86..566a9596b4 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -46,7 +46,7 @@ "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@rjsf/utils": "5.18.5", + "@rjsf/utils": "5.20.1", "@types/react": "^16.13.1 || ^17.0.0" }, "devDependencies": { diff --git a/plugins/home/package.json b/plugins/home/package.json index 7204942d52..f760ab8bd3 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -70,10 +70,10 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@rjsf/core": "5.18.5", - "@rjsf/material-ui": "5.18.5", - "@rjsf/utils": "5.18.5", - "@rjsf/validator-ajv8": "5.18.5", + "@rjsf/core": "5.20.1", + "@rjsf/material-ui": "5.20.1", + "@rjsf/utils": "5.20.1", + "@rjsf/validator-ajv8": "5.20.1", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "luxon": "^3.4.3", diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index d952986e6e..17fe2a5524 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -72,10 +72,10 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@react-hookz/web": "^24.0.0", - "@rjsf/core": "5.18.5", - "@rjsf/material-ui": "5.18.5", - "@rjsf/utils": "5.18.5", - "@rjsf/validator-ajv8": "5.18.5", + "@rjsf/core": "5.20.1", + "@rjsf/material-ui": "5.20.1", + "@rjsf/utils": "5.20.1", + "@rjsf/validator-ajv8": "5.20.1", "@types/json-schema": "^7.0.9", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "ajv-errors": "^3.0.0", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 2e75d776d2..9bfe01e6c6 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -81,10 +81,10 @@ "@material-ui/lab": "4.0.0-alpha.61", "@microsoft/fetch-event-source": "^2.0.1", "@react-hookz/web": "^24.0.0", - "@rjsf/core": "5.18.5", - "@rjsf/material-ui": "5.18.5", - "@rjsf/utils": "5.18.5", - "@rjsf/validator-ajv8": "5.18.5", + "@rjsf/core": "5.20.1", + "@rjsf/material-ui": "5.20.1", + "@rjsf/utils": "5.20.1", + "@rjsf/validator-ajv8": "5.20.1", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@uiw/react-codemirror": "^4.9.3", "classnames": "^2.2.6", diff --git a/yarn.lock b/yarn.lock index b4eb92b7b0..abde1680fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6231,7 +6231,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 - "@rjsf/utils": 5.18.5 + "@rjsf/utils": 5.20.1 "@types/react": ^16.13.1 || ^17.0.0 "@types/react-grid-layout": ^1.3.2 peerDependencies: @@ -6262,10 +6262,10 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@rjsf/core": 5.18.5 - "@rjsf/material-ui": 5.18.5 - "@rjsf/utils": 5.18.5 - "@rjsf/validator-ajv8": 5.18.5 + "@rjsf/core": 5.20.1 + "@rjsf/material-ui": 5.20.1 + "@rjsf/utils": 5.20.1 + "@rjsf/validator-ajv8": 5.20.1 "@testing-library/dom": ^10.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^15.0.0 @@ -7256,10 +7256,10 @@ __metadata: "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 "@react-hookz/web": ^24.0.0 - "@rjsf/core": 5.18.5 - "@rjsf/material-ui": 5.18.5 - "@rjsf/utils": 5.18.5 - "@rjsf/validator-ajv8": 5.18.5 + "@rjsf/core": 5.20.1 + "@rjsf/material-ui": 5.20.1 + "@rjsf/utils": 5.20.1 + "@rjsf/validator-ajv8": 5.20.1 "@testing-library/dom": ^10.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^15.0.0 @@ -7323,10 +7323,10 @@ __metadata: "@material-ui/lab": 4.0.0-alpha.61 "@microsoft/fetch-event-source": ^2.0.1 "@react-hookz/web": ^24.0.0 - "@rjsf/core": 5.18.5 - "@rjsf/material-ui": 5.18.5 - "@rjsf/utils": 5.18.5 - "@rjsf/validator-ajv8": 5.18.5 + "@rjsf/core": 5.20.1 + "@rjsf/material-ui": 5.20.1 + "@rjsf/utils": 5.20.1 + "@rjsf/validator-ajv8": 5.20.1 "@testing-library/dom": ^10.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^15.0.0 @@ -14621,9 +14621,9 @@ __metadata: languageName: node linkType: hard -"@rjsf/core@npm:5.18.5": - version: 5.18.5 - resolution: "@rjsf/core@npm:5.18.5" +"@rjsf/core@npm:5.20.1": + version: 5.20.1 + resolution: "@rjsf/core@npm:5.20.1" dependencies: lodash: ^4.17.21 lodash-es: ^4.17.21 @@ -14631,28 +14631,28 @@ __metadata: nanoid: ^3.3.7 prop-types: ^15.8.1 peerDependencies: - "@rjsf/utils": ^5.18.x + "@rjsf/utils": ^5.20.x react: ^16.14.0 || >=17 - checksum: 7b986ec075d5b2de503d01172abdb3e3d33d04e5298fbf80842400e682f0aecdabfe4bb8482dde8a6aed90a35f4f5574cb7b24a11d689566c0cc0599485b334a + checksum: a75a5261090bc1dd46594060981a130721060c38805031d1554b077e46673f84ffb40c489c2b579cf50e4fbb709210585a139e1c5d9eaccd603d957e95c2ead2 languageName: node linkType: hard -"@rjsf/material-ui@npm:5.18.5": - version: 5.18.5 - resolution: "@rjsf/material-ui@npm:5.18.5" +"@rjsf/material-ui@npm:5.20.1": + version: 5.20.1 + resolution: "@rjsf/material-ui@npm:5.20.1" peerDependencies: "@material-ui/core": ^4.12.3 "@material-ui/icons": ^4.11.2 - "@rjsf/core": ^5.18.x - "@rjsf/utils": ^5.18.x + "@rjsf/core": ^5.20.x + "@rjsf/utils": ^5.20.x react: ^16.14.0 || >=17 - checksum: 61337141bcd7bcb46013d81ae6b618919eff3f55319800511443e524876bf5c84089970e14148716de203d5def341aa15c4d102243f0b694f120f05189a3adb7 + checksum: d7f5d4107ccf31d3320ef2f42e50c79004aaff44cac76dba60382f8f7e051fbb3b0470675bdccc25aa15c18ac84c77301509546eb0f0cfac897fd06a0f95facd languageName: node linkType: hard -"@rjsf/utils@npm:5.18.5": - version: 5.18.5 - resolution: "@rjsf/utils@npm:5.18.5" +"@rjsf/utils@npm:5.20.1": + version: 5.20.1 + resolution: "@rjsf/utils@npm:5.20.1" dependencies: json-schema-merge-allof: ^0.8.1 jsonpointer: ^5.0.1 @@ -14661,21 +14661,21 @@ __metadata: react-is: ^18.2.0 peerDependencies: react: ^16.14.0 || >=17 - checksum: 8da3ce82a0ec83e78806461db7226fd20489f6e8c41906ccde9d55c554aec3f50b8b8bac8c35e3cdf12d788c18fdad71c992f0d8b44f6161a1bf9e13598ded99 + checksum: 8bf59caeb9d32d40ec492df7fc702f0573ad2054042a3a0676fb7e10afa56d0d85740be28a752782fc16273952553ab76b40eb17e4e5b1178f473628003109f4 languageName: node linkType: hard -"@rjsf/validator-ajv8@npm:5.18.5": - version: 5.18.5 - resolution: "@rjsf/validator-ajv8@npm:5.18.5" +"@rjsf/validator-ajv8@npm:5.20.1": + version: 5.20.1 + resolution: "@rjsf/validator-ajv8@npm:5.20.1" dependencies: ajv: ^8.12.0 ajv-formats: ^2.1.1 lodash: ^4.17.21 lodash-es: ^4.17.21 peerDependencies: - "@rjsf/utils": ^5.18.x - checksum: 350d1f8043b7bbd88cf5d7fa28560062f4141e1ef9ef004c2997b63d0281d4a1af04be4b951c2c9c4d1c56d10f0e2fbab665c7176b38e87dd6da6d5303e1c84e + "@rjsf/utils": ^5.20.x + checksum: b6d9833335903465fa79cc808e4ad564d6f09adb23e728d3a3d0d309202b45846494ea1bed2bbc32b07d8583d37c9ce8fe9772ba48aa77c0660829162517c4d2 languageName: node linkType: hard From bad217de5e4355de777663be2915b04197e5c77c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 08:30:57 +0000 Subject: [PATCH 210/317] fix(deps): update aws-sdk-js-v3 monorepo Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 248 +++++++++++++++++++++++++++--------------------------- 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/yarn.lock b/yarn.lock index b4eb92b7b0..c87eed01d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -500,22 +500,22 @@ __metadata: linkType: hard "@aws-sdk/client-codecommit@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/client-codecommit@npm:3.637.0" + version: 3.645.0 + resolution: "@aws-sdk/client-codecommit@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -545,27 +545,27 @@ __metadata: "@smithy/util-utf8": ^3.0.0 tslib: ^2.6.2 uuid: ^9.0.1 - checksum: 25b9ac4e58e6d6a5ca1062253ecfee723ed7f54058f4c2f7f9c1a6af56b65de27f5c0c3a46870a0f5e652576719ed6f332a9dd5cfd5ceed451586f04a088995d + checksum: 237f37d78618a3e385e1f48b37f99ea35394b009a6d7703c20cf4e9c39dc8905b0fb2e19137c284f6ed5790d4910d19af37e89351f128d24d0779f6cc20f0a2d languageName: node linkType: hard -"@aws-sdk/client-cognito-identity@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.637.0" +"@aws-sdk/client-cognito-identity@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -594,27 +594,27 @@ __metadata: "@smithy/util-retry": ^3.0.3 "@smithy/util-utf8": ^3.0.0 tslib: ^2.6.2 - checksum: cfb3b3926012340c36dcc839e529f19e31addb0566e6da49f4928510d4b683aeb2b2926576f9013442ca862d48638316b3e7f9c9737149f276b5d5d0ac5eb3d0 + checksum: 77270145fd39384181933815b94f52d97ea7c9dccdaa02d74cf59783d27a308798de5ffab8a34149f9b731dfaebe737073394b53b15181d5c75d3538b1ced532 languageName: node linkType: hard "@aws-sdk/client-eks@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/client-eks@npm:3.637.0" + version: 3.645.0 + resolution: "@aws-sdk/client-eks@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -645,27 +645,27 @@ __metadata: "@smithy/util-waiter": ^3.1.2 tslib: ^2.6.2 uuid: ^9.0.1 - checksum: 7c2f318d7b5a647c7c993ae6ae9468ed9c4d3a99b7a92035f8f3e17770025d7c80486abd56f29f018d088376ea8035beb36fec8cd9b3e30aade1bad3061cab64 + checksum: 77b4841984ce3fc500660095e04392ea333e7b79785f793af3916f51cd74c4775a5ea8bfaf596a30a6b6f812892967e69e5087431d1c59926c579869218b600a languageName: node linkType: hard "@aws-sdk/client-organizations@npm:^3.350.0": - version: 3.638.0 - resolution: "@aws-sdk/client-organizations@npm:3.638.0" + version: 3.645.0 + resolution: "@aws-sdk/client-organizations@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -694,21 +694,21 @@ __metadata: "@smithy/util-retry": ^3.0.3 "@smithy/util-utf8": ^3.0.0 tslib: ^2.6.2 - checksum: 91340539b3b272545b3d9ca389144f8e5640fcd8bc14ff6b0670a3614fc29e36e577f14384f61996bdd83c380dc7e167baeea8445d93ff3271198db8f0a8646a + checksum: 93be30563c9c6dbf885e9e571ad8de84a09e2d29d9491936e8519265a3cfcf06e56a16a24fce4bec6209d0bc654880d42f03a2003845dba8e0b2cfc9c34ff9b3 languageName: node linkType: hard "@aws-sdk/client-s3@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/client-s3@npm:3.637.0" + version: 3.645.0 + resolution: "@aws-sdk/client-s3@npm:3.645.0" dependencies: "@aws-crypto/sha1-browser": 5.2.0 "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-bucket-endpoint": 3.620.0 "@aws-sdk/middleware-expect-continue": 3.620.0 "@aws-sdk/middleware-flexible-checksums": 3.620.0 @@ -718,11 +718,11 @@ __metadata: "@aws-sdk/middleware-recursion-detection": 3.620.0 "@aws-sdk/middleware-sdk-s3": 3.635.0 "@aws-sdk/middleware-ssec": 3.609.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/signature-v4-multi-region": 3.635.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@aws-sdk/xml-builder": 3.609.0 @@ -760,27 +760,27 @@ __metadata: "@smithy/util-utf8": ^3.0.0 "@smithy/util-waiter": ^3.1.2 tslib: ^2.6.2 - checksum: 244e8de633ffb291c6e9a3dc952727cc71022b81690292d3eacf719d8b395342b9912a0710c2172cf8b986b5d58ca5c40970b69e0148a881453a48c604276cb4 + checksum: 9088ffbd8d95666d6830c8b3d155641c137fc008e4ab11ecae0b13db17da27d20e531cff47f5968f7892d8f10b3794059b23f64a743a1b975aea444c1ea29c1c languageName: node linkType: hard "@aws-sdk/client-ses@npm:^3.550.0": - version: 3.637.0 - resolution: "@aws-sdk/client-ses@npm:3.637.0" + version: 3.645.0 + resolution: "@aws-sdk/client-ses@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -810,28 +810,28 @@ __metadata: "@smithy/util-utf8": ^3.0.0 "@smithy/util-waiter": ^3.1.2 tslib: ^2.6.2 - checksum: 4ab9ae76590cdac74e5cc8f3ced77c40df741d7f31a7ac7ac24a72493e5199ce808a66824a787196deb546f6bd3a39e9e2612618b219698a6268418ef3d8e5f6 + checksum: 22719e939f0f8b84a867e08ca823c13f43e2ac25da78fd51133b62acc5b305a6f651a76b3363b2bbeed9e4e6f2f9cde7596e58ebc8df72a3ec9a8918ce96746c languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/client-sqs@npm:3.637.0" + version: 3.645.0 + resolution: "@aws-sdk/client-sqs@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 "@aws-sdk/middleware-sdk-sqs": 3.635.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -861,25 +861,25 @@ __metadata: "@smithy/util-retry": ^3.0.3 "@smithy/util-utf8": ^3.0.0 tslib: ^2.6.2 - checksum: 9c64774215ece7768cdf895261a7ae3c857637e7a3da9e077b7cc31e3c1afa0772aee646d93f704d6b7ee66eca4e154af53d29b7ca9d50002ae2eff3ede94ac6 + checksum: 6e2e0b07dce77cfd3eace0bb0a088c014f36cf645f09aa9b478fd1aa98ad70ade7b6f1db69a78bb52678a92b3db930a57f8446d61f8e5b0457b48de6b8a14b24 languageName: node linkType: hard -"@aws-sdk/client-sso-oidc@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/client-sso-oidc@npm:3.637.0" +"@aws-sdk/client-sso-oidc@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -909,14 +909,14 @@ __metadata: "@smithy/util-utf8": ^3.0.0 tslib: ^2.6.2 peerDependencies: - "@aws-sdk/client-sts": ^3.637.0 - checksum: d56a94144b7b3f49c1bab26fa69dfffdc6227d1c8bc6d8094590bc76f66869c3c49639833f762fcd573863866983469472693a289302da9125820d1b3a0aa86a + "@aws-sdk/client-sts": ^3.645.0 + checksum: 00e1b0bba75365fbd0ea5752111e97960701553e0402bf4fc4eba3849fdc121801edd16792cd38b8cd4bdb38f3ee41dc64ecf9d6654f7f9864c4120178afb094 languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/client-sso@npm:3.637.0" +"@aws-sdk/client-sso@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/client-sso@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 @@ -924,10 +924,10 @@ __metadata: "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -956,26 +956,26 @@ __metadata: "@smithy/util-retry": ^3.0.3 "@smithy/util-utf8": ^3.0.0 tslib: ^2.6.2 - checksum: 7d8fa5ca9740fcce7f8eb22859c6315fc4a2148633af143dd7eadb12ddf84e79cb403562861090904addd3f002f0d2143541bfefe92051f9ad275ed7627dde6a + checksum: c27e82109dbe89f6d7dfb1e1a670ebadc687cc9536298618e75cdc2ef431c4698ceba2331ae660d1bc1aca213e4ca9246621645d87e88cb518ac681aa19de341 languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.637.0, @aws-sdk/client-sts@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/client-sts@npm:3.637.0" +"@aws-sdk/client-sts@npm:3.645.0, @aws-sdk/client-sts@npm:^3.350.0": + version: 3.645.0 + resolution: "@aws-sdk/client-sts@npm:3.645.0" dependencies: "@aws-crypto/sha256-browser": 5.2.0 "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/client-sso-oidc": 3.637.0 + "@aws-sdk/client-sso-oidc": 3.645.0 "@aws-sdk/core": 3.635.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/middleware-host-header": 3.620.0 "@aws-sdk/middleware-logger": 3.609.0 "@aws-sdk/middleware-recursion-detection": 3.620.0 - "@aws-sdk/middleware-user-agent": 3.637.0 + "@aws-sdk/middleware-user-agent": 3.645.0 "@aws-sdk/region-config-resolver": 3.614.0 "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@aws-sdk/util-user-agent-browser": 3.609.0 "@aws-sdk/util-user-agent-node": 3.614.0 "@smithy/config-resolver": ^3.0.5 @@ -1004,7 +1004,7 @@ __metadata: "@smithy/util-retry": ^3.0.3 "@smithy/util-utf8": ^3.0.0 tslib: ^2.6.2 - checksum: 5045a9a11eec97690bb6fde581657f85ff9be5d11441594adbf9d95e7825c5a40c78697284e2630ce731ab687ff410b7085f4f60cea88683936a41cec0b472a1 + checksum: af36231a2fcda67323ec3611630643f8005b04f0c2f326a181e7000a813d010dba7c2d541adfbea0798d7c337ea70b5a57a2b61ce77bb4ece6dd7b01879174ed languageName: node linkType: hard @@ -1026,16 +1026,16 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-cognito-identity@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.637.0" +"@aws-sdk/credential-provider-cognito-identity@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.645.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.637.0 + "@aws-sdk/client-cognito-identity": 3.645.0 "@aws-sdk/types": 3.609.0 "@smithy/property-provider": ^3.1.3 "@smithy/types": ^3.3.0 tslib: ^2.6.2 - checksum: 17991646b1d5d71faca2afff4459bc2bf0ae4bf5b208b2de7a5770116c32a150569dc5d31ba6c286ae0688cca7c5b8c7234b04f21c7ea1e9bef460013db61ecc + checksum: 5a70bab9d26aa95ba8f95e201bf237f0fe51b4776e499cadfcbda90bdb26be77b22252d1e1d2317cd1137fc21dfad5a08502307371ab21b7dd6b1515f761754b languageName: node linkType: hard @@ -1068,14 +1068,14 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.637.0" +"@aws-sdk/credential-provider-ini@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.645.0" dependencies: "@aws-sdk/credential-provider-env": 3.620.1 "@aws-sdk/credential-provider-http": 3.635.0 "@aws-sdk/credential-provider-process": 3.620.1 - "@aws-sdk/credential-provider-sso": 3.637.0 + "@aws-sdk/credential-provider-sso": 3.645.0 "@aws-sdk/credential-provider-web-identity": 3.621.0 "@aws-sdk/types": 3.609.0 "@smithy/credential-provider-imds": ^3.2.0 @@ -1084,20 +1084,20 @@ __metadata: "@smithy/types": ^3.3.0 tslib: ^2.6.2 peerDependencies: - "@aws-sdk/client-sts": ^3.637.0 - checksum: ff005eb0f451f7a95e01dbdaee6ac67e3edcd8587a25d77544c03c05589e5f033a4b3aca0137d0d067ecb4f1e3ed194c06dc6e15b9a0d95bac89d613a3c26399 + "@aws-sdk/client-sts": ^3.645.0 + checksum: ef0cae8931a38eebab6009f8cfb4a42313cb277670f0b06e3d5b1a2526089fa6cc50ad50b78c79c8fbbf132010fffa67622963fe5fe04a73401f84faa146495f languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.637.0, @aws-sdk/credential-provider-node@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.637.0" +"@aws-sdk/credential-provider-node@npm:3.645.0, @aws-sdk/credential-provider-node@npm:^3.350.0": + version: 3.645.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.645.0" dependencies: "@aws-sdk/credential-provider-env": 3.620.1 "@aws-sdk/credential-provider-http": 3.635.0 - "@aws-sdk/credential-provider-ini": 3.637.0 + "@aws-sdk/credential-provider-ini": 3.645.0 "@aws-sdk/credential-provider-process": 3.620.1 - "@aws-sdk/credential-provider-sso": 3.637.0 + "@aws-sdk/credential-provider-sso": 3.645.0 "@aws-sdk/credential-provider-web-identity": 3.621.0 "@aws-sdk/types": 3.609.0 "@smithy/credential-provider-imds": ^3.2.0 @@ -1105,7 +1105,7 @@ __metadata: "@smithy/shared-ini-file-loader": ^3.1.4 "@smithy/types": ^3.3.0 tslib: ^2.6.2 - checksum: 53fe4d083bc25e1d0b34ad2ca30aded81f3417fb896fbcaa1ef7209b67fedd7a81f7e00a1c87b35f0e245a8d19629c3644e14d3106cd0ee4cbea8a7c6713f9e3 + checksum: f8ccaacbee4b7ed3a635bba85cbba764e7ea8c46e45d1e6cb2df3c8d3f97d4f093e65c501487aefd8c89ea5d32560ee4a9265947b8a3dba38f0374897a37d410 languageName: node linkType: hard @@ -1122,18 +1122,18 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.637.0" +"@aws-sdk/credential-provider-sso@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.645.0" dependencies: - "@aws-sdk/client-sso": 3.637.0 + "@aws-sdk/client-sso": 3.645.0 "@aws-sdk/token-providers": 3.614.0 "@aws-sdk/types": 3.609.0 "@smithy/property-provider": ^3.1.3 "@smithy/shared-ini-file-loader": ^3.1.4 "@smithy/types": ^3.3.0 tslib: ^2.6.2 - checksum: 7f8670ca253ee26e56da3011136ad8049f877a07c4d040b46ae1380ffc53502dbbacafe4f2dc84aa94b3d760abe634c36351c2a0114176a6a09fe8477317a5f8 + checksum: 26a119c106d58bc7cb6cfc273f5f8eca9f2399ec462556516947bd12e1502e6c98d29032d2e9e18b72bee57f0dc82f56c141ebe419e6f30b668097d4c44bef6e languageName: node linkType: hard @@ -1152,26 +1152,26 @@ __metadata: linkType: hard "@aws-sdk/credential-providers@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/credential-providers@npm:3.637.0" + version: 3.645.0 + resolution: "@aws-sdk/credential-providers@npm:3.645.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.637.0 - "@aws-sdk/client-sso": 3.637.0 - "@aws-sdk/client-sts": 3.637.0 - "@aws-sdk/credential-provider-cognito-identity": 3.637.0 + "@aws-sdk/client-cognito-identity": 3.645.0 + "@aws-sdk/client-sso": 3.645.0 + "@aws-sdk/client-sts": 3.645.0 + "@aws-sdk/credential-provider-cognito-identity": 3.645.0 "@aws-sdk/credential-provider-env": 3.620.1 "@aws-sdk/credential-provider-http": 3.635.0 - "@aws-sdk/credential-provider-ini": 3.637.0 - "@aws-sdk/credential-provider-node": 3.637.0 + "@aws-sdk/credential-provider-ini": 3.645.0 + "@aws-sdk/credential-provider-node": 3.645.0 "@aws-sdk/credential-provider-process": 3.620.1 - "@aws-sdk/credential-provider-sso": 3.637.0 + "@aws-sdk/credential-provider-sso": 3.645.0 "@aws-sdk/credential-provider-web-identity": 3.621.0 "@aws-sdk/types": 3.609.0 "@smithy/credential-provider-imds": ^3.2.0 "@smithy/property-provider": ^3.1.3 "@smithy/types": ^3.3.0 tslib: ^2.6.2 - checksum: a211dbf654b5159d8cf2d933f36be35c307ca8c2f26e82f8e7420465d30c79c1c3ed14a266839684118a302f4b468ef10b7c75076eb51923cbb6c250f0d70a58 + checksum: 5f48a6cad85f05f92feb1112aad9915b294ef18b0cb30c2dc201cd6172d265f0365656f644a327cd1d909ead9869c98e401fb943877e178fd9553ce24d484720 languageName: node linkType: hard @@ -1197,8 +1197,8 @@ __metadata: linkType: hard "@aws-sdk/lib-storage@npm:^3.350.0": - version: 3.637.0 - resolution: "@aws-sdk/lib-storage@npm:3.637.0" + version: 3.645.0 + resolution: "@aws-sdk/lib-storage@npm:3.645.0" dependencies: "@smithy/abort-controller": ^3.1.1 "@smithy/middleware-endpoint": ^3.1.0 @@ -1208,8 +1208,8 @@ __metadata: stream-browserify: 3.0.0 tslib: ^2.6.2 peerDependencies: - "@aws-sdk/client-s3": ^3.637.0 - checksum: dc90cfe6874b4ca23f3ebac2a11ef6b751abadaaa9c04777d13b096e8ef3bb764100d06cd97f48f803cacb2642725f36e1237d00cde6ffe02bd429658c944c6a + "@aws-sdk/client-s3": ^3.645.0 + checksum: 26badfa298a20044f9d78e01cd678e5d068a8e1ed8f80d9e5ce84192f8f966af795ca20c4e8b46385f17ffa11146878feef404e41eb712ac384d228011fbab3d languageName: node linkType: hard @@ -1372,16 +1372,16 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.637.0" +"@aws-sdk/middleware-user-agent@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.645.0" dependencies: "@aws-sdk/types": 3.609.0 - "@aws-sdk/util-endpoints": 3.637.0 + "@aws-sdk/util-endpoints": 3.645.0 "@smithy/protocol-http": ^4.1.0 "@smithy/types": ^3.3.0 tslib: ^2.6.2 - checksum: e6dfa9d45853d47fbdb60e2ee3989900dd587e7a1224392014a7cac2d07952d99c3da0c82f6099b6cc9e2b438960eda620e99e939c374b2524795f75f51ba777 + checksum: 7b0ba381c01625c0448370d5181fd322132e913d0844f6eab2fd846b394d3033a2f3f758c26ad4d8c6846cbcd0618f7ec9b9470941094d347c2c60b04f1cab66 languageName: node linkType: hard @@ -1538,15 +1538,15 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-endpoints@npm:3.637.0": - version: 3.637.0 - resolution: "@aws-sdk/util-endpoints@npm:3.637.0" +"@aws-sdk/util-endpoints@npm:3.645.0": + version: 3.645.0 + resolution: "@aws-sdk/util-endpoints@npm:3.645.0" dependencies: "@aws-sdk/types": 3.609.0 "@smithy/types": ^3.3.0 "@smithy/util-endpoints": ^2.0.5 tslib: ^2.6.2 - checksum: 55fe86f46f77b3d41ac5c15bbb58b7271175327dc59958ff16a58822ebe26c5ab0bc577dfaaddcbb76466702bee58cd7066eac10d6fa0b43357bddf2cd3dba2f + checksum: 3f2cda6f58d713c57f6add3073e32307e243a5554133b693ad3a83a1b1802d40a6a21778658f2001bf252567dd5d2089251e4745ee89b51b7896643bd93fbca8 languageName: node linkType: hard From e98af172c4665d5659224c6ae1335290da558a07 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:15:24 +0000 Subject: [PATCH 211/317] fix(deps): update dependency @swc/helpers to v0.5.13 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b1d5a5c7e3..3ff31477ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16912,11 +16912,11 @@ __metadata: linkType: hard "@swc/helpers@npm:^0.5.0, @swc/helpers@npm:^0.5.8": - version: 0.5.12 - resolution: "@swc/helpers@npm:0.5.12" + version: 0.5.13 + resolution: "@swc/helpers@npm:0.5.13" dependencies: tslib: ^2.4.0 - checksum: 293c0cb8f41804f94beb04a764bbcfaf316707ec43947713154ac66311590299446bf9b96ab253ce59ce9e435a0edc8cc2bf93f88dc3989f9241271507dd5fe9 + checksum: d50c2c10da6ef940af423c6b03ad9c3c94cf9de59314b1e921a7d1bcc081a6074481c9d67b655fc8fe66a73288f98b25950743792a63882bfb5793b362494fc0 languageName: node linkType: hard From 416d1312391cb121115c11ee4dce5674183fd8a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:16:33 +0000 Subject: [PATCH 212/317] fix(deps): update dependency @google-cloud/container to v5.17.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b1d5a5c7e3..2911e2a9ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9473,11 +9473,11 @@ __metadata: linkType: hard "@google-cloud/container@npm:^5.0.0": - version: 5.16.0 - resolution: "@google-cloud/container@npm:5.16.0" + version: 5.17.0 + resolution: "@google-cloud/container@npm:5.17.0" dependencies: google-gax: ^4.0.3 - checksum: 2f9410843b04d7780f9cb4a8dfcacec3aa05dcbd75dc05442fe734cddc19aa5b92122053d6df5f11ca959164eaa937f14d8140009a34b39da67ad38943c3d8e9 + checksum: 8e583f9cd1f12cf860dd9d483f4e0e8c1e4edf7191d0152c8b821c2debbe80b77eb663b45fa3526faf1d3182e5000df532628360bbae53420e090b92043c3880 languageName: node linkType: hard From bf7ca8753a135ed97620b194228ebae1bafaa84b Mon Sep 17 00:00:00 2001 From: Enoch <160036989+vavole@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:26:07 +0300 Subject: [PATCH 213/317] Update CONTRIBUTING.md Minor grammatical changes to CONTRIBUTING.md Signed-off-by: Enoch <160036989+vavole@users.noreply.github.com> --- CONTRIBUTING.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f10116844b..c1a0283f09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Our vision for Backstage is for it to become the trusted standard toolbox (read: UX layer) for the open source infrastructure landscape. Think of it like Kubernetes for developer experience. We realize this is an ambitious goal. We can’t do it alone. -Therefore we want to create a strong community of contributors -- all working together to create the kind of delightful experience that our developers deserve. +Therefore, we want to create a strong community of contributors -- all working together to create the kind of delightful experience that our developers deserve. Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. ❤️ @@ -41,7 +41,7 @@ We encourage you to catch any accessibility issues already in the development ph ## Get Started! -So...feel ready to jump in? Let's do this. 👏🏻💯 +So... feel ready to jump in? Let's do this. 👏🏻💯 ### Cloning the Repository @@ -103,7 +103,7 @@ If you want to get a better understanding of the layout of the repo now that you ## Coding Guidelines -All code is formatted with `prettier` using the configuration in the repo. If possible we recommend configuring your editor to format automatically, but you can also use the `yarn prettier --write ` command to format files. +All code is formatted with `prettier` using the configuration in the repo. If possible, we recommend configuring your editor to format automatically, but you can also use the `yarn prettier --write ` command to format files. A consistent coding style is included via [EditorConfig](https://editorconfig.org/) with the file [`.editorconfig`](.editorconfig) at the root of the repo. Depending on your editor of choice, it will either support it out of the box or you can [download a plugin](https://editorconfig.org/#download) for the config to be applied. @@ -184,7 +184,7 @@ Changesets **are** needed for new packages, as that is what triggers the package ### Writing changesets -Changesets are an important part of the development process. They are used to generate Changelog entries for all changes to the project. Ultimately they are read by the end users to learn about important changes and fixes to the project. Some of these fixes might require manual intervention from users so it's important to write changesets that users understand and can take action on. +Changesets are an important part of the development process. They are used to generate Changelog entries for all changes to the project. Ultimately, they are read by the end users to learn about important changes and fixes to the project. Some of these fixes might require manual intervention from users so it's important to write changesets that users understand and can take action on. Here are some important do's and don'ts when writing changesets: @@ -274,7 +274,7 @@ These changes are **required** to `packages/backend/src/plugins/catalog.ts` As with other CNCF projects, Backstage has adopted a [Developers Certificate of Origin (DCO)](https://developercertificate.org/). A DCO is a lightweight way for a developer to certify that they wrote or otherwise have the right to submit code or documentation to a project. -To certify the code you submit to the repository you'll need to add a `Signed-off-by` line to your commits. +To certify the code you submit to the repository, you'll need to add a `Signed-off-by` line to your commits. `$ git commit -s -m 'Awesome commit message'` @@ -330,9 +330,9 @@ There are two ways you can do this: 1. You can run `yarn build:api-reports` from the root of the project and it will go through all of the existing API Reports and update them or create new ones as needed. This may take a while but is generally the best method if you are new to this. 2. You can run `yarn build:api-reports plugins/` from the root and it will update the existing API Report or create a new one. -> Note: the above commands assume you've run `yarn install` before hand or recently +> Note: the above commands assume you've run `yarn install` beforehand or recently. -Each plugin/package has its own API Report which means you might see more then one file updated or created depending on your changes. These changes will then need to be committed as well. +Each plugin/package has its own API Report which means you might see more than one file updated or created depending on your changes. These changes will then need to be committed as well. ## Submitting a Pull Request @@ -374,7 +374,7 @@ Once you've submitted a Pull Request (PR) the various bots will come out and do - checking for commits for their DCO (Developer Certificate of Origin) - kick of the various CI builds -Once these have been completed it's just a matter of being patient as the reviewers have time they will begin to review your PR. When the review begins there may be a few layers to this but the general rule is that you need approval from one of the core maintainers and one from the specific area impacted by your PR. You may also have someone from the community review your changes, this can really help speed things up as they may catch some early items making the review for the maintainers simpler. Once you have the two (2) approvals it's ready to be merged, this task is also done by the maintainers. +Once these steps are completed, it's just a matter of being patient. As the reviewers have time, they will begin reviewing your PR. When the review process begins, there may be a few layers to this, but the general rule is that you need approval from one of the core maintainers and one from the specific area impacted by your PR. You may also have someone from the community review your changes. This can really help speed things up as they may catch some early items making the review for the maintainers simpler. Once you have the two (2) approvals, it's ready to be merged, a task that is also performed by the maintainers. ### Review Tips From da8c66b4f6cf7bb7ffd19ba68bc25cae4cdcbb92 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:36:17 +0000 Subject: [PATCH 214/317] fix(deps): update dependency @opensearch-project/opensearch to v2.12.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b6f571d299..372cfbe905 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12556,8 +12556,8 @@ __metadata: linkType: hard "@opensearch-project/opensearch@npm:^2.2.1": - version: 2.11.0 - resolution: "@opensearch-project/opensearch@npm:2.11.0" + version: 2.12.0 + resolution: "@opensearch-project/opensearch@npm:2.12.0" dependencies: aws4: ^1.11.0 debug: ^4.3.1 @@ -12565,7 +12565,7 @@ __metadata: json11: ^1.1.2 ms: ^2.1.3 secure-json-parse: ^2.4.0 - checksum: 328a900ca3e0cc49a7486aef029a9ae6bc7ca90606e7aabb3c64acdec8ef09ece1ecc3019dd98bd553110f6757236f43c8c9a2db4ef167b3fc7ba56579404784 + checksum: 5bf64305a401a1fe2e3a9d06c2582ba5173f809cbe04ccec7e70247070ccd973c9e6a9f09a00507d0a937cc830354642e1b6e5adcac383a53bbec05b240f857b languageName: node linkType: hard From 119851ed762ad8c9f923a5fc5d153f8ac7772846 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 11:37:26 +0000 Subject: [PATCH 215/317] fix(deps): update dependency @opentelemetry/auto-instrumentations-node to ^0.50.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/backend/package.json | 2 +- yarn.lock | 1029 ++++++++++++--------------------- 2 files changed, 382 insertions(+), 649 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 7f81417b31..d177e1c975 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -57,7 +57,7 @@ "@backstage/plugin-search-backend-node": "workspace:^", "@backstage/plugin-signals-backend": "workspace:^", "@backstage/plugin-techdocs-backend": "workspace:^", - "@opentelemetry/auto-instrumentations-node": "^0.49.0", + "@opentelemetry/auto-instrumentations-node": "^0.50.0", "@opentelemetry/exporter-prometheus": "^0.53.0", "@opentelemetry/sdk-node": "^0.53.0", "example-app": "link:../app" diff --git a/yarn.lock b/yarn.lock index b6f571d299..9c31e79072 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12569,16 +12569,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api-logs@npm:0.52.1, @opentelemetry/api-logs@npm:^0.52.0": - version: 0.52.1 - resolution: "@opentelemetry/api-logs@npm:0.52.1" - dependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 500cd35527580732921d198bd7007224402cb89fef791f0b64bea839c9f2ad796d54486ee9aee0ee6422ded3963cba793408086eda0adfec2bd1d66f9114d96c - languageName: node - linkType: hard - -"@opentelemetry/api-logs@npm:0.53.0": +"@opentelemetry/api-logs@npm:0.53.0, @opentelemetry/api-logs@npm:^0.53.0": version: 0.53.0 resolution: "@opentelemetry/api-logs@npm:0.53.0" dependencies: @@ -12594,69 +12585,60 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/auto-instrumentations-node@npm:^0.49.0": - version: 0.49.2 - resolution: "@opentelemetry/auto-instrumentations-node@npm:0.49.2" +"@opentelemetry/auto-instrumentations-node@npm:^0.50.0": + version: 0.50.0 + resolution: "@opentelemetry/auto-instrumentations-node@npm:0.50.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/instrumentation-amqplib": ^0.41.0 - "@opentelemetry/instrumentation-aws-lambda": ^0.43.0 - "@opentelemetry/instrumentation-aws-sdk": ^0.43.1 - "@opentelemetry/instrumentation-bunyan": ^0.40.0 - "@opentelemetry/instrumentation-cassandra-driver": ^0.40.0 - "@opentelemetry/instrumentation-connect": ^0.38.0 - "@opentelemetry/instrumentation-cucumber": ^0.8.0 - "@opentelemetry/instrumentation-dataloader": ^0.11.0 - "@opentelemetry/instrumentation-dns": ^0.38.0 - "@opentelemetry/instrumentation-express": ^0.41.1 - "@opentelemetry/instrumentation-fastify": ^0.38.0 - "@opentelemetry/instrumentation-fs": ^0.14.0 - "@opentelemetry/instrumentation-generic-pool": ^0.38.1 - "@opentelemetry/instrumentation-graphql": ^0.42.0 - "@opentelemetry/instrumentation-grpc": ^0.52.0 - "@opentelemetry/instrumentation-hapi": ^0.40.0 - "@opentelemetry/instrumentation-http": ^0.52.0 - "@opentelemetry/instrumentation-ioredis": ^0.42.0 - "@opentelemetry/instrumentation-kafkajs": ^0.2.0 - "@opentelemetry/instrumentation-knex": ^0.39.0 - "@opentelemetry/instrumentation-koa": ^0.42.0 - "@opentelemetry/instrumentation-lru-memoizer": ^0.39.0 - "@opentelemetry/instrumentation-memcached": ^0.38.0 - "@opentelemetry/instrumentation-mongodb": ^0.46.0 - "@opentelemetry/instrumentation-mongoose": ^0.41.0 - "@opentelemetry/instrumentation-mysql": ^0.40.0 - "@opentelemetry/instrumentation-mysql2": ^0.40.0 - "@opentelemetry/instrumentation-nestjs-core": ^0.39.0 - "@opentelemetry/instrumentation-net": ^0.38.0 - "@opentelemetry/instrumentation-pg": ^0.43.0 - "@opentelemetry/instrumentation-pino": ^0.41.0 - "@opentelemetry/instrumentation-redis": ^0.41.0 - "@opentelemetry/instrumentation-redis-4": ^0.41.1 - "@opentelemetry/instrumentation-restify": ^0.40.0 - "@opentelemetry/instrumentation-router": ^0.39.0 - "@opentelemetry/instrumentation-socket.io": ^0.41.0 - "@opentelemetry/instrumentation-tedious": ^0.13.0 - "@opentelemetry/instrumentation-undici": ^0.5.0 - "@opentelemetry/instrumentation-winston": ^0.39.0 - "@opentelemetry/resource-detector-alibaba-cloud": ^0.29.0 - "@opentelemetry/resource-detector-aws": ^1.6.0 - "@opentelemetry/resource-detector-azure": ^0.2.10 - "@opentelemetry/resource-detector-container": ^0.4.0 - "@opentelemetry/resource-detector-gcp": ^0.29.10 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/instrumentation-amqplib": ^0.42.0 + "@opentelemetry/instrumentation-aws-lambda": ^0.44.0 + "@opentelemetry/instrumentation-aws-sdk": ^0.44.0 + "@opentelemetry/instrumentation-bunyan": ^0.41.0 + "@opentelemetry/instrumentation-cassandra-driver": ^0.41.0 + "@opentelemetry/instrumentation-connect": ^0.39.0 + "@opentelemetry/instrumentation-cucumber": ^0.9.0 + "@opentelemetry/instrumentation-dataloader": ^0.12.0 + "@opentelemetry/instrumentation-dns": ^0.39.0 + "@opentelemetry/instrumentation-express": ^0.42.0 + "@opentelemetry/instrumentation-fastify": ^0.39.0 + "@opentelemetry/instrumentation-fs": ^0.15.0 + "@opentelemetry/instrumentation-generic-pool": ^0.39.0 + "@opentelemetry/instrumentation-graphql": ^0.43.0 + "@opentelemetry/instrumentation-grpc": ^0.53.0 + "@opentelemetry/instrumentation-hapi": ^0.41.0 + "@opentelemetry/instrumentation-http": ^0.53.0 + "@opentelemetry/instrumentation-ioredis": ^0.43.0 + "@opentelemetry/instrumentation-kafkajs": ^0.3.0 + "@opentelemetry/instrumentation-knex": ^0.40.0 + "@opentelemetry/instrumentation-koa": ^0.43.0 + "@opentelemetry/instrumentation-lru-memoizer": ^0.40.0 + "@opentelemetry/instrumentation-memcached": ^0.39.0 + "@opentelemetry/instrumentation-mongodb": ^0.47.0 + "@opentelemetry/instrumentation-mongoose": ^0.42.0 + "@opentelemetry/instrumentation-mysql": ^0.41.0 + "@opentelemetry/instrumentation-mysql2": ^0.41.0 + "@opentelemetry/instrumentation-nestjs-core": ^0.40.0 + "@opentelemetry/instrumentation-net": ^0.39.0 + "@opentelemetry/instrumentation-pg": ^0.44.0 + "@opentelemetry/instrumentation-pino": ^0.42.0 + "@opentelemetry/instrumentation-redis": ^0.42.0 + "@opentelemetry/instrumentation-redis-4": ^0.42.0 + "@opentelemetry/instrumentation-restify": ^0.41.0 + "@opentelemetry/instrumentation-router": ^0.40.0 + "@opentelemetry/instrumentation-socket.io": ^0.42.0 + "@opentelemetry/instrumentation-tedious": ^0.14.0 + "@opentelemetry/instrumentation-undici": ^0.6.0 + "@opentelemetry/instrumentation-winston": ^0.40.0 + "@opentelemetry/resource-detector-alibaba-cloud": ^0.29.1 + "@opentelemetry/resource-detector-aws": ^1.6.1 + "@opentelemetry/resource-detector-azure": ^0.2.11 + "@opentelemetry/resource-detector-container": ^0.4.1 + "@opentelemetry/resource-detector-gcp": ^0.29.11 "@opentelemetry/resources": ^1.24.0 - "@opentelemetry/sdk-node": ^0.52.0 + "@opentelemetry/sdk-node": ^0.53.0 peerDependencies: "@opentelemetry/api": ^1.4.1 - checksum: 383a7cac4994a48e5ca918fc84dddeb25f35c1744e1e6da186cd85b58e5b248c69d3ec92719828401877aec05913c94c51d987b5ec46d7965c9b99a1cd31af80 - languageName: node - linkType: hard - -"@opentelemetry/context-async-hooks@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/context-async-hooks@npm:1.25.1" - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: fb2ac7381ea8203a1321e2a4989c193b6eede0b0f46bafc150e452ac5fc4645127f0ca66f60e44ff2816032afc68cbe3ab9cf235fbdffb0ad83f484729b70e82 + checksum: 0d78211226f86a33f2fc55dc53e902726f89b70eece49db55f3c79aa37a643269989c672733739d9ad83f32d26c009207b302174f64b673567c80f5c8f68ac19 languageName: node linkType: hard @@ -12680,17 +12662,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/core@npm:1.25.1" - dependencies: - "@opentelemetry/semantic-conventions": 1.25.1 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: ba1672fde4a1cfd9b55bf6070db71b808702fe59c4a70cda52a6156b2c813827954a6b4d3c3641283d394ff75a69b6359a0487459b4d26cd7d714ab3d21bc780 - languageName: node - linkType: hard - "@opentelemetry/core@npm:1.26.0, @opentelemetry/core@npm:^1.0.0, @opentelemetry/core@npm:^1.1.0, @opentelemetry/core@npm:^1.25.0, @opentelemetry/core@npm:^1.25.1, @opentelemetry/core@npm:^1.8.0": version: 1.26.0 resolution: "@opentelemetry/core@npm:1.26.0" @@ -12762,22 +12733,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-grpc@npm:0.52.1": - version: 0.52.1 - resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.52.1" - dependencies: - "@grpc/grpc-js": ^1.7.1 - "@opentelemetry/core": 1.25.1 - "@opentelemetry/otlp-grpc-exporter-base": 0.52.1 - "@opentelemetry/otlp-transformer": 0.52.1 - "@opentelemetry/resources": 1.25.1 - "@opentelemetry/sdk-trace-base": 1.25.1 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: c8f21e7a5802a7089c1f9f01a1f21934da7adf8526f36a16da6ba4f78e1a59e3da7c301070cd6df78d07555babb1853ab23180ea7df02eefea4ece66fccdf3d1 - languageName: node - linkType: hard - "@opentelemetry/exporter-trace-otlp-grpc@npm:0.53.0": version: 0.53.0 resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.53.0" @@ -12794,21 +12749,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-http@npm:0.52.1": - version: 0.52.1 - resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.52.1" - dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/otlp-exporter-base": 0.52.1 - "@opentelemetry/otlp-transformer": 0.52.1 - "@opentelemetry/resources": 1.25.1 - "@opentelemetry/sdk-trace-base": 1.25.1 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: fa72c84c35268f500b8fdc4d1b388fbf7275170aa67a2b2712c1e6b0d657e208a1e1d222081f0a1860c3f204f12ff78c2f6fe622d08ea8123410f276883659e2 - languageName: node - linkType: hard - "@opentelemetry/exporter-trace-otlp-http@npm:0.53.0": version: 0.53.0 resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.53.0" @@ -12824,21 +12764,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-proto@npm:0.52.1": - version: 0.52.1 - resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.52.1" - dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/otlp-exporter-base": 0.52.1 - "@opentelemetry/otlp-transformer": 0.52.1 - "@opentelemetry/resources": 1.25.1 - "@opentelemetry/sdk-trace-base": 1.25.1 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 1d46eb5055925fb1f4383bb65ccec7addcf53ab88f2f5e7393e4e99e72d4fe48a1d45027736d534aeb76e37c217f5a4f9deda51351afa614340196a28db55dab - languageName: node - linkType: hard - "@opentelemetry/exporter-trace-otlp-proto@npm:0.53.0": version: 0.53.0 resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.53.0" @@ -12854,20 +12779,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-zipkin@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/exporter-zipkin@npm:1.25.1" - dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/resources": 1.25.1 - "@opentelemetry/sdk-trace-base": 1.25.1 - "@opentelemetry/semantic-conventions": 1.25.1 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 3ceade67522724642115e008e355b149b606088345c703b39ae4d7a2db3e4d883e9aa22181b49d50444bf0847ff7e0112b3ec4e5feb6acb1411038b3a3e81222 - languageName: node - linkType: hard - "@opentelemetry/exporter-zipkin@npm:1.26.0": version: 1.26.0 resolution: "@opentelemetry/exporter-zipkin@npm:1.26.0" @@ -12882,516 +12793,500 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation-amqplib@npm:^0.41.0": - version: 0.41.0 - resolution: "@opentelemetry/instrumentation-amqplib@npm:0.41.0" +"@opentelemetry/instrumentation-amqplib@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-amqplib@npm:0.42.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: aebf6727120935dd9f05e9d71dd6b2376ee8cdb56e2c8dd7c0fc6820ec1eb998cfa3e5bbb50201dc4e1c1257df293e08b8dc009888f7c55e651f0dfe33a16ede + checksum: 64c5a07cca54f2673aa9708638ba8a3dce864d69686924acb926a08f981941b080d68dcfc465b3139dc6824f239f1be9fc49257557c9bfcb460cb2c1f3b4dc80 languageName: node linkType: hard -"@opentelemetry/instrumentation-aws-lambda@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-aws-lambda@npm:0.43.0" +"@opentelemetry/instrumentation-aws-lambda@npm:^0.44.0": + version: 0.44.0 + resolution: "@opentelemetry/instrumentation-aws-lambda@npm:0.44.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 "@opentelemetry/propagator-aws-xray": ^1.3.1 "@opentelemetry/resources": ^1.8.0 - "@opentelemetry/semantic-conventions": ^1.22.0 - "@types/aws-lambda": 8.10.122 + "@opentelemetry/semantic-conventions": ^1.27.0 + "@types/aws-lambda": 8.10.143 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: f2e7c8ba4a1f5a99f47071d879ac7a9180434a00c7e8db776a803e2a12e90299ad8d1ad3062626f5e8c58dd02232adc07b4a0736a26175b85a951efab5064def + checksum: 675e002c5d77cb5720d4eb53fd1a2b738bfe72de9d25140c4b6fd4a8353d03ddf29ff1bbdb72b23d26f79e92c9212731f883071dcc66b66666f29dc83f0b5954 languageName: node linkType: hard -"@opentelemetry/instrumentation-aws-sdk@npm:^0.43.1": - version: 0.43.1 - resolution: "@opentelemetry/instrumentation-aws-sdk@npm:0.43.1" +"@opentelemetry/instrumentation-aws-sdk@npm:^0.44.0": + version: 0.44.0 + resolution: "@opentelemetry/instrumentation-aws-sdk@npm:0.44.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/propagation-utils": ^0.30.10 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/propagation-utils": ^0.30.11 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: b3c5a3893123cfff6f820be65e515d3d42cb7895a98d5c50b8d072e182dc8b8f475936df78a4180d4fa39b5d8e6e7a84b52952842bf14e7708f2c2d5e415492b + checksum: 7ab0fe1c9a93866a2bc01027d794cf5f1c59973490890982d9db82b50d555b4f896537e768df62374e365d4322e4afd4d27941910d124c6f48ac63a5212484f1 languageName: node linkType: hard -"@opentelemetry/instrumentation-bunyan@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-bunyan@npm:0.40.0" +"@opentelemetry/instrumentation-bunyan@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-bunyan@npm:0.41.0" dependencies: - "@opentelemetry/api-logs": ^0.52.0 - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/api-logs": ^0.53.0 + "@opentelemetry/instrumentation": ^0.53.0 "@types/bunyan": 1.8.9 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 67c76e0c39305f069b6564f4fe8315a2b91cdedadea66e2506a9cbeaf04de2c6d86fce75fcf982e556be0fa9e4f84baf2c59b02a5870590ecbf3838d41f8b1d5 + checksum: f53f0fd1f285e6f7bd7d5a1ccb5747ea0c928c83f855dd4853b8d37b8b1e163d060a592957a89d1891e740b5dab800d0cda7d5b9db29206adb7938679e6f05e1 languageName: node linkType: hard -"@opentelemetry/instrumentation-cassandra-driver@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-cassandra-driver@npm:0.40.0" +"@opentelemetry/instrumentation-cassandra-driver@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-cassandra-driver@npm:0.41.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 6979a3c0c28226ace0071880d05cf0a418c119b3cf8fcea2e0398288dc7d541ac98935d1a6f9a77e80e8ea9f186e5044b9f791cd5668e3ed33406db6c0a9895f + checksum: e9c620e241938f16e667746c741480c809b959b44e8affe1b27e90c412ab898da2631307eae3fdee1f8fb928f50b88bc7c74275592beefc3fffc6c5eef83a184 languageName: node linkType: hard -"@opentelemetry/instrumentation-connect@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-connect@npm:0.38.0" +"@opentelemetry/instrumentation-connect@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-connect@npm:0.39.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 "@types/connect": 3.4.36 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 4dff447ff9a7ee2ca94872d904df260213e8e05b27742713f8dce35593ec8f52bcb07adfb7730e7b208b8b49566e032cdee316f0ccea405e42ffc5804d222750 + checksum: 946c3d7ea0ca569942fd1e45a1f149f94fed4ee86609b4f2aedb23d67feeafe29a563beffa75d97f9b10c6b18b01c42d5eabf92d4f9f160bc938317c88ebc0de languageName: node linkType: hard -"@opentelemetry/instrumentation-cucumber@npm:^0.8.0": - version: 0.8.0 - resolution: "@opentelemetry/instrumentation-cucumber@npm:0.8.0" +"@opentelemetry/instrumentation-cucumber@npm:^0.9.0": + version: 0.9.0 + resolution: "@opentelemetry/instrumentation-cucumber@npm:0.9.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 951b9a1d60dfb33a3b4bdcd381e83fd89c2e820f49f86ff1317bcd1127345ec945702760cbbfdc06af75b67e17d85ab9a5f859d90f45d244e17503d0d1e3363d + checksum: 31d702a2e3404298a8410362c7834efb840ec10de56144811803207ba9e0a12af9bcac483da96a5df32143597dc0b74c32ba697b6525b3db3601edb97d192e84 languageName: node linkType: hard -"@opentelemetry/instrumentation-dataloader@npm:^0.11.0": - version: 0.11.0 - resolution: "@opentelemetry/instrumentation-dataloader@npm:0.11.0" +"@opentelemetry/instrumentation-dataloader@npm:^0.12.0": + version: 0.12.0 + resolution: "@opentelemetry/instrumentation-dataloader@npm:0.12.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 9aa330d08cd6219ee927ab72db3d2d4d080831251bf5dde49eb6c7a1852ac7ea34b8df88d56c19e2638333b58527a90e7c459c9a1529008b74bd489385119ce1 + checksum: 7d502bd4ca6eac448bebc971d7b7b85b1e1e959bfcbf4b7927ea3fe90784aec8132d147fef2e773e468a8d20241df825edec6e0753be824b155a15d89e6f57a1 languageName: node linkType: hard -"@opentelemetry/instrumentation-dns@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-dns@npm:0.38.0" +"@opentelemetry/instrumentation-dns@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-dns@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 semver: ^7.5.4 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: e2105d6c8211525573657052dc1d64859046ab259de402b39ee901e8bcab2eafd16f6590e667f86e59db105aea7dab2a4130e2fba9c60e4588fa80cd52427360 + checksum: 691358e32532c8a830a4dc4133ae90f80bd659b4acc2db308353d3027663a4223de9e0ae7ef8c24be895b20be5995cb78f1bb86e57b32b8397b43339f58b5555 languageName: node linkType: hard -"@opentelemetry/instrumentation-express@npm:^0.41.1": - version: 0.41.1 - resolution: "@opentelemetry/instrumentation-express@npm:0.41.1" - dependencies: - "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: fe2939ab377cc4b1c6dfe77cae6d23156ccd227cc9984bf03eb5f108fa048815d6dd0be9a8240bb3bd053a271789c9928a9e544725e2e099709e9b2c60d456f0 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-fastify@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-fastify@npm:0.38.0" - dependencies: - "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 3cd7996398d783397782fc327a336e37b1eaabee6c9d8a25daa15d86cf6634408bc790e67207548dcae61f064a4a85780d9ae51bc31414bd61e0291d22a8d3f8 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-fs@npm:^0.14.0": - version: 0.14.0 - resolution: "@opentelemetry/instrumentation-fs@npm:0.14.0" - dependencies: - "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: cc6b90e9496bddbbc992adf3cb585b728260e6b22d35e48720e06505e2477e962bd1b62d9d1bf331c877c04ce7b393d87d2f61a3ade08519b73252a342351bf0 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-generic-pool@npm:^0.38.1": - version: 0.38.1 - resolution: "@opentelemetry/instrumentation-generic-pool@npm:0.38.1" - dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 19183c1d160d4c54d684305f6638163b5d2b84112b69e8281cd507e5ec5fa505431bc9a435f6c537bdea66a2820b4a231ed64c19da847d76e795036fcbfbd393 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-graphql@npm:^0.42.0": +"@opentelemetry/instrumentation-express@npm:^0.42.0": version: 0.42.0 - resolution: "@opentelemetry/instrumentation-graphql@npm:0.42.0" - dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: cae523cf75312457dc176a1ae2e4157b41025c5d01d8174467c8a62f8031c3a90151888fcdd804778183f58618ec320a24fe78f5bb0a1a72a119dc37245be676 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-grpc@npm:^0.52.0": - version: 0.52.1 - resolution: "@opentelemetry/instrumentation-grpc@npm:0.52.1" - dependencies: - "@opentelemetry/instrumentation": 0.52.1 - "@opentelemetry/semantic-conventions": 1.25.1 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: aa14515e5cb8b922f3414540fdaf986bb68f283ab05c57d5f6dad6f9a1f44c6416e5d8e4f6d2467829fb3f4afaf38c5d1c107574481297052477a34e28892cd1 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-hapi@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-hapi@npm:0.40.0" + resolution: "@opentelemetry/instrumentation-express@npm:0.42.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 71ff845ca2376b5f1cf7514d649f829044dec6486e784351a26278afc092a1cd90eb0eaf34ab79afcae9244e8b126d6059ac0c7c286743ceb8e12126cf7e0255 + checksum: c4606bd593ee841eb891927f404609b05e1375b28f9e5cd179381b744d826e3a6d07d069835d02b06e2cd8de6861c0ecebaa2d3a180181733e9fdc6b80444837 languageName: node linkType: hard -"@opentelemetry/instrumentation-http@npm:^0.52.0": - version: 0.52.1 - resolution: "@opentelemetry/instrumentation-http@npm:0.52.1" +"@opentelemetry/instrumentation-fastify@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-fastify@npm:0.39.0" dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/instrumentation": 0.52.1 - "@opentelemetry/semantic-conventions": 1.25.1 + "@opentelemetry/core": ^1.8.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: a64ffd35f01e6ae08a7de7092ef34ec186c284ebada7f8e4d87420440bbc5a00b568624e7c1dd35b00550d68ccb24ae905ad8488adb7eaf14344f334d436244e + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-fs@npm:^0.15.0": + version: 0.15.0 + resolution: "@opentelemetry/instrumentation-fs@npm:0.15.0" + dependencies: + "@opentelemetry/core": ^1.8.0 + "@opentelemetry/instrumentation": ^0.53.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: d1f50b541629e9155dcbfbdd26d5fa43d6569f835abf88ea35261e0c03c6afb89b26cd2056dda6e9c4de72a49d7e2ca0336e1dc75604256db09e0e09d2182466 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-generic-pool@npm:^0.39.0": + version: 0.39.0 + resolution: "@opentelemetry/instrumentation-generic-pool@npm:0.39.0" + dependencies: + "@opentelemetry/instrumentation": ^0.53.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 2d094b217afe9c1d388f06cdaca07f9801b70f0d01ee3db374f1b4f07cd18efba55e90679f0237c1f2a7db22f9ee0e564e8583161bb7d578ecea779b874e967e + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-graphql@npm:^0.43.0": + version: 0.43.0 + resolution: "@opentelemetry/instrumentation-graphql@npm:0.43.0" + dependencies: + "@opentelemetry/instrumentation": ^0.53.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 4164f3dad9c44f3d9684750d879ba79a51949fe69179d8437f5736762cdd0156c63a8914c7accd767b090a18db45f011a9cff5e238d3240077f1c095d3c5dd28 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-grpc@npm:^0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/instrumentation-grpc@npm:0.53.0" + dependencies: + "@opentelemetry/instrumentation": 0.53.0 + "@opentelemetry/semantic-conventions": 1.27.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 937e96ec7fe655d3599ba71ffe48d79ff068e3ce820600249ed2807d2e7b2769e0771b723e5961bcd11ae8585df26f596a39e320497a9d3299d816510bb61eca + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-hapi@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-hapi@npm:0.41.0" + dependencies: + "@opentelemetry/core": ^1.8.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 2195c8366d9b81a40dd6d8db041c13687932ab7ec761c9dfea13162c4000638c699dfebf384e22f985aea2d7692c742ed8090cb5bad056e5137d2db79a615380 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-http@npm:^0.53.0": + version: 0.53.0 + resolution: "@opentelemetry/instrumentation-http@npm:0.53.0" + dependencies: + "@opentelemetry/core": 1.26.0 + "@opentelemetry/instrumentation": 0.53.0 + "@opentelemetry/semantic-conventions": 1.27.0 semver: ^7.5.2 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 67a31e14d9ee4da745b7529777c1525f717b2ddf09f2a9247acaa9677f32e4a863d8f0712c95917fdbe74d8211cf650065bb3a95ef2fdd9f81630e063cbc2d4f + checksum: 4ee569f7fc8c7ce50fabaff016d33577f36e63272b0634ac45806d70bffdf38fcf09db3cd9dd27c3150f6c4547fec673c356c419a6ed2399ff2849b9487a6e89 languageName: node linkType: hard -"@opentelemetry/instrumentation-ioredis@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-ioredis@npm:0.42.0" +"@opentelemetry/instrumentation-ioredis@npm:^0.43.0": + version: 0.43.0 + resolution: "@opentelemetry/instrumentation-ioredis@npm:0.43.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 "@opentelemetry/redis-common": ^0.36.2 - "@opentelemetry/semantic-conventions": ^1.23.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: ae4804732b4380007b09ba11051aae927da0c53cd74d556916a838fb4de4f3c2aa7156bc05c4f34e6669411835604a4587822b2b585c51042e502d0e5079c143 + checksum: 16ae17687db3b4bbe78d92667ce86f3afb212adb5eda8f17b549a06ff9e1ba3e0f146ded9e943cb781780b64853a6b7fa3b85d2c2f5e4f42fd76d651c90aa71f languageName: node linkType: hard -"@opentelemetry/instrumentation-kafkajs@npm:^0.2.0": - version: 0.2.0 - resolution: "@opentelemetry/instrumentation-kafkajs@npm:0.2.0" +"@opentelemetry/instrumentation-kafkajs@npm:^0.3.0": + version: 0.3.0 + resolution: "@opentelemetry/instrumentation-kafkajs@npm:0.3.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.24.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: f5790d426d87d35159954eed4368b52818f9faf5a999ade66fffc1662c12234caa79a04493266ea2995205e94051b173a831012e3a57c5a9a2417bd07a0e1378 + checksum: 9ea20f4f70edd7d47b551f56f56b6b0462e5f9dff3981462d9d061084a3bb345f1989ddc46a2d8fabebf2a9650c52ab6c6fb906a03e9176191cc71073c2bf22c languageName: node linkType: hard -"@opentelemetry/instrumentation-knex@npm:^0.39.0": - version: 0.39.0 - resolution: "@opentelemetry/instrumentation-knex@npm:0.39.0" +"@opentelemetry/instrumentation-knex@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-knex@npm:0.40.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: fd44998d57fd6403fcb90fb465cdf071da3d8cbdd161470277ffcb7a7553b04222ceb8d95285391cdb6d5abdd09d90c6fe5d6740a2248c004f163386f10fc235 + checksum: ca7f08b4a783ebb470663fa53a5418074cc04a42ad7b6f19939be04aff1c0db2070ca8b7ada17e363a412b1013149bde38bf1930f05c09cd6207a90711db0372 languageName: node linkType: hard -"@opentelemetry/instrumentation-koa@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-koa@npm:0.42.0" +"@opentelemetry/instrumentation-koa@npm:^0.43.0": + version: 0.43.0 + resolution: "@opentelemetry/instrumentation-koa@npm:0.43.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 4e41f4b1c2abb4d31151ae3b5999af3247affa99fadcf87ff542a8523f108275d83a54057d79453ff56bc40938a622803562db87c826bfac9f99fb409207c6fd + checksum: 7b30d11f8cbabc1570eeca771f421c65f014d87b850cf17a8f2aec98dc84a8a43ce6fe15634f5d16944466f8da1011508367498e215b3f6378e8f8d618a12ec9 languageName: node linkType: hard -"@opentelemetry/instrumentation-lru-memoizer@npm:^0.39.0": +"@opentelemetry/instrumentation-lru-memoizer@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-lru-memoizer@npm:0.40.0" + dependencies: + "@opentelemetry/instrumentation": ^0.53.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 1a65e7c397707f9d09fe11f9fbd127d5094406ec98f84e74309c1d9869da23173c3895c851bccf9c5c1b8aeef6fc516340311f87d154832c74a344a0ed4ac9ef + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-memcached@npm:^0.39.0": version: 0.39.0 - resolution: "@opentelemetry/instrumentation-lru-memoizer@npm:0.39.0" + resolution: "@opentelemetry/instrumentation-memcached@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 4a9b973bbf4fe77b11fafb4215bd9985f6a3c7ce487af965d98e5502e91a3fb78fc70a3f2238ef46a9c065e1555dfc08c22c39f0d1eb3925cd88c3201c8a79e5 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-memcached@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-memcached@npm:0.38.0" - dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.23.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 "@types/memcached": ^2.2.6 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 05e8e25c809461e8aedaf3ba37eec1bbed6cec1307a28a93a986ea1828b791dc2488cd2f83b0fec678c402b7e703fc7ab884274036d0b5df48c8e70b9376648b + checksum: bbf976f90463fe1b8a64d6e20057fc1926342a1f644e4cfd160ce1b0a032269b48b754e6cd86eff41faf92bd2e3617f1572a1ef272cb5059ed21aaf7c6591ccb languageName: node linkType: hard -"@opentelemetry/instrumentation-mongodb@npm:^0.46.0": - version: 0.46.0 - resolution: "@opentelemetry/instrumentation-mongodb@npm:0.46.0" +"@opentelemetry/instrumentation-mongodb@npm:^0.47.0": + version: 0.47.0 + resolution: "@opentelemetry/instrumentation-mongodb@npm:0.47.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 "@opentelemetry/sdk-metrics": ^1.9.1 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: fab5db536e2e95177a0ae0975bd8b9fc76f2d59bf89a6023547ba39071b97789859e1ac0582f86498a263611403a70b5f543b025207180770c07e178b04af5ad + checksum: 1d7c87532b5857e8d6d72272ce874f02a4b2adc8a135d07c48e432cc990a4493c110b15c842c72dfc79e33ddc63f518185b69e242e2c55521a06fe6503cb1980 languageName: node linkType: hard -"@opentelemetry/instrumentation-mongoose@npm:^0.41.0": - version: 0.41.0 - resolution: "@opentelemetry/instrumentation-mongoose@npm:0.41.0" +"@opentelemetry/instrumentation-mongoose@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-mongoose@npm:0.42.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 0961538ef1a3d9798e1f70baf206f22318b9a463b2ee50b0270faca434ef974a2bac2d872bebe698cbace17594a27dee43893e11d5ede3704055463fdd781271 + checksum: 77d36437073ee589ccd73f4a10a4602afd0f4a3a89a9c572be18b2df75941f5a6885d4823094d8ffe8859801d827d0b46f93dee03f39286d89eead373edf76bb languageName: node linkType: hard -"@opentelemetry/instrumentation-mysql2@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-mysql2@npm:0.40.0" +"@opentelemetry/instrumentation-mysql2@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-mysql2@npm:0.41.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 "@opentelemetry/sql-common": ^0.40.1 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: acdb9883d1ab92d0bfbe507736888cc0b0d7b25c3d24c27b51add17abceff4bf15988a3cd2e54b93256f28bbc65aa172f116df9277545143e20e8d7b497e2e63 + checksum: 37c8a57fb6591ae1db353e149ad5f38ac68c5f727e313ba6d1cc403d910ae8e3b743c3234500272012ea6eecb5b838803ee493ce5096ad40bb285b09090f4a65 languageName: node linkType: hard -"@opentelemetry/instrumentation-mysql@npm:^0.40.0": +"@opentelemetry/instrumentation-mysql@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/instrumentation-mysql@npm:0.41.0" + dependencies: + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 + "@types/mysql": 2.15.26 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: c8c23a1273e900902867ca4da8c026581d3f584e420a5f26ec738ace20c1a85695a411721541e63e0639d9ddbb8c3713a2bc25621886f81912014882395d3531 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-nestjs-core@npm:^0.40.0": version: 0.40.0 - resolution: "@opentelemetry/instrumentation-mysql@npm:0.40.0" + resolution: "@opentelemetry/instrumentation-nestjs-core@npm:0.40.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 - "@types/mysql": 2.15.22 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 6eeba06f7507ab9086ef18b9e37802f12ac0a9f6182d4aba3aaf71a3e0f0759cb51c044247653eb8e359383d922a12a0fc0f129b2982bae17910462450cdb189 + checksum: ce6b2d9186d95e5fa5c65efd902254c724e296172787d1fa9382a6204ae9d089e861ce8831ba6e6865d449a96a8aada7b1049568217f1bda21af3f36e01b9512 languageName: node linkType: hard -"@opentelemetry/instrumentation-nestjs-core@npm:^0.39.0": +"@opentelemetry/instrumentation-net@npm:^0.39.0": version: 0.39.0 - resolution: "@opentelemetry/instrumentation-nestjs-core@npm:0.39.0" + resolution: "@opentelemetry/instrumentation-net@npm:0.39.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.23.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 4394754ce3ef4747e39a59a602c744112f9d6d3ee7ef43a51f1562a977a8e2bb2944687d3dd199f0a5941517e71a84ff9ea9ce183d94b63a3311e9de6ac8e09c + checksum: ae3c7cdb4894a9aae7ea7a4d6a00cbef4afc2194539226f01f68ee78d25fb2d5debfb738b8a79a9842c4987bc4c673c7e6386d328c1e6bfc09e2613bf4677ed3 languageName: node linkType: hard -"@opentelemetry/instrumentation-net@npm:^0.38.0": - version: 0.38.0 - resolution: "@opentelemetry/instrumentation-net@npm:0.38.0" +"@opentelemetry/instrumentation-pg@npm:^0.44.0": + version: 0.44.0 + resolution: "@opentelemetry/instrumentation-pg@npm:0.44.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.23.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 13b4e12272ec3c7c2930a4dfd5eabf843d9e008a651c6a5f778236676526a76c563af7d313bf0edb390377d62112c07382b176f64beeffd534ed4f2c04923ccb - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-pg@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-pg@npm:0.43.0" - dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 "@opentelemetry/sql-common": ^0.40.1 "@types/pg": 8.6.1 - "@types/pg-pool": 2.0.4 + "@types/pg-pool": 2.0.6 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: d255806b9e5ffd449b7ab3c7c072e919ad8a18118cdf5aec636d89a89d9ac63ef282f280d3b2dd7fa9c57e748fc0c9b38294911bf1aaff35e821079aaeacbd82 + checksum: 13cf8bacee1139b551622cb76670c627e87b37ee5ad2b6c63417d985812c7db53ce7aae23bd04156cb5c0d1a214bb8c65890fde43fbd99aac540c625216703b7 languageName: node linkType: hard -"@opentelemetry/instrumentation-pino@npm:^0.41.0": - version: 0.41.0 - resolution: "@opentelemetry/instrumentation-pino@npm:0.41.0" +"@opentelemetry/instrumentation-pino@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-pino@npm:0.42.0" dependencies: - "@opentelemetry/api-logs": ^0.52.0 + "@opentelemetry/api-logs": ^0.53.0 "@opentelemetry/core": ^1.25.0 - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 1eb2a67513952e3cd7db0ea5fac39a6dffdf28f8ce514af7f39d70263780d1495773e6554bf386e6935c2f0145ca4a73cb4829f6ee91c630bbc85d5ccd164ce0 + checksum: 005146e233c53ffbf703c97d855a523fe91d9d2632b77fd0b094caec6f3646908fd03c22bea77575d4a75eac0a2aa2065c7a2d9caac45c370e9592abe9b231c1 languageName: node linkType: hard -"@opentelemetry/instrumentation-redis-4@npm:^0.41.1": - version: 0.41.1 - resolution: "@opentelemetry/instrumentation-redis-4@npm:0.41.1" +"@opentelemetry/instrumentation-redis-4@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-redis-4@npm:0.42.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 "@opentelemetry/redis-common": ^0.36.2 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: d67c542e9ea30bf7dcfb651eba033636d8ee379a135455c314688327c6bcb6512e7f41b7e1e97cbd62475f4afda2582f9be5ac0fd87cf2e008cdfa337a5b667d + checksum: b6d92ad302c31add32b1cdfe6f6ea836367a777737beb187b178bfb65047f84263b38855cda56746b793582eb6012942c03ed91f6e8e6710058bf3eb427fba2d languageName: node linkType: hard -"@opentelemetry/instrumentation-redis@npm:^0.41.0": +"@opentelemetry/instrumentation-redis@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-redis@npm:0.42.0" + dependencies: + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/redis-common": ^0.36.2 + "@opentelemetry/semantic-conventions": ^1.27.0 + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 1a84b516599a290de78eaf1a1db897bf2e866baaf4a77a76880b24261786f6ffa2673fcec36f360abf73d5e52104557ff957964b7b3683962b0bde2bbfbb074e + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-restify@npm:^0.41.0": version: 0.41.0 - resolution: "@opentelemetry/instrumentation-redis@npm:0.41.0" - dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/redis-common": ^0.36.2 - "@opentelemetry/semantic-conventions": ^1.22.0 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: a1caf71394ece352da20159e007d938fb22cea6c0c266addfa5e59dcf639c7d16404531edb1a7c94a3464db275927f8886f13e67eacedc3e94d97ff4b55c23aa - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-restify@npm:^0.40.0": - version: 0.40.0 - resolution: "@opentelemetry/instrumentation-restify@npm:0.40.0" + resolution: "@opentelemetry/instrumentation-restify@npm:0.41.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 813b755bd4510d8eef5322e1c9d0aa93407eea62a9bf4f3a82fa2ba38ecfa7e9fe287299eeef84cd4a2912ae9aceecb6392b40fb763d4329f953cb86a69eae61 + checksum: 28aac1e5bf2cdca2fa18c8423db3edb530bc338bb05a30537c24575bac12f4588d975598b020f58817393cf68354b913ca07e3720b3ff8497278887baee7b10b languageName: node linkType: hard -"@opentelemetry/instrumentation-router@npm:^0.39.0": - version: 0.39.0 - resolution: "@opentelemetry/instrumentation-router@npm:0.39.0" +"@opentelemetry/instrumentation-router@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-router@npm:0.40.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 802d4179caab4a0ae40745f200809efe6ed1a0e837483dacddb8dcecfe808f7ca42ddac9358715c8949bd59e91da2e7ef1e2afbd0203e22a48af1620f9437c10 + checksum: 7f8fb67d755cb8fa94dbe44f698d22e1ce80971071f574aa9a8b177410cf6a8ab37abd449777670705b73aa6a4272212e80d6dc3c6ec0aa7f99ce161238c7f33 languageName: node linkType: hard -"@opentelemetry/instrumentation-socket.io@npm:^0.41.0": - version: 0.41.0 - resolution: "@opentelemetry/instrumentation-socket.io@npm:0.41.0" +"@opentelemetry/instrumentation-socket.io@npm:^0.42.0": + version: 0.42.0 + resolution: "@opentelemetry/instrumentation-socket.io@npm:0.42.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: a50e66dabc1670906dba51bbcbabcdfdb865157b38e7086ce07275b8d3bde3263fa527a1b054ce55d8bbf8bba3ad6aba64319019015eb85299d46f1e3334f8e0 + checksum: 21f92a17b26627ac03bf19f8c5eea550bac915366919a7b7f73893f0bdd8669421e02ec0379387439a46b1e1ad05dfe8dc9a6f6784b9dc8919f8c7dc05694dd8 languageName: node linkType: hard -"@opentelemetry/instrumentation-tedious@npm:^0.13.0": - version: 0.13.0 - resolution: "@opentelemetry/instrumentation-tedious@npm:0.13.0" +"@opentelemetry/instrumentation-tedious@npm:^0.14.0": + version: 0.14.0 + resolution: "@opentelemetry/instrumentation-tedious@npm:0.14.0" dependencies: - "@opentelemetry/instrumentation": ^0.52.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/instrumentation": ^0.53.0 + "@opentelemetry/semantic-conventions": ^1.27.0 "@types/tedious": ^4.0.14 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 71833d6989d91a4696c3c73836fc12f31930f9fe30e95d20107d5e54054d1276baeeda7ee125c4f43d9859a1337fe017d1d91899e28d506ab275a68ab84e3bee + checksum: 31d6f0d10c3c1f03c9bf3207d7b5569f23ff202f954cb5b91242d788813b5807e5fc204748d14c3b12d320fa5dc1205d4b54131847496117af54e205f025a23e languageName: node linkType: hard -"@opentelemetry/instrumentation-undici@npm:^0.5.0": - version: 0.5.0 - resolution: "@opentelemetry/instrumentation-undici@npm:0.5.0" +"@opentelemetry/instrumentation-undici@npm:^0.6.0": + version: 0.6.0 + resolution: "@opentelemetry/instrumentation-undici@npm:0.6.0" dependencies: "@opentelemetry/core": ^1.8.0 - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/instrumentation": ^0.53.0 peerDependencies: "@opentelemetry/api": ^1.7.0 - checksum: 534a5f9171fcd806332096a996fe1a3ceffaef5e04c3332b82d73242909b21941c4b857e6658d75a29c140c4f5c89d39a157bc7023a774d61b559d2086137138 + checksum: b16b9ff23fbca13a18889fc302ddae3db8b22ebd9d8224b0297bd45014bb914d6bea89e55aedda6879cee1760c93bcedc2eaa12e60e88f45dd8c9e5f61a0aefd languageName: node linkType: hard -"@opentelemetry/instrumentation-winston@npm:^0.39.0": - version: 0.39.0 - resolution: "@opentelemetry/instrumentation-winston@npm:0.39.0" +"@opentelemetry/instrumentation-winston@npm:^0.40.0": + version: 0.40.0 + resolution: "@opentelemetry/instrumentation-winston@npm:0.40.0" dependencies: - "@opentelemetry/api-logs": ^0.52.0 - "@opentelemetry/instrumentation": ^0.52.0 + "@opentelemetry/api-logs": ^0.53.0 + "@opentelemetry/instrumentation": ^0.53.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: df2d28ae4dbe65294afeaee36b5b914d96473674e968f939bfd64a2288730b21911c920168b4f2e4961d8b03c988e327ae828b84a5d1875cb424b84b6fd80e1a + checksum: be7c3f6f8a5672db940b8196e9254a4188c9c6bb717a272088fe5e8319874a80a28a56b1073ad130bb2233a29d4459a754b4ea2f36855f90a679a9c379f5cf16 languageName: node linkType: hard -"@opentelemetry/instrumentation@npm:0.52.1, @opentelemetry/instrumentation@npm:^0.52.0": - version: 0.52.1 - resolution: "@opentelemetry/instrumentation@npm:0.52.1" - dependencies: - "@opentelemetry/api-logs": 0.52.1 - "@types/shimmer": ^1.0.2 - import-in-the-middle: ^1.8.1 - require-in-the-middle: ^7.1.1 - semver: ^7.5.2 - shimmer: ^1.2.1 - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: e8b4f202dc9355ca46714349a5e1663346e162f79706eed38015edf38fc536330fde4cc19ed7d3d6b03258c890c1dc0ba6d658d7aac3f41f1803bd03699d2701 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation@npm:0.53.0": +"@opentelemetry/instrumentation@npm:0.53.0, @opentelemetry/instrumentation@npm:^0.53.0": version: 0.53.0 resolution: "@opentelemetry/instrumentation@npm:0.53.0" dependencies: @@ -13407,18 +13302,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-exporter-base@npm:0.52.1": - version: 0.52.1 - resolution: "@opentelemetry/otlp-exporter-base@npm:0.52.1" - dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/otlp-transformer": 0.52.1 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: a1629886907a393e24a91ef5adc389e093d3725dd2957bc29903442541b2a30673100b13c200c37fe0a2e2fec53ced8b47ad90064ebc49f2e9382c2b1719d3c3 - languageName: node - linkType: hard - "@opentelemetry/otlp-exporter-base@npm:0.53.0": version: 0.53.0 resolution: "@opentelemetry/otlp-exporter-base@npm:0.53.0" @@ -13431,20 +13314,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-grpc-exporter-base@npm:0.52.1": - version: 0.52.1 - resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.52.1" - dependencies: - "@grpc/grpc-js": ^1.7.1 - "@opentelemetry/core": 1.25.1 - "@opentelemetry/otlp-exporter-base": 0.52.1 - "@opentelemetry/otlp-transformer": 0.52.1 - peerDependencies: - "@opentelemetry/api": ^1.0.0 - checksum: 22b1eb92ceec038807d60b7341bea7237ea903f0e2cf91b4fefbaf4cc283518b89a819da18b966a4b61915c7143e00ccc51ef909c3de5fa518b0dbe6c78abc5f - languageName: node - linkType: hard - "@opentelemetry/otlp-grpc-exporter-base@npm:0.53.0": version: 0.53.0 resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.53.0" @@ -13459,23 +13328,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-transformer@npm:0.52.1": - version: 0.52.1 - resolution: "@opentelemetry/otlp-transformer@npm:0.52.1" - dependencies: - "@opentelemetry/api-logs": 0.52.1 - "@opentelemetry/core": 1.25.1 - "@opentelemetry/resources": 1.25.1 - "@opentelemetry/sdk-logs": 0.52.1 - "@opentelemetry/sdk-metrics": 1.25.1 - "@opentelemetry/sdk-trace-base": 1.25.1 - protobufjs: ^7.3.0 - peerDependencies: - "@opentelemetry/api": ">=1.3.0 <1.10.0" - checksum: 0e083ee484a79506d8ce41a8dd5d2d4d7669c380ac1ef9c313c348a6af8384365d2b90d99165590cced6453ce9192c0fe270d9cc974e9240e8ca02a6cf70151d - languageName: node - linkType: hard - "@opentelemetry/otlp-transformer@npm:0.53.0": version: 0.53.0 resolution: "@opentelemetry/otlp-transformer@npm:0.53.0" @@ -13493,12 +13345,12 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/propagation-utils@npm:^0.30.10": - version: 0.30.10 - resolution: "@opentelemetry/propagation-utils@npm:0.30.10" +"@opentelemetry/propagation-utils@npm:^0.30.11": + version: 0.30.11 + resolution: "@opentelemetry/propagation-utils@npm:0.30.11" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 6e193bdb6fb0c1b93d9cf5dab518ae73f152b51710081472f1cc4f74fcc30dcf545ba74881c182347ac894595f5db719a989ee9dbbc905b70218a7ddc5a625aa + checksum: 4cce9958ca40b042adda8601b651799f1a140a513dd8c085094cb88265470bf40a04ed5f84301d891fef90f59698802e20828311b6189e4ab6c741101d8ac248 languageName: node linkType: hard @@ -13513,17 +13365,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/propagator-b3@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/propagator-b3@npm:1.25.1" - dependencies: - "@opentelemetry/core": 1.25.1 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 7aef1e192b33533dfc9fd759efc9cea65cc29f1f35c0e1a2bb4065244ed9a78b18d4a9c843c646484e61f83834d6162ae2a1ebfc40750e4eae844e5dd4b85565 - languageName: node - linkType: hard - "@opentelemetry/propagator-b3@npm:1.26.0": version: 1.26.0 resolution: "@opentelemetry/propagator-b3@npm:1.26.0" @@ -13535,17 +13376,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/propagator-jaeger@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/propagator-jaeger@npm:1.25.1" - dependencies: - "@opentelemetry/core": 1.25.1 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 89b1e9f4daa2494ef472a16ddf1eb8cad547f78bf3255cf724113c346fe9be9965fd36ed5ffcb4098ecfec25ebb120d8e3e06a5783999cdf552f164047938028 - languageName: node - linkType: hard - "@opentelemetry/propagator-jaeger@npm:1.26.0": version: 1.26.0 resolution: "@opentelemetry/propagator-jaeger@npm:1.26.0" @@ -13564,79 +13394,67 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resource-detector-alibaba-cloud@npm:^0.29.0": - version: 0.29.0 - resolution: "@opentelemetry/resource-detector-alibaba-cloud@npm:0.29.0" +"@opentelemetry/resource-detector-alibaba-cloud@npm:^0.29.1": + version: 0.29.1 + resolution: "@opentelemetry/resource-detector-alibaba-cloud@npm:0.29.1" dependencies: "@opentelemetry/resources": ^1.0.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: aaeec6ea8e36f93bd2d38ac004fa9f53fd87c6d352977f138fe4ee67d93f64b4fb2d48a9d88b523622cfa1b307aa2ef1a4a21943f8844e98d19ae5f5cfc965a3 + checksum: 8b32490aaa18996aaed79d2b512d947ea0509c326002b98ec63fcba8ad3793e63365f338f5792bf0dd6c4141126ca491dff3bbf079fa37fdf78a772bb6c8d487 languageName: node linkType: hard -"@opentelemetry/resource-detector-aws@npm:^1.6.0": - version: 1.6.0 - resolution: "@opentelemetry/resource-detector-aws@npm:1.6.0" +"@opentelemetry/resource-detector-aws@npm:^1.6.1": + version: 1.6.1 + resolution: "@opentelemetry/resource-detector-aws@npm:1.6.1" dependencies: "@opentelemetry/core": ^1.0.0 "@opentelemetry/resources": ^1.10.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: e552552fca0817874cb722bb0054b5d2e25922dea4ec42658cafdcb6465ee4015c6a56b53c6c7ab2d5f1eea4ef04018953f28bf41e4395e1a9caba6a060e0cd2 + checksum: ff466f439c2dc061e5c587226ebace5f6d88ddda923889842d6c7fc4076dd88bf27096e4f80f23e8c37bde4e309b3666c7e0fd90637f2dffa6581e9e9cf75a33 languageName: node linkType: hard -"@opentelemetry/resource-detector-azure@npm:^0.2.10": - version: 0.2.10 - resolution: "@opentelemetry/resource-detector-azure@npm:0.2.10" +"@opentelemetry/resource-detector-azure@npm:^0.2.11": + version: 0.2.11 + resolution: "@opentelemetry/resource-detector-azure@npm:0.2.11" dependencies: "@opentelemetry/core": ^1.25.1 "@opentelemetry/resources": ^1.10.1 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 8cb8b1d2633407414f5f9f3b250d8a31ee76d47216d6bfe396a59ee2e091eb7a6881c399b27bb47de509154ff2e43f13f6b419f6a3a9c743a2bb1a42b5775ed2 + checksum: 13515be1d57f9a9611064224e55766a7a66176d85bcbc62e7313e261c0a40e8c02e6ec024ed51f1e20ce16f3e969a601a756d2130f01b24a1e088743886903b3 languageName: node linkType: hard -"@opentelemetry/resource-detector-container@npm:^0.4.0": - version: 0.4.0 - resolution: "@opentelemetry/resource-detector-container@npm:0.4.0" +"@opentelemetry/resource-detector-container@npm:^0.4.1": + version: 0.4.1 + resolution: "@opentelemetry/resource-detector-container@npm:0.4.1" dependencies: "@opentelemetry/resources": ^1.10.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/semantic-conventions": ^1.27.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: a847f147412afb6ecfabe9ee37742a3d80e83a96657c80da4c05ee5380e4cc4d596d5ff8872b45402bd550462ff1d6363bed035f04d67004c3a3e864f1eb658d + checksum: 7a8e5715469209881f63fe21745d807e821fa3cdf4bd4751b2da43078607b9498a18773a7bd47ba22e872397766d2c3bbf55d168ad0d9ad31c159979a32d62bf languageName: node linkType: hard -"@opentelemetry/resource-detector-gcp@npm:^0.29.10": - version: 0.29.10 - resolution: "@opentelemetry/resource-detector-gcp@npm:0.29.10" +"@opentelemetry/resource-detector-gcp@npm:^0.29.11": + version: 0.29.11 + resolution: "@opentelemetry/resource-detector-gcp@npm:0.29.11" dependencies: "@opentelemetry/core": ^1.0.0 "@opentelemetry/resources": ^1.0.0 - "@opentelemetry/semantic-conventions": ^1.22.0 + "@opentelemetry/semantic-conventions": ^1.27.0 gcp-metadata: ^6.0.0 peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 68c3ae4bffb96aba16292e47a1981e115c9971457d1e5952c1cc1cd2f82e675b0f1ede55181e11a044de79ae729c9afe801f5e14ac0a70540f734c0b9e89944d - languageName: node - linkType: hard - -"@opentelemetry/resources@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/resources@npm:1.25.1" - dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/semantic-conventions": 1.25.1 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 806e5aabbc93afcab767dc84707f702ca51bbc93e4565eb69a8591ed2fe78439aca19c5ca0d9f044c85ed97b9efb35936fdb65bef01f5f3e68504002c8a07220 + checksum: 4e47558b767c97e9dae67f648449987cd7dd372a8d29e1ee7eefa3da6cf15d944f7f8f9a8a8f07026e7843b95958faf26739c24cabbb106e923f898abeae5132 languageName: node linkType: hard @@ -13652,19 +13470,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-logs@npm:0.52.1": - version: 0.52.1 - resolution: "@opentelemetry/sdk-logs@npm:0.52.1" - dependencies: - "@opentelemetry/api-logs": 0.52.1 - "@opentelemetry/core": 1.25.1 - "@opentelemetry/resources": 1.25.1 - peerDependencies: - "@opentelemetry/api": ">=1.4.0 <1.10.0" - checksum: 16bdccd8250d96df0ffcadb63b107135d207072c1da52d056f00b211ca56924413d53a529cc0e362d96bd1bf33aa801eba51b395c5cb290b8c66fb1b988a7ff6 - languageName: node - linkType: hard - "@opentelemetry/sdk-logs@npm:0.53.0": version: 0.53.0 resolution: "@opentelemetry/sdk-logs@npm:0.53.0" @@ -13678,19 +13483,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-metrics@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/sdk-metrics@npm:1.25.1" - dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/resources": 1.25.1 - lodash.merge: ^4.6.2 - peerDependencies: - "@opentelemetry/api": ">=1.3.0 <1.10.0" - checksum: efd3902d30e75bfc16e4208ffc92096743148ed8cec84900d05f98cc17ff146c711c398c3a526589433509c82399641b0759b4ba9fffc12be2e5007a55af7517 - languageName: node - linkType: hard - "@opentelemetry/sdk-metrics@npm:1.26.0, @opentelemetry/sdk-metrics@npm:^1.9.1": version: 1.26.0 resolution: "@opentelemetry/sdk-metrics@npm:1.26.0" @@ -13703,29 +13495,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-node@npm:^0.52.0": - version: 0.52.1 - resolution: "@opentelemetry/sdk-node@npm:0.52.1" - dependencies: - "@opentelemetry/api-logs": 0.52.1 - "@opentelemetry/core": 1.25.1 - "@opentelemetry/exporter-trace-otlp-grpc": 0.52.1 - "@opentelemetry/exporter-trace-otlp-http": 0.52.1 - "@opentelemetry/exporter-trace-otlp-proto": 0.52.1 - "@opentelemetry/exporter-zipkin": 1.25.1 - "@opentelemetry/instrumentation": 0.52.1 - "@opentelemetry/resources": 1.25.1 - "@opentelemetry/sdk-logs": 0.52.1 - "@opentelemetry/sdk-metrics": 1.25.1 - "@opentelemetry/sdk-trace-base": 1.25.1 - "@opentelemetry/sdk-trace-node": 1.25.1 - "@opentelemetry/semantic-conventions": 1.25.1 - peerDependencies: - "@opentelemetry/api": ">=1.3.0 <1.10.0" - checksum: 601f598fb0c7d50387c4d078a7f0436598c99f3a4c0eda4dd18bae00474d6ee9836adb148fb230b6bc351b8119c5dcba1f9803adfb807d19f30956c0a9a7eba5 - languageName: node - linkType: hard - "@opentelemetry/sdk-node@npm:^0.53.0": version: 0.53.0 resolution: "@opentelemetry/sdk-node@npm:0.53.0" @@ -13752,19 +13521,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-trace-base@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/sdk-trace-base@npm:1.25.1" - dependencies: - "@opentelemetry/core": 1.25.1 - "@opentelemetry/resources": 1.25.1 - "@opentelemetry/semantic-conventions": 1.25.1 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 8ac97f7d8d36bf412c5f47ff98ded07c5dfd11602a6ae7657ec7b5f50bb6ddaa20fc682626afcf74e21b375dbad0d1d47c8e20204d5139431afec25165f6252b - languageName: node - linkType: hard - "@opentelemetry/sdk-trace-base@npm:1.26.0": version: 1.26.0 resolution: "@opentelemetry/sdk-trace-base@npm:1.26.0" @@ -13778,22 +13534,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-trace-node@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/sdk-trace-node@npm:1.25.1" - dependencies: - "@opentelemetry/context-async-hooks": 1.25.1 - "@opentelemetry/core": 1.25.1 - "@opentelemetry/propagator-b3": 1.25.1 - "@opentelemetry/propagator-jaeger": 1.25.1 - "@opentelemetry/sdk-trace-base": 1.25.1 - semver: ^7.5.2 - peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: f6835329651f5d888a90d95f6f86d5f660158029af2e01b68a5a0e21b2eb9dd40147dd6b0c321a12f65924c3ec1574d4ed2c6f05eea5c85280a82f3a95436bdb - languageName: node - linkType: hard - "@opentelemetry/sdk-trace-node@npm:1.26.0": version: 1.26.0 resolution: "@opentelemetry/sdk-trace-node@npm:1.26.0" @@ -13817,14 +13557,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.25.1": - version: 1.25.1 - resolution: "@opentelemetry/semantic-conventions@npm:1.25.1" - checksum: fea418a4b09c55121c6da11c49dd2105116533838c484aead17e8acf8029dad711e145849812f9c61f9e48fad8e2b6cf103d2c18847ca993032ce9b27c2f863d - languageName: node - linkType: hard - -"@opentelemetry/semantic-conventions@npm:1.27.0, @opentelemetry/semantic-conventions@npm:^1.22.0, @opentelemetry/semantic-conventions@npm:^1.23.0, @opentelemetry/semantic-conventions@npm:^1.24.0": +"@opentelemetry/semantic-conventions@npm:1.27.0, @opentelemetry/semantic-conventions@npm:^1.27.0": version: 1.27.0 resolution: "@opentelemetry/semantic-conventions@npm:1.27.0" checksum: 26d85f8d13c8c64024f7a84528cff41d56afc9829e7ff8a654576404f8b2c1a9c264adcc6fa5a9551bacdd938a4a464041fa9493e0a722e5605f2c2ae6752398 @@ -17202,10 +16935,10 @@ __metadata: languageName: node linkType: hard -"@types/aws-lambda@npm:8.10.122, @types/aws-lambda@npm:^8.10.83": - version: 8.10.122 - resolution: "@types/aws-lambda@npm:8.10.122" - checksum: 5c2e02ae8fc0eea90fa3b1014f401a8567695e65910fb53452e813b9b58761c956fba50ac7da606b97e07d881d264ff513573d279e7116f3c6b9590fdb093f31 +"@types/aws-lambda@npm:8.10.143, @types/aws-lambda@npm:^8.10.83": + version: 8.10.143 + resolution: "@types/aws-lambda@npm:8.10.143" + checksum: b2ec1db79c57916651a47c173f14ef90564273542a455d79cc07d3eeabef11a39a3d63c3d26061335fbc557210ec4c8d4daf90bc8b51a38ef43a3299223dc3bf languageName: node linkType: hard @@ -18086,12 +17819,12 @@ __metadata: languageName: node linkType: hard -"@types/mysql@npm:2.15.22": - version: 2.15.22 - resolution: "@types/mysql@npm:2.15.22" +"@types/mysql@npm:2.15.26": + version: 2.15.26 + resolution: "@types/mysql@npm:2.15.26" dependencies: "@types/node": "*" - checksum: 325120f027b04052b3ed056fef096d186ecc0988d9efe110a52bd3f2233d02e17fb802ea42da7fa1ae1d150b0194cddf56ff71bfb28411bc05361f947b0635af + checksum: c43395643aca3565492a12f7bb7168cea7f5fa1b6539caabc99e657bbbea3ef330d65e8d8fc873fedfbbbed48a01ac8c1ce0d31c7989cd44a4845b32253daf6c languageName: node linkType: hard @@ -18297,12 +18030,12 @@ __metadata: languageName: node linkType: hard -"@types/pg-pool@npm:2.0.4": - version: 2.0.4 - resolution: "@types/pg-pool@npm:2.0.4" +"@types/pg-pool@npm:2.0.6": + version: 2.0.6 + resolution: "@types/pg-pool@npm:2.0.6" dependencies: "@types/pg": "*" - checksum: 5ae1c49fe1820ec011f8e2a877198a62f4c9795d2cc340dff4527c26f24ee22dffe99a8ca5cdec6edb54613bded820cc51256fb668e0eb4d22794181b94fad82 + checksum: cc54ce97115effc982bd052f79901a78215e76554aca0ecc92e78eb907e4fb2962924039369cd9aaf48075f1637593ce14647c62d3a2eb03789ce5d1c6df750b languageName: node linkType: hard @@ -18633,7 +18366,7 @@ __metadata: languageName: node linkType: hard -"@types/shimmer@npm:^1.0.2, @types/shimmer@npm:^1.2.0": +"@types/shimmer@npm:^1.2.0": version: 1.2.0 resolution: "@types/shimmer@npm:1.2.0" checksum: f081a31d826ce7bfe8cc7ba8129d2b1dffae44fd580eba4fcf741237646c4c2494ae6de2cada4b7713d138f35f4bc512dbf01311d813dee82020f97d7d8c491c @@ -26896,7 +26629,7 @@ __metadata: "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-signals-backend": "workspace:^" "@backstage/plugin-techdocs-backend": "workspace:^" - "@opentelemetry/auto-instrumentations-node": ^0.49.0 + "@opentelemetry/auto-instrumentations-node": ^0.50.0 "@opentelemetry/exporter-prometheus": ^0.53.0 "@opentelemetry/sdk-node": ^0.53.0 example-app: "link:../app" From 49999ec5ecf467e48a518373ae50962244318d15 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:27:32 +0000 Subject: [PATCH 216/317] fix(deps): update dependency eslint-plugin-import to v2.30.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 88 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/yarn.lock b/yarn.lock index 372cfbe905..e5b8a02985 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14901,6 +14901,13 @@ __metadata: languageName: node linkType: hard +"@rtsao/scc@npm:^1.1.0": + version: 1.1.0 + resolution: "@rtsao/scc@npm:1.1.0" + checksum: 17d04adf404e04c1e61391ed97bca5117d4c2767a76ae3e879390d6dec7b317fcae68afbf9e98badee075d0b64fa60f287729c4942021b4d19cd01db77385c01 + languageName: node + linkType: hard + "@rushstack/node-core-library@npm:3.59.7": version: 3.59.7 resolution: "@rushstack/node-core-library@npm:3.59.7" @@ -20901,7 +20908,7 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.6, array-includes@npm:^3.1.7, array-includes@npm:^3.1.8": +"array-includes@npm:^3.1.6, array-includes@npm:^3.1.8": version: 3.1.8 resolution: "array-includes@npm:3.1.8" dependencies: @@ -20943,16 +20950,17 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.3": - version: 1.2.3 - resolution: "array.prototype.findlastindex@npm:1.2.3" +"array.prototype.findlastindex@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.findlastindex@npm:1.2.5" dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - es-shim-unscopables: ^1.0.0 - get-intrinsic: ^1.2.1 - checksum: 31f35d7b370c84db56484618132041a9af401b338f51899c2e78ef7690fbba5909ee7ca3c59a7192085b328cc0c68c6fd1f6d1553db01a689a589ae510f3966e + call-bind: ^1.0.7 + define-properties: ^1.2.1 + es-abstract: ^1.23.2 + es-errors: ^1.3.0 + es-object-atoms: ^1.0.0 + es-shim-unscopables: ^1.0.2 + checksum: 2c81cff2a75deb95bf1ed89b6f5f2bfbfb882211e3b7cc59c3d6b87df774cd9d6b36949a8ae39ac476e092c1d4a4905f5ee11a86a456abb10f35f8211ae4e710 languageName: node linkType: hard @@ -26210,15 +26218,15 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.8.0": - version: 2.8.0 - resolution: "eslint-module-utils@npm:2.8.0" +"eslint-module-utils@npm:^2.9.0": + version: 2.9.0 + resolution: "eslint-module-utils@npm:2.9.0" dependencies: debug: ^3.2.7 peerDependenciesMeta: eslint: optional: true - checksum: 74c6dfea7641ebcfe174be61168541a11a14aa8d72e515f5f09af55cd0d0862686104b0524aa4b8e0ce66418a44aa38a94d2588743db5fd07a6b49ffd16921d2 + checksum: 304ec1279cac62b242d9347859625235c07be8b8870306d85502375b2b4beeccda2da2f03884ec00a1820e643d87718e5a2eea149730c61aeb42ea8f4cb9f4a6 languageName: node linkType: hard @@ -26237,29 +26245,30 @@ __metadata: linkType: hard "eslint-plugin-import@npm:^2.25.4": - version: 2.29.1 - resolution: "eslint-plugin-import@npm:2.29.1" + version: 2.30.0 + resolution: "eslint-plugin-import@npm:2.30.0" dependencies: - array-includes: ^3.1.7 - array.prototype.findlastindex: ^1.2.3 + "@rtsao/scc": ^1.1.0 + array-includes: ^3.1.8 + array.prototype.findlastindex: ^1.2.5 array.prototype.flat: ^1.3.2 array.prototype.flatmap: ^1.3.2 debug: ^3.2.7 doctrine: ^2.1.0 eslint-import-resolver-node: ^0.3.9 - eslint-module-utils: ^2.8.0 - hasown: ^2.0.0 - is-core-module: ^2.13.1 + eslint-module-utils: ^2.9.0 + hasown: ^2.0.2 + is-core-module: ^2.15.1 is-glob: ^4.0.3 minimatch: ^3.1.2 - object.fromentries: ^2.0.7 - object.groupby: ^1.0.1 - object.values: ^1.1.7 + object.fromentries: ^2.0.8 + object.groupby: ^1.0.3 + object.values: ^1.2.0 semver: ^6.3.1 tsconfig-paths: ^3.15.0 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: e65159aef808136d26d029b71c8c6e4cb5c628e65e5de77f1eb4c13a379315ae55c9c3afa847f43f4ff9df7e54515c77ffc6489c6a6f81f7dd7359267577468c + checksum: 0ec1ad69c0d22f15bc4a49ee97ae757e4adfc3181996f2c4a1ed4d5028bd99bab38e7623e58ef4477ba1db8425f441e4e986367125273efa4c5f7ad2c4467a9a languageName: node linkType: hard @@ -30064,12 +30073,12 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": - version: 2.13.1 - resolution: "is-core-module@npm:2.13.1" +"is-core-module@npm:^2.1.0, 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" dependencies: - hasown: ^2.0.0 - checksum: 256559ee8a9488af90e4bad16f5583c6d59e92f0742e9e8bb4331e758521ee86b810b93bae44f390766ffbc518a0488b18d9dab7da9a5ff997d499efc9403f7c + hasown: ^2.0.2 + checksum: df134c168115690724b62018c37b2f5bba0d5745fa16960b329c5a00883a8bea6a5632fdb1e3efcce237c201826ba09f93197b7cd95577ea56b0df335be23633 languageName: node linkType: hard @@ -35585,7 +35594,7 @@ __metadata: languageName: node linkType: hard -"object.fromentries@npm:^2.0.7, object.fromentries@npm:^2.0.8": +"object.fromentries@npm:^2.0.8": version: 2.0.8 resolution: "object.fromentries@npm:2.0.8" dependencies: @@ -35597,19 +35606,18 @@ __metadata: languageName: node linkType: hard -"object.groupby@npm:^1.0.1": - version: 1.0.1 - resolution: "object.groupby@npm:1.0.1" +"object.groupby@npm:^1.0.3": + version: 1.0.3 + resolution: "object.groupby@npm:1.0.3" dependencies: - call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - get-intrinsic: ^1.2.1 - checksum: d7959d6eaaba358b1608066fc67ac97f23ce6f573dc8fc661f68c52be165266fcb02937076aedb0e42722fdda0bdc0bbf74778196ac04868178888e9fd3b78b5 + call-bind: ^1.0.7 + define-properties: ^1.2.1 + es-abstract: ^1.23.2 + checksum: 0d30693ca3ace29720bffd20b3130451dca7a56c612e1926c0a1a15e4306061d84410bdb1456be2656c5aca53c81b7a3661eceaa362db1bba6669c2c9b6d1982 languageName: node linkType: hard -"object.values@npm:^1.1.6, object.values@npm:^1.1.7, object.values@npm:^1.2.0": +"object.values@npm:^1.1.6, object.values@npm:^1.2.0": version: 1.2.0 resolution: "object.values@npm:1.2.0" dependencies: From 9b234dc2b3472f64f13301e8ccfb13982384ba67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:28:35 +0000 Subject: [PATCH 217/317] fix(deps): update dependency eslint-plugin-jsx-a11y to v6.10.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 372cfbe905..fd73458fb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21283,7 +21283,7 @@ __metadata: languageName: node linkType: hard -"axe-core@npm:^4.9.1": +"axe-core@npm:^4.10.0": version: 4.10.0 resolution: "axe-core@npm:4.10.0" checksum: 7eca827fd8d98d7e4b561df65437be608155c613d8f262ae9e4a6ade02c156c7362dcbc3f71b4b526edce686f7c686280236bcff1d6725e2ef8327def72a8c41 @@ -21334,12 +21334,10 @@ __metadata: languageName: node linkType: hard -"axobject-query@npm:~3.1.1": - version: 3.1.1 - resolution: "axobject-query@npm:3.1.1" - dependencies: - deep-equal: ^2.0.5 - checksum: c12a5da10dc7bab75e1cda9b6a3b5fcf10eba426ddf1a17b71ef65a434ed707ede7d1c4f013ba1609e970bc8c0cddac01365080d376204314e9b294719acd8a5 +"axobject-query@npm:^4.1.0": + version: 4.1.0 + resolution: "axobject-query@npm:4.1.0" + checksum: 7d1e87bf0aa7ae7a76cd39ab627b7c48fda3dc40181303d9adce4ba1d5b5ce73b5e5403ee6626ec8e91090448c887294d6144e24b6741a976f5be9347e3ae1df languageName: node linkType: hard @@ -26282,15 +26280,15 @@ __metadata: linkType: hard "eslint-plugin-jsx-a11y@npm:^6.5.1": - version: 6.9.0 - resolution: "eslint-plugin-jsx-a11y@npm:6.9.0" + version: 6.10.0 + resolution: "eslint-plugin-jsx-a11y@npm:6.10.0" dependencies: aria-query: ~5.1.3 array-includes: ^3.1.8 array.prototype.flatmap: ^1.3.2 ast-types-flow: ^0.0.8 - axe-core: ^4.9.1 - axobject-query: ~3.1.1 + axe-core: ^4.10.0 + axobject-query: ^4.1.0 damerau-levenshtein: ^1.0.8 emoji-regex: ^9.2.2 es-iterator-helpers: ^1.0.19 @@ -26302,8 +26300,8 @@ __metadata: safe-regex-test: ^1.0.3 string.prototype.includes: ^2.0.0 peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - checksum: 122cbd22bbd8c3e4a37f386ec183ada63a4ecfa7af7d40cd8a110777ac5ad5ff542f60644596a9e2582ed138a1cc6d96c5d5ca934105e29d5245d6c951ebc3ef + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + checksum: 1009deca12ddbe3624586bc5fc3534ca98d00a5841a2563cb6abd9339b984f0a99075dc2a703a517f4087eb84d659c87e60beda17645883de2ba1d86f2b20c96 languageName: node linkType: hard From 3606843e44f685964c0a0164c54ebe509ab14428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 5 Sep 2024 14:50:41 +0200 Subject: [PATCH 218/317] fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/rich-cheetahs-vanish.md | 6 ++++++ .../deprecated/util/DockerContainerRunner.ts | 20 +++++++++++++------ .../stages/generate/DockerContainerRunner.ts | 17 ++++++++++------ 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 .changeset/rich-cheetahs-vanish.md diff --git a/.changeset/rich-cheetahs-vanish.md b/.changeset/rich-cheetahs-vanish.md new file mode 100644 index 0000000000..26cc55b917 --- /dev/null +++ b/.changeset/rich-cheetahs-vanish.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-common': patch +'@backstage/plugin-techdocs-node': patch +--- + +Internal fixes to match `testcontainers` update diff --git a/packages/backend-common/src/deprecated/util/DockerContainerRunner.ts b/packages/backend-common/src/deprecated/util/DockerContainerRunner.ts index ea576e4a3a..a9f5fb0432 100644 --- a/packages/backend-common/src/deprecated/util/DockerContainerRunner.ts +++ b/packages/backend-common/src/deprecated/util/DockerContainerRunner.ts @@ -64,11 +64,19 @@ export class DockerContainerRunner implements ContainerRunner { if (pullImage) { await new Promise((resolve, reject) => { this.dockerClient.pull(imageName, pullOptions, (err, stream) => { - if (err) return reject(err); - stream.pipe(logStream, { end: false }); - stream.on('end', () => resolve()); - stream.on('error', (error: Error) => reject(error)); - return undefined; + if (err) { + reject(err); + } else if (!stream) { + reject( + new Error( + 'Unexpeected error: no stream returned from Docker while pulling image', + ), + ); + } else { + stream.pipe(logStream, { end: false }); + stream.on('end', () => resolve()); + stream.on('error', (error: Error) => reject(error)); + } }); }); } @@ -99,7 +107,7 @@ export class DockerContainerRunner implements ContainerRunner { } // Create docker environment variables array - const Env = []; + const Env = new Array(); for (const [key, value] of Object.entries(envVars)) { Env.push(`${key}=${value}`); } diff --git a/plugins/techdocs-node/src/stages/generate/DockerContainerRunner.ts b/plugins/techdocs-node/src/stages/generate/DockerContainerRunner.ts index b7cb7f1c86..a9fe55d85f 100644 --- a/plugins/techdocs-node/src/stages/generate/DockerContainerRunner.ts +++ b/plugins/techdocs-node/src/stages/generate/DockerContainerRunner.ts @@ -76,12 +76,17 @@ export class DockerContainerRunner { this.dockerClient.pull(imageName, {}, (err, stream) => { if (err) { reject(err); - return; + } else if (!stream) { + reject( + new Error( + 'Unexpeected error: no stream returned from Docker while pulling image', + ), + ); + } else { + pipeline(stream, logStream, { end: false }) + .then(resolve) + .catch(reject); } - - pipeline(stream, logStream, { end: false }) - .then(resolve) - .catch(reject); }); }); } @@ -112,7 +117,7 @@ export class DockerContainerRunner { } // Create docker environment variables array - const Env = []; + const Env = new Array(); for (const [key, value] of Object.entries(envVars)) { Env.push(`${key}=${value}`); } From 0ecbb8db10e26325cc3274905ac48b8d0a9754d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 5 Sep 2024 15:08:46 +0200 Subject: [PATCH 219/317] fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../src/components/CodeSnippet/CodeSnippet.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/src/components/CodeSnippet/CodeSnippet.stories.tsx b/packages/core-components/src/components/CodeSnippet/CodeSnippet.stories.tsx index a266c8c6cb..c4ed5ae072 100644 --- a/packages/core-components/src/components/CodeSnippet/CodeSnippet.stories.tsx +++ b/packages/core-components/src/components/CodeSnippet/CodeSnippet.stories.tsx @@ -52,7 +52,7 @@ greet(world) export const Default = () => ( - + ); From 16ffdd68f322bc9e1519abf8f86c00d1b0dcc3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 5 Sep 2024 14:39:55 +0200 Subject: [PATCH 220/317] remove direct vite dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/beige-jobs-begin.md | 5 + packages/app-next/package.json | 1 - packages/app/package.json | 1 - packages/cli/package.json | 1 - packages/cli/src/lib/bundler/server.ts | 1 + yarn.lock | 290 +------------------------ 6 files changed, 7 insertions(+), 292 deletions(-) create mode 100644 .changeset/beige-jobs-begin.md diff --git a/.changeset/beige-jobs-begin.md b/.changeset/beige-jobs-begin.md new file mode 100644 index 0000000000..f93e534857 --- /dev/null +++ b/.changeset/beige-jobs-begin.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Remove direct `vite` dependency diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 397be2197a..0e80f188f7 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -62,7 +62,6 @@ "react-router": "^6.3.0", "react-router-dom": "^6.3.0", "react-use": "^17.2.4", - "vite": "^4.4.9", "vite-plugin-html": "^3.2.0", "vite-plugin-node-polyfills": "^0.22.0", "zen-observable": "^0.10.0" diff --git a/packages/app/package.json b/packages/app/package.json index 9d9431b6c1..e60f7bd548 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -81,7 +81,6 @@ "react-router": "^6.3.0", "react-router-dom": "^6.3.0", "react-use": "^17.2.4", - "vite": "^4.4.9", "vite-plugin-html": "^3.2.0", "vite-plugin-node-polyfills": "^0.22.0", "zen-observable": "^0.10.0" diff --git a/packages/cli/package.json b/packages/cli/package.json index 9d15efe9e4..03c11a9966 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -187,7 +187,6 @@ "del": "^7.0.0", "msw": "^1.0.0", "nodemon": "^3.0.1", - "vite": "^4.4.9", "vite-plugin-html": "^3.2.0", "vite-plugin-node-polyfills": "^0.22.0" }, diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 259f3263eb..d73bcc662e 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -54,6 +54,7 @@ DEPRECATION WARNING: React Router Beta is deprecated and support for it will be const { name } = await fs.readJson(libPaths.resolveTarget('package.json')); let webpackServer: WebpackDevServer | undefined = undefined; + // @ts-ignore let viteServer: import('vite').ViteDevServer | undefined = undefined; let latestFrontendAppConfigs: AppConfig[] = []; diff --git a/yarn.lock b/yarn.lock index 39441760f4..5659968a13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4026,7 +4026,6 @@ __metadata: tar: ^6.1.12 terser-webpack-plugin: ^5.1.3 util: ^0.12.3 - vite: ^4.4.9 vite-plugin-html: ^3.2.0 vite-plugin-node-polyfills: ^0.22.0 webpack: ^5.70.0 @@ -8805,13 +8804,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/android-arm64@npm:0.18.20" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm64@npm:0.21.5" @@ -8826,13 +8818,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/android-arm@npm:0.18.20" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - "@esbuild/android-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm@npm:0.21.5" @@ -8847,13 +8832,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/android-x64@npm:0.18.20" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - "@esbuild/android-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-x64@npm:0.21.5" @@ -8868,13 +8846,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/darwin-arm64@npm:0.18.20" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/darwin-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-arm64@npm:0.21.5" @@ -8889,13 +8860,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/darwin-x64@npm:0.18.20" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - "@esbuild/darwin-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-x64@npm:0.21.5" @@ -8910,13 +8874,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/freebsd-arm64@npm:0.18.20" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/freebsd-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-arm64@npm:0.21.5" @@ -8931,13 +8888,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/freebsd-x64@npm:0.18.20" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/freebsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-x64@npm:0.21.5" @@ -8952,13 +8902,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-arm64@npm:0.18.20" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/linux-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm64@npm:0.21.5" @@ -8973,13 +8916,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-arm@npm:0.18.20" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - "@esbuild/linux-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm@npm:0.21.5" @@ -8994,13 +8930,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-ia32@npm:0.18.20" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/linux-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ia32@npm:0.21.5" @@ -9015,13 +8944,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-loong64@npm:0.18.20" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - "@esbuild/linux-loong64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-loong64@npm:0.21.5" @@ -9036,13 +8958,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-mips64el@npm:0.18.20" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - "@esbuild/linux-mips64el@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-mips64el@npm:0.21.5" @@ -9057,13 +8972,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-ppc64@npm:0.18.20" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/linux-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ppc64@npm:0.21.5" @@ -9078,13 +8986,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-riscv64@npm:0.18.20" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - "@esbuild/linux-riscv64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-riscv64@npm:0.21.5" @@ -9099,13 +9000,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-s390x@npm:0.18.20" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - "@esbuild/linux-s390x@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-s390x@npm:0.21.5" @@ -9120,13 +9014,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-x64@npm:0.18.20" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - "@esbuild/linux-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-x64@npm:0.21.5" @@ -9141,13 +9028,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/netbsd-x64@npm:0.18.20" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/netbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/netbsd-x64@npm:0.21.5" @@ -9169,13 +9049,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/openbsd-x64@npm:0.18.20" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/openbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/openbsd-x64@npm:0.21.5" @@ -9190,13 +9063,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/sunos-x64@npm:0.18.20" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - "@esbuild/sunos-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/sunos-x64@npm:0.21.5" @@ -9211,13 +9077,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/win32-arm64@npm:0.18.20" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/win32-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-arm64@npm:0.21.5" @@ -9232,13 +9091,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/win32-ia32@npm:0.18.20" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/win32-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-ia32@npm:0.21.5" @@ -9253,13 +9105,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/win32-x64@npm:0.18.20" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@esbuild/win32-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-x64@npm:0.21.5" @@ -25579,83 +25424,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.10": - version: 0.18.20 - resolution: "esbuild@npm:0.18.20" - dependencies: - "@esbuild/android-arm": 0.18.20 - "@esbuild/android-arm64": 0.18.20 - "@esbuild/android-x64": 0.18.20 - "@esbuild/darwin-arm64": 0.18.20 - "@esbuild/darwin-x64": 0.18.20 - "@esbuild/freebsd-arm64": 0.18.20 - "@esbuild/freebsd-x64": 0.18.20 - "@esbuild/linux-arm": 0.18.20 - "@esbuild/linux-arm64": 0.18.20 - "@esbuild/linux-ia32": 0.18.20 - "@esbuild/linux-loong64": 0.18.20 - "@esbuild/linux-mips64el": 0.18.20 - "@esbuild/linux-ppc64": 0.18.20 - "@esbuild/linux-riscv64": 0.18.20 - "@esbuild/linux-s390x": 0.18.20 - "@esbuild/linux-x64": 0.18.20 - "@esbuild/netbsd-x64": 0.18.20 - "@esbuild/openbsd-x64": 0.18.20 - "@esbuild/sunos-x64": 0.18.20 - "@esbuild/win32-arm64": 0.18.20 - "@esbuild/win32-ia32": 0.18.20 - "@esbuild/win32-x64": 0.18.20 - dependenciesMeta: - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 5d253614e50cdb6ec22095afd0c414f15688e7278a7eb4f3720a6dd1306b0909cf431e7b9437a90d065a31b1c57be60130f63fe3e8d0083b588571f31ee6ec7b - languageName: node - linkType: hard - "esbuild@npm:^0.21.0": version: 0.21.5 resolution: "esbuild@npm:0.21.5" @@ -26451,7 +26219,6 @@ __metadata: react-router: ^6.3.0 react-router-dom: ^6.3.0 react-use: ^17.2.4 - vite: ^4.4.9 vite-plugin-html: ^3.2.0 vite-plugin-node-polyfills: ^0.22.0 zen-observable: ^0.10.0 @@ -26531,7 +26298,6 @@ __metadata: react-router: ^6.3.0 react-router-dom: ^6.3.0 react-use: ^17.2.4 - vite: ^4.4.9 vite-plugin-html: ^3.2.0 vite-plugin-node-polyfills: ^0.22.0 zen-observable: ^0.10.0 @@ -37185,7 +36951,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.0, postcss@npm:^8.4.27, postcss@npm:^8.4.33": +"postcss@npm:^8.1.0, postcss@npm:^8.4.33": version: 8.4.45 resolution: "postcss@npm:8.4.45" dependencies: @@ -39639,20 +39405,6 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^3.27.1": - version: 3.29.4 - resolution: "rollup@npm:3.29.4" - dependencies: - fsevents: ~2.3.2 - dependenciesMeta: - fsevents: - optional: true - bin: - rollup: dist/bin/rollup - checksum: 8bb20a39c8d91130825159c3823eccf4dc2295c9a0a5c4ed851a5bf2167dbf24d9a29f23461a54c955e5506395e6cc188eafc8ab0e20399d7489fb33793b184e - languageName: node - linkType: hard - "rollup@npm:^4.0.0": version: 4.21.2 resolution: "rollup@npm:4.21.2" @@ -43827,46 +43579,6 @@ __metadata: languageName: node linkType: hard -"vite@npm:^4.4.9": - version: 4.5.3 - resolution: "vite@npm:4.5.3" - dependencies: - esbuild: ^0.18.10 - fsevents: ~2.3.2 - postcss: ^8.4.27 - rollup: ^3.27.1 - peerDependencies: - "@types/node": ">= 14" - less: "*" - lightningcss: ^1.21.0 - sass: "*" - stylus: "*" - sugarss: "*" - terser: ^5.4.0 - dependenciesMeta: - fsevents: - optional: true - peerDependenciesMeta: - "@types/node": - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - bin: - vite: bin/vite.js - checksum: fd3f512ce48ca2a1fe60ad0376283b832de9272725fdbc65064ae9248f792de87b0f27a89573115e23e26784800daca329f8a9234d298ba6f60e808a9c63883c - languageName: node - linkType: hard - "vm-browserify@npm:^1.0.1": version: 1.1.2 resolution: "vm-browserify@npm:1.1.2" From cf5bf5c0b4aae84f59b797b8d671d46ec54f3594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 5 Sep 2024 14:55:04 +0200 Subject: [PATCH 221/317] MORE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/app-next/package.json | 3 - packages/app/package.json | 3 - packages/cli/package.json | 5 +- yarn.lock | 284 +++------------------------------ 4 files changed, 22 insertions(+), 273 deletions(-) diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 0e80f188f7..0b31445e4b 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -55,15 +55,12 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@octokit/rest": "^19.0.3", - "@vitejs/plugin-react": "^4.0.4", "history": "^5.0.0", "react": "^18.0.2", "react-dom": "^18.0.2", "react-router": "^6.3.0", "react-router-dom": "^6.3.0", "react-use": "^17.2.4", - "vite-plugin-html": "^3.2.0", - "vite-plugin-node-polyfills": "^0.22.0", "zen-observable": "^0.10.0" }, "devDependencies": { diff --git a/packages/app/package.json b/packages/app/package.json index e60f7bd548..f7dccc1edd 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -74,15 +74,12 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@octokit/rest": "^19.0.3", - "@vitejs/plugin-react": "^4.0.4", "history": "^5.0.0", "react": "^18.0.2", "react-dom": "^18.0.2", "react-router": "^6.3.0", "react-router-dom": "^6.3.0", "react-use": "^17.2.4", - "vite-plugin-html": "^3.2.0", - "vite-plugin-node-polyfills": "^0.22.0", "zen-observable": "^0.10.0" }, "devDependencies": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 03c11a9966..130ad8c213 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -183,12 +183,9 @@ "@types/tar": "^6.1.1", "@types/terser-webpack-plugin": "^5.0.4", "@types/yarnpkg__lockfile": "^1.1.4", - "@vitejs/plugin-react": "^4.0.4", "del": "^7.0.0", "msw": "^1.0.0", - "nodemon": "^3.0.1", - "vite-plugin-html": "^3.2.0", - "vite-plugin-node-polyfills": "^0.22.0" + "nodemon": "^3.0.1" }, "peerDependencies": { "@vitejs/plugin-react": "^4.0.4", diff --git a/yarn.lock b/yarn.lock index 5659968a13..ccc1e25409 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1889,7 +1889,7 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.19.6, @babel/core@npm:^7.24.5, @babel/core@npm:^7.24.7": +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.19.6, @babel/core@npm:^7.24.7": version: 7.25.2 resolution: "@babel/core@npm:7.25.2" dependencies: @@ -2973,28 +2973,6 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-react-jsx-self@npm:^7.24.5": - version: 7.24.7 - resolution: "@babel/plugin-transform-react-jsx-self@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": ^7.24.7 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: 2d72c33664e614031b8a03fc2d4cfd185e99efb1d681cbde4b0b4ab379864b31d83ee923509892f6d94b2c5893c309f0217d33bcda3e470ed42297f958138381 - languageName: node - linkType: hard - -"@babel/plugin-transform-react-jsx-source@npm:^7.24.1": - version: 7.24.7 - resolution: "@babel/plugin-transform-react-jsx-source@npm:7.24.7" - dependencies: - "@babel/helper-plugin-utils": ^7.24.7 - peerDependencies: - "@babel/core": ^7.0.0-0 - checksum: c9afcb2259dd124a2de76f8a578589c18bd2f24dbcf78fe02b53c5cbc20c493c4618369604720e4e699b52be10ba0751b97140e1ef8bc8f0de0a935280e9d5b7 - languageName: node - linkType: hard - "@babel/plugin-transform-react-jsx@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx@npm:7.22.5" @@ -3953,7 +3931,6 @@ __metadata: "@types/yarnpkg__lockfile": ^1.1.4 "@typescript-eslint/eslint-plugin": ^6.12.0 "@typescript-eslint/parser": ^6.7.2 - "@vitejs/plugin-react": ^4.0.4 "@yarnpkg/lockfile": ^1.1.0 "@yarnpkg/parsers": ^3.0.0 bfj: ^8.0.0 @@ -4026,8 +4003,6 @@ __metadata: tar: ^6.1.12 terser-webpack-plugin: ^5.1.3 util: ^0.12.3 - vite-plugin-html: ^3.2.0 - vite-plugin-node-polyfills: ^0.22.0 webpack: ^5.70.0 webpack-dev-server: ^5.0.0 webpack-node-externals: ^3.0.0 @@ -14276,22 +14251,6 @@ __metadata: languageName: node linkType: hard -"@rollup/plugin-inject@npm:^5.0.5": - version: 5.0.5 - resolution: "@rollup/plugin-inject@npm:5.0.5" - dependencies: - "@rollup/pluginutils": ^5.0.1 - estree-walker: ^2.0.2 - magic-string: ^0.30.3 - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - checksum: 22cb772fd6f7178308b2ece95cdde5f8615f6257197832166294552a7e4c0d3976dc996cbfa6470af3151d8b86c00091aa93da5f4db6ec563f11b6db29fd1b63 - languageName: node - linkType: hard - "@rollup/plugin-json@npm:^6.0.0": version: 6.1.0 resolution: "@rollup/plugin-json@npm:6.1.0" @@ -14341,7 +14300,7 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^4.2.0, @rollup/pluginutils@npm:^4.2.1": +"@rollup/pluginutils@npm:^4.2.1": version: 4.2.1 resolution: "@rollup/pluginutils@npm:4.2.1" dependencies: @@ -16803,7 +16762,7 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.5": +"@types/babel__core@npm:^7.1.14": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" dependencies: @@ -19018,21 +18977,6 @@ __metadata: languageName: node linkType: hard -"@vitejs/plugin-react@npm:^4.0.4": - version: 4.3.1 - resolution: "@vitejs/plugin-react@npm:4.3.1" - dependencies: - "@babel/core": ^7.24.5 - "@babel/plugin-transform-react-jsx-self": ^7.24.5 - "@babel/plugin-transform-react-jsx-source": ^7.24.1 - "@types/babel__core": ^7.20.5 - react-refresh: ^0.14.2 - peerDependencies: - vite: ^4.2.0 || ^5.0.0 - checksum: 57872e0193c7e545c5ef4852cbe1adf17a6b35406a2aba4b3acce06c173a9dabbf6ff4c72701abc11bb3cbe24a056f5054f39018f7034c9aa57133a3a7770237 - languageName: node - linkType: hard - "@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1": version: 1.12.1 resolution: "@webassemblyjs/ast@npm:1.12.1" @@ -20647,18 +20591,6 @@ __metadata: languageName: node linkType: hard -"assert@npm:^2.0.0": - version: 2.0.0 - resolution: "assert@npm:2.0.0" - dependencies: - es6-object-assign: ^1.1.0 - is-nan: ^1.2.1 - object-is: ^1.0.1 - util: ^0.12.0 - checksum: bb91f181a86d10588ee16c5e09c280f9811373974c29974cbe401987ea34e966699d7989a812b0e19377b511ea0bc627f5905647ce569311824848ede382cae8 - languageName: node - linkType: hard - "ast-types-flow@npm:^0.0.8": version: 0.0.8 resolution: "ast-types-flow@npm:0.0.8" @@ -21509,15 +21441,6 @@ __metadata: languageName: node linkType: hard -"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.0, browserify-aes@npm:^1.0.4": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" @@ -21679,7 +21602,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.5.0, buffer@npm:^5.7.1": +"buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -21892,7 +21815,7 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": +"call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": version: 1.0.7 resolution: "call-bind@npm:1.0.7" dependencies: @@ -22627,7 +22550,7 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.10, colorette@npm:^2.0.16, colorette@npm:^2.0.20": +"colorette@npm:^2.0.10, colorette@npm:^2.0.20": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 0c016fea2b91b733eb9f4bcdb580018f52c0bc0979443dad930e5037a968237ac53d9beb98e218d2e9235834f8eebce7f8e080422d6194e957454255bde71d3d @@ -22954,13 +22877,6 @@ __metadata: languageName: node linkType: hard -"connect-history-api-fallback@npm:^1.6.0": - version: 1.6.0 - resolution: "connect-history-api-fallback@npm:1.6.0" - checksum: 804ca2be28c999032ecd37a9f71405e5d7b7a4b3defcebbe41077bb8c5a0a150d7b59f51dcc33b2de30bc7e217a31d10f8cfad27e8e74c2fc7655eeba82d6e7e - languageName: node - linkType: hard - "connect-history-api-fallback@npm:^2.0.0": version: 2.0.0 resolution: "connect-history-api-fallback@npm:2.0.0" @@ -22990,7 +22906,7 @@ __metadata: languageName: node linkType: hard -"consola@npm:^2.15.0, consola@npm:^2.15.3": +"consola@npm:^2.15.0": version: 2.15.3 resolution: "consola@npm:2.15.3" checksum: 8ef7a09b703ec67ac5c389a372a33b6dc97eda6c9876443a60d76a3076eea0259e7f67a4e54fd5a52f97df73690822d090cf8b7e102b5761348afef7c6d03e28 @@ -23360,7 +23276,7 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0, create-require@npm:^1.1.1": +"create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" checksum: a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff @@ -23578,7 +23494,7 @@ __metadata: languageName: node linkType: hard -"css-select@npm:^4.1.3, css-select@npm:^4.2.1": +"css-select@npm:^4.1.3": version: 4.3.0 resolution: "css-select@npm:4.3.0" dependencies: @@ -24682,13 +24598,6 @@ __metadata: languageName: node linkType: hard -"domain-browser@npm:^4.22.0": - version: 4.22.0 - resolution: "domain-browser@npm:4.22.0" - checksum: e7ce1c19073e17dec35cfde050a3ddaac437d3ba8b870adabf9d5682e665eab3084df05de432dedf25b34303f0a2c71ac30f1cdba61b1aea018047b10de3d988 - languageName: node - linkType: hard - "domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": version: 2.3.0 resolution: "domelementtype@npm:2.3.0" @@ -24803,14 +24712,7 @@ __metadata: languageName: node linkType: hard -"dotenv-expand@npm:^8.0.2": - version: 8.0.3 - resolution: "dotenv-expand@npm:8.0.3" - checksum: 128ce90ac825b543de3ece0154a51b056ab0dc36bb26d97a68cd0b8707327ecd3c182fb6ac63b26a0fcdfa85064419906a1065cb634f1f9dc08ad311375f1fc0 - languageName: node - linkType: hard - -"dotenv@npm:^16.0.0, dotenv@npm:^16.0.3, dotenv@npm:^16.3.1": +"dotenv@npm:^16.0.3, dotenv@npm:^16.3.1": version: 16.4.5 resolution: "dotenv@npm:16.4.5" checksum: 301a12c3d44fd49888b74eb9ccf9f07a1f5df43f489e7fcb89647a2edcd84c42d6bc349dc8df099cd18f07c35c7b04685c1a4f3e6a6a9e6b30f8d48c15b7f49c @@ -25381,13 +25283,6 @@ __metadata: languageName: node linkType: hard -"es6-object-assign@npm:^1.1.0": - version: 1.1.0 - resolution: "es6-object-assign@npm:1.1.0" - checksum: 8d4fdf63484d78b5c64cacc2c2e1165bc7b6a64b739d2a9db6a4dc8641d99cc9efb433cdd4dc3d3d6b00bfa6ce959694e4665e3255190339945c5f33b692b5d8 - languageName: node - linkType: hard - "es6-symbol@npm:^3.1.1, es6-symbol@npm:^3.1.3": version: 3.1.3 resolution: "es6-symbol@npm:3.1.3" @@ -26211,7 +26106,6 @@ __metadata: "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 - "@vitejs/plugin-react": ^4.0.4 cross-env: ^7.0.0 history: ^5.0.0 react: ^18.0.2 @@ -26219,8 +26113,6 @@ __metadata: react-router: ^6.3.0 react-router-dom: ^6.3.0 react-use: ^17.2.4 - vite-plugin-html: ^3.2.0 - vite-plugin-node-polyfills: ^0.22.0 zen-observable: ^0.10.0 languageName: unknown linkType: soft @@ -26290,7 +26182,6 @@ __metadata: "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 - "@vitejs/plugin-react": ^4.0.4 cross-env: ^7.0.0 history: ^5.0.0 react: ^18.0.2 @@ -26298,8 +26189,6 @@ __metadata: react-router: ^6.3.0 react-router-dom: ^6.3.0 react-use: ^17.2.4 - vite-plugin-html: ^3.2.0 - vite-plugin-node-polyfills: ^0.22.0 zen-observable: ^0.10.0 languageName: unknown linkType: soft @@ -27401,7 +27290,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:10.1.0, fs-extra@npm:^10.0.0, fs-extra@npm:^10.0.1": +"fs-extra@npm:10.1.0, fs-extra@npm:^10.0.0": version: 10.1.0 resolution: "fs-extra@npm:10.1.0" dependencies: @@ -28640,7 +28529,7 @@ __metadata: languageName: node linkType: hard -"html-minifier-terser@npm:^6.0.2, html-minifier-terser@npm:^6.1.0": +"html-minifier-terser@npm:^6.0.2": version: 6.1.0 resolution: "html-minifier-terser@npm:6.1.0" dependencies: @@ -29773,16 +29662,6 @@ __metadata: languageName: node linkType: hard -"is-nan@npm:^1.2.1": - 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-negative-zero@npm:^2.0.3": version: 2.0.3 resolution: "is-negative-zero@npm:2.0.3" @@ -30244,13 +30123,6 @@ __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" @@ -34546,16 +34418,6 @@ __metadata: languageName: node linkType: hard -"node-html-parser@npm:^5.3.3": - version: 5.4.2 - resolution: "node-html-parser@npm:5.4.2" - dependencies: - css-select: ^4.2.1 - he: 1.2.0 - checksum: 2d2391147c83b402786eeab95d23ea4e24ca8608e0e70a2823bfd4f2a248be13a8cc31acfd55a0109e051131e4f0c17d7ada8d999ce70ff2e342ab0110f5da59 - languageName: node - linkType: hard - "node-html-parser@npm:^6.1.1": version: 6.1.5 resolution: "node-html-parser@npm:6.1.5" @@ -34659,41 +34521,6 @@ __metadata: languageName: node linkType: hard -"node-stdlib-browser@npm:^1.2.0": - version: 1.2.0 - resolution: "node-stdlib-browser@npm:1.2.0" - 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.11.0 - 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.0 - util: ^0.12.4 - vm-browserify: ^1.0.1 - checksum: fe491f0839319fd9bb95964c6f7da81fc7fde4c3ac9062aa367f19bc5a6060d0d9e423d3de4196cb51f8259d6aaf6cf380048c48a86eb3721c6223dd0dcc5bfd - languageName: node - linkType: hard - "node-watch@npm:0.7.3": version: 0.7.3 resolution: "node-watch@npm:0.7.3" @@ -35051,7 +34878,7 @@ __metadata: languageName: node linkType: hard -"object-is@npm:^1.0.1, object-is@npm:^1.1.5": +"object-is@npm:^1.1.5": version: 1.1.6 resolution: "object-is@npm:1.1.6" dependencies: @@ -36107,13 +35934,6 @@ __metadata: languageName: node linkType: hard -"pathe@npm:^0.2.0": - version: 0.2.0 - resolution: "pathe@npm:0.2.0" - checksum: 9a8149ce152088f30d15b0b03a7c128ba21f16b4dc1f3f90fe38eee9f6d0f1d6da8e4e47bd2a4f9e14aaac7c30ed01cfc86216479011de2bdc598b65e6f19f41 - languageName: node - linkType: hard - "pause@npm:0.0.1": version: 0.0.1 resolution: "pause@npm:0.0.1" @@ -36432,15 +36252,6 @@ __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" @@ -37453,7 +37264,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^1.2.4, punycode@npm:^1.3.2, punycode@npm:^1.4.1": +"punycode@npm:^1.2.4, punycode@npm:^1.3.2": version: 1.4.1 resolution: "punycode@npm:1.4.1" checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 @@ -37527,7 +37338,7 @@ __metadata: languageName: node linkType: hard -"querystring-es3@npm:^0.2.0, querystring-es3@npm:^0.2.1": +"querystring-es3@npm:^0.2.0": version: 0.2.1 resolution: "querystring-es3@npm:0.2.1" checksum: 691e8d6b8b157e7cd49ae8e83fcf86de39ab3ba948c25abaa94fba84c0986c641aa2f597770848c64abce290ed17a39c9df6df737dfa7e87c3b63acc7d225d61 @@ -38147,7 +37958,7 @@ __metadata: languageName: node linkType: hard -"react-refresh@npm:^0.14.0, react-refresh@npm:^0.14.2": +"react-refresh@npm:^0.14.0": version: 0.14.2 resolution: "react-refresh@npm:0.14.2" checksum: d80db4bd40a36dab79010dc8aa317a5b931f960c0d83c4f3b81f0552cbcf7f29e115b84bb7908ec6a1eb67720fff7023084eff73ece8a7ddc694882478464382 @@ -39064,7 +38875,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:1.22.8, 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.1": +"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.1": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -39100,7 +38911,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.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.1#~builtin": +"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.1#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -40813,7 +40624,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: @@ -40869,18 +40680,6 @@ __metadata: languageName: node linkType: hard -"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.4 - readable-stream: ^3.6.0 - xtend: ^4.0.2 - checksum: c9b78453aeb0c84fcc59555518ac62bacab9fa98e323e7b7666e5f9f58af8f3155e34481078509b02929bd1268427f664d186604cdccee95abc446099b339f83 - languageName: node - linkType: hard - "stream-json@npm:^1.7.4": version: 1.8.0 resolution: "stream-json@npm:1.8.0" @@ -42391,13 +42190,6 @@ __metadata: languageName: node linkType: hard -"tty-browserify@npm:0.0.1": - version: 0.0.1 - resolution: "tty-browserify@npm:0.0.1" - checksum: 93b745d43fa5a7d2b948fa23be8d313576d1d884b48acd957c07710bac1c0d8ac34c0556ad4c57c73d36e11741763ef66b3fb4fb97b06b7e4d525315a3cd45f5 - languageName: node - linkType: hard - "tunnel-agent@npm:^0.6.0": version: 0.6.0 resolution: "tunnel-agent@npm:0.6.0" @@ -43299,7 +43091,7 @@ __metadata: languageName: node linkType: hard -"util@npm:^0.12.0, util@npm:^0.12.3, util@npm:^0.12.4": +"util@npm:^0.12.3": version: 0.12.5 resolution: "util@npm:0.12.5" dependencies: @@ -43545,40 +43337,6 @@ __metadata: languageName: node linkType: hard -"vite-plugin-html@npm:^3.2.0": - version: 3.2.2 - resolution: "vite-plugin-html@npm:3.2.2" - dependencies: - "@rollup/pluginutils": ^4.2.0 - colorette: ^2.0.16 - connect-history-api-fallback: ^1.6.0 - consola: ^2.15.3 - dotenv: ^16.0.0 - dotenv-expand: ^8.0.2 - ejs: ^3.1.6 - fast-glob: ^3.2.11 - fs-extra: ^10.0.1 - html-minifier-terser: ^6.1.0 - node-html-parser: ^5.3.3 - pathe: ^0.2.0 - peerDependencies: - vite: ">=2.0.0" - checksum: 2fd6e1f91f74a4432222ed28e68d5f27e58ccbc9ad44e71ff9d02b684b358b0c634bdb4dd32e9d93d09e88d83c3b7b74b89698e25510bc5b94173cdc067b3ac2 - languageName: node - linkType: hard - -"vite-plugin-node-polyfills@npm:^0.22.0": - version: 0.22.0 - resolution: "vite-plugin-node-polyfills@npm:0.22.0" - dependencies: - "@rollup/plugin-inject": ^5.0.5 - node-stdlib-browser: ^1.2.0 - peerDependencies: - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - checksum: c08d3df0d5cc3102280483d3f7b216f92a18e0708fcb9f67f78f01ab865474254756bacee17caa90b3f46afe5834cb9d8de0dd0e58c1bbbdae1b949edc1e6b57 - languageName: node - linkType: hard - "vm-browserify@npm:^1.0.1": version: 1.1.2 resolution: "vm-browserify@npm:1.1.2" @@ -44356,7 +44114,7 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.0, xtend@npm:^4.0.2": +"xtend@npm:^4.0.0": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a From c7870b5e9d6c9b6c5d23f30a61c0985b03770fbc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:16:47 +0000 Subject: [PATCH 222/317] fix(deps): update dependency prism-react-renderer to v2.4.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 27cb6c7847..949098dc1a 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -9634,14 +9634,14 @@ __metadata: linkType: hard "prism-react-renderer@npm:^2.1.0, prism-react-renderer@npm:^2.3.0": - version: 2.3.1 - resolution: "prism-react-renderer@npm:2.3.1" + version: 2.4.0 + resolution: "prism-react-renderer@npm:2.4.0" dependencies: "@types/prismjs": ^1.26.0 clsx: ^2.0.0 peerDependencies: react: ">=16.0.0" - checksum: b12a7d502c1e764d94f7d3c84aee9cd6fccc676bb7e21dee94d37eb2e7e62e097a343999e1979887cb83a57cbdea48d2046aa74d07bce05caa25f4c296df30b6 + checksum: d15d944a8cbf05f7b04deecd2cf4ffb08229a6027918641b6f046cd8ab24b65ca4ebe4ac8e95a53a7d7cefb1bba8df3ce394fe4f1d75418e34fa92553dc63ef7 languageName: node linkType: hard From 939995b28b85397bfda726ce1c588b70b5fbf655 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:17:09 +0000 Subject: [PATCH 223/317] fix(deps): update dependency sass to v1.78.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 27cb6c7847..2703a28112 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -10444,15 +10444,15 @@ __metadata: linkType: hard "sass@npm:^1.57.1": - version: 1.77.8 - resolution: "sass@npm:1.77.8" + version: 1.78.0 + resolution: "sass@npm:1.78.0" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 6b5dce17faa1bd1e349b4825bf7f76559a32f3f95d789cd2847623c88ee9635e1485d3458532a05fa5b9134cfbce79a4bad3f13dc63c2433632347674db0abae + checksum: ea856bd224c85d831a5800195750c2dd008d101771d071dbaca886c47fe4f131c30c328755d7a974ad944ba5b3aafa7a9f6010952da306436dcebddb41580e1c languageName: node linkType: hard From d29fc1b251571a8b10d2aefeb1ca54e3d1625beb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:27:11 +0000 Subject: [PATCH 224/317] fix(deps): update dependency @module-federation/enhanced to ^0.6.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-11b1a0c.md | 5 + packages/cli/package.json | 2 +- yarn.lock | 245 ++++++++++++++++----------------- 3 files changed, 122 insertions(+), 130 deletions(-) create mode 100644 .changeset/renovate-11b1a0c.md diff --git a/.changeset/renovate-11b1a0c.md b/.changeset/renovate-11b1a0c.md new file mode 100644 index 0000000000..73a955494c --- /dev/null +++ b/.changeset/renovate-11b1a0c.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated dependency `@module-federation/enhanced` to `^0.6.0`. diff --git a/packages/cli/package.json b/packages/cli/package.json index 9d15efe9e4..c304086ac1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -53,7 +53,7 @@ "@backstage/release-manifests": "workspace:^", "@backstage/types": "workspace:^", "@manypkg/get-packages": "^1.1.3", - "@module-federation/enhanced": "^0.3.5", + "@module-federation/enhanced": "^0.6.0", "@octokit/graphql": "^5.0.0", "@octokit/graphql-schema": "^13.7.0", "@octokit/oauth-app": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index 39441760f4..1d9fce15ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3911,7 +3911,7 @@ __metadata: "@backstage/theme": "workspace:^" "@backstage/types": "workspace:^" "@manypkg/get-packages": ^1.1.3 - "@module-federation/enhanced": ^0.3.5 + "@module-federation/enhanced": ^0.6.0 "@octokit/graphql": ^5.0.0 "@octokit/graphql-schema": ^13.7.0 "@octokit/oauth-app": ^4.2.0 @@ -11008,29 +11008,31 @@ __metadata: languageName: node linkType: hard -"@module-federation/bridge-react-webpack-plugin@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.3.5" +"@module-federation/bridge-react-webpack-plugin@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.6.0" dependencies: - "@module-federation/sdk": 0.3.5 - checksum: 9fb0aab200bd63a07c1adcc71e3342c713de4806dccc2b4a8ea2eb19677b2c8753b638906ed48ab49909c753319f33792cb2afd08c0d6a80b4af742d1dc57bd1 + "@module-federation/sdk": 0.6.0 + "@types/semver": 7.5.8 + semver: 7.6.3 + checksum: 650267ba793cdd28349ec82c5316e308ce1cabe4a6831bf8440c324e74ae8f02904fbada183f14b4db27a5bd3ade40104d7b7f0bac8fde7b4620702e63c39df1 languageName: node linkType: hard -"@module-federation/dts-plugin@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/dts-plugin@npm:0.3.5" +"@module-federation/dts-plugin@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/dts-plugin@npm:0.6.0" dependencies: - "@module-federation/managers": 0.3.5 - "@module-federation/sdk": 0.3.5 - "@module-federation/third-party-dts-extractor": 0.3.5 + "@module-federation/managers": 0.6.0 + "@module-federation/sdk": 0.6.0 + "@module-federation/third-party-dts-extractor": 0.6.0 adm-zip: ^0.5.10 ansi-colors: ^4.1.3 - axios: ^1.6.7 + axios: ^1.7.4 chalk: 3.0.0 fs-extra: 9.1.0 isomorphic-ws: 5.0.0 - koa: 2.11.0 + koa: 2.15.3 lodash.clonedeepwith: 4.5.0 log4js: 6.9.1 node-schedule: 2.1.1 @@ -11042,21 +11044,21 @@ __metadata: peerDependenciesMeta: vue-tsc: optional: true - checksum: 8c8e653d0818b3df14ec399d6db0b5784f202c7909112e4884f29c4e268bd9ff6b2d891677776a65ac6204bf4df9cb2f377b7cb8884c1fee98d275d8d2f28f29 + checksum: 959dbd06c1b99f6c91b7ca0af5c0592e66f3e690b6f395b2e3b661f77de006894a9ce7edbd6d52092f4446b66a180c1d57a119f2222119713569c168981ea96b languageName: node linkType: hard -"@module-federation/enhanced@npm:^0.3.5": - version: 0.3.5 - resolution: "@module-federation/enhanced@npm:0.3.5" +"@module-federation/enhanced@npm:^0.6.0": + version: 0.6.0 + resolution: "@module-federation/enhanced@npm:0.6.0" dependencies: - "@module-federation/bridge-react-webpack-plugin": 0.3.5 - "@module-federation/dts-plugin": 0.3.5 - "@module-federation/managers": 0.3.5 - "@module-federation/manifest": 0.3.5 - "@module-federation/rspack": 0.3.5 - "@module-federation/runtime-tools": 0.3.5 - "@module-federation/sdk": 0.3.5 + "@module-federation/bridge-react-webpack-plugin": 0.6.0 + "@module-federation/dts-plugin": 0.6.0 + "@module-federation/managers": 0.6.0 + "@module-federation/manifest": 0.6.0 + "@module-federation/rspack": 0.6.0 + "@module-federation/runtime-tools": 0.6.0 + "@module-federation/sdk": 0.6.0 btoa: ^1.2.1 upath: 2.0.1 peerDependencies: @@ -11070,44 +11072,44 @@ __metadata: optional: true webpack: optional: true - checksum: 40130aa86c1ae2c90cf49360754f4e9688f33900087d376215c14b64d1f596141785eb7794457bb76387bb79725a60bee1271e3911d95183e9379c6454145894 + checksum: 54780a7b0e7faef31bfc21b833767e986f656b550377c01c665656c5398d996db1206b497fdc67ed27839b6e9a6c32343d0b2d56dc3aea2a9c2d75733c6e72da languageName: node linkType: hard -"@module-federation/managers@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/managers@npm:0.3.5" +"@module-federation/managers@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/managers@npm:0.6.0" dependencies: - "@module-federation/sdk": 0.3.5 + "@module-federation/sdk": 0.6.0 find-pkg: 2.0.0 fs-extra: 9.1.0 - checksum: a60a21c2c9bf84fd0911365980e2f0e163485f0a952992e3fec242e8e7a33f0c803c62e1c463fb1326845f0077aef2d45d90eba0f6f69545e60e0ea708ebcaba + checksum: 99314c11df098d7c5eaa3a66a747653c8500a555a2d6ba63232f818ab5356e82ea53dfadea816c7ced0d30957a4ae8864ebbdcf4b51b852e3c6c05f4fea45e95 languageName: node linkType: hard -"@module-federation/manifest@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/manifest@npm:0.3.5" +"@module-federation/manifest@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/manifest@npm:0.6.0" dependencies: - "@module-federation/dts-plugin": 0.3.5 - "@module-federation/managers": 0.3.5 - "@module-federation/sdk": 0.3.5 + "@module-federation/dts-plugin": 0.6.0 + "@module-federation/managers": 0.6.0 + "@module-federation/sdk": 0.6.0 chalk: 3.0.0 find-pkg: 2.0.0 - checksum: 0b0f41802630536a9a90b3e958052d2a1503bdd3927c40e1962e371ee26f2646172ef895b868fd287921a318e0bac5a513ef069517565c445fb659c150e3a257 + checksum: 225e9d52e87a586c002b6b9e3472600c5411a13ed7c2b5d4c645ae7d5c543c75b123a21067ab8ce82e06bdeec9566117753c70850f3d6abc74e5d83ab5478deb languageName: node linkType: hard -"@module-federation/rspack@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/rspack@npm:0.3.5" +"@module-federation/rspack@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/rspack@npm:0.6.0" dependencies: - "@module-federation/bridge-react-webpack-plugin": 0.3.5 - "@module-federation/dts-plugin": 0.3.5 - "@module-federation/managers": 0.3.5 - "@module-federation/manifest": 0.3.5 - "@module-federation/runtime-tools": 0.3.5 - "@module-federation/sdk": 0.3.5 + "@module-federation/bridge-react-webpack-plugin": 0.6.0 + "@module-federation/dts-plugin": 0.6.0 + "@module-federation/managers": 0.6.0 + "@module-federation/manifest": 0.6.0 + "@module-federation/runtime-tools": 0.6.0 + "@module-federation/sdk": 0.6.0 peerDependencies: typescript: ^4.9.0 || ^5.0.0 vue-tsc: ">=1.0.24" @@ -11116,54 +11118,54 @@ __metadata: optional: true vue-tsc: optional: true - checksum: 0afd72784ebbc92ab5d295de1be938cec259f7fcdc59b753af881bbf5b7f982b816c9c04b1ee5e052326556b8a4647001a623c58562c908ee7ffe316f4171df6 + checksum: b30a3c2ff1b2af61259ac14412f7f2d46cebbcf96fc3ffb5c9958a344e3442e98f3a26b0a95da095b80297e2c8f580ce56e26183b0213174aea187b69de440e3 languageName: node linkType: hard -"@module-federation/runtime-tools@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/runtime-tools@npm:0.3.5" +"@module-federation/runtime-tools@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/runtime-tools@npm:0.6.0" dependencies: - "@module-federation/runtime": 0.3.5 - "@module-federation/webpack-bundler-runtime": 0.3.5 - checksum: 5287783b12fff5a37d45cb904d9e004c569bb0803680c133a19002810f7c84f8bff87594dacbc473cdc813788eb8c9feb0915fb2f446ca0f4c609d0be9e33191 + "@module-federation/runtime": 0.6.0 + "@module-federation/webpack-bundler-runtime": 0.6.0 + checksum: d087e0bfd84fb88693669cc0604ae6973e077a6af9c64aac9b230f33ac2a1668629ac829a57dbb2c096dd2a21cbb428300b9d146fac566684ade32eb4db9797b languageName: node linkType: hard -"@module-federation/runtime@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/runtime@npm:0.3.5" +"@module-federation/runtime@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/runtime@npm:0.6.0" dependencies: - "@module-federation/sdk": 0.3.5 - checksum: 61ef8a6535e80d583d5d755d33f4edad0b6e4df6118f38296dbab5eb5f23a624c8ca7ceab8c00d6d7d4249179615cff7235f3554476933aabbc9b988321944ae + "@module-federation/sdk": 0.6.0 + checksum: ee2a37a70d843e17b7153c5864da7692e97e87768bf70d7e30d8ef07e4440a05216957544762e91cfb4a433eb0436139f60ab5b6131937ab62b2cc9fa45ed040 languageName: node linkType: hard -"@module-federation/sdk@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/sdk@npm:0.3.5" - checksum: 80f720af685def16bd80d6104dd7d831df03b1b3eedb11674a95f0e4420dbbaf732d4f619fc5e06421b36549483ab31484ad8a0490ac3033ed85ac300172be7d +"@module-federation/sdk@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/sdk@npm:0.6.0" + checksum: f4905de79548ec92acb4bbb2bffdd7a2e1d0130122e512765c11cd765d6b5db12b141fabfbd2adbf929a2016f9beaa39013d18ce8df404c62c3ad41884ae3d8d languageName: node linkType: hard -"@module-federation/third-party-dts-extractor@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/third-party-dts-extractor@npm:0.3.5" +"@module-federation/third-party-dts-extractor@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/third-party-dts-extractor@npm:0.6.0" dependencies: find-pkg: 2.0.0 fs-extra: 9.1.0 resolve: 1.22.8 - checksum: f7413bee4bf77fa8576dce31580fd91a636d0db6cffa17cd5f485039c5470254410005386f4b4160298b0fced985b9d1768411a64cc96f90a0b73f2e7d1a463f + checksum: c166e9b3946c7947c30055fd194fe730a73d186269cf2c511a864f6d846b55c3788362cc788bfdf31c88d0ca558e76e7548f247a06f60456d961e11c5d3009ab languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:0.3.5": - version: 0.3.5 - resolution: "@module-federation/webpack-bundler-runtime@npm:0.3.5" +"@module-federation/webpack-bundler-runtime@npm:0.6.0": + version: 0.6.0 + resolution: "@module-federation/webpack-bundler-runtime@npm:0.6.0" dependencies: - "@module-federation/runtime": 0.3.5 - "@module-federation/sdk": 0.3.5 - checksum: a4ba483be2ae1a157bcbaf1e895bbbe4c6cfe3039f6403ce543aa5163add4207c69d03f98a2b2910f21fbf61086f56c1f14ff19e44b36bb201a12ddf54ee3491 + "@module-federation/runtime": 0.6.0 + "@module-federation/sdk": 0.6.0 + checksum: f6f5324893610a70e514f38bc0f005649349988ab94c7c61910f0e541eefd3ab6342425ecd8a3a89c05be69253ffd498136f45996f4b8178ebcca7301c0a3b7e languageName: node linkType: hard @@ -18318,7 +18320,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.1.0, @types/semver@npm:^7.3.12, @types/semver@npm:^7.5.0": +"@types/semver@npm:7.5.8, @types/semver@npm:^7.1.0, @types/semver@npm:^7.3.12, @types/semver@npm:^7.5.0": version: 7.5.8 resolution: "@types/semver@npm:7.5.8" checksum: ea6f5276f5b84c55921785a3a27a3cd37afee0111dfe2bcb3e03c31819c197c782598f17f0b150a69d453c9584cd14c4c4d7b9a55d2c5e6cacd4d66fdb3b3663 @@ -20418,7 +20420,7 @@ __metadata: languageName: node linkType: hard -"any-promise@npm:^1.0.0, any-promise@npm:^1.1.0": +"any-promise@npm:^1.0.0": version: 1.3.0 resolution: "any-promise@npm:1.3.0" checksum: 0ee8a9bdbe882c90464d75d1f55cf027f5458650c4bd1f0467e65aec38ccccda07ca5844969ee77ed46d04e7dded3eaceb027e8d32f385688523fe305fa7e1de @@ -21054,7 +21056,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:1.7.4, axios@npm:^1.0.0, axios@npm:^1.4.0, axios@npm:^1.6.0, axios@npm:^1.6.7": +"axios@npm:1.7.4": version: 1.7.4 resolution: "axios@npm:1.7.4" dependencies: @@ -21075,6 +21077,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.0.0, axios@npm:^1.4.0, axios@npm:^1.6.0, axios@npm:^1.7.4": + version: 1.7.7 + resolution: "axios@npm:1.7.7" + dependencies: + follow-redirects: ^1.15.6 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: 882d4fe0ec694a07c7f5c1f68205eb6dc5a62aecdb632cc7a4a3d0985188ce3030e0b277e1a8260ac3f194d314ae342117660a151fabffdc5081ca0b5a8b47fe + languageName: node + linkType: hard + "axobject-query@npm:^4.1.0": version: 4.1.0 resolution: "axobject-query@npm:4.1.0" @@ -23285,13 +23298,13 @@ __metadata: languageName: node linkType: hard -"cookies@npm:~0.8.0": - version: 0.8.0 - resolution: "cookies@npm:0.8.0" +"cookies@npm:~0.9.0": + version: 0.9.1 + resolution: "cookies@npm:0.9.1" dependencies: depd: ~2.0.0 keygrip: ~1.1.0 - checksum: 806055a44f128705265b1bc6a853058da18bf80dea3654ad99be20985b1fa1b14f86c1eef73644aab8071241f8a78acd57202b54c4c5c70769fc694fbb9c4edc + checksum: 213e4d14847b582fbd8a003203d3621a4b9fa792a315c37954e89332d38fac5bcc34ba92ef316ad6d5fe28f0187aaa115927fbbe2080744ad1707a93b4313247 languageName: node linkType: hard @@ -24233,15 +24246,6 @@ __metadata: languageName: node linkType: hard -"debug@npm:~3.1.0": - version: 3.1.0 - resolution: "debug@npm:3.1.0" - dependencies: - ms: 2.0.0 - checksum: 0b52718ab957254a5b3ca07fc34543bc778f358620c206a08452251eb7fc193c3ea3505072acbf4350219c14e2d71ceb7bdaa0d3370aa630b50da790458d08b3 - languageName: node - linkType: hard - "debuglog@npm:^1.0.1": version: 1.0.1 resolution: "debuglog@npm:1.0.1" @@ -24505,7 +24509,7 @@ __metadata: languageName: node linkType: hard -"depd@npm:2.0.0, depd@npm:~2.0.0": +"depd@npm:2.0.0, depd@npm:^2.0.0, depd@npm:~2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a @@ -25312,13 +25316,6 @@ __metadata: languageName: node linkType: hard -"error-inject@npm:^1.0.0": - version: 1.0.0 - resolution: "error-inject@npm:1.0.0" - checksum: 258cb26c7c7e04d9b730d074926ff5e18755b6945781540fdd124cafc5015610d97e4b971eb3226469f407fd34fa899a60fbcf9ade8923ab42fa2a3c61e246cf - languageName: node - linkType: hard - "error-stack-parser@npm:^2.0.6": version: 2.0.6 resolution: "error-stack-parser@npm:2.0.6" @@ -32016,15 +32013,6 @@ __metadata: languageName: node linkType: hard -"koa-compose@npm:^3.0.0": - version: 3.2.1 - resolution: "koa-compose@npm:3.2.1" - dependencies: - any-promise: ^1.1.0 - checksum: ff8e5fc0348455acf751179c6c613eb030a5fac6406d3b49ae9e00460b7ee8770db3ef62633fd3db0306cd4a6d2a0b5152399ebd5bb5e684418f9eeeb251c2de - languageName: node - linkType: hard - "koa-compose@npm:^4.1.0": version: 4.1.0 resolution: "koa-compose@npm:4.1.0" @@ -32032,45 +32020,44 @@ __metadata: languageName: node linkType: hard -"koa-convert@npm:^1.2.0": - version: 1.2.0 - resolution: "koa-convert@npm:1.2.0" +"koa-convert@npm:^2.0.0": + version: 2.0.0 + resolution: "koa-convert@npm:2.0.0" dependencies: co: ^4.6.0 - koa-compose: ^3.0.0 - checksum: a33944dbda4ed87565985f5b37ba1122a012db872724b216b6fd8f9176d4bba42c4a9bf3c129330e45f6474d28f50ca0ed28d41b9bccd2ab5d36d6436cf0d676 + koa-compose: ^4.1.0 + checksum: 7385b3391995f59c1312142e110d5dff677f9850dbfbcf387cd36a7b0af03b5d26e82b811eb9bb008b4f3e661cdab1f8817596e46b1929da2cf6e97a2f7456ed languageName: node linkType: hard -"koa@npm:2.11.0": - version: 2.11.0 - resolution: "koa@npm:2.11.0" +"koa@npm:2.15.3": + version: 2.15.3 + resolution: "koa@npm:2.15.3" dependencies: accepts: ^1.3.5 cache-content-type: ^1.0.0 content-disposition: ~0.5.2 content-type: ^1.0.4 - cookies: ~0.8.0 - debug: ~3.1.0 + cookies: ~0.9.0 + debug: ^4.3.2 delegates: ^1.0.0 - depd: ^1.1.2 + depd: ^2.0.0 destroy: ^1.0.4 encodeurl: ^1.0.2 - error-inject: ^1.0.0 escape-html: ^1.0.3 fresh: ~0.5.2 http-assert: ^1.3.0 http-errors: ^1.6.3 is-generator-function: ^1.0.7 koa-compose: ^4.1.0 - koa-convert: ^1.2.0 + koa-convert: ^2.0.0 on-finished: ^2.3.0 only: ~0.0.2 parseurl: ^1.3.2 statuses: ^1.5.0 type-is: ^1.6.16 vary: ^1.1.2 - checksum: b08e1aea03e70fe4ff6e35dee9f9e979e8608461ee1002f6e8dd72f45fc49404873888ea9a3aab2904e24bf43522df7c601033522f4151189e4055e87f94a979 + checksum: 7c3537443b1a588cf5c3e5554b914ff2bad510323d22b41861d5e0c97d47e9c5997965f303ede8be8bd83d309a4eea1f82cd45d35d6838bc21bb1bb6a90d5d25 languageName: node linkType: hard @@ -40078,6 +40065,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:7.6.3, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 4110ec5d015c9438f322257b1c51fe30276e5f766a3f64c09edd1d7ea7118ecbc3f379f3b69032bacf13116dc7abc4ad8ce0d7e2bd642e26b0d271b56b61a7d8 + languageName: node + linkType: hard + "semver@npm:^5.5.0, semver@npm:^5.6.0": version: 5.7.2 resolution: "semver@npm:5.7.2" @@ -40096,15 +40092,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": - version: 7.6.3 - resolution: "semver@npm:7.6.3" - bin: - semver: bin/semver.js - checksum: 4110ec5d015c9438f322257b1c51fe30276e5f766a3f64c09edd1d7ea7118ecbc3f379f3b69032bacf13116dc7abc4ad8ce0d7e2bd642e26b0d271b56b61a7d8 - languageName: node - linkType: hard - "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" From c891b694c4a7cfa2cc20ca825b68eb27b8134acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Erik=20Bergstr=C3=B6m?= Date: Thu, 5 Sep 2024 15:28:56 +0200 Subject: [PATCH 225/317] feat: add overrideable FavoriteToggle to core-components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carl-Erik Bergström --- .changeset/afraid-boxes-rhyme.md | 9 ++ packages/core-components/api-report.md | 27 ++++++ .../FavoriteToggle/FavoriteToggle.stories.tsx | 68 ++++++++++++++ .../FavoriteToggle/FavoriteToggle.test.tsx | 55 +++++++++++ .../FavoriteToggle/FavoriteToggle.tsx | 92 +++++++++++++++++++ .../src/components/FavoriteToggle/index.ts | 18 ++++ .../core-components/src/components/index.ts | 1 + .../src/overridableComponents.ts | 2 + .../FavoriteEntity/FavoriteEntity.tsx | 25 ++--- .../components/CatalogTable/CatalogTable.tsx | 17 +--- .../StarredEntityListItem.tsx | 19 ++-- .../src/home/components/Tables/actions.tsx | 12 +-- 12 files changed, 289 insertions(+), 56 deletions(-) create mode 100644 .changeset/afraid-boxes-rhyme.md create mode 100644 packages/core-components/src/components/FavoriteToggle/FavoriteToggle.stories.tsx create mode 100644 packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx create mode 100644 packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx create mode 100644 packages/core-components/src/components/FavoriteToggle/index.ts diff --git a/.changeset/afraid-boxes-rhyme.md b/.changeset/afraid-boxes-rhyme.md new file mode 100644 index 0000000000..328a589f7e --- /dev/null +++ b/.changeset/afraid-boxes-rhyme.md @@ -0,0 +1,9 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-home': patch +--- + +Add `FavoriteToggle` in `core-components` to standardise favorite marking diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 597b1a9abf..d04df69af4 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -19,6 +19,7 @@ import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; +import IconButton from '@material-ui/core/IconButton'; import { IconComponent } from '@backstage/core-plugin-api'; import { Icons } from '@material-table/core'; import { IdentityApi } from '@backstage/core-plugin-api'; @@ -401,6 +402,32 @@ export type ErrorPanelProps = { title?: string; }; +// @public +export function FavoriteToggle({ + id, + title, + isFavorite: value, + onToggle: onChange, + ...iconButtonProps +}: FavoriteToggleProps): React_2.JSX.Element; + +// @public +export function FavoriteToggleIcon({ + isFavorite, +}: { + isFavorite: boolean; +}): React_2.JSX.Element; + +// Warning: (ae-missing-release-tag) "FavoriteToggleProps" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type FavoriteToggleProps = ComponentProps & { + id: string; + title: string; + isFavorite: boolean; + onToggle: (value: boolean) => void; +}; + // @public (undocumented) export type FeatureCalloutCircleClassKey = | '@keyframes pulsateSlightly' diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.stories.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.stories.tsx new file mode 100644 index 0000000000..cfe44b8728 --- /dev/null +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.stories.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { FavoriteToggle } from './FavoriteToggle'; +import { + UnifiedThemeProvider, + createBaseThemeOptions, + createUnifiedTheme, + palettes, +} from '@backstage/theme'; + +export default { + title: 'Core/FavoriteToggle', + component: FavoriteToggle, +}; + +export const Default = () => { + const [isFavorite, setFavorite] = React.useState(false); + return ( + + ); +}; + +const theme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.dark, + }), + components: { + BackstageFavoriteToggleIcon: { + styleOverrides: { + icon: () => ({ color: 'aqua' }), + iconBorder: () => ({ color: 'white' }), + }, + }, + }, +}); + +export const WithThemeOverride = () => { + const [isFavorite, setFavorite] = React.useState(false); + return ( + + + + ); +}; diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx new file mode 100644 index 0000000000..d40e3c99b3 --- /dev/null +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx @@ -0,0 +1,55 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { render } from '@testing-library/react'; +import { FavoriteToggle, FavoriteToggleProps } from './FavoriteToggle'; +import React from 'react'; +import userEvent from '@testing-library/user-event'; + +describe('', () => { + const onToggle = jest.fn(); + + const props: FavoriteToggleProps = { + title: 'Favorite this thing', + id: 'some-thing-favorite', + onToggle, + isFavorite: true, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders with valid props', async () => { + const { getByRole } = render(); + + expect(getByRole('button', { name: props.title })).toBeInTheDocument(); + }); + + it('should return inverted value on toggle', async () => { + const { getByRole } = render(); + + await userEvent.click(getByRole('button', { name: props.title })); + expect(onToggle).toHaveBeenCalledWith(!props.isFavorite); + }); + + it('should show accessible tooltip', async () => { + const { findByRole, getByRole } = render(); + + await userEvent.hover(getByRole('button', { name: props.title })); + + expect(await findByRole('tooltip')).toHaveTextContent(props.title); + }); +}); diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx new file mode 100644 index 0000000000..b3b319fddf --- /dev/null +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx @@ -0,0 +1,92 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { ComponentProps } from 'react'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; +import { Theme, makeStyles } from '@material-ui/core/styles'; +import Star from '@material-ui/icons/Star'; +import StarBorder from '@material-ui/icons/StarBorder'; + +const useStyles = makeStyles( + theme => ({ + icon: { + color: '#f3ba37', + cursor: 'pointer', + }, + iconBorder: { + color: theme.palette.text.primary, + cursor: 'pointer', + }, + }), + { name: 'BackstageFavoriteToggleIcon' }, +); + +// @public (undocumented) +export type FavoriteToggleIconClassKey = 'icon' | 'iconBorder'; + +// @public (undocumented) +export type FavoriteToggleProps = ComponentProps & { + id: string; + title: string; + isFavorite: boolean; + onToggle: (value: boolean) => void; +}; + +/** + * Icon used in FavoriteToggle component. + * + * Can be used independently, useful when used as {@link @material-table/core#MaterialTableProps.actions} in {@link @material-table/core#MaterialTable} + * + * @public + */ +export function FavoriteToggleIcon({ isFavorite }: { isFavorite: boolean }) { + const classes = useStyles(); + + return isFavorite ? ( + + ) : ( + + ); +} + +/** + * Toggle encapsulating logic for marking something as favorite, + * primarily used in various instances of entity lists and cards but can be used elsewhere. + * + * This component can only be used in as a controlled toggle and does not keep internal state. + * + * @public + */ +export function FavoriteToggle({ + id, + title, + isFavorite: value, + onToggle: onChange, + ...iconButtonProps +}: FavoriteToggleProps) { + return ( + + onChange(!value)} + {...iconButtonProps} + > + + + + ); +} diff --git a/packages/core-components/src/components/FavoriteToggle/index.ts b/packages/core-components/src/components/FavoriteToggle/index.ts new file mode 100644 index 0000000000..539bb1baf9 --- /dev/null +++ b/packages/core-components/src/components/FavoriteToggle/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { FavoriteToggle, FavoriteToggleIcon } from './FavoriteToggle'; +export type { FavoriteToggleProps } from './FavoriteToggle'; diff --git a/packages/core-components/src/components/index.ts b/packages/core-components/src/components/index.ts index a58208fabb..905893597e 100644 --- a/packages/core-components/src/components/index.ts +++ b/packages/core-components/src/components/index.ts @@ -25,6 +25,7 @@ export * from './DependencyGraph'; export * from './DismissableBanner'; export * from './EmptyState'; export * from './ErrorPanel'; +export * from './FavoriteToggle'; export * from './ResponseErrorPanel'; export * from './FeatureDiscovery'; export * from './HeaderIconLinkRow'; diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index 85e9dc96a8..1e23bd69e1 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -91,6 +91,7 @@ import { BoldHeaderClassKey, CardTabClassKey, } from './layout'; +import { FavoriteToggleIconClassKey } from './components/FavoriteToggle/FavoriteToggle'; type BackstageComponentsNameToClassKey = { BackstageAvatar: AvatarClassKey; @@ -163,6 +164,7 @@ type BackstageComponentsNameToClassKey = { BackstageTabbedCard: TabbedCardClassKey; BackstageTabbedCardBoldHeader: BoldHeaderClassKey; BackstageCardTab: CardTabClassKey; + BackstageFavoriteToggleIcon: FavoriteToggleIconClassKey; }; /** @public */ diff --git a/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx index ab674ff98e..f0d59247ad 100644 --- a/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx +++ b/plugins/catalog-react/src/components/FavoriteEntity/FavoriteEntity.tsx @@ -16,26 +16,17 @@ import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import IconButton from '@material-ui/core/IconButton'; -import Tooltip from '@material-ui/core/Tooltip'; -import { withStyles } from '@material-ui/core/styles'; -import Star from '@material-ui/icons/Star'; -import StarBorder from '@material-ui/icons/StarBorder'; import React, { ComponentProps } from 'react'; import { useStarredEntity } from '../../hooks/useStarredEntity'; import { catalogReactTranslationRef } from '../../translation'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { FavoriteToggle } from '@backstage/core-components'; /** @public */ export type FavoriteEntityProps = ComponentProps & { entity: Entity; }; -const YellowStar = withStyles({ - root: { - color: '#f3ba37', - }, -})(Star); - /** * IconButton for showing if a current entity is starred and adding/removing it from the favorite entities * @param props - MaterialUI IconButton props extended by required `entity` prop @@ -56,16 +47,12 @@ export const FavoriteEntity = (props: FavoriteEntityProps) => { )}`; return ( - toggleStarredEntity()} - > - - {isStarredEntity ? : } - - + /> ); }; diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 1026dc5d26..ac459b83d6 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -35,12 +35,9 @@ import { useStarredEntities, } from '@backstage/plugin-catalog-react'; import Typography from '@material-ui/core/Typography'; -import { withStyles } from '@material-ui/core/styles'; import { visuallyHidden } from '@mui/utils'; import Edit from '@material-ui/icons/Edit'; import OpenInNew from '@material-ui/icons/OpenInNew'; -import Star from '@material-ui/icons/Star'; -import StarBorder from '@material-ui/icons/StarBorder'; import { capitalize } from 'lodash'; import pluralize from 'pluralize'; import React, { ReactNode, useMemo } from 'react'; @@ -50,6 +47,7 @@ import { PaginatedCatalogTable } from './PaginatedCatalogTable'; import { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { catalogTranslationRef } from '../../alpha/translation'; +import { FavoriteToggleIcon } from '@backstage/core-components'; /** * Props for {@link CatalogTable}. @@ -64,12 +62,6 @@ export interface CatalogTableProps { subtitle?: string; } -const YellowStar = withStyles({ - root: { - color: '#f3ba37', - }, -})(Star); - const refCompare = (a: Entity, b: Entity) => { const toRef = (entity: Entity) => entity.metadata.title || @@ -160,12 +152,7 @@ export const CatalogTable = (props: CatalogTableProps) => { return { cellStyle: { paddingLeft: '1em' }, - icon: () => ( - <> - {title} - {isStarred ? : } - - ), + icon: () => , tooltip: title, onClick: () => toggleStarredEntity(entity), }; diff --git a/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx index 579116b9a9..8081e71407 100644 --- a/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx +++ b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx @@ -17,14 +17,12 @@ import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { entityRouteParams } from '@backstage/plugin-catalog-react'; import ListItem from '@material-ui/core/ListItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; -import Tooltip from '@material-ui/core/Tooltip'; -import IconButton from '@material-ui/core/IconButton'; import ListItemText from '@material-ui/core/ListItemText'; import React from 'react'; import { Link } from 'react-router-dom'; import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { useRouteRef } from '@backstage/core-plugin-api'; -import StarIcon from '@material-ui/icons/Star'; +import { FavoriteToggle } from '@backstage/core-components'; type EntityListItemProps = { entity: Entity; @@ -40,15 +38,12 @@ export const StarredEntityListItem = ({ return ( - - onToggleStarredEntity(entity)} - > - - - + onToggleStarredEntity(entity)} + /> diff --git a/plugins/techdocs/src/home/components/Tables/actions.tsx b/plugins/techdocs/src/home/components/Tables/actions.tsx index c95dd8b94c..bc89d76560 100644 --- a/plugins/techdocs/src/home/components/Tables/actions.tsx +++ b/plugins/techdocs/src/home/components/Tables/actions.tsx @@ -17,15 +17,7 @@ import React from 'react'; import ShareIcon from '@material-ui/icons/Share'; import { DocsTableRow } from './types'; -import { withStyles } from '@material-ui/core/styles'; -import Star from '@material-ui/icons/Star'; -import StarBorder from '@material-ui/icons/StarBorder'; - -const YellowStar = withStyles({ - root: { - color: '#f3ba37', - }, -})(Star); +import { FavoriteToggleIcon } from '@backstage/core-components'; /** * Not directly exported, but through DocsTable.actions and EntityListDocsTable.actions @@ -52,7 +44,7 @@ export const actionFactories = { const isStarred = isStarredEntity(entity); return { cellStyle: { paddingLeft: '1em' }, - icon: () => (isStarred ? : ), + icon: () => , tooltip: isStarred ? 'Remove from favorites' : 'Add to favorites', onClick: () => toggleStarredEntity(entity), }; From de76df9c63ec65127fb277fd6db43f89331aff6b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:04:50 +0000 Subject: [PATCH 226/317] chore(deps): update dependency @types/node to v18.19.50 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/yarn.lock b/yarn.lock index c8b71f95be..32702d037a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17675,12 +17675,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=13.7.0, @types/node@npm:^20.1.1, @types/node@npm:^20.11.16": - version: 20.12.7 - resolution: "@types/node@npm:20.12.7" +"@types/node@npm:*, @types/node@npm:>=13.7.0": + version: 22.5.4 + resolution: "@types/node@npm:22.5.4" dependencies: - undici-types: ~5.26.4 - checksum: 7cc979f7e2ca9a339ec71318c3901b9978555257929ef3666987f3e447123bc6dc92afcc89f6347e09e07d602fde7d51bcddea626c23aa2bb74aeaacfd1e1686 + undici-types: ~6.19.2 + checksum: 77ac225c38c428200036780036da0bc6764e2721cfa8f528c7e7da7cfefe01a32a5791e28a54efbeedbc977949058d7db902b2e00139298225d4686cee4ae6db languageName: node linkType: hard @@ -17699,9 +17699,9 @@ __metadata: linkType: hard "@types/node@npm:^16.11.26, @types/node@npm:^16.7.10, @types/node@npm:^16.9.2": - version: 16.18.74 - resolution: "@types/node@npm:16.18.74" - checksum: 33d8f157b51cb2f6907a7019e0bef0733f6ebd025f208863e6e6ea4721348902f2c7ac9eeaadff9f1e138927921e620c7f0a655475a298569d7551f2d28e35f3 + version: 16.18.108 + resolution: "@types/node@npm:16.18.108" + checksum: 5029ce7b0d247690360f2698da4fe765ef5cd5e2409b63b3a45e90bbf7bbaaf3497e769336fb7725be04a221d6e934e3a4616ec7fc205812943def3ea9d70862 languageName: node linkType: hard @@ -17713,11 +17713,20 @@ __metadata: linkType: hard "@types/node@npm:^18.17.15, @types/node@npm:^18.17.8": - version: 18.19.8 - resolution: "@types/node@npm:18.19.8" + version: 18.19.50 + resolution: "@types/node@npm:18.19.50" dependencies: undici-types: ~5.26.4 - checksum: fa291495d6157a9d9393b4c3bdbf1ce12a8f661dc9da6a4fa19bcdb19af1c62bb8dbf7fb66ae135f29cd788b618e9845b83e9c47edcf39f0953a8561fdacd9a3 + checksum: 73bdd2b46fb96816a1f7309e1b609f0832a29739c87df7daa729ff497160be143e02cf18486a0112e1981b092358aed3ca0716b532aff93c7e05f7dbb4f7586a + languageName: node + linkType: hard + +"@types/node@npm:^20.1.1, @types/node@npm:^20.11.16": + version: 20.16.5 + resolution: "@types/node@npm:20.16.5" + dependencies: + undici-types: ~6.19.2 + checksum: f38b7bd8c4993dcf38943afa2ffdd7dfd18fc94f8f3f28d0c1045a10d39871a6cc1b8f8d3bf0c7ed848457d0e1d283482f6ca125579c13fed1b7575d23e8e8f5 languageName: node linkType: hard @@ -42583,6 +42592,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~6.19.2": + version: 6.19.8 + resolution: "undici-types@npm:6.19.8" + checksum: de51f1b447d22571cf155dfe14ff6d12c5bdaec237c765085b439c38ca8518fc360e88c70f99469162bf2e14188a7b0bcb06e1ed2dc031042b984b0bb9544017 + languageName: node + linkType: hard + "undici@npm:^5.28.4": version: 5.28.4 resolution: "undici@npm:5.28.4" From b5c3b405747e00c3be3322665419ef7fe431b0ec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:21:31 +0000 Subject: [PATCH 227/317] fix(deps): update dependency webpack-dev-server to v5.1.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 48 +++++++++++++----------------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/yarn.lock b/yarn.lock index c8b71f95be..d922a63e08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24155,15 +24155,6 @@ __metadata: languageName: node linkType: hard -"default-gateway@npm:^6.0.3": - version: 6.0.3 - resolution: "default-gateway@npm:6.0.3" - dependencies: - execa: ^5.0.0 - checksum: 126f8273ecac8ee9ff91ea778e8784f6cd732d77c3157e8c5bdd6ed03651b5291f71446d05bc02d04073b1e67583604db5394ea3cf992ede0088c70ea15b7378 - languageName: node - linkType: hard - "defaults@npm:^1.0.3": version: 1.0.3 resolution: "defaults@npm:1.0.3" @@ -26495,7 +26486,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.14.0, express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.1, express@npm:^4.18.2, express@npm:^4.19.2": +"express@npm:^4.14.0, express@npm:^4.17.1, express@npm:^4.18.1, express@npm:^4.18.2, express@npm:^4.19.2": version: 4.19.2 resolution: "express@npm:4.19.2" dependencies: @@ -27758,7 +27749,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": +"glob@npm:^10.0.0, glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.4.5 resolution: "glob@npm:10.4.5" dependencies: @@ -39060,17 +39051,6 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^5.0.5": - version: 5.0.5 - resolution: "rimraf@npm:5.0.5" - dependencies: - glob: ^10.3.7 - bin: - rimraf: dist/esm/bin.mjs - checksum: d66eef829b2e23b16445f34e73d75c7b7cf4cbc8834b04720def1c8f298eb0753c3d76df77325fad79d0a2c60470525d95f89c2475283ad985fd7441c32732d1 - languageName: node - linkType: hard - "rimraf@npm:~2.6.2": version: 2.6.3 resolution: "rimraf@npm:2.6.3" @@ -43490,9 +43470,9 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:^7.1.0": - version: 7.2.1 - resolution: "webpack-dev-middleware@npm:7.2.1" +"webpack-dev-middleware@npm:^7.4.2": + version: 7.4.2 + resolution: "webpack-dev-middleware@npm:7.4.2" dependencies: colorette: ^2.0.10 memfs: ^4.6.0 @@ -43505,13 +43485,13 @@ __metadata: peerDependenciesMeta: webpack: optional: true - checksum: bb8c75f7ceabc13ee2c3bc9648190e05a0a8c6d40b940ef72b09ea858a63d16bcb434b49995f1025125a1c3a1c8d40274beb5d26ef2fb1458b19e7f6fe3a91fe + checksum: 39314ec5e4468d177dd61fb51af87ec097e920fe0f0dc101e1bf71796740a7e49fd4f7f939cf91e130232714d6d2fffd948d72dc65dec10f87ac30339929f018 languageName: node linkType: hard "webpack-dev-server@npm:^5.0.0": - version: 5.0.4 - resolution: "webpack-dev-server@npm:5.0.4" + version: 5.1.0 + resolution: "webpack-dev-server@npm:5.1.0" dependencies: "@types/bonjour": ^3.5.13 "@types/connect-history-api-fallback": ^1.5.4 @@ -43526,8 +43506,7 @@ __metadata: colorette: ^2.0.10 compression: ^1.7.4 connect-history-api-fallback: ^2.0.0 - default-gateway: ^6.0.3 - express: ^4.17.3 + express: ^4.19.2 graceful-fs: ^4.2.6 html-entities: ^2.4.0 http-proxy-middleware: ^2.0.3 @@ -43535,14 +43514,13 @@ __metadata: launch-editor: ^2.6.1 open: ^10.0.3 p-retry: ^6.2.0 - rimraf: ^5.0.5 schema-utils: ^4.2.0 selfsigned: ^2.4.1 serve-index: ^1.9.1 sockjs: ^0.3.24 spdy: ^4.0.2 - webpack-dev-middleware: ^7.1.0 - ws: ^8.16.0 + webpack-dev-middleware: ^7.4.2 + ws: ^8.18.0 peerDependencies: webpack: ^5.0.0 peerDependenciesMeta: @@ -43552,7 +43530,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: b3535d01e8d895f4ce6d74b5f76e29398b712476216cd6d459365e5cc2f2fb1e49240aef6c23b2b943b04dbf768d7d18301af3eb064038bde4e11d03c241202d + checksum: 3128fffeb76b97cc4c506607f81bb644437f6961cf310915e22ecaf79a45c185893d7fc8e1844183fb44827061ec2f3d321e937840f02d4989959a09551a8e35 languageName: node linkType: hard @@ -43943,7 +43921,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.14.2, ws@npm:^8.16.0, ws@npm:^8.17.0, ws@npm:^8.8.0": +"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.14.2, ws@npm:^8.17.0, ws@npm:^8.18.0, ws@npm:^8.8.0": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: From 76eaaf3154aafd661abb0c1a1642c69eba45ded1 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 5 Sep 2024 16:31:39 +0200 Subject: [PATCH 228/317] chore: fix api-report warnings Signed-off-by: blam --- packages/core-components/api-report.md | 2 -- .../src/components/FavoriteToggle/FavoriteToggle.tsx | 8 ++++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index d04df69af4..c6ad474357 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -418,8 +418,6 @@ export function FavoriteToggleIcon({ isFavorite: boolean; }): React_2.JSX.Element; -// Warning: (ae-missing-release-tag) "FavoriteToggleProps" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type FavoriteToggleProps = ComponentProps & { id: string; diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx index b3b319fddf..c315efbd68 100644 --- a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx @@ -34,10 +34,14 @@ const useStyles = makeStyles( { name: 'BackstageFavoriteToggleIcon' }, ); -// @public (undocumented) +/** + * @public + */ export type FavoriteToggleIconClassKey = 'icon' | 'iconBorder'; -// @public (undocumented) +/** + * @public + */ export type FavoriteToggleProps = ComponentProps & { id: string; title: string; From 0d91710f44a488e6b87aac97d23586554c0fdd55 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 5 Sep 2024 16:33:24 +0200 Subject: [PATCH 229/317] chrore: export from components Signed-off-by: blam --- packages/core-components/src/components/index.ts | 1 + packages/core-components/src/overridableComponents.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core-components/src/components/index.ts b/packages/core-components/src/components/index.ts index 905893597e..828df3ec03 100644 --- a/packages/core-components/src/components/index.ts +++ b/packages/core-components/src/components/index.ts @@ -48,3 +48,4 @@ export * from './TabbedLayout'; export * from './Table'; export * from './TrendLine'; export * from './WarningPanel'; +export * from './FavoriteToggle'; diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index 1e23bd69e1..06e147cc78 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -61,6 +61,7 @@ import { FiltersContainerClassKey, TableClassKey, WarningPanelClassKey, + FavoriteToggleIconClassKey, } from './components'; import { @@ -91,7 +92,6 @@ import { BoldHeaderClassKey, CardTabClassKey, } from './layout'; -import { FavoriteToggleIconClassKey } from './components/FavoriteToggle/FavoriteToggle'; type BackstageComponentsNameToClassKey = { BackstageAvatar: AvatarClassKey; From ef3c50735c7a4ee93914b746e50372ff29f600b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:35:45 +0000 Subject: [PATCH 230/317] fix(deps): update dependency typescript-json-schema to ^0.65.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-2a3da15.md | 5 +++ packages/config-loader/package.json | 2 +- yarn.lock | 58 ++++++++++++++--------------- 3 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 .changeset/renovate-2a3da15.md diff --git a/.changeset/renovate-2a3da15.md b/.changeset/renovate-2a3da15.md new file mode 100644 index 0000000000..3f612a07ae --- /dev/null +++ b/.changeset/renovate-2a3da15.md @@ -0,0 +1,5 @@ +--- +'@backstage/config-loader': patch +--- + +Updated dependency `typescript-json-schema` to `^0.65.0`. diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 98d233b348..1e40f7a5e4 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -50,7 +50,7 @@ "lodash": "^4.17.21", "minimist": "^1.2.5", "node-fetch": "^2.7.0", - "typescript-json-schema": "^0.63.0", + "typescript-json-schema": "^0.65.0", "yaml": "^2.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 32702d037a..bc2b435d09 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4068,7 +4068,7 @@ __metadata: minimist: ^1.2.5 msw: ^1.0.0 node-fetch: ^2.7.0 - typescript-json-schema: ^0.63.0 + typescript-json-schema: ^0.65.0 yaml: ^2.0.0 zen-observable: ^0.10.0 languageName: unknown @@ -17698,7 +17698,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^16.11.26, @types/node@npm:^16.7.10, @types/node@npm:^16.9.2": +"@types/node@npm:^16.11.26, @types/node@npm:^16.7.10": version: 16.18.108 resolution: "@types/node@npm:16.18.108" checksum: 5029ce7b0d247690360f2698da4fe765ef5cd5e2409b63b3a45e90bbf7bbaaf3497e769336fb7725be04a221d6e934e3a4616ec7fc205812943def3ea9d70862 @@ -17712,7 +17712,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^18.17.15, @types/node@npm:^18.17.8": +"@types/node@npm:^18.11.9, @types/node@npm:^18.17.15, @types/node@npm:^18.17.8": version: 18.19.50 resolution: "@types/node@npm:18.19.50" dependencies: @@ -42412,21 +42412,21 @@ __metadata: languageName: node linkType: hard -"typescript-json-schema@npm:^0.63.0": - version: 0.63.0 - resolution: "typescript-json-schema@npm:0.63.0" +"typescript-json-schema@npm:^0.65.0": + version: 0.65.1 + resolution: "typescript-json-schema@npm:0.65.1" dependencies: "@types/json-schema": ^7.0.9 - "@types/node": ^16.9.2 + "@types/node": ^18.11.9 glob: ^7.1.7 path-equal: ^1.2.5 safe-stable-stringify: ^2.2.0 ts-node: ^10.9.1 - typescript: ~5.1.0 + typescript: ~5.5.0 yargs: ^17.1.1 bin: typescript-json-schema: bin/typescript-json-schema - checksum: 619ab7aece08e140ba9542c6378c335751dbff3994a23343d0af67786a0c1e682d532a436c1674ddb10bca3f34972ecac7ba529b66d0e9b3e00ca81defb3aa77 + checksum: f67af357d3ba7f7953124437f7d36e15ad4171c35b2db945a4b1b3c77114f35328825e9109a3b71150cfc2a1e942da589d956a065eb31eb1dfca6c67fa54e30f languageName: node linkType: hard @@ -42440,16 +42440,6 @@ __metadata: languageName: node linkType: hard -"typescript@npm:~5.1.0": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 - languageName: node - linkType: hard - "typescript@npm:~5.2.0": version: 5.2.2 resolution: "typescript@npm:5.2.2" @@ -42460,6 +42450,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:~5.5.0": + version: 5.5.4 + resolution: "typescript@npm:5.5.4" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: b309040f3a1cd91c68a5a58af6b9fdd4e849b8c42d837b2c2e73f9a4f96a98c4f1ed398a9aab576ee0a4748f5690cf594e6b99dbe61de7839da748c41e6d6ca8 + languageName: node + linkType: hard + "typescript@patch:typescript@~5.0.4#~builtin": version: 5.0.4 resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=b5f058" @@ -42470,16 +42470,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@~5.1.0#~builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be - languageName: node - linkType: hard - "typescript@patch:typescript@~5.2.0#~builtin": version: 5.2.2 resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=f3b441" @@ -42490,6 +42480,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@~5.5.0#~builtin": + version: 5.5.4 + resolution: "typescript@patch:typescript@npm%3A5.5.4#~builtin::version=5.5.4&hash=5adc0c" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: fc52962f31a5bcb716d4213bef516885e4f01f30cea797a831205fc9ef12b405a40561c40eae3127ab85ba1548e7df49df2bcdee6b84a94bfbe3a0d7eff16b14 + languageName: node + linkType: hard + "ua-parser-js@npm:^0.7.30": version: 0.7.33 resolution: "ua-parser-js@npm:0.7.33" From cfeaec21f9d9b2646ede2cfb34f95d623e6954cf Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 5 Sep 2024 16:42:35 +0200 Subject: [PATCH 231/317] chore: small cleanup Signed-off-by: blam --- packages/core-components/api-report.md | 26 ++++++-------- .../FavoriteToggle/FavoriteToggle.test.tsx | 4 +-- .../FavoriteToggle/FavoriteToggle.tsx | 35 +++++++++---------- .../src/components/FavoriteToggle/index.ts | 2 +- 4 files changed, 30 insertions(+), 37 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index c6ad474357..8062278c68 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -403,28 +403,22 @@ export type ErrorPanelProps = { }; // @public -export function FavoriteToggle({ - id, - title, - isFavorite: value, - onToggle: onChange, - ...iconButtonProps -}: FavoriteToggleProps): React_2.JSX.Element; +export function FavoriteToggle( + props: ComponentProps & { + id: string; + title: string; + isFavorite: boolean; + onToggle: (value: boolean) => void; + }, +): React_2.JSX.Element; // @public -export function FavoriteToggleIcon({ - isFavorite, -}: { +export function FavoriteToggleIcon(props: { isFavorite: boolean; }): React_2.JSX.Element; // @public (undocumented) -export type FavoriteToggleProps = ComponentProps & { - id: string; - title: string; - isFavorite: boolean; - onToggle: (value: boolean) => void; -}; +export type FavoriteToggleIconClassKey = 'icon' | 'iconBorder'; // @public (undocumented) export type FeatureCalloutCircleClassKey = diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx index d40e3c99b3..c5ca4c33f2 100644 --- a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx @@ -14,14 +14,14 @@ * limitations under the License. */ import { render } from '@testing-library/react'; -import { FavoriteToggle, FavoriteToggleProps } from './FavoriteToggle'; +import { FavoriteToggle } from './FavoriteToggle'; import React from 'react'; import userEvent from '@testing-library/user-event'; describe('', () => { const onToggle = jest.fn(); - const props: FavoriteToggleProps = { + const props = { title: 'Favorite this thing', id: 'some-thing-favorite', onToggle, diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx index c315efbd68..65124ab05c 100644 --- a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx @@ -39,16 +39,6 @@ const useStyles = makeStyles( */ export type FavoriteToggleIconClassKey = 'icon' | 'iconBorder'; -/** - * @public - */ -export type FavoriteToggleProps = ComponentProps & { - id: string; - title: string; - isFavorite: boolean; - onToggle: (value: boolean) => void; -}; - /** * Icon used in FavoriteToggle component. * @@ -56,7 +46,8 @@ export type FavoriteToggleProps = ComponentProps & { * * @public */ -export function FavoriteToggleIcon({ isFavorite }: { isFavorite: boolean }) { +export function FavoriteToggleIcon(props: { isFavorite: boolean }) { + const { isFavorite } = props; const classes = useStyles(); return isFavorite ? ( @@ -74,13 +65,21 @@ export function FavoriteToggleIcon({ isFavorite }: { isFavorite: boolean }) { * * @public */ -export function FavoriteToggle({ - id, - title, - isFavorite: value, - onToggle: onChange, - ...iconButtonProps -}: FavoriteToggleProps) { +export function FavoriteToggle( + props: ComponentProps & { + id: string; + title: string; + isFavorite: boolean; + onToggle: (value: boolean) => void; + }, +) { + const { + id, + title, + isFavorite: value, + onToggle: onChange, + ...iconButtonProps + } = props; return ( Date: Thu, 5 Sep 2024 16:42:56 +0000 Subject: [PATCH 232/317] fix(deps): update dependency @gitbeaker/rest to v40.1.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index dad34665ac..c6e425a2ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9220,14 +9220,14 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/core@npm:^40.1.3": - version: 40.1.3 - resolution: "@gitbeaker/core@npm:40.1.3" +"@gitbeaker/core@npm:^40.1.4": + version: 40.1.4 + resolution: "@gitbeaker/core@npm:40.1.4" dependencies: - "@gitbeaker/requester-utils": ^40.1.3 + "@gitbeaker/requester-utils": ^40.1.4 qs: ^6.12.2 xcase: ^2.0.1 - checksum: 2bb114b1a9d0d5b0b5c3fed82c0880f8b8d47633605a2913c6f489931ac9be8988e8f6c07bc6e58e526d25b012868e0dbd7b5bee96d6b92658d4220e364ca691 + checksum: 21a3dde1013e78ee2f03cfcdb5a5e4ee270a186b491bf76890799b0687b7afda56213c4e8fe97dfe104e51128a09f4b4c72f1ba3d10ab323413a5398c59dcbe7 languageName: node linkType: hard @@ -9267,15 +9267,15 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/requester-utils@npm:^40.1.3": - version: 40.1.3 - resolution: "@gitbeaker/requester-utils@npm:40.1.3" +"@gitbeaker/requester-utils@npm:^40.1.4": + version: 40.1.4 + resolution: "@gitbeaker/requester-utils@npm:40.1.4" dependencies: picomatch-browser: ^2.2.6 qs: ^6.12.2 rate-limiter-flexible: ^4.0.1 xcase: ^2.0.1 - checksum: fc2e312a1d7e4639b733c1df729d80b1cc2fc3305205ce0f5b6e6e366e95b73f4e3bfd62a26df308d4947151eedb0eeeb30ba64e6d62cca45c1f570a0d05773d + checksum: 4edcb0011b3b305f58c856cbb3a8e09f9f1c32c34179cc222a82192a3e3c9d8c50fae39b7bfe47a16564ba8f2f5138d0a143011d59979b1bf8202db9cb202611 languageName: node linkType: hard @@ -9290,12 +9290,12 @@ __metadata: linkType: hard "@gitbeaker/rest@npm:^40.0.3": - version: 40.1.3 - resolution: "@gitbeaker/rest@npm:40.1.3" + version: 40.1.4 + resolution: "@gitbeaker/rest@npm:40.1.4" dependencies: - "@gitbeaker/core": ^40.1.3 - "@gitbeaker/requester-utils": ^40.1.3 - checksum: 1258f7877481598649122a856b24390188e7f84734538346b1dd413fad896725905ac6ab8c8cf0751e1ef4f7165347405e010dc7a735bae701871a795a885db4 + "@gitbeaker/core": ^40.1.4 + "@gitbeaker/requester-utils": ^40.1.4 + checksum: 8182546ab331af01afe253fb429e5f7fe19a93e889fc5859a72ce4d1a4925d3c083d2461740ed2a9454580f8fef7bdbcf4a375803c1e725b61bafcc879084672 languageName: node linkType: hard From 7dd8011e0e0837243ed40bfd5dc4046488c53c85 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:44:33 +0000 Subject: [PATCH 233/317] chore(deps): update dependency eslint-plugin-notice to v1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 4028f7bacd..02e7c84861 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "cross-env": "^7.0.0", "e2e-test": "workspace:*", "eslint": "^8.6.0", - "eslint-plugin-notice": "^0.9.10", + "eslint-plugin-notice": "^1.0.0", "eslint-plugin-react": "^7.28.0", "eslint-plugin-testing-library": "^6.0.0", "fs-extra": "^11.2.0", diff --git a/yarn.lock b/yarn.lock index dad34665ac..95b452fc32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25722,16 +25722,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-notice@npm:^0.9.10": - version: 0.9.10 - resolution: "eslint-plugin-notice@npm:0.9.10" +"eslint-plugin-notice@npm:^1.0.0": + version: 1.0.0 + resolution: "eslint-plugin-notice@npm:1.0.0" dependencies: find-root: ^1.1.0 - lodash: ^4.17.15 + lodash: ^4.17.21 metric-lcs: ^0.1.2 peerDependencies: eslint: ">=3.0.0" - checksum: 0892ea3da8f31f57390941b6ae3472a34fe4037a21dfe73763c2e5011a08859bff4be6af50d1c54b04e16e43b10342458acb83874b806fcd5e2839be9f99c92e + checksum: 9a745463e89078d08d75cd13ce7d868b73aa3ca12e607bb2d0436512192a6c18398e47abf5e4b624fd8147d89052fe984529592c44c9b2ad836754bbf2cae5e5 languageName: node linkType: hard @@ -39286,7 +39286,7 @@ __metadata: cross-env: ^7.0.0 e2e-test: "workspace:*" eslint: ^8.6.0 - eslint-plugin-notice: ^0.9.10 + eslint-plugin-notice: ^1.0.0 eslint-plugin-react: ^7.28.0 eslint-plugin-testing-library: ^6.0.0 fs-extra: ^11.2.0 From c2b63ab9bc57ae47e3c8816af7f4939578b0d24f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:28:05 +0000 Subject: [PATCH 234/317] chore(deps): update dependency supertest to v7 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-94fd31b.md | 38 +++++++ packages/backend-app-api/package.json | 2 +- packages/backend-common/package.json | 2 +- packages/backend-defaults/package.json | 2 +- packages/backend-openapi-utils/package.json | 2 +- packages/backend-test-utils/package.json | 2 +- plugins/app-backend/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- plugins/auth-backend/package.json | 2 +- plugins/auth-node/package.json | 2 +- plugins/catalog-backend/package.json | 2 +- plugins/devtools-backend/package.json | 2 +- plugins/events-backend/package.json | 2 +- .../example-todo-list-backend/package.json | 2 +- plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes-node/package.json | 2 +- plugins/notifications-backend/package.json | 2 +- plugins/permission-backend/package.json | 2 +- plugins/permission-node/package.json | 2 +- plugins/scaffolder-backend/package.json | 2 +- plugins/search-backend/package.json | 2 +- plugins/signals-backend/package.json | 2 +- plugins/techdocs-backend/package.json | 2 +- plugins/techdocs-node/package.json | 2 +- plugins/user-settings-backend/package.json | 2 +- yarn.lock | 100 +++++++++--------- 37 files changed, 122 insertions(+), 86 deletions(-) create mode 100644 .changeset/renovate-94fd31b.md diff --git a/.changeset/renovate-94fd31b.md b/.changeset/renovate-94fd31b.md new file mode 100644 index 0000000000..6c6883c0d6 --- /dev/null +++ b/.changeset/renovate-94fd31b.md @@ -0,0 +1,38 @@ +--- +'@backstage/backend-app-api': patch +'@backstage/backend-common': patch +'@backstage/backend-defaults': patch +'@backstage/backend-openapi-utils': patch +'@backstage/backend-test-utils': patch +'@backstage/plugin-app-backend': patch +'@backstage/plugin-auth-backend-module-atlassian-provider': patch +'@backstage/plugin-auth-backend-module-bitbucket-provider': patch +'@backstage/plugin-auth-backend-module-github-provider': patch +'@backstage/plugin-auth-backend-module-gitlab-provider': patch +'@backstage/plugin-auth-backend-module-google-provider': patch +'@backstage/plugin-auth-backend-module-microsoft-provider': patch +'@backstage/plugin-auth-backend-module-oauth2-provider': patch +'@backstage/plugin-auth-backend-module-oidc-provider': patch +'@backstage/plugin-auth-backend-module-okta-provider': patch +'@backstage/plugin-auth-backend-module-onelogin-provider': patch +'@backstage/plugin-auth-backend-module-pinniped-provider': patch +'@backstage/plugin-auth-backend-module-vmware-cloud-provider': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-auth-node': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-devtools-backend': patch +'@backstage/plugin-events-backend': patch +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-kubernetes-node': patch +'@backstage/plugin-notifications-backend': patch +'@backstage/plugin-permission-backend': patch +'@backstage/plugin-permission-node': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-search-backend': patch +'@backstage/plugin-signals-backend': patch +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-techdocs-node': patch +'@backstage/plugin-user-settings-backend': patch +--- + +Updated dependency `supertest` to `^7.0.0`. diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 6582a60515..5c7ec141f7 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -96,7 +96,7 @@ "@types/stoppable": "^1.1.0", "http-errors": "^2.0.0", "msw": "^1.0.0", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 1e052c8eba..760abf9b51 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -142,7 +142,7 @@ "http-errors": "^2.0.0", "msw": "^1.0.0", "mysql2": "^3.0.0", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "peerDependencies": { "pg-connection-string": "^2.3.0" diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 2f157580bb..5b848164a9 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -198,7 +198,7 @@ "aws-sdk-client-mock": "^4.0.0", "http-errors": "^2.0.0", "msw": "^1.0.0", - "supertest": "^6.1.3", + "supertest": "^7.0.0", "wait-for-expect": "^3.0.2" }, "configSchema": "config.d.ts" diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 37ae412be6..9332f86a89 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@backstage/cli": "workspace:^", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "files": [ "dist" diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index a84aca9b52..262fd87a93 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -77,7 +77,7 @@ "devDependencies": { "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "peerDependencies": { "@types/jest": "*" diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 4f438a7d9b..a632b3f978 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -84,7 +84,7 @@ "@backstage/types": "workspace:^", "@types/supertest": "^2.0.8", "node-fetch": "^2.7.0", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index 105e69150b..10710ba470 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -45,7 +45,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index e2385ca6ed..877037264d 100644 --- a/plugins/auth-backend-module-bitbucket-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-provider/package.json @@ -45,7 +45,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 08e3acaf8f..479a0838cb 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -43,7 +43,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index dc562c16e8..db3cdc81d7 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -45,7 +45,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index 3f4305d8e5..7fbb49d1f2 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -48,7 +48,7 @@ "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", "@types/passport-google-oauth20": "^2.0.3", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 1a81038994..4ddecd475c 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -49,7 +49,7 @@ "@backstage/plugin-auth-backend": "workspace:^", "@types/passport-microsoft": "^1.0.0", "msw": "^1.0.0", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index f5ad94f1b8..002a8d9206 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -44,7 +44,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 1a1d6c9c8a..f6858e55f2 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -52,7 +52,7 @@ "express-session": "^1.17.3", "jose": "^5.0.0", "msw": "^1.3.1", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 0965e1548d..dacb8b91f4 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -45,7 +45,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index 53e8e646ca..9a283fd50f 100644 --- a/plugins/auth-backend-module-onelogin-provider/package.json +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -45,7 +45,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", - "supertest": "^6.3.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index 932ad93b38..f304672730 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -50,6 +50,6 @@ "jose": "^5.0.0", "msw": "^1.3.0", "passport": "^0.7.0", - "supertest": "^6.3.3" + "supertest": "^7.0.0" } } diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index 2f198ebacb..7880477e09 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -48,6 +48,6 @@ "@backstage/errors": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", "msw": "^2.0.8", - "supertest": "^6.3.3" + "supertest": "^7.0.0" } } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index f090b86f4e..9f1bf8ddad 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -115,7 +115,7 @@ "@types/passport-strategy": "^0.2.35", "@types/xml2js": "^0.4.7", "msw": "^1.0.0", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 23818fcc31..4abed7a275 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -62,7 +62,7 @@ "express-promise-router": "^4.1.1", "lodash": "^4.17.21", "msw": "^1.0.0", - "supertest": "^6.1.3", + "supertest": "^7.0.0", "uuid": "^9.0.0" } } diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index e3a09b65ec..a21b43163c 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -112,7 +112,7 @@ "better-sqlite3": "^11.0.0", "luxon": "^3.0.0", "msw": "^1.0.0", - "supertest": "^6.1.3", + "supertest": "^7.0.0", "wait-for-expect": "^3.0.2", "winston": "^3.13.0" }, diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 4bde07d5f7..e8c18fe53a 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -68,7 +68,7 @@ "@types/ping": "^0.4.1", "@types/supertest": "^2.0.8", "@types/yarnpkg__lockfile": "^1.1.4", - "supertest": "^6.2.4" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index ee057c91fd..3cb228e6fc 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -65,7 +65,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-events-backend-test-utils": "workspace:^", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index cf6e61f6ab..84ebeb3ea1 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -54,6 +54,6 @@ "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "@types/uuid": "^9.0.0", - "supertest": "^6.1.6" + "supertest": "^7.0.0" } } diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index dc42a2b7b2..5eae34ca7f 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -106,7 +106,7 @@ "@backstage/plugin-permission-backend-module-allow-all-policy": "workspace:^", "@types/aws4": "^1.5.1", "msw": "^1.0.0", - "supertest": "^6.1.3", + "supertest": "^7.0.0", "ws": "^8.13.0" }, "configSchema": "config.d.ts" diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index cc6aa3dd65..af7dc3358a 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -54,6 +54,6 @@ "@backstage/cli": "workspace:^", "@backstage/plugin-kubernetes-backend": "workspace:^", "msw": "^1.3.1", - "supertest": "^6.1.3" + "supertest": "^7.0.0" } } diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index d91f0f7aea..b4a35ed76c 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -69,6 +69,6 @@ "@types/express": "^4.17.6", "@types/supertest": "^2.0.8", "msw": "^1.0.0", - "supertest": "^6.2.4" + "supertest": "^7.0.0" } } diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 282af7cc0b..5aa49386d2 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -73,6 +73,6 @@ "@types/lodash": "^4.14.151", "@types/supertest": "^2.0.8", "msw": "^1.0.0", - "supertest": "^6.1.6" + "supertest": "^7.0.0" } } diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 0323950376..257b8e255d 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -73,6 +73,6 @@ "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "msw": "^1.0.0", - "supertest": "^6.1.3" + "supertest": "^7.0.0" } } diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 4017dc760b..5c98d0cab7 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -126,7 +126,7 @@ "@types/zen-observable": "^0.8.0", "esbuild": "^0.23.0", "strip-ansi": "^7.1.0", - "supertest": "^6.1.3", + "supertest": "^7.0.0", "wait-for-expect": "^3.0.2" }, "configSchema": "config.d.ts" diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index c3689b9478..2f3d5e9532 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -80,7 +80,7 @@ "@backstage/cli": "workspace:^", "@backstage/repo-tools": "workspace:^", "@types/supertest": "^2.0.8", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index be39008ad4..b2d2d8afba 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -64,6 +64,6 @@ "@types/supertest": "^2.0.8", "@types/ws": "^8.5.10", "msw": "^1.0.0", - "supertest": "^6.2.4" + "supertest": "^7.0.0" } } diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 468b349f11..3623e003f3 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -86,7 +86,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "msw": "^1.0.0", - "supertest": "^6.1.3" + "supertest": "^7.0.0" }, "configSchema": "config.d.ts" } diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 8fa90e66d8..243c306f19 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -86,6 +86,6 @@ "@types/recursive-readdir": "^2.2.0", "@types/supertest": "^2.0.8", "aws-sdk-client-mock": "^4.0.0", - "supertest": "^6.1.3" + "supertest": "^7.0.0" } } diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index dcbd3dac2d..a730bf1e88 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -71,6 +71,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", - "supertest": "^6.1.3" + "supertest": "^7.0.0" } } diff --git a/yarn.lock b/yarn.lock index c6e425a2ba..abae6349ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3479,7 +3479,7 @@ __metadata: path-to-regexp: ^6.2.1 selfsigned: ^2.0.0 stoppable: ^1.1.0 - supertest: ^6.1.3 + supertest: ^7.0.0 triple-beam: ^1.4.1 uuid: ^9.0.0 winston: ^3.2.1 @@ -3568,7 +3568,7 @@ __metadata: raw-body: ^2.4.1 selfsigned: ^2.0.0 stoppable: ^1.1.0 - supertest: ^6.1.3 + supertest: ^7.0.0 tar: ^6.1.12 triple-beam: ^1.4.1 uuid: ^9.0.0 @@ -3661,7 +3661,7 @@ __metadata: raw-body: ^2.4.1 selfsigned: ^2.0.0 stoppable: ^1.1.0 - supertest: ^6.1.3 + supertest: ^7.0.0 tar: ^6.1.12 triple-beam: ^1.4.1 uuid: ^9.0.0 @@ -3735,7 +3735,7 @@ __metadata: lodash: ^4.17.21 openapi-merge: ^1.3.2 openapi3-ts: ^3.1.2 - supertest: ^6.1.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -3789,7 +3789,7 @@ __metadata: mysql2: ^3.0.0 pg: ^8.11.3 pg-connection-string: ^2.3.0 - supertest: ^6.1.3 + supertest: ^7.0.0 testcontainers: ^10.0.0 textextensions: ^5.16.0 uuid: ^9.0.0 @@ -4714,7 +4714,7 @@ __metadata: lodash: ^4.17.21 luxon: ^3.0.0 node-fetch: ^2.7.0 - supertest: ^6.1.3 + supertest: ^7.0.0 yn: ^4.0.0 languageName: unknown linkType: soft @@ -4791,7 +4791,7 @@ __metadata: express: ^4.18.2 passport: ^0.7.0 passport-atlassian-oauth2: ^2.1.0 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -4846,7 +4846,7 @@ __metadata: express: ^4.18.2 passport: ^0.7.0 passport-bitbucket-oauth2: ^0.1.2 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -4898,7 +4898,7 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" passport-github2: ^0.1.12 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -4915,7 +4915,7 @@ __metadata: express: ^4.18.2 passport: ^0.7.0 passport-gitlab2: ^5.0.0 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -4931,7 +4931,7 @@ __metadata: "@types/passport-google-oauth20": ^2.0.3 google-auth-library: ^9.0.0 passport-google-oauth20: ^2.0.0 - supertest: ^6.1.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -4969,7 +4969,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.7.0 passport-microsoft: ^1.0.0 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -4985,7 +4985,7 @@ __metadata: "@backstage/plugin-auth-node": "workspace:^" passport: ^0.7.0 passport-oauth2: ^1.6.1 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -5022,7 +5022,7 @@ __metadata: msw: ^1.3.1 openid-client: ^5.5.0 passport: ^0.7.0 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -5039,7 +5039,7 @@ __metadata: "@davidzemon/passport-okta-oauth": ^0.0.5 express: ^4.18.2 passport: ^0.7.0 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -5056,7 +5056,7 @@ __metadata: express: ^4.18.2 passport: ^0.7.0 passport-onelogin-oauth: ^0.0.1 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -5079,7 +5079,7 @@ __metadata: msw: ^1.3.0 openid-client: ^5.4.3 passport: ^0.7.0 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -5100,7 +5100,7 @@ __metadata: jose: ^5.0.0 msw: ^2.0.8 passport-oauth2: ^1.6.1 - supertest: ^6.3.3 + supertest: ^7.0.0 languageName: unknown linkType: soft @@ -5175,7 +5175,7 @@ __metadata: passport-microsoft: ^1.0.0 passport-oauth2: ^1.6.1 passport-onelogin-oauth: ^0.0.1 - supertest: ^6.1.3 + supertest: ^7.0.0 uuid: ^9.0.0 winston: ^3.2.1 yn: ^4.0.0 @@ -5205,7 +5205,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.7.0 passport: ^0.7.0 - supertest: ^6.1.3 + supertest: ^7.0.0 uuid: ^9.0.0 winston: ^3.2.1 zod: ^3.22.4 @@ -5688,7 +5688,7 @@ __metadata: node-fetch: ^2.7.0 p-limit: ^3.0.2 prom-client: ^15.0.0 - supertest: ^6.1.3 + supertest: ^7.0.0 uuid: ^9.0.0 wait-for-expect: ^3.0.2 winston: ^3.13.0 @@ -6033,7 +6033,7 @@ __metadata: node-fetch: ^2.7.0 ping: ^0.4.1 semver: ^7.5.3 - supertest: ^6.2.4 + supertest: ^7.0.0 yn: ^4.0.0 languageName: unknown linkType: soft @@ -6180,7 +6180,7 @@ __metadata: "@types/express": ^4.17.6 express: ^4.17.1 express-promise-router: ^4.1.0 - supertest: ^6.1.3 + supertest: ^7.0.0 winston: ^3.2.1 languageName: unknown linkType: soft @@ -6307,7 +6307,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.7.0 stream-buffers: ^3.0.2 - supertest: ^6.1.3 + supertest: ^7.0.0 winston: ^3.2.1 ws: ^8.13.0 yn: ^4.0.0 @@ -6380,7 +6380,7 @@ __metadata: "@kubernetes/client-node": ^0.20.0 msw: ^1.3.1 node-fetch: ^2.7.0 - supertest: ^6.1.3 + supertest: ^7.0.0 winston: ^3.2.1 languageName: unknown linkType: soft @@ -6516,7 +6516,7 @@ __metadata: knex: ^3.0.0 msw: ^1.0.0 node-fetch: ^2.7.0 - supertest: ^6.2.4 + supertest: ^7.0.0 uuid: ^9.0.0 winston: ^3.2.1 yn: ^4.0.0 @@ -6691,7 +6691,7 @@ __metadata: lodash: ^4.17.21 msw: ^1.0.0 node-fetch: ^2.7.0 - supertest: ^6.1.6 + supertest: ^7.0.0 yn: ^4.0.0 zod: ^3.22.4 languageName: unknown @@ -6730,7 +6730,7 @@ __metadata: express: ^4.17.1 express-promise-router: ^4.1.0 msw: ^1.0.0 - supertest: ^6.1.3 + supertest: ^7.0.0 zod: ^3.22.4 zod-to-json-schema: ^3.20.4 languageName: unknown @@ -7142,7 +7142,7 @@ __metadata: p-queue: ^6.6.2 prom-client: ^15.0.0 strip-ansi: ^7.1.0 - supertest: ^6.1.3 + supertest: ^7.0.0 tar: ^6.1.12 triple-beam: ^1.4.1 uuid: ^9.0.0 @@ -7501,7 +7501,7 @@ __metadata: express: ^4.17.1 lodash: ^4.17.21 qs: ^6.10.1 - supertest: ^6.1.3 + supertest: ^7.0.0 yn: ^4.0.0 zod: ^3.22.4 languageName: unknown @@ -7611,7 +7611,7 @@ __metadata: http-proxy-middleware: ^2.0.0 msw: ^1.0.0 node-fetch: ^2.7.0 - supertest: ^6.2.4 + supertest: ^7.0.0 uuid: ^9.0.0 winston: ^3.2.1 ws: ^8.17.0 @@ -7736,7 +7736,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.7.0 p-limit: ^3.1.0 - supertest: ^6.1.3 + supertest: ^7.0.0 winston: ^3.2.1 languageName: unknown linkType: soft @@ -7817,7 +7817,7 @@ __metadata: mime-types: ^2.1.27 p-limit: ^3.1.0 recursive-readdir: ^2.2.2 - supertest: ^6.1.3 + supertest: ^7.0.0 winston: ^3.2.1 languageName: unknown linkType: soft @@ -7919,7 +7919,7 @@ __metadata: express: ^4.17.1 express-promise-router: ^4.1.0 knex: ^3.0.0 - supertest: ^6.1.3 + supertest: ^7.0.0 yn: ^4.0.0 languageName: unknown linkType: soft @@ -9899,7 +9899,7 @@ __metadata: "@types/uuid": ^9.0.0 express: ^4.17.1 express-promise-router: ^4.1.0 - supertest: ^6.1.6 + supertest: ^7.0.0 uuid: ^9.0.0 yn: ^4.0.0 languageName: unknown @@ -27222,15 +27222,14 @@ __metadata: languageName: node linkType: hard -"formidable@npm:^2.1.2": - version: 2.1.2 - resolution: "formidable@npm:2.1.2" +"formidable@npm:^3.5.1": + version: 3.5.1 + resolution: "formidable@npm:3.5.1" dependencies: dezalgo: ^1.0.4 hexoid: ^1.0.0 once: ^1.4.0 - qs: ^6.11.0 - checksum: 81c8e5d89f5eb873e992893468f0de22c01678ca3d315db62be0560f9de1c77d4faefc9b1f4575098eb2263b3c81ba1024833a9fc3206297ddbac88a4f69b7a8 + checksum: 46b21496f9f985161cf7636163147b6728f9997c7e1d59433680d92619758bf6862330e6d105b5816bafcd1ab32f27ef183455991f93ef836ea731c68db62af9 languageName: node linkType: hard @@ -41154,31 +41153,30 @@ __metadata: languageName: node linkType: hard -"superagent@npm:^8.1.2": - version: 8.1.2 - resolution: "superagent@npm:8.1.2" +"superagent@npm:^9.0.1": + version: 9.0.2 + resolution: "superagent@npm:9.0.2" dependencies: component-emitter: ^1.3.0 cookiejar: ^2.1.4 debug: ^4.3.4 fast-safe-stringify: ^2.1.1 form-data: ^4.0.0 - formidable: ^2.1.2 + formidable: ^3.5.1 methods: ^1.1.2 mime: 2.6.0 qs: ^6.11.0 - semver: ^7.3.8 - checksum: f3601c5ccae34d5ba684a03703394b5d25931f4ae2e1e31a1de809f88a9400e997ece037f9accf148a21c408f950dc829db1e4e23576a7f9fe0efa79fd5c9d2f + checksum: f471461b21f034d844fd0aca332128d61e3afb75c2ee5950f3339f2a3b5ca8b23e2861224f19ad9b43f21c9184d28b7d9384af5a4fde64fdef479efdb15036db languageName: node linkType: hard -"supertest@npm:^6.1.3, supertest@npm:^6.1.6, supertest@npm:^6.2.4, supertest@npm:^6.3.3": - version: 6.3.4 - resolution: "supertest@npm:6.3.4" +"supertest@npm:^7.0.0": + version: 7.0.0 + resolution: "supertest@npm:7.0.0" dependencies: methods: ^1.1.2 - superagent: ^8.1.2 - checksum: 875c6fa7940f21e5be9bb646579cdb030d4057bf2da643e125e1f0480add1200395d2b17e10b8e54e1009efc63e047422501e9eb30e12828668498c0910f295f + superagent: ^9.0.1 + checksum: 974743aa511ec0f387135dfca05e378f6202366c81f0850dfbcc2c3d6fc690e856dda27e175c70db38510e21d87f331c0f62e1a942afea4c447953c647c26c8b languageName: node linkType: hard From 54a8c86aacd4d4fe2375a306577c6aca8dab73d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:24:22 +0000 Subject: [PATCH 235/317] chore(deps): update docker/build-push-action action to v6 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/deploy_docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 86db3795bc..9da484a497 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -61,7 +61,7 @@ jobs: uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - name: Build and push - uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: context: './example-app' file: ./example-app/packages/backend/Dockerfile From da86ac28b2ed1052914e7ea354dd064dc1ea35b6 Mon Sep 17 00:00:00 2001 From: darylgraham Date: Wed, 4 Sep 2024 09:36:27 +0000 Subject: [PATCH 236/317] Add changeset Signed-off-by: darylgraham --- .changeset/seven-beds-deliver.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/seven-beds-deliver.md diff --git a/.changeset/seven-beds-deliver.md b/.changeset/seven-beds-deliver.md new file mode 100644 index 0000000000..dcc74fb889 --- /dev/null +++ b/.changeset/seven-beds-deliver.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Update README to clarify location of additional tabs example From 523556bc364794c783a5ffd029d1407c500e7328 Mon Sep 17 00:00:00 2001 From: Stephen Glass Date: Thu, 5 Sep 2024 19:09:58 -0400 Subject: [PATCH 237/317] update favorite toggle icons Signed-off-by: Stephen Glass --- .changeset/happy-bags-invite.md | 7 +------ .../components/FavoriteToggle/FavoriteToggle.tsx | 15 +++++++++------ packages/theme/api-report.md | 9 --------- packages/theme/src/base/palettes.ts | 6 ------ packages/theme/src/base/types.ts | 3 --- .../src/components/CatalogTable/CatalogTable.tsx | 2 -- 6 files changed, 10 insertions(+), 32 deletions(-) diff --git a/.changeset/happy-bags-invite.md b/.changeset/happy-bags-invite.md index ba8f3e846e..e97300150b 100644 --- a/.changeset/happy-bags-invite.md +++ b/.changeset/happy-bags-invite.md @@ -2,13 +2,8 @@ '@backstage/core-components': patch '@backstage/app-defaults': patch '@backstage/plugin-catalog-react': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-catalog': patch -'@backstage/theme': patch -'@backstage/plugin-home': patch '@backstage/core-app-api': patch '@backstage/test-utils': patch --- -- Allow custom star icons to be provided via the `star` and `unstarred` icon overides. See how to override existing icons in the [Backstage documentation](https://backstage.io/docs/getting-started/app-custom-theme/#custom-icons). -- Add `entityStarButton.color` theme option to allow custom themes to specify color for filled star icons. +Allow custom star icons to be provided via the `star` and `unstarred` icon overides. See how to override existing icons in the [Backstage documentation](https://backstage.io/docs/getting-started/app-custom-theme/#custom-icons). diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx index 65124ab05c..8a517d27ea 100644 --- a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.tsx @@ -16,9 +16,9 @@ import React, { ComponentProps } from 'react'; import IconButton from '@material-ui/core/IconButton'; import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; import { Theme, makeStyles } from '@material-ui/core/styles'; -import Star from '@material-ui/icons/Star'; -import StarBorder from '@material-ui/icons/StarBorder'; +import { StarIcon, UnstarredIcon } from '../../icons'; const useStyles = makeStyles( theme => ({ @@ -50,10 +50,13 @@ export function FavoriteToggleIcon(props: { isFavorite: boolean }) { const { isFavorite } = props; const classes = useStyles(); - return isFavorite ? ( - - ) : ( - + return ( + + {isFavorite ? : } + ); } diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index 4c6a8c1b5d..988569ece7 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -81,9 +81,6 @@ export type BackstagePaletteAdditions = { closeButtonColor?: string; warning?: string; }; - entityStarButton: { - color: string; - }; }; // @public @deprecated @@ -313,9 +310,6 @@ export const palettes: { tabbar: { indicator: string; }; - entityStarButton: { - color: string; - }; }; dark: { type: 'dark'; @@ -390,9 +384,6 @@ export const palettes: { tabbar: { indicator: string; }; - entityStarButton: { - color: string; - }; }; }; diff --git a/packages/theme/src/base/palettes.ts b/packages/theme/src/base/palettes.ts index 55d21a060d..5e70afd7b8 100644 --- a/packages/theme/src/base/palettes.ts +++ b/packages/theme/src/base/palettes.ts @@ -89,9 +89,6 @@ export const palettes = { tabbar: { indicator: '#9BF0E1', }, - entityStarButton: { - color: '#F3BA37', - }, }, dark: { type: 'dark' as const, @@ -166,8 +163,5 @@ export const palettes = { tabbar: { indicator: '#9BF0E1', }, - entityStarButton: { - color: '#F3BA37', - }, }, }; diff --git a/packages/theme/src/base/types.ts b/packages/theme/src/base/types.ts index 4872ee8042..d62d944741 100644 --- a/packages/theme/src/base/types.ts +++ b/packages/theme/src/base/types.ts @@ -82,9 +82,6 @@ export type BackstagePaletteAdditions = { closeButtonColor?: string; warning?: string; }; - entityStarButton: { - color: string; - }; }; /** diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 38f50d9eb1..ac459b83d6 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -23,8 +23,6 @@ import { } from '@backstage/catalog-model'; import { CodeSnippet, - StarIcon, - UnstarredIcon, Table, TableColumn, TableProps, From 9dffc6cc2ef66d3ce4d0c0736ee83b12411859f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 23:21:23 +0000 Subject: [PATCH 238/317] fix(deps): update dependency @uiw/react-codemirror to v4.23.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index c6e425a2ba..c6af2a0a63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18735,9 +18735,9 @@ __metadata: languageName: node linkType: hard -"@uiw/codemirror-extensions-basic-setup@npm:4.23.0": - version: 4.23.0 - resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.23.0" +"@uiw/codemirror-extensions-basic-setup@npm:4.23.1": + version: 4.23.1 + resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.23.1" dependencies: "@codemirror/autocomplete": ^6.0.0 "@codemirror/commands": ^6.0.0 @@ -18754,19 +18754,19 @@ __metadata: "@codemirror/search": ">=6.0.0" "@codemirror/state": ">=6.0.0" "@codemirror/view": ">=6.0.0" - checksum: bc48d3aa82dc0b7cf9724c2806fe013423448135b38ab7d2472eb1df80d3297d75f4a45e3dbd817cb32423ebc57169a46505598e185d351ad819b59f8887c539 + checksum: 391ee4bcbcf959e637ac2e8f30231105b49708f8b78dee72b1d5dbde9df6241845a22b0d0e9077a3af861752d1d5ea20a121fd8b0718c3701c707648d63a2fda languageName: node linkType: hard "@uiw/react-codemirror@npm:^4.9.3": - version: 4.23.0 - resolution: "@uiw/react-codemirror@npm:4.23.0" + version: 4.23.1 + resolution: "@uiw/react-codemirror@npm:4.23.1" dependencies: "@babel/runtime": ^7.18.6 "@codemirror/commands": ^6.1.0 "@codemirror/state": ^6.1.1 "@codemirror/theme-one-dark": ^6.0.0 - "@uiw/codemirror-extensions-basic-setup": 4.23.0 + "@uiw/codemirror-extensions-basic-setup": 4.23.1 codemirror: ^6.0.0 peerDependencies: "@babel/runtime": ">=7.11.0" @@ -18776,7 +18776,7 @@ __metadata: codemirror: ">=6.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: b1341dee1f0257c1fd62bd3b356a207ff2bb72b9eb509c5c52cabf87b8aa23b4dac812ebb25ad7b84cddf58d8674b09842bce3c763bb63a9f8cc1d731d7de0be + checksum: 4366fcac6498abc5d2c385aec6f05125f568fb36b122a51ef89cb578db4bf028a981bf16d4090a3c1c4131872489cca84ebb865c551adbe3ac5fa75dc962a9f4 languageName: node linkType: hard From e66846dc126024c2adcb80ea0e0bb662e6bad1ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 23:21:47 +0000 Subject: [PATCH 239/317] chore(deps): update dependency @playwright/test to v1.47.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index c6e425a2ba..b9c5cfb536 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13436,13 +13436,13 @@ __metadata: linkType: hard "@playwright/test@npm:^1.32.3": - version: 1.46.1 - resolution: "@playwright/test@npm:1.46.1" + version: 1.47.0 + resolution: "@playwright/test@npm:1.47.0" dependencies: - playwright: 1.46.1 + playwright: 1.47.0 bin: playwright: cli.js - checksum: 180d90c6421ee6a8c00a1ae65d91d5f69767e2de95c3f5582ac2bdf3a388e621fdd3ee00ecb7836db427bb0d4cee58111c14f133cb62150357d8e4f43e3e2295 + checksum: 4f5a921c57d2577c64b0bc03d4b97937a6f853eee6afb58f2fc8fb140147b7f33800afe490c9ba3af515d5feb104f65319663931ec1ee75e10f001c1d69b138c languageName: node linkType: hard @@ -36267,27 +36267,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.46.1": - version: 1.46.1 - resolution: "playwright-core@npm:1.46.1" +"playwright-core@npm:1.47.0": + version: 1.47.0 + resolution: "playwright-core@npm:1.47.0" bin: playwright-core: cli.js - checksum: 99a03f97d76af02b0565aa09758eb03427e13497ef7e9e9044fd1184a5f050e7545a517dd7ad8988a68b3ea76e2ba0d411f81e410b1cb29f5ac9161c49689822 + checksum: 64c6adae3ba76cd9f05e6569b1350a6e6990fd8236bb17d1f241b49bea68592a4f4390adb38863d2ee8ef5124e9cb77ff50b9076f8ef3a0890b2a37f87fb842a languageName: node linkType: hard -"playwright@npm:1.46.1": - version: 1.46.1 - resolution: "playwright@npm:1.46.1" +"playwright@npm:1.47.0": + version: 1.47.0 + resolution: "playwright@npm:1.47.0" dependencies: fsevents: 2.3.2 - playwright-core: 1.46.1 + playwright-core: 1.47.0 dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 9e721cb27f919cd92ea8c7461f4a8e97e32eade9da036baa4747f483549e33a8d77585f51e228d6639a1e28a76b0c1d59ca0a05dbd07aa3cfe427be7aaeb9ec8 + checksum: e055fee751035f306a636267d0e080f08d4a523a6c1e3ebb01c91c18838c29bb9165ec3e63eb706798886fa2715f272dec908a7e2e1abcd7a55984688dbac2db languageName: node linkType: hard From e03455c6e08e15264a079eb60103a4bb6feca65d Mon Sep 17 00:00:00 2001 From: Stephen Glass Date: Thu, 5 Sep 2024 19:34:51 -0400 Subject: [PATCH 240/317] fix favorite toggle test to render in test app Signed-off-by: Stephen Glass --- .../components/FavoriteToggle/FavoriteToggle.test.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx index c5ca4c33f2..66b420b467 100644 --- a/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx +++ b/packages/core-components/src/components/FavoriteToggle/FavoriteToggle.test.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { render } from '@testing-library/react'; +import { renderInTestApp } from '@backstage/test-utils'; import { FavoriteToggle } from './FavoriteToggle'; import React from 'react'; import userEvent from '@testing-library/user-event'; @@ -33,20 +33,22 @@ describe('', () => { }); it('renders with valid props', async () => { - const { getByRole } = render(); + const { getByRole } = await renderInTestApp(); expect(getByRole('button', { name: props.title })).toBeInTheDocument(); }); it('should return inverted value on toggle', async () => { - const { getByRole } = render(); + const { getByRole } = await renderInTestApp(); await userEvent.click(getByRole('button', { name: props.title })); expect(onToggle).toHaveBeenCalledWith(!props.isFavorite); }); it('should show accessible tooltip', async () => { - const { findByRole, getByRole } = render(); + const { findByRole, getByRole } = await renderInTestApp( + , + ); await userEvent.hover(getByRole('button', { name: props.title })); From 36fc9dd7a03e2399a84da9ec39001ac12ac6294e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 01:03:09 +0000 Subject: [PATCH 241/317] fix(deps): update dependency @google-cloud/firestore to v7.10.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index bdd64437b7..a83a90a13a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9309,14 +9309,15 @@ __metadata: linkType: hard "@google-cloud/firestore@npm:^7.0.0": - version: 7.9.0 - resolution: "@google-cloud/firestore@npm:7.9.0" + version: 7.10.0 + resolution: "@google-cloud/firestore@npm:7.10.0" dependencies: + "@opentelemetry/api": ^1.3.0 fast-deep-equal: ^3.1.1 functional-red-black-tree: ^1.0.1 google-gax: ^4.3.3 protobufjs: ^7.2.6 - checksum: 5d0657736f1e364cd110ea6083eb8b6aeb1605ea409ea5478d5c8430bfa568c62d472b41e892d0849dfb405b06d8aba412fd57a5d1e807429ba5264c48eb2673 + checksum: c534e49c6b52b7b46366eba009e3f56e8ee10ca0501896a7794470cab32513c6a314f364ff5a3fe5a44b832ba39d1afc86d38b42be3826a615c0a59108185c7d languageName: node linkType: hard From 639d401c888a26d30dd6f40299519508aa847296 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 02:08:28 +0000 Subject: [PATCH 242/317] fix(deps): update dependency express-openapi-validator to v5.3.5 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a83a90a13a..91eae312e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26427,8 +26427,8 @@ __metadata: linkType: hard "express-openapi-validator@npm:^5.0.4": - version: 5.3.4 - resolution: "express-openapi-validator@npm:5.3.4" + version: 5.3.5 + resolution: "express-openapi-validator@npm:5.3.5" dependencies: "@apidevtools/json-schema-ref-parser": ^11.7.0 "@types/multer": ^1.4.11 @@ -26445,7 +26445,7 @@ __metadata: path-to-regexp: ^6.2.2 peerDependencies: express: "*" - checksum: 0c699e8f12d499d759e11c08009cc479d8748bb1adf9e31b72e7f1c0d56d6c6c2333e72124517724cb1d6c7bd428a1a6dfe241b15fbe16f513f373222baf2d77 + checksum: 325636c456790ee909509ca43b90b4083436cbdca2cb4e3a2e959cb4f7b1ccd9af0ddf60bb588c31f005d05bdb2e7ad12de5ee5277e62462570bb09b3597b7ac languageName: node linkType: hard From dbbd93ec7f05b8ea7fd95cd8ded0353594754fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 5 Sep 2024 16:38:59 +0200 Subject: [PATCH 243/317] fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/quick-poets-relax.md | 6 + .../src/components/Table/Table.tsx | 64 +++-- plugins/search-react/api-report.md | 13 +- .../src/components/SearchBar/SearchBar.tsx | 254 +++++++++--------- 4 files changed, 177 insertions(+), 160 deletions(-) create mode 100644 .changeset/quick-poets-relax.md diff --git a/.changeset/quick-poets-relax.md b/.changeset/quick-poets-relax.md new file mode 100644 index 0000000000..159744233b --- /dev/null +++ b/.changeset/quick-poets-relax.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-search-react': patch +--- + +Internal update to match recent React types diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index 4fb7edf076..e0bd3541f5 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -63,27 +63,57 @@ import { TableLoadingBody } from './TableLoadingBody'; // Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51 const tableIcons: Icons = { - Add: forwardRef((props, ref) => ), - Check: forwardRef((props, ref) => ), - Clear: forwardRef((props, ref) => ), - Delete: forwardRef((props, ref) => ), - DetailPanel: forwardRef((props, ref) => ( + Add: forwardRef((props, ref) => ( + + )), + Check: forwardRef((props, ref) => ( + + )), + Clear: forwardRef((props, ref) => ( + + )), + Delete: forwardRef((props, ref) => ( + + )), + DetailPanel: forwardRef((props, ref) => ( )), - Edit: forwardRef((props, ref) => ), - Export: forwardRef((props, ref) => ), - Filter: forwardRef((props, ref) => ), - FirstPage: forwardRef((props, ref) => ), - LastPage: forwardRef((props, ref) => ), - NextPage: forwardRef((props, ref) => ), - PreviousPage: forwardRef((props, ref) => ( + Edit: forwardRef((props, ref) => ( + + )), + Export: forwardRef((props, ref) => ( + + )), + Filter: forwardRef((props, ref) => ( + + )), + FirstPage: forwardRef((props, ref) => ( + + )), + LastPage: forwardRef((props, ref) => ( + + )), + NextPage: forwardRef((props, ref) => ( + + )), + PreviousPage: forwardRef((props, ref) => ( )), - ResetSearch: forwardRef((props, ref) => ), - Search: forwardRef((props, ref) => ), - SortArrow: forwardRef((props, ref) => ), - ThirdStateCheck: forwardRef((props, ref) => ), - ViewColumn: forwardRef((props, ref) => ), + ResetSearch: forwardRef((props, ref) => ( + + )), + Search: forwardRef((props, ref) => ( + + )), + SortArrow: forwardRef((props, ref) => ( + + )), + ThirdStateCheck: forwardRef((props, ref) => ( + + )), + ViewColumn: forwardRef((props, ref) => ( + + )), }; // TODO: Material table might already have such a function internally that we can use? diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index d151ef1187..34d79fb16f 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -132,21 +132,12 @@ export type SearchAutocompleteProps