From 66261b4ab44180c046c175e48d676fb38c992327 Mon Sep 17 00:00:00 2001 From: alessandro Date: Thu, 23 Mar 2023 16:13:34 +0100 Subject: [PATCH 001/255] feat(catalog-backend-module-gitlab): Added option to skip forked repos Signed-off-by: alessandro --- .changeset/silent-garlics-drum.md | 5 +++++ docs/integrations/gitlab/discovery.md | 2 ++ .../src/GitLabDiscoveryProcessor.ts | 9 ++++++++- plugins/catalog-backend-module-gitlab/src/lib/types.ts | 5 +++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .changeset/silent-garlics-drum.md diff --git a/.changeset/silent-garlics-drum.md b/.changeset/silent-garlics-drum.md new file mode 100644 index 0000000000..6515efbd4b --- /dev/null +++ b/.changeset/silent-garlics-drum.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': minor +--- + +Added option to skip forked repos diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md index 45991b2cf5..4ddb0413b0 100644 --- a/docs/integrations/gitlab/discovery.md +++ b/docs/integrations/gitlab/discovery.md @@ -109,3 +109,5 @@ export default async function createPlugin( ``` If you don't want create location object if file with component definition do not exists in project, you can set the `skipReposWithoutExactFileMatch` option. That can reduce count of request to gitlab with 404 status code. + +If you don't want to create location object if the project is a fork, you can set the `skipForkedRepos` option. diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts index 9f2cd9b17a..1d6ce44176 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts @@ -42,10 +42,11 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { private readonly logger: Logger; private readonly cache: CacheClient; private readonly skipReposWithoutExactFileMatch: boolean; + private readonly skipForkedRepos: boolean; static fromConfig( config: Config, - options: { logger: Logger; skipReposWithoutExactFileMatch?: boolean }, + options: { logger: Logger; skipReposWithoutExactFileMatch?: boolean; skipForkedRepos?: boolean }, ): GitLabDiscoveryProcessor { const integrations = ScmIntegrations.fromConfig(config); const pluginCache = @@ -63,12 +64,14 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { pluginCache: PluginCacheManager; logger: Logger; skipReposWithoutExactFileMatch?: boolean; + skipForkedRepos?: boolean; }) { this.integrations = options.integrations; this.cache = options.pluginCache.getClient(); this.logger = options.logger; this.skipReposWithoutExactFileMatch = options.skipReposWithoutExactFileMatch || false; + this.skipForkedRepos = options.skipForkedRepos || false; } getProcessorName(): string { @@ -140,6 +143,10 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { } } + if (this.skipForkedRepos && project.hasOwnProperty('forked_from_project')) { + continue; + } + res.matches.push(project); } diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index c27007861b..cc382c9a43 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -22,6 +22,10 @@ export type GitlabGroupDescription = { projects: GitLabProject[]; }; +export type GitlabProjectForkedFrom = { + id: number +} + export type GitLabProject = { id: number; default_branch?: string; @@ -29,6 +33,7 @@ export type GitLabProject = { last_activity_at: string; web_url: string; path_with_namespace?: string; + forked_from_project?: GitlabProjectForkedFrom; }; export type GitLabUser = { From a06c52c511e96fe3bb293eb6f66467588838dce9 Mon Sep 17 00:00:00 2001 From: alessandro Date: Thu, 23 Mar 2023 16:38:06 +0100 Subject: [PATCH 002/255] feat(catalog-backend-module-gitlab): lint Signed-off-by: alessandro --- .../src/GitLabDiscoveryProcessor.ts | 11 +++++++++-- .../catalog-backend-module-gitlab/src/lib/types.ts | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts index 1d6ce44176..c9bd4d9187 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.ts @@ -46,7 +46,11 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { static fromConfig( config: Config, - options: { logger: Logger; skipReposWithoutExactFileMatch?: boolean; skipForkedRepos?: boolean }, + options: { + logger: Logger; + skipReposWithoutExactFileMatch?: boolean; + skipForkedRepos?: boolean; + }, ): GitLabDiscoveryProcessor { const integrations = ScmIntegrations.fromConfig(config); const pluginCache = @@ -143,7 +147,10 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { } } - if (this.skipForkedRepos && project.hasOwnProperty('forked_from_project')) { + if ( + this.skipForkedRepos && + project.hasOwnProperty('forked_from_project') + ) { continue; } diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index cc382c9a43..341e2a9d9a 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -23,8 +23,8 @@ export type GitlabGroupDescription = { }; export type GitlabProjectForkedFrom = { - id: number -} + id: number; +}; export type GitLabProject = { id: number; From b31fe8154e2596a7ef9d4c93c287a1289b027dc0 Mon Sep 17 00:00:00 2001 From: alessandro Date: Thu, 23 Mar 2023 17:16:09 +0100 Subject: [PATCH 003/255] feat(catalog-backend-module-gitlab): fixed api-report.md Signed-off-by: alessandro --- plugins/catalog-backend-module-gitlab/api-report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog-backend-module-gitlab/api-report.md b/plugins/catalog-backend-module-gitlab/api-report.md index 2596b5c99a..6620f6445f 100644 --- a/plugins/catalog-backend-module-gitlab/api-report.md +++ b/plugins/catalog-backend-module-gitlab/api-report.md @@ -40,6 +40,7 @@ export class GitLabDiscoveryProcessor implements CatalogProcessor { options: { logger: Logger; skipReposWithoutExactFileMatch?: boolean; + skipForkedRepos?: boolean; }, ): GitLabDiscoveryProcessor; // (undocumented) From dfe4cab0c1726e5bb0a9029978920b160d3c0bf0 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:08:58 -0400 Subject: [PATCH 004/255] feat: Add pipeline verification of the MkDocs configuration Signed-off-by: Adam Harvey --- .github/workflows/verify_techdocs_mkdocs.yml | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/verify_techdocs_mkdocs.yml diff --git a/.github/workflows/verify_techdocs_mkdocs.yml b/.github/workflows/verify_techdocs_mkdocs.yml new file mode 100644 index 0000000000..7bd08c60fa --- /dev/null +++ b/.github/workflows/verify_techdocs_mkdocs.yml @@ -0,0 +1,21 @@ +name: Verify TechDocs MkDocs +on: + pull_request: + paths: + - 'docs/**' + - 'mkdocs.yml' + +jobs: + build-mkdocs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + + - name: Install dependencies + run: pip3 install mkdocs mkdocs-techdocs-core + + - name: Build MkDocs for TechDocs + run: mkdocs build --strict From a742e89bd38a3cf47e47608c16bb0392701b9a7a Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:11:15 -0400 Subject: [PATCH 005/255] fix: Remove invalid Glossary item in nav Signed-off-by: Adam Harvey --- mkdocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 742b3e2ca6..724932de06 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -211,5 +211,4 @@ nav: - ADR013 - Plugin Package Structure: 'architecture-decisions/adr013-use-node-fetch.md' - Support: - Backstage Project Structure: 'support/project-structure.md' - - Glossary: glossary.md - FAQ: FAQ.md From dd436dae1907b1d86af5d140219e9bb5206cb3bb Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:17:59 -0400 Subject: [PATCH 006/255] fix: Remove invalid files from the nav Signed-off-by: Adam Harvey --- mkdocs.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 724932de06..c6512af231 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,12 +66,10 @@ nav: - Builtin Actions: 'features/software-templates/builtin-actions.md' - Writing Custom Actions: 'features/software-templates/writing-custom-actions.md' - Writing Custom Step Layouts: 'features/software-templates/writing-custom-step-layouts.md' - - Writing Templates (Legacy): 'features/software-templates/legacy.md' - - Migrating from v1alpha1 to v1beta2 templates: 'features/software-templates/migrating-from-v1alpha1-to-v1beta2.md' + - Migrating from v1beta2 to v1beta3 templates: 'features/software-templates/migrating-from-v1beta2-to-v2beta3.md' - Backstage Search: - Overview: 'features/search/README.md' - Getting Started: 'features/search/getting-started.md' - - Getting Started, configuring Backstage: 'features/search/configuration.md' - Concepts: 'features/search/concepts.md' - Search Architecture: 'features/search/architecture.md' - Search Engines: 'features/search/search-engines.md' @@ -98,8 +96,6 @@ nav: - Locations: 'integrations/azure/locations.md' - Discovery: 'integrations/azure/discovery.md' - Org Data: 'integrations/azure/org.md' - - Bitbucket: - - Discovery: 'integrations/bitbucket/discovery.md' - Bitbucket Cloud: - Locations: 'integrations/bitbucketCloud/locations.md' - Discovery: 'integrations/bitbucketCloud/discovery.md' @@ -209,6 +205,4 @@ nav: - ADR011 - Plugin Package Structure: 'architecture-decisions/adr011-plugin-package-structure.md' - ADR012 - Plugin Package Structure: 'architecture-decisions/adr012-use-luxon-locale-and-date-presets.md' - ADR013 - Plugin Package Structure: 'architecture-decisions/adr013-use-node-fetch.md' - - Support: - - Backstage Project Structure: 'support/project-structure.md' - FAQ: FAQ.md From 873728f394d30481ed85f9747bd58419fb5fef34 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:23:21 -0400 Subject: [PATCH 007/255] chore: Fix comment typo Signed-off-by: Adam Harvey --- .github/workflows/verify_storybook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 42326fd71d..92d6222b52 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -49,7 +49,7 @@ jobs: - uses: chromaui/action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - # projetToken intentionally shared to allow collaborators to run Chromatic on forks + # projectToken intentionally shared to allow collaborators to run Chromatic on forks # https://www.chromatic.com/docs/custom-ci-provider#run-chromatic-on-external-forks-of-open-source-projects projectToken: 9tzak77m9nj workingDir: storybook From f48b44e3583e85ba49862d6fda50a71e858e8b49 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:23:44 -0400 Subject: [PATCH 008/255] chore: Build API reference so it can be cross referenced Signed-off-by: Adam Harvey --- .github/workflows/verify_techdocs_mkdocs.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/verify_techdocs_mkdocs.yml b/.github/workflows/verify_techdocs_mkdocs.yml index 7bd08c60fa..6d83ed0772 100644 --- a/.github/workflows/verify_techdocs_mkdocs.yml +++ b/.github/workflows/verify_techdocs_mkdocs.yml @@ -10,6 +10,25 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + + # Generate the API Reference docs which MkDocs cross-references + + - name: use node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: yarn install + uses: backstage/actions/yarn-install@v0.6.3 + with: + cache-prefix: ${{ runner.os }}-v18.x + + - name: build API reference + run: yarn build:api-docs + + + # Setup and start the MkDocs configuration + - uses: actions/setup-python@v4 with: python-version: '3.10' From 22c0334610ae30dd03931f9af4673d01dc46a055 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:28:20 -0400 Subject: [PATCH 009/255] chore: Increase memory Signed-off-by: Adam Harvey --- .github/workflows/verify_techdocs_mkdocs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/verify_techdocs_mkdocs.yml b/.github/workflows/verify_techdocs_mkdocs.yml index 6d83ed0772..c92aa3f4c8 100644 --- a/.github/workflows/verify_techdocs_mkdocs.yml +++ b/.github/workflows/verify_techdocs_mkdocs.yml @@ -8,6 +8,9 @@ on: jobs: build-mkdocs: runs-on: ubuntu-latest + env: + CI: true + NODE_OPTIONS: --max-old-space-size=4096 steps: - uses: actions/checkout@v3 From 9297559f828f4dff8e967861efbb43bbe855a644 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:37:42 -0400 Subject: [PATCH 010/255] fix: software template nav filename Signed-off-by: Adam Harvey --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index c6512af231..8c29645840 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,7 +66,7 @@ nav: - Builtin Actions: 'features/software-templates/builtin-actions.md' - Writing Custom Actions: 'features/software-templates/writing-custom-actions.md' - Writing Custom Step Layouts: 'features/software-templates/writing-custom-step-layouts.md' - - Migrating from v1beta2 to v1beta3 templates: 'features/software-templates/migrating-from-v1beta2-to-v2beta3.md' + - Migrating from v1beta2 to v1beta3 templates: 'features/software-templates/migrating-from-v1beta2-to-v1beta3.md' - Backstage Search: - Overview: 'features/search/README.md' - Getting Started: 'features/search/getting-started.md' From dab23038c43a4da4eb226a97d4049860ba31af0c Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Wed, 3 May 2023 20:53:52 -0400 Subject: [PATCH 011/255] chore: Prettier formatting Signed-off-by: Adam Harvey --- .github/workflows/verify_techdocs_mkdocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/verify_techdocs_mkdocs.yml b/.github/workflows/verify_techdocs_mkdocs.yml index c92aa3f4c8..288afa238b 100644 --- a/.github/workflows/verify_techdocs_mkdocs.yml +++ b/.github/workflows/verify_techdocs_mkdocs.yml @@ -29,7 +29,6 @@ jobs: - name: build API reference run: yarn build:api-docs - # Setup and start the MkDocs configuration - uses: actions/setup-python@v4 From b5c2256cc0bd056a037415391e4696bb06caadd4 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 4 May 2023 10:52:42 -0400 Subject: [PATCH 012/255] chore: Improve docs wording Signed-off-by: Adam Harvey --- docs/features/techdocs/how-to-guides.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index ceccbe1468..3789d8582d 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -509,8 +509,8 @@ Done! You now have support for TechDocs in your own software template! ## how to enable iframes in TechDocs -Techdocs uses the [DOMPurify](https://github.com/cure53/DOMPurify) to sanitizes -HTML and prevents XSS attacks +TechDocs uses the [DOMPurify](https://github.com/cure53/DOMPurify) library to +sanitize HTML and prevent XSS attacks. It's possible to allow some iframes based on a list of allowed hosts. To do this, add the allowed hosts in the `techdocs.sanitizer.allowedIframeHosts` @@ -530,7 +530,7 @@ This way, all iframes where the host of src attribute is in the ## How to add Mermaid support in TechDocs -To add `Mermaid` support in Techdocs, you can use [`kroki`](https://kroki.io) +To add `Mermaid` support in TechDocs, you can use [`kroki`](https://kroki.io) that creates diagrams from Textual descriptions. It is a single rendering gateway for all popular diagrams-as-a-code tools. It supports an enormous number of diagram types. @@ -559,7 +559,7 @@ docker build . -t dockerHub_Username/repositoryName:tagName Once the docker image is ready, push it to DockerHub. -2. **Update app-config.yaml:** So that when your app generates techdocs, it will +2. **Update app-config.yaml:** So that when your app generates TechDocs, it will pull your docker image from DockerHub. ```python From f8ac46f62739df9f656d74902cc3f6f606a541f5 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 4 May 2023 13:45:51 -0400 Subject: [PATCH 013/255] chore: Additional cleanup and formatting Signed-off-by: Adam Harvey --- docs/features/techdocs/how-to-guides.md | 27 +++++++++---------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 3789d8582d..3d86de53f2 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -335,7 +335,6 @@ bucket matches this lower-case entity triplet expectation. able to read/copy/rename/move/delete files. The exact instructions vary by storage provider, but check the [using cloud storage][using-cloud-storage] page for details. - 2. **Run a non-destructive migration of files**: Ensure you have the latest version of `techdocs-cli` installed. Then run the following command, using the details relevant for your provider / configuration. This will copy all @@ -349,12 +348,10 @@ techdocs-cli migrate --publisher-type --stora 3. **Deploy the updated versions of the TechDocs plugins**: Once the migration above has been run, you can deploy the beta versions of the TechDocs backend and frontend plugins to your Backstage instance. - 4. **Verify that your TechDocs sites are still loading/accessible**: Try accessing a TechDocs site using different entity-triplet case variants, e.g. `/docs/namespace/KIND/name` or `/docs/namespace/kind/name`. Your TechDocs site should load regardless of the URL path casing you use. - 5. **Clean up the old objects from storage**: Once you've verified that your TechDocs site is accessible, you can clean up your storage bucket by re-running the `migrate` command on the TechDocs CLI, but with an additional @@ -491,7 +488,7 @@ plugins: The `docs/index.md` can for example have the following content: -``` +```markdown # ${{ values.component_id }} ${{ values.description }} @@ -535,15 +532,15 @@ that creates diagrams from Textual descriptions. It is a single rendering gateway for all popular diagrams-as-a-code tools. It supports an enormous number of diagram types. -1. **Create and Publish docker image:** Create the docker image from the - following Dockerfile and publish it to DockerHub. +1. **Create and Publish Docker image:** Create the Docker image from the + following `Dockerfile` and publish it to DockerHub. ```docker -FROM python:3.8-alpine +FROM python:3.10-alpine RUN apk update && apk --no-cache add gcc musl-dev openjdk11-jdk curl graphviz ttf-dejavu fontconfig -RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==1.1.7 +RUN pip install --upgrade pip && pip install mkdocs-techdocs-core==1.2.0 RUN pip install mkdocs-kroki-plugin @@ -551,16 +548,14 @@ ENTRYPOINT [ "mkdocs" ] ``` Create a repository in your DockerHub and run the below command in the same -folder where your Dockerfile is present: +folder where your `Dockerfile` is present: ```shell docker build . -t dockerHub_Username/repositoryName:tagName ``` -Once the docker image is ready, push it to DockerHub. - -2. **Update app-config.yaml:** So that when your app generates TechDocs, it will - pull your docker image from DockerHub. +Once the docker image is ready, push it to DockerHub. 2. **Update app-config.yaml:** So that when your app generates TechDocs, it will +pull your docker image from DockerHub. ```python techdocs: @@ -573,7 +568,7 @@ techdocs: type: 'local' # Alternatives - 'googleGcs' or 'awsS3'. Read documentation for using alternatives. ``` -3. **Add the `kroki` plugin in mkdocs.yml:** +3. **Add the `kroki` plugin in `mkdocs.yml`:** ```yml plugins: @@ -586,9 +581,7 @@ plugins: > you have sensitive information in your organization's diagrams, you should set > up a [server of your own](https://docs.kroki.io/kroki/setup/install/) and use it > instead. Check out [mkdocs-kroki-plugin config](https://github.com/AVATEAM-IT-SYSTEMHAUS/mkdocs-kroki-plugin#config) -> for more plugin configuration details. - -4. **Add mermaid code into techdocs:** +> for more plugin configuration details. 4. **Add mermaid code into TechDocs:** ````md ```kroki-mermaid From 9fc9d2a73684dfb20b4bcec18b49be7bbb1ad42b Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 4 May 2023 13:48:37 -0400 Subject: [PATCH 014/255] chore: More formatting Signed-off-by: Adam Harvey --- docs/features/techdocs/how-to-guides.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md index 3d86de53f2..a45112bcfe 100644 --- a/docs/features/techdocs/how-to-guides.md +++ b/docs/features/techdocs/how-to-guides.md @@ -335,6 +335,7 @@ bucket matches this lower-case entity triplet expectation. able to read/copy/rename/move/delete files. The exact instructions vary by storage provider, but check the [using cloud storage][using-cloud-storage] page for details. + 2. **Run a non-destructive migration of files**: Ensure you have the latest version of `techdocs-cli` installed. Then run the following command, using the details relevant for your provider / configuration. This will copy all @@ -348,10 +349,12 @@ techdocs-cli migrate --publisher-type --stora 3. **Deploy the updated versions of the TechDocs plugins**: Once the migration above has been run, you can deploy the beta versions of the TechDocs backend and frontend plugins to your Backstage instance. + 4. **Verify that your TechDocs sites are still loading/accessible**: Try accessing a TechDocs site using different entity-triplet case variants, e.g. `/docs/namespace/KIND/name` or `/docs/namespace/kind/name`. Your TechDocs site should load regardless of the URL path casing you use. + 5. **Clean up the old objects from storage**: Once you've verified that your TechDocs site is accessible, you can clean up your storage bucket by re-running the `migrate` command on the TechDocs CLI, but with an additional @@ -554,8 +557,10 @@ folder where your `Dockerfile` is present: docker build . -t dockerHub_Username/repositoryName:tagName ``` -Once the docker image is ready, push it to DockerHub. 2. **Update app-config.yaml:** So that when your app generates TechDocs, it will -pull your docker image from DockerHub. +Once the docker image is ready, push it to DockerHub. + +2. **Update app-config.yaml:** So that when your app generates TechDocs, it will + pull your docker image from DockerHub. ```python techdocs: @@ -581,7 +586,9 @@ plugins: > you have sensitive information in your organization's diagrams, you should set > up a [server of your own](https://docs.kroki.io/kroki/setup/install/) and use it > instead. Check out [mkdocs-kroki-plugin config](https://github.com/AVATEAM-IT-SYSTEMHAUS/mkdocs-kroki-plugin#config) -> for more plugin configuration details. 4. **Add mermaid code into TechDocs:** +> for more plugin configuration details. + +4. **Add mermaid code into TechDocs:** ````md ```kroki-mermaid From 5b5060d68a0b27cb79b772dc165ac0dd695fd03c Mon Sep 17 00:00:00 2001 From: Ainhoa Date: Fri, 5 May 2023 10:18:25 +0100 Subject: [PATCH 015/255] Add permission tag Signed-off-by: Ainhoa --- .github/labeler.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index fb35f6e0e6..94e8566de5 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -21,7 +21,8 @@ microsite: - microsite/**/* auth: - plugins/auth-backend/**/* - - plugins/permission-*/**/* - packages/core-app-api/src/apis/implementations/auth/**/* - packages/core-app-api/src/lib/Auth*/**/* - packages/core-plugin-api/src/apis/definitions/auth.ts +permission: + - plugins/permission-*/**/* From 6d3a88b20287eb60ede30db7b6836df7a09798a3 Mon Sep 17 00:00:00 2001 From: Ainhoa Date: Fri, 5 May 2023 10:26:36 +0100 Subject: [PATCH 016/255] Add Permission Project area Signed-off-by: Ainhoa --- OWNERS.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/OWNERS.md b/OWNERS.md index 0a567ee269..a1da3d7121 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -77,6 +77,18 @@ Scope: The TechDocs plugin and related tooling | Morgan Bentell | Spotify | Pulp Fiction | [agentbellnorm](http://github.com/agentbellnorm) | morganbentell#9030 | | Raghunandan Balachandran | Spotify | Pulp Fiction | [soapraj](http://github.com/soapraj) | raghunandanb#1114 | +### Permission Framework + +Team: @backstage/permission-maintainers + +Scope: The Permission Framework and plugins integrating with the permission framework + +| Name | Organization | Team | GitHub | Discord | +| ------------------------ | ------------ | --------------- | ------------------------------------------------ | ------------------ | +| Vincenzo Scamporlino | Spotify | Imaginary Goats | [vinzscam](http://github.com/vinzscam) | vinzscam#6944 | +| Ainhoa Larumbe | Spotify | Imaginary Goats | [ainhoaL](http://github.com/ainhoaL) | ainhoa#8085 | + + ## Sponsors | Name | Organization | GitHub | Email | From 1bc46f2b1881e00851ad7f041c6aced60da762ec Mon Sep 17 00:00:00 2001 From: Ainhoa Date: Fri, 5 May 2023 10:27:12 +0100 Subject: [PATCH 017/255] order project areas Signed-off-by: Ainhoa --- OWNERS.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/OWNERS.md b/OWNERS.md index a1da3d7121..dafd1de967 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -66,17 +66,6 @@ Scope: The Kubernetes plugin and the base it provides for other plugins to build | Matthew Clarke | Spotify | | [mclarke47](http://github.com/mclarke47) | mclarke#0725 | | Jamie Klassen | VMware | | [jamieklassen](http://github.com/jamieklassen) | jamieklassen#3047 | -### TechDocs - -Team: @backstage/techdocs-maintainers - -Scope: The TechDocs plugin and related tooling - -| Name | Organization | Team | GitHub | Discord | -| ------------------------ | ------------ | ------------ | ------------------------------------------------ | ------------------ | -| Morgan Bentell | Spotify | Pulp Fiction | [agentbellnorm](http://github.com/agentbellnorm) | morganbentell#9030 | -| Raghunandan Balachandran | Spotify | Pulp Fiction | [soapraj](http://github.com/soapraj) | raghunandanb#1114 | - ### Permission Framework Team: @backstage/permission-maintainers @@ -88,6 +77,17 @@ Scope: The Permission Framework and plugins integrating with the permission fram | Vincenzo Scamporlino | Spotify | Imaginary Goats | [vinzscam](http://github.com/vinzscam) | vinzscam#6944 | | Ainhoa Larumbe | Spotify | Imaginary Goats | [ainhoaL](http://github.com/ainhoaL) | ainhoa#8085 | +### TechDocs + +Team: @backstage/techdocs-maintainers + +Scope: The TechDocs plugin and related tooling + +| Name | Organization | Team | GitHub | Discord | +| ------------------------ | ------------ | ------------ | ------------------------------------------------ | ------------------ | +| Morgan Bentell | Spotify | Pulp Fiction | [agentbellnorm](http://github.com/agentbellnorm) | morganbentell#9030 | +| Raghunandan Balachandran | Spotify | Pulp Fiction | [soapraj](http://github.com/soapraj) | raghunandanb#1114 | + ## Sponsors From 9512f13eb3af7d28e003a335d8be5c2f9b954a0d Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Mon, 8 May 2023 07:55:40 +0100 Subject: [PATCH 018/255] add a homepage component to pagerduty Signed-off-by: Brian Fletcher --- .changeset/giant-maps-compete.md | 5 + plugins/pagerduty/README.md | 24 ++ plugins/pagerduty/package.json | 1 + plugins/pagerduty/src/api/client.ts | 10 +- plugins/pagerduty/src/api/types.ts | 9 + .../EntityPagerDutyCard/index.test.tsx | 386 ++++++++++++++++++ .../components/EntityPagerDutyCard/index.tsx | 41 ++ .../Errors/ServiceNotFoundError.tsx | 2 +- .../components/PagerDutyCard/index.test.tsx | 168 +++----- .../src/components/PagerDutyCard/index.tsx | 26 +- .../PagerDutyHomepageCard/Content.tsx | 29 ++ .../components/PagerDutyHomepageCard/index.ts | 16 + .../TriggerDialog/TriggerDialog.test.tsx | 30 +- .../TriggerDialog/TriggerDialog.tsx | 6 +- plugins/pagerduty/src/components/index.ts | 6 +- plugins/pagerduty/src/index.ts | 1 + plugins/pagerduty/src/plugin.ts | 22 +- yarn.lock | 1 + 18 files changed, 615 insertions(+), 168 deletions(-) create mode 100644 .changeset/giant-maps-compete.md create mode 100644 plugins/pagerduty/src/components/EntityPagerDutyCard/index.test.tsx create mode 100644 plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx create mode 100644 plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx create mode 100644 plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts diff --git a/.changeset/giant-maps-compete.md b/.changeset/giant-maps-compete.md new file mode 100644 index 0000000000..776332b308 --- /dev/null +++ b/.changeset/giant-maps-compete.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-pagerduty': patch +--- + +Add a homepage widget for the `Pagerduty` component. diff --git a/plugins/pagerduty/README.md b/plugins/pagerduty/README.md index ac3ba529dd..c59e28dff2 100644 --- a/plugins/pagerduty/README.md +++ b/plugins/pagerduty/README.md @@ -95,6 +95,30 @@ annotations: pagerduty.com/integration-key: [INTEGRATION_KEY] ``` +### The homepage component + +You may also add the component to the homepage of Backstage. Edit the `HomePage.tsx` file, import `PagerDutyHomepageCard` and add it to the home page. e.g. + +```typescript jsx +... +export const homePage = ( + + ... + + + ... + + + + +); +``` + Next, provide the [API token](https://support.pagerduty.com/docs/generating-api-keys#generating-a-general-access-rest-api-key) that the client will use to make requests to the [PagerDuty API](https://developer.pagerduty.com/docs/rest-api-v2/rest-api/). Add the proxy configuration in `app-config.yaml`: diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 8e599ca240..85bb5ff38c 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -38,6 +38,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-home": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/pagerduty/src/api/client.ts b/plugins/pagerduty/src/api/client.ts index 7d99be219d..350b838951 100644 --- a/plugins/pagerduty/src/api/client.ts +++ b/plugins/pagerduty/src/api/client.ts @@ -30,6 +30,7 @@ import { createApiRef, ConfigApi } from '@backstage/core-plugin-api'; import { NotFoundError } from '@backstage/errors'; import { Entity } from '@backstage/catalog-model'; import { getPagerDutyEntity } from '../components/pagerDutyEntity'; +import { PagerDutyEntity } from '../types'; /** @public */ export class UnauthorizedError extends Error {} @@ -63,8 +64,10 @@ export class PagerDutyClient implements PagerDutyApi { constructor(private readonly config: PagerDutyClientApiConfig) {} - async getServiceByEntity(entity: Entity): Promise { - const { integrationKey, serviceId } = getPagerDutyEntity(entity); + async getServiceByPagerDutyEntity( + pagerDutyEntity: PagerDutyEntity, + ): Promise { + const { integrationKey, serviceId } = pagerDutyEntity; let response: PagerDutyServiceResponse; let url: string; @@ -92,6 +95,9 @@ export class PagerDutyClient implements PagerDutyApi { return response; } + async getServiceByEntity(entity: Entity): Promise { + return await this.getServiceByPagerDutyEntity(getPagerDutyEntity(entity)); + } async getIncidentsByServiceId( serviceId: string, ): Promise { diff --git a/plugins/pagerduty/src/api/types.ts b/plugins/pagerduty/src/api/types.ts index f1971d53d5..a878afc447 100644 --- a/plugins/pagerduty/src/api/types.ts +++ b/plugins/pagerduty/src/api/types.ts @@ -22,6 +22,7 @@ import { } from '../components/types'; import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; +import { PagerDutyEntity } from '../types'; export type PagerDutyServicesResponse = { services: PagerDutyService[]; @@ -57,6 +58,14 @@ export type PagerDutyTriggerAlarmRequest = { /** @public */ export interface PagerDutyApi { + /** + * Fetches the service for the provided pager duty Entity. + * + */ + getServiceByPagerDutyEntity( + pagerDutyEntity: PagerDutyEntity, + ): Promise; + /** * Fetches the service for the provided Entity. * diff --git a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.test.tsx b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.test.tsx new file mode 100644 index 0000000000..3f3a4ca1a8 --- /dev/null +++ b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.test.tsx @@ -0,0 +1,386 @@ +/* + * 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 from 'react'; +import { render, waitFor, fireEvent, act } from '@testing-library/react'; +import { + EntityPagerDutyCard, + isPluginApplicableToEntity, +} from '../EntityPagerDutyCard'; +import { Entity } from '@backstage/catalog-model'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { NotFoundError } from '@backstage/errors'; +import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils'; +import { pagerDutyApiRef, UnauthorizedError, PagerDutyClient } from '../../api'; +import { PagerDutyService, PagerDutyUser } from '../types'; + +import { alertApiRef } from '@backstage/core-plugin-api'; +import { ApiProvider } from '@backstage/core-app-api'; + +const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/integration-key': 'abc123', + }, + }, +}; + +const entityWithoutAnnotations: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: {}, + }, +}; + +const entityWithServiceId: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/service-id': 'def456', + }, + }, +}; + +const entityWithAllAnnotations: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'pagerduty-test', + annotations: { + 'pagerduty.com/integration-key': 'abc123', + 'pagerduty.com/service-id': 'def456', + }, + }, +}; + +const user: PagerDutyUser = { + name: 'person1', + id: 'p1', + summary: 'person1', + email: 'person1@example.com', + html_url: 'http://a.com/id1', +}; + +const service: PagerDutyService = { + id: 'def456', + name: 'pagerduty-name', + html_url: 'www.example.com', + escalation_policy: { + id: 'def', + user: user, + html_url: 'http://a.com/id1', + }, + integrationKey: 'abc123', +}; + +const mockPagerDutyApi: Partial = { + getServiceByEntity: async () => ({ service }), + getServiceByPagerDutyEntity: async () => ({ service }), + getOnCallByPolicyId: async () => ({ oncalls: [] }), + getIncidentsByServiceId: async () => ({ incidents: [] }), +}; + +const apis = TestApiRegistry.from( + [pagerDutyApiRef, mockPagerDutyApi], + [alertApiRef, {}], +); + +describe('isPluginApplicableToEntity', () => { + describe('when entity has no annotations', () => { + it('returns false', () => { + expect(isPluginApplicableToEntity(entityWithoutAnnotations)).toBe(false); + }); + }); + + describe('when entity has the pagerduty.com/integration-key annotation', () => { + it('returns true', () => { + expect(isPluginApplicableToEntity(entity)).toBe(true); + }); + }); + + describe('when entity has the pagerduty.com/service-id annotation', () => { + it('returns true', () => { + expect(isPluginApplicableToEntity(entityWithServiceId)).toBe(true); + }); + }); + + describe('when entity has all annotations', () => { + it('returns true', () => { + expect(isPluginApplicableToEntity(entityWithAllAnnotations)).toBe(true); + }); + }); +}); + +describe('EntityPagerDutyCard', () => { + it('Render pagerduty', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Create Incident')).toBeInTheDocument(); + expect(getByText('Nice! No incidents found!')).toBeInTheDocument(); + expect(getByText('Empty escalation policy')).toBeInTheDocument(); + }); + + it('Handles custom error for missing token', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockRejectedValueOnce(new UnauthorizedError()); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Missing or invalid PagerDuty Token')).toBeInTheDocument(); + }); + + it('Handles custom NotFoundError', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockRejectedValueOnce(new NotFoundError()); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('PagerDuty Service Not Found')).toBeInTheDocument(); + }); + + it('handles general error', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockRejectedValueOnce(new Error('An error occurred')); + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + + expect( + getByText( + 'Error encountered while fetching information. An error occurred', + ), + ).toBeInTheDocument(); + }); + + it('opens the dialog when trigger button is clicked', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { getByText, queryByTestId, getByRole } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Service Directory')).toBeInTheDocument(); + + const triggerLink = getByText('Create Incident'); + await act(async () => { + fireEvent.click(triggerLink); + }); + expect(getByRole('dialog')).toBeInTheDocument(); + }); + + describe('when entity has the pagerduty.com/service-id annotation', () => { + it('Renders PagerDuty service information', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Create Incident')).toBeInTheDocument(); + expect(getByText('Nice! No incidents found!')).toBeInTheDocument(); + expect(getByText('Empty escalation policy')).toBeInTheDocument(); + }); + + it('Handles custom error for missing token', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockRejectedValueOnce(new UnauthorizedError()); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect( + getByText('Missing or invalid PagerDuty Token'), + ).toBeInTheDocument(); + }); + + it('Handles custom NotFoundError', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockRejectedValueOnce(new NotFoundError()); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('PagerDuty Service Not Found')).toBeInTheDocument(); + }); + + it('handles general error', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockRejectedValueOnce(new Error('An error occurred')); + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + + expect( + getByText( + 'Error encountered while fetching information. An error occurred', + ), + ).toBeInTheDocument(); + }); + + it('disables the Create Incident button', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { queryByTestId, getByTitle } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect( + getByTitle('Must provide an integration-key to create incidents') + .className, + ).toMatch('disabled'); + }); + }); + + describe('when entity has all annotations', () => { + it('queries by integration key', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Create Incident')).toBeInTheDocument(); + expect(getByText('Nice! No incidents found!')).toBeInTheDocument(); + expect(getByText('Empty escalation policy')).toBeInTheDocument(); + }); + }); + + describe('when entity has all annotations but the plugin has been configured to be "read only"', () => { + it('queries by integration key but does not render the "Create Incident" button', async () => { + mockPagerDutyApi.getServiceByPagerDutyEntity = jest + .fn() + .mockImplementationOnce(async () => ({ service })); + + const { getByText, queryByTestId } = render( + wrapInTestApp( + + + + + , + ), + ); + await waitFor(() => !queryByTestId('progress')); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Nice! No incidents found!')).toBeInTheDocument(); + expect(getByText('Empty escalation policy')).toBeInTheDocument(); + expect(() => getByText('Create Incident')).toThrow(); + }); + }); +}); diff --git a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx new file mode 100644 index 0000000000..e9e70e8f1b --- /dev/null +++ b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx @@ -0,0 +1,41 @@ +/* + * 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 from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { PAGERDUTY_INTEGRATION_KEY, PAGERDUTY_SERVICE_ID } from '../constants'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { getPagerDutyEntity } from '../pagerDutyEntity'; +import { PagerDutyCard } from '../PagerDutyCard'; + +/** @public */ +export const isPluginApplicableToEntity = (entity: Entity) => + Boolean( + entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY] || + entity.metadata.annotations?.[PAGERDUTY_SERVICE_ID], + ); + +/** @public */ +export type PagerDutyCardProps = { + readOnly?: boolean; +}; + +/** @public */ +export const EntityPagerDutyCard = (props: PagerDutyCardProps) => { + const { readOnly } = props; + const { entity } = useEntity(); + const pagerDutyEntity = getPagerDutyEntity(entity); + return ; +}; diff --git a/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx b/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx index d8b61ef7cd..236585735e 100644 --- a/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx +++ b/plugins/pagerduty/src/components/Errors/ServiceNotFoundError.tsx @@ -21,7 +21,7 @@ export const ServiceNotFoundError = () => ( { - describe('when entity has no annotations', () => { - it('returns false', () => { - expect(isPluginApplicableToEntity(entityWithoutAnnotations)).toBe(false); - }); - }); - - describe('when entity has the pagerduty.com/integration-key annotation', () => { - it('returns true', () => { - expect(isPluginApplicableToEntity(entity)).toBe(true); - }); - }); - - describe('when entity has the pagerduty.com/service-id annotation', () => { - it('returns true', () => { - expect(isPluginApplicableToEntity(entityWithServiceId)).toBe(true); - }); - }); - - describe('when entity has all annotations', () => { - it('returns true', () => { - expect(isPluginApplicableToEntity(entityWithAllAnnotations)).toBe(true); - }); - }); -}); - -describe('PageDutyCard', () => { +describe('PagerDutyCard', () => { it('Render pagerduty', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockImplementationOnce(async () => ({ service })); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -149,16 +76,14 @@ describe('PageDutyCard', () => { }); it('Handles custom error for missing token', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockRejectedValueOnce(new UnauthorizedError()); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -167,16 +92,14 @@ describe('PageDutyCard', () => { }); it('Handles custom NotFoundError', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockRejectedValueOnce(new NotFoundError()); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -185,15 +108,13 @@ describe('PageDutyCard', () => { }); it('handles general error', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockRejectedValueOnce(new Error('An error occurred')); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -207,16 +128,14 @@ describe('PageDutyCard', () => { }); it('opens the dialog when trigger button is clicked', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockImplementationOnce(async () => ({ service })); const { getByText, queryByTestId, getByRole } = render( wrapInTestApp( - - - + , ), ); @@ -232,16 +151,14 @@ describe('PageDutyCard', () => { describe('when entity has the pagerduty.com/service-id annotation', () => { it('Renders PagerDuty service information', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockImplementationOnce(async () => ({ service })); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -253,16 +170,18 @@ describe('PageDutyCard', () => { }); it('Handles custom error for missing token', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockRejectedValueOnce(new UnauthorizedError()); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -273,16 +192,18 @@ describe('PageDutyCard', () => { }); it('Handles custom NotFoundError', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockRejectedValueOnce(new NotFoundError()); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -291,15 +212,17 @@ describe('PageDutyCard', () => { }); it('handles general error', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockRejectedValueOnce(new Error('An error occurred')); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -313,16 +236,14 @@ describe('PageDutyCard', () => { }); it('disables the Create Incident button', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockImplementationOnce(async () => ({ service })); const { queryByTestId, getByTitle } = render( wrapInTestApp( - - - + , ), ); @@ -336,16 +257,18 @@ describe('PageDutyCard', () => { describe('when entity has all annotations', () => { it('queries by integration key', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockImplementationOnce(async () => ({ service })); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); @@ -359,16 +282,19 @@ describe('PageDutyCard', () => { describe('when entity has all annotations but the plugin has been configured to be "read only"', () => { it('queries by integration key but does not render the "Create Incident" button', async () => { - mockPagerDutyApi.getServiceByEntity = jest + mockPagerDutyApi.getServiceByPagerDutyEntity = jest .fn() .mockImplementationOnce(async () => ({ service })); const { getByText, queryByTestId } = render( wrapInTestApp( - - - + , ), ); diff --git a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx index fd23166d88..bb89865c5f 100644 --- a/plugins/pagerduty/src/components/PagerDutyCard/index.tsx +++ b/plugins/pagerduty/src/components/PagerDutyCard/index.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ import React, { ReactNode, useState, useCallback } from 'react'; -import { Entity } from '@backstage/catalog-model'; import { Card, CardHeader, Divider, CardContent } from '@material-ui/core'; import { Incidents } from '../Incident'; import { EscalationPolicy } from '../Escalation'; @@ -25,7 +24,6 @@ import AlarmAddIcon from '@material-ui/icons/AlarmAdd'; import { MissingTokenError, ServiceNotFoundError } from '../Errors'; import WebIcon from '@material-ui/icons/Web'; import DateRangeIcon from '@material-ui/icons/DateRange'; -import { PAGERDUTY_INTEGRATION_KEY, PAGERDUTY_SERVICE_ID } from '../constants'; import { TriggerDialog } from '../TriggerDialog'; import { ChangeEvents } from '../ChangeEvents'; @@ -39,30 +37,20 @@ import { CardTab, InfoCard, } from '@backstage/core-components'; -import { useEntity } from '@backstage/plugin-catalog-react'; -import { getPagerDutyEntity } from '../pagerDutyEntity'; +import { PagerDutyEntity } from '../../types'; const BasicCard = ({ children }: { children: ReactNode }) => ( {children} ); /** @public */ -export const isPluginApplicableToEntity = (entity: Entity) => - Boolean( - entity.metadata.annotations?.[PAGERDUTY_INTEGRATION_KEY] || - entity.metadata.annotations?.[PAGERDUTY_SERVICE_ID], - ); - -/** @public */ -export type PagerDutyCardProps = { +export type PagerDutyCardProps = PagerDutyEntity & { readOnly?: boolean; }; /** @public */ export const PagerDutyCard = (props: PagerDutyCardProps) => { - const { readOnly } = props; - const { entity } = useEntity(); - const pagerDutyEntity = getPagerDutyEntity(entity); + const { readOnly, integrationKey, name } = props; const api = useApi(pagerDutyApiRef); const [refreshIncidents, setRefreshIncidents] = useState(false); const [refreshChangeEvents, setRefreshChangeEvents] = @@ -86,7 +74,9 @@ export const PagerDutyCard = (props: PagerDutyCardProps) => { loading, error, } = useAsync(async () => { - const { service: foundService } = await api.getServiceByEntity(entity); + const { service: foundService } = await api.getServiceByPagerDutyEntity( + props, + ); return { id: foundService.id, @@ -137,7 +127,7 @@ export const PagerDutyCard = (props: PagerDutyCardProps) => { * There is no guarantee the current user entity has a valid email association, so instead just * only allow triggering incidents when an integration key is present. */ - const createIncidentDisabled = !pagerDutyEntity.integrationKey; + const createIncidentDisabled = !integrationKey; const triggerLink: IconLinkVerticalProps = { label: 'Create Incident', onClick: showDialog, @@ -195,6 +185,8 @@ export const PagerDutyCard = (props: PagerDutyCardProps) => { showDialog={dialogShown} handleDialog={hideDialog} onIncidentCreated={handleRefresh} + name={name} + integrationKey={integrationKey} /> )} diff --git a/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx b/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx new file mode 100644 index 0000000000..479e1f621d --- /dev/null +++ b/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx @@ -0,0 +1,29 @@ +/* + * 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 from 'react'; + +import { PagerDutyEntity } from '../../types'; +import { PagerDutyCard } from '../PagerDutyCard'; + +/** @public */ +export type PagerDutyCardProps = PagerDutyEntity & { + readOnly?: boolean; +}; + +/** @public */ +export const Content = (props: PagerDutyCardProps) => { + return ; +}; diff --git a/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts b/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts new file mode 100644 index 0000000000..fdbeb7cb70 --- /dev/null +++ b/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { Content } from './Content'; diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx index c6874cc7f3..74cd5100fa 100644 --- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx +++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx @@ -17,8 +17,6 @@ import React from 'react'; import { fireEvent, act } from '@testing-library/react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import { pagerDutyApiRef } from '../../api'; -import { Entity } from '@backstage/catalog-model'; -import { EntityProvider } from '@backstage/plugin-catalog-react'; import { TriggerDialog } from './TriggerDialog'; import { ApiProvider } from '@backstage/core-app-api'; @@ -49,26 +47,15 @@ describe('TriggerDialog', () => { ); it('open the dialog and trigger an alarm', async () => { - const entity: Entity = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'pagerduty-test', - annotations: { - 'pagerduty.com/integration-key': 'abc123', - }, - }, - }; - const { getByText, getByRole, getByTestId } = await renderInTestApp( - - {}} - onIncidentCreated={() => {}} - /> - + {}} + onIncidentCreated={() => {}} + /> , ); @@ -89,8 +76,7 @@ describe('TriggerDialog', () => { }); expect(mockTriggerAlarmFn).toHaveBeenCalled(); expect(mockTriggerAlarmFn).toHaveBeenCalledWith({ - integrationKey: - entity!.metadata!.annotations!['pagerduty.com/integration-key'], + integrationKey: 'abc123', source: window.location.toString(), description, userName: 'guest', diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx index 4dc4f04699..a1e88b8fa6 100644 --- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx +++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx @@ -28,7 +28,6 @@ import { import useAsyncFn from 'react-use/lib/useAsyncFn'; import { pagerDutyApiRef } from '../../api'; import { Alert } from '@material-ui/lab'; -import { usePagerdutyEntity } from '../../hooks'; import { useApi, alertApiRef, @@ -40,14 +39,17 @@ type Props = { showDialog: boolean; handleDialog: () => void; onIncidentCreated?: () => void; + name: string; + integrationKey: string; }; export const TriggerDialog = ({ showDialog, handleDialog, onIncidentCreated: onIncidentCreated, + name, + integrationKey, }: Props) => { - const { name, integrationKey } = usePagerdutyEntity(); const alertApi = useApi(alertApiRef); const identityApi = useApi(identityApiRef); const api = useApi(pagerDutyApiRef); diff --git a/plugins/pagerduty/src/components/index.ts b/plugins/pagerduty/src/components/index.ts index e7c1ff4e72..cd093498d1 100644 --- a/plugins/pagerduty/src/components/index.ts +++ b/plugins/pagerduty/src/components/index.ts @@ -25,10 +25,12 @@ export type { export type { PagerDutyCardProps } from './PagerDutyCard'; +export { PagerDutyCard } from './PagerDutyCard'; + export { isPluginApplicableToEntity, isPluginApplicableToEntity as isPagerDutyAvailable, - PagerDutyCard, -} from './PagerDutyCard'; + EntityPagerDutyCard, +} from './EntityPagerDutyCard'; export { TriggerButton } from './TriggerButton'; diff --git a/plugins/pagerduty/src/index.ts b/plugins/pagerduty/src/index.ts index 2e0eddc9a8..0517b0752a 100644 --- a/plugins/pagerduty/src/index.ts +++ b/plugins/pagerduty/src/index.ts @@ -24,6 +24,7 @@ export { pagerDutyPlugin, pagerDutyPlugin as plugin, EntityPagerDutyCard, + PagerDutyHomepageCard, } from './plugin'; export * from './components'; diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index 73e7e0b397..28ed6861b6 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -23,6 +23,8 @@ import { configApiRef, createComponentExtension, } from '@backstage/core-plugin-api'; +import { createCardExtension } from '@backstage/plugin-home'; +import { PagerDutyCardProps } from './components/PagerDutyHomepageCard/Content'; export const rootRouteRef = createRouteRef({ id: 'pagerduty', @@ -51,7 +53,25 @@ export const EntityPagerDutyCard = pagerDutyPlugin.provide( name: 'EntityPagerDutyCard', component: { lazy: () => - import('./components/PagerDutyCard').then(m => m.PagerDutyCard), + import('./components/EntityPagerDutyCard').then( + m => m.EntityPagerDutyCard, + ), }, }), ); + +export const homePlugin = createPlugin({ + id: 'pagerduty-home', + routes: { + root: rootRouteRef, + }, +}); + +/** @public */ +export const PagerDutyHomepageCard = homePlugin.provide( + createCardExtension({ + name: 'PagerDutyHomepageCard', + title: 'Pager Duty Homepage Card', + components: () => import('./components/PagerDutyHomepageCard'), + }), +); diff --git a/yarn.lock b/yarn.lock index bae4aa0e17..7778cb8e7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7681,6 +7681,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" + "@backstage/plugin-home": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.12.2 From f3f8b722d198eccb6cd2d0db6807a730cb4654be Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Mon, 8 May 2023 08:19:27 +0100 Subject: [PATCH 019/255] some little fixes Signed-off-by: Brian Fletcher --- plugins/pagerduty/dev/index.tsx | 23 +++++++++++++++++++ .../src/components/TriggerButton/index.tsx | 9 ++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/plugins/pagerduty/dev/index.tsx b/plugins/pagerduty/dev/index.tsx index e45e0ffd8f..0dca77cc76 100644 --- a/plugins/pagerduty/dev/index.tsx +++ b/plugins/pagerduty/dev/index.tsx @@ -25,6 +25,7 @@ import { PagerDutyIncident, PagerDutyChangeEvent, } from '../src/components/types'; +import { PagerDutyEntity } from '../src'; const mockEntity: Entity = { apiVersion: 'backstage.io/v1alpha1', @@ -46,6 +47,28 @@ const mockEntity: Entity = { }; const mockPagerDutyApi: PagerDutyApi = { + async getServiceByPagerDutyEntity(pagerDutyEntity: PagerDutyEntity) { + return { + service: { + name: pagerDutyEntity.name, + integrationKey: 'key', + id: '123', + html_url: 'http://service', + escalation_policy: { + id: '123', + html_url: 'http://escalationpolicy', + user: { + id: '123', + summary: 'summary', + email: 'email@email.com', + html_url: 'http://user', + name: 'some-user', + }, + }, + }, + }; + }, + async getServiceByEntity(entity: Entity) { return { service: { diff --git a/plugins/pagerduty/src/components/TriggerButton/index.tsx b/plugins/pagerduty/src/components/TriggerButton/index.tsx index 732298c6d8..1c12103d73 100644 --- a/plugins/pagerduty/src/components/TriggerButton/index.tsx +++ b/plugins/pagerduty/src/components/TriggerButton/index.tsx @@ -33,7 +33,7 @@ const useStyles = makeStyles(theme => ({ /** @public */ export function TriggerButton(props: { children?: ReactNode }) { const { buttonStyle } = useStyles(); - const { integrationKey } = usePagerdutyEntity(); + const { integrationKey, name } = usePagerdutyEntity(); const [dialogShown, setDialogShown] = useState(false); const showDialog = useCallback(() => { @@ -57,7 +57,12 @@ export function TriggerButton(props: { children?: ReactNode }) { : 'Missing integration key'} {integrationKey && ( - + )} ); From eaff299d3f7924014da715caaadaa0f18b6478ec Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Mon, 8 May 2023 09:11:49 +0100 Subject: [PATCH 020/255] api-report Signed-off-by: Brian Fletcher --- plugins/pagerduty/api-report.md | 35 ++++++++++++++----- .../components/EntityPagerDutyCard/index.tsx | 4 +-- .../PagerDutyHomepageCard/Content.tsx | 4 +-- .../components/PagerDutyHomepageCard/index.ts | 1 + plugins/pagerduty/src/components/index.ts | 5 ++- plugins/pagerduty/src/plugin.ts | 4 +-- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index b460086976..854b7b6f29 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -7,6 +7,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { CardExtensionProps } from '@backstage/plugin-home'; import { ConfigApi } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; @@ -14,7 +15,14 @@ import { FetchApi } from '@backstage/core-plugin-api'; import { ReactNode } from 'react'; // @public (undocumented) -export const EntityPagerDutyCard: (props: PagerDutyCardProps) => JSX.Element; +export const EntityPagerDutyCard: ( + props: EntityPagerDutyCardProps, +) => JSX.Element; + +// @public (undocumented) +export type EntityPagerDutyCardProps = { + readOnly?: boolean; +}; // @public (undocumented) const isPluginApplicableToEntity: (entity: Entity) => boolean; @@ -31,6 +39,9 @@ export interface PagerDutyApi { ): Promise; getOnCallByPolicyId(policyId: string): Promise; getServiceByEntity(entity: Entity): Promise; + getServiceByPagerDutyEntity( + pagerDutyEntity: PagerDutyEntity, + ): Promise; triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise; } @@ -44,14 +55,6 @@ export type PagerDutyAssignee = { html_url: string; }; -// @public (undocumented) -export const PagerDutyCard: (props: PagerDutyCardProps) => JSX.Element; - -// @public (undocumented) -export type PagerDutyCardProps = { - readOnly?: boolean; -}; - // @public (undocumented) export type PagerDutyChangeEvent = { id: string; @@ -98,6 +101,10 @@ export class PagerDutyClient implements PagerDutyApi { // (undocumented) getServiceByEntity(entity: Entity): Promise; // (undocumented) + getServiceByPagerDutyEntity( + pagerDutyEntity: PagerDutyEntity, + ): Promise; + // (undocumented) triggerAlarm(request: PagerDutyTriggerAlarmRequest): Promise; } @@ -119,6 +126,16 @@ export type PagerDutyEntity = { name: string; }; +// @public (undocumented) +export const PagerDutyHomepageCard: ( + props: CardExtensionProps, +) => JSX.Element; + +// @public (undocumented) +export type PagerDutyHomepageCardProps = PagerDutyEntity & { + readOnly?: boolean; +}; + // @public (undocumented) export type PagerDutyIncident = { id: string; diff --git a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx index e9e70e8f1b..50e6f3a096 100644 --- a/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx +++ b/plugins/pagerduty/src/components/EntityPagerDutyCard/index.tsx @@ -28,12 +28,12 @@ export const isPluginApplicableToEntity = (entity: Entity) => ); /** @public */ -export type PagerDutyCardProps = { +export type EntityPagerDutyCardProps = { readOnly?: boolean; }; /** @public */ -export const EntityPagerDutyCard = (props: PagerDutyCardProps) => { +export const EntityPagerDutyCard = (props: EntityPagerDutyCardProps) => { const { readOnly } = props; const { entity } = useEntity(); const pagerDutyEntity = getPagerDutyEntity(entity); diff --git a/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx b/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx index 479e1f621d..017c83e564 100644 --- a/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx +++ b/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx @@ -19,11 +19,11 @@ import { PagerDutyEntity } from '../../types'; import { PagerDutyCard } from '../PagerDutyCard'; /** @public */ -export type PagerDutyCardProps = PagerDutyEntity & { +export type PagerDutyHomepageCardProps = PagerDutyEntity & { readOnly?: boolean; }; /** @public */ -export const Content = (props: PagerDutyCardProps) => { +export const Content = (props: PagerDutyHomepageCardProps) => { return ; }; diff --git a/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts b/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts index fdbeb7cb70..834c01199f 100644 --- a/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts +++ b/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export { Content } from './Content'; +export type { PagerDutyHomepageCardProps } from './Content'; diff --git a/plugins/pagerduty/src/components/index.ts b/plugins/pagerduty/src/components/index.ts index cd093498d1..892290e401 100644 --- a/plugins/pagerduty/src/components/index.ts +++ b/plugins/pagerduty/src/components/index.ts @@ -23,9 +23,8 @@ export type { PagerDutyUser, } from './types'; -export type { PagerDutyCardProps } from './PagerDutyCard'; - -export { PagerDutyCard } from './PagerDutyCard'; +export type { EntityPagerDutyCardProps } from './EntityPagerDutyCard'; +export type { PagerDutyHomepageCardProps } from './PagerDutyHomepageCard'; export { isPluginApplicableToEntity, diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index 28ed6861b6..b31c2fdbd3 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -24,7 +24,7 @@ import { createComponentExtension, } from '@backstage/core-plugin-api'; import { createCardExtension } from '@backstage/plugin-home'; -import { PagerDutyCardProps } from './components/PagerDutyHomepageCard/Content'; +import { PagerDutyHomepageCardProps } from './components/PagerDutyHomepageCard/Content'; export const rootRouteRef = createRouteRef({ id: 'pagerduty', @@ -69,7 +69,7 @@ export const homePlugin = createPlugin({ /** @public */ export const PagerDutyHomepageCard = homePlugin.provide( - createCardExtension({ + createCardExtension({ name: 'PagerDutyHomepageCard', title: 'Pager Duty Homepage Card', components: () => import('./components/PagerDutyHomepageCard'), From 1298ab0003887aa9f9e54df2b77e2448a3096def Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Mon, 8 May 2023 14:29:22 +0100 Subject: [PATCH 021/255] add ability to configure the pagerduty settings Signed-off-by: Brian Fletcher --- plugins/pagerduty/src/plugin.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index b31c2fdbd3..8916690a24 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -73,5 +73,25 @@ export const PagerDutyHomepageCard = homePlugin.provide( name: 'PagerDutyHomepageCard', title: 'Pager Duty Homepage Card', components: () => import('./components/PagerDutyHomepageCard'), + settings: { + schema: { + title: 'Pagerduty', + type: 'object', + properties: { + integrationKey: { + title: 'Pager duty integration key', + type: 'string', + }, + serviceId: { + title: 'Pager duty service id', + type: 'string', + }, + name: { + title: 'Pager duty service name', + type: 'string', + }, + }, + }, + }, }), ); From cf34311cdbe13e663d07131f31ebd08cee8d6c6e Mon Sep 17 00:00:00 2001 From: Andreas Berger Date: Wed, 10 May 2023 15:23:29 +0200 Subject: [PATCH 022/255] scaffolder: extract `ui:*` fields from conditional fields. With this change the `ui:*` schema config is extracted also for the `then` and `else` branch of a conditional schema Signed-off-by: Andreas Berger --- .changeset/silent-kangaroos-pay.md | 5 ++ .../src/next/lib/schema.test.ts | 80 +++++++++++++++++++ .../scaffolder-react/src/next/lib/schema.ts | 19 ++++- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 .changeset/silent-kangaroos-pay.md diff --git a/.changeset/silent-kangaroos-pay.md b/.changeset/silent-kangaroos-pay.md new file mode 100644 index 0000000000..f3da827a34 --- /dev/null +++ b/.changeset/silent-kangaroos-pay.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Extract `ui:*` fields from conditional `then` and `else` schema branches. diff --git a/plugins/scaffolder-react/src/next/lib/schema.test.ts b/plugins/scaffolder-react/src/next/lib/schema.test.ts index 01e6cea3cc..4dc6cda603 100644 --- a/plugins/scaffolder-react/src/next/lib/schema.test.ts +++ b/plugins/scaffolder-react/src/next/lib/schema.test.ts @@ -411,4 +411,84 @@ describe('extractSchemaFromStep', () => { uiSchema: expectedUiSchema, }); }); + + it('transforms conditional schema', () => { + const inputSchema: JsonObject = { + type: 'object', + properties: { + flag: { + type: 'boolean', + }, + }, + if: { + properties: { + flag: { + const: true, + }, + }, + }, + then: { + properties: { + user: { + type: 'string', + 'ui:field': 'EntityPicker', + 'ui:options': { + catalogFilter: [{ kind: 'User' }], + }, + }, + }, + }, + else: { + properties: { + email: { + type: 'string', + }, + }, + }, + }; + const expectedSchema = { + type: 'object', + properties: { + flag: { + type: 'boolean', + }, + }, + if: { + properties: { + flag: { + const: true, + }, + }, + }, + then: { + properties: { + user: { + type: 'string', + }, + }, + }, + else: { + properties: { + email: { + type: 'string', + }, + }, + }, + }; + const expectedUiSchema = { + flag: {}, + user: { + 'ui:field': 'EntityPicker', + 'ui:options': { + catalogFilter: [{ kind: 'User' }], + }, + }, + email: {}, + }; + + expect(extractSchemaFromStep(inputSchema)).toEqual({ + schema: expectedSchema, + uiSchema: expectedUiSchema, + }); + }); }); diff --git a/plugins/scaffolder-react/src/next/lib/schema.ts b/plugins/scaffolder-react/src/next/lib/schema.ts index e8b819be4b..c606d715e7 100644 --- a/plugins/scaffolder-react/src/next/lib/schema.ts +++ b/plugins/scaffolder-react/src/next/lib/schema.ts @@ -25,7 +25,16 @@ function extractUiSchema(schema: JsonObject, uiSchema: JsonObject) { return; } - const { properties, items, anyOf, oneOf, allOf, dependencies } = schema; + const { + properties, + items, + anyOf, + oneOf, + allOf, + dependencies, + then, + else: _else, + } = schema; for (const propName in schema) { if (!schema.hasOwnProperty(propName)) { @@ -96,6 +105,14 @@ function extractUiSchema(schema: JsonObject, uiSchema: JsonObject) { extractUiSchema(schemaNode, uiSchema); } } + + if (isObject(then)) { + extractUiSchema(then, uiSchema); + } + + if (isObject(_else)) { + extractUiSchema(_else, uiSchema); + } } /** From 30e6edd7f6a556e7648da4213f56503af74158bf Mon Sep 17 00:00:00 2001 From: Andreas Berger Date: Thu, 11 May 2023 12:43:21 +0200 Subject: [PATCH 023/255] Add support for dry run for `gitlab:group:ensureExists` action. Signed-off-by: Andreas Berger --- .changeset/five-turtles-play.md | 5 +++ ...reateGitlabGroupEnsureExistsAction.test.ts | 31 +++++++++++++++++++ .../createGitlabGroupEnsureExistsAction.ts | 8 ++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .changeset/five-turtles-play.md diff --git a/.changeset/five-turtles-play.md b/.changeset/five-turtles-play.md new file mode 100644 index 0000000000..447cd60b3f --- /dev/null +++ b/.changeset/five-turtles-play.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +Add support for dry run for `gitlab:group:ensureExists` action. diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.test.ts index 6fb119413f..3c67477ed5 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.test.ts @@ -137,4 +137,35 @@ describe('gitlab:group:ensureExists', () => { expect(mockContext.output).toHaveBeenCalledWith('groupId', 42); }); + + it('should not call API on dryRun', async () => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, + }); + const integrations = ScmIntegrations.fromConfig(config); + + const action = createGitlabGroupEnsureExistsAction({ integrations }); + + await action.handler({ + ...mockContext, + isDryRun: true, + input: { + repoUrl: 'gitlab.com', + path: ['foo', 'bar'], + }, + }); + + expect(mockGitlabClient.Groups.search).not.toHaveBeenCalled(); + expect(mockGitlabClient.Groups.create).not.toHaveBeenCalled(); + + expect(mockContext.output).toHaveBeenCalledWith('groupId', 42); + }); }); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.ts index 698d402697..644af12b5d 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabGroupEnsureExistsAction.ts @@ -35,6 +35,7 @@ export const createGitlabGroupEnsureExistsAction = (options: { return createTemplateAction({ id: 'gitlab:group:ensureExists', description: 'Ensures a Gitlab group exists', + supportsDryRun: true, schema: { input: commonGitlabConfig.and( z.object({ @@ -52,6 +53,11 @@ export const createGitlabGroupEnsureExistsAction = (options: { }), }, async handler(ctx) { + if (ctx.isDryRun) { + ctx.output('groupId', 42); + return; + } + const { path } = ctx.input; const { token, integrationConfig } = getToken(ctx.input, integrations); @@ -66,7 +72,7 @@ export const createGitlabGroupEnsureExistsAction = (options: { const fullPath = `${currentPath}/${pathElement}`; const result = (await api.Groups.search( fullPath, - )) as any as Array; // recast since the return type for search is wrong in the gitbeaker typings + )) as unknown as Array; // recast since the return type for search is wrong in the gitbeaker typings const subGroup = result.find( searchPathElem => searchPathElem.full_path === fullPath, ); From c03307ded3e6c28306eacb50af52245de00ee35e Mon Sep 17 00:00:00 2001 From: David Weber Date: Fri, 12 May 2023 19:47:05 +0200 Subject: [PATCH 024/255] fix: the check description is now rendered with markdown Signed-off-by: David Weber --- .changeset/poor-months-switch.md | 5 ++ plugins/tech-insights/dev/index.tsx | 37 ++++++++++++++- plugins/tech-insights/dev/mocks.tsx | 46 +++++++++++++++++++ .../ScorecardsList/ScorecardsList.tsx | 9 +++- 4 files changed, 93 insertions(+), 4 deletions(-) create mode 100644 .changeset/poor-months-switch.md create mode 100644 plugins/tech-insights/dev/mocks.tsx diff --git a/.changeset/poor-months-switch.md b/.changeset/poor-months-switch.md new file mode 100644 index 0000000000..732152a965 --- /dev/null +++ b/.changeset/poor-months-switch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-insights': patch +--- + +The check description is now rendered with markdown. diff --git a/plugins/tech-insights/dev/index.tsx b/plugins/tech-insights/dev/index.tsx index 46337cf5fe..c5deda2ea5 100644 --- a/plugins/tech-insights/dev/index.tsx +++ b/plugins/tech-insights/dev/index.tsx @@ -18,12 +18,45 @@ import { createDevApp } from '@backstage/dev-utils'; import { techInsightsPlugin, EntityTechInsightsScorecardContent, -} from '../src/plugin'; + techInsightsApiRef, + TechInsightsApi, + Check, +} from '../src'; +import { CompoundEntityRef, Entity } from '@backstage/catalog-model'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { checkResultRenderers, runChecksResponse } from './mocks'; + +const entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'random-name', + }, +} as Entity; createDevApp() .registerPlugin(techInsightsPlugin) + .registerApi({ + api: techInsightsApiRef, + deps: {}, + factory: () => + ({ + getCheckResultRenderers: (_: string[]) => checkResultRenderers, + getAllChecks: async () => [], + runChecks: async (_: CompoundEntityRef, __?: string[]) => + runChecksResponse, + runBulkChecks: async (_: CompoundEntityRef[], __?: Check[]) => + '' as any, + getFacts: async (_: CompoundEntityRef, __: string[]) => '' as any, + getFactSchemas: async () => [], + } as TechInsightsApi), + }) .addPage({ - element: , + element: ( + + + + ), title: 'Root Page', path: '/tech-insight-scorecard', }) diff --git a/plugins/tech-insights/dev/mocks.tsx b/plugins/tech-insights/dev/mocks.tsx new file mode 100644 index 0000000000..eec9b66054 --- /dev/null +++ b/plugins/tech-insights/dev/mocks.tsx @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { CheckResult } from '@backstage/plugin-tech-insights-common'; +import { BooleanCheck, CheckResultRenderer } from '../src'; + +export const runChecksResponse = [ + { + facts: { + 'fact-id': { + id: '1', + type: 'boolean', + description: 'fact description', + value: true, + }, + }, + check: { + id: 'fact-id', + type: 'boolean', + name: 'The check name', + description: `The check description \nusing markdown **bold** and a [link](https://backstage.io)`, + factIds: ['1'], + }, + result: true, + } as CheckResult, +]; + +export const checkResultRenderers = [ + { + type: 'boolean', + component: (check: CheckResult) => , + } as CheckResultRenderer, +]; diff --git a/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx b/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx index 9973abef71..e8e57b0afe 100644 --- a/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx +++ b/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx @@ -17,10 +17,11 @@ import React from 'react'; import { useApi } from '@backstage/core-plugin-api'; import { makeStyles, List, ListItem, ListItemText } from '@material-ui/core'; -import { techInsightsApiRef } from '../../api/TechInsightsApi'; +import { techInsightsApiRef } from '../../api'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; import { BackstageTheme } from '@backstage/theme'; import { Alert } from '@material-ui/lab'; +import { MarkdownContent } from '@backstage/core-components'; const useStyles = makeStyles((theme: BackstageTheme) => ({ listItemText: { @@ -49,7 +50,11 @@ export const ScorecardsList = (props: { checkResults: CheckResult[] }) => { key={index} primary={result.check.name} secondary={ - description ? description(result) : result.check.description + description ? ( + description(result) + ) : ( + + ) } className={classes.listItemText} /> From 33eae4b39a955d6c9a23d4b7535260161ceae524 Mon Sep 17 00:00:00 2001 From: kmarkow Date: Tue, 16 May 2023 10:09:18 -0400 Subject: [PATCH 025/255] Deprecate target field and make targetRef required Signed-off-by: kmarkow --- .changeset/sour-papayas-wink.md | 5 +++++ .github/vale/Vocab/Backstage/accept.txt | 1 + .../src/schema/shared/common.schema.json | 5 +++-- .../validation/entityKindSchemaValidator.test.ts | 6 +++++- .../src/validation/entitySchemaValidator.test.ts | 13 +++++++++++-- 5 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 .changeset/sour-papayas-wink.md diff --git a/.changeset/sour-papayas-wink.md b/.changeset/sour-papayas-wink.md new file mode 100644 index 0000000000..30d5110fff --- /dev/null +++ b/.changeset/sour-papayas-wink.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': minor +--- + +Deprecate target field and make targetRef required in common.schema.json diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 83f05e390d..f1b9de492c 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -361,6 +361,7 @@ SVGs talkdesk Talkdesk Tanzu +targetRef tasklist techdocs Telenor diff --git a/packages/catalog-model/src/schema/shared/common.schema.json b/packages/catalog-model/src/schema/shared/common.schema.json index 5a5eb97bb7..77eb4a2612 100644 --- a/packages/catalog-model/src/schema/shared/common.schema.json +++ b/packages/catalog-model/src/schema/shared/common.schema.json @@ -32,7 +32,7 @@ "$id": "#relation", "type": "object", "description": "A directed relation from one entity to another.", - "required": ["type", "target"], + "required": ["type", "targetRef"], "additionalProperties": false, "properties": { "type": { @@ -42,7 +42,8 @@ "description": "The type of relation." }, "target": { - "$ref": "#reference" + "$ref": "#reference", + "deprecated": true }, "targetRef": { "type": "string", diff --git a/packages/catalog-model/src/validation/entityKindSchemaValidator.test.ts b/packages/catalog-model/src/validation/entityKindSchemaValidator.test.ts index ee8f39b140..14cfcbe50a 100644 --- a/packages/catalog-model/src/validation/entityKindSchemaValidator.test.ts +++ b/packages/catalog-model/src/validation/entityKindSchemaValidator.test.ts @@ -51,7 +51,11 @@ describe('entityKindSchemaValidator', () => { owner: 'me', }, relations: [ - { type: 't', target: { kind: 'k', namespace: 'ns', name: 'n' } }, + { + type: 't', + targetRef: 'someTargetRef', + target: { kind: 'k', namespace: 'ns', name: 'n' }, + }, ], status: { items: [ diff --git a/packages/catalog-model/src/validation/entitySchemaValidator.test.ts b/packages/catalog-model/src/validation/entitySchemaValidator.test.ts index 12f890d9db..7ae6a1fa05 100644 --- a/packages/catalog-model/src/validation/entitySchemaValidator.test.ts +++ b/packages/catalog-model/src/validation/entitySchemaValidator.test.ts @@ -52,7 +52,11 @@ describe('entitySchemaValidator', () => { owner: 'me', }, relations: [ - { type: 't', target: { kind: 'k', namespace: 'ns', name: 'n' } }, + { + type: 't', + targetRef: 'someTargetRef', + target: { kind: 'k', namespace: 'ns', name: 'n' }, + }, ], status: { items: [ @@ -361,8 +365,13 @@ describe('entitySchemaValidator', () => { expect(() => validator(entity)).toThrow(/relations/); }); - it('rejects missing relations.target', () => { + it('does not reject missing relations.target', () => { delete entity.relations[0].target; + expect(() => validator(entity)).not.toThrow(/relations/); + }); + + it('does reject missing relations.targetRef', () => { + delete entity.relations[0].targetRef; expect(() => validator(entity)).toThrow(/relations/); }); From 79ec83067415d30abddbb682e9842cd3931cfc56 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 16 May 2023 15:25:12 -0400 Subject: [PATCH 026/255] feat: Add MkDocs strict to check nav Signed-off-by: Adam Harvey --- .github/workflows/verify_microsite.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index e5037684e9..3d1da77ac4 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -37,6 +37,9 @@ jobs: - name: build API reference run: yarn build:api-docs + - name: Build MkDocs for TechDocs + run: mkdocs build --strict + - name: verify yarn dependency duplicates run: node scripts/verify-lockfile-duplicates.js From 9c8d98b2cf7c75187402b509716729ee9f8500b0 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 16 May 2023 15:25:39 -0400 Subject: [PATCH 027/255] chore: Remove new workflow Signed-off-by: Adam Harvey --- .github/workflows/verify_techdocs_mkdocs.yml | 42 -------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/verify_techdocs_mkdocs.yml diff --git a/.github/workflows/verify_techdocs_mkdocs.yml b/.github/workflows/verify_techdocs_mkdocs.yml deleted file mode 100644 index 288afa238b..0000000000 --- a/.github/workflows/verify_techdocs_mkdocs.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Verify TechDocs MkDocs -on: - pull_request: - paths: - - 'docs/**' - - 'mkdocs.yml' - -jobs: - build-mkdocs: - runs-on: ubuntu-latest - env: - CI: true - NODE_OPTIONS: --max-old-space-size=4096 - steps: - - uses: actions/checkout@v3 - - # Generate the API Reference docs which MkDocs cross-references - - - name: use node.js 18.x - uses: actions/setup-node@v3 - with: - node-version: 18.x - - - name: yarn install - uses: backstage/actions/yarn-install@v0.6.3 - with: - cache-prefix: ${{ runner.os }}-v18.x - - - name: build API reference - run: yarn build:api-docs - - # Setup and start the MkDocs configuration - - - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - - name: Install dependencies - run: pip3 install mkdocs mkdocs-techdocs-core - - - name: Build MkDocs for TechDocs - run: mkdocs build --strict From 8983bd53a6a50fec72183ec5677686edaaab206c Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 16 May 2023 15:28:49 -0400 Subject: [PATCH 028/255] chore: Improve paths for Microsite actions Signed-off-by: Adam Harvey --- .github/workflows/verify_microsite-noop.yml | 2 ++ .github/workflows/verify_microsite.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/verify_microsite-noop.yml b/.github/workflows/verify_microsite-noop.yml index 0fa23812f5..cca7e512c5 100644 --- a/.github/workflows/verify_microsite-noop.yml +++ b/.github/workflows/verify_microsite-noop.yml @@ -6,7 +6,9 @@ on: pull_request: paths-ignore: - '.github/workflows/verify_microsite-next.yml' + - '.github/workflows/verify_microsite.yml' - 'microsite/**' + - 'mkdocs.yml' - 'docs/**' jobs: diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 3d1da77ac4..9bb662f21c 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -4,7 +4,9 @@ on: pull_request: paths: - '.github/workflows/verify_microsite-next.yml' + - '.github/workflows/verify_microsite.yml' - 'microsite/**' + - 'mkdocs.yml' - 'docs/**' jobs: From 01dea69d26fc65209b62496728e283a3fb78b1a3 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Tue, 16 May 2023 16:27:47 -0400 Subject: [PATCH 029/255] fix: Install MkDocs dependencies Signed-off-by: Adam Harvey --- .github/workflows/verify_microsite.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 9bb662f21c..d219d0ea74 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -39,6 +39,9 @@ jobs: - name: build API reference run: yarn build:api-docs + - name: Install MkDocs dependencies + run: pip3 install mkdocs mkdocs-techdocs-core + - name: Build MkDocs for TechDocs run: mkdocs build --strict From 11e0f625583f09a24c960103ef9cecd10878a5d2 Mon Sep 17 00:00:00 2001 From: Sergey Shevchenko Date: Wed, 17 May 2023 11:37:51 +0300 Subject: [PATCH 030/255] fix: Fix Fix wrong gitlabUrl format in repoUrl input description Signed-off-by: Sergey Shevchenko --- .changeset/modern-dancers-sit.md | 5 +++++ .../src/scaffolder/actions/builtin/publish/gitlab.ts | 1 + .../scaffolder/actions/builtin/publish/gitlabMergeRequest.ts | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/modern-dancers-sit.md diff --git a/.changeset/modern-dancers-sit.md b/.changeset/modern-dancers-sit.md new file mode 100644 index 0000000000..2f933cf8e2 --- /dev/null +++ b/.changeset/modern-dancers-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Fix wrong gitlabUrl format in repoUrl input description diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts index a621d27d55..4045fd6958 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts @@ -57,6 +57,7 @@ export function createPublishGitlabAction(options: { repoUrl: { title: 'Repository Location', type: 'string', + description: `Accepts the format 'gitlab.com?repo=project_name&owner=group_name' where 'project_name' is the repository name and 'group_name' is a group or username`, }, repoVisibility: { title: 'Repository Visibility', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts index 06e056cda3..d802147922 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts @@ -57,7 +57,7 @@ export const createPublishGitlabMergeRequestAction = (options: { repoUrl: { type: 'string', title: 'Repository Location', - description: `Accepts the format 'gitlab.com/group_name/project_name' where 'project_name' is the repository name and 'group_name' is a group or username`, + description: `Accepts the format 'gitlab.com?repo=project_name&owner=group_name' where 'project_name' is the repository name and 'group_name' is a group or username`, }, /** @deprecated projectID is passed as query parameters in the repoUrl */ projectid: { From ab6492255992d31dfe9577c9bcca67350cda7522 Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Fri, 19 May 2023 15:54:12 +0100 Subject: [PATCH 031/255] review comments addressed Signed-off-by: Brian Fletcher --- plugins/pagerduty/api-report.md | 27 ++-- plugins/pagerduty/dev/index.tsx | 151 +----------------- plugins/pagerduty/dev/mockEntity.ts | 35 ++++ plugins/pagerduty/dev/mockPagerDutyApi.ts | 146 +++++++++++++++++ .../Content.tsx | 4 +- .../index.ts | 2 +- plugins/pagerduty/src/components/index.ts | 2 +- plugins/pagerduty/src/index.ts | 7 +- plugins/pagerduty/src/plugin.ts | 18 +-- 9 files changed, 218 insertions(+), 174 deletions(-) create mode 100644 plugins/pagerduty/dev/mockEntity.ts create mode 100644 plugins/pagerduty/dev/mockPagerDutyApi.ts rename plugins/pagerduty/src/components/{PagerDutyHomepageCard => HomePagePagerDutyCard}/Content.tsx (86%) rename plugins/pagerduty/src/components/{PagerDutyHomepageCard => HomePagePagerDutyCard}/index.ts (91%) diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index 854b7b6f29..7ab3a5e1af 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -20,7 +20,19 @@ export const EntityPagerDutyCard: ( ) => JSX.Element; // @public (undocumented) -export type EntityPagerDutyCardProps = { +type EntityPagerDutyCardProps = { + readOnly?: boolean; +}; +export { EntityPagerDutyCardProps }; +export { EntityPagerDutyCardProps as PagerDutyCardProps }; + +// @public (undocumented) +export const HomePagePagerDutyCard: ( + props: CardExtensionProps, +) => JSX.Element; + +// @public (undocumented) +export type HomePagePagerDutyCardProps = PagerDutyEntity & { readOnly?: boolean; }; @@ -55,6 +67,9 @@ export type PagerDutyAssignee = { html_url: string; }; +// @public (undocumented) +export const PagerDutyCard: (props: EntityPagerDutyCardProps) => JSX.Element; + // @public (undocumented) export type PagerDutyChangeEvent = { id: string; @@ -126,16 +141,6 @@ export type PagerDutyEntity = { name: string; }; -// @public (undocumented) -export const PagerDutyHomepageCard: ( - props: CardExtensionProps, -) => JSX.Element; - -// @public (undocumented) -export type PagerDutyHomepageCardProps = PagerDutyEntity & { - readOnly?: boolean; -}; - // @public (undocumented) export type PagerDutyIncident = { id: string; diff --git a/plugins/pagerduty/dev/index.tsx b/plugins/pagerduty/dev/index.tsx index 0dca77cc76..4ed8e07263 100644 --- a/plugins/pagerduty/dev/index.tsx +++ b/plugins/pagerduty/dev/index.tsx @@ -15,159 +15,12 @@ */ import React from 'react'; -import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-react'; import { createDevApp } from '@backstage/dev-utils'; import { pagerDutyPlugin, EntityPagerDutyCard } from '../src/plugin'; import { pagerDutyApiRef } from '../src/api'; -import { PagerDutyApi, PagerDutyTriggerAlarmRequest } from '../src/api/types'; -import { - PagerDutyIncident, - PagerDutyChangeEvent, -} from '../src/components/types'; -import { PagerDutyEntity } from '../src'; - -const mockEntity: Entity = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'backstage', - description: 'backstage.io', - annotations: { - 'github.com/project-slug': 'backstage/backstage', - 'pagerduty.com/service-id': 'foo', - 'pagerduty.com/integration-key': 'foo', - }, - }, - spec: { - lifecycle: 'production', - type: 'website', - owner: 'user:guest', - }, -}; - -const mockPagerDutyApi: PagerDutyApi = { - async getServiceByPagerDutyEntity(pagerDutyEntity: PagerDutyEntity) { - return { - service: { - name: pagerDutyEntity.name, - integrationKey: 'key', - id: '123', - html_url: 'http://service', - escalation_policy: { - id: '123', - html_url: 'http://escalationpolicy', - user: { - id: '123', - summary: 'summary', - email: 'email@email.com', - html_url: 'http://user', - name: 'some-user', - }, - }, - }, - }; - }, - - async getServiceByEntity(entity: Entity) { - return { - service: { - name: entity.metadata.name, - integrationKey: 'key', - id: '123', - html_url: 'http://service', - escalation_policy: { - id: '123', - html_url: 'http://escalationpolicy', - user: { - id: '123', - summary: 'summary', - email: 'email@email.com', - html_url: 'http://user', - name: 'some-user', - }, - }, - }, - }; - }, - - async getIncidentsByServiceId(serviceId: string) { - const incident = (title: string) => { - return { - id: '123', - title: title, - status: 'acknowledged', - html_url: 'http://incident', - assignments: [ - { - assignee: { - id: '123', - summary: 'Jane Doe', - html_url: 'http://assignee', - }, - }, - ], - serviceId: serviceId, - created_at: '2015-10-06T21:30:42Z', - } as PagerDutyIncident; - }; - - return { - incidents: [ - incident('Some Alerting Incident'), - incident('Another Alerting Incident'), - ], - }; - }, - - async getChangeEventsByServiceId(serviceId: string) { - const changeEvent = (description: string) => { - return { - id: serviceId, - source: 'some-source', - html_url: 'http://changeevent', - links: [ - { - href: 'http://link', - text: 'link text', - }, - ], - summary: description, - timestamp: '2018-10-06T21:30:42Z', - } as PagerDutyChangeEvent; - }; - - return { - change_events: [ - changeEvent('us-east-1 deployment'), - changeEvent('us-west-2 deployment'), - ], - }; - }, - - async getOnCallByPolicyId() { - const oncall = (name: string, escalation: number) => { - return { - user: { - id: '123', - name: name, - html_url: 'http://assignee', - summary: 'summary', - email: 'email@email.com', - }, - escalation_level: escalation, - }; - }; - - return { - oncalls: [oncall('Jane Doe', 1), oncall('John Doe', 2)], - }; - }, - - async triggerAlarm(request: PagerDutyTriggerAlarmRequest) { - return new Response(request.description); - }, -}; +import { mockPagerDutyApi } from './mockPagerDutyApi'; +import { mockEntity } from './mockEntity'; createDevApp() .registerApi({ diff --git a/plugins/pagerduty/dev/mockEntity.ts b/plugins/pagerduty/dev/mockEntity.ts new file mode 100644 index 0000000000..251ba80706 --- /dev/null +++ b/plugins/pagerduty/dev/mockEntity.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Entity } from '@backstage/catalog-model'; + +export const mockEntity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'backstage', + description: 'backstage.io', + annotations: { + 'github.com/project-slug': 'backstage/backstage', + 'pagerduty.com/service-id': 'foo', + 'pagerduty.com/integration-key': 'foo', + }, + }, + spec: { + lifecycle: 'production', + type: 'website', + owner: 'user:guest', + }, +}; diff --git a/plugins/pagerduty/dev/mockPagerDutyApi.ts b/plugins/pagerduty/dev/mockPagerDutyApi.ts new file mode 100644 index 0000000000..f8836d7043 --- /dev/null +++ b/plugins/pagerduty/dev/mockPagerDutyApi.ts @@ -0,0 +1,146 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + PagerDutyApi, + PagerDutyChangeEvent, + PagerDutyEntity, + PagerDutyIncident, + PagerDutyTriggerAlarmRequest, +} from '../src'; +import { Entity } from '@backstage/catalog-model'; + +export const mockPagerDutyApi: PagerDutyApi = { + async getServiceByPagerDutyEntity(pagerDutyEntity: PagerDutyEntity) { + return { + service: { + name: pagerDutyEntity.name, + integrationKey: 'key', + id: '123', + html_url: 'http://service', + escalation_policy: { + id: '123', + html_url: 'http://escalationpolicy', + user: { + id: '123', + summary: 'summary', + email: 'email@email.com', + html_url: 'http://user', + name: 'some-user', + }, + }, + }, + }; + }, + + async getServiceByEntity(entity: Entity) { + return { + service: { + name: entity.metadata.name, + integrationKey: 'key', + id: '123', + html_url: 'http://service', + escalation_policy: { + id: '123', + html_url: 'http://escalationpolicy', + user: { + id: '123', + summary: 'summary', + email: 'email@email.com', + html_url: 'http://user', + name: 'some-user', + }, + }, + }, + }; + }, + + async getIncidentsByServiceId(serviceId: string) { + const incident = (title: string) => { + return { + id: '123', + title: title, + status: 'acknowledged', + html_url: 'http://incident', + assignments: [ + { + assignee: { + id: '123', + summary: 'Jane Doe', + html_url: 'http://assignee', + }, + }, + ], + serviceId: serviceId, + created_at: '2015-10-06T21:30:42Z', + } as PagerDutyIncident; + }; + + return { + incidents: [ + incident('Some Alerting Incident'), + incident('Another Alerting Incident'), + ], + }; + }, + + async getChangeEventsByServiceId(serviceId: string) { + const changeEvent = (description: string) => { + return { + id: serviceId, + source: 'some-source', + html_url: 'http://changeevent', + links: [ + { + href: 'http://link', + text: 'link text', + }, + ], + summary: description, + timestamp: '2018-10-06T21:30:42Z', + } as PagerDutyChangeEvent; + }; + + return { + change_events: [ + changeEvent('us-east-1 deployment'), + changeEvent('us-west-2 deployment'), + ], + }; + }, + + async getOnCallByPolicyId() { + const oncall = (name: string, escalation: number) => { + return { + user: { + id: '123', + name: name, + html_url: 'http://assignee', + summary: 'summary', + email: 'email@email.com', + }, + escalation_level: escalation, + }; + }; + + return { + oncalls: [oncall('Jane Doe', 1), oncall('John Doe', 2)], + }; + }, + + async triggerAlarm(request: PagerDutyTriggerAlarmRequest) { + return new Response(request.description); + }, +}; diff --git a/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx b/plugins/pagerduty/src/components/HomePagePagerDutyCard/Content.tsx similarity index 86% rename from plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx rename to plugins/pagerduty/src/components/HomePagePagerDutyCard/Content.tsx index 017c83e564..b665a6c294 100644 --- a/plugins/pagerduty/src/components/PagerDutyHomepageCard/Content.tsx +++ b/plugins/pagerduty/src/components/HomePagePagerDutyCard/Content.tsx @@ -19,11 +19,11 @@ import { PagerDutyEntity } from '../../types'; import { PagerDutyCard } from '../PagerDutyCard'; /** @public */ -export type PagerDutyHomepageCardProps = PagerDutyEntity & { +export type HomePagePagerDutyCardProps = PagerDutyEntity & { readOnly?: boolean; }; /** @public */ -export const Content = (props: PagerDutyHomepageCardProps) => { +export const Content = (props: HomePagePagerDutyCardProps) => { return ; }; diff --git a/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts b/plugins/pagerduty/src/components/HomePagePagerDutyCard/index.ts similarity index 91% rename from plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts rename to plugins/pagerduty/src/components/HomePagePagerDutyCard/index.ts index 834c01199f..e9038852a6 100644 --- a/plugins/pagerduty/src/components/PagerDutyHomepageCard/index.ts +++ b/plugins/pagerduty/src/components/HomePagePagerDutyCard/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ export { Content } from './Content'; -export type { PagerDutyHomepageCardProps } from './Content'; +export type { HomePagePagerDutyCardProps } from './Content'; diff --git a/plugins/pagerduty/src/components/index.ts b/plugins/pagerduty/src/components/index.ts index 892290e401..264d7195e9 100644 --- a/plugins/pagerduty/src/components/index.ts +++ b/plugins/pagerduty/src/components/index.ts @@ -24,7 +24,7 @@ export type { } from './types'; export type { EntityPagerDutyCardProps } from './EntityPagerDutyCard'; -export type { PagerDutyHomepageCardProps } from './PagerDutyHomepageCard'; +export type { HomePagePagerDutyCardProps } from './HomePagePagerDutyCard'; export { isPluginApplicableToEntity, diff --git a/plugins/pagerduty/src/index.ts b/plugins/pagerduty/src/index.ts index 0517b0752a..19b8456476 100644 --- a/plugins/pagerduty/src/index.ts +++ b/plugins/pagerduty/src/index.ts @@ -24,10 +24,15 @@ export { pagerDutyPlugin, pagerDutyPlugin as plugin, EntityPagerDutyCard, - PagerDutyHomepageCard, + HomePagePagerDutyCard, } from './plugin'; export * from './components'; + +// @deprecated Please use EntityPagerDutyCard +export { EntityPagerDutyCard as PagerDutyCard } from './components'; +export type { EntityPagerDutyCardProps as PagerDutyCardProps } from './components'; + export * from './api'; export type { PagerDutyEntity } from './types'; diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index 8916690a24..408df991e2 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -24,7 +24,7 @@ import { createComponentExtension, } from '@backstage/core-plugin-api'; import { createCardExtension } from '@backstage/plugin-home'; -import { PagerDutyHomepageCardProps } from './components/PagerDutyHomepageCard/Content'; +import { HomePagePagerDutyCardProps } from './components/HomePagePagerDutyCard/Content'; export const rootRouteRef = createRouteRef({ id: 'pagerduty', @@ -68,26 +68,26 @@ export const homePlugin = createPlugin({ }); /** @public */ -export const PagerDutyHomepageCard = homePlugin.provide( - createCardExtension({ - name: 'PagerDutyHomepageCard', - title: 'Pager Duty Homepage Card', - components: () => import('./components/PagerDutyHomepageCard'), +export const HomePagePagerDutyCard = homePlugin.provide( + createCardExtension({ + name: 'HomePagePagerDutyCard', + title: 'PagerDuty Homepage Card', + components: () => import('./components/HomePagePagerDutyCard'), settings: { schema: { title: 'Pagerduty', type: 'object', properties: { integrationKey: { - title: 'Pager duty integration key', + title: 'PagerDuty integration key', type: 'string', }, serviceId: { - title: 'Pager duty service id', + title: 'PagerDuty service id', type: 'string', }, name: { - title: 'Pager duty service name', + title: 'PagerDuty service name', type: 'string', }, }, From 36160b709a2e5cc1298568110c144e02a4a7a8a8 Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Fri, 19 May 2023 15:58:39 +0100 Subject: [PATCH 032/255] casing Signed-off-by: Brian Fletcher --- plugins/pagerduty/src/plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index 408df991e2..2cf330864d 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -75,7 +75,7 @@ export const HomePagePagerDutyCard = homePlugin.provide( components: () => import('./components/HomePagePagerDutyCard'), settings: { schema: { - title: 'Pagerduty', + title: 'PagerDuty', type: 'object', properties: { integrationKey: { From 2f660eb573ccccf16a0fac27466dd67eb3cfc2aa Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Wed, 17 May 2023 19:28:55 -0500 Subject: [PATCH 033/255] fix(search): Fix SearchBar styles & update StoryBook stories for custom styles for `notchedOutline` class Signed-off-by: Carlos Esteban Lopez --- .changeset/pink-goats-talk.md | 6 ++++++ .../src/components/SearchBar/SearchBar.stories.tsx | 4 ++-- .../src/components/SearchBar/SearchBar.tsx | 1 + .../HomePageComponent/HomePageSearchBar.stories.tsx | 4 ++++ .../HomePageComponent/HomePageSearchBar.tsx | 11 ++++++++++- 5 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 .changeset/pink-goats-talk.md diff --git a/.changeset/pink-goats-talk.md b/.changeset/pink-goats-talk.md new file mode 100644 index 0000000000..622c9f4188 --- /dev/null +++ b/.changeset/pink-goats-talk.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-react': patch +'@backstage/plugin-search': patch +--- + +Fix SearchBar styles & update StoryBook stories for custom styles for `notchedOutline` class. diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx index 7e97d790a7..3abd40b179 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx @@ -92,7 +92,7 @@ const useStyles = makeStyles({ borderRadius: '50px', margin: 'auto', }, - notchedOutline: { + searchBarOutline: { borderStyle: 'none', }, }); @@ -102,7 +102,7 @@ export const CustomStyles = () => { return ( ); diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.tsx index cfd0538694..ac4aee3c1d 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.tsx @@ -178,6 +178,7 @@ export const SearchBarBase: ForwardRefExoticComponent = fullWidth={fullWidth} onChange={handleChange} onKeyDown={handleKeyDown} + style={{ margin: 0 }} {...rest} /> diff --git a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.stories.tsx b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.stories.tsx index 4c20ddec39..9a10aaa41b 100644 --- a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.stories.tsx +++ b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.stories.tsx @@ -61,6 +61,9 @@ const useStyles = makeStyles(theme => ({ borderRadius: '50px', margin: 'auto', }, + searchBarOutline: { + borderStyle: 'none', + }, })); export const CustomStyles = () => { @@ -71,6 +74,7 @@ export const CustomStyles = () => { diff --git a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx index 4246f76ed0..9d126c6449 100644 --- a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx +++ b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx @@ -24,9 +24,11 @@ import { useNavigateToQuery } from '../util'; const useStyles = makeStyles({ root: { + fontSize: '1.5em', + }, + searchBarOutline: { border: '1px solid #555', borderRadius: '6px', - fontSize: '1.5em', }, }); @@ -64,6 +66,13 @@ export const HomePageSearchBar = (props: HomePageSearchBarProps) => { value={query} onSubmit={handleSubmit} onChange={handleChange} + InputProps={{ + classes: { + notchedOutline: classes.searchBarOutline, + ...props.InputProps?.classes, + }, + ...props.InputProps, + }} {...props} /> ); From ba261f9dcd058eee7dca7e41972c20417307eb4a Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 18 May 2023 13:00:37 +0200 Subject: [PATCH 034/255] fix(search): input base styles Signed-off-by: Camila Belo --- .../templates/DefaultTemplate.stories.tsx | 17 ++-- .../SearchAutocomplete.stories.tsx | 59 ++++--------- .../SearchAutocomplete/SearchAutocomplete.tsx | 34 +++++--- .../SearchBar/SearchBar.stories.tsx | 58 +++++-------- .../src/components/SearchBar/SearchBar.tsx | 1 - .../HomePageComponent/HomePageSearchBar.tsx | 4 +- .../src/search/components/TechDocsSearch.tsx | 82 ++++++++----------- 7 files changed, 104 insertions(+), 151 deletions(-) diff --git a/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx b/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx index 9cf2e8ab35..52fab3c55f 100644 --- a/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx +++ b/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx @@ -140,15 +140,16 @@ export default { }; const useStyles = makeStyles(theme => ({ - searchBar: { - display: 'flex', + searchBarInput: { maxWidth: '60vw', - backgroundColor: theme.palette.background.paper, - boxShadow: theme.shadows[1], - padding: '8px 0', - borderRadius: '50px', margin: 'auto', + backgroundColor: theme.palette.background.paper, + borderRadius: '50px', + boxShadow: theme.shadows[1], }, + searchBarOutline: { + borderStyle: 'none' + } })); const useLogoStyles = makeStyles(theme => ({ @@ -177,9 +178,9 @@ export const DefaultTemplate = () => { className={container} logo={} /> - + diff --git a/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.stories.tsx b/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.stories.tsx index a43b6b048e..6db7955ec8 100644 --- a/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.stories.tsx +++ b/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.stories.tsx @@ -16,7 +16,7 @@ import React, { ComponentType } from 'react'; -import { Grid, makeStyles, Paper } from '@material-ui/core'; +import { Grid } from '@material-ui/core'; import LabelIcon from '@material-ui/icons/Label'; import { TestApiProvider } from '@backstage/test-utils'; @@ -45,51 +45,24 @@ export default { ], }; -const useStyles = makeStyles(theme => ({ - root: { - padding: theme.spacing(1), - }, -})); - export const Default = () => { - const classes = useStyles(); - return ( - - - - ); + return ; }; export const Outlined = () => { - const classes = useStyles(); - return ( - - - - ); + return ; }; export const Initialized = () => { - const classes = useStyles(); const options = ['hello-word', 'petstore', 'spotify']; - return ( - - - - ); + return ; }; export const LoadingOptions = () => { - const classes = useStyles(); - return ( - - - - ); + return ; }; export const RenderingCustomOptions = () => { - const classes = useStyles(); const options = [ { title: 'hello-world', @@ -106,17 +79,15 @@ export const RenderingCustomOptions = () => { ]; return ( - - ( - } - primaryText={option.title} - secondaryText={option.text} - /> - )} - /> - + ( + } + primaryText={option.title} + secondaryText={option.text} + /> + )} + /> ); }; diff --git a/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.tsx b/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.tsx index 8a5ae7a3ce..2a3cb6bee4 100644 --- a/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.tsx +++ b/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.tsx @@ -16,7 +16,7 @@ import React, { ChangeEvent, useCallback, useMemo } from 'react'; -import { CircularProgress } from '@material-ui/core'; +import { CircularProgress, makeStyles } from '@material-ui/core'; import { Autocomplete, AutocompleteProps, @@ -28,6 +28,13 @@ import { import { SearchContextProvider, useSearch } from '../../context'; import { SearchBar, SearchBarProps } from '../SearchBar'; +const useStyles = makeStyles(theme => ({ + loading: { + right: theme.spacing(1), + position: 'absolute', + }, +})); + /** * Props for {@link SearchAutocomplete}. * @@ -61,6 +68,18 @@ const withContext = ( ); }; +const SearchAutocompleteLoadingAdornment = () => { + const classes = useStyles(); + return ( + + ); +}; + /** * Recommended search autocomplete when you use the Search Provider or Search Context. * @@ -116,7 +135,7 @@ export const SearchAutocomplete = withContext( const renderInput = useCallback( ({ - InputProps: { ref, endAdornment }, + InputProps: { ref, className, endAdornment }, InputLabelProps, ...params }: AutocompleteRenderInputParams) => ( @@ -128,16 +147,9 @@ export const SearchAutocomplete = withContext( placeholder={inputPlaceholder} debounceTime={inputDebounceTime} endAdornment={ - loading ? ( - - ) : ( - endAdornment - ) + loading ? : endAdornment } + InputProps={{ className }} /> ), [loading, inputValue, inputPlaceholder, inputDebounceTime], diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx index 3abd40b179..05a46d11e1 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.stories.tsx @@ -15,7 +15,7 @@ */ import React, { ComponentType } from 'react'; -import { Grid, makeStyles, Paper } from '@material-ui/core'; +import { Grid, makeStyles } from '@material-ui/core'; import { TestApiProvider } from '@backstage/test-utils'; @@ -43,67 +43,51 @@ export default { }; export const Default = () => { - return ( - - - - ); + return ; }; export const CustomPlaceholder = () => { - return ( - - - - ); + return ; }; export const CustomLabel = () => { - return ( - - - - ); + return ; }; export const Focused = () => { return ( - - {/* decision up to adopter, read https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-autofocus.md#no-autofocus */} - {/* eslint-disable-next-line jsx-a11y/no-autofocus */} - - + // decision up to adopter, read https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-autofocus.md#no-autofocus + // eslint-disable-next-line jsx-a11y/no-autofocus + ); }; export const WithoutClearButton = () => { - return ( - - - - ); + return ; }; -const useStyles = makeStyles({ - search: { - display: 'flex', - justifyContent: 'space-between', +const useStyles = makeStyles(theme => ({ + searchBarRoot: { padding: '8px 16px', + background: theme.palette.background.paper, + boxShadow: theme.shadows[1], borderRadius: '50px', - margin: 'auto', }, searchBarOutline: { borderStyle: 'none', }, -}); +})); export const CustomStyles = () => { const classes = useStyles(); return ( - - - + ); }; diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.tsx index ac4aee3c1d..cfd0538694 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.tsx @@ -178,7 +178,6 @@ export const SearchBarBase: ForwardRefExoticComponent = fullWidth={fullWidth} onChange={handleChange} onKeyDown={handleKeyDown} - style={{ margin: 0 }} {...rest} /> diff --git a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx index 9d126c6449..4b9161aff7 100644 --- a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx +++ b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx @@ -23,7 +23,7 @@ import { import { useNavigateToQuery } from '../util'; const useStyles = makeStyles({ - root: { + searchBarRoot: { fontSize: '1.5em', }, searchBarOutline: { @@ -62,12 +62,12 @@ export const HomePageSearchBar = (props: HomePageSearchBarProps) => { return ( ({ - root: { - width: '100%', - }, - bar: { - padding: theme.spacing(1), - }, -})); - /** * Props for {@link TechDocsSearch} * @@ -75,7 +65,6 @@ const TechDocsSearchBar = (props: TechDocsSearchProps) => { setFilters, result: { loading, value: searchVal }, } = useSearch(); - const classes = useStyles(); const [options, setOptions] = useState([]); useEffect(() => { let mounted = true; @@ -117,43 +106,40 @@ const TechDocsSearchBar = (props: TechDocsSearchProps) => { }; return ( - - ''} - filterOptions={x => { - return x; // This is needed to get renderOption to be called after options change. Bug in material-ui? - }} - onClose={() => { - setOpen(false); - }} - onFocus={() => { - setOpen(true); - }} - onChange={handleSelection} - blurOnSelect - noOptionsText="No results found" - value={null} - options={options} - renderOption={({ document, highlight }) => ( - - )} - loading={loading} - inputDebounceTime={debounceTime} - inputPlaceholder={`Search ${entityTitle || entityId.name} docs`} - freeSolo={false} - /> - + ''} + filterOptions={x => { + return x; // This is needed to get renderOption to be called after options change. Bug in material-ui? + }} + onClose={() => { + setOpen(false); + }} + onFocus={() => { + setOpen(true); + }} + onChange={handleSelection} + blurOnSelect + noOptionsText="No results found" + value={null} + options={options} + renderOption={({ document, highlight }) => ( + + )} + loading={loading} + inputDebounceTime={debounceTime} + inputPlaceholder={`Search ${entityTitle || entityId.name} docs`} + freeSolo={false} + /> ); }; From 6b281bda6641ed61b7c0a537ccbb9255edb868ba Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Fri, 19 May 2023 10:59:49 -0500 Subject: [PATCH 035/255] fix(search): Fix accidental HomePageSearchBar input classes override on spread operator Signed-off-by: Carlos Esteban Lopez --- .changeset/pink-goats-talk.md | 1 + .../src/components/HomePageComponent/HomePageSearchBar.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/pink-goats-talk.md b/.changeset/pink-goats-talk.md index 622c9f4188..3b67af81f4 100644 --- a/.changeset/pink-goats-talk.md +++ b/.changeset/pink-goats-talk.md @@ -1,6 +1,7 @@ --- '@backstage/plugin-search-react': patch '@backstage/plugin-search': patch +'@backstage/plugin-techdocs': patch --- Fix SearchBar styles & update StoryBook stories for custom styles for `notchedOutline` class. diff --git a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx index 4b9161aff7..b829bf8ac2 100644 --- a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx +++ b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.tsx @@ -66,12 +66,12 @@ export const HomePageSearchBar = (props: HomePageSearchBarProps) => { onSubmit={handleSubmit} onChange={handleChange} InputProps={{ + ...props.InputProps, classes: { root: classes.searchBarRoot, notchedOutline: classes.searchBarOutline, ...props.InputProps?.classes, }, - ...props.InputProps, }} {...props} /> From 40e775ccc5554b4201597b1562a152de815d84c0 Mon Sep 17 00:00:00 2001 From: Nicholas Bucher Date: Fri, 19 May 2023 15:13:07 -0400 Subject: [PATCH 036/255] Added solo.io portal plugin information and logo. Signed-off-by: Nicholas Bucher --- microsite/data/plugins/solo-io-dev-portal.yaml | 10 ++++++++++ microsite/static/img/solo-io-gloo-outline.svg | 3 +++ 2 files changed, 13 insertions(+) create mode 100644 microsite/data/plugins/solo-io-dev-portal.yaml create mode 100644 microsite/static/img/solo-io-gloo-outline.svg diff --git a/microsite/data/plugins/solo-io-dev-portal.yaml b/microsite/data/plugins/solo-io-dev-portal.yaml new file mode 100644 index 0000000000..a2cf2eaa8b --- /dev/null +++ b/microsite/data/plugins/solo-io-dev-portal.yaml @@ -0,0 +1,10 @@ +--- +title: Solo.io Gloo Portal +author: Solo.io +authorUrl: https://solo.io +category: Infrastructure +description: View, test, and manage your Gloo Portal APIs through our Backstage plugin. +documentation: https://github.com/solo-io/dev-portal-backstage-public#readme +iconUrl: img/solo-io-gloo-outline.svg +npmPackageName: '@solo.io/dev-portal-backstage-plugin' +addedDate: '2023-05-19' diff --git a/microsite/static/img/solo-io-gloo-outline.svg b/microsite/static/img/solo-io-gloo-outline.svg new file mode 100644 index 0000000000..debb47038d --- /dev/null +++ b/microsite/static/img/solo-io-gloo-outline.svg @@ -0,0 +1,3 @@ + + + From 6816352500a767c694e2bba965de7e9a66cacdf0 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 5 May 2023 13:51:20 +0200 Subject: [PATCH 037/255] Add discover feature to onboard command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Co-authored-by: Johan Haals Co-authored-by: Philipp Hugenroth Signed-off-by: Marcus Eide --- .changeset/two-mayflies-tell.md | 6 + packages/cli/package.json | 5 + packages/cli/src/commands/onboard/command.ts | 18 +- .../commands/onboard/discovery/Discovery.ts | 71 ++++++++ .../analyzers/BasicRepositoryAnalyzer.ts | 56 ++++++ .../analyzers/DefaultAnalysisOutputs.ts | 29 ++++ .../analyzers/PackageJsonAnalyzer.ts | 124 +++++++++++++ .../onboard/discovery/analyzers/types.ts | 37 ++++ .../src/commands/onboard/discovery/index.ts | 141 +++++++++++++++ .../github/GithubDiscoveryProvider.ts | 163 ++++++++++++++++++ .../discovery/providers/github/GithubFile.ts | 35 ++++ .../providers/github/GithubRepository.ts | 84 +++++++++ .../gitlab/GitlabDiscoveryProvider.ts | 106 ++++++++++++ .../discovery/providers/gitlab/GitlabFile.ts | 38 ++++ .../providers/gitlab/GitlabProject.ts | 89 ++++++++++ .../onboard/discovery/providers/types.ts | 53 ++++++ packages/cli/src/commands/onboard/files.ts | 6 + packages/integration/api-report.md | 26 +++ .../DefaultGitlabCredentialsProvider.ts | 57 ++++++ ...SingleInstanceGitlabCredentialsProvider.ts | 39 +++++ packages/integration/src/gitlab/index.ts | 2 + packages/integration/src/gitlab/types.ts | 30 ++++ yarn.lock | 17 +- 23 files changed, 1229 insertions(+), 3 deletions(-) create mode 100644 .changeset/two-mayflies-tell.md create mode 100644 packages/cli/src/commands/onboard/discovery/Discovery.ts create mode 100644 packages/cli/src/commands/onboard/discovery/analyzers/BasicRepositoryAnalyzer.ts create mode 100644 packages/cli/src/commands/onboard/discovery/analyzers/DefaultAnalysisOutputs.ts create mode 100644 packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts create mode 100644 packages/cli/src/commands/onboard/discovery/analyzers/types.ts create mode 100644 packages/cli/src/commands/onboard/discovery/index.ts create mode 100644 packages/cli/src/commands/onboard/discovery/providers/github/GithubDiscoveryProvider.ts create mode 100644 packages/cli/src/commands/onboard/discovery/providers/github/GithubFile.ts create mode 100644 packages/cli/src/commands/onboard/discovery/providers/github/GithubRepository.ts create mode 100644 packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabDiscoveryProvider.ts create mode 100644 packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabFile.ts create mode 100644 packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabProject.ts create mode 100644 packages/cli/src/commands/onboard/discovery/providers/types.ts create mode 100644 packages/integration/src/gitlab/DefaultGitlabCredentialsProvider.ts create mode 100644 packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts create mode 100644 packages/integration/src/gitlab/types.ts diff --git a/.changeset/two-mayflies-tell.md b/.changeset/two-mayflies-tell.md new file mode 100644 index 0000000000..bf1d42b20b --- /dev/null +++ b/.changeset/two-mayflies-tell.md @@ -0,0 +1,6 @@ +--- +'@backstage/integration': patch +'@backstage/cli': patch +--- + +Add discovery feature to the onboard cli command. diff --git a/packages/cli/package.json b/packages/cli/package.json index 28c5ce7030..363cd8b072 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -30,16 +30,20 @@ "backstage-cli": "bin/backstage-cli" }, "dependencies": { + "@backstage/catalog-model": "workspace:^", "@backstage/cli-common": "workspace:^", "@backstage/cli-node": "workspace:^", "@backstage/config": "workspace:^", "@backstage/config-loader": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/eslint-plugin": "workspace:^", + "@backstage/integration": "workspace:^", "@backstage/release-manifests": "workspace:^", "@backstage/types": "workspace:^", "@esbuild-kit/cjs-loader": "^2.4.1", "@manypkg/get-packages": "^1.1.3", + "@octokit/graphql": "^5.0.0", + "@octokit/graphql-schema": "^13.7.0", "@octokit/oauth-app": "^4.2.0", "@octokit/request": "^6.0.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -88,6 +92,7 @@ "express": "^4.17.1", "fork-ts-checker-webpack-plugin": "^7.0.0-alpha.8", "fs-extra": "10.1.0", + "git-url-parse": "^13.0.0", "glob": "^7.1.7", "global-agent": "^3.0.0", "handlebars": "^4.7.3", diff --git a/packages/cli/src/commands/onboard/command.ts b/packages/cli/src/commands/onboard/command.ts index 5cf25953ab..0e57fe892a 100644 --- a/packages/cli/src/commands/onboard/command.ts +++ b/packages/cli/src/commands/onboard/command.ts @@ -19,11 +19,13 @@ import inquirer from 'inquirer'; import { Task } from '../../lib/tasks'; import { auth } from './auth'; import { integrations } from './integrations'; +import { discover } from './discovery'; export async function command(): Promise { const answers = await inquirer.prompt<{ shouldSetupAuth: boolean; shouldSetupScaffolder: boolean; + shouldDiscoverEntities: boolean; }>([ { type: 'confirm', @@ -37,9 +39,17 @@ export async function command(): Promise { message: 'Do you want to use Software Templates in this project?', default: true, }, + { + type: 'confirm', + name: 'shouldDiscoverEntities', + message: + 'Do you want to discover entities and add them to the Software Catalog?', + default: true, + }, ]); - const { shouldSetupAuth, shouldSetupScaffolder } = answers; + const { shouldSetupAuth, shouldSetupScaffolder, shouldDiscoverEntities } = + answers; let providerInfo; if (shouldSetupAuth) { @@ -50,7 +60,11 @@ export async function command(): Promise { await integrations(providerInfo); } - if (!shouldSetupAuth && !shouldSetupScaffolder) { + if (shouldDiscoverEntities) { + await discover(providerInfo); + } + + if (!shouldSetupAuth && !shouldSetupScaffolder && !shouldDiscoverEntities) { Task.log( chalk.yellow( 'If you change your mind, feel free to re-run this command.', diff --git a/packages/cli/src/commands/onboard/discovery/Discovery.ts b/packages/cli/src/commands/onboard/discovery/Discovery.ts new file mode 100644 index 0000000000..b0db4255ad --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/Discovery.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import chalk from 'chalk'; +import { Entity } from '@backstage/catalog-model'; +import { Analyzer } from './analyzers/types'; +import { Provider } from './providers/types'; +import { DefaultAnalysisOutputs } from './analyzers/DefaultAnalysisOutputs'; +import { Task } from '../../../lib/tasks'; + +export class Discovery { + readonly #providers: Provider[] = []; + readonly #analyzers: Analyzer[] = []; + + addProvider(provider: Provider) { + this.#providers.push(provider); + } + + addAnalyzer(analyzer: Analyzer) { + this.#analyzers.push(analyzer); + } + + async run(url: string): Promise<{ entities: Entity[] }> { + Task.log(`Running discovery for ${chalk.cyan(url)}`); + const result: Entity[] = []; + + for (const provider of this.#providers) { + const repositories = await provider.discover(url); + if (repositories && repositories.length) { + Task.log( + `Discovered ${chalk.cyan( + repositories.length, + )} repositories for ${chalk.cyan(provider.name())}`, + ); + + for (const repository of repositories) { + await Task.forItem('Analyzing', repository.name, async () => { + const output = new DefaultAnalysisOutputs(); + for (const analyzer of this.#analyzers) { + await analyzer.analyzeRepository({ repository, output }); + } + + output + .list() + .filter(entry => entry.type === 'entity') + .forEach(({ entity }) => result.push(entity)); + }); + } + + Task.log(`Produced ${chalk.cyan(result.length || 'no')} entities`); + } + } + + return { + entities: result, + }; + } +} diff --git a/packages/cli/src/commands/onboard/discovery/analyzers/BasicRepositoryAnalyzer.ts b/packages/cli/src/commands/onboard/discovery/analyzers/BasicRepositoryAnalyzer.ts new file mode 100644 index 0000000000..82c8c2cdae --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/analyzers/BasicRepositoryAnalyzer.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentEntity } from '@backstage/catalog-model'; +import { AnalysisOutputs, Analyzer } from './types'; +import { Repository } from '../providers/types'; + +/** + * Naive analyzer that produces a single entity that represents the repository + * as a whole. + */ +export class BasicRepositoryAnalyzer implements Analyzer { + name(): string { + return BasicRepositoryAnalyzer.name; + } + + async analyzeRepository(options: { + repository: Repository; + output: AnalysisOutputs; + }): Promise { + const entity: ComponentEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: options.repository.name, + ...(options.repository.description + ? { description: options.repository.description } + : {}), + }, + spec: { + type: 'service', + lifecycle: 'production', + owner: 'user:guest', + }, + }; + + options.output.produce({ + type: 'entity', + path: '/', + entity: entity, + }); + } +} diff --git a/packages/cli/src/commands/onboard/discovery/analyzers/DefaultAnalysisOutputs.ts b/packages/cli/src/commands/onboard/discovery/analyzers/DefaultAnalysisOutputs.ts new file mode 100644 index 0000000000..a370b8f166 --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/analyzers/DefaultAnalysisOutputs.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AnalysisOutput, AnalysisOutputs } from './types'; + +export class DefaultAnalysisOutputs implements AnalysisOutputs { + readonly #outputs = new Map(); + + produce(output: AnalysisOutput) { + this.#outputs.set(output.entity.metadata.name, output); + } + + list() { + return Array.from(this.#outputs).map(([_, output]) => output); + } +} diff --git a/packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts b/packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts new file mode 100644 index 0000000000..1f4cb116ad --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts @@ -0,0 +1,124 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + ANNOTATION_SOURCE_LOCATION, + ComponentEntity, +} from '@backstage/catalog-model'; +import z from 'zod'; +import { AnalysisOutputs, Analyzer } from './types'; +import { Repository, RepositoryFile } from '../providers/types'; + +export class PackageJsonAnalyzer implements Analyzer { + name(): string { + return PackageJsonAnalyzer.name; + } + + async analyzeRepository(options: { + repository: Repository; + output: AnalysisOutputs; + }): Promise { + const packageJson = await options.repository.file('package.json'); + if (!packageJson) { + return; + } + + const content = await readPackageJson(packageJson); + if (!content) { + return; + } + + const name = sanitizeName(content?.name) ?? options.repository.name; + + const entity: ComponentEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name, + ...(options.repository.description + ? { description: options.repository.description } + : {}), + tags: ['javascript'], + annotations: { + [ANNOTATION_SOURCE_LOCATION]: `url:${options.repository.url}`, + }, + }, + spec: { + type: 'website', + lifecycle: 'production', + owner: 'user:guest', + }, + }; + + const decorate = options.output + .list() + .find(entry => entry.entity.metadata.name === name); + + if (decorate) { + decorate.entity.spec = { + ...decorate.entity.spec, + type: 'website', + }; + + decorate.entity.metadata.tags = [ + ...(decorate.entity.metadata.tags ?? []), + 'javascript', + ]; + + decorate.entity.metadata.annotations = { + ...decorate.entity.metadata.annotations, + [ANNOTATION_SOURCE_LOCATION]: `url:${options.repository.url}`, + }; + + return; + } + + options.output.produce({ + type: 'entity', + path: '/', + entity, + }); + } +} + +const packageSchema = z.object({ + name: z.string().optional(), +}); + +/** + * Makes sure that a name retrieved from a package.json file + * is reasonable and conforms to the catalog naming format. + */ +function sanitizeName(name?: string) { + return name && name !== 'root' + ? name.replace(/[^a-z0-9A-Z]/g, '_').substring(0, 62) + : undefined; +} + +async function readPackageJson( + file: RepositoryFile, +): Promise | undefined> { + try { + const text = await file.text(); + const result = packageSchema.safeParse(JSON.parse(text)); + if (!result.success) { + return undefined; + } + return { name: result.data.name }; + } catch (e) { + return undefined; + } +} diff --git a/packages/cli/src/commands/onboard/discovery/analyzers/types.ts b/packages/cli/src/commands/onboard/discovery/analyzers/types.ts new file mode 100644 index 0000000000..57b6f2152b --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/analyzers/types.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { Repository } from '../providers/types'; + +export type AnalysisOutput = { + type: 'entity'; + path: string; + entity: Entity; +}; + +export interface AnalysisOutputs { + produce(output: AnalysisOutput): void; + list(): AnalysisOutput[]; +} + +export interface Analyzer { + name(): string; + analyzeRepository(options: { + repository: Repository; + output: AnalysisOutputs; + }): Promise; +} diff --git a/packages/cli/src/commands/onboard/discovery/index.ts b/packages/cli/src/commands/onboard/discovery/index.ts new file mode 100644 index 0000000000..b2b8bddb50 --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/index.ts @@ -0,0 +1,141 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import yaml from 'yaml'; +import inquirer from 'inquirer'; +import chalk from 'chalk'; +import { loadCliConfig } from '../../../lib/config'; +import { updateConfigFile } from '../config'; +import { APP_CONFIG_FILE, DISCOVERED_ENTITIES_FILE } from '../files'; +import { Discovery } from './Discovery'; +import { BasicRepositoryAnalyzer } from './analyzers/BasicRepositoryAnalyzer'; +import { PackageJsonAnalyzer } from './analyzers/PackageJsonAnalyzer'; +import { GithubDiscoveryProvider } from './providers/github/GithubDiscoveryProvider'; +import { GitlabDiscoveryProvider } from './providers/gitlab/GitlabDiscoveryProvider'; +import { GitHubAnswers, GitLabAnswers } from '../auth'; +import { Task } from '../../../lib/tasks'; + +export async function discover(providerInfo?: { + provider: string; + answers: GitHubAnswers | GitLabAnswers; +}) { + Task.log(` + Would you like to scan for - and create - Software Catalog entities? + + You will need to select which SCM (Source Code Management) provider you are using, + and then which repository or organization you want to scan. + + This will generate a new file in the root of your project containing discovered entities, + which will be included in the Software Catalog when you start up Backstage next time. + + Note that this command requires an access token, which can be either added through the integration config or + provided as an environment variable. + `); + + const answers = await inquirer.prompt<{ + shouldContinue: boolean; + provider: string; + url: string; + }>([ + { + type: 'confirm', + name: 'shouldContinue', + message: 'Do you want to continue?', + }, + { + type: 'list', + name: 'provider', + message: 'Please select which SCM provider you want to use:', + choices: ['GitHub', 'GitLab'], + default: providerInfo?.provider, + when: ({ shouldContinue }) => shouldContinue, + }, + { + type: 'input', + name: 'url', + message: `Which repository do you want to scan?`, + when: ({ shouldContinue }) => shouldContinue, + filter: (input, { provider }) => { + if (provider === 'GitLab') { + return `https://gitlab.com/${input}`; + } + if (provider === 'GitHub') { + return `https://github.com/${input}`; + } + return false; + }, + }, + ]); + + if (!answers.shouldContinue) { + Task.log( + chalk.yellow( + 'If you change your mind, feel free to re-run this command.', + ), + ); + return; + } + + const { fullConfig: config } = await loadCliConfig({ args: [] }); + + const discovery = new Discovery(); + + if (answers.provider === 'GitHub') { + discovery.addProvider(GithubDiscoveryProvider.fromConfig(config)); + } + if (answers.provider === 'GitLab') { + discovery.addProvider(GitlabDiscoveryProvider.fromConfig(config)); + } + + discovery.addAnalyzer(new BasicRepositoryAnalyzer()); + discovery.addAnalyzer(new PackageJsonAnalyzer()); + + const { entities } = await discovery.run(answers.url); + + if (!entities.length) { + Task.log( + chalk.yellow(` + We could not find enough information to be able to generate any Software Catalog entities for you. + Perhaps you can try again with a different repository?`), + ); + return; + } + + await Task.forItem('Creating', DISCOVERED_ENTITIES_FILE, async () => { + const payload: string[] = []; + for (const entity of entities) { + payload.push('---\n', yaml.stringify(entity)); + } + await fs.writeFile(DISCOVERED_ENTITIES_FILE, payload.join('')); + }); + + await Task.forItem( + 'Updating', + APP_CONFIG_FILE, + async () => + await updateConfigFile(APP_CONFIG_FILE, { + catalog: { + locations: [ + { + type: 'file', + target: DISCOVERED_ENTITIES_FILE, + }, + ], + }, + }), + ); +} diff --git a/packages/cli/src/commands/onboard/discovery/providers/github/GithubDiscoveryProvider.ts b/packages/cli/src/commands/onboard/discovery/providers/github/GithubDiscoveryProvider.ts new file mode 100644 index 0000000000..d5a3dc4a77 --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/providers/github/GithubDiscoveryProvider.ts @@ -0,0 +1,163 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; +import { + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { graphql } from '@octokit/graphql'; +import { + Repository as GraphqlRepository, + Query as GraphqlQuery, +} from '@octokit/graphql-schema'; +import parseGitUrl from 'git-url-parse'; +import { Provider, Repository } from '../types'; +import { GithubRepository } from './GithubRepository'; + +export class GithubDiscoveryProvider implements Provider { + readonly #envToken: string | undefined; + readonly #scmIntegrations: ScmIntegrations; + readonly #credentialsProvider: GithubCredentialsProvider; + + static fromConfig(config: Config): GithubDiscoveryProvider { + const envToken = process.env.GITHUB_TOKEN || undefined; + const scmIntegrations = ScmIntegrations.fromConfig(config); + const credentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(scmIntegrations); + return new GithubDiscoveryProvider( + envToken, + scmIntegrations, + credentialsProvider, + ); + } + + private constructor( + envToken: string | undefined, + integrations: ScmIntegrations, + credentialsProvider: GithubCredentialsProvider, + ) { + this.#envToken = envToken; + this.#scmIntegrations = integrations; + this.#credentialsProvider = credentialsProvider; + } + + name(): string { + return 'GitHub'; + } + + async discover(url: string): Promise { + if (!url.startsWith('https://github.com/')) { + return false; + } + + const scmIntegration = this.#scmIntegrations.github.byUrl(url); + if (!scmIntegration) { + throw new Error(`No GitHub integration found for ${url}`); + } + + const parsed = parseGitUrl(url); + const { name, organization } = parsed; + const org = organization || name; // depends on if it's a repo url or an org url... + + const client = graphql.defaults({ + baseUrl: scmIntegration.config.apiBaseUrl, + headers: await this.#getRequestHeaders(url), + }); + + const { repositories } = await this.#getOrganizationRepositories( + client, + org, + ); + + return repositories + .filter(repo => repo.url.startsWith(url)) + .map(repo => new GithubRepository(client, repo, org)); + } + + async #getRequestHeaders(url: string): Promise> { + const credentials = await this.#credentialsProvider.getCredentials({ + url, + }); + + if (credentials.headers) { + return credentials.headers; + } else if (credentials.token) { + return { authorization: `token ${credentials.token}` }; + } + + if (this.#envToken) { + return { authorization: `token ${this.#envToken}` }; + } + + throw new Error( + 'No token available for GitHub, please configure your integrations or set a GITHUB_TOKEN env variable', + ); + } + + async #getOrganizationRepositories(client: typeof graphql, org: string) { + const query = `query repositories($org: String!, $cursor: String) { + repositoryOwner(login: $org) { + login + repositories(first: 100, after: $cursor) { + nodes { + name + url + description + isArchived + isFork + } + pageInfo { + hasNextPage + endCursor + } + } + } + }`; + + const result: GraphqlRepository[] = []; + + let cursor: string | undefined | null = undefined; + let hasNextPage = true; + + while (hasNextPage) { + const response: GraphqlQuery = await client(query, { + org, + cursor, + }); + + const { repositories: connection } = response.repositoryOwner ?? {}; + + if (!connection) { + throw new Error(`Found no repositories for ${org}`); + } + + for (const repository of connection.nodes ?? []) { + if (repository && !repository.isArchived && !repository.isFork) { + result.push(repository); + } + } + + cursor = connection.pageInfo.endCursor; + hasNextPage = connection.pageInfo.hasNextPage; + } + + return { + repositories: result, + }; + } +} diff --git a/packages/cli/src/commands/onboard/discovery/providers/github/GithubFile.ts b/packages/cli/src/commands/onboard/discovery/providers/github/GithubFile.ts new file mode 100644 index 0000000000..25279b0949 --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/providers/github/GithubFile.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RepositoryFile } from '../types'; + +export class GithubFile implements RepositoryFile { + readonly #path: string; + readonly #content: string; + + constructor(path: string, content: string) { + this.#path = path; + this.#content = content; + } + + get path(): string { + return this.#path; + } + + async text(): Promise { + return this.#content; + } +} diff --git a/packages/cli/src/commands/onboard/discovery/providers/github/GithubRepository.ts b/packages/cli/src/commands/onboard/discovery/providers/github/GithubRepository.ts new file mode 100644 index 0000000000..f3c68851a0 --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/providers/github/GithubRepository.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { graphql } from '@octokit/graphql'; +import { + Repository as GraphqlRepository, + Blob as GraphqlBlob, +} from '@octokit/graphql-schema'; +import { Repository, RepositoryFile } from '../types'; +import { GithubFile } from './GithubFile'; + +export class GithubRepository implements Repository { + readonly #client: typeof graphql; + readonly #repo: GraphqlRepository; + readonly #org: string; + + constructor(client: typeof graphql, repo: GraphqlRepository, org: string) { + this.#client = client; + this.#repo = repo; + this.#org = org; + } + + get url(): string { + return this.#repo.url; + } + + get name(): string { + return this.#repo.name; + } + + get owner(): string { + return this.#org; + } + + get description(): string | undefined { + return this.#repo.description ?? undefined; + } + + async file(filename: string): Promise { + const content = await this.#getFileContent(filename); + if (!content || content.isBinary || !content.text) { + return undefined; + } + + return new GithubFile(filename, content.text ?? ''); + } + + async #getFileContent(filename: string) { + const query = `query RepoFiles($owner: String!, $name: String!, $expr: String!) { + repository(owner: $owner, name: $name) { + object(expression: $expr) { + ...on Blob { + text + isBinary + } + } + } + }`; + + const response = await this.#client<{ repository: GraphqlRepository }>( + query, + { + name: this.#repo.name, + owner: this.#org, + expr: `HEAD:${filename}`, + }, + ); + + return response.repository.object as GraphqlBlob; + } +} diff --git a/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabDiscoveryProvider.ts b/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabDiscoveryProvider.ts new file mode 100644 index 0000000000..df9ffa4206 --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabDiscoveryProvider.ts @@ -0,0 +1,106 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Config } from '@backstage/config'; +import { + DefaultGitlabCredentialsProvider, + GitlabCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import fetch from 'node-fetch'; +import { Provider } from '../types'; +import { GitlabProject, ProjectResponse } from './GitlabProject'; + +export class GitlabDiscoveryProvider implements Provider { + readonly #envToken: string | undefined; + readonly #scmIntegrations: ScmIntegrations; + readonly #credentialsProvider: GitlabCredentialsProvider; + + static fromConfig(config: Config): GitlabDiscoveryProvider { + const envToken = process.env.GITLAB_TOKEN || undefined; + const scmIntegrations = ScmIntegrations.fromConfig(config); + const credentialsProvider = + DefaultGitlabCredentialsProvider.fromIntegrations(scmIntegrations); + + return new GitlabDiscoveryProvider( + envToken, + scmIntegrations, + credentialsProvider, + ); + } + + private constructor( + envToken: string | undefined, + integrations: ScmIntegrations, + credentialsProvider: GitlabCredentialsProvider, + ) { + this.#envToken = envToken; + this.#scmIntegrations = integrations; + this.#credentialsProvider = credentialsProvider; + } + + name(): string { + return 'GitLab'; + } + + async discover(url: string): Promise { + const { origin, pathname } = new URL(url); + const [, user] = pathname.split('/'); + + const scmIntegration = this.#scmIntegrations.gitlab.byUrl(origin); + if (!scmIntegration) { + throw new Error(`No GitLab integration found for ${origin}`); + } + + const headers = await this.#getRequestHeaders(origin); + + const response = await fetch( + `${scmIntegration.config.apiBaseUrl}/users/${user}/projects`, + { headers }, + ); + + if (!response.ok) { + throw new Error(`${response.status} ${response.statusText}`); + } + + const projects: ProjectResponse[] = await response.json(); + + return projects.map( + project => + new GitlabProject(project, scmIntegration.config.apiBaseUrl, headers), + ); + } + + async #getRequestHeaders(url: string): Promise> { + const credentials = await this.#credentialsProvider.getCredentials({ + url, + }); + + if (credentials.headers) { + return credentials.headers; + } else if (credentials.token) { + return { authorization: `Bearer ${credentials.token}` }; + } + + if (this.#envToken) { + return { authorization: `Bearer ${this.#envToken}` }; + } + + throw new Error( + 'No token available for GitLab, please set a GITLAB_TOKEN env variable', + ); + } +} diff --git a/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabFile.ts b/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabFile.ts new file mode 100644 index 0000000000..06f3189ba6 --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabFile.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RepositoryFile } from '../types'; + +/** + * A single file in a GitLab repository. + */ +export class GitlabFile implements RepositoryFile { + readonly #path: string; + readonly #content: string; + + constructor(path: string, content: string) { + this.#path = path; + this.#content = content; + } + + get path(): string { + return this.#path; + } + + async text(): Promise { + return this.#content; + } +} diff --git a/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabProject.ts b/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabProject.ts new file mode 100644 index 0000000000..258e8176bb --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/providers/gitlab/GitlabProject.ts @@ -0,0 +1,89 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fetch from 'node-fetch'; +import { GitlabFile } from './GitlabFile'; +import { Repository, RepositoryFile } from '../types'; + +export type ProjectResponse = { + id: string; + name: string; + description: string; + owner: { + username: string; + }; + web_url: string; +}; + +type BranchResponse = { + default: boolean; + name: string; +}; + +type FileContentResponse = { + content: string; +}; + +export class GitlabProject implements Repository { + constructor( + private readonly project: ProjectResponse, + private readonly apiBaseUrl: string, + private readonly headers: { [name: string]: string }, + ) {} + + get url(): string { + return this.project.web_url; + } + + get name(): string { + return this.project.name; + } + + get owner(): string { + return this.project.owner.username; + } + + get description(): string { + return this.project.description; + } + + async file(filename: string): Promise { + const mainBranch = await this.#getMainBranch(); + const content = await this.#getFileContent(filename, mainBranch); + + return new GitlabFile(filename, content); + } + + async #getFileContent(path: string, mainBranch: string): Promise { + const response = await fetch( + `${this.apiBaseUrl}/projects/${this.project.id}/repository/files/${path}?ref=${mainBranch}`, + { headers: this.headers }, + ); + const { content }: FileContentResponse = await response.json(); + + return Buffer.from(content, 'base64').toString('ascii'); + } + + async #getMainBranch(): Promise { + const response = await fetch( + `${this.apiBaseUrl}/projects/${this.project.id}/repository/branches`, + { headers: this.headers }, + ); + const branches: BranchResponse[] = await response.json(); + + return branches.find(branch => branch.default)?.name ?? 'main'; + } +} diff --git a/packages/cli/src/commands/onboard/discovery/providers/types.ts b/packages/cli/src/commands/onboard/discovery/providers/types.ts new file mode 100644 index 0000000000..2d8ce70ecf --- /dev/null +++ b/packages/cli/src/commands/onboard/discovery/providers/types.ts @@ -0,0 +1,53 @@ +/* + * 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. + */ + +/** + * Abstraction for a single repository. + */ +export interface Repository { + url: string; + + name: string; + + owner: string; + + description?: string; + + file(filename: string): Promise; +} + +/** + * Abstraction for a single repository file. + */ +export interface RepositoryFile { + /** + * The filepath of the data. + */ + path: string; + + /** + * The textual contents of the file. + */ + text(): Promise; +} + +/** + * One integration that supports discovery of repositories. + */ +export interface Provider { + name(): string; + discover(url: string): Promise; +} diff --git a/packages/cli/src/commands/onboard/files.ts b/packages/cli/src/commands/onboard/files.ts index 17f8a0f85e..febec35d4a 100644 --- a/packages/cli/src/commands/onboard/files.ts +++ b/packages/cli/src/commands/onboard/files.ts @@ -19,7 +19,13 @@ import * as path from 'path'; /* eslint-disable-next-line no-restricted-syntax */ const { targetRoot, ownDir } = findPaths(__dirname); + export const APP_CONFIG_FILE = path.join(targetRoot, 'app-config.local.yaml'); +export const DISCOVERED_ENTITIES_FILE = path.join( + targetRoot, + 'examples', + 'discovered-entities.yaml', +); export const PATCH_FOLDER = path.join( ownDir, 'src', diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index d45c8ca06b..a1a14689ca 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -165,6 +165,18 @@ export class DefaultGithubCredentialsProvider getCredentials(opts: { url: string }): Promise; } +// @public +export class DefaultGitlabCredentialsProvider + implements GitlabCredentialsProvider +{ + // (undocumented) + static fromIntegrations( + integrations: ScmIntegrationRegistry, + ): DefaultGitlabCredentialsProvider; + // (undocumented) + getCredentials(opts: { url: string }): Promise; +} + // @public export function defaultScmResolveUrl(options: { url: string; @@ -478,6 +490,20 @@ export type GithubIntegrationConfig = { apps?: GithubAppConfig[]; }; +// @public (undocumented) +export type GitlabCredentials = { + headers?: { + [name: string]: string; + }; + token?: string; +}; + +// @public (undocumented) +export interface GitlabCredentialsProvider { + // (undocumented) + getCredentials(opts?: { url: string }): Promise; +} + // @public export class GitLabIntegration implements ScmIntegration { constructor(integrationConfig: GitLabIntegrationConfig); diff --git a/packages/integration/src/gitlab/DefaultGitlabCredentialsProvider.ts b/packages/integration/src/gitlab/DefaultGitlabCredentialsProvider.ts new file mode 100644 index 0000000000..71e9a1e2b7 --- /dev/null +++ b/packages/integration/src/gitlab/DefaultGitlabCredentialsProvider.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ScmIntegrationRegistry } from '../registry'; +import { SingleInstanceGitlabCredentialsProvider } from './SingleInstanceGitlabCredentialsProvider'; +import { GitlabCredentials, GitlabCredentialsProvider } from './types'; + +/** + * Handles the creation and caching of credentials for GitLab integrations. + * + * @public + */ +export class DefaultGitlabCredentialsProvider + implements GitlabCredentialsProvider +{ + static fromIntegrations(integrations: ScmIntegrationRegistry) { + const credentialsProviders: Map = + new Map(); + + integrations.gitlab.list().forEach(integration => { + const credentialsProvider = + SingleInstanceGitlabCredentialsProvider.create(integration.config); + credentialsProviders.set(integration.config.host, credentialsProvider); + }); + return new DefaultGitlabCredentialsProvider(credentialsProviders); + } + + private constructor( + private readonly providers: Map, + ) {} + + async getCredentials(opts: { url: string }): Promise { + const parsed = new URL(opts.url); + const provider = this.providers.get(parsed.host); + + if (!provider) { + throw new Error( + `There is no GitLab integration that matches ${opts.url}. Please add a configuration for an integration.`, + ); + } + + return provider.getCredentials(opts); + } +} diff --git a/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts b/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts new file mode 100644 index 0000000000..39f22ab9d5 --- /dev/null +++ b/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { GitLabIntegrationConfig } from './config'; +import { GitlabCredentials, GitlabCredentialsProvider } from './types'; + +export class SingleInstanceGitlabCredentialsProvider + implements GitlabCredentialsProvider +{ + static create: ( + config: GitLabIntegrationConfig, + ) => GitlabCredentialsProvider = config => { + return new SingleInstanceGitlabCredentialsProvider(config.token); + }; + + constructor(private readonly token?: string) {} + + async getCredentials(): Promise { + return { + headers: this.token + ? { Authorization: `Bearer ${this.token}` } + : undefined, + token: this.token, + }; + } +} diff --git a/packages/integration/src/gitlab/index.ts b/packages/integration/src/gitlab/index.ts index 1a5063c51c..82ba460dab 100644 --- a/packages/integration/src/gitlab/index.ts +++ b/packages/integration/src/gitlab/index.ts @@ -22,3 +22,5 @@ export { export type { GitLabIntegrationConfig } from './config'; export { getGitLabFileFetchUrl, getGitLabRequestOptions } from './core'; export { GitLabIntegration, replaceGitLabUrlType } from './GitLabIntegration'; +export { DefaultGitlabCredentialsProvider } from './DefaultGitlabCredentialsProvider'; +export type { GitlabCredentials, GitlabCredentialsProvider } from './types'; diff --git a/packages/integration/src/gitlab/types.ts b/packages/integration/src/gitlab/types.ts new file mode 100644 index 0000000000..ef8ccf8d6c --- /dev/null +++ b/packages/integration/src/gitlab/types.ts @@ -0,0 +1,30 @@ +/* + * 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. + */ + +/** + * @public + */ +export type GitlabCredentials = { + headers?: { [name: string]: string }; + token?: string; +}; + +/** + * @public + */ +export interface GitlabCredentialsProvider { + getCredentials(opts?: { url: string }): Promise; +} diff --git a/yarn.lock b/yarn.lock index f80cac5e4f..e2b21e2d10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3848,6 +3848,7 @@ __metadata: resolution: "@backstage/cli@workspace:packages/cli" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/catalog-model": "workspace:^" "@backstage/cli-common": "workspace:^" "@backstage/cli-node": "workspace:^" "@backstage/config": "workspace:^" @@ -3858,12 +3859,15 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/eslint-plugin": "workspace:^" + "@backstage/integration": "workspace:^" "@backstage/release-manifests": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@backstage/types": "workspace:^" "@esbuild-kit/cjs-loader": ^2.4.1 "@manypkg/get-packages": ^1.1.3 + "@octokit/graphql": ^5.0.0 + "@octokit/graphql-schema": ^13.7.0 "@octokit/oauth-app": ^4.2.0 "@octokit/request": ^6.0.0 "@pmmmwh/react-refresh-webpack-plugin": ^0.5.7 @@ -3930,6 +3934,7 @@ __metadata: express: ^4.17.1 fork-ts-checker-webpack-plugin: ^7.0.0-alpha.8 fs-extra: 10.1.0 + git-url-parse: ^13.0.0 glob: ^7.1.7 global-agent: ^3.0.0 handlebars: ^4.7.3 @@ -13130,6 +13135,16 @@ __metadata: languageName: node linkType: hard +"@octokit/graphql-schema@npm:^13.7.0": + version: 13.10.0 + resolution: "@octokit/graphql-schema@npm:13.10.0" + dependencies: + graphql: ^16.0.0 + graphql-tag: ^2.10.3 + checksum: fdec9c9a4df1f90b733ea0e24964744faceaf65e5d350b1727892e8e0e5821df1d29aec5cfa039925a044c6f56d4ed2028505108db7fbc0c68011053853c2411 + languageName: node + linkType: hard + "@octokit/graphql@npm:^5.0.0": version: 5.0.6 resolution: "@octokit/graphql@npm:5.0.6" @@ -25543,7 +25558,7 @@ __metadata: languageName: node linkType: hard -"graphql-tag@npm:^2.11.0, graphql-tag@npm:^2.12.6": +"graphql-tag@npm:^2.10.3, graphql-tag@npm:^2.11.0, graphql-tag@npm:^2.12.6": version: 2.12.6 resolution: "graphql-tag@npm:2.12.6" dependencies: From a52fd3452b70a262767c2b054f509f4a82823cea Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Fri, 5 May 2023 14:12:43 +0200 Subject: [PATCH 038/255] Add doc reference to catalog format adr Signed-off-by: Marcus Eide --- .../onboard/discovery/analyzers/PackageJsonAnalyzer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts b/packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts index 1f4cb116ad..16dd4b9301 100644 --- a/packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts +++ b/packages/cli/src/commands/onboard/discovery/analyzers/PackageJsonAnalyzer.ts @@ -101,6 +101,10 @@ const packageSchema = z.object({ /** * Makes sure that a name retrieved from a package.json file * is reasonable and conforms to the catalog naming format. + * + * Read more about the naming format here: + * ADR002: Default Software Catalog File Format + * https://backstage.io/docs/architecture-decisions/adrs-adr002/ */ function sanitizeName(name?: string) { return name && name !== 'root' From 7aec28347cc2af3fae98c967f07c8030b6083883 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 16 May 2023 09:47:42 +0200 Subject: [PATCH 039/255] Make options to getCredentials required Signed-off-by: Marcus Eide --- packages/integration/api-report.md | 2 +- .../SingleInstanceGitlabCredentialsProvider.ts | 12 ++++++++---- packages/integration/src/gitlab/types.ts | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index a1a14689ca..8939d24b20 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -501,7 +501,7 @@ export type GitlabCredentials = { // @public (undocumented) export interface GitlabCredentialsProvider { // (undocumented) - getCredentials(opts?: { url: string }): Promise; + getCredentials(opts: { url: string }): Promise; } // @public diff --git a/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts b/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts index 39f22ab9d5..76068a948e 100644 --- a/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts +++ b/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts @@ -28,11 +28,15 @@ export class SingleInstanceGitlabCredentialsProvider constructor(private readonly token?: string) {} - async getCredentials(): Promise { + async getCredentials(_opts: { url: string }): Promise { + if (!this.token) { + return {}; + } + return { - headers: this.token - ? { Authorization: `Bearer ${this.token}` } - : undefined, + headers: { + Authorization: `Bearer ${this.token}`, + }, token: this.token, }; } diff --git a/packages/integration/src/gitlab/types.ts b/packages/integration/src/gitlab/types.ts index ef8ccf8d6c..b980aaba7b 100644 --- a/packages/integration/src/gitlab/types.ts +++ b/packages/integration/src/gitlab/types.ts @@ -26,5 +26,5 @@ export type GitlabCredentials = { * @public */ export interface GitlabCredentialsProvider { - getCredentials(opts?: { url: string }): Promise; + getCredentials(opts: { url: string }): Promise; } From ae0ee957d93636a1452714bf05407465daf1c773 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 16 May 2023 09:49:44 +0200 Subject: [PATCH 040/255] Private constructor Signed-off-by: Marcus Eide --- .../src/gitlab/SingleInstanceGitlabCredentialsProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts b/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts index 76068a948e..85c8fc8ab5 100644 --- a/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts +++ b/packages/integration/src/gitlab/SingleInstanceGitlabCredentialsProvider.ts @@ -26,7 +26,7 @@ export class SingleInstanceGitlabCredentialsProvider return new SingleInstanceGitlabCredentialsProvider(config.token); }; - constructor(private readonly token?: string) {} + private constructor(private readonly token?: string) {} async getCredentials(_opts: { url: string }): Promise { if (!this.token) { From 576ba9fc17510c1e552e066ab3924ce5eaf997b5 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 16 May 2023 09:52:26 +0200 Subject: [PATCH 041/255] Clarify where the discovered entities will be created Signed-off-by: Marcus Eide --- packages/cli/src/commands/onboard/discovery/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/onboard/discovery/index.ts b/packages/cli/src/commands/onboard/discovery/index.ts index b2b8bddb50..8b2a5b694a 100644 --- a/packages/cli/src/commands/onboard/discovery/index.ts +++ b/packages/cli/src/commands/onboard/discovery/index.ts @@ -39,7 +39,7 @@ export async function discover(providerInfo?: { You will need to select which SCM (Source Code Management) provider you are using, and then which repository or organization you want to scan. - This will generate a new file in the root of your project containing discovered entities, + This will generate a new file in the root of your app containing discovered entities, which will be included in the Software Catalog when you start up Backstage next time. Note that this command requires an access token, which can be either added through the integration config or From bac63d0e2864ae3b3a12d82bf84505d56c7f662d Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Tue, 16 May 2023 09:53:42 +0200 Subject: [PATCH 042/255] Early return if nothing selected Signed-off-by: Marcus Eide --- packages/cli/src/commands/onboard/command.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/commands/onboard/command.ts b/packages/cli/src/commands/onboard/command.ts index 0e57fe892a..f68685f3e6 100644 --- a/packages/cli/src/commands/onboard/command.ts +++ b/packages/cli/src/commands/onboard/command.ts @@ -51,6 +51,15 @@ export async function command(): Promise { const { shouldSetupAuth, shouldSetupScaffolder, shouldDiscoverEntities } = answers; + if (!shouldSetupAuth && !shouldSetupScaffolder && !shouldDiscoverEntities) { + Task.log( + chalk.yellow( + 'If you change your mind, feel free to re-run this command.', + ), + ); + return; + } + let providerInfo; if (shouldSetupAuth) { providerInfo = await auth(); @@ -64,15 +73,6 @@ export async function command(): Promise { await discover(providerInfo); } - if (!shouldSetupAuth && !shouldSetupScaffolder && !shouldDiscoverEntities) { - Task.log( - chalk.yellow( - 'If you change your mind, feel free to re-run this command.', - ), - ); - return; - } - Task.log(); Task.log( `You can now start your app with ${chalk.inverse( From a316d226c780daf6d4718af97153c8a469381364 Mon Sep 17 00:00:00 2001 From: Marcus Eide Date: Wed, 17 May 2023 10:22:38 +0200 Subject: [PATCH 043/255] Update changesets Signed-off-by: Marcus Eide --- .changeset/lazy-dancers-begin.md | 5 +++++ .changeset/two-mayflies-tell.md | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changeset/lazy-dancers-begin.md diff --git a/.changeset/lazy-dancers-begin.md b/.changeset/lazy-dancers-begin.md new file mode 100644 index 0000000000..27282ec981 --- /dev/null +++ b/.changeset/lazy-dancers-begin.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': minor +--- + +Add credential provider for GitLab. diff --git a/.changeset/two-mayflies-tell.md b/.changeset/two-mayflies-tell.md index bf1d42b20b..a5226b5164 100644 --- a/.changeset/two-mayflies-tell.md +++ b/.changeset/two-mayflies-tell.md @@ -1,5 +1,4 @@ --- -'@backstage/integration': patch '@backstage/cli': patch --- From 591723ebc3b77bfa6eaffed2846f9e76643b67c4 Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Fri, 19 May 2023 15:58:39 +0100 Subject: [PATCH 044/255] casing Signed-off-by: Brian Fletcher --- packages/app/src/components/home/HomePage.tsx | 2 ++ plugins/pagerduty/src/plugin.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx index da1d0d425c..aa3739728e 100644 --- a/packages/app/src/components/home/HomePage.tsx +++ b/packages/app/src/components/home/HomePage.tsx @@ -30,6 +30,7 @@ import { HomePageCalendar } from '@backstage/plugin-gcalendar'; import { MicrosoftCalendarCard } from '@backstage/plugin-microsoft-calendar'; import React from 'react'; import HomeIcon from '@material-ui/icons/Home'; +import { HomePagePagerDutyCard } from '@backstage/plugin-pagerduty'; const clockConfigs: ClockConfig[] = [ { @@ -93,6 +94,7 @@ export const homePage = ( + diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index 408df991e2..2cf330864d 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -75,7 +75,7 @@ export const HomePagePagerDutyCard = homePlugin.provide( components: () => import('./components/HomePagePagerDutyCard'), settings: { schema: { - title: 'Pagerduty', + title: 'PagerDuty', type: 'object', properties: { integrationKey: { From a1dc5415d7ae4c3a8c8cbc4e2b15c28e23e72784 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 22 May 2023 10:38:53 +0200 Subject: [PATCH 045/255] catalog-react: decouple EntityLifecyclePicker from backendEntities Signed-off-by: Vincenzo Scamporlino --- .../EntityAutocompletePicker.tsx | 14 +- .../EntityLifecyclePicker.test.tsx | 266 +++++++++--------- .../EntityLifecyclePicker.tsx | 113 +------- 3 files changed, 150 insertions(+), 243 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx index 3a9a100357..b6de85937d 100644 --- a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx +++ b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx @@ -54,6 +54,7 @@ export type EntityAutocompletePickerProps< showCounts?: boolean; Filter: ConstructableFilter>; InputProps?: TextFieldProps; + initialSelectedOptions?: string[]; }; /** @public */ @@ -61,7 +62,15 @@ export function EntityAutocompletePicker< T extends DefaultEntityFilters = DefaultEntityFilters, Name extends AllowedEntityFilters = AllowedEntityFilters, >(props: EntityAutocompletePickerProps) { - const { label, name, path, showCounts, Filter, InputProps } = props; + const { + label, + name, + path, + showCounts, + Filter, + InputProps, + initialSelectedOptions = [], + } = props; const { updateFilters, @@ -90,7 +99,8 @@ export function EntityAutocompletePicker< const [selectedOptions, setSelectedOptions] = useState( queryParameters.length ? queryParameters - : (filters[name] as unknown as { values: string[] })?.values ?? [], + : (filters[name] as unknown as { values: string[] })?.values ?? + initialSelectedOptions, ); // Set selected options on query parameter updates; this happens at initial page load and from diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx index 3ee610ddaf..19554f18b7 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx @@ -14,146 +14,116 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; import { EntityLifecycleFilter } from '../../filters'; import { EntityLifecyclePicker } from './EntityLifecyclePicker'; - -const sampleEntities: Entity[] = [ - { - apiVersion: '1', - kind: 'Component', - metadata: { - name: 'component-1', - }, - spec: { - lifecycle: 'production', - }, - }, - { - apiVersion: '1', - kind: 'Component', - metadata: { - name: 'component-2', - }, - spec: { - lifecycle: 'experimental', - }, - }, - { - apiVersion: '1', - kind: 'Component', - metadata: { - name: 'component-3', - }, - spec: { - lifecycle: 'experimental', - }, - }, -]; +import { TestApiProvider } from '@backstage/test-utils'; +import { catalogApiRef } from '../../api'; +import { CatalogApi } from '@backstage/catalog-client'; describe('', () => { - it('renders all lifecycles', () => { - render( - - - , - ); - expect(screen.getByText('Lifecycle')).toBeInTheDocument(); + const catalogApi = { + getEntityFacets: jest.fn(), + } as unknown as jest.Mocked; - fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); - sampleEntities - .map(e => e.spec?.lifecycle!) - .forEach(lifecycle => { - expect(screen.getByText(lifecycle as string)).toBeInTheDocument(); - }); + beforeEach(() => { + catalogApi.getEntityFacets.mockResolvedValue({ + facets: { + 'spec.lifecycle': [ + { count: 1, value: 'experimental' }, + { count: 1, value: 'production' }, + ], + }, + }); }); - it('renders unique lifecycles in alphabetical order', () => { - render( - - - , - ); - expect(screen.getByText('Lifecycle')).toBeInTheDocument(); - - fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); - - expect(screen.getAllByRole('option').map(o => o.textContent)).toEqual([ - 'experimental', - 'production', - ]); + afterEach(() => { + jest.resetAllMocks(); }); - it('respects the query parameter filter value', () => { + it('renders all lifecycles', async () => { + render( + + + + + , + ); + expect(await screen.findByText('Lifecycle')).toBeInTheDocument(); + + fireEvent.click(await screen.findByTestId('lifecycles-picker-expand')); + expect(screen.getByText('experimental')).toBeInTheDocument(); + expect(screen.getByText('production')).toBeInTheDocument(); + }); + + it('respects the query parameter filter value', async () => { const updateFilters = jest.fn(); const queryParameters = { lifecycles: ['experimental'] }; render( - - - , + + + + + , ); + await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled()); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['experimental']), }); }); - it('adds lifecycles to filters', () => { + it('adds lifecycles to filters', async () => { const updateFilters = jest.fn(); render( - - - , + + + + + , ); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: undefined, }); - fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); + fireEvent.click(await screen.findByTestId('lifecycles-picker-expand')); fireEvent.click(screen.getByText('production')); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); }); - it('removes lifecycles from filters', () => { + it('removes lifecycles from filters', async () => { const updateFilters = jest.fn(); render( - - - , + + + + + , ); + + await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled()); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); - fireEvent.click(screen.getByTestId('lifecycle-picker-expand')); + fireEvent.click(screen.getByTestId('lifecycles-picker-expand')); expect(screen.getByLabelText('production')).toBeChecked(); fireEvent.click(screen.getByLabelText('production')); @@ -162,67 +132,85 @@ describe('', () => { }); }); - it('responds to external queryParameters changes', () => { + it('responds to external queryParameters changes', async () => { const updateFilters = jest.fn(); const rendered = render( - - - , + + + + + , ); + + await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled()); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['experimental']), }); + rendered.rerender( - - - , + + + + + , ); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); }); - it('removes lifecycles from filters if there are no available lifecycles', () => { + + it('removes lifecycles from filters if there are no available lifecycles', async () => { + catalogApi.getEntityFacets.mockResolvedValue({ + facets: { + 'spec.lifecycle': [], + }, + }); + const updateFilters = jest.fn(); render( - - - , + + + + + , ); + + await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled()); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: undefined, }); }); - it('responds to initialFilter prop', () => { + + it('responds to initialFilter prop', async () => { const updateFilters = jest.fn(); render( - - - , + + + + + , ); + + await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled()); expect(updateFilters).toHaveBeenLastCalledWith({ lifecycles: new EntityLifecycleFilter(['production']), }); diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx index dd107b2673..a1bd6ef86b 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx @@ -14,22 +14,10 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; -import { - Box, - Checkbox, - FormControlLabel, - makeStyles, - TextField, - Typography, -} from '@material-ui/core'; -import CheckBoxIcon from '@material-ui/icons/CheckBox'; -import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import { Autocomplete } from '@material-ui/lab'; -import React, { useEffect, useMemo, useState } from 'react'; -import { useEntityList } from '../../hooks/useEntityListProvider'; +import { makeStyles } from '@material-ui/core'; +import React from 'react'; import { EntityLifecycleFilter } from '../../filters'; +import { EntityAutocompletePicker } from '../EntityAutocompletePicker'; /** @public */ export type CatalogReactEntityLifecyclePickerClassKey = 'input'; @@ -43,98 +31,19 @@ const useStyles = makeStyles( }, ); -const icon = ; -const checkedIcon = ; - /** @public */ export const EntityLifecyclePicker = (props: { initialFilter?: string[] }) => { const { initialFilter = [] } = props; const classes = useStyles(); - const { - updateFilters, - backendEntities, - filters, - queryParameters: { lifecycles: lifecyclesParameter }, - } = useEntityList(); - - const queryParamLifecycles = useMemo( - () => [lifecyclesParameter].flat().filter(Boolean) as string[], - [lifecyclesParameter], - ); - - const [selectedLifecycles, setSelectedLifecycles] = useState( - queryParamLifecycles.length - ? queryParamLifecycles - : filters.lifecycles?.values ?? initialFilter, - ); - - // Set selected lifecycles on query parameter updates; this happens at initial page load and from - // external updates to the page location. - useEffect(() => { - if (queryParamLifecycles.length) { - setSelectedLifecycles(queryParamLifecycles); - } - }, [queryParamLifecycles]); - - const availableLifecycles = useMemo( - () => - [ - ...new Set( - backendEntities - .map((e: Entity) => e.spec?.lifecycle) - .filter(Boolean) as string[], - ), - ].sort(), - [backendEntities], - ); - - useEffect(() => { - updateFilters({ - lifecycles: - selectedLifecycles.length && availableLifecycles.length - ? new EntityLifecycleFilter(selectedLifecycles) - : undefined, - }); - }, [selectedLifecycles, updateFilters, availableLifecycles]); - - if (!availableLifecycles.length) return null; return ( - - - Lifecycle - - setSelectedLifecycles(value) - } - renderOption={(option, { selected }) => ( - - } - onClick={event => event.preventDefault()} - label={option} - /> - )} - size="small" - popupIcon={} - renderInput={params => ( - - )} - /> - - + ); }; From 429319d080cdb8ce8419ccb59b90344648406e60 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 22 May 2023 10:50:25 +0200 Subject: [PATCH 046/255] add changesets Signed-off-by: Vincenzo Scamporlino --- .changeset/silver-ducks-drum.md | 5 +++++ .changeset/three-guests-beam.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/silver-ducks-drum.md create mode 100644 .changeset/three-guests-beam.md diff --git a/.changeset/silver-ducks-drum.md b/.changeset/silver-ducks-drum.md new file mode 100644 index 0000000000..6cca57fdd4 --- /dev/null +++ b/.changeset/silver-ducks-drum.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +`EntityAutocompletePicker` add `initialSelectedOptions` prop diff --git a/.changeset/three-guests-beam.md b/.changeset/three-guests-beam.md new file mode 100644 index 0000000000..b46e80a13c --- /dev/null +++ b/.changeset/three-guests-beam.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +`EntityLifecycleFilter` loads data using the facets endpoint From 7d4a09304f67729ff3942b526a100f0bb35ea2f7 Mon Sep 17 00:00:00 2001 From: Morgan Bentell Date: Mon, 22 May 2023 11:56:57 +0200 Subject: [PATCH 047/255] use latest techdocs docker image by default Signed-off-by: Morgan Bentell --- .changeset/heavy-ravens-fold.md | 5 +++++ plugins/techdocs-node/src/stages/generate/techdocs.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/heavy-ravens-fold.md diff --git a/.changeset/heavy-ravens-fold.md b/.changeset/heavy-ravens-fold.md new file mode 100644 index 0000000000..f5f88c7662 --- /dev/null +++ b/.changeset/heavy-ravens-fold.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +Use latest techdocs docker image by default. The latest image contains security updates. diff --git a/plugins/techdocs-node/src/stages/generate/techdocs.ts b/plugins/techdocs-node/src/stages/generate/techdocs.ts index d2ad0021be..06160b85c9 100644 --- a/plugins/techdocs-node/src/stages/generate/techdocs.ts +++ b/plugins/techdocs-node/src/stages/generate/techdocs.ts @@ -53,7 +53,7 @@ export class TechdocsGenerator implements GeneratorBase { * The default docker image (and version) used to generate content. Public * and static so that techdocs-node consumers can use the same version. */ - public static readonly defaultDockerImage = 'spotify/techdocs:v1.2.0'; + public static readonly defaultDockerImage = 'spotify/techdocs:v1.2.1'; private readonly logger: Logger; private readonly containerRunner?: ContainerRunner; private readonly options: GeneratorConfig; From 277da332d6ed9feef92bf1d1768b28dd8f66bda8 Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Mon, 22 May 2023 12:38:30 +0100 Subject: [PATCH 048/255] more review comments Signed-off-by: Brian Fletcher --- plugins/pagerduty/package.json | 2 +- plugins/pagerduty/src/index.ts | 8 +++++++- plugins/pagerduty/src/plugin.ts | 11 ++--------- yarn.lock | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index b0eea1764e..ac52bd7daf 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -38,7 +38,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", - "@backstage/plugin-home": "workspace:^", + "@backstage/plugin-home-react": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/pagerduty/src/index.ts b/plugins/pagerduty/src/index.ts index 19b8456476..d0320e3343 100644 --- a/plugins/pagerduty/src/index.ts +++ b/plugins/pagerduty/src/index.ts @@ -29,8 +29,14 @@ export { export * from './components'; -// @deprecated Please use EntityPagerDutyCard +/** + * @deprecated Please use EntityPagerDutyCard + */ export { EntityPagerDutyCard as PagerDutyCard } from './components'; + +/** + * @deprecated Please use EntityPagerDutyCardProps + */ export type { EntityPagerDutyCardProps as PagerDutyCardProps } from './components'; export * from './api'; diff --git a/plugins/pagerduty/src/plugin.ts b/plugins/pagerduty/src/plugin.ts index 2cf330864d..59cc5cd8cc 100644 --- a/plugins/pagerduty/src/plugin.ts +++ b/plugins/pagerduty/src/plugin.ts @@ -23,7 +23,7 @@ import { configApiRef, createComponentExtension, } from '@backstage/core-plugin-api'; -import { createCardExtension } from '@backstage/plugin-home'; +import { createCardExtension } from '@backstage/plugin-home-react'; import { HomePagePagerDutyCardProps } from './components/HomePagePagerDutyCard/Content'; export const rootRouteRef = createRouteRef({ @@ -60,15 +60,8 @@ export const EntityPagerDutyCard = pagerDutyPlugin.provide( }), ); -export const homePlugin = createPlugin({ - id: 'pagerduty-home', - routes: { - root: rootRouteRef, - }, -}); - /** @public */ -export const HomePagePagerDutyCard = homePlugin.provide( +export const HomePagePagerDutyCard = pagerDutyPlugin.provide( createCardExtension({ name: 'HomePagePagerDutyCard', title: 'PagerDuty Homepage Card', diff --git a/yarn.lock b/yarn.lock index 7411abf9e8..eab5bf2d91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7726,7 +7726,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" - "@backstage/plugin-home": "workspace:^" + "@backstage/plugin-home-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.12.2 From d019c40b244905dc6c13b6661821c5d55f2d85b9 Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Mon, 22 May 2023 12:43:31 +0100 Subject: [PATCH 049/255] api reports Signed-off-by: Brian Fletcher --- plugins/pagerduty/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index 7ab3a5e1af..4e0d4784f3 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -7,7 +7,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { CardExtensionProps } from '@backstage/plugin-home'; +import { CardExtensionProps } from '@backstage/plugin-home-react'; import { ConfigApi } from '@backstage/core-plugin-api'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; From 918f0e2c17d00c88bd589471c593933bb2d2c26a Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 22 May 2023 14:41:44 +0200 Subject: [PATCH 050/255] catalog-react: api reports Signed-off-by: Vincenzo Scamporlino --- plugins/catalog-react/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index f9725e41e8..90bddecd71 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -195,7 +195,7 @@ export class EntityLifecycleFilter implements EntityFilter { // @public (undocumented) export const EntityLifecyclePicker: (props: { initialFilter?: string[]; -}) => JSX.Element | null; +}) => JSX.Element; // @public export const EntityListContext: React_2.Context< From 6b80545f8459be578f0fbb9e10ab5e009fb6db4a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 13:23:15 +0000 Subject: [PATCH 051/255] chore(deps): update alpine docker tag to v3.18 Signed-off-by: Renovate Bot --- plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile b/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile index b33c7cbe64..a16248239c 100644 --- a/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile +++ b/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.7 +FROM alpine:3.18 RUN apk add --update \ git \ From 50c4457119ec893564121928bb910a2d05c3dcaa Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 22 May 2023 16:30:22 +0200 Subject: [PATCH 052/255] fixed publish configurations Signed-off-by: Patrik Oldsberg --- .changeset/few-games-swim.md | 6 ++++++ plugins/cicd-statistics-module-gitlab/package.json | 3 +-- plugins/scaffolder-backend-module-gitlab/package.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changeset/few-games-swim.md diff --git a/.changeset/few-games-swim.md b/.changeset/few-games-swim.md new file mode 100644 index 0000000000..d0cd13ab11 --- /dev/null +++ b/.changeset/few-games-swim.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-cicd-statistics-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +Fixed publish configuration. diff --git a/plugins/cicd-statistics-module-gitlab/package.json b/plugins/cicd-statistics-module-gitlab/package.json index ac719e1c1b..fe12303d43 100644 --- a/plugins/cicd-statistics-module-gitlab/package.json +++ b/plugins/cicd-statistics-module-gitlab/package.json @@ -7,8 +7,7 @@ "license": "Apache-2.0", "publishConfig": { "access": "public", - "main": "dist/index.cjs.js", - "module": "dist/index.esm.js", + "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, "backstage": { diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 07cc725570..0ebab509bb 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "publishConfig": { "access": "public", - "main": "dist/index.esm.js", + "main": "dist/index.cjs.js", "types": "dist/index.d.ts" }, "backstage": { From c312192e61dd6b31603d8794cf3df98beb4b9379 Mon Sep 17 00:00:00 2001 From: Brian Phillips <28457+brianphillips@users.noreply.github.com> Date: Wed, 17 May 2023 13:48:40 -0500 Subject: [PATCH 053/255] Expose devtools plugin permissions through the metadata endpoint Signed-off-by: Brian Phillips <28457+brianphillips@users.noreply.github.com> --- .changeset/fast-zebras-watch.md | 6 ++++++ plugins/devtools-backend/src/service/router.ts | 7 +++++++ plugins/devtools-common/src/permissions.ts | 12 ++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 .changeset/fast-zebras-watch.md diff --git a/.changeset/fast-zebras-watch.md b/.changeset/fast-zebras-watch.md new file mode 100644 index 0000000000..4a114347e1 --- /dev/null +++ b/.changeset/fast-zebras-watch.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-devtools-backend': patch +'@backstage/plugin-devtools-common': patch +--- + +Expose permissions through the metadata endpoint. diff --git a/plugins/devtools-backend/src/service/router.ts b/plugins/devtools-backend/src/service/router.ts index a346ad3183..fbeeb5af84 100644 --- a/plugins/devtools-backend/src/service/router.ts +++ b/plugins/devtools-backend/src/service/router.ts @@ -21,6 +21,7 @@ import { devToolsConfigReadPermission, devToolsExternalDependenciesReadPermission, devToolsInfoReadPermission, + devToolsPermissions, } from '@backstage/plugin-devtools-common'; import { Config } from '@backstage/config'; @@ -31,6 +32,7 @@ import Router from 'express-promise-router'; import { errorHandler } from '@backstage/backend-common'; import express from 'express'; import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; /** @public */ export interface RouterOptions { @@ -51,6 +53,11 @@ export async function createRouter( const router = Router(); router.use(express.json()); + router.use( + createPermissionIntegrationRouter({ + permissions: devToolsPermissions, + }), + ); router.get('/health', (_req, res) => { res.status(200).json({ status: 'ok' }); diff --git a/plugins/devtools-common/src/permissions.ts b/plugins/devtools-common/src/permissions.ts index ae62cfdc19..ba464f7d80 100644 --- a/plugins/devtools-common/src/permissions.ts +++ b/plugins/devtools-common/src/permissions.ts @@ -47,3 +47,15 @@ export const devToolsExternalDependenciesReadPermission = createPermission({ name: 'devtools.external-dependencies', attributes: { action: 'read' }, }); + +/** + * List of all Devtools permissions + * + * @public + */ +export const devToolsPermissions = [ + devToolsAdministerPermission, + devToolsInfoReadPermission, + devToolsConfigReadPermission, + devToolsExternalDependenciesReadPermission, +]; From 4c7e90706e9a827a8a9bb9924b33281ad3a5dad2 Mon Sep 17 00:00:00 2001 From: Brian Phillips <28457+brianphillips@users.noreply.github.com> Date: Wed, 17 May 2023 14:01:42 -0500 Subject: [PATCH 054/255] add api report Signed-off-by: Brian Phillips <28457+brianphillips@users.noreply.github.com> --- plugins/devtools-common/api-report.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/devtools-common/api-report.md b/plugins/devtools-common/api-report.md index 7e38ff05b0..6594b0d8a4 100644 --- a/plugins/devtools-common/api-report.md +++ b/plugins/devtools-common/api-report.md @@ -40,6 +40,9 @@ export type DevToolsInfo = { // @public (undocumented) export const devToolsInfoReadPermission: BasicPermission; +// @public +export const devToolsPermissions: BasicPermission[]; + // @public (undocumented) export type Endpoint = { name: string; From 459331f657ece3dfdb41182a8c78aacdc8c35f37 Mon Sep 17 00:00:00 2001 From: Brian Phillips <28457+brianphillips@users.noreply.github.com> Date: Wed, 17 May 2023 20:37:51 -0500 Subject: [PATCH 055/255] Split changeset for each package Signed-off-by: Brian Phillips <28457+brianphillips@users.noreply.github.com> --- .changeset/fast-zebras-watch.md | 1 - .changeset/plenty-roses-raise.md | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changeset/plenty-roses-raise.md diff --git a/.changeset/fast-zebras-watch.md b/.changeset/fast-zebras-watch.md index 4a114347e1..7466565e79 100644 --- a/.changeset/fast-zebras-watch.md +++ b/.changeset/fast-zebras-watch.md @@ -1,6 +1,5 @@ --- '@backstage/plugin-devtools-backend': patch -'@backstage/plugin-devtools-common': patch --- Expose permissions through the metadata endpoint. diff --git a/.changeset/plenty-roses-raise.md b/.changeset/plenty-roses-raise.md new file mode 100644 index 0000000000..71605705e3 --- /dev/null +++ b/.changeset/plenty-roses-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-devtools-common': patch +--- + +Export the list of permissions From fb47daef2481f9e587b0aeefe407aecd6dafd6f2 Mon Sep 17 00:00:00 2001 From: Christopher Diaz Date: Mon, 22 May 2023 12:56:18 -0400 Subject: [PATCH 056/255] chore: adds test to ToolDocumentCollatorFactory Signed-off-by: Christopher Diaz --- .../ToolDocumentCollatorFactory.test.ts | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts diff --git a/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts new file mode 100644 index 0000000000..46bbd55600 --- /dev/null +++ b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts @@ -0,0 +1,122 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + getVoidLogger, + PluginEndpointDiscovery, + TokenManager, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { TestPipeline } from '@backstage/plugin-search-backend-node'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { rest } from 'msw'; +import { setupServer } from 'msw'; +import { Readable } from 'stream'; +import { ToolDocumentCollatorFactory } from './ToolDocumentCollatorFactory'; + +const logger = getVoidLogger(); + +const mockTools = { + tools: [ + { + title: 'tool1', + description: 'tool 1 description', + url: 'https://some-url.com', + image: 'https://some-url.com/image.png', + tags: ['one'], + }, + { + title: 'tool2', + description: 'tool 2 description', + url: 'https://some-url.com', + image: 'https://some-url.com/image.png', + tags: ['two'], + }, + { + title: 'tool3', + description: 'tool 3 description', + url: 'https://some-url.com', + image: 'https://some-url.com/image.png', + tags: ['three'], + }, + ], +}; + +describe('ToolDocumentCollatorFactory', () => { + const config = new ConfigReader({}); + const mockDiscoveryApi: jest.Mocked = { + getBaseUrl: jest.fn().mockResolvedValue('http://test-backend/api/explore'), + getExternalBaseUrl: jest.fn(), + }; + const mockTokenManager: jest.Mocked = { + getToken: jest.fn().mockResolvedValue({ token: '' }), + authenticate: jest.fn(), + }; + const options = { + discovery: mockDiscoveryApi, + logger, + tokenManager: mockTokenManager, + }; + + it('has expected type', () => { + const factory = ToolDocumentCollatorFactory.fromConfig(config, options); + expect(factory.type).toBe('tools'); + }); + + describe('getCollator', () => { + let factory: ToolDocumentCollatorFactory; + let collator: Readable; + + const worker = setupServer(); + setupRequestMockHandlers(worker); + + beforeEach(async () => { + factory = ToolDocumentCollatorFactory.fromConfig(config, options); + collator = await factory.getCollator(); + + worker.use( + rest.get('http://test-backend/api/explore/tools', (_, res, ctx) => + res(ctx.status(200), ctx.json(mockTools)), + ), + ); + }); + + it('returns a readable stream', async () => { + expect(collator).toBeInstanceOf(Readable); + }); + + it('runs against mock tools', async () => { + const pipeline = TestPipeline.fromCollator(collator); + const { documents } = await pipeline.execute(); + expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('explore'); + expect(documents).toHaveLength(mockTools.tools.length); + }); + + it('non-authenticated backend', async () => { + // Provide an alternate location template. + factory = ToolDocumentCollatorFactory.fromConfig(config, { + discovery: mockDiscoveryApi, + logger, + }); + collator = await factory.getCollator(); + + const pipeline = TestPipeline.fromCollator(collator); + const { documents } = await pipeline.execute(); + + expect(mockDiscoveryApi.getBaseUrl).toHaveBeenCalledWith('explore'); + expect(documents).toHaveLength(mockTools.tools.length); + }); + }); +}); From 733173e1d8bf6597f77300f3766b9a55977001dd Mon Sep 17 00:00:00 2001 From: Christopher Diaz Date: Mon, 22 May 2023 13:37:24 -0400 Subject: [PATCH 057/255] fix setup server import Signed-off-by: Christopher Diaz --- .../src/collators/ToolDocumentCollatorFactory.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts index 46bbd55600..e087f870df 100644 --- a/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts +++ b/plugins/search-backend-module-explore/src/collators/ToolDocumentCollatorFactory.test.ts @@ -22,7 +22,7 @@ import { ConfigReader } from '@backstage/config'; import { TestPipeline } from '@backstage/plugin-search-backend-node'; import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { rest } from 'msw'; -import { setupServer } from 'msw'; +import { setupServer } from 'msw/node'; import { Readable } from 'stream'; import { ToolDocumentCollatorFactory } from './ToolDocumentCollatorFactory'; From df8411779da1db1f4a4f4b18bb124cdfe50df2fa Mon Sep 17 00:00:00 2001 From: Andrew Ochsner Date: Mon, 22 May 2023 12:35:17 -0500 Subject: [PATCH 058/255] Add repository variables and secrets Signed-off-by: Andrew Ochsner --- .changeset/wet-vans-drive.md | 5 + plugins/scaffolder-backend/api-report.md | 20 ++ plugins/scaffolder-backend/package.json | 4 +- .../builtin/github/githubRepoCreate.test.ts | 76 +++++++ .../builtin/github/githubRepoCreate.ts | 8 + .../actions/builtin/github/helpers.ts | 45 ++++ .../actions/builtin/github/inputProperties.ts | 14 ++ .../actions/builtin/publish/github.test.ts | 76 +++++++ .../actions/builtin/publish/github.ts | 8 + yarn.lock | 197 ++++++++---------- 10 files changed, 346 insertions(+), 107 deletions(-) create mode 100644 .changeset/wet-vans-drive.md diff --git a/.changeset/wet-vans-drive.md b/.changeset/wet-vans-drive.md new file mode 100644 index 0000000000..903e32209e --- /dev/null +++ b/.changeset/wet-vans-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Add support for Repository Variables and Secrets to the `publish:github` and `github:repo:create` scaffolder actions. diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 07dd5eaf1d..5e18ad15bf 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -308,6 +308,16 @@ export function createGithubRepoCreateAction(options: { hasIssues?: boolean | undefined; token?: string | undefined; topics?: string[] | undefined; + repoVariables?: + | { + [key: string]: string; + } + | undefined; + secrets?: + | { + [key: string]: string; + } + | undefined; requireCommitSigning?: boolean | undefined; }, JsonObject @@ -552,6 +562,16 @@ export function createPublishGithubAction(options: { hasIssues?: boolean | undefined; token?: string | undefined; topics?: string[] | undefined; + repoVariables?: + | { + [key: string]: string; + } + | undefined; + secrets?: + | { + [key: string]: string; + } + | undefined; requiredCommitSigning?: boolean | undefined; }, JsonObject diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index aeb7861f4b..1e655a01c1 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -81,12 +81,13 @@ "isomorphic-git": "^1.23.0", "jsonschema": "^1.2.6", "knex": "^2.0.0", + "libsodium-wrappers": "^0.7.11", "lodash": "^4.17.21", "luxon": "^3.0.0", "morgan": "^1.10.0", "node-fetch": "^2.6.7", "nunjucks": "^3.2.3", - "octokit": "^2.0.0", + "octokit": "^2.0.3", "octokit-plugin-create-pull-request": "^3.10.0", "p-limit": "^3.1.0", "p-queue": "^6.6.2", @@ -104,6 +105,7 @@ "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", + "@types/libsodium-wrappers": "^0.7.10", "@types/mock-fs": "^4.13.0", "@types/nunjucks": "^3.1.4", "@types/supertest": "^2.0.8", diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts index 4f23839fca..b5b31d0773 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts @@ -30,6 +30,8 @@ import { PassThrough } from 'stream'; import { createGithubRepoCreateAction } from './githubRepoCreate'; import { entityRefToName } from '../helpers'; +const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU='; + const mockOctokit = { rest: { users: { @@ -45,6 +47,11 @@ const mockOctokit = { addOrUpdateRepoPermissionsInOrg: jest.fn(), getByName: jest.fn(), }, + actions: { + createRepoVariable: jest.fn(), + createOrUpdateRepoSecret: jest.fn(), + getRepoPublicKey: jest.fn(), + }, }, }; jest.mock('octokit', () => ({ @@ -91,6 +98,12 @@ describe('github:repo:create', () => { githubCredentialsProvider, }); (entityRefToName as jest.Mock).mockImplementation((s: string) => s); + mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({ + data: { + key: publicKey, + key_id: 'keyid', + }, + }); }); afterEach(jest.resetAllMocks); @@ -571,6 +584,69 @@ describe('github:repo:create', () => { }); }); + it('should add variables when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoVariables: { + foo: 'bar', + }, + }, + }); + + expect(mockOctokit.rest.actions.createRepoVariable).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + name: 'foo', + value: 'bar', + }); + }); + + it('should add secrets when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + secrets: { + foo: 'bar', + }, + }, + }); + + expect( + mockOctokit.rest.actions.createOrUpdateRepoSecret, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + secret_name: 'foo', + key_id: 'keyid', + encrypted_value: expect.any(String), + }); + }); + it('should call output with the remoteUrl', async () => { mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts index b5f779b8a7..0ec1c83910 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts @@ -90,6 +90,8 @@ export function createGithubRepoCreateAction(options: { hasIssues?: boolean; token?: string; topics?: string[]; + repoVariables?: { [key: string]: string }; + secrets?: { [key: string]: string }; requireCommitSigning?: boolean; }>({ id: 'github:repo:create', @@ -125,6 +127,8 @@ export function createGithubRepoCreateAction(options: { hasIssues: inputProps.hasIssues, token: inputProps.token, topics: inputProps.topics, + repoVariables: inputProps.repoVariables, + secrets: inputProps.secrets, requiredCommitSigning: inputProps.requiredCommitSigning, }, }, @@ -155,6 +159,8 @@ export function createGithubRepoCreateAction(options: { hasWiki = undefined, hasIssues = undefined, topics, + repoVariables, + secrets, token: providedToken, } = ctx.input; @@ -192,6 +198,8 @@ export function createGithubRepoCreateAction(options: { hasWiki, hasIssues, topics, + repoVariables, + secrets, ctx.logger, ); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts index b41beaede8..dc1b4eb91c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts @@ -30,6 +30,7 @@ import { } from '../helpers'; import { getRepoSourceDirectory, parseRepoUrl } from '../publish/util'; import { entityRefToName } from '../../builtin/helpers'; +import Sodium from 'libsodium-wrappers'; const DEFAULT_TIMEOUT_MS = 60_000; @@ -129,6 +130,8 @@ export async function createGithubRepoWithCollaboratorsAndTopics( hasWiki: boolean | undefined, hasIssues: boolean | undefined, topics: string[] | undefined, + repoVariables: { [key: string]: string } | undefined, + secrets: { [key: string]: string } | undefined, logger: Logger, ) { // eslint-disable-next-line testing-library/no-await-sync-query @@ -146,6 +149,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics( name: repo, org: owner, private: repoVisibility === 'private', + // @ts-ignore visibility: repoVisibility, description: description, delete_branch_on_merge: deleteBranchOnMerge, @@ -254,6 +258,47 @@ export async function createGithubRepoWithCollaboratorsAndTopics( } } + for (const [key, value] of Object.entries(repoVariables ?? {})) { + await client.rest.actions.createRepoVariable({ + owner, + repo, + name: key, + value: value, + }); + } + + if (secrets) { + const publicKeyResponse = await client.rest.actions.getRepoPublicKey({ + owner, + repo, + }); + + await Sodium.ready; + const binaryKey = Sodium.from_base64( + publicKeyResponse.data.key, + Sodium.base64_variants.ORIGINAL, + ); + for (const [key, value] of Object.entries(secrets)) { + const binarySecret = Sodium.from_string(value); + const encryptedBinarySecret = Sodium.crypto_box_seal( + binarySecret, + binaryKey, + ); + const encryptedBase64Secret = Sodium.to_base64( + encryptedBinarySecret, + Sodium.base64_variants.ORIGINAL, + ); + + await client.rest.actions.createOrUpdateRepoSecret({ + owner, + repo, + secret_name: key, + encrypted_value: encryptedBase64Secret, + key_id: publicKeyResponse.data.key_id, + }); + } + } + return newRepo; } diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts index b1286953a6..96e1f3b6e1 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts @@ -265,6 +265,18 @@ const requiredCommitSigning = { description: `Require commit signing so that you must sign commits on this branch.`, }; +const repoVariables = { + title: 'Repository Variables', + description: `Variables attached to the repository`, + type: 'object', +}; + +const secrets = { + title: 'Repository Secrets', + description: `Secrets attached to the repository`, + type: 'object', +}; + export { access }; export { allowMergeCommit }; export { allowRebaseMerge }; @@ -299,3 +311,5 @@ export { sourcePath }; export { token }; export { topics }; export { requiredCommitSigning }; +export { repoVariables }; +export { secrets }; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts index 50d40f1bd5..e15d085cbf 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts @@ -32,6 +32,8 @@ import { } from '../helpers'; import { createPublishGithubAction } from './github'; +const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU='; + const initRepoAndPushMocked = initRepoAndPush as jest.Mock< Promise<{ commitHash: string }> >; @@ -51,6 +53,11 @@ const mockOctokit = { getByName: jest.fn(), addOrUpdateRepoPermissionsInOrg: jest.fn(), }, + actions: { + createRepoVariable: jest.fn(), + createOrUpdateRepoSecret: jest.fn(), + getRepoPublicKey: jest.fn(), + }, }, }; jest.mock('octokit', () => ({ @@ -107,6 +114,12 @@ describe('publish:github', () => { (entityRefToName as jest.Mock).mockImplementation( realFamiliarizeEntityName, ); + mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({ + data: { + key: publicKey, + key_id: 'keyid', + }, + }); }); afterEach(jest.resetAllMocks); @@ -836,6 +849,69 @@ describe('publish:github', () => { }); }); + it('should add variables when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoVariables: { + foo: 'bar', + }, + }, + }); + + expect(mockOctokit.rest.actions.createRepoVariable).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + name: 'foo', + value: 'bar', + }); + }); + + it('should add secrets when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + secrets: { + foo: 'bar', + }, + }, + }); + + expect( + mockOctokit.rest.actions.createOrUpdateRepoSecret, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + secret_name: 'foo', + key_id: 'keyid', + encrypted_value: expect.any(String), + }); + }); + it('should call output with the remoteUrl and the repoContentsUrl', async () => { mockOctokit.rest.users.getByUsername.mockResolvedValue({ data: { type: 'User' }, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index ef74c62083..9336c38b63 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -104,6 +104,8 @@ export function createPublishGithubAction(options: { hasIssues?: boolean | undefined; token?: string; topics?: string[]; + repoVariables?: { [key: string]: string }; + secrets?: { [key: string]: string }; requiredCommitSigning?: boolean; }>({ id: 'publish:github', @@ -148,6 +150,8 @@ export function createPublishGithubAction(options: { hasIssues: inputProps.hasIssues, token: inputProps.token, topics: inputProps.topics, + repoVariables: inputProps.repoVariables, + secrets: inputProps.secrets, requiredCommitSigning: inputProps.requiredCommitSigning, }, }, @@ -193,6 +197,8 @@ export function createPublishGithubAction(options: { hasWiki = undefined, hasIssues = undefined, topics, + repoVariables, + secrets, token: providedToken, requiredCommitSigning = false, } = ctx.input; @@ -231,6 +237,8 @@ export function createPublishGithubAction(options: { hasWiki, hasIssues, topics, + repoVariables, + secrets, ctx.logger, ); diff --git a/yarn.lock b/yarn.lock index 7fbe9f51ff..23314279d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8234,6 +8234,7 @@ __metadata: "@types/express": ^4.17.6 "@types/fs-extra": ^9.0.1 "@types/git-url-parse": ^9.0.0 + "@types/libsodium-wrappers": ^0.7.10 "@types/luxon": ^3.0.0 "@types/mock-fs": ^4.13.0 "@types/nunjucks": ^3.1.4 @@ -8254,6 +8255,7 @@ __metadata: jest-when: ^3.1.0 jsonschema: ^1.2.6 knex: ^2.0.0 + libsodium-wrappers: ^0.7.11 lodash: ^4.17.21 luxon: ^3.0.0 mock-fs: ^5.1.0 @@ -8261,7 +8263,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.6.7 nunjucks: ^3.2.3 - octokit: ^2.0.0 + octokit: ^2.0.3 octokit-plugin-create-pull-request: ^3.10.0 p-limit: ^3.1.0 p-queue: ^6.6.2 @@ -13021,22 +13023,22 @@ __metadata: languageName: node linkType: hard -"@octokit/app@npm:^13.0.5": - version: 13.0.5 - resolution: "@octokit/app@npm:13.0.5" +"@octokit/app@npm:^13.1.3": + version: 13.1.4 + resolution: "@octokit/app@npm:13.1.4" dependencies: - "@octokit/auth-app": ^4.0.0 + "@octokit/auth-app": ^4.0.8 "@octokit/auth-unauthenticated": ^3.0.0 "@octokit/core": ^4.0.0 - "@octokit/oauth-app": ^4.0.4 - "@octokit/plugin-paginate-rest": ^3.0.0 - "@octokit/types": ^6.27.1 + "@octokit/oauth-app": ^4.0.7 + "@octokit/plugin-paginate-rest": ^6.0.0 + "@octokit/types": ^9.0.0 "@octokit/webhooks": ^10.0.0 - checksum: 15fdff892f0ec82a1121b94e82eb09aad8a868147efecb0613dc44b06aeb8252f5a4caade2d929644fd7fa8bff2df56f1fb702506574dafa3df8653421a6015a + checksum: 1c322fea80a5e7186b692990cdc07fc4ef4ee187b2098e1a687310c0d5d717ea144ab241eb6f1ca15841fdcfdf1bfbd5d10eeb34278d02b9ee8c7e1bd8f20dfa languageName: node linkType: hard -"@octokit/auth-app@npm:^4.0.0": +"@octokit/auth-app@npm:^4.0.0, @octokit/auth-app@npm:^4.0.8": version: 4.0.13 resolution: "@octokit/auth-app@npm:4.0.13" dependencies: @@ -13125,18 +13127,18 @@ __metadata: languageName: node linkType: hard -"@octokit/core@npm:^4.0.0, @octokit/core@npm:^4.0.4, @octokit/core@npm:^4.1.0": - version: 4.1.0 - resolution: "@octokit/core@npm:4.1.0" +"@octokit/core@npm:^4.0.0, @octokit/core@npm:^4.1.0, @octokit/core@npm:^4.2.1": + version: 4.2.1 + resolution: "@octokit/core@npm:4.2.1" dependencies: "@octokit/auth-token": ^3.0.0 "@octokit/graphql": ^5.0.0 "@octokit/request": ^6.0.0 "@octokit/request-error": ^3.0.0 - "@octokit/types": ^8.0.0 + "@octokit/types": ^9.0.0 before-after-hook: ^2.2.0 universal-user-agent: ^6.0.0 - checksum: 4e53e02ff3ebe808b74385be0055cc1cce4b548060b20e3f2d5dd1bf7877ff7b6556f11278edc070842bf24aa869f9f59a02638f1b14083eb290b9e297447a2d + checksum: f82d52e937e12da1c7c163341c845b8e27e7fa75678f5e5954e6fa017a94f1833d6e5c4e43f0be796fbfea9dc5e1137087f655dbd5acb3d57879e1b28568e0a9 languageName: node linkType: hard @@ -13173,7 +13175,7 @@ __metadata: languageName: node linkType: hard -"@octokit/oauth-app@npm:^4.0.4, @octokit/oauth-app@npm:^4.0.6, @octokit/oauth-app@npm:^4.2.0": +"@octokit/oauth-app@npm:^4.0.7, @octokit/oauth-app@npm:^4.2.0, @octokit/oauth-app@npm:^4.2.1": version: 4.2.2 resolution: "@octokit/oauth-app@npm:4.2.2" dependencies: @@ -13244,50 +13246,22 @@ __metadata: languageName: node linkType: hard -"@octokit/openapi-types@npm:^14.0.0": - version: 14.0.0 - resolution: "@octokit/openapi-types@npm:14.0.0" - checksum: 0a1f8f3be998cd82c5a640e9166d43fd183b33d5d36f5e1a9b81608e94d0da87c01ec46c9988f69cd26585d4e2ffc4d3ec99ee4f75e5fe997fc86dad0aa8293c +"@octokit/openapi-types@npm:^17.2.0": + version: 17.2.0 + resolution: "@octokit/openapi-types@npm:17.2.0" + checksum: 29995e34f98d9d64ba234d64a7ae9486c66d2bb6ac0d30d9a42decdbb4b03b13e811769b1e1505a1748ff20c22d35724985e6c128cd11a3f14f8322201520093 languageName: node linkType: hard -"@octokit/openapi-types@npm:^16.0.0": - version: 16.0.0 - resolution: "@octokit/openapi-types@npm:16.0.0" - checksum: 844f30a545da380d63c712e0eb733366bc567d1aab34529c79fdfbec3d73810e81d83f06fdab13058a5cbc7dae786db1a9b90b5b61b1e606854ee45d5ec5f194 - languageName: node - linkType: hard - -"@octokit/plugin-paginate-rest@npm:^3.0.0": - version: 3.0.0 - resolution: "@octokit/plugin-paginate-rest@npm:3.0.0" +"@octokit/plugin-paginate-rest@npm:^6.0.0, @octokit/plugin-paginate-rest@npm:^6.1.0": + version: 6.1.2 + resolution: "@octokit/plugin-paginate-rest@npm:6.1.2" dependencies: - "@octokit/types": ^6.39.0 + "@octokit/tsconfig": ^1.0.2 + "@octokit/types": ^9.2.3 peerDependencies: "@octokit/core": ">=4" - checksum: 1d2c900254f3dcd43f7ba69dfd12ff63f93a0d39a1bf542b1d0f006e95da4924ae0a26044c864ad7fb0309047f44becaf76293aae334d14c946910d65edd2523 - languageName: node - linkType: hard - -"@octokit/plugin-paginate-rest@npm:^4.0.0": - version: 4.0.0 - resolution: "@octokit/plugin-paginate-rest@npm:4.0.0" - dependencies: - "@octokit/types": ^7.0.0 - peerDependencies: - "@octokit/core": ">=4" - checksum: a3fbc1d540cc919df8935dea88b36d59ae85f417713476d8679f1cf8b733cfb44b96d3020094bd7f8daa7b84e4ac9b8403c1cdad323289d586fdb2ed1dcae7c0 - languageName: node - linkType: hard - -"@octokit/plugin-paginate-rest@npm:^6.0.0": - version: 6.0.0 - resolution: "@octokit/plugin-paginate-rest@npm:6.0.0" - dependencies: - "@octokit/types": ^9.0.0 - peerDependencies: - "@octokit/core": ">=4" - checksum: 4ad89568d883373898b733837cada7d849d51eef32157c11d4a81cef5ce8e509720d79b46918cada3c132f9b29847e383f17b7cd5c39ede7c93cdcd2f850b47f + checksum: a7b3e686c7cbd27ec07871cde6e0b1dc96337afbcef426bbe3067152a17b535abd480db1861ca28c88d93db5f7bfdbcadd0919ead19818c28a69d0e194038065 languageName: node linkType: hard @@ -13300,49 +13274,39 @@ __metadata: languageName: node linkType: hard -"@octokit/plugin-rest-endpoint-methods@npm:^6.0.0": - version: 6.7.0 - resolution: "@octokit/plugin-rest-endpoint-methods@npm:6.7.0" +"@octokit/plugin-rest-endpoint-methods@npm:^7.0.0, @octokit/plugin-rest-endpoint-methods@npm:^7.1.1": + version: 7.1.2 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:7.1.2" dependencies: - "@octokit/types": ^8.0.0 + "@octokit/types": ^9.2.3 deprecation: ^2.3.1 peerDependencies: "@octokit/core": ">=3" - checksum: 513c6c0717d08f3e85848029bd700412b7d9787750f78cc79a3dede356e94b238bf8a518b108f556a7efe626871720dd0466de9f31091578ea4e0f5a016f0ae7 + checksum: 159d29bf28d7aecbe39f08c25cf376d39b6c90ce17e50a55eafb44f3e4b9e1053a300c1edd72f308ae386146a17cbad46c410c1cfd000b048adf9c21d6922a1a languageName: node linkType: hard -"@octokit/plugin-rest-endpoint-methods@npm:^7.0.0": - version: 7.0.1 - resolution: "@octokit/plugin-rest-endpoint-methods@npm:7.0.1" +"@octokit/plugin-retry@npm:^4.1.3": + version: 4.1.3 + resolution: "@octokit/plugin-retry@npm:4.1.3" dependencies: "@octokit/types": ^9.0.0 - deprecation: ^2.3.1 + bottleneck: ^2.15.3 peerDependencies: "@octokit/core": ">=3" - checksum: cdb8734ec960f75acc2405284920c58efac9a71b1c3b2a71662b9100ffbc22dac597150acff017a93459c57e9a492d9e1c27872b068387dbb90597de75065fcf + checksum: f9ed5869be23dddcf1ee896ce996e46a412a586259b55612ba44c82cdeed91436102e6e3ec57db879bd91a4446dcafbaa94632e4e059c6af56d9cca9b163eacb languageName: node linkType: hard -"@octokit/plugin-retry@npm:^3.0.9": - version: 3.0.9 - resolution: "@octokit/plugin-retry@npm:3.0.9" +"@octokit/plugin-throttling@npm:^5.2.2": + version: 5.2.3 + resolution: "@octokit/plugin-throttling@npm:5.2.3" dependencies: - "@octokit/types": ^6.0.3 - bottleneck: ^2.15.3 - checksum: 5744780d308dd2f2b8174264604a9f8ea977374256f5eaf0314e5181c32f96ec53a3cfcee67bf1b48dc7eed401ebefebd2fa744b41cf03103affac92f397a874 - languageName: node - linkType: hard - -"@octokit/plugin-throttling@npm:^4.0.1": - version: 4.0.1 - resolution: "@octokit/plugin-throttling@npm:4.0.1" - dependencies: - "@octokit/types": ^6.0.1 + "@octokit/types": ^9.0.0 bottleneck: ^2.15.3 peerDependencies: "@octokit/core": ^4.0.0 - checksum: 7642e5968922b8348510782e7675189810d6ed6cb3fc6ae231fd4614ebdb011759628c8f5fbca40f37e5a2cf69898b8b58cdb7722ae75da59012430c6cf7b4d8 + checksum: ce7ca75d150c63cf1bbcb5b385513bd8cd1f714c5e59f33d25c2afd08fa730250055ef8dffa74113f92e7fb3f209a147442242151607a513f55e4ce382c8e80c languageName: node linkType: hard @@ -13408,7 +13372,14 @@ __metadata: languageName: node linkType: hard -"@octokit/types@npm:^6.0.1, @octokit/types@npm:^6.0.3, @octokit/types@npm:^6.10.0, @octokit/types@npm:^6.12.2, @octokit/types@npm:^6.16.1, @octokit/types@npm:^6.27.1, @octokit/types@npm:^6.39.0, @octokit/types@npm:^6.8.2": +"@octokit/tsconfig@npm:^1.0.2": + version: 1.0.2 + resolution: "@octokit/tsconfig@npm:1.0.2" + checksum: 74d56f3e9f326a8dd63700e9a51a7c75487180629c7a68bbafee97c612fbf57af8347369bfa6610b9268a3e8b833c19c1e4beb03f26db9a9dce31f6f7a19b5b1 + languageName: node + linkType: hard + +"@octokit/types@npm:^6.0.3, @octokit/types@npm:^6.10.0, @octokit/types@npm:^6.12.2, @octokit/types@npm:^6.16.1, @octokit/types@npm:^6.8.2": version: 6.41.0 resolution: "@octokit/types@npm:6.41.0" dependencies: @@ -13426,21 +13397,12 @@ __metadata: languageName: node linkType: hard -"@octokit/types@npm:^8.0.0": - version: 8.0.0 - resolution: "@octokit/types@npm:8.0.0" +"@octokit/types@npm:^9.0.0, @octokit/types@npm:^9.2.2, @octokit/types@npm:^9.2.3": + version: 9.2.3 + resolution: "@octokit/types@npm:9.2.3" dependencies: - "@octokit/openapi-types": ^14.0.0 - checksum: 1a0197b2c4c522ac90f145e02b3f8cb048a47f71c2c6bdbf021a03db7dd30ca92a899c0186acb401337f218efe44e60d33cc1cc68715b622bb75bc1a4e79515d - languageName: node - linkType: hard - -"@octokit/types@npm:^9.0.0": - version: 9.0.0 - resolution: "@octokit/types@npm:9.0.0" - dependencies: - "@octokit/openapi-types": ^16.0.0 - checksum: 5c7f5cca8f00f7c4daa0d00f4fe991c1598ec47cd6ced50b1c5fbe9721bb9dee0adc2acdee265a3a715bb984e53ef3dc7f1cfb7326f712c6d809d59fc5c6648d + "@octokit/openapi-types": ^17.2.0 + checksum: 6806413089f20a8302237ef85aa2e83bace7499e95fdc3db2d304f9e6dc6e87fb6766452f92e08ddf475046b69753e11beabaeff6733c38bdaf3e21dfd7d3341 languageName: node linkType: hard @@ -16114,6 +16076,13 @@ __metadata: languageName: node linkType: hard +"@types/libsodium-wrappers@npm:^0.7.10": + version: 0.7.10 + resolution: "@types/libsodium-wrappers@npm:0.7.10" + checksum: 717054ebcb5fa553e378144b8d564bed8b691905c0d4e90b95c64d77ba24ec9fe798cb2c58cd61dad545ceacb1f05ab69b5597217f9829f2da7a23f0688d11d0 + languageName: node + linkType: hard + "@types/linkify-it@npm:*": version: 3.0.2 resolution: "@types/linkify-it@npm:3.0.2" @@ -29294,6 +29263,22 @@ __metadata: languageName: node linkType: hard +"libsodium-wrappers@npm:^0.7.11": + version: 0.7.11 + resolution: "libsodium-wrappers@npm:0.7.11" + dependencies: + libsodium: ^0.7.11 + checksum: 6a6ef47b2213e3fb4687196c28fee4c9885f70d89547d845e62d96014d3d5ad9f59cb05fadc601debc0031a3cfd0b9b416d7efbeb5bf66db6aa0ed69f55a6293 + languageName: node + linkType: hard + +"libsodium@npm:^0.7.11": + version: 0.7.11 + resolution: "libsodium@npm:0.7.11" + checksum: 0a3493ac1829d1e346178b6984c4eb449dc77157c906876441386c0c653142e3fa56f623ce980bb50e580196578689298c9cd406ce6d514904090e370c6bc0f7 + languageName: node + linkType: hard + "lilconfig@npm:2.1.0, lilconfig@npm:^2.0.3": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" @@ -32204,19 +32189,19 @@ __metadata: languageName: node linkType: hard -"octokit@npm:^2.0.0, octokit@npm:^2.0.4": - version: 2.0.7 - resolution: "octokit@npm:2.0.7" +"octokit@npm:^2.0.3, octokit@npm:^2.0.4": + version: 2.0.16 + resolution: "octokit@npm:2.0.16" dependencies: - "@octokit/app": ^13.0.5 - "@octokit/core": ^4.0.4 - "@octokit/oauth-app": ^4.0.6 - "@octokit/plugin-paginate-rest": ^4.0.0 - "@octokit/plugin-rest-endpoint-methods": ^6.0.0 - "@octokit/plugin-retry": ^3.0.9 - "@octokit/plugin-throttling": ^4.0.1 - "@octokit/types": ^7.0.0 - checksum: 26eeba2b3d0614db4147980f6a9acb2a595f3ea6d78e9db5ef485b2c1b4606724e8a0b4c342a9f264903242ada5c329ee82eccb3fd8df822dff1944aa5bf20db + "@octokit/app": ^13.1.3 + "@octokit/core": ^4.2.1 + "@octokit/oauth-app": ^4.2.1 + "@octokit/plugin-paginate-rest": ^6.1.0 + "@octokit/plugin-rest-endpoint-methods": ^7.1.1 + "@octokit/plugin-retry": ^4.1.3 + "@octokit/plugin-throttling": ^5.2.2 + "@octokit/types": ^9.2.2 + checksum: 975e636bc99c4965c65ad4bb17e6f6bdc7b3ab911f8b0273f149ad3f2c80982f0a382015255869d9a2ca85ab6d4d2b1e491d29084fff9c0b75e89ec77dfafdd8 languageName: node linkType: hard From 273d4be90109f607ed7ff78151f0679b11c56d88 Mon Sep 17 00:00:00 2001 From: Christopher Diaz Date: Mon, 22 May 2023 14:11:17 -0400 Subject: [PATCH 059/255] add dev dependency Signed-off-by: Christopher Diaz --- plugins/search-backend-module-explore/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 6a6f92a9cc..0676a9455a 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -48,7 +48,8 @@ }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", - "@backstage/cli": "workspace:^" + "@backstage/cli": "workspace:^", + "msw": "^1.2.1" }, "files": [ "dist" From 468ed810768e5334ae1f1766bb056cb13e984724 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 20:19:59 +0000 Subject: [PATCH 060/255] chore(deps): update dependency webpack to v5.83.1 Signed-off-by: Renovate Bot --- storybook/yarn.lock | 216 +++++++++++++++++++------------------- yarn.lock | 246 ++++++++++++++++++++++---------------------- 2 files changed, 231 insertions(+), 231 deletions(-) diff --git a/storybook/yarn.lock b/storybook/yarn.lock index a4d5837ba5..4205733e1c 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -3444,154 +3444,154 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/ast@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ast@npm:1.11.1" +"@webassemblyjs/ast@npm:1.11.6, @webassemblyjs/ast@npm:^1.11.5": + version: 1.11.6 + resolution: "@webassemblyjs/ast@npm:1.11.6" dependencies: - "@webassemblyjs/helper-numbers": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - checksum: 1eee1534adebeece635362f8e834ae03e389281972611408d64be7895fc49f48f98fddbbb5339bf8a72cb101bcb066e8bca3ca1bf1ef47dadf89def0395a8d87 + "@webassemblyjs/helper-numbers": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + checksum: 38ef1b526ca47c210f30975b06df2faf1a8170b1636ce239fc5738fc231ce28389dd61ecedd1bacfc03cbe95b16d1af848c805652080cb60982836eb4ed2c6cf languageName: node linkType: hard -"@webassemblyjs/floating-point-hex-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.1" - checksum: b8efc6fa08e4787b7f8e682182d84dfdf8da9d9c77cae5d293818bc4a55c1f419a87fa265ab85252b3e6c1fd323d799efea68d825d341a7c365c64bc14750e97 +"@webassemblyjs/floating-point-hex-parser@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.6" + checksum: 29b08758841fd8b299c7152eda36b9eb4921e9c584eb4594437b5cd90ed6b920523606eae7316175f89c20628da14326801090167cc7fbffc77af448ac84b7e2 languageName: node linkType: hard -"@webassemblyjs/helper-api-error@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-api-error@npm:1.11.1" - checksum: 0792813f0ed4a0e5ee0750e8b5d0c631f08e927f4bdfdd9fe9105dc410c786850b8c61bff7f9f515fdfb149903bec3c976a1310573a4c6866a94d49bc7271959 +"@webassemblyjs/helper-api-error@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-api-error@npm:1.11.6" + checksum: e8563df85161096343008f9161adb138a6e8f3c2cc338d6a36011aa55eabb32f2fd138ffe63bc278d009ada001cc41d263dadd1c0be01be6c2ed99076103689f languageName: node linkType: hard -"@webassemblyjs/helper-buffer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-buffer@npm:1.11.1" - checksum: a337ee44b45590c3a30db5a8b7b68a717526cf967ada9f10253995294dbd70a58b2da2165222e0b9830cd4fc6e4c833bf441a721128d1fe2e9a7ab26b36003ce +"@webassemblyjs/helper-buffer@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-buffer@npm:1.11.6" + checksum: b14d0573bf680d22b2522e8a341ec451fddd645d1f9c6bd9012ccb7e587a2973b86ab7b89fe91e1c79939ba96095f503af04369a3b356c8023c13a5893221644 languageName: node linkType: hard -"@webassemblyjs/helper-numbers@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-numbers@npm:1.11.1" +"@webassemblyjs/helper-numbers@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-numbers@npm:1.11.6" dependencies: - "@webassemblyjs/floating-point-hex-parser": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 + "@webassemblyjs/floating-point-hex-parser": 1.11.6 + "@webassemblyjs/helper-api-error": 1.11.6 "@xtuc/long": 4.2.2 - checksum: 44d2905dac2f14d1e9b5765cf1063a0fa3d57295c6d8930f6c59a36462afecc6e763e8a110b97b342a0f13376166c5d41aa928e6ced92e2f06b071fd0db59d3a + checksum: f4b562fa219f84368528339e0f8d273ad44e047a07641ffcaaec6f93e5b76fd86490a009aa91a294584e1436d74b0a01fa9fde45e333a4c657b58168b04da424 languageName: node linkType: hard -"@webassemblyjs/helper-wasm-bytecode@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.1" - checksum: eac400113127832c88f5826bcc3ad1c0db9b3dbd4c51a723cfdb16af6bfcbceb608170fdaac0ab7731a7e18b291be7af68a47fcdb41cfe0260c10857e7413d97 +"@webassemblyjs/helper-wasm-bytecode@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6" + checksum: 3535ef4f1fba38de3475e383b3980f4bbf3de72bbb631c2b6584c7df45be4eccd62c6ff48b5edd3f1bcff275cfd605a37679ec199fc91fd0a7705d7f1e3972dc languageName: node linkType: hard -"@webassemblyjs/helper-wasm-section@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.1" +"@webassemblyjs/helper-wasm-section@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - checksum: 617696cfe8ecaf0532763162aaf748eb69096fb27950219bb87686c6b2e66e11cd0614d95d319d0ab1904bc14ebe4e29068b12c3e7c5e020281379741fe4bedf + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-buffer": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/wasm-gen": 1.11.6 + checksum: b2cf751bf4552b5b9999d27bbb7692d0aca75260140195cb58ea6374d7b9c2dc69b61e10b211a0e773f66209c3ddd612137ed66097e3684d7816f854997682e9 languageName: node linkType: hard -"@webassemblyjs/ieee754@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ieee754@npm:1.11.1" +"@webassemblyjs/ieee754@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/ieee754@npm:1.11.6" dependencies: "@xtuc/ieee754": ^1.2.0 - checksum: 23a0ac02a50f244471631802798a816524df17e56b1ef929f0c73e3cde70eaf105a24130105c60aff9d64a24ce3b640dad443d6f86e5967f922943a7115022ec + checksum: 13574b8e41f6ca39b700e292d7edf102577db5650fe8add7066a320aa4b7a7c09a5056feccac7a74eb68c10dea9546d4461412af351f13f6b24b5f32379b49de languageName: node linkType: hard -"@webassemblyjs/leb128@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/leb128@npm:1.11.1" +"@webassemblyjs/leb128@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/leb128@npm:1.11.6" dependencies: "@xtuc/long": 4.2.2 - checksum: 33ccc4ade2f24de07bf31690844d0b1ad224304ee2062b0e464a610b0209c79e0b3009ac190efe0e6bd568b0d1578d7c3047fc1f9d0197c92fc061f56224ff4a + checksum: 7ea942dc9777d4b18a5ebfa3a937b30ae9e1d2ce1fee637583ed7f376334dd1d4274f813d2e250056cca803e0952def4b954913f1a3c9068bcd4ab4ee5143bf0 languageName: node linkType: hard -"@webassemblyjs/utf8@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/utf8@npm:1.11.1" - checksum: 972c5cfc769d7af79313a6bfb96517253a270a4bf0c33ba486aa43cac43917184fb35e51dfc9e6b5601548cd5931479a42e42c89a13bb591ffabebf30c8a6a0b +"@webassemblyjs/utf8@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/utf8@npm:1.11.6" + checksum: 807fe5b5ce10c390cfdd93e0fb92abda8aebabb5199980681e7c3743ee3306a75729bcd1e56a3903980e96c885ee53ef901fcbaac8efdfa480f9c0dae1d08713 languageName: node linkType: hard -"@webassemblyjs/wasm-edit@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-edit@npm:1.11.1" +"@webassemblyjs/wasm-edit@npm:^1.11.5": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-edit@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/helper-wasm-section": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-opt": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - "@webassemblyjs/wast-printer": 1.11.1 - checksum: 6d7d9efaec1227e7ef7585a5d7ff0be5f329f7c1c6b6c0e906b18ed2e9a28792a5635e450aca2d136770d0207225f204eff70a4b8fd879d3ac79e1dcc26dbeb9 + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-buffer": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/helper-wasm-section": 1.11.6 + "@webassemblyjs/wasm-gen": 1.11.6 + "@webassemblyjs/wasm-opt": 1.11.6 + "@webassemblyjs/wasm-parser": 1.11.6 + "@webassemblyjs/wast-printer": 1.11.6 + checksum: 29ce75870496d6fad864d815ebb072395a8a3a04dc9c3f4e1ffdc63fc5fa58b1f34304a1117296d8240054cfdbc38aca88e71fb51483cf29ffab0a61ef27b481 languageName: node linkType: hard -"@webassemblyjs/wasm-gen@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-gen@npm:1.11.1" +"@webassemblyjs/wasm-gen@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-gen@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1f6921e640293bf99fb16b21e09acb59b340a79f986c8f979853a0ae9f0b58557534b81e02ea2b4ef11e929d946708533fd0693c7f3712924128fdafd6465f5b + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/ieee754": 1.11.6 + "@webassemblyjs/leb128": 1.11.6 + "@webassemblyjs/utf8": 1.11.6 + checksum: a645a2eecbea24833c3260a249704a7f554ef4a94c6000984728e94bb2bc9140a68dfd6fd21d5e0bbb09f6dfc98e083a45760a83ae0417b41a0196ff6d45a23a languageName: node linkType: hard -"@webassemblyjs/wasm-opt@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-opt@npm:1.11.1" +"@webassemblyjs/wasm-opt@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-opt@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - checksum: 21586883a20009e2b20feb67bdc451bbc6942252e038aae4c3a08e6f67b6bae0f5f88f20bfc7bd0452db5000bacaf5ab42b98cf9aa034a6c70e9fc616142e1db + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-buffer": 1.11.6 + "@webassemblyjs/wasm-gen": 1.11.6 + "@webassemblyjs/wasm-parser": 1.11.6 + checksum: b4557f195487f8e97336ddf79f7bef40d788239169aac707f6eaa2fa5fe243557c2d74e550a8e57f2788e70c7ae4e7d32f7be16101afe183d597b747a3bdd528 languageName: node linkType: hard -"@webassemblyjs/wasm-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-parser@npm:1.11.1" +"@webassemblyjs/wasm-parser@npm:1.11.6, @webassemblyjs/wasm-parser@npm:^1.11.5": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-parser@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1521644065c360e7b27fad9f4bb2df1802d134dd62937fa1f601a1975cde56bc31a57b6e26408b9ee0228626ff3ba1131ae6f74ffb7d718415b6528c5a6dbfc2 + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-api-error": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/ieee754": 1.11.6 + "@webassemblyjs/leb128": 1.11.6 + "@webassemblyjs/utf8": 1.11.6 + checksum: 8200a8d77c15621724a23fdabe58d5571415cda98a7058f542e670ea965dd75499f5e34a48675184947c66f3df23adf55df060312e6d72d57908e3f049620d8a languageName: node linkType: hard -"@webassemblyjs/wast-printer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wast-printer@npm:1.11.1" +"@webassemblyjs/wast-printer@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/wast-printer@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/ast": 1.11.6 "@xtuc/long": 4.2.2 - checksum: f15ae4c2441b979a3b4fce78f3d83472fb22350c6dc3fd34bfe7c3da108e0b2360718734d961bba20e7716cb8578e964b870da55b035e209e50ec9db0378a3f7 + checksum: d2fa6a4c427325ec81463e9c809aa6572af6d47f619f3091bf4c4a6fc34f1da3df7caddaac50b8e7a457f8784c62cd58c6311b6cb69b0162ccd8d4c072f79cf8 languageName: node linkType: hard @@ -5513,13 +5513,13 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.10.0": - version: 5.10.0 - resolution: "enhanced-resolve@npm:5.10.0" +"enhanced-resolve@npm:^5.14.0": + version: 5.14.0 + resolution: "enhanced-resolve@npm:5.14.0" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: 0bb9830704db271610f900e8d79d70a740ea16f251263362b0c91af545576d09fe50103496606c1300a05e588372d6f9780a9bc2e30ce8ef9b827ec8f44687ff + checksum: fff1aaebbf376371e5df4502e111967f6247c37611ad3550e4e7fca657f6dcb29ef7ffe88bf14e5010b78997f1ddd984a8db97af87ee0a5477771398fd326f5b languageName: node linkType: hard @@ -10282,14 +10282,14 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": - version: 3.1.1 - resolution: "schema-utils@npm:3.1.1" +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1, schema-utils@npm:^3.1.2": + version: 3.1.2 + resolution: "schema-utils@npm:3.1.2" dependencies: "@types/json-schema": ^7.0.8 ajv: ^6.12.5 ajv-keywords: ^3.5.2 - checksum: fb73f3d759d43ba033c877628fe9751620a26879f6301d3dbeeb48cf2a65baec5cdf99da65d1bf3b4ff5444b2e59cbe4f81c2456b5e0d2ba7d7fd4aed5da29ce + checksum: 39683edfe3beff018cdb1ae4fa296fc55cea13a080aa2b4d9351895cd64b22ba4d87e2e548c2a2ac1bc76e60980670adb0f413a58104479f1a0c12e5663cb8ca languageName: node linkType: hard @@ -11863,19 +11863,19 @@ __metadata: linkType: hard "webpack@npm:^5.73.0": - version: 5.79.0 - resolution: "webpack@npm:5.79.0" + version: 5.83.1 + resolution: "webpack@npm:5.83.1" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^1.0.0 - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/wasm-edit": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 + "@webassemblyjs/ast": ^1.11.5 + "@webassemblyjs/wasm-edit": ^1.11.5 + "@webassemblyjs/wasm-parser": ^1.11.5 acorn: ^8.7.1 acorn-import-assertions: ^1.7.6 browserslist: ^4.14.5 chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.10.0 + enhanced-resolve: ^5.14.0 es-module-lexer: ^1.2.1 eslint-scope: 5.1.1 events: ^3.2.0 @@ -11885,7 +11885,7 @@ __metadata: loader-runner: ^4.2.0 mime-types: ^2.1.27 neo-async: ^2.6.2 - schema-utils: ^3.1.0 + schema-utils: ^3.1.2 tapable: ^2.1.1 terser-webpack-plugin: ^5.3.7 watchpack: ^2.4.0 @@ -11895,7 +11895,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 3fbd82dadc75c8f823c900c5d50263830b77e6be6a8abb26eec12f93dec94c2f07fa44c1ef1f28319682404e532d9707ed04ed6cb89af87ca7d544e435d8ef95 + checksum: 219d5ef50380bc0fd3702ed17feddf13819d8173b78f7a5b857dc74ac177e63d1f79c050792754411cc088bbc02e0971b989efddadbb8e393cf27d64c0ad9ff8 languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index fce0be422f..99ffbb6eb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15700,13 +15700,6 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:^0.0.51": - version: 0.0.51 - resolution: "@types/estree@npm:0.0.51" - checksum: e56a3bcf759fd9185e992e7fdb3c6a5f81e8ff120e871641607581fb3728d16c811702a7d40fa5f869b7f7b4437ab6a87eb8d98ffafeee51e85bbe955932a189 - languageName: node - linkType: hard - "@types/event-source-polyfill@npm:^1.0.0": version: 1.0.1 resolution: "@types/event-source-polyfill@npm:1.0.1" @@ -17035,13 +17028,13 @@ __metadata: linkType: hard "@types/webpack@npm:^5.28.0": - version: 5.28.0 - resolution: "@types/webpack@npm:5.28.0" + version: 5.28.1 + resolution: "@types/webpack@npm:5.28.1" dependencies: "@types/node": "*" tapable: ^2.2.0 webpack: ^5 - checksum: a038d7e12dd109c6a8d2eb744fd32070ef94f1655e730fb1443b370db98864c3a0e408638b02d12ba08269b9c012b3be8b801117ced2d1102e7676203fd663ed + checksum: 9f9bfe041382acfab36015f95011f6539705ba116fd9b3b8d7599a6396d996b7efcdb71947dba6d24a1a1bf397d15fdfb3c50d2f73e0aa5366a76198d5ec98b3 languageName: node linkType: hard @@ -17415,154 +17408,154 @@ __metadata: languageName: node linkType: hard -"@webassemblyjs/ast@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ast@npm:1.11.1" +"@webassemblyjs/ast@npm:1.11.6, @webassemblyjs/ast@npm:^1.11.5": + version: 1.11.6 + resolution: "@webassemblyjs/ast@npm:1.11.6" dependencies: - "@webassemblyjs/helper-numbers": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - checksum: 1eee1534adebeece635362f8e834ae03e389281972611408d64be7895fc49f48f98fddbbb5339bf8a72cb101bcb066e8bca3ca1bf1ef47dadf89def0395a8d87 + "@webassemblyjs/helper-numbers": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + checksum: 38ef1b526ca47c210f30975b06df2faf1a8170b1636ce239fc5738fc231ce28389dd61ecedd1bacfc03cbe95b16d1af848c805652080cb60982836eb4ed2c6cf languageName: node linkType: hard -"@webassemblyjs/floating-point-hex-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.1" - checksum: b8efc6fa08e4787b7f8e682182d84dfdf8da9d9c77cae5d293818bc4a55c1f419a87fa265ab85252b3e6c1fd323d799efea68d825d341a7c365c64bc14750e97 +"@webassemblyjs/floating-point-hex-parser@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.11.6" + checksum: 29b08758841fd8b299c7152eda36b9eb4921e9c584eb4594437b5cd90ed6b920523606eae7316175f89c20628da14326801090167cc7fbffc77af448ac84b7e2 languageName: node linkType: hard -"@webassemblyjs/helper-api-error@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-api-error@npm:1.11.1" - checksum: 0792813f0ed4a0e5ee0750e8b5d0c631f08e927f4bdfdd9fe9105dc410c786850b8c61bff7f9f515fdfb149903bec3c976a1310573a4c6866a94d49bc7271959 +"@webassemblyjs/helper-api-error@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-api-error@npm:1.11.6" + checksum: e8563df85161096343008f9161adb138a6e8f3c2cc338d6a36011aa55eabb32f2fd138ffe63bc278d009ada001cc41d263dadd1c0be01be6c2ed99076103689f languageName: node linkType: hard -"@webassemblyjs/helper-buffer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-buffer@npm:1.11.1" - checksum: a337ee44b45590c3a30db5a8b7b68a717526cf967ada9f10253995294dbd70a58b2da2165222e0b9830cd4fc6e4c833bf441a721128d1fe2e9a7ab26b36003ce +"@webassemblyjs/helper-buffer@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-buffer@npm:1.11.6" + checksum: b14d0573bf680d22b2522e8a341ec451fddd645d1f9c6bd9012ccb7e587a2973b86ab7b89fe91e1c79939ba96095f503af04369a3b356c8023c13a5893221644 languageName: node linkType: hard -"@webassemblyjs/helper-numbers@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-numbers@npm:1.11.1" +"@webassemblyjs/helper-numbers@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-numbers@npm:1.11.6" dependencies: - "@webassemblyjs/floating-point-hex-parser": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 + "@webassemblyjs/floating-point-hex-parser": 1.11.6 + "@webassemblyjs/helper-api-error": 1.11.6 "@xtuc/long": 4.2.2 - checksum: 44d2905dac2f14d1e9b5765cf1063a0fa3d57295c6d8930f6c59a36462afecc6e763e8a110b97b342a0f13376166c5d41aa928e6ced92e2f06b071fd0db59d3a + checksum: f4b562fa219f84368528339e0f8d273ad44e047a07641ffcaaec6f93e5b76fd86490a009aa91a294584e1436d74b0a01fa9fde45e333a4c657b58168b04da424 languageName: node linkType: hard -"@webassemblyjs/helper-wasm-bytecode@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.1" - checksum: eac400113127832c88f5826bcc3ad1c0db9b3dbd4c51a723cfdb16af6bfcbceb608170fdaac0ab7731a7e18b291be7af68a47fcdb41cfe0260c10857e7413d97 +"@webassemblyjs/helper-wasm-bytecode@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.11.6" + checksum: 3535ef4f1fba38de3475e383b3980f4bbf3de72bbb631c2b6584c7df45be4eccd62c6ff48b5edd3f1bcff275cfd605a37679ec199fc91fd0a7705d7f1e3972dc languageName: node linkType: hard -"@webassemblyjs/helper-wasm-section@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.1" +"@webassemblyjs/helper-wasm-section@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - checksum: 617696cfe8ecaf0532763162aaf748eb69096fb27950219bb87686c6b2e66e11cd0614d95d319d0ab1904bc14ebe4e29068b12c3e7c5e020281379741fe4bedf + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-buffer": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/wasm-gen": 1.11.6 + checksum: b2cf751bf4552b5b9999d27bbb7692d0aca75260140195cb58ea6374d7b9c2dc69b61e10b211a0e773f66209c3ddd612137ed66097e3684d7816f854997682e9 languageName: node linkType: hard -"@webassemblyjs/ieee754@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/ieee754@npm:1.11.1" +"@webassemblyjs/ieee754@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/ieee754@npm:1.11.6" dependencies: "@xtuc/ieee754": ^1.2.0 - checksum: 23a0ac02a50f244471631802798a816524df17e56b1ef929f0c73e3cde70eaf105a24130105c60aff9d64a24ce3b640dad443d6f86e5967f922943a7115022ec + checksum: 13574b8e41f6ca39b700e292d7edf102577db5650fe8add7066a320aa4b7a7c09a5056feccac7a74eb68c10dea9546d4461412af351f13f6b24b5f32379b49de languageName: node linkType: hard -"@webassemblyjs/leb128@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/leb128@npm:1.11.1" +"@webassemblyjs/leb128@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/leb128@npm:1.11.6" dependencies: "@xtuc/long": 4.2.2 - checksum: 33ccc4ade2f24de07bf31690844d0b1ad224304ee2062b0e464a610b0209c79e0b3009ac190efe0e6bd568b0d1578d7c3047fc1f9d0197c92fc061f56224ff4a + checksum: 7ea942dc9777d4b18a5ebfa3a937b30ae9e1d2ce1fee637583ed7f376334dd1d4274f813d2e250056cca803e0952def4b954913f1a3c9068bcd4ab4ee5143bf0 languageName: node linkType: hard -"@webassemblyjs/utf8@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/utf8@npm:1.11.1" - checksum: 972c5cfc769d7af79313a6bfb96517253a270a4bf0c33ba486aa43cac43917184fb35e51dfc9e6b5601548cd5931479a42e42c89a13bb591ffabebf30c8a6a0b +"@webassemblyjs/utf8@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/utf8@npm:1.11.6" + checksum: 807fe5b5ce10c390cfdd93e0fb92abda8aebabb5199980681e7c3743ee3306a75729bcd1e56a3903980e96c885ee53ef901fcbaac8efdfa480f9c0dae1d08713 languageName: node linkType: hard -"@webassemblyjs/wasm-edit@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-edit@npm:1.11.1" +"@webassemblyjs/wasm-edit@npm:^1.11.5": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-edit@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/helper-wasm-section": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-opt": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - "@webassemblyjs/wast-printer": 1.11.1 - checksum: 6d7d9efaec1227e7ef7585a5d7ff0be5f329f7c1c6b6c0e906b18ed2e9a28792a5635e450aca2d136770d0207225f204eff70a4b8fd879d3ac79e1dcc26dbeb9 + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-buffer": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/helper-wasm-section": 1.11.6 + "@webassemblyjs/wasm-gen": 1.11.6 + "@webassemblyjs/wasm-opt": 1.11.6 + "@webassemblyjs/wasm-parser": 1.11.6 + "@webassemblyjs/wast-printer": 1.11.6 + checksum: 29ce75870496d6fad864d815ebb072395a8a3a04dc9c3f4e1ffdc63fc5fa58b1f34304a1117296d8240054cfdbc38aca88e71fb51483cf29ffab0a61ef27b481 languageName: node linkType: hard -"@webassemblyjs/wasm-gen@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-gen@npm:1.11.1" +"@webassemblyjs/wasm-gen@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-gen@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1f6921e640293bf99fb16b21e09acb59b340a79f986c8f979853a0ae9f0b58557534b81e02ea2b4ef11e929d946708533fd0693c7f3712924128fdafd6465f5b + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/ieee754": 1.11.6 + "@webassemblyjs/leb128": 1.11.6 + "@webassemblyjs/utf8": 1.11.6 + checksum: a645a2eecbea24833c3260a249704a7f554ef4a94c6000984728e94bb2bc9140a68dfd6fd21d5e0bbb09f6dfc98e083a45760a83ae0417b41a0196ff6d45a23a languageName: node linkType: hard -"@webassemblyjs/wasm-opt@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-opt@npm:1.11.1" +"@webassemblyjs/wasm-opt@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-opt@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-buffer": 1.11.1 - "@webassemblyjs/wasm-gen": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - checksum: 21586883a20009e2b20feb67bdc451bbc6942252e038aae4c3a08e6f67b6bae0f5f88f20bfc7bd0452db5000bacaf5ab42b98cf9aa034a6c70e9fc616142e1db + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-buffer": 1.11.6 + "@webassemblyjs/wasm-gen": 1.11.6 + "@webassemblyjs/wasm-parser": 1.11.6 + checksum: b4557f195487f8e97336ddf79f7bef40d788239169aac707f6eaa2fa5fe243557c2d74e550a8e57f2788e70c7ae4e7d32f7be16101afe183d597b747a3bdd528 languageName: node linkType: hard -"@webassemblyjs/wasm-parser@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wasm-parser@npm:1.11.1" +"@webassemblyjs/wasm-parser@npm:1.11.6, @webassemblyjs/wasm-parser@npm:^1.11.5": + version: 1.11.6 + resolution: "@webassemblyjs/wasm-parser@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/helper-api-error": 1.11.1 - "@webassemblyjs/helper-wasm-bytecode": 1.11.1 - "@webassemblyjs/ieee754": 1.11.1 - "@webassemblyjs/leb128": 1.11.1 - "@webassemblyjs/utf8": 1.11.1 - checksum: 1521644065c360e7b27fad9f4bb2df1802d134dd62937fa1f601a1975cde56bc31a57b6e26408b9ee0228626ff3ba1131ae6f74ffb7d718415b6528c5a6dbfc2 + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/helper-api-error": 1.11.6 + "@webassemblyjs/helper-wasm-bytecode": 1.11.6 + "@webassemblyjs/ieee754": 1.11.6 + "@webassemblyjs/leb128": 1.11.6 + "@webassemblyjs/utf8": 1.11.6 + checksum: 8200a8d77c15621724a23fdabe58d5571415cda98a7058f542e670ea965dd75499f5e34a48675184947c66f3df23adf55df060312e6d72d57908e3f049620d8a languageName: node linkType: hard -"@webassemblyjs/wast-printer@npm:1.11.1": - version: 1.11.1 - resolution: "@webassemblyjs/wast-printer@npm:1.11.1" +"@webassemblyjs/wast-printer@npm:1.11.6": + version: 1.11.6 + resolution: "@webassemblyjs/wast-printer@npm:1.11.6" dependencies: - "@webassemblyjs/ast": 1.11.1 + "@webassemblyjs/ast": 1.11.6 "@xtuc/long": 4.2.2 - checksum: f15ae4c2441b979a3b4fce78f3d83472fb22350c6dc3fd34bfe7c3da108e0b2360718734d961bba20e7716cb8578e964b870da55b035e209e50ec9db0378a3f7 + checksum: d2fa6a4c427325ec81463e9c809aa6572af6d47f619f3091bf4c4a6fc34f1da3df7caddaac50b8e7a457f8784c62cd58c6311b6cb69b0162ccd8d4c072f79cf8 languageName: node linkType: hard @@ -22538,13 +22531,13 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.10.0": - version: 5.10.0 - resolution: "enhanced-resolve@npm:5.10.0" +"enhanced-resolve@npm:^5.14.0": + version: 5.14.0 + resolution: "enhanced-resolve@npm:5.14.0" dependencies: graceful-fs: ^4.2.4 tapable: ^2.2.0 - checksum: 0bb9830704db271610f900e8d79d70a740ea16f251263362b0c91af545576d09fe50103496606c1300a05e588372d6f9780a9bc2e30ce8ef9b827ec8f44687ff + checksum: fff1aaebbf376371e5df4502e111967f6247c37611ad3550e4e7fca657f6dcb29ef7ffe88bf14e5010b78997f1ddd984a8db97af87ee0a5477771398fd326f5b languageName: node linkType: hard @@ -22686,13 +22679,20 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^0.9.0, es-module-lexer@npm:^0.9.3": +"es-module-lexer@npm:^0.9.3": version: 0.9.3 resolution: "es-module-lexer@npm:0.9.3" checksum: 84bbab23c396281db2c906c766af58b1ae2a1a2599844a504df10b9e8dc77ec800b3211fdaa133ff700f5703d791198807bba25d9667392d27a5e9feda344da8 languageName: node linkType: hard +"es-module-lexer@npm:^1.2.1": + version: 1.2.1 + resolution: "es-module-lexer@npm:1.2.1" + checksum: c4145b853e1491eaa5d591e4580926d242978c38071ad3d09165c3b6d50314cc0ae3bf6e1dec81a9e53768b9299df2063d2e4a67d7742a5029ddeae6c4fc26f0 + languageName: node + linkType: hard + "es-shim-unscopables@npm:^1.0.0": version: 1.0.0 resolution: "es-shim-unscopables@npm:1.0.0" @@ -36593,14 +36593,14 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.0, schema-utils@npm:^3.1.1": - version: 3.1.1 - resolution: "schema-utils@npm:3.1.1" +"schema-utils@npm:^3.0.0, schema-utils@npm:^3.1.1, schema-utils@npm:^3.1.2": + version: 3.1.2 + resolution: "schema-utils@npm:3.1.2" dependencies: "@types/json-schema": ^7.0.8 ajv: ^6.12.5 ajv-keywords: ^3.5.2 - checksum: fb73f3d759d43ba033c877628fe9751620a26879f6301d3dbeeb48cf2a65baec5cdf99da65d1bf3b4ff5444b2e59cbe4f81c2456b5e0d2ba7d7fd4aed5da29ce + checksum: 39683edfe3beff018cdb1ae4fa296fc55cea13a080aa2b4d9351895cd64b22ba4d87e2e548c2a2ac1bc76e60980670adb0f413a58104479f1a0c12e5663cb8ca languageName: node linkType: hard @@ -38487,7 +38487,7 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:*, terser-webpack-plugin@npm:^5.1.3": +"terser-webpack-plugin@npm:*, terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.7": version: 5.3.9 resolution: "terser-webpack-plugin@npm:5.3.9" dependencies: @@ -40399,20 +40399,20 @@ __metadata: linkType: hard "webpack@npm:^5, webpack@npm:^5.70.0": - version: 5.76.3 - resolution: "webpack@npm:5.76.3" + version: 5.83.1 + resolution: "webpack@npm:5.83.1" dependencies: "@types/eslint-scope": ^3.7.3 - "@types/estree": ^0.0.51 - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/wasm-edit": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 + "@types/estree": ^1.0.0 + "@webassemblyjs/ast": ^1.11.5 + "@webassemblyjs/wasm-edit": ^1.11.5 + "@webassemblyjs/wasm-parser": ^1.11.5 acorn: ^8.7.1 acorn-import-assertions: ^1.7.6 browserslist: ^4.14.5 chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.10.0 - es-module-lexer: ^0.9.0 + enhanced-resolve: ^5.14.0 + es-module-lexer: ^1.2.1 eslint-scope: 5.1.1 events: ^3.2.0 glob-to-regexp: ^0.4.1 @@ -40421,9 +40421,9 @@ __metadata: loader-runner: ^4.2.0 mime-types: ^2.1.27 neo-async: ^2.6.2 - schema-utils: ^3.1.0 + schema-utils: ^3.1.2 tapable: ^2.1.1 - terser-webpack-plugin: ^5.1.3 + terser-webpack-plugin: ^5.3.7 watchpack: ^2.4.0 webpack-sources: ^3.2.3 peerDependenciesMeta: @@ -40431,7 +40431,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 363f536b56971d056e34ab4cffa4cbc630b220e51be1a8c3adea87d9f0b51c49cfc7c3720d6614a1fd2c8c63f1ab3100db916fe8367c8bb9299327ff8c3f856d + checksum: 219d5ef50380bc0fd3702ed17feddf13819d8173b78f7a5b857dc74ac177e63d1f79c050792754411cc088bbc02e0971b989efddadbb8e393cf27d64c0ad9ff8 languageName: node linkType: hard From 25c44edf3c859b14e6b403a128ebd325e8f73e27 Mon Sep 17 00:00:00 2001 From: Andrew Ochsner Date: Mon, 22 May 2023 16:13:26 -0500 Subject: [PATCH 061/255] fix tsc? Signed-off-by: Andrew Ochsner --- plugins/scaffolder-backend/package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 1e655a01c1..11d5208f37 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -87,7 +87,7 @@ "morgan": "^1.10.0", "node-fetch": "^2.6.7", "nunjucks": "^3.2.3", - "octokit": "^2.0.3", + "octokit": "^2.0.5", "octokit-plugin-create-pull-request": "^3.10.0", "p-limit": "^3.1.0", "p-queue": "^6.6.2", diff --git a/yarn.lock b/yarn.lock index 23314279d1..420185ec81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8263,7 +8263,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.6.7 nunjucks: ^3.2.3 - octokit: ^2.0.3 + octokit: ^2.0.5 octokit-plugin-create-pull-request: ^3.10.0 p-limit: ^3.1.0 p-queue: ^6.6.2 @@ -32189,9 +32189,9 @@ __metadata: languageName: node linkType: hard -"octokit@npm:^2.0.3, octokit@npm:^2.0.4": - version: 2.0.16 - resolution: "octokit@npm:2.0.16" +"octokit@npm:^2.0.4, octokit@npm:^2.0.5": + version: 2.0.18 + resolution: "octokit@npm:2.0.18" dependencies: "@octokit/app": ^13.1.3 "@octokit/core": ^4.2.1 @@ -32201,7 +32201,7 @@ __metadata: "@octokit/plugin-retry": ^4.1.3 "@octokit/plugin-throttling": ^5.2.2 "@octokit/types": ^9.2.2 - checksum: 975e636bc99c4965c65ad4bb17e6f6bdc7b3ab911f8b0273f149ad3f2c80982f0a382015255869d9a2ca85ab6d4d2b1e491d29084fff9c0b75e89ec77dfafdd8 + checksum: 7693c94ff41cb78e9ec9bb84476c0282219fa3ca9afd8a09d1a06b9361144c0aec4e01663bf860ceac152a0b1fadfcf590e7c0f06ce52230376dbc66d3cead37 languageName: node linkType: hard From 364135375264102c271a6eaa720ccecb96011eee Mon Sep 17 00:00:00 2001 From: Andrew Ochsner Date: Mon, 22 May 2023 16:53:00 -0500 Subject: [PATCH 062/255] fix tsc errors Signed-off-by: Andrew Ochsner --- .changeset/wet-vans-drive.md | 4 +++- .../src/github/SingleInstanceGithubCredentialsProvider.ts | 6 ++++-- .../src/processors/GithubMultiOrgReaderProcessor.ts | 1 + .../src/providers/GithubMultiOrgEntityProvider.ts | 1 + plugins/scaffolder-backend/package.json | 2 +- yarn.lock | 4 ++-- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.changeset/wet-vans-drive.md b/.changeset/wet-vans-drive.md index 903e32209e..a61c764122 100644 --- a/.changeset/wet-vans-drive.md +++ b/.changeset/wet-vans-drive.md @@ -1,5 +1,7 @@ --- '@backstage/plugin-scaffolder-backend': minor +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/integration': patch --- -Add support for Repository Variables and Secrets to the `publish:github` and `github:repo:create` scaffolder actions. +Add support for Repository Variables and Secrets to the `publish:github` and `github:repo:create` scaffolder actions. Upgrade octokit introduces some breaking changes. diff --git a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts index 402a62d97c..dc3a86b72b 100644 --- a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts +++ b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts @@ -174,8 +174,10 @@ class GithubAppManager { const allInstallations = await this.getInstallations(); const installation = allInstallations.find( inst => - inst.account?.login?.toLocaleLowerCase('en-US') === - owner.toLocaleLowerCase('en-US'), + inst.account && + 'login' in inst.account && + inst.account.login?.toLocaleLowerCase('en-US') === + owner.toLocaleLowerCase('en-US'), ); if (installation) { return { diff --git a/plugins/catalog-backend-module-github/src/processors/GithubMultiOrgReaderProcessor.ts b/plugins/catalog-backend-module-github/src/processors/GithubMultiOrgReaderProcessor.ts index 9709977010..cf5e484fc2 100644 --- a/plugins/catalog-backend-module-github/src/processors/GithubMultiOrgReaderProcessor.ts +++ b/plugins/catalog-backend-module-github/src/processors/GithubMultiOrgReaderProcessor.ts @@ -233,6 +233,7 @@ export class GithubMultiOrgReaderProcessor implements CatalogProcessor { .map(install => install.target_type === 'Organization' && install.account && + 'login' in install.account && install.account.login ? { name: install.account.login, diff --git a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts index 51796c2116..9ac9f2c45d 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubMultiOrgEntityProvider.ts @@ -838,6 +838,7 @@ export class GithubMultiOrgEntityProvider implements EntityProvider { .map(install => install.target_type === 'Organization' && install.account && + 'login' in install.account && install.account.login ? install.account.login : undefined, diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 11d5208f37..103ae6d1fd 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -87,7 +87,7 @@ "morgan": "^1.10.0", "node-fetch": "^2.6.7", "nunjucks": "^3.2.3", - "octokit": "^2.0.5", + "octokit": "^2.0.0", "octokit-plugin-create-pull-request": "^3.10.0", "p-limit": "^3.1.0", "p-queue": "^6.6.2", diff --git a/yarn.lock b/yarn.lock index 420185ec81..4f9c03518b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8263,7 +8263,7 @@ __metadata: msw: ^1.0.0 node-fetch: ^2.6.7 nunjucks: ^3.2.3 - octokit: ^2.0.5 + octokit: ^2.0.0 octokit-plugin-create-pull-request: ^3.10.0 p-limit: ^3.1.0 p-queue: ^6.6.2 @@ -32189,7 +32189,7 @@ __metadata: languageName: node linkType: hard -"octokit@npm:^2.0.4, octokit@npm:^2.0.5": +"octokit@npm:^2.0.0, octokit@npm:^2.0.4": version: 2.0.18 resolution: "octokit@npm:2.0.18" dependencies: From 4b2a9b55092fb071df4c5a918dc0d6125eb65e42 Mon Sep 17 00:00:00 2001 From: rdeepc <12953177+rdeepc@users.noreply.github.com> Date: Mon, 22 May 2023 21:45:52 -0400 Subject: [PATCH 063/255] Updated fields search for postgres to support array item Signed-off-by: rdeepc <12953177+rdeepc@users.noreply.github.com> --- .../src/database/DatabaseDocumentStore.test.ts | 16 ++++++++++++++++ .../src/database/DatabaseDocumentStore.ts | 6 +++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts index 2f99a02521..0804f643ab 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts @@ -453,6 +453,12 @@ describe('DatabaseDocumentStore', () => { text: 'Around the world', location: 'LOCATION-1', }, + { + title: 'Sed ut perspiciatis', + text: 'Hello World', + myField: ['that', 'not'], + location: 'LOCATION-1', + } as unknown as IndexableDocument, ]); await store.completeInsert(tx, 'my-type'); }); @@ -488,6 +494,16 @@ describe('DatabaseDocumentStore', () => { rank: expect.any(Number), type: 'my-type', }, + { + document: { + location: 'LOCATION-1', + text: 'Hello World', + title: 'Sed ut perspiciatis', + myField: ['that', 'not'], + }, + rank: expect.any(Number), + type: 'my-type', + }, ]); }, ); diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts index 023992a335..719cc1c755 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts @@ -171,9 +171,13 @@ export class DatabaseDocumentStore implements DatabaseStore { Object.keys(fields).forEach(key => { const value = fields[key]; const valueArray = Array.isArray(value) ? value : [value]; - const valueCompare = valueArray + const fieldValueCompare = valueArray .map(v => ({ [key]: v })) .map(v => JSON.stringify(v)); + const arrayValueCompare = valueArray + .map(v => ({ [key]: [v] })) + .map(v => JSON.stringify(v)); + const valueCompare = [...fieldValueCompare, ...arrayValueCompare]; query.whereRaw( `(${valueCompare.map(() => 'document @> ?').join(' OR ')})`, valueCompare, From 3c09e8d3cb0cbe3dbf1c64e1b2265f829f54968d Mon Sep 17 00:00:00 2001 From: rdeepc <12953177+rdeepc@users.noreply.github.com> Date: Mon, 22 May 2023 21:55:40 -0400 Subject: [PATCH 064/255] Updated fields search for postgres to support array item Signed-off-by: rdeepc <12953177+rdeepc@users.noreply.github.com> --- .changeset/shaggy-terms-travel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shaggy-terms-travel.md diff --git a/.changeset/shaggy-terms-travel.md b/.changeset/shaggy-terms-travel.md new file mode 100644 index 0000000000..a131608487 --- /dev/null +++ b/.changeset/shaggy-terms-travel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-pg': patch +--- + +Updated Postgres search query filter in DatabaseDocumentStore to support field value search in array. From 8359c7647d6ed10c6336d08b088bc01b570890ea Mon Sep 17 00:00:00 2001 From: Brian Fletcher Date: Tue, 23 May 2023 08:15:20 +0100 Subject: [PATCH 065/255] export deprecated properly Signed-off-by: Brian Fletcher --- plugins/pagerduty/api-report.md | 9 +++++---- plugins/pagerduty/src/deprecated.ts | 29 +++++++++++++++++++++++++++++ plugins/pagerduty/src/index.ts | 11 +---------- 3 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 plugins/pagerduty/src/deprecated.ts diff --git a/plugins/pagerduty/api-report.md b/plugins/pagerduty/api-report.md index 4e0d4784f3..dd24aa9bec 100644 --- a/plugins/pagerduty/api-report.md +++ b/plugins/pagerduty/api-report.md @@ -20,11 +20,9 @@ export const EntityPagerDutyCard: ( ) => JSX.Element; // @public (undocumented) -type EntityPagerDutyCardProps = { +export type EntityPagerDutyCardProps = { readOnly?: boolean; }; -export { EntityPagerDutyCardProps }; -export { EntityPagerDutyCardProps as PagerDutyCardProps }; // @public (undocumented) export const HomePagePagerDutyCard: ( @@ -67,9 +65,12 @@ export type PagerDutyAssignee = { html_url: string; }; -// @public (undocumented) +// @public @deprecated (undocumented) export const PagerDutyCard: (props: EntityPagerDutyCardProps) => JSX.Element; +// @public @deprecated (undocumented) +export type PagerDutyCardProps = EntityPagerDutyCardProps; + // @public (undocumented) export type PagerDutyChangeEvent = { id: string; diff --git a/plugins/pagerduty/src/deprecated.ts b/plugins/pagerduty/src/deprecated.ts new file mode 100644 index 0000000000..e9f60f6894 --- /dev/null +++ b/plugins/pagerduty/src/deprecated.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityPagerDutyCardProps, EntityPagerDutyCard } from './components'; + +/** + * @public + * @deprecated Please use EntityPagerDutyCard + */ +export const PagerDutyCard = EntityPagerDutyCard; + +/** + * @public + * @deprecated Please use EntityPagerDutyCardProps + */ +export type PagerDutyCardProps = EntityPagerDutyCardProps; diff --git a/plugins/pagerduty/src/index.ts b/plugins/pagerduty/src/index.ts index d0320e3343..c6f7aef89a 100644 --- a/plugins/pagerduty/src/index.ts +++ b/plugins/pagerduty/src/index.ts @@ -29,16 +29,7 @@ export { export * from './components'; -/** - * @deprecated Please use EntityPagerDutyCard - */ -export { EntityPagerDutyCard as PagerDutyCard } from './components'; - -/** - * @deprecated Please use EntityPagerDutyCardProps - */ -export type { EntityPagerDutyCardProps as PagerDutyCardProps } from './components'; - export * from './api'; +export * from './deprecated'; export type { PagerDutyEntity } from './types'; From af75c6f684592e56d62bc24410083e0b4223d6fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 13:51:44 +0000 Subject: [PATCH 066/255] chore(deps): update ruby docker tag to v3.2 Signed-off-by: Renovate Bot --- plugins/scaffolder-backend-module-rails/Rails.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-rails/Rails.dockerfile b/plugins/scaffolder-backend-module-rails/Rails.dockerfile index 0774fdc975..921faa601d 100644 --- a/plugins/scaffolder-backend-module-rails/Rails.dockerfile +++ b/plugins/scaffolder-backend-module-rails/Rails.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.0 +FROM ruby:3.2 RUN apt-get update -qq && \ apt-get install -y nodejs postgresql-client git && \ From dbbffb4470eb8216dc16358d53dad7a0e67d99a1 Mon Sep 17 00:00:00 2001 From: Christopher Diaz Date: Tue, 23 May 2023 11:19:46 -0400 Subject: [PATCH 067/255] add dev dependency Signed-off-by: Christopher Diaz --- yarn.lock | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7fbe9f51ff..ebd5852444 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8487,6 +8487,7 @@ __metadata: "@backstage/plugin-explore-common": "workspace:^" "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-search-common": "workspace:^" + msw: ^1.2.1 node-fetch: ^2.6.7 winston: ^3.2.1 languageName: unknown @@ -25883,10 +25884,10 @@ __metadata: languageName: node linkType: hard -"headers-polyfill@npm:^3.1.0": - version: 3.1.0 - resolution: "headers-polyfill@npm:3.1.0" - checksum: a95257065684653b7185efbb9a380c547ea832002991b5adf0d90cd222073da2701be9dc2849d1970ecf15e8c35b383984358566afe6e76ca8ff1dbd7cdce3af +"headers-polyfill@npm:^3.1.0, headers-polyfill@npm:^3.1.2": + version: 3.1.2 + resolution: "headers-polyfill@npm:3.1.2" + checksum: 510ca9637ef652404dbd432e680418f8d418ba18094ef2f64c3d8de955ebf6e68d553c7f0aeaa5fc937d130b139c1e2d7c2066cd4cf0f740a4627924eaaee9db languageName: node linkType: hard @@ -27022,10 +27023,10 @@ __metadata: languageName: node linkType: hard -"is-node-process@npm:^1.0.1": - version: 1.0.1 - resolution: "is-node-process@npm:1.0.1" - checksum: 3ddb8a892a00f6eb9c2aea7e7e1426b8683512d9419933d95114f4f64b5455e26601c23a31c0682463890032136dd98a326988a770ab6b4eed54a43ade8bed50 +"is-node-process@npm:^1.0.1, is-node-process@npm:^1.2.0": + version: 1.2.0 + resolution: "is-node-process@npm:1.2.0" + checksum: 930765cdc6d81ab8f1bbecbea4a8d35c7c6d88a3ff61f3630e0fc7f22d624d7661c1df05c58547d0eb6a639dfa9304682c8e342c4113a6ed51472b704cee2928 languageName: node linkType: hard @@ -31297,9 +31298,9 @@ __metadata: languageName: node linkType: hard -"msw@npm:^1.0.0, msw@npm:^1.0.1": - version: 1.2.0 - resolution: "msw@npm:1.2.0" +"msw@npm:^1.0.0, msw@npm:^1.0.1, msw@npm:^1.2.1": + version: 1.2.1 + resolution: "msw@npm:1.2.1" dependencies: "@mswjs/cookies": ^0.2.2 "@mswjs/interceptors": ^0.17.5 @@ -31310,12 +31311,12 @@ __metadata: chokidar: ^3.4.2 cookie: ^0.4.2 graphql: ^15.0.0 || ^16.0.0 - headers-polyfill: ^3.1.0 + headers-polyfill: ^3.1.2 inquirer: ^8.2.0 - is-node-process: ^1.0.1 + is-node-process: ^1.2.0 js-levenshtein: ^1.1.6 node-fetch: ^2.6.7 - outvariant: ^1.3.0 + outvariant: ^1.4.0 path-to-regexp: ^6.2.0 strict-event-emitter: ^0.4.3 type-fest: ^2.19.0 @@ -31327,7 +31328,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 2cea7fe0f2ebb59b11534896cbaa91dca65f4fb2272402549079cc4190847fc42f5367f331c552d576d59a3521f498da0384d53cf4960beaebd6165bbc44593b + checksum: 97b9c5ffc81b4380dd24ff40ac52ca02f406c88f86d8209ba991da19aad0bcfc37e807794aa1b9bf29f89cd86fd31e492bf4418c5ff02559b4f80d6ce4fb417b languageName: node linkType: hard @@ -32439,10 +32440,10 @@ __metadata: languageName: node linkType: hard -"outvariant@npm:^1.2.1, outvariant@npm:^1.3.0": - version: 1.3.0 - resolution: "outvariant@npm:1.3.0" - checksum: ac76ca375c1c642989e1c74f0e9ebac84c05bc9fdc8f28be949c16fae1658e9f1f2fb1133fe3cc1e98afabef78fe4298fe9360b5734baf8e6ad440c182680848 +"outvariant@npm:^1.2.1, outvariant@npm:^1.3.0, outvariant@npm:^1.4.0": + version: 1.4.0 + resolution: "outvariant@npm:1.4.0" + checksum: ec32dfc315c464bb6e4906b2f450d259ce0b86caf70b70b249054359d9af21a7fccf53a8b6aa232f8d718449e31c1cfa594e6ebffaafe7bf908b502495256d7b languageName: node linkType: hard From 08f177b91084d42bb97aad679271f20dc80a8b46 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Fri, 19 May 2023 12:58:25 -0500 Subject: [PATCH 068/255] feat(catalog): Link launch template in Entity's AboutCard Signed-off-by: Carlos Esteban Lopez --- .changeset/chatty-seals-tap.md | 6 +++++ packages/app/src/App.tsx | 1 + plugins/catalog/package.json | 1 + .../src/components/AboutCard/AboutCard.tsx | 24 +++++++++++++++++-- plugins/catalog/src/plugin.ts | 7 +++++- plugins/catalog/src/routes.ts | 6 +++++ yarn.lock | 1 + 7 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 .changeset/chatty-seals-tap.md diff --git a/.changeset/chatty-seals-tap.md b/.changeset/chatty-seals-tap.md new file mode 100644 index 0000000000..384a89e6ce --- /dev/null +++ b/.changeset/chatty-seals-tap.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog': patch +'example-app': patch +--- + +Add link from Template entity to the scaffolder launch page for the template in the AboutCard. diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index bc9eac8c64..0efcafba26 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -142,6 +142,7 @@ const app = createApp({ bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, viewTechDoc: techdocsPlugin.routes.docRoot, + selectedTemplateRoute: scaffolderPlugin.routes.selectedTemplate, }); bind(apiDocsPlugin.externalRoutes, { registerApi: catalogImportPlugin.routes.importPage, diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 21983f762e..16176b9662 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -40,6 +40,7 @@ "@backstage/integration-react": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@backstage/plugin-search-react": "workspace:^", "@backstage/theme": "workspace:^", diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index cb48be2b26..4be201b555 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -49,12 +49,14 @@ import { IconButton, makeStyles, } from '@material-ui/core'; +import AddIcon from '@material-ui/icons/Add'; import CachedIcon from '@material-ui/icons/Cached'; import DocsIcon from '@material-ui/icons/Description'; import EditIcon from '@material-ui/icons/Edit'; import React, { useCallback } from 'react'; -import { viewTechDocRouteRef } from '../../routes'; +import { selectedTemplateRouteRef, viewTechDocRouteRef } from '../../routes'; import { AboutContent } from './AboutContent'; +import { isTemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; const useStyles = makeStyles({ gridItemCard: { @@ -97,6 +99,7 @@ export function AboutCard(props: AboutCardProps) { const alertApi = useApi(alertApiRef); const errorApi = useApi(errorApiRef); const viewTechdocLink = useRouteRef(viewTechDocRouteRef); + const templateRoute = useRouteRef(selectedTemplateRouteRef); const entitySourceLocation = getEntitySourceLocation( entity, @@ -126,6 +129,23 @@ export function AboutCard(props: AboutCardProps) { }), }; + const subHeaderLinks = [viewInSource, viewInTechDocs]; + + if (isTemplateEntityV1beta3(entity)) { + const launchTemplate: IconLinkVerticalProps = { + label: 'Launch Template', + icon: , + href: + templateRoute && + templateRoute({ + templateName: entity.metadata.name, + namespace: entity.metadata.namespace || DEFAULT_NAMESPACE, + }), + }; + + subHeaderLinks.push(launchTemplate); + } + let cardClass = ''; if (variant === 'gridItem') { cardClass = classes.gridItemCard; @@ -179,7 +199,7 @@ export function AboutCard(props: AboutCardProps) { } - subheader={} + subheader={} /> diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts index ea6ddcaff2..22c85a755d 100644 --- a/plugins/catalog/src/plugin.ts +++ b/plugins/catalog/src/plugin.ts @@ -21,7 +21,11 @@ import { entityRouteRef, starredEntitiesApiRef, } from '@backstage/plugin-catalog-react'; -import { createComponentRouteRef, viewTechDocRouteRef } from './routes'; +import { + createComponentRouteRef, + selectedTemplateRouteRef, + viewTechDocRouteRef, +} from './routes'; import { createApiFactory, createComponentExtension, @@ -77,6 +81,7 @@ export const catalogPlugin = createPlugin({ externalRoutes: { createComponent: createComponentRouteRef, viewTechDoc: viewTechDocRouteRef, + selectedTemplateRoute: selectedTemplateRouteRef, }, __experimentalConfigure( options?: CatalogInputPluginOptions, diff --git a/plugins/catalog/src/routes.ts b/plugins/catalog/src/routes.ts index 5c0d195d74..2d9667d8e2 100644 --- a/plugins/catalog/src/routes.ts +++ b/plugins/catalog/src/routes.ts @@ -30,6 +30,12 @@ export const viewTechDocRouteRef = createExternalRouteRef({ params: ['namespace', 'kind', 'name'], }); +export const selectedTemplateRouteRef = createExternalRouteRef({ + id: 'selected-template', + optional: true, + params: ['namespace', 'templateName'], +}); + export const rootRouteRef = createRouteRef({ id: 'catalog', }); diff --git a/yarn.lock b/yarn.lock index 2e6f814cf5..6ce19fdede 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5739,6 +5739,7 @@ __metadata: "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/plugin-permission-react": "workspace:^" + "@backstage/plugin-scaffolder-common": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@backstage/plugin-search-react": "workspace:^" "@backstage/test-utils": "workspace:^" From 76cd142840953026c2f355ac4b16c8c3a5f4e882 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Fri, 19 May 2023 15:02:27 -0500 Subject: [PATCH 069/255] tests(catalog): Fix tests & Icon from system icons Signed-off-by: Carlos Esteban Lopez --- .changeset/chatty-seals-tap.md | 1 - plugins/catalog/api-report.md | 7 + .../components/AboutCard/AboutCard.test.tsx | 163 +++++++++++++++++- .../src/components/AboutCard/AboutCard.tsx | 10 +- .../api-report.md | 6 +- 5 files changed, 179 insertions(+), 8 deletions(-) diff --git a/.changeset/chatty-seals-tap.md b/.changeset/chatty-seals-tap.md index 384a89e6ce..5511fe4520 100644 --- a/.changeset/chatty-seals-tap.md +++ b/.changeset/chatty-seals-tap.md @@ -1,6 +1,5 @@ --- '@backstage/plugin-catalog': patch -'example-app': patch --- Add link from Template entity to the scaffolder launch page for the template in the AboutCard. diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 970d8d3251..915e89d0c6 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -105,6 +105,13 @@ export const catalogPlugin: BackstagePlugin< }, true >; + selectedTemplateRoute: ExternalRouteRef< + { + namespace: string; + templateName: string; + }, + true + >; }, CatalogInputPluginOptions >; diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx index 869d6f1757..43af858300 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -30,7 +30,7 @@ import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import userEvent from '@testing-library/user-event'; import { screen } from '@testing-library/react'; import React from 'react'; -import { viewTechDocRouteRef } from '../../routes'; +import { selectedTemplateRouteRef, viewTechDocRouteRef } from '../../routes'; import { AboutCard } from './AboutCard'; describe('', () => { @@ -505,4 +505,165 @@ describe('', () => { expect(screen.getByText('View TechDocs')).toBeVisible(); expect(screen.getByText('View TechDocs').closest('a')).toBeNull(); }); + + it('renders launch template link', async () => { + const entity = { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + name: 'create-react-app-template', + namespace: 'default', + }, + }; + + await renderInTestApp( + + + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + '/create/templates/:namespace/:templateName': + selectedTemplateRouteRef, + }, + }, + ); + + expect(screen.getByText('Launch Template')).toBeVisible(); + expect(screen.getByText('Launch Template').closest('a')).toHaveAttribute( + 'href', + '/create/templates/default/create-react-app-template', + ); + }); + + it.each([ + { + testName: 'entity is not a template', + entity: { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Component', + metadata: { + name: 'create-react-app-template', + namespace: 'default', + }, + }, + }, + { + testName: 'apiVersion is not scaffolder.backstage.io/v1beta3', + entity: { + apiVersion: 'v1', + kind: 'Template', + metadata: { + name: 'create-react-app-template', + namespace: 'default', + }, + }, + }, + ])( + 'should not render launch template link when $testName', + async ({ entity }) => { + await renderInTestApp( + + + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + '/create/templates/:namespace/:templateName': + selectedTemplateRouteRef, + }, + }, + ); + + expect(screen.queryByText('Launch Template')).toBeNull(); + }, + ); + + it('renders disabled launch template link when route is not bound', async () => { + const entity = { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + name: 'create-react-app-template', + namespace: 'default', + }, + }; + + await renderInTestApp( + + + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ); + + expect(screen.getByText('Launch Template')).toBeVisible(); + expect(screen.getByText('Launch Template').closest('a')).toBeNull(); + }); }); diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index 4be201b555..2933aea9e8 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -30,6 +30,7 @@ import { alertApiRef, errorApiRef, useApi, + useApp, useRouteRef, } from '@backstage/core-plugin-api'; import { @@ -90,8 +91,8 @@ export interface AboutCardProps { /** * Exported publicly via the EntityAboutCard */ -export function AboutCard(props: AboutCardProps) { - const { variant } = props; +export function AboutCard({ variant }: AboutCardProps) { + const app = useApp(); const classes = useStyles(); const { entity } = useEntity(); const scmIntegrationsApi = useApi(scmIntegrationsApiRef); @@ -132,9 +133,12 @@ export function AboutCard(props: AboutCardProps) { const subHeaderLinks = [viewInSource, viewInTechDocs]; if (isTemplateEntityV1beta3(entity)) { + const Icon = app.getSystemIcon('scaffolder') ?? AddIcon; + const launchTemplate: IconLinkVerticalProps = { label: 'Launch Template', - icon: , + icon: , + disabled: !templateRoute, href: templateRoute && templateRoute({ diff --git a/plugins/scaffolder-backend-module-gitlab/api-report.md b/plugins/scaffolder-backend-module-gitlab/api-report.md index f7cf6024ec..eccada5d51 100644 --- a/plugins/scaffolder-backend-module-gitlab/api-report.md +++ b/plugins/scaffolder-backend-module-gitlab/api-report.md @@ -26,8 +26,8 @@ export const createGitlabProjectAccessTokenAction: (options: { integrations: ScmIntegrationRegistry; }) => TemplateAction< { - projectId: string | number; repoUrl: string; + projectId: string | number; token?: string | undefined; name?: string | undefined; accessLevel?: number | undefined; @@ -44,8 +44,8 @@ export const createGitlabProjectDeployTokenAction: (options: { }) => TemplateAction< { name: string; - projectId: string | number; repoUrl: string; + projectId: string | number; token?: string | undefined; username?: string | undefined; scopes?: string[] | undefined; @@ -63,8 +63,8 @@ export const createGitlabProjectVariableAction: (options: { { key: string; value: string; - projectId: string | number; repoUrl: string; + projectId: string | number; variableType: string; token?: string | undefined; variableProtected?: boolean | undefined; From 62f4c6d195f5888db1c8426362fbb069758005b9 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Fri, 19 May 2023 16:20:25 -0500 Subject: [PATCH 070/255] feat(catalog): Sync external link with create-app default-app App.tsx Signed-off-by: Carlos Esteban Lopez --- .../create-app/templates/default-app/packages/app/src/App.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 056402f2a8..cac3e7f0a9 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -40,12 +40,14 @@ const app = createApp({ bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, viewTechDoc: techdocsPlugin.routes.docRoot, + selectedTemplateRoute: scaffolderPlugin.routes.selectedTemplate, }); bind(apiDocsPlugin.externalRoutes, { registerApi: catalogImportPlugin.routes.importPage, }); bind(scaffolderPlugin.externalRoutes, { registerComponent: catalogImportPlugin.routes.importPage, + viewTechDoc: techdocsPlugin.routes.docRoot, }); bind(orgPlugin.externalRoutes, { catalogIndex: catalogPlugin.routes.catalogIndex, From 62d2a70660c31fce96bcb993ee860fd562c262b7 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Tue, 23 May 2023 11:10:11 -0500 Subject: [PATCH 071/255] fix(catalog): Address PR feedback Signed-off-by: Carlos Esteban Lopez --- .changeset/chatty-seals-tap.md | 1 + packages/app/src/App.tsx | 2 +- .../default-app/packages/app/src/App.tsx | 2 +- plugins/catalog/api-report.md | 2 +- .../src/components/AboutCard/AboutCard.test.tsx | 6 +++--- .../src/components/AboutCard/AboutCard.tsx | 15 ++++++++------- plugins/catalog/src/plugin.ts | 4 ++-- plugins/catalog/src/routes.ts | 4 ++-- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.changeset/chatty-seals-tap.md b/.changeset/chatty-seals-tap.md index 5511fe4520..01d69e1562 100644 --- a/.changeset/chatty-seals-tap.md +++ b/.changeset/chatty-seals-tap.md @@ -1,4 +1,5 @@ --- +'@backstage/create-app': patch '@backstage/plugin-catalog': patch --- diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 0efcafba26..d46774a757 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -142,7 +142,7 @@ const app = createApp({ bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, viewTechDoc: techdocsPlugin.routes.docRoot, - selectedTemplateRoute: scaffolderPlugin.routes.selectedTemplate, + createFromTemplate: scaffolderPlugin.routes.selectedTemplate, }); bind(apiDocsPlugin.externalRoutes, { registerApi: catalogImportPlugin.routes.importPage, diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index cac3e7f0a9..8d62f29c52 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -40,7 +40,7 @@ const app = createApp({ bind(catalogPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, viewTechDoc: techdocsPlugin.routes.docRoot, - selectedTemplateRoute: scaffolderPlugin.routes.selectedTemplate, + createFromTemplate: scaffolderPlugin.routes.selectedTemplate, }); bind(apiDocsPlugin.externalRoutes, { registerApi: catalogImportPlugin.routes.importPage, diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 915e89d0c6..6bb4fcc4a1 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -105,7 +105,7 @@ export const catalogPlugin: BackstagePlugin< }, true >; - selectedTemplateRoute: ExternalRouteRef< + createFromTemplate: ExternalRouteRef< { namespace: string; templateName: string; diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx index 43af858300..e8d2657e9f 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.test.tsx @@ -30,7 +30,7 @@ import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import userEvent from '@testing-library/user-event'; import { screen } from '@testing-library/react'; import React from 'react'; -import { selectedTemplateRouteRef, viewTechDocRouteRef } from '../../routes'; +import { createFromTemplateRouteRef, viewTechDocRouteRef } from '../../routes'; import { AboutCard } from './AboutCard'; describe('', () => { @@ -545,7 +545,7 @@ describe('', () => { mountedRoutes: { '/catalog/:namespace/:kind/:name': entityRouteRef, '/create/templates/:namespace/:templateName': - selectedTemplateRouteRef, + createFromTemplateRouteRef, }, }, ); @@ -612,7 +612,7 @@ describe('', () => { mountedRoutes: { '/catalog/:namespace/:kind/:name': entityRouteRef, '/create/templates/:namespace/:templateName': - selectedTemplateRouteRef, + createFromTemplateRouteRef, }, }, ); diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index 2933aea9e8..a08a9be5a4 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { ANNOTATION_EDIT_URL, ANNOTATION_LOCATION, @@ -42,6 +41,7 @@ import { getEntitySourceLocation, useEntity, } from '@backstage/plugin-catalog-react'; +import { isTemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { Card, CardContent, @@ -50,14 +50,14 @@ import { IconButton, makeStyles, } from '@material-ui/core'; -import AddIcon from '@material-ui/icons/Add'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import CachedIcon from '@material-ui/icons/Cached'; import DocsIcon from '@material-ui/icons/Description'; import EditIcon from '@material-ui/icons/Edit'; import React, { useCallback } from 'react'; -import { selectedTemplateRouteRef, viewTechDocRouteRef } from '../../routes'; + +import { createFromTemplateRouteRef, viewTechDocRouteRef } from '../../routes'; import { AboutContent } from './AboutContent'; -import { isTemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; const useStyles = makeStyles({ gridItemCard: { @@ -91,7 +91,8 @@ export interface AboutCardProps { /** * Exported publicly via the EntityAboutCard */ -export function AboutCard({ variant }: AboutCardProps) { +export function AboutCard(props: AboutCardProps) { + const { variant } = props; const app = useApp(); const classes = useStyles(); const { entity } = useEntity(); @@ -100,7 +101,7 @@ export function AboutCard({ variant }: AboutCardProps) { const alertApi = useApi(alertApiRef); const errorApi = useApi(errorApiRef); const viewTechdocLink = useRouteRef(viewTechDocRouteRef); - const templateRoute = useRouteRef(selectedTemplateRouteRef); + const templateRoute = useRouteRef(createFromTemplateRouteRef); const entitySourceLocation = getEntitySourceLocation( entity, @@ -133,7 +134,7 @@ export function AboutCard({ variant }: AboutCardProps) { const subHeaderLinks = [viewInSource, viewInTechDocs]; if (isTemplateEntityV1beta3(entity)) { - const Icon = app.getSystemIcon('scaffolder') ?? AddIcon; + const Icon = app.getSystemIcon('scaffolder') ?? CreateComponentIcon; const launchTemplate: IconLinkVerticalProps = { label: 'Launch Template', diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts index 22c85a755d..711f52767a 100644 --- a/plugins/catalog/src/plugin.ts +++ b/plugins/catalog/src/plugin.ts @@ -23,7 +23,7 @@ import { } from '@backstage/plugin-catalog-react'; import { createComponentRouteRef, - selectedTemplateRouteRef, + createFromTemplateRouteRef, viewTechDocRouteRef, } from './routes'; import { @@ -81,7 +81,7 @@ export const catalogPlugin = createPlugin({ externalRoutes: { createComponent: createComponentRouteRef, viewTechDoc: viewTechDocRouteRef, - selectedTemplateRoute: selectedTemplateRouteRef, + createFromTemplate: createFromTemplateRouteRef, }, __experimentalConfigure( options?: CatalogInputPluginOptions, diff --git a/plugins/catalog/src/routes.ts b/plugins/catalog/src/routes.ts index 2d9667d8e2..ab70a3c551 100644 --- a/plugins/catalog/src/routes.ts +++ b/plugins/catalog/src/routes.ts @@ -30,8 +30,8 @@ export const viewTechDocRouteRef = createExternalRouteRef({ params: ['namespace', 'kind', 'name'], }); -export const selectedTemplateRouteRef = createExternalRouteRef({ - id: 'selected-template', +export const createFromTemplateRouteRef = createExternalRouteRef({ + id: 'create-from-template', optional: true, params: ['namespace', 'templateName'], }); From 8181a1330f7abe66596fa5c341b92cc690f3a57e Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Tue, 23 May 2023 14:05:54 -0400 Subject: [PATCH 072/255] Update based on PR comments - leave target as required while still in schema Signed-off-by: Malikah Montgomery --- packages/catalog-model/src/schema/shared/common.schema.json | 2 +- .../src/validation/entitySchemaValidator.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/catalog-model/src/schema/shared/common.schema.json b/packages/catalog-model/src/schema/shared/common.schema.json index 77eb4a2612..7468bd9d70 100644 --- a/packages/catalog-model/src/schema/shared/common.schema.json +++ b/packages/catalog-model/src/schema/shared/common.schema.json @@ -32,7 +32,7 @@ "$id": "#relation", "type": "object", "description": "A directed relation from one entity to another.", - "required": ["type", "targetRef"], + "required": ["type", "target", "targetRef"], "additionalProperties": false, "properties": { "type": { diff --git a/packages/catalog-model/src/validation/entitySchemaValidator.test.ts b/packages/catalog-model/src/validation/entitySchemaValidator.test.ts index 7ae6a1fa05..bc31c5d800 100644 --- a/packages/catalog-model/src/validation/entitySchemaValidator.test.ts +++ b/packages/catalog-model/src/validation/entitySchemaValidator.test.ts @@ -365,9 +365,9 @@ describe('entitySchemaValidator', () => { expect(() => validator(entity)).toThrow(/relations/); }); - it('does not reject missing relations.target', () => { + it('does reject missing relations.target', () => { delete entity.relations[0].target; - expect(() => validator(entity)).not.toThrow(/relations/); + expect(() => validator(entity)).toThrow(/relations/); }); it('does reject missing relations.targetRef', () => { From eb7fae840e899365b4e46cc69e093bd60a53ae9d Mon Sep 17 00:00:00 2001 From: Nicholas Bucher Date: Tue, 23 May 2023 14:36:28 -0400 Subject: [PATCH 073/255] Updated title Signed-off-by: Nicholas Bucher --- microsite/data/plugins/solo-io-dev-portal.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsite/data/plugins/solo-io-dev-portal.yaml b/microsite/data/plugins/solo-io-dev-portal.yaml index a2cf2eaa8b..dcc5722092 100644 --- a/microsite/data/plugins/solo-io-dev-portal.yaml +++ b/microsite/data/plugins/solo-io-dev-portal.yaml @@ -1,9 +1,9 @@ --- -title: Solo.io Gloo Portal +title: Solo.io Gloo Platform Portal author: Solo.io authorUrl: https://solo.io category: Infrastructure -description: View, test, and manage your Gloo Portal APIs through our Backstage plugin. +description: View, test, and manage your Gloo Platform Portal APIs through our Backstage plugin. documentation: https://github.com/solo-io/dev-portal-backstage-public#readme iconUrl: img/solo-io-gloo-outline.svg npmPackageName: '@solo.io/dev-portal-backstage-plugin' From 2e4940e1a8f8a5490a76692581bd7a896b33332f Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Tue, 2 May 2023 11:03:06 +0300 Subject: [PATCH 074/255] feat: allow more customization of the custom homepage This allows users to modify the actual grid properties from backstage interface. Signed-off-by: Heikki Hellgren --- .changeset/dull-bottles-teach.md | 5 + .../CustomizableTemplate.stories.tsx | 153 ++++++++++++++++++ plugins/home/README.md | 3 +- plugins/home/api-report.md | 14 +- .../CustomHomepage/CustomHomepageButtons.tsx | 22 ++- .../CustomHomepage/CustomHomepageGrid.tsx | 103 +++++++++++- .../src/components/CustomHomepage/index.ts | 2 +- 7 files changed, 293 insertions(+), 9 deletions(-) create mode 100644 .changeset/dull-bottles-teach.md create mode 100644 packages/app/src/components/home/templates/CustomizableTemplate.stories.tsx diff --git a/.changeset/dull-bottles-teach.md b/.changeset/dull-bottles-teach.md new file mode 100644 index 0000000000..74203eb758 --- /dev/null +++ b/.changeset/dull-bottles-teach.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Allow more customization for the CustomHomepageGrid diff --git a/packages/app/src/components/home/templates/CustomizableTemplate.stories.tsx b/packages/app/src/components/home/templates/CustomizableTemplate.stories.tsx new file mode 100644 index 0000000000..e587e52da4 --- /dev/null +++ b/packages/app/src/components/home/templates/CustomizableTemplate.stories.tsx @@ -0,0 +1,153 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + HomePageStarredEntities, + HomePageRandomJoke, + CustomHomepageGrid, +} from '@backstage/plugin-home'; +import { + starredEntitiesApiRef, + MockStarredEntitiesApi, + entityRouteRef, + catalogApiRef, +} from '@backstage/plugin-catalog-react'; +import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { ConfigReader } from '@backstage/config'; +import { searchApiRef } from '@backstage/plugin-search-react'; +import { HomePageSearchBar, searchPlugin } from '@backstage/plugin-search'; +import { HomePageCalendar } from '@backstage/plugin-gcalendar'; +import { MicrosoftCalendarCard } from '@backstage/plugin-microsoft-calendar'; +import React, { ComponentType } from 'react'; + +const entities = [ + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity', + title: 'Mock Starred Entity!', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity-2', + title: 'Mock Starred Entity 2!', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity-3', + title: 'Mock Starred Entity 3!', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'mock-starred-entity-4', + title: 'Mock Starred Entity 4!', + }, + }, +]; + +const mockCatalogApi = { + getEntities: async () => ({ items: entities }), +}; + +const starredEntitiesApi = new MockStarredEntitiesApi(); +starredEntitiesApi.toggleStarred('component:default/example-starred-entity'); +starredEntitiesApi.toggleStarred('component:default/example-starred-entity-2'); +starredEntitiesApi.toggleStarred('component:default/example-starred-entity-3'); +starredEntitiesApi.toggleStarred('component:default/example-starred-entity-4'); + +export default { + title: 'Plugins/Home/Templates', + decorators: [ + (Story: ComponentType<{}>) => + wrapInTestApp( + <> + Promise.resolve({ results: [] }) }], + [ + configApiRef, + new ConfigReader({ + backend: { + baseUrl: 'https://localhost:7007', + }, + }), + ], + ]} + > + + + , + { + mountedRoutes: { + '/hello-company': searchPlugin.routes.root, + '/catalog/:namespace/:kind/:name': entityRouteRef, + }, + }, + ), + ], +}; +export const CustomizableTemplate = () => { + // This is the default configuration that is shown to the user + // when first arriving to the homepage. + const defaultConfig = [ + { + component: 'HomePageSearchBar', + x: 0, + y: 0, + width: 12, + height: 5, + }, + { + component: 'HomePageRandomJoke', + x: 0, + y: 2, + width: 6, + height: 16, + }, + { + component: 'HomePageStarredEntities', + x: 6, + y: 2, + width: 6, + height: 12, + }, + ]; + + return ( + + // Insert the allowed widgets inside the grid. User can add, organize and + // remove the widgets as they want. + + + + + + + ); +}; diff --git a/plugins/home/README.md b/plugins/home/README.md index 20b91289b1..42fbf73d18 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -247,4 +247,5 @@ Additionally, the API is at a very early state, so contributing with additional ### Homepage Templates -We are hoping that we together can build up a collection of Homepage templates. We therefore put together a place where we can collect all the templates for the Home Plugin in the [storybook](https://backstage.io/storybook/?path=/story/plugins-home-templates). If you would like to contribute with a template, start by taking a look at the [DefaultTemplate storybook example to create your own](/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion. +We are hoping that we together can build up a collection of Homepage templates. We therefore put together a place where we can collect all the templates for the Home Plugin in the [storybook](https://backstage.io/storybook/?path=/story/plugins-home-templates). +If you would like to contribute with a template, start by taking a look at the [DefaultTemplate storybook example](/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx) or [CustomizableTemplate storybook example](/packages/app/src/components/home/templates/CustomizableTemplate.stories.tsx) to create your own, and then open a PR with your suggestion. diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 56b1f54331..e4ff522062 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -19,6 +19,9 @@ import { ReactNode } from 'react'; import { RendererProps as RendererProps_2 } from '@backstage/plugin-home-react'; import { RouteRef } from '@backstage/core-plugin-api'; +// @public +export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + // @public @deprecated (undocumented) export type CardConfig = CardConfig_2; @@ -77,11 +80,20 @@ export const CustomHomepageGrid: ( props: CustomHomepageGridProps, ) => JSX.Element; -// @public (undocumented) +// @public export type CustomHomepageGridProps = { children?: ReactNode; config?: LayoutConfiguration[]; rowHeight?: number; + breakpoints?: Record; + cols?: Record; + containerPadding?: [number, number] | Record; + containerMargin?: [number, number] | Record; + maxRows?: number; + style?: React_2.CSSProperties; + compactType?: 'vertical' | 'horizontal' | null; + allowOverlap?: boolean; + preventCollision?: boolean; }; // @public diff --git a/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx b/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx index c2fdf5b979..22c88a30c7 100644 --- a/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx +++ b/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx @@ -20,6 +20,7 @@ import SaveIcon from '@material-ui/icons/Save'; import DeleteIcon from '@material-ui/icons/Delete'; import AddIcon from '@material-ui/icons/Add'; import EditIcon from '@material-ui/icons/Edit'; +import CancelIcon from '@material-ui/icons/Cancel'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -41,6 +42,8 @@ interface CustomHomepageButtonsProps { clearLayout: () => void; setAddWidgetDialogOpen: (open: boolean) => void; changeEditMode: (mode: boolean) => void; + defaultConfigAvailable: boolean; + restoreDefault: () => void; } export const CustomHomepageButtons = (props: CustomHomepageButtonsProps) => { const { @@ -49,6 +52,8 @@ export const CustomHomepageButtons = (props: CustomHomepageButtonsProps) => { clearLayout, setAddWidgetDialogOpen, changeEditMode, + defaultConfigAvailable, + restoreDefault, } = props; const styles = useStyles(); @@ -59,27 +64,41 @@ export const CustomHomepageButtons = (props: CustomHomepageButtonsProps) => { variant="contained" color="primary" onClick={() => changeEditMode(true)} + size="small" startIcon={} > Edit ) : ( <> + {defaultConfigAvailable && ( + + )} {numWidgets > 0 && ( )}