diff --git a/.changeset/cost-insights-five-baboons-attack.md b/.changeset/cost-insights-five-baboons-attack.md deleted file mode 100644 index f2c070acb4..0000000000 --- a/.changeset/cost-insights-five-baboons-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-cost-insights': minor ---- - -make change ratio optional diff --git a/.changeset/spotty-pigs-bathe.md b/.changeset/spotty-pigs-bathe.md deleted file mode 100644 index 0585f335dc..0000000000 --- a/.changeset/spotty-pigs-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-tech-radar': patch ---- - -Update README for composability diff --git a/.changeset/thick-cobras-switch.md b/.changeset/thick-cobras-switch.md deleted file mode 100644 index 002c724421..0000000000 --- a/.changeset/thick-cobras-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-proxy-backend': patch ---- - -Prefix proxy routes with `/` if not present in configuration diff --git a/.changeset/tough-walls-wash.md b/.changeset/tough-walls-wash.md deleted file mode 100644 index a45adcfbf6..0000000000 --- a/.changeset/tough-walls-wash.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/catalog-model': patch -'@backstage/cli': patch -'@backstage/config-loader': patch -'@backstage/config': patch -'@backstage/plugin-scaffolder': patch ---- - -Bump `json-schema` dependency from `0.2.5` to `0.3.0`. diff --git a/.changeset/tricky-yaks-melt.md b/.changeset/tricky-yaks-melt.md deleted file mode 100644 index 3679e6d1da..0000000000 --- a/.changeset/tricky-yaks-melt.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Adding optional config to enable S3-like API for tech-docs using s3ForcePathStyle option. -This allows providers like LocalStack, Minio and Wasabi (+possibly others) to be used to host tech docs. diff --git a/.changeset/twenty-peas-deny.md b/.changeset/twenty-peas-deny.md deleted file mode 100644 index d149a9337c..0000000000 --- a/.changeset/twenty-peas-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-client': patch ---- - -Allow `filter` parameter to be specified multiple times diff --git a/docs/cli/commands.md b/docs/cli/commands.md index 324d17463c..a99d4cd9d0 100644 --- a/docs/cli/commands.md +++ b/docs/cli/commands.md @@ -111,6 +111,7 @@ Usage: backstage-cli app:build Options: --stats Write bundle stats to output directory + --lax Do not require environment variables to be set --config <path> Config files to load instead of app-config.yaml (default: []) -h, --help display help for command ``` @@ -486,6 +487,7 @@ Usage: backstage-cli config:print [options] Options: --package <name> Only load config schema that applies to the given package + --lax Do not require environment variables to be set --frontend Print only the frontend configuration --with-secrets Include secrets in the printed configuration --format <format> Format to print the configuration in, either json or yaml [yaml] @@ -506,6 +508,7 @@ Usage: backstage-cli config:check [options] Options: --package <name> Only load config schema that applies to the given package + --lax Do not require environment variables to be set --config <path> Config files to load instead of app-config.yaml (default: []) -h, --help display help for command ``` diff --git a/docs/features/kubernetes/installation.md b/docs/features/kubernetes/installation.md index 10968b427c..36bb724be6 100644 --- a/docs/features/kubernetes/installation.md +++ b/docs/features/kubernetes/installation.md @@ -8,44 +8,36 @@ The Kubernetes feature is a plugin to Backstage, and it is exposed as a tab when viewing entities in the software catalog. If you haven't setup Backstage already, read the -[Getting Started](../../getting-started/index.md). +[Getting Started](../../getting-started/index.md) guide. ## Adding the Kubernetes frontend plugin -The first step is to add the frontend Kubernetes plugin to your Backstage -application. Navigate to your new Backstage application directory. And then to +The first step is to add the Kubernetes frontend plugin to your Backstage +application. Navigate to your new Backstage application directory, and then to your `packages/app` directory, and install the `@backstage/plugin-kubernetes` package. ```bash -cd my-backstage-app/ +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-kubernetes ``` Once the package has been installed, you need to import the plugin in your app -by adding the "Kubernetes" tab to the catalog entity page. In -`packages/app/src/components/catalog/EntityPage.tsx`, you'll add a router to get -to the tab, and add the tab itself. - -`EntityPage.tsx`: +by adding the "Kubernetes" tab to the respective catalog pages. ```tsx -import { Router as KubernetesRouter } from '@backstage/plugin-kubernetes'; +// In packages/app/src/components/catalog/EntityPage.tsx +import { EntityKubernetesContent } from '@backstage/plugin-kubernetes'; -// ... - -const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( - - // ... - } - /> - // ... - -); +// You can add the tab to any number of pages, the service page is shown as an +// example here +const serviceEntityPage = ( + + {/* other tabs... */} + + + ``` That's it! But now, we need the Kubernetes Backend plugin for the frontend to @@ -57,17 +49,16 @@ Navigate to `packages/backend` of your Backstage app, and install the `@backstage/plugin-kubernetes-backend` package. ```bash -cd my-backstage-app/ +# From your Backstage root directory cd packages/backend yarn add @backstage/plugin-kubernetes-backend ``` Create a file called `kubernetes.ts` inside `packages/backend/src/plugins/` and -add the following - -`kubernetes.ts`: +add the following: ```typescript +// In packages/backend/src/plugins/kubernetes.ts import { createRouter } from '@backstage/plugin-kubernetes-backend'; import { PluginEnvironment } from '../types'; @@ -83,18 +74,15 @@ And import the plugin to `packages/backend/src/index.ts`. There are three lines of code you'll need to add, and they should be added near similar code in your existing Backstage backend. -`index.ts`: - ```typescript +// In packages/backend/src/index.ts import kubernetes from './plugins/kubernetes'; - // ... - -const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes')); - -// ... - -apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv)); +async function main() { + // ... + const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes')); + // ... + apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv)); ``` That's it! The Kubernetes frontend and backend have now been added to your diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index 1afdff418a..320b82d0ad 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -37,6 +37,13 @@ The locations added through static configuration cannot be removed through the catalog locations API. To remove these locations, you must remove them from the configuration. +Syntax errors or other types of errors present in `catalog-info.yaml` files will +be logged for investigation. Errors do not cause processing to abort. + +When multiple `catalog-info.yaml` files with the same `metadata.name` property +are discovered, one will be processed and all others will be skipped. This +action is logged for further investigation. + ### Integration Processors Integrations may simply provide a mechanism to handle `url` location type for an @@ -111,8 +118,7 @@ catalog: > deleting entities will not work in this mode.** A common use case for this configuration is when organizations have a remote -source that should be mirrored into backstage. If we want backstage to be a -mirror of this remote source we cannot allow users to also register entities -with e.g. +source that should be mirrored into Backstage. To make Backstage a mirror of +this remote source, users cannot also register new entities with e.g. the [catalog-import](https://github.com/backstage/backstage/tree/master/plugins/catalog-import) plugin. diff --git a/docs/features/software-catalog/installation.md b/docs/features/software-catalog/installation.md index df473f886f..0b622fb093 100644 --- a/docs/features/software-catalog/installation.md +++ b/docs/features/software-catalog/installation.md @@ -18,6 +18,7 @@ The catalog frontend plugin should be installed in your `app` package, which is created as a part of `@backstage/create-app`. To install the package, run: ```bash +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-catalog ``` @@ -103,6 +104,7 @@ The catalog backend should be installed in your `backend` package, which is created as a part of `@backstage/create-app`. To install the package, run: ```bash +# From your Backstage root directory cd packages/backend yarn add @backstage/plugin-catalog-backend ``` diff --git a/docs/features/software-templates/installation.md b/docs/features/software-templates/installation.md index 96eea3c750..ad7e6d1062 100644 --- a/docs/features/software-templates/installation.md +++ b/docs/features/software-templates/installation.md @@ -20,6 +20,7 @@ The scaffolder frontend plugin should be installed in your `app` package, which is created as a part of `@backstage/create-app`. To install the package, run: ```bash +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-scaffolder ``` @@ -57,6 +58,7 @@ The scaffolder backend should be installed in your `backend` package, which is created as a part of `@backstage/create-app`. To install the package, run: ```bash +# From your Backstage root directory cd packages/backend yarn add @backstage/plugin-scaffolder-backend ``` @@ -68,6 +70,10 @@ creating a file called `packages/backend/src/plugins/scaffolder.ts` with the following contents to get you up and running quickly. ```ts +import { + DockerContainerRunner, + SingleHostDiscovery, +} from '@backstage/backend-common'; import { CookieCutter, createRouter, @@ -76,7 +82,6 @@ import { CreateReactAppTemplater, Templaters, } from '@backstage/plugin-scaffolder-backend'; -import { SingleHostDiscovery } from '@backstage/backend-common'; import type { PluginEnvironment } from '../types'; import Docker from 'dockerode'; import { CatalogClient } from '@backstage/catalog-client'; @@ -87,8 +92,11 @@ export default async function createPlugin({ database, reader, }: PluginEnvironment) { - const cookiecutterTemplater = new CookieCutter(); - const craTemplater = new CreateReactAppTemplater(); + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + const cookiecutterTemplater = new CookieCutter({ containerRunner }); + const craTemplater = new CreateReactAppTemplater({ containerRunner }); const templaters = new Templaters(); templaters.register('cookiecutter', cookiecutterTemplater); @@ -97,8 +105,6 @@ export default async function createPlugin({ const preparers = await Preparers.fromConfig(config, { logger }); const publishers = await Publishers.fromConfig(config, { logger }); - const dockerClient = new Docker(); - const discovery = SingleHostDiscovery.fromConfig(config); const catalogClient = new CatalogClient({ discoveryApi: discovery }); @@ -108,7 +114,6 @@ export default async function createPlugin({ publishers, logger, config, - dockerClient, database, catalogClient, reader, @@ -262,6 +267,6 @@ library. By default it will use the [spotify/backstage-cookiecutter](https://github.com/backstage/backstage/blob/37e35b910afc7d1270855aed0ec4718aba366c91/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile) docker image. -If you are running backstage from a Docker container and you want to avoid +If you are running Backstage from a Docker container and you want to avoid calling a container inside a container, you can set up Cookiecutter in your own image, this will use the local installation instead. diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index ff145ee5af..90dd603c8f 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -106,7 +106,6 @@ return await createRouter({ publishers, logger, config, - dockerClient, database, catalogClient, reader, @@ -124,7 +123,6 @@ return await createRouter({ publishers, logger, config, - dockerClient, database, catalogClient, reader, @@ -136,11 +134,9 @@ return await createRouter({ want to have those as well as your new one, you'll need to do the following: ```ts - -import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend`; +import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; const builtInActions = createBuiltinActions({ - dockerClient, integrations, catalogClient, templaters, @@ -155,7 +151,6 @@ return await createRouter({ publishers, logger, config, - dockerClient, database, catalogClient, reader, diff --git a/docs/features/techdocs/configuring-ci-cd.md b/docs/features/techdocs/configuring-ci-cd.md index a9844ef528..0dec7ec24e 100644 --- a/docs/features/techdocs/configuring-ci-cd.md +++ b/docs/features/techdocs/configuring-ci-cd.md @@ -148,6 +148,23 @@ jobs: - uses: actions/setup-node@v2 - uses: actions/setup-python@v2 + # the 2 steps below can be removed if you aren't using plantuml in your documentation + - name: setup java + uses: actions/setup-java@v2 + with: + distribution: 'zulu' + java-version: '11' + - name: download, validate, install plantuml and its dependencies + run: | + curl -o plantuml.jar -L http://sourceforge.net/projects/plantuml/files/plantuml.1.2021.4.jar/download + echo "be498123d20eaea95a94b174d770ef94adfdca18 plantuml.jar" | sha1sum -c - + mv plantuml.jar /opt/plantuml.jar + mkdir -p "$HOME/.local/bin" + echo $'#!/bin/sh\n\njava -jar '/opt/plantuml.jar' ${@}' >> "$HOME/.local/bin/plantuml" + chmod +x "$HOME/.local/bin/plantuml" + echo "$HOME/.local/bin" >> $GITHUB_PATH + sudo apt-get install -y graphviz + - name: Install techdocs-cli run: sudo npm install -g @techdocs/cli diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index 223b276d68..51f07d5cde 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -22,7 +22,7 @@ Navigate to your new Backstage application directory. And then to your `packages/app` directory, and install the `@backstage/plugin-techdocs` package. ```bash -cd my-backstage-app/ +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-techdocs ``` @@ -54,7 +54,7 @@ Navigate to `packages/backend` of your Backstage app, and install the `@backstage/plugin-techdocs-backend` package. ```bash -cd my-backstage-app/ +# From your Backstage root directory cd packages/backend yarn add @backstage/plugin-techdocs-backend ``` @@ -63,6 +63,7 @@ Create a file called `techdocs.ts` inside `packages/backend/src/plugins/` and add the following ```typescript +import { DockerContainerRunner } from '@backstage/backend-common'; import { createRouter, Generators, @@ -84,9 +85,14 @@ export default async function createPlugin({ reader, }); + // Docker client (conditionally) used by the generators, based on techdocs.generators config. + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + // Generators are used for generating documentation sites. const generators = await Generators.fromConfig(config, { logger, + containerRunner, }); // Publisher is used for @@ -97,14 +103,13 @@ export default async function createPlugin({ discovery, }); - // Docker client (conditionally) used by the generators, based on techdocs.generators config. - const dockerClient = new Docker(); + // checks if the publisher is working and logs the result + await publisher.getReadiness(); return await createRouter({ preparers, generators, publisher, - dockerClient, logger, config, discovery, diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md index 181f541929..4dbc6bfa48 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/getting-started/app-custom-theme.md @@ -135,3 +135,31 @@ const themeOptions = createThemeOptions({ }, }); ``` + +## Custom Logo + +In addition to a custom theme, you can also customize the logo displayed at the +far top left of the site. + +In your frontend app, locate `src/components/Root/` folder. You'll find two +components: + +- `LogoFull.tsx` - A larger logo used when the Sidebar navigation is opened. +- `LogoIcon.tsx` - A smaller logo used when the sidebar navigation is closed. + +To replace the images, you can simply replace the relevant code in those +components with raw SVG definitions. + +You can also use another web image format such as PNG by importing it. To do +this, place your new image into a new subdirectory such as +`src/components/Root/logo/my-company-logo.png`, and then add this code: + +```jsx +import MyCustomLogoFull from './logo/my-company-logo.png'; + +//... + +const LogoFull = () => { + return ; +}; +``` diff --git a/docs/plugins/integrating-plugin-into-service-catalog.md b/docs/plugins/integrating-plugin-into-service-catalog.md index 01de779635..047b6f14e6 100644 --- a/docs/plugins/integrating-plugin-into-service-catalog.md +++ b/docs/plugins/integrating-plugin-into-service-catalog.md @@ -1,7 +1,7 @@ --- id: integrating-plugin-into-service-catalog title: Integrate into the Service Catalog -description: Documentation on How to integrate plugin into service catalog +description: How to integrate a plugin into service catalog --- > This is an advanced use case and currently is an experimental feature. Expect diff --git a/docs/tutorials/switching-sqlite-postgres.md b/docs/tutorials/switching-sqlite-postgres.md index a759b7dc49..fafd2ad586 100644 --- a/docs/tutorials/switching-sqlite-postgres.md +++ b/docs/tutorials/switching-sqlite-postgres.md @@ -17,11 +17,11 @@ switch between database backends. ## Install PostgreSQL -First, swap out SQLite for PostgreSQL in your `backend` package: +First, add PostgreSQL to your `backend` package: ```shell +# From your Backstage root directory cd packages/backend -yarn remove sqlite3 yarn add pg ``` @@ -46,7 +46,6 @@ backend: + #ssl: require # see https://www.postgresql.org/docs/current/libpq-ssl.html Table 33.1. SSL Mode Descriptions (e.g. require) + #ca: # if you have a CA file and want to verify it you can uncomment this section + #$file: /ca/server.crt - ``` If you have an `app-config.local.yaml` for local development, a similar update diff --git a/microsite/README.md b/microsite/README.md index 9589def3e3..285622f1f8 100644 --- a/microsite/README.md +++ b/microsite/README.md @@ -1,3 +1,9 @@ +# Backstage Documentation + +This folder holds the service and configuration that runs Backstage's documentation hosted at https://backstage.io. + +It pulls content in from the [root `/docs`](../docs/) folder and builds it into the resulting HTML web site. + This website was created with [Docusaurus](https://docusaurus.io/). # What's In This Document @@ -10,6 +16,8 @@ This website was created with [Docusaurus](https://docusaurus.io/). # Getting Started +Testing the web site locally is a great way to see what final website will look like after publishing, and is ideal for testing more complex changes, large updates to navigation, or complex page designs that may include special alignment, which may not otherwise be validated through continuous integration. + ## Installation ``` @@ -22,7 +30,7 @@ $ yarn install $ yarn start ``` -This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. +This command starts a local development server and opens up a browser window. Most content changes made to the `docs/` root folder are reflected live without having to restart the server. ## Build @@ -57,9 +65,9 @@ my-docusaurus/ siteConfig.js ``` -# Editing Content +## Editing Content -## Editing an existing docs page +### Editing an existing docs page Edit docs by navigating to `docs/` and editing the corresponding document: @@ -76,7 +84,7 @@ Edit me... For more information about docs, click [here](https://docusaurus.io/docs/en/navigation) -## Editing an existing blog post +### Editing an existing blog post Edit blog posts by navigating to `website/blog` and editing the corresponding post: @@ -93,9 +101,9 @@ Edit me... For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) -# Adding Content +## Adding Content -## Adding a new docs page to an existing sidebar +### Adding a new docs page to an existing sidebar 1. Create the doc as a new markdown file in `/docs`, example `docs/newly-created-doc.md`: @@ -126,7 +134,7 @@ My new content here.. For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation) -## Adding a new blog post +### Adding a new blog post 1. Make sure there is a header link to your blog in `website/siteConfig.js`: @@ -157,7 +165,7 @@ Lorem Ipsum... For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) -## Adding items to your site's top navigation bar +### Adding items to your site's top navigation bar 1. Add links to docs, custom pages or external links by editing the headerLinks field of `website/siteConfig.js`: @@ -181,7 +189,7 @@ For more information about blog posts, click [here](https://docusaurus.io/docs/e For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation) -## Adding custom pages +### Adding custom pages 1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`: 1. If you want your page to show up in your navigation header, you will need to update `website/siteConfig.js` to add to the `headerLinks` element: @@ -199,11 +207,11 @@ For more information about the navigation bar, click [here](https://docusaurus.i } ``` -For more information about custom pages, click [here](https://docusaurus.io/docs/en/custom-pages). +Learn more about [Docusaurus custom pages](https://docusaurus.io/docs/en/custom-pages). -# Full Documentation +## Full Documentation -Full documentation can be found on the [website](https://docusaurus.io/). +Full documentation can be found on the [Docusaurus website](https://docusaurus.io/). ## Additional notes diff --git a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md index 65102c919f..7289878bc1 100644 --- a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md +++ b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.md @@ -45,7 +45,7 @@ The only thing you need to do is to start the app: ```bash cd my-app -yarn start +yarn dev ``` And you are good to go! 👍 @@ -101,18 +101,12 @@ We provide a collection of public Backstage plugins (look for packages with the Install in your app’s package folder (`/packages/app`) with: ```bash +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin- ``` -Then add it to your app's `plugin.ts` file to import and register it: - -`/packages/app/src/plugin.ts`: - -```js -export { plugin as PluginName } from '@backstage/plugin-'; -``` - -A plugin registers its own `route` in the app — read the documentation for the specific plugin you are installing for more information on that. +After that, you inject the plugin into the application where you want it to be exposed. Please read the documentation for the specific plugin you are installing for more information. ### Creating an internal plugin diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index e9d3e63ad0..33ef204f63 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,36 @@ # example-app +## 0.2.27 + +### Patch Changes + +- Updated dependencies [6f1b82b14] +- Updated dependencies [38ca05168] +- Updated dependencies [f65adcde7] +- Updated dependencies [81c54d1f2] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [8aedbb4af] +- Updated dependencies [fc79a6dd3] +- Updated dependencies [f53fba29f] +- Updated dependencies [b2e2ec753] +- Updated dependencies [9314a8592] +- Updated dependencies [2e05277e0] +- Updated dependencies [4075c6367] +- Updated dependencies [d8b81fd28] + - @backstage/plugin-cost-insights@0.9.0 + - @backstage/plugin-catalog-import@0.5.5 + - @backstage/plugin-github-actions@0.4.5 + - @backstage/cli@0.6.10 + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-react@0.1.5 + - @backstage/theme@0.2.7 + - @backstage/plugin-kubernetes@0.4.3 + - @backstage/plugin-tech-radar@0.3.10 + - @backstage/plugin-scaffolder@0.9.3 + - @backstage/plugin-techdocs@0.9.1 + - @backstage/catalog-model@0.7.8 + ## 0.2.26 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 74cabfcaf3..76ee100278 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,46 +1,46 @@ { "name": "example-app", - "version": "0.2.26", + "version": "0.2.27", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.7.7", - "@backstage/cli": "^0.6.9", - "@backstage/core": "^0.7.7", + "@backstage/catalog-model": "^0.7.8", + "@backstage/cli": "^0.6.10", + "@backstage/core": "^0.7.8", "@backstage/integration-react": "^0.1.1", "@backstage/plugin-api-docs": "^0.4.12", "@backstage/plugin-badges": "^0.2.0", "@backstage/plugin-catalog": "^0.5.6", - "@backstage/plugin-catalog-import": "^0.5.4", - "@backstage/plugin-catalog-react": "^0.1.3", + "@backstage/plugin-catalog-import": "^0.5.5", + "@backstage/plugin-catalog-react": "^0.1.5", "@backstage/plugin-circleci": "^0.2.13", "@backstage/plugin-cloudbuild": "^0.2.13", "@backstage/plugin-code-coverage": "^0.1.2", - "@backstage/plugin-cost-insights": "^0.8.5", + "@backstage/plugin-cost-insights": "^0.9.0", "@backstage/plugin-explore": "^0.3.4", "@backstage/plugin-gcp-projects": "^0.2.5", - "@backstage/plugin-github-actions": "^0.4.4", + "@backstage/plugin-github-actions": "^0.4.5", "@backstage/plugin-graphiql": "^0.2.10", "@backstage/plugin-jenkins": "^0.4.2", "@backstage/plugin-kafka": "^0.2.6", - "@backstage/plugin-kubernetes": "^0.4.2", + "@backstage/plugin-kubernetes": "^0.4.3", "@backstage/plugin-lighthouse": "^0.2.15", "@backstage/plugin-newrelic": "^0.2.6", "@backstage/plugin-org": "^0.3.12", "@backstage/plugin-pagerduty": "0.3.3", "@backstage/plugin-rollbar": "^0.3.4", - "@backstage/plugin-scaffolder": "^0.9.2", + "@backstage/plugin-scaffolder": "^0.9.3", "@backstage/plugin-search": "^0.3.5", "@backstage/plugin-sentry": "^0.3.9", "@backstage/plugin-shortcuts": "^0.1.1", - "@backstage/plugin-tech-radar": "^0.3.9", - "@backstage/plugin-techdocs": "^0.9.0", + "@backstage/plugin-tech-radar": "^0.3.10", + "@backstage/plugin-techdocs": "^0.9.1", "@backstage/plugin-todo": "^0.1.0", "@backstage/plugin-user-settings": "^0.2.8", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", - "@octokit/rest": "^18.0.12", + "@octokit/rest": "^18.5.3", "@roadiehq/backstage-plugin-buildkite": "^1.0.0", "@roadiehq/backstage-plugin-github-insights": "^1.0.0", "@roadiehq/backstage-plugin-github-pull-requests": "^1.0.0", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 68fab7d861..3568adf099 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,73 @@ # @backstage/backend-common +## 0.7.0 + +### Minor Changes + +- e0bfd3d44: Refactor the `runDockerContainer(…)` function to an interface-based api. + This gives the option to replace the docker runtime in the future. + + Packages and plugins that previously used the `dockerode` as argument should be migrated to use the new `ContainerRunner` interface instead. + + ```diff + import { + - runDockerContainer, + + ContainerRunner, + PluginEndpointDiscovery, + } from '@backstage/backend-common'; + - import Docker from 'dockerode'; + + type RouterOptions = { + // ... + - dockerClient: Docker, + + containerRunner: ContainerRunner; + }; + + export async function createRouter({ + // ... + - dockerClient, + + containerRunner, + }: RouterOptions): Promise { + // ... + + + await containerRunner.runContainer({ + - await runDockerContainer({ + image: 'docker', + // ... + - dockerClient, + }); + + // ... + } + ``` + + To keep the `dockerode` based runtime, use the `DockerContainerRunner` implementation: + + ```diff + + import { + + ContainerRunner, + + DockerContainerRunner + + } from '@backstage/backend-common'; + - import { runDockerContainer } from '@backstage/backend-common'; + + + const containerRunner: ContainerRunner = new DockerContainerRunner({dockerClient}); + + await containerRunner.runContainer({ + - await runDockerContainer({ + image: 'docker', + // ... + - dockerClient, + }); + ``` + +### Patch Changes + +- 38ca05168: The default `@octokit/rest` dependency was bumped to `"^18.5.3"`. +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/integration@0.5.2 + - @backstage/config-loader@0.6.1 + - @backstage/config@0.1.5 + ## 0.6.3 ### Patch Changes diff --git a/packages/backend-common/README.md b/packages/backend-common/README.md index dc08d89416..dc69790bc3 100644 --- a/packages/backend-common/README.md +++ b/packages/backend-common/README.md @@ -8,6 +8,8 @@ error handling and similar. Add the library to your backend package: ```sh +# From your Backstage root directory +cd packages/backend yarn add @backstage/backend-common ``` diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 58b9aa36d9..f0cb066dc0 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -12,7 +12,6 @@ import cors from 'cors'; import Docker from 'dockerode'; import { ErrorRequestHandler } from 'express'; import express from 'express'; -import { Format } from 'logform'; import { GithubCredentialsProvider } from '@backstage/integration'; import { GitHubIntegration } from '@backstage/integration'; import { GitLabIntegration } from '@backstage/integration'; @@ -64,7 +63,13 @@ export class BitbucketUrlReader implements UrlReader { } // @public (undocumented) -export const coloredFormat: Format; +export const coloredFormat: winston.Logform.Format; + +// @public (undocumented) +export interface ContainerRunner { + // (undocumented) + runContainer(opts: RunContainerOptions): Promise; +} // @public @deprecated export const createDatabase: typeof createDatabaseClient; @@ -81,6 +86,15 @@ export function createServiceBuilder(_module: NodeModule): ServiceBuilderImpl; // @public (undocumented) export function createStatusCheckRouter(options: StatusCheckRouterOptions): Promise; +// @public (undocumented) +export class DockerContainerRunner implements ContainerRunner { + constructor({ dockerClient }: { + dockerClient: Docker; + }); + // (undocumented) + runContainer({ imageName, command, args, logStream, mountDirs, workingDir, envVars, }: RunContainerOptions): Promise; +} + // @public export function ensureDatabaseExists(dbConfig: Config, ...databases: Array): Promise; @@ -256,10 +270,15 @@ export function requestLoggingHandler(logger?: Logger): RequestHandler; export function resolvePackagePath(name: string, ...paths: string[]): string; // @public (undocumented) -export const runDockerContainer: ({ imageName, args, logStream, dockerClient, mountDirs, workingDir, envVars, createOptions, }: RunDockerContainerOptions) => Promise<{ - error: any; - statusCode: any; -}>; +export type RunContainerOptions = { + imageName: string; + command?: string | string[]; + args: string[]; + logStream?: Writable; + mountDirs?: Record; + workingDir?: string; + envVars?: Record; +}; // @public export type SearchResponse = { diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 998ac475d9..a144f1c23f 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.6.3", + "version": "0.7.0", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -30,12 +30,12 @@ }, "dependencies": { "@backstage/cli-common": "^0.1.1", - "@backstage/config": "^0.1.4", - "@backstage/config-loader": "^0.6.0", + "@backstage/config": "^0.1.5", + "@backstage/config-loader": "^0.6.1", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.1", + "@backstage/integration": "^0.5.2", "@google-cloud/storage": "^5.8.0", - "@octokit/rest": "^18.0.12", + "@octokit/rest": "^18.5.3", "@types/cors": "^2.8.6", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", @@ -73,7 +73,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/test-utils": "^0.1.10", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", diff --git a/plugins/kubernetes/schema.d.ts b/packages/backend-common/src/util/ContainerRunner.ts similarity index 55% rename from plugins/kubernetes/schema.d.ts rename to packages/backend-common/src/util/ContainerRunner.ts index 196f5ba76d..7740e384e4 100644 --- a/plugins/kubernetes/schema.d.ts +++ b/packages/backend-common/src/util/ContainerRunner.ts @@ -14,29 +14,18 @@ * limitations under the License. */ -import { - ClusterLocatorMethod, - CustomResource, -} from '@backstage/plugin-kubernetes-backend'; +import { Writable } from 'stream'; -export interface Config { - kubernetes?: { - /** - * @visibility frontend - */ - serviceLocatorMethod: { - /** - * @visibility frontend - */ - type: 'multiTenant'; - }; - /** - * @visibility frontend - */ - clusterLocatorMethods: ClusterLocatorMethod[]; - /** - * @visibility frontend - */ - customResources?: CustomResource[]; - }; +export type RunContainerOptions = { + imageName: string; + command?: string | string[]; + args: string[]; + logStream?: Writable; + mountDirs?: Record; + workingDir?: string; + envVars?: Record; +}; + +export interface ContainerRunner { + runContainer(opts: RunContainerOptions): Promise; } diff --git a/packages/backend-common/src/util/docker.test.ts b/packages/backend-common/src/util/DockerContainerRunner.test.ts similarity index 89% rename from packages/backend-common/src/util/docker.test.ts rename to packages/backend-common/src/util/DockerContainerRunner.test.ts index 2ec14494da..942264af84 100644 --- a/packages/backend-common/src/util/docker.test.ts +++ b/packages/backend-common/src/util/DockerContainerRunner.test.ts @@ -13,17 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import Docker from 'dockerode'; import mockFs from 'mock-fs'; import os from 'os'; import path from 'path'; import Stream, { PassThrough } from 'stream'; -import { runDockerContainer, UserOptions } from './docker'; +import { ContainerRunner } from './ContainerRunner'; +import { DockerContainerRunner, UserOptions } from './DockerContainerRunner'; const mockDocker = new Docker() as jest.Mocked; const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; -describe('runDockerContainer', () => { +describe('DockerContainerRunner', () => { + let containerTaskApi: ContainerRunner; + beforeEach(() => { mockFs({ [rootDir]: { @@ -49,6 +53,8 @@ describe('runDockerContainer', () => { jest .spyOn(mockDocker, 'ping') .mockResolvedValue(Buffer.from('OK', 'utf-8')); + + containerTaskApi = new DockerContainerRunner({ dockerClient: mockDocker }); }); afterEach(() => { @@ -66,10 +72,9 @@ describe('runDockerContainer', () => { const envVarsArray = ['HOME=/tmp', 'LOG_LEVEL=debug']; it('should pull the docker container', async () => { - await runDockerContainer({ + await containerTaskApi.runContainer({ imageName, args, - dockerClient: mockDocker, }); expect(mockDocker.pull).toHaveBeenCalledWith( @@ -82,13 +87,12 @@ describe('runDockerContainer', () => { }); it('should call the dockerClient run command with the correct arguments passed through', async () => { - await runDockerContainer({ + await containerTaskApi.runContainer({ imageName, args, mountDirs, envVars, workingDir, - dockerClient: mockDocker, }); expect(mockDocker.run).toHaveBeenCalledWith( @@ -113,20 +117,18 @@ describe('runDockerContainer', () => { }); it('should ping docker to test availability', async () => { - await runDockerContainer({ + await containerTaskApi.runContainer({ imageName, args, - dockerClient: mockDocker, }); expect(mockDocker.ping).toHaveBeenCalled(); }); it('should pass through the user and group id from the host machine and set the home dir', async () => { - await runDockerContainer({ + await containerTaskApi.runContainer({ imageName, args, - dockerClient: mockDocker, }); const userOptions: UserOptions = {}; @@ -153,10 +155,9 @@ describe('runDockerContainer', () => { ]); await expect( - runDockerContainer({ + containerTaskApi.runContainer({ imageName, args, - dockerClient: mockDocker, }), ).rejects.toThrow(/Something went wrong with docker/); }); @@ -172,10 +173,9 @@ describe('runDockerContainer', () => { it('should throw with a descriptive error message including the docker error message', async () => { await expect( - runDockerContainer({ + containerTaskApi.runContainer({ imageName, args, - dockerClient: mockDocker, }), ).rejects.toThrow(new RegExp(`.+: ${dockerError}`)); }); @@ -183,11 +183,10 @@ describe('runDockerContainer', () => { it('should pass through the log stream to the docker client', async () => { const logStream = new PassThrough(); - await runDockerContainer({ + await containerTaskApi.runContainer({ imageName, args, logStream, - dockerClient: mockDocker, }); expect(mockDocker.run).toHaveBeenCalledWith( diff --git a/packages/backend-common/src/util/DockerContainerRunner.ts b/packages/backend-common/src/util/DockerContainerRunner.ts new file mode 100644 index 0000000000..a96ca1351b --- /dev/null +++ b/packages/backend-common/src/util/DockerContainerRunner.ts @@ -0,0 +1,117 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 Docker from 'dockerode'; +import fs from 'fs-extra'; +import { PassThrough } from 'stream'; +import { ContainerRunner, RunContainerOptions } from './ContainerRunner'; + +export type UserOptions = { + User?: string; +}; + +export class DockerContainerRunner implements ContainerRunner { + private readonly dockerClient: Docker; + + constructor({ dockerClient }: { dockerClient: Docker }) { + this.dockerClient = dockerClient; + } + + async runContainer({ + imageName, + command, + args, + logStream = new PassThrough(), + mountDirs = {}, + workingDir, + envVars = {}, + }: RunContainerOptions) { + // Show a better error message when Docker is unavailable. + try { + await this.dockerClient.ping(); + } catch (e) { + throw new Error( + `This operation requires Docker. Docker does not appear to be available. Docker.ping() failed with: ${e.message}`, + ); + } + + await new Promise((resolve, reject) => { + this.dockerClient.pull(imageName, {}, (err, stream) => { + if (err) return reject(err); + stream.pipe(logStream, { end: false }); + stream.on('end', () => resolve()); + stream.on('error', (error: Error) => reject(error)); + return undefined; + }); + }); + + const userOptions: UserOptions = {}; + if (process.getuid && process.getgid) { + // Files that are created inside the Docker container will be owned by + // root on the host system on non Mac systems, because of reasons. Mainly the fact that + // volume sharing is done using NFS on Mac and actual mounts in Linux world. + // So we set the user in the container as the same user and group id as the host. + // On Windows we don't have process.getuid nor process.getgid + userOptions.User = `${process.getuid()}:${process.getgid()}`; + } + + // Initialize volumes to mount based on mountDirs map + const Volumes: { [T: string]: object } = {}; + for (const containerDir of Object.values(mountDirs)) { + Volumes[containerDir] = {}; + } + + // Create bind volumes + const Binds: string[] = []; + for (const [hostDir, containerDir] of Object.entries(mountDirs)) { + // Need to use realpath here as Docker mounting does not like + // symlinks for binding volumes + const realHostDir = await fs.realpath(hostDir); + Binds.push(`${realHostDir}:${containerDir}`); + } + + // Create docker environment variables array + const Env = []; + for (const [key, value] of Object.entries(envVars)) { + Env.push(`${key}=${value}`); + } + + const [ + { Error: error, StatusCode: statusCode }, + ] = await this.dockerClient.run(imageName, args, logStream, { + Volumes, + HostConfig: { + Binds, + }, + ...(workingDir ? { WorkingDir: workingDir } : {}), + Entrypoint: command, + Env, + ...userOptions, + } as Docker.ContainerCreateOptions); + + if (error) { + throw new Error( + `Docker failed to run with the following error message: ${error}`, + ); + } + + if (statusCode !== 0) { + throw new Error( + `Docker container returned a non-zero exit code (${statusCode})`, + ); + } + } +} diff --git a/packages/backend-common/src/util/docker.ts b/packages/backend-common/src/util/docker.ts deleted file mode 100644 index 356d645465..0000000000 --- a/packages/backend-common/src/util/docker.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 Docker from 'dockerode'; -import fs from 'fs-extra'; -import { PassThrough, Writable } from 'stream'; - -export type UserOptions = { - User?: string; -}; - -export type RunDockerContainerOptions = { - imageName: string; - args: string[]; - logStream?: Writable; - dockerClient: Docker; - mountDirs?: Record; - workingDir?: string; - envVars?: Record; - createOptions?: Docker.ContainerCreateOptions; -}; - -/** - * - * @param options the options object - * @param options.imageName the image to run - * @param options.args the arguments to pass the container - * @param options.logStream the log streamer to capture log messages - * @param options.dockerClient the dockerClient to use - * @param options.mountDirs A map of host directories to mount on the container. - * Object Key: Path on host machine, Value: Path on Docker container - * @param options.workingDir Working dir in the container - * @param options.envVars Environment variables to set in the container. e.g. {'HOME': '/tmp'} - */ -export const runDockerContainer = async ({ - imageName, - args, - logStream = new PassThrough(), - dockerClient, - mountDirs = {}, - workingDir, - envVars = {}, - createOptions = {}, -}: RunDockerContainerOptions) => { - // Show a better error message when Docker is unavailable. - try { - await dockerClient.ping(); - } catch (e) { - throw new Error( - `This operation requires Docker. Docker does not appear to be available. Docker.ping() failed with: ${e.message}`, - ); - } - - await new Promise((resolve, reject) => { - dockerClient.pull(imageName, {}, (err, stream) => { - if (err) return reject(err); - stream.pipe(logStream, { end: false }); - stream.on('end', () => resolve()); - stream.on('error', (error: Error) => reject(error)); - return undefined; - }); - }); - - const userOptions: UserOptions = {}; - if (process.getuid && process.getgid) { - // Files that are created inside the Docker container will be owned by - // root on the host system on non Mac systems, because of reasons. Mainly the fact that - // volume sharing is done using NFS on Mac and actual mounts in Linux world. - // So we set the user in the container as the same user and group id as the host. - // On Windows we don't have process.getuid nor process.getgid - userOptions.User = `${process.getuid()}:${process.getgid()}`; - } - - // Initialize volumes to mount based on mountDirs map - const Volumes: { [T: string]: object } = {}; - for (const containerDir of Object.values(mountDirs)) { - Volumes[containerDir] = {}; - } - - // Create bind volumes - const Binds: string[] = []; - for (const [hostDir, containerDir] of Object.entries(mountDirs)) { - // Need to use realpath here as Docker mounting does not like - // symlinks for binding volumes - const realHostDir = await fs.realpath(hostDir); - Binds.push(`${realHostDir}:${containerDir}`); - } - - // Create docker environment variables array - const Env = []; - for (const [key, value] of Object.entries(envVars)) { - Env.push(`${key}=${value}`); - } - - const [{ Error: error, StatusCode: statusCode }] = await dockerClient.run( - imageName, - args, - logStream, - { - Volumes, - HostConfig: { - Binds, - }, - ...(workingDir ? { WorkingDir: workingDir } : {}), - Env, - ...userOptions, - ...createOptions, - }, - ); - - if (error) { - throw new Error( - `Docker failed to run with the following error message: ${error}`, - ); - } - - if (statusCode !== 0) { - throw new Error( - `Docker container returned a non-zero exit code (${statusCode})`, - ); - } - - return { error, statusCode }; -}; diff --git a/packages/backend-common/src/util/index.ts b/packages/backend-common/src/util/index.ts index 04715824b4..85c5436a2e 100644 --- a/packages/backend-common/src/util/index.ts +++ b/packages/backend-common/src/util/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export { runDockerContainer } from './docker'; +export type { ContainerRunner, RunContainerOptions } from './ContainerRunner'; +export { DockerContainerRunner } from './DockerContainerRunner'; diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index ee04e2bb6b..ac65632142 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,41 @@ # example-backend +## 0.2.27 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [e0bfd3d44] +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [b219821a0] +- Updated dependencies [69eefb5ae] +- Updated dependencies [f53fba29f] +- Updated dependencies [75c8cec39] +- Updated dependencies [227439a72] +- Updated dependencies [cdb3426e5] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/plugin-scaffolder-backend@0.11.0 + - @backstage/backend-common@0.7.0 + - @backstage/plugin-techdocs-backend@0.8.0 + - @backstage/plugin-catalog-backend@0.8.2 + - @backstage/plugin-kubernetes-backend@0.3.6 + - @backstage/plugin-proxy-backend@0.2.7 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + - example-app@0.2.27 + - @backstage/plugin-app-backend@0.3.12 + - @backstage/plugin-auth-backend@0.3.9 + - @backstage/plugin-badges-backend@0.1.3 + - @backstage/plugin-code-coverage-backend@0.1.4 + - @backstage/plugin-graphql-backend@0.1.7 + - @backstage/plugin-kafka-backend@0.2.4 + - @backstage/plugin-rollbar-backend@0.1.10 + - @backstage/plugin-search-backend@0.1.4 + - @backstage/plugin-todo-backend@0.1.4 + ## 0.2.25 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 440ceca8e5..6f70db7b19 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.25", + "version": "0.2.27", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,30 +27,30 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.6.3", - "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.5", - "@backstage/config": "^0.1.4", - "@backstage/plugin-app-backend": "^0.3.10", - "@backstage/plugin-auth-backend": "^0.3.7", - "@backstage/plugin-badges-backend": "^0.1.1", - "@backstage/plugin-catalog-backend": "^0.8.0", - "@backstage/plugin-code-coverage-backend": "^0.1.2", - "@backstage/plugin-graphql-backend": "^0.1.6", - "@backstage/plugin-kubernetes-backend": "^0.3.5", - "@backstage/plugin-kafka-backend": "^0.2.3", - "@backstage/plugin-proxy-backend": "^0.2.6", - "@backstage/plugin-rollbar-backend": "^0.1.9", - "@backstage/plugin-scaffolder-backend": "^0.10.0", - "@backstage/plugin-search-backend": "^0.1.3", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", + "@backstage/plugin-app-backend": "^0.3.12", + "@backstage/plugin-auth-backend": "^0.3.9", + "@backstage/plugin-badges-backend": "^0.1.3", + "@backstage/plugin-catalog-backend": "^0.8.2", + "@backstage/plugin-code-coverage-backend": "^0.1.4", + "@backstage/plugin-graphql-backend": "^0.1.7", + "@backstage/plugin-kubernetes-backend": "^0.3.6", + "@backstage/plugin-kafka-backend": "^0.2.4", + "@backstage/plugin-proxy-backend": "^0.2.7", + "@backstage/plugin-rollbar-backend": "^0.1.10", + "@backstage/plugin-scaffolder-backend": "^0.11.0", + "@backstage/plugin-search-backend": "^0.1.4", "@backstage/plugin-search-backend-node": "^0.1.3", - "@backstage/plugin-techdocs-backend": "^0.7.0", - "@backstage/plugin-todo-backend": "^0.1.3", + "@backstage/plugin-techdocs-backend": "^0.8.0", + "@backstage/plugin-todo-backend": "^0.1.4", "@gitbeaker/node": "^28.0.2", - "@octokit/rest": "^18.0.12", + "@octokit/rest": "^18.5.3", "azure-devops-node-api": "^10.1.1", "dockerode": "^3.2.1", - "example-app": "^0.2.25", + "example-app": "^0.2.27", "express": "^4.17.1", "express-promise-router": "^4.1.0", "knex": "^0.95.1", @@ -60,7 +60,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5" diff --git a/packages/backend/src/plugins/catalog.ts b/packages/backend/src/plugins/catalog.ts index e58d8cd3eb..7651836e2a 100644 --- a/packages/backend/src/plugins/catalog.ts +++ b/packages/backend/src/plugins/catalog.ts @@ -42,7 +42,7 @@ export default async function createPlugin( } = await builder.build(); // TODO(jhaals): run and manage in background. - processingEngine.start(); + await processingEngine.start(); return await createRouter({ entitiesCatalog, diff --git a/packages/backend/src/plugins/kubernetes.ts b/packages/backend/src/plugins/kubernetes.ts index 131c5c8eee..7906765533 100644 --- a/packages/backend/src/plugins/kubernetes.ts +++ b/packages/backend/src/plugins/kubernetes.ts @@ -15,12 +15,11 @@ */ import { createRouter } from '@backstage/plugin-kubernetes-backend'; -import { Router } from 'express'; import { PluginEnvironment } from '../types'; export default async function createPlugin({ logger, config, -}: PluginEnvironment): Promise { +}: PluginEnvironment) { return await createRouter({ logger, config }); } diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index fca3f79270..9c7c3c12f3 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -14,7 +14,10 @@ * limitations under the License. */ -import { SingleHostDiscovery } from '@backstage/backend-common'; +import { + DockerContainerRunner, + SingleHostDiscovery, +} from '@backstage/backend-common'; import { CatalogClient } from '@backstage/catalog-client'; import { CookieCutter, @@ -34,8 +37,11 @@ export default async function createPlugin({ database, reader, }: PluginEnvironment): Promise { - const cookiecutterTemplater = new CookieCutter(); - const craTemplater = new CreateReactAppTemplater(); + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + const cookiecutterTemplater = new CookieCutter({ containerRunner }); + const craTemplater = new CreateReactAppTemplater({ containerRunner }); const templaters = new Templaters(); templaters.register('cookiecutter', cookiecutterTemplater); @@ -44,8 +50,6 @@ export default async function createPlugin({ const preparers = await Preparers.fromConfig(config, { logger }); const publishers = await Publishers.fromConfig(config, { logger }); - const dockerClient = new Docker(); - const discovery = SingleHostDiscovery.fromConfig(config); const catalogClient = new CatalogClient({ discoveryApi: discovery }); @@ -55,7 +59,6 @@ export default async function createPlugin({ publishers, logger, config, - dockerClient, database, catalogClient, reader, diff --git a/packages/backend/src/plugins/techdocs.ts b/packages/backend/src/plugins/techdocs.ts index d0387cd15d..dbdc455e09 100644 --- a/packages/backend/src/plugins/techdocs.ts +++ b/packages/backend/src/plugins/techdocs.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { DockerContainerRunner } from '@backstage/backend-common'; import { createRouter, Generators, @@ -35,9 +36,14 @@ export default async function createPlugin({ reader, }); + // Docker client (conditionally) used by the generators, based on techdocs.generators config. + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + // Generators are used for generating documentation sites. const generators = await Generators.fromConfig(config, { logger, + containerRunner, }); // Publisher is used for @@ -51,14 +57,10 @@ export default async function createPlugin({ // checks if the publisher is working and logs the result await publisher.getReadiness(); - // Docker client (conditionally) used by the generators, based on techdocs.generators config. - const dockerClient = new Docker(); - return await createRouter({ preparers, generators, publisher, - dockerClient, logger, config, discovery, diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 0076202c64..e5eb166ec2 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/catalog-client +## 0.3.11 + +### Patch Changes + +- d1b1306d9: Allow `filter` parameter to be specified multiple times +- Updated dependencies [d8b81fd28] + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.3.10 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 91845e56ab..f9638dee0d 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "0.3.10", + "version": "0.3.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,13 +29,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "cross-fetch": "^3.0.6" }, "devDependencies": { - "@backstage/cli": "^0.6.8", + "@backstage/cli": "^0.6.10", "@types/jest": "^26.0.7", "msw": "^0.21.2" }, diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 445da3afab..e289c5a0b3 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/catalog-model +## 0.7.8 + +### Patch Changes + +- d8b81fd28: Bump `json-schema` dependency from `0.2.5` to `0.3.0`. +- Updated dependencies [d8b81fd28] + - @backstage/config@0.1.5 + ## 0.7.7 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 941877aec5..ac3b046a32 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.7.7", + "version": "0.7.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.3", + "@backstage/config": "^0.1.5", "@types/json-schema": "^7.0.5", "@types/yup": "^0.29.8", "ajv": "^7.0.3", @@ -39,7 +39,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.8", + "@backstage/cli": "^0.6.10", "@types/express": "^4.17.6", "@types/jest": "^26.0.7", "@types/lodash": "^4.14.151", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index af452b3c9d..996513f60e 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/cli +## 0.6.10 + +### Patch Changes + +- f65adcde7: Fix some transitive dependency warnings in yarn +- fc79a6dd3: Added lax option to backstage-cli app:build command +- d8b81fd28: Bump `json-schema` dependency from `0.2.5` to `0.3.0`. +- Updated dependencies [d8b81fd28] + - @backstage/config-loader@0.6.1 + - @backstage/config@0.1.5 + ## 0.6.9 ### Patch Changes diff --git a/packages/cli/README.md b/packages/cli/README.md index cd6ad8094e..eb5e0e461b 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -7,13 +7,13 @@ This package provides a CLI for developing Backstage plugins and apps. Install the package via npm or Yarn: ```sh -$ npm install --save @backstage/cli +npm install --save @backstage/cli ``` or ```sh -$ yarn add @backstage/cli +yarn add @backstage/cli ``` ## Development diff --git a/packages/cli/package.json b/packages/cli/package.json index 1110efefe6..4bed089b98 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.6.9", + "version": "0.6.10", "private": false, "publishConfig": { "access": "public" @@ -31,8 +31,8 @@ "@babel/core": "^7.4.4", "@babel/plugin-transform-modules-commonjs": "^7.4.4", "@backstage/cli-common": "^0.1.1", - "@backstage/config": "^0.1.4", - "@backstage/config-loader": "^0.6.0", + "@backstage/config": "^0.1.5", + "@backstage/config-loader": "^0.6.1", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", @@ -86,6 +86,7 @@ "lodash": "^4.17.19", "mini-css-extract-plugin": "^0.9.0", "ora": "^5.3.0", + "postcss": "^8.1.0", "raw-loader": "^4.0.1", "react": "^16.0.0", "react-dev-utils": "^11.0.4", @@ -116,12 +117,12 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.6.3", - "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.6", + "@backstage/backend-common": "^0.7.0", + "@backstage/config": "^0.1.5", + "@backstage/core": "^0.7.8", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", "@types/fs-extra": "^9.0.1", diff --git a/packages/cli/src/commands/app/build.ts b/packages/cli/src/commands/app/build.ts index 21189e9427..0da4112646 100644 --- a/packages/cli/src/commands/app/build.ts +++ b/packages/cli/src/commands/app/build.ts @@ -30,6 +30,7 @@ export default async (cmd: Command) => { ...(await loadCliConfig({ args: cmd.config, fromPackage: name, + mockEnv: cmd.lax, })), }); }; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index c770239464..ae39941106 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -29,6 +29,7 @@ export function registerCommands(program: CommanderStatic) { .command('app:build') .description('Build an app for a production release') .option('--stats', 'Write bundle stats to output directory') + .option('--lax', 'Do not require environment variables to be set') .option(...configOption) .action(lazy(() => import('./app/build').then(m => m.default))); diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index cb8ef123ce..d6b1c9a751 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/config-loader +## 0.6.1 + +### Patch Changes + +- d8b81fd28: Bump `json-schema` dependency from `0.2.5` to `0.3.0`. +- Updated dependencies [d8b81fd28] + - @backstage/config@0.1.5 + ## 0.6.0 ### Minor Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 55d86314af..16d7923d0b 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.6.0", + "version": "0.6.1", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/cli-common": "^0.1.1", - "@backstage/config": "^0.1.1", + "@backstage/config": "^0.1.5", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", "fs-extra": "^9.0.0", diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index c190207547..5cababf94e 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/config +## 0.1.5 + +### Patch Changes + +- d8b81fd28: Bump `json-schema` dependency from `0.2.5` to `0.3.0`. + ## 0.1.4 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index 8f08a5dcfc..400e4548cd 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config", "description": "Config API used by Backstage core, backend, and CLI", - "version": "0.1.4", + "version": "0.1.5", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-api/README.md b/packages/core-api/README.md index f7b8b6c337..a4325a9c5d 100644 --- a/packages/core-api/README.md +++ b/packages/core-api/README.md @@ -7,13 +7,13 @@ This package provides the core API used by Backstage plugins and apps. Do not install this package directly, it is reexported by `@backstage/core`. Install that instead: ```sh -$ npm install --save @backstage/core +npm install --save @backstage/core ``` or ```sh -$ yarn add @backstage/core +yarn add @backstage/core ``` ## Documentation diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 974dcd3b91..a215c7cb8e 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core +## 0.7.8 + +### Patch Changes + +- f65adcde7: Fix some transitive dependency warnings in yarn +- 80888659b: Bump react-hook-form version to be the same for the entire project. +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] + - @backstage/theme@0.2.7 + - @backstage/config@0.1.5 + ## 0.7.7 ### Patch Changes diff --git a/packages/core/README.md b/packages/core/README.md index 6d8519d46f..0f827d6074 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -7,13 +7,13 @@ This package provides the core API used by Backstage plugins and apps. Install the package via npm or Yarn: ```sh -$ npm install --save @backstage/core +npm install --save @backstage/core ``` or ```sh -$ yarn add @backstage/core +yarn add @backstage/core ``` ## Documentation diff --git a/packages/core/package.json b/packages/core/package.json index 59391e122a..4da981d064 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.7.7", + "version": "0.7.8", "private": false, "publishConfig": { "access": "public", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.4", + "@backstage/config": "^0.1.5", "@backstage/core-api": "^0.2.17", "@backstage/errors": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -48,6 +48,7 @@ "d3-shape": "^2.0.0", "d3-zoom": "^2.0.0", "dagre": "^0.8.5", + "history": "^5.0.0", "immer": "^9.0.1", "lodash": "^4.17.15", "material-table": "^1.69.1", @@ -57,7 +58,7 @@ "react": "^16.12.0", "react-dom": "^16.12.0", "react-helmet": "6.1.0", - "react-hook-form": "^6.6.0", + "react-hook-form": "^6.15.4", "react-markdown": "^5.0.2", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", @@ -69,7 +70,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 430e7f003a..968c798625 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,162 @@ # @backstage/create-app +## 0.3.21 + +### Patch Changes + +- 38ca05168: The default `@octokit/rest` dependency was bumped to `"^18.5.3"`. +- e0bfd3d44: The `scaffolder-backend` and `techdocs-backend` plugins have been updated. + In order to update, you need to apply the following changes to your existing backend application: + + `@backstage/plugin-techdocs-backend`: + + ```diff + // packages/backend/src/plugin/techdocs.ts + + + import { DockerContainerRunner } from '@backstage/backend-common'; + + // ... + + export default async function createPlugin({ + logger, + config, + discovery, + reader, + }: PluginEnvironment): Promise { + // Preparers are responsible for fetching source files for documentation. + const preparers = await Preparers.fromConfig(config, { + logger, + reader, + }); + + + // Docker client (conditionally) used by the generators, based on techdocs.generators config. + + const dockerClient = new Docker(); + + const containerRunner = new DockerContainerRunner({ dockerClient }); + + // Generators are used for generating documentation sites. + const generators = await Generators.fromConfig(config, { + logger, + + containerRunner, + }); + + // Publisher is used for + // 1. Publishing generated files to storage + // 2. Fetching files from storage and passing them to TechDocs frontend. + const publisher = await Publisher.fromConfig(config, { + logger, + discovery, + }); + + // checks if the publisher is working and logs the result + await publisher.getReadiness(); + + - // Docker client (conditionally) used by the generators, based on techdocs.generators config. + - const dockerClient = new Docker(); + + return await createRouter({ + preparers, + generators, + publisher, + - dockerClient, + logger, + config, + discovery, + }); + } + ``` + + `@backstage/plugin-scaffolder-backend`: + + ```diff + // packages/backend/src/plugin/scaffolder.ts + + - import { SingleHostDiscovery } from '@backstage/backend-common'; + + import { + + DockerContainerRunner, + + SingleHostDiscovery, + + } from '@backstage/backend-common'; + + + export default async function createPlugin({ + logger, + config, + database, + reader, + }: PluginEnvironment): Promise { + + const dockerClient = new Docker(); + + const containerRunner = new DockerContainerRunner({ dockerClient }); + + + const cookiecutterTemplater = new CookieCutter({ containerRunner }); + - const cookiecutterTemplater = new CookieCutter(); + + const craTemplater = new CreateReactAppTemplater({ containerRunner }); + - const craTemplater = new CreateReactAppTemplater(); + const templaters = new Templaters(); + + templaters.register('cookiecutter', cookiecutterTemplater); + templaters.register('cra', craTemplater); + + const preparers = await Preparers.fromConfig(config, { logger }); + const publishers = await Publishers.fromConfig(config, { logger }); + + - const dockerClient = new Docker(); + + const discovery = SingleHostDiscovery.fromConfig(config); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + preparers, + templaters, + publishers, + logger, + config, + - dockerClient, + database, + catalogClient, + reader, + }); + } + ``` + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [e0bfd3d44] +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [f65adcde7] +- Updated dependencies [80888659b] +- Updated dependencies [b219821a0] +- Updated dependencies [7b8272fb7] +- Updated dependencies [8aedbb4af] +- Updated dependencies [fc79a6dd3] +- Updated dependencies [69eefb5ae] +- Updated dependencies [75c8cec39] +- Updated dependencies [b2e2ec753] +- Updated dependencies [227439a72] +- Updated dependencies [9314a8592] +- Updated dependencies [2e05277e0] +- Updated dependencies [4075c6367] +- Updated dependencies [cdb3426e5] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/plugin-scaffolder-backend@0.11.0 + - @backstage/backend-common@0.7.0 + - @backstage/plugin-techdocs-backend@0.8.0 + - @backstage/plugin-catalog-import@0.5.5 + - @backstage/plugin-github-actions@0.4.5 + - @backstage/cli@0.6.10 + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-backend@0.8.2 + - @backstage/theme@0.2.7 + - @backstage/plugin-tech-radar@0.3.10 + - @backstage/plugin-scaffolder@0.9.3 + - @backstage/plugin-techdocs@0.9.1 + - @backstage/plugin-proxy-backend@0.2.7 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + - @backstage/plugin-app-backend@0.3.12 + - @backstage/plugin-auth-backend@0.3.9 + - @backstage/plugin-rollbar-backend@0.1.10 + ## 0.3.20 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 071a0a5ebd..169e9a8637 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.3.20", + "version": "0.3.21", "private": false, "publishConfig": { "access": "public" diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs index 138bcaec8d..e2b5b04a60 100644 --- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs @@ -29,7 +29,7 @@ "@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}", "@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}", "@gitbeaker/node": "^28.0.2", - "@octokit/rest": "^18.0.12", + "@octokit/rest": "^18.5.3", "dockerode": "^3.2.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts index a3416c5080..333ffa11df 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/scaffolder.ts @@ -1,4 +1,7 @@ -import { SingleHostDiscovery } from '@backstage/backend-common'; +import { + DockerContainerRunner, + SingleHostDiscovery, +} from '@backstage/backend-common'; import { CatalogClient } from '@backstage/catalog-client'; import { CookieCutter, @@ -6,7 +9,7 @@ import { createRouter, Preparers, Publishers, - Templaters + Templaters, } from '@backstage/plugin-scaffolder-backend'; import Docker from 'dockerode'; import { Router } from 'express'; @@ -18,8 +21,11 @@ export default async function createPlugin({ database, reader, }: PluginEnvironment): Promise { - const cookiecutterTemplater = new CookieCutter(); - const craTemplater = new CreateReactAppTemplater(); + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + const cookiecutterTemplater = new CookieCutter({ containerRunner }); + const craTemplater = new CreateReactAppTemplater({ containerRunner }); const templaters = new Templaters(); templaters.register('cookiecutter', cookiecutterTemplater); @@ -28,8 +34,6 @@ export default async function createPlugin({ const preparers = await Preparers.fromConfig(config, { logger }); const publishers = await Publishers.fromConfig(config, { logger }); - const dockerClient = new Docker(); - const discovery = SingleHostDiscovery.fromConfig(config); const catalogClient = new CatalogClient({ discoveryApi: discovery }); @@ -39,9 +43,8 @@ export default async function createPlugin({ publishers, logger, config, - dockerClient, database, catalogClient, - reader + reader, }); } diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts index 231a7e7fd7..906d86d4a2 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts @@ -1,9 +1,9 @@ +import { DockerContainerRunner } from '@backstage/backend-common'; import { createRouter, - - Generators, Preparers, - - Publisher + Generators, + Preparers, + Publisher, } from '@backstage/plugin-techdocs-backend'; import Docker from 'dockerode'; import { Router } from 'express'; @@ -21,9 +21,14 @@ export default async function createPlugin({ reader, }); + // Docker client (conditionally) used by the generators, based on techdocs.generators config. + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + // Generators are used for generating documentation sites. const generators = await Generators.fromConfig(config, { logger, + containerRunner, }); // Publisher is used for @@ -37,14 +42,10 @@ export default async function createPlugin({ // checks if the publisher is working and logs the result await publisher.getReadiness(); - // Docker client (conditionally) used by the generators, based on techdocs.generators config. - const dockerClient = new Docker(); - return await createRouter({ preparers, generators, publisher, - dockerClient, logger, config, discovery, diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md index 4b08a5ca6d..85d961ad0b 100644 --- a/packages/dev-utils/README.md +++ b/packages/dev-utils/README.md @@ -9,13 +9,13 @@ This package provides utilities that help in developing plugins for Backstage, l Install the package via npm or Yarn: ```sh -$ npm install --save-dev @backstage/dev-utils +npm install --save-dev @backstage/dev-utils ``` or ```sh -$ yarn add -D @backstage/dev-utils +yarn add -D @backstage/dev-utils ``` ## Documentation diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 6c65643409..d892523d25 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/integration +## 0.5.2 + +### Patch Changes + +- 38ca05168: The default `@octokit/rest` dependency was bumped to `"^18.5.3"`. +- Updated dependencies [d8b81fd28] + - @backstage/config@0.1.5 + ## 0.5.1 ### Patch Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index dcfae4ea05..441b42e584 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "0.5.1", + "version": "0.5.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,16 +29,16 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.2", + "@backstage/config": "^0.1.5", "cross-fetch": "^3.0.6", "git-url-parse": "^11.4.4", - "@octokit/rest": "^18.0.12", - "@octokit/auth-app": "^2.10.5", + "@octokit/rest": "^18.5.3", + "@octokit/auth-app": "^3.4.0", "luxon": "^1.25.0" }, "devDependencies": { - "@backstage/cli": "^0.6.7", - "@backstage/config-loader": "^0.6.0", + "@backstage/cli": "^0.6.10", + "@backstage/config-loader": "^0.6.1", "@backstage/test-utils": "^0.1.10", "@types/jest": "^26.0.7", "@types/luxon": "^1.25.0", diff --git a/packages/storybook/package.json b/packages/storybook/package.json index 01aabac0de..4a4c1431c5 100644 --- a/packages/storybook/package.json +++ b/packages/storybook/package.json @@ -14,7 +14,9 @@ ] }, "dependencies": { - "@backstage/theme": "^0.2.0" + "@backstage/theme": "^0.2.0", + "react": "^16.12.0", + "react-dom": "^16.12.0" }, "devDependencies": { "@storybook/addon-actions": "^6.1.11", diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index 1788502e45..644a3ebb5f 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/techdocs-common +## 0.6.0 + +### Minor Changes + +- e0bfd3d44: Migrate the package to use the `ContainerRunner` interface instead of `runDockerContainer(…)`. + It also no longer provides the `ContainerRunner` as an input to the `GeneratorBase#run(…)` function, but expects it as a constructor parameter instead. + + If you use the `TechdocsGenerator` you need to update the usage: + + ```diff + + const containerRunner = new DockerContainerRunner({ dockerClient }); + + - const generator = new TechdocsGenerator(logger, config); + + const techdocsGenerator = new TechdocsGenerator({ + + logger, + + containerRunner, + + config, + + }); + + await this.generator.run({ + inputDir: preparedDir, + outputDir, + - dockerClient: this.dockerClient, + parsedLocationAnnotation, + etag: newEtag, + }); + ``` + +### Patch Changes + +- e9e56b01a: Adding optional config to enable S3-like API for tech-docs using s3ForcePathStyle option. + This allows providers like LocalStack, Minio and Wasabi (+possibly others) to be used to host tech docs. +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/integration@0.5.2 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.5.1 ### Patch Changes diff --git a/packages/techdocs-common/api-report.md b/packages/techdocs-common/api-report.md index a53fb9e681..3fff030eee 100644 --- a/packages/techdocs-common/api-report.md +++ b/packages/techdocs-common/api-report.md @@ -6,7 +6,7 @@ import { AzureIntegrationConfig } from '@backstage/integration'; import { Config } from '@backstage/config'; -import Docker from 'dockerode'; +import { ContainerRunner } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import express from 'express'; @@ -50,8 +50,9 @@ export type GeneratorBuilder = { // @public (undocumented) export class Generators implements GeneratorBuilder { // (undocumented) - static fromConfig(config: Config, { logger }: { + static fromConfig(config: Config, { logger, containerRunner, }: { logger: Logger; + containerRunner: ContainerRunner; }): Promise; // (undocumented) get(entity: Entity): GeneratorBase; @@ -151,9 +152,13 @@ export type RemoteProtocol = 'url' | 'dir' | 'github' | 'gitlab' | 'file' | 'azu // @public (undocumented) export class TechdocsGenerator implements GeneratorBase { - constructor(logger: Logger, config: Config); + constructor({ logger, containerRunner, config, }: { + logger: Logger; + containerRunner: ContainerRunner; + config: Config; + }); // (undocumented) - run({ inputDir, outputDir, dockerClient, parsedLocationAnnotation, etag, }: GeneratorRunOptions): Promise; + run({ inputDir, outputDir, parsedLocationAnnotation, etag, }: GeneratorRunOptions): Promise; } // @public diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index e749b33919..f2d19ba8c5 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.5.1", + "version": "0.6.0", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -38,17 +38,15 @@ "dependencies": { "@azure/identity": "^1.2.2", "@azure/storage-blob": "^12.4.0", - "@backstage/backend-common": "^0.6.0", - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.1", + "@backstage/integration": "^0.5.2", "@google-cloud/storage": "^5.6.0", - "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", "aws-sdk": "^2.840.0", "cross-fetch": "^3.0.6", - "dockerode": "^3.2.1", "express": "^4.17.1", "fs-extra": "^9.0.1", "git-url-parse": "^11.4.4", @@ -62,7 +60,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.6.8", + "@backstage/cli": "^0.6.10", "@types/fs-extra": "^9.0.5", "@types/git-url-parse": "^9.0.0", "@types/js-yaml": "^4.0.0", diff --git a/packages/techdocs-common/src/stages/generate/generators.test.ts b/packages/techdocs-common/src/stages/generate/generators.test.ts index 032aafbf49..610a3e8ad5 100644 --- a/packages/techdocs-common/src/stages/generate/generators.test.ts +++ b/packages/techdocs-common/src/stages/generate/generators.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; +import { ContainerRunner, getVoidLogger } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { Generators } from './generators'; import { TechdocsGenerator } from './techdocs'; @@ -30,6 +30,10 @@ const mockEntity = { }; describe('generators', () => { + const containerRunner: jest.Mocked = { + runContainer: jest.fn(), + }; + it('should return error if no generator is registered', async () => { const generators = new Generators(); @@ -40,7 +44,11 @@ describe('generators', () => { it('should return correct registered generator', async () => { const generators = new Generators(); - const techdocs = new TechdocsGenerator(logger, new ConfigReader({})); + const techdocs = new TechdocsGenerator({ + logger, + containerRunner, + config: new ConfigReader({}), + }); generators.register('techdocs', techdocs); diff --git a/packages/techdocs-common/src/stages/generate/generators.ts b/packages/techdocs-common/src/stages/generate/generators.ts index 57c4017b15..4a3af72931 100644 --- a/packages/techdocs-common/src/stages/generate/generators.ts +++ b/packages/techdocs-common/src/stages/generate/generators.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ContainerRunner } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { Logger } from 'winston'; @@ -30,11 +31,18 @@ export class Generators implements GeneratorBuilder { static async fromConfig( config: Config, - { logger }: { logger: Logger }, + { + logger, + containerRunner, + }: { logger: Logger; containerRunner: ContainerRunner }, ): Promise { const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(logger, config); + const techdocsGenerator = new TechdocsGenerator({ + logger, + containerRunner, + config, + }); generators.register('techdocs', techdocsGenerator); return generators; diff --git a/packages/techdocs-common/src/stages/generate/techdocs.ts b/packages/techdocs-common/src/stages/generate/techdocs.ts index 0dfa952a17..1a6b0a581c 100644 --- a/packages/techdocs-common/src/stages/generate/techdocs.ts +++ b/packages/techdocs-common/src/stages/generate/techdocs.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { runDockerContainer } from '@backstage/backend-common'; +import { ContainerRunner } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import path from 'path'; import { PassThrough } from 'stream'; @@ -48,20 +48,29 @@ const createStream = (): [string[], PassThrough] => { export class TechdocsGenerator implements GeneratorBase { private readonly logger: Logger; + private readonly containerRunner: ContainerRunner; private readonly options: TechdocsGeneratorOptions; - constructor(logger: Logger, config: Config) { + constructor({ + logger, + containerRunner, + config, + }: { + logger: Logger; + containerRunner: ContainerRunner; + config: Config; + }) { this.logger = logger; this.options = { runGeneratorIn: config.getOptionalString('techdocs.generators.techdocs') ?? 'docker', }; + this.containerRunner = containerRunner; } public async run({ inputDir, outputDir, - dockerClient, parsedLocationAnnotation, etag, }: GeneratorRunOptions): Promise { @@ -100,7 +109,7 @@ export class TechdocsGenerator implements GeneratorBase { ); break; case 'docker': - await runDockerContainer({ + await this.containerRunner.runContainer({ imageName: 'spotify/techdocs', args: ['build', '-d', '/output'], logStream, @@ -109,7 +118,6 @@ export class TechdocsGenerator implements GeneratorBase { // Set the home directory inside the container as something that applications can // write to, otherwise they will just fail trying to write to / envVars: { HOME: '/tmp' }, - dockerClient, }); this.logger.info( `Successfully generated docs from ${inputDir} into ${outputDir} using techdocs-container`, diff --git a/packages/techdocs-common/src/stages/generate/types.ts b/packages/techdocs-common/src/stages/generate/types.ts index fda41ed442..249b809592 100644 --- a/packages/techdocs-common/src/stages/generate/types.ts +++ b/packages/techdocs-common/src/stages/generate/types.ts @@ -14,7 +14,6 @@ * limitations under the License. */ import { Entity } from '@backstage/catalog-model'; -import Docker from 'dockerode'; import { Writable } from 'stream'; import { ParsedLocationAnnotation } from '../../helpers'; @@ -23,7 +22,6 @@ import { ParsedLocationAnnotation } from '../../helpers'; * * @param {string} inputDir The directory of the uncompiled documentation, with the values from the frontend * @param {string} outputDir Directory to store generated docs in. Usually - a newly created temporary directory. - * @param {Docker} dockerClient A docker client to run any generator on top of your directory * @param {ParsedLocationAnnotation} parsedLocationAnnotation backstage.io/techdocs-ref annotation of an entity * @param {string} etag A unique identifier for the prepared tree e.g. commit SHA. If provided it will be stored in techdocs_metadata.json. * @param {Writable} [logStream] A dedicated log stream @@ -31,7 +29,6 @@ import { ParsedLocationAnnotation } from '../../helpers'; export type GeneratorRunOptions = { inputDir: string; outputDir: string; - dockerClient: Docker; parsedLocationAnnotation?: ParsedLocationAnnotation; etag?: string; logStream?: Writable; diff --git a/packages/test-utils/README.md b/packages/test-utils/README.md index 0f95d4ab26..ace7339733 100644 --- a/packages/test-utils/README.md +++ b/packages/test-utils/README.md @@ -7,13 +7,13 @@ This package provides utilities that can be used to test plugins and apps for Ba Install the package via npm or Yarn: ```sh -$ npm install --save-dev @backstage/test-utils +npm install --save-dev @backstage/test-utils ``` or ```sh -$ yarn add -D @backstage/test-utils +yarn add -D @backstage/test-utils ``` ## Documentation diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 22964d6b25..e70364016d 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/theme +## 0.2.7 + +### Patch Changes + +- 7b8272fb7: Remove extra bottom padding in InfoCard content + ## 0.2.6 ### Patch Changes diff --git a/packages/theme/README.md b/packages/theme/README.md index 9855d6730d..de94ff1623 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -7,13 +7,13 @@ This package provides the extended Material UI Theme(s) that power Backstage. Install the package via npm or Yarn: ```sh -$ npm install --save @backstage/theme +npm install --save @backstage/theme ``` or ```sh -$ yarn add @backstage/theme +yarn add @backstage/theme ``` ## Documentation diff --git a/packages/theme/package.json b/packages/theme/package.json index 87821b6751..f93c793ff8 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/theme", "description": "material-ui theme for use with Backstage.", - "version": "0.2.6", + "version": "0.2.7", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "@material-ui/core": "^4.11.0" }, "devDependencies": { - "@backstage/cli": "^0.6.9" + "@backstage/cli": "^0.6.10" }, "files": [ "dist" diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/baseTheme.ts index e7bb7e58b6..4f5627eec9 100644 --- a/packages/theme/src/baseTheme.ts +++ b/packages/theme/src/baseTheme.ts @@ -257,6 +257,9 @@ export function createThemeOverrides(theme: BackstageTheme): Overrides { // etc) end up at the bottom of the card instead of just below the body // contents. flexGrow: 1, + '&:last-child': { + paddingBottom: undefined, + }, }, }, MuiCardActions: { diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md index dac4b6ff4d..60d8986d9b 100644 --- a/plugins/api-docs/README.md +++ b/plugins/api-docs/README.md @@ -28,15 +28,15 @@ To link that a component provides or consumes an API, see the [`providesApis`](h 1. Install the API docs plugin ```bash -# packages/app - +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-api-docs ``` 2. Add the `ApiExplorerPage` extension to the app: ```tsx -// packages/app/src/App.tsx +// In packages/app/src/App.tsx import { ApiExplorerPage } from '@backstage/plugin-api-docs'; @@ -56,7 +56,7 @@ import { } from '@backstage/plugin-api-docs'; const apiPage = ( - + @@ -80,7 +80,7 @@ const apiPage = ( - + ); // ... diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 5c9f9ca7ee..e8e8bb8774 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -31,9 +31,9 @@ "dependencies": { "@asyncapi/react-component": "^0.23.0", "@backstage/catalog-model": "^0.7.7", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -49,7 +49,7 @@ "swagger-ui-react": "^3.37.2" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx index b157e898ca..fa956cd042 100644 --- a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.test.tsx @@ -96,14 +96,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'API', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/api-docs/src/components/ApisCards/HasApisCard.test.tsx b/plugins/api-docs/src/components/ApisCards/HasApisCard.test.tsx index 5db3ec2b48..e721f6e918 100644 --- a/plugins/api-docs/src/components/ApisCards/HasApisCard.test.tsx +++ b/plugins/api-docs/src/components/ApisCards/HasApisCard.test.tsx @@ -96,14 +96,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'API', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx index bb9b2e60d9..67d95605e4 100644 --- a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.test.tsx @@ -96,14 +96,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'API', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'API', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx index 7944b3f90a..104b360ff6 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ConsumingComponentsCard.test.tsx @@ -101,14 +101,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'Component', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx index ab1751b9ba..c7c6bacc84 100644 --- a/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx +++ b/plugins/api-docs/src/components/ComponentsCards/ProvidingComponentsCard.test.tsx @@ -101,14 +101,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'Component', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 85fab4455d..73fa9fc3b1 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-app-backend +## 0.3.12 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/config-loader@0.6.1 + - @backstage/config@0.1.5 + ## 0.3.11 ### Patch Changes diff --git a/plugins/app-backend/README.md b/plugins/app-backend/README.md index 2dc302accb..a35cecfb9c 100644 --- a/plugins/app-backend/README.md +++ b/plugins/app-backend/README.md @@ -7,7 +7,9 @@ This backend plugin can be installed to serve static content of a Backstage app. Add both this package and your local frontend app package as dependencies to your backend, for example ```bash -yarn add @backstage/plugin-app-backend example-app +# From your Backstage root directory +cd packages/backend +yarn add @backstage/plugin-app-backend app ``` By adding the app package as a dependency we ensure that it is built as part of the backend, and that it can be resolved at runtime. diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index c6a1dd5a6b..84fe743550 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.3.11", + "version": "0.3.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.2", - "@backstage/config-loader": "^0.6.0", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/config-loader": "^0.6.1", + "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -40,7 +40,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.7", + "@backstage/cli": "^0.6.10", "@types/supertest": "^2.0.8", "msw": "^0.20.5", "supertest": "^6.1.3" diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 47616b68c7..e51942f742 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-auth-backend +## 0.3.9 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/backend-common@0.7.0 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + ## 0.3.8 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 4af8a1736d..41a47c7788 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.3.8", + "version": "0.3.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,10 +29,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.2", - "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.6", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "@backstage/test-utils": "^0.1.10", "@types/express": "^4.17.6", @@ -68,7 +68,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.7", + "@backstage/cli": "^0.6.10", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 69785a645f..abbc4e70bd 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-badges-backend +## 0.1.3 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/backend-common@0.7.0 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + ## 0.1.2 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index eb2a93fe67..347f8b9219 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-badges-backend", - "version": "0.1.2", + "version": "0.1.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,10 +30,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.2", - "@backstage/catalog-client": "^0.3.6", - "@backstage/catalog-model": "^0.7.6", - "@backstage/config": "^0.1.3", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "@types/express": "^4.17.6", "badge-maker": "^3.3.0", @@ -45,7 +45,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.7", + "@backstage/cli": "^0.6.10", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 8f7b5865a1..7078b9627e 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -21,10 +21,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.6", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.3", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,7 +34,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/bitrise/README.md b/plugins/bitrise/README.md index 411db3198e..1add03df23 100644 --- a/plugins/bitrise/README.md +++ b/plugins/bitrise/README.md @@ -8,9 +8,9 @@ Welcome to the Bitrise plugin! ## Installation ```sh -# The plugin must be added in the app package -$ cd packages/app -$ yarn add @backstage/plugin-bitrise +# From your Backstage root directory +cd packages/app +yarn add @backstage/plugin-bitrise ``` Bitrise Plugin exposes an entity tab component named `EntityBitriseContent`. You can include it in the @@ -22,7 +22,7 @@ import { EntityBitriseContent } from '@backstage/plugin-bitrise'; // Farther down at the website declaration const websiteEntityPage = ( - + {/* Place the following section where you want the tab to appear */} diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 950392f732..cd0564dc41 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -21,9 +21,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.2", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -37,7 +37,7 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index ed87e4dbc5..8d40d7c4bb 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend +## 0.8.2 + +### Patch Changes + +- b219821a0: Expose `BitbucketRepositoryParser` introduced in [#5295](https://github.com/backstage/backstage/pull/5295) +- 227439a72: Add support for non-organization accounts in GitHub Discovery +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/integration@0.5.2 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.8.1 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 7649067a44..c8e17bb943 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.8.1", + "version": "0.8.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,11 +30,11 @@ }, "dependencies": { "@azure/msal-node": "^1.0.0-beta.3", - "@backstage/backend-common": "^0.6.3", - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.1", + "@backstage/integration": "^0.5.2", "@backstage/plugin-search-backend-node": "^0.1.4", "@backstage/search-common": "^0.1.1", "@octokit/graphql": "^4.5.8", @@ -63,7 +63,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/test-utils": "^0.1.9", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", @@ -73,7 +73,8 @@ "@types/yup": "^0.29.8", "msw": "^0.21.2", "sqlite3": "^5.0.0", - "supertest": "^6.1.3" + "supertest": "^6.1.3", + "wait-for-expect": "^3.0.2" }, "files": [ "dist", diff --git a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts index 73f4281a51..1f01b77f42 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.test.ts @@ -17,21 +17,73 @@ import { getVoidLogger } from '@backstage/backend-common'; import { BitbucketDiscoveryProcessor } from './BitbucketDiscoveryProcessor'; import { ConfigReader } from '@backstage/config'; import { LocationSpec } from '@backstage/catalog-model'; -import { - BitbucketClient, - BitbucketRepositoryParser, - PagedResponse, -} from './bitbucket'; +import { BitbucketRepositoryParser, PagedResponse } from './bitbucket'; import { results } from './index'; +import { RequestHandler, rest } from 'msw'; +import { setupServer } from 'msw/node'; -function pagedResponse(values: any): PagedResponse { - return { - values: values, - isLastPage: true, - } as PagedResponse; +const server = setupServer(); + +function setupStubs(projects: any[]) { + function pagedResponse(values: any): PagedResponse { + return { + values: values, + isLastPage: true, + } as PagedResponse; + } + + function stubbedProject( + project: string, + repos: string[], + ): RequestHandler { + return rest.get( + `https://bitbucket.mycompany.com/api/rest/1.0/projects/${project}/repos`, + (_, res, ctx) => { + const response = []; + for (const repo of repos) { + response.push({ + slug: repo, + links: { + self: [ + { + href: `https://bitbucket.mycompany.com/projects/${project}/repos/${repo}/browse`, + }, + ], + }, + }); + } + return res(ctx.json(pagedResponse(response))); + }, + ); + } + + server.use( + rest.get( + `https://bitbucket.mycompany.com/api/rest/1.0/projects`, + (_, res, ctx) => { + return res( + ctx.json( + pagedResponse( + projects.map(p => { + return { key: p.key }; + }), + ), + ), + ); + }, + ), + ); + + for (const project of projects) { + server.use(stubbedProject(project.key, project.repos)); + } } describe('BitbucketDiscoveryProcessor', () => { + beforeAll(() => server.listen()); + afterEach(() => server.resetHandlers()); + afterAll(() => server.close()); + afterEach(() => jest.resetAllMocks()); describe('reject unrelated entries', () => { @@ -81,58 +133,29 @@ describe('BitbucketDiscoveryProcessor', () => { const processor = BitbucketDiscoveryProcessor.fromConfig( new ConfigReader({ integrations: { - bitbucket: [{ host: 'bitbucket.mycompany.com', token: 'blob' }], + bitbucket: [ + { + host: 'bitbucket.mycompany.com', + token: 'blob', + apiBaseUrl: 'https://bitbucket.mycompany.com/api/rest/1.0', + }, + ], }, }), { logger: getVoidLogger() }, ); it('output all repositories', async () => { + setupStubs([ + { key: 'backstage', repos: ['backstage'] }, + { key: 'demo', repos: ['demo'] }, + ]); const location: LocationSpec = { type: 'bitbucket-discovery', target: 'https://bitbucket.mycompany.com/projects/*/repos/*/catalog.yaml', }; - jest - .spyOn(BitbucketClient.prototype, 'listProjects') - .mockResolvedValue( - pagedResponse([{ key: 'backstage' }, { key: 'demo' }]), - ); - jest - .spyOn(BitbucketClient.prototype, 'listRepositories') - .mockResolvedValueOnce( - pagedResponse([ - { - slug: 'backstage', - links: { - self: [ - { - href: - 'https://bitbucket.mycompany.com/projects/backstage/repos/backstage/browse', - }, - ], - }, - }, - ]), - ); - jest - .spyOn(BitbucketClient.prototype, 'listRepositories') - .mockResolvedValueOnce( - pagedResponse([ - { - slug: 'demo', - links: { - self: [ - { - href: - 'https://bitbucket.mycompany.com/projects/demo/repos/demo/browse', - }, - ], - }, - }, - ]), - ); const emitter = jest.fn(); await processor.readLocation(location, false, emitter); @@ -158,44 +181,16 @@ describe('BitbucketDiscoveryProcessor', () => { }); it('output repositories with wildcards', async () => { + setupStubs([ + { key: 'backstage', repos: ['backstage', 'techdocs-cli'] }, + { key: 'demo', repos: ['demo'] }, + ]); const location: LocationSpec = { type: 'bitbucket-discovery', target: 'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-*/catalog.yaml', }; - jest - .spyOn(BitbucketClient.prototype, 'listProjects') - .mockResolvedValue(pagedResponse([{ key: 'backstage' }])); - jest - .spyOn(BitbucketClient.prototype, 'listRepositories') - .mockResolvedValueOnce( - pagedResponse([ - { slug: 'backstage' }, - { - slug: 'techdocs-cli', - links: { - self: [ - { - href: - 'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-cli/browse', - }, - ], - }, - }, - { - slug: 'techdocs-container', - links: { - self: [ - { - href: - 'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-container/browse', - }, - ], - }, - }, - ]), - ); const emitter = jest.fn(); await processor.readLocation(location, false, emitter); @@ -208,46 +203,15 @@ describe('BitbucketDiscoveryProcessor', () => { }, optional: true, }); - expect(emitter).toHaveBeenCalledWith({ - type: 'location', - location: { - type: 'url', - target: - 'https://bitbucket.mycompany.com/projects/backstage/repos/techdocs-container/browse/catalog.yaml', - }, - optional: true, - }); }); it('filter unrelated repositories', async () => { + setupStubs([{ key: 'backstage', repos: ['test', 'abctest', 'testxyz'] }]); const location: LocationSpec = { type: 'bitbucket-discovery', target: 'https://bitbucket.mycompany.com/projects/backstage/repos/test/catalog.yaml', }; - jest - .spyOn(BitbucketClient.prototype, 'listProjects') - .mockResolvedValue(pagedResponse([{ key: 'backstage' }])); - jest - .spyOn(BitbucketClient.prototype, 'listRepositories') - .mockResolvedValue( - pagedResponse([ - { slug: 'abstest' }, - { slug: 'testxyz' }, - { - slug: 'test', - links: { - self: [ - { - href: - 'https://bitbucket.mycompany.com/projects/backstage/repos/test', - }, - ], - }, - }, - ]), - ); - const emitter = jest.fn(); await processor.readLocation(location, false, emitter); @@ -256,7 +220,7 @@ describe('BitbucketDiscoveryProcessor', () => { location: { type: 'url', target: - 'https://bitbucket.mycompany.com/projects/backstage/repos/test/catalog.yaml', + 'https://bitbucket.mycompany.com/projects/backstage/repos/test/browse/catalog.yaml', }, optional: true, }); @@ -277,26 +241,27 @@ describe('BitbucketDiscoveryProcessor', () => { const processor = BitbucketDiscoveryProcessor.fromConfig( new ConfigReader({ integrations: { - bitbucket: [{ host: 'bitbucket.mycompany.com', token: 'blob' }], + bitbucket: [ + { + host: 'bitbucket.mycompany.com', + token: 'blob', + apiBaseUrl: 'https://bitbucket.mycompany.com/api/rest/1.0', + }, + ], }, }), { parser: customRepositoryParser, logger: getVoidLogger() }, ); it('use custom repository parser', async () => { + setupStubs([{ key: 'backstage', repos: ['test'] }]); + const location: LocationSpec = { type: 'bitbucket-discovery', target: 'https://bitbucket.mycompany.com/projects/backstage/repos/test/catalog.yaml', }; - jest - .spyOn(BitbucketClient.prototype, 'listProjects') - .mockResolvedValue(pagedResponse([{ key: 'backstage' }])); - jest - .spyOn(BitbucketClient.prototype, 'listRepositories') - .mockResolvedValue(pagedResponse([{ slug: 'test' }])); - const emitter = jest.fn(); await processor.readLocation(location, false, emitter); diff --git a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts index f92a5c6f7c..399adfbc14 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BitbucketDiscoveryProcessor.ts @@ -22,11 +22,11 @@ import { } from '@backstage/integration'; import { LocationSpec } from '@backstage/catalog-model'; import { - Repository, BitbucketRepositoryParser, BitbucketClient, defaultRepositoryParser, paginated, + BitbucketRepository, } from './bitbucket'; import { CatalogProcessor, CatalogProcessorEmit } from './types'; @@ -66,20 +66,19 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor { return false; } - const bitbucketConfig = this.integrations.bitbucket.byUrl(location.target) - ?.config; - if (!bitbucketConfig) { + const integration = this.integrations.bitbucket.byUrl(location.target); + if (!integration) { throw new Error( `There is no Bitbucket integration that matches ${location.target}. Please add a configuration entry for it under integrations.bitbucket`, ); - } else if (bitbucketConfig.host === 'bitbucket.org') { + } else if (integration.config.host === 'bitbucket.org') { throw new Error( `Component discovery for Bitbucket Cloud is not yet supported`, ); } const client = new BitbucketClient({ - config: bitbucketConfig, + config: integration.config, }); const startTimestamp = Date.now(); this.logger.info(`Reading Bitbucket repositories from ${location.target}`); @@ -90,9 +89,9 @@ export class BitbucketDiscoveryProcessor implements CatalogProcessor { for (const repository of result.matches) { for await (const entity of this.parser({ - client: client, - repository: repository, - path: catalogPath, + integration: integration, + target: `${repository.links.self[0].href}${catalogPath}`, + logger: this.logger, })) { emit(entity); } @@ -159,5 +158,5 @@ function escapeRegExp(str: string): RegExp { type Result = { scanned: number; - matches: Repository[]; + matches: BitbucketRepository[]; }; diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts index ab5080f446..3d42d9c4ba 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.test.ts @@ -14,9 +14,9 @@ * limitations under the License. */ import { defaultRepositoryParser } from './BitbucketRepositoryParser'; -import { Project, Repository } from './types'; -import { BitbucketClient } from './client'; import { results } from '../index'; +import { getVoidLogger } from '@backstage/backend-common'; +import { BitbucketIntegration } from '@backstage/integration'; describe('BitbucketRepositoryParser', () => { describe('defaultRepositoryParser', () => { @@ -34,15 +34,9 @@ describe('BitbucketRepositoryParser', () => { ), ]; const actual = await defaultRepositoryParser({ - client: {} as BitbucketClient, - repository: { - project: {} as Project, - slug: 'repo-slug', - links: { - self: [{ href: browseUrl }], - }, - } as Repository, - path: path, + integration: {} as BitbucketIntegration, + target: `${browseUrl}${path}`, + logger: getVoidLogger(), }); let i = 0; diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts index f786b6dac8..9ad038f9ec 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/BitbucketRepositoryParser.ts @@ -13,25 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Repository } from './types'; import { CatalogProcessorResult } from '../types'; import { results } from '../index'; -import { BitbucketClient } from './client'; +import { Logger } from 'winston'; +import { BitbucketIntegration } from '@backstage/integration'; export type BitbucketRepositoryParser = (options: { - client: BitbucketClient; - repository: Repository; - path: string; + integration: BitbucketIntegration; + target: string; + logger: Logger; }) => AsyncIterable; export const defaultRepositoryParser: BitbucketRepositoryParser = async function* defaultRepositoryParser({ - repository, - path, + target, }) { yield results.location( { type: 'url', - target: `${repository.links.self[0].href}${path}`, + target: target, }, // Not all locations may actually exist, since the user defined them as a wildcard pattern. // Thus, we emit them as optional and let the downstream processor find them while not outputting diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts index 601461dcf5..c3c27aedfc 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/client.ts @@ -41,15 +41,6 @@ export class BitbucketClient { ); } - async getRaw( - projectKey: string, - repo: string, - path: string, - ): Promise { - const request = `${this.config.apiBaseUrl}/projects/${projectKey}/repos/${repo}/raw/${path}`; - return fetch(request, getBitbucketRequestOptions(this.config)); - } - private async pagedRequest( endpoint: string, options?: ListOptions, diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/index.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/index.ts index ba2a2b3afe..06effffcd2 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/index.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ export { BitbucketClient, paginated } from './client'; -export type { PagedResponse } from './client'; -export * from './types'; -export type { BitbucketRepositoryParser } from './BitbucketRepositoryParser'; export { defaultRepositoryParser } from './BitbucketRepositoryParser'; +export type { PagedResponse } from './client'; +export type { BitbucketRepository } from './types'; +export type { BitbucketRepositoryParser } from './BitbucketRepositoryParser'; diff --git a/plugins/catalog-backend/src/ingestion/processors/bitbucket/types.ts b/plugins/catalog-backend/src/ingestion/processors/bitbucket/types.ts index 75dd372faa..32dc28eb98 100644 --- a/plugins/catalog-backend/src/ingestion/processors/bitbucket/types.ts +++ b/plugins/catalog-backend/src/ingestion/processors/bitbucket/types.ts @@ -13,16 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export type Project = { - key: string; -}; - -export type Repository = { - project: Project; +export type BitbucketRepository = { + project: { + key: string; + }; slug: string; - links: Record; -}; - -export type Link = { - href: string; + links: Record< + string, + { + href: string; + }[] + >; }; diff --git a/plugins/catalog-backend/src/ingestion/processors/github/github.test.ts b/plugins/catalog-backend/src/ingestion/processors/github/github.test.ts index 15280b96b8..88bddc93fb 100644 --- a/plugins/catalog-backend/src/ingestion/processors/github/github.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/github/github.test.ts @@ -156,7 +156,7 @@ describe('github', () => { describe('getOrganizationRepositories', () => { it('read repositories', async () => { const input: QueryResponse = { - organization: { + repositoryOwner: { repositories: { nodes: [ { diff --git a/plugins/catalog-backend/src/ingestion/processors/github/github.ts b/plugins/catalog-backend/src/ingestion/processors/github/github.ts index e07ea8917b..f6cfcb8901 100644 --- a/plugins/catalog-backend/src/ingestion/processors/github/github.ts +++ b/plugins/catalog-backend/src/ingestion/processors/github/github.ts @@ -20,7 +20,8 @@ import { graphql } from '@octokit/graphql'; // Graphql types export type QueryResponse = { - organization: Organization; + organization?: Organization; + repositoryOwner?: Organization | User; }; export type Organization = { @@ -41,6 +42,7 @@ export type User = { avatarUrl?: string; email?: string; name?: string; + repositories?: Connection; }; export type Team = { @@ -228,28 +230,27 @@ export async function getOrganizationRepositories( org: string, ): Promise<{ repositories: Repository[] }> { const query = ` - query repositories($org: String!, $cursor: String) { - organization(login: $org) { - name - repositories(first: 100, after: $cursor) { - nodes { - name - url - isArchived - } - pageInfo { - hasNextPage - endCursor + query repositories($org: String!, $cursor: String) { + repositoryOwner(login: $org) { + login + repositories(first: 100, after: $cursor) { + nodes { + name + url + isArchived + } + pageInfo { + hasNextPage + endCursor + } } } - } - } - `; + }`; const repositories = await queryWithPaging( client, query, - r => r.organization?.repositories, + r => r.repositoryOwner?.repositories, x => x, { org }, ); diff --git a/plugins/catalog-backend/src/ingestion/processors/index.ts b/plugins/catalog-backend/src/ingestion/processors/index.ts index 8aedc8889b..a92cc9ed3b 100644 --- a/plugins/catalog-backend/src/ingestion/processors/index.ts +++ b/plugins/catalog-backend/src/ingestion/processors/index.ts @@ -35,3 +35,5 @@ export * from './types'; export { UrlReaderProcessor } from './UrlReaderProcessor'; export { parseEntityYaml } from './util/parse'; export { results }; + +export type { BitbucketRepositoryParser } from './bitbucket'; diff --git a/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts b/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts new file mode 100644 index 0000000000..97e554d0f6 --- /dev/null +++ b/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2021 Spotify AB + * + * 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 { TransactionValue } from './TransactionValue'; +import { Knex } from 'knex'; +import { BackgroundContext } from './BackgroundContext'; + +describe('TransactionValue Context', () => { + it('should be able to store tx values and retrieve them from a context', () => { + const tx = {} as Knex.Transaction; + const ctx = new BackgroundContext(); + + const nextCtx = TransactionValue.in(ctx, tx); + + expect(TransactionValue.from(nextCtx)).toBe(tx); + }); + + it('should throw when there is no tx value in the context', () => { + const ctx = new BackgroundContext(); + + expect(() => TransactionValue.from(ctx)).toThrow( + /No transaction available in context/, + ); + }); +}); diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts new file mode 100644 index 0000000000..376af6e748 --- /dev/null +++ b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts @@ -0,0 +1,161 @@ +/* + * Copyright 2021 Spotify AB + * + * 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 } from '@backstage/backend-common'; +import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; + +import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; +import { Stitcher } from './Stitcher'; +import { CatalogProcessingOrchestrator } from './types'; +import waitForExpect from 'wait-for-expect'; + +describe('DefaultCatalogProcessingEngine', () => { + const db = ({ + transaction: jest.fn(), + getProcessableEntities: jest.fn(), + updateProcessedEntity: jest.fn(), + } as unknown) as jest.Mocked; + const orchestrator: jest.Mocked = { + process: jest.fn(), + }; + const stitcher = ({ + stitch: jest.fn(), + } as unknown) as jest.Mocked; + + beforeEach(() => { + jest.resetAllMocks(); + }); + it('should process stuff', async () => { + orchestrator.process.mockResolvedValue({ + ok: true, + completedEntity: { + apiVersion: '1', + kind: 'Location', + metadata: { name: 'test' }, + }, + relations: [], + errors: [], + deferredEntities: [], + state: new Map(), + }); + const engine = new DefaultCatalogProcessingEngine( + getVoidLogger(), + [], + db, + orchestrator, + stitcher, + ); + + db.transaction.mockImplementation(cb => cb((() => {}) as any)); + + db.getProcessableEntities + .mockImplementation(async () => { + await engine.stop(); + return { items: [] }; + }) + .mockResolvedValueOnce({ + items: [ + { + entityRef: 'foo', + id: '1', + unprocessedEntity: { + apiVersion: '1', + kind: 'Location', + metadata: { name: 'test' }, + }, + state: new Map(), + nextUpdateAt: '', + lastDiscoveryAt: '', + }, + ], + }); + + await engine.start(); + await waitForExpect(() => { + expect(orchestrator.process).toBeCalledTimes(1); + expect(orchestrator.process).toBeCalledWith({ + entity: { + apiVersion: '1', + kind: 'Location', + metadata: { name: 'test' }, + }, + state: expect.anything(), + }); + }); + await engine.stop(); + }); + + it('should process stuff even if the first attempt fail', async () => { + orchestrator.process.mockResolvedValue({ + ok: true, + completedEntity: { + apiVersion: '1', + kind: 'Location', + metadata: { name: 'test' }, + }, + relations: [], + errors: [], + deferredEntities: [], + state: new Map(), + }); + const engine = new DefaultCatalogProcessingEngine( + getVoidLogger(), + [], + db, + orchestrator, + stitcher, + ); + + db.transaction.mockImplementation(cb => cb((() => {}) as any)); + + db.getProcessableEntities + .mockImplementation(async () => { + await engine.stop(); + return { items: [] }; + }) + .mockRejectedValueOnce(new Error('I FAILED')) + .mockResolvedValueOnce({ + items: [ + { + entityRef: 'foo', + id: '1', + unprocessedEntity: { + apiVersion: '1', + kind: 'Location', + metadata: { name: 'test' }, + }, + state: new Map(), + nextUpdateAt: '', + lastDiscoveryAt: '', + }, + ], + }); + + await engine.start(); + await waitForExpect(() => { + expect(orchestrator.process).toBeCalledTimes(1); + expect(orchestrator.process).toBeCalledWith({ + entity: { + apiVersion: '1', + kind: 'Location', + metadata: { name: 'test' }, + }, + state: expect.anything(), + }); + }); + await engine.stop(); + }); +}); diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts index e724fe79d8..55cb8e2029 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts @@ -16,6 +16,7 @@ import { stringifyEntityRef } from '@backstage/catalog-model'; import { Logger } from 'winston'; +import { ProcessingDatabase } from './database/types'; import { Stitcher } from './Stitcher'; import { CatalogProcessingEngine, @@ -23,44 +24,46 @@ import { EntityProvider, EntityProviderConnection, EntityProviderMutation, - ProcessingStateManager, } from './types'; class Connection implements EntityProviderConnection { constructor( private readonly config: { - stateManager: ProcessingStateManager; + processingDatabase: ProcessingDatabase; id: string; }, ) {} async applyMutation(mutation: EntityProviderMutation): Promise { + const db = this.config.processingDatabase; if (mutation.type === 'full') { - await this.config.stateManager.replaceProcessingItems({ - sourceKey: this.config.id, - type: 'full', - items: mutation.entities, + await db.transaction(async tx => { + await db.replaceUnprocessedEntities(tx, { + sourceKey: this.config.id, + type: 'full', + items: mutation.entities, + }); }); - return; } - - await this.config.stateManager.replaceProcessingItems({ - sourceKey: this.config.id, - type: 'delta', - added: mutation.added, - removed: mutation.removed, + await db.transaction(async tx => { + await db.replaceUnprocessedEntities(tx, { + sourceKey: this.config.id, + type: 'delta', + added: mutation.added, + removed: mutation.removed, + }); }); } } export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { - private running: boolean = false; + private running = false; constructor( private readonly logger: Logger, private readonly entityProviders: EntityProvider[], - private readonly stateManager: ProcessingStateManager, + private readonly processingDatabase: ProcessingDatabase, private readonly orchestrator: CatalogProcessingOrchestrator, private readonly stitcher: Stitcher, ) {} @@ -69,52 +72,77 @@ export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine { for (const provider of this.entityProviders) { await provider.connect( new Connection({ - stateManager: this.stateManager, + processingDatabase: this.processingDatabase, id: provider.getProviderName(), }), ); } - this.running = true; + this.run(); + } + private async run() { while (this.running) { - const { - id, - entity, - state: initialState, - } = await this.stateManager.getNextProcessingItem(); + try { + // TODO: We want to disconnect the queue popping and message processing + // so that if the queue popping fails we exponentially back off in order to give the DB room to sort itself out. + await this.process(); + } catch (e) { + this.logger.warn('Processing failed with:', e); + // TODO: this can be a little smarter as mentioned in the above comment. + // But for now, if something fails, wait a brief time to pick up the next message. + await this.wait(); + } + } + } - const result = await this.orchestrator.process({ - entity, - state: initialState, + private async process() { + const { items } = await this.processingDatabase.transaction(async tx => { + return this.processingDatabase.getProcessableEntities(tx, { + processBatchSize: 1, }); + }); - for (const error of result.errors) { - this.logger.warn(error.message); - } + if (!items.length) { + // No items to process, wait and try again. + await this.wait(); + return; + } - if (!result.ok) { - return; - } + const { id, state, unprocessedEntity } = items[0]; - result.completedEntity.metadata.uid = id; - await this.stateManager.setProcessingItemResult({ + const result = await this.orchestrator.process({ + entity: unprocessedEntity, + state, + }); + for (const error of result.errors) { + this.logger.warn(error.message); + } + if (!result.ok) { + return; + } + + result.completedEntity.metadata.uid = id; + await this.processingDatabase.transaction(async tx => { + await this.processingDatabase.updateProcessedEntity(tx, { id, - entity: result.completedEntity, + processedEntity: result.completedEntity, state: result.state, - errors: result.errors, + errors: JSON.stringify(result.errors), relations: result.relations, deferredEntities: result.deferredEntities, }); + }); - const setOfThingsToStitch = new Set([ - stringifyEntityRef(result.completedEntity), - ...result.relations.map(relation => - stringifyEntityRef(relation.source), - ), - ]); - await this.stitcher.stitch(setOfThingsToStitch); - } + const setOfThingsToStitch = new Set([ + stringifyEntityRef(result.completedEntity), + ...result.relations.map(relation => stringifyEntityRef(relation.source)), + ]); + await this.stitcher.stitch(setOfThingsToStitch); + } + + private async wait() { + await new Promise(resolve => setTimeout(resolve, 1000)); } async stop() { diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts b/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts index f9bb97477c..88806d0ace 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts +++ b/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts @@ -17,16 +17,17 @@ import { v4 as uuid } from 'uuid'; import { DatabaseManager } from './database/DatabaseManager'; import { DefaultLocationStore } from './DefaultLocationStore'; -/* eslint-disable */ -xdescribe('DefaultLocationStore', () => { - const createLocationStore = async () => { - const db = await DatabaseManager.createTestDatabase(); - const connection = { applyMutation: jest.fn() }; - const store = new DefaultLocationStore(db); - await store.connect(connection); - return { store, connection }; - }; +const createLocationStore = async () => { + const knex = await DatabaseManager.createTestDatabaseConnection(); + await DatabaseManager.createDatabase(knex); + const connection = { applyMutation: jest.fn() }; + const store = new DefaultLocationStore(knex); + await store.connect(connection); + return { store, connection }; +}; + +describe('DefaultLocationStore', () => { it('should do a full sync with the locations on connect', async () => { const { connection } = await createLocationStore(); @@ -39,13 +40,11 @@ xdescribe('DefaultLocationStore', () => { describe('listLocations', () => { it('lists empty locations when there is no locations', async () => { const { store } = await createLocationStore(); - expect(await store.listLocations()).toEqual([]); }); it('lists locations that are added to the db', async () => { const { store } = await createLocationStore(); - await store.createLocation({ target: 'https://github.com/backstage/demo/blob/master/catalog-info.yml', @@ -53,7 +52,6 @@ xdescribe('DefaultLocationStore', () => { }); const listLocations = await store.listLocations(); - expect(listLocations).toHaveLength(1); expect(listLocations).toEqual( expect.arrayContaining([ @@ -76,7 +74,6 @@ xdescribe('DefaultLocationStore', () => { type: 'url', }; await store.createLocation(spec); - await expect(() => store.createLocation(spec)).rejects.toThrow( new RegExp(`Location ${spec.type}:${spec.target} already exists`), ); @@ -84,7 +81,6 @@ xdescribe('DefaultLocationStore', () => { it('calls apply mutation when adding a new location', async () => { const { store, connection } = await createLocationStore(); - await store.createLocation({ target: 'https://github.com/backstage/demo/blob/master/catalog-info.yml', @@ -110,9 +106,7 @@ xdescribe('DefaultLocationStore', () => { describe('deleteLocation', () => { it('throws if the location does not exist', async () => { const { store } = await createLocationStore(); - const id = uuid(); - await expect(() => store.deleteLocation(id)).rejects.toThrow( new RegExp(`Found no location with ID ${id}`), ); diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.ts b/plugins/catalog-backend/src/next/DefaultLocationStore.ts index f6267df6a5..fe1eb30467 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/next/DefaultLocationStore.ts @@ -15,7 +15,6 @@ */ import { LocationSpec, Location } from '@backstage/catalog-model'; -import { Database } from '../database'; import { LocationStore, EntityProvider, @@ -23,80 +22,96 @@ import { } from './types'; import { v4 as uuid } from 'uuid'; import { locationSpecToLocationEntity } from './util'; -import { ConflictError } from '@backstage/errors'; +import { ConflictError, NotFoundError } from '@backstage/errors'; +import { Knex } from 'knex'; + +type DbLocationsRow = { + id: string; + type: string; + target: string; +}; export class DefaultLocationStore implements LocationStore, EntityProvider { private _connection: EntityProviderConnection | undefined; - constructor(private readonly db: Database) {} + constructor(private readonly db: Knex) {} getProviderName(): string { return 'DefaultLocationStore'; } async createLocation(spec: LocationSpec): Promise { - return this.db.transaction(async tx => { + const location = await this.db.transaction(async tx => { // Attempt to find a previous location matching the spec - const previousLocations = await this.listLocations(); + const previousLocations = await this.locations(tx); + // TODO: when location id's are a compilation of spec target we can remove this full + // lookup of locations first and just grab the by that instead. const previousLocation = previousLocations.some( l => spec.type === l.type && spec.target === l.target, ); - if (previousLocation) { throw new ConflictError( `Location ${spec.type}:${spec.target} already exists`, ); } - // TODO: id should really be type and target combined and not a uuid. - const location = await this.db.addLocation(tx, { + const inner: DbLocationsRow = { id: uuid(), type: spec.type, target: spec.target, - }); + }; - await this.connection.applyMutation({ - type: 'delta', - added: [locationSpecToLocationEntity(location)], - removed: [], - }); + await tx('locations').insert(inner); - return location; + return inner; }); + + await this.connection.applyMutation({ + type: 'delta', + added: [locationSpecToLocationEntity(location)], + removed: [], + }); + + return location; } async listLocations(): Promise { - const dbLocations = await this.db.locations(); - return ( - dbLocations - // TODO(blam): We should create a mutation to remove this location for everyone - // eventually when it's all done and dusted - .filter(({ type }) => type !== 'bootstrap') - .map(item => ({ - id: item.id, - target: item.target, - type: item.type, - })) - ); + return await this.locations(); } - getLocation(id: string): Promise { - return this.db.location(id); + async getLocation(id: string): Promise { + const items = await this.db('locations') + .where({ id }) + .select(); + + if (!items.length) { + throw new NotFoundError(`Found no location with ID ${id}`); + } + return items[0]; } - deleteLocation(id: string): Promise { + async deleteLocation(id: string): Promise { if (!this.connection) { throw new Error('location store is not initialized'); } - return this.db.transaction(async tx => { - const location = await this.db.location(id); - await this.db.removeLocation(tx, id); - await this.connection.applyMutation({ - type: 'delta', - added: [], - removed: [locationSpecToLocationEntity(location)], - }); + const deleted = await this.db.transaction(async tx => { + const [location] = await tx('locations') + .where({ id }) + .select(); + + if (!location) { + throw new NotFoundError(`Found no location with ID ${id}`); + } + + await tx('locations').where({ id }).del(); + return location; + }); + + await this.connection.applyMutation({ + type: 'delta', + added: [], + removed: [locationSpecToLocationEntity(deleted)], }); } @@ -110,13 +125,31 @@ export class DefaultLocationStore implements LocationStore, EntityProvider { async connect(connection: EntityProviderConnection): Promise { this._connection = connection; - const locations = await this.listLocations(); + + const locations = await this.locations(); + const entities = locations.map(location => { return locationSpecToLocationEntity(location); }); + await this.connection.applyMutation({ type: 'full', entities, }); } + + private async locations(dbOrTx: Knex.Transaction | Knex = this.db) { + const locations = await dbOrTx('locations').select(); + return ( + locations + // TODO(blam): We should create a mutation to remove this location for everyone + // eventually when it's all done and dusted + .filter(({ type }) => type !== 'bootstrap') + .map(item => ({ + id: item.id, + target: item.target, + type: item.type, + })) + ); + } } diff --git a/plugins/catalog-backend/src/next/DefaultProcessingStateManager.ts b/plugins/catalog-backend/src/next/DefaultProcessingStateManager.ts deleted file mode 100644 index 9c1d20ae9c..0000000000 --- a/plugins/catalog-backend/src/next/DefaultProcessingStateManager.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2021 Spotify AB - * - * 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 { ProcessingDatabase } from './database/types'; -import { - ProcessingItem, - ProcessingItemResult, - ProcessingStateManager, - ReplaceProcessingItemsRequest, -} from './types'; - -export class DefaultProcessingStateManager implements ProcessingStateManager { - constructor(private readonly db: ProcessingDatabase) {} - - replaceProcessingItems( - request: ReplaceProcessingItemsRequest, - ): Promise { - return this.db.transaction(async tx => { - await this.db.replaceUnprocessedEntities(tx, request); - }); - } - - async setProcessingItemResult(result: ProcessingItemResult) { - return this.db.transaction(async tx => { - await this.db.updateProcessedEntity(tx, { - id: result.id, - processedEntity: result.entity, - errors: JSON.stringify(result.errors), - state: result.state, - relations: result.relations, - deferredEntities: result.deferredEntities, - }); - }); - } - - async getNextProcessingItem(): Promise { - for (;;) { - const { items } = await this.db.transaction(async tx => { - return this.db.getProcessableEntities(tx, { - processBatchSize: 1, - }); - }); - - if (items.length) { - const { id, state, unprocessedEntity } = items[0]; - return { - id, - entity: unprocessedEntity, - state, - }; - } - - await new Promise(resolve => setTimeout(resolve, 1000)); - } - } -} diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index 10500fdc15..92f3822055 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -69,7 +69,6 @@ import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase' import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; import { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator'; import { DefaultLocationStore } from './DefaultLocationStore'; -import { DefaultProcessingStateManager } from './DefaultProcessingStateManager'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; import { Stitcher } from './Stitcher'; @@ -252,7 +251,6 @@ export class NextCatalogBuilder { const db = new CommonDatabase(dbClient, logger); const processingDatabase = new DefaultProcessingDatabase(dbClient, logger); - const stateManager = new DefaultProcessingStateManager(processingDatabase); const integrations = ScmIntegrations.fromConfig(config); const orchestrator = new DefaultCatalogProcessingOrchestrator({ processors, @@ -263,13 +261,13 @@ export class NextCatalogBuilder { }); const entitiesCatalog = new NextEntitiesCatalog(dbClient); - const locationStore = new DefaultLocationStore(db); + const locationStore = new DefaultLocationStore(dbClient); const stitcher = new Stitcher(dbClient, logger); const configLocationProvider = new ConfigLocationEntityProvider(config); const processingEngine = new DefaultCatalogProcessingEngine( logger, [locationStore, configLocationProvider], - stateManager, + processingDatabase, orchestrator, stitcher, ); diff --git a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts b/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts index d76bd74260..7a0094dbf0 100644 --- a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts +++ b/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts @@ -23,15 +23,26 @@ import { DefaultProcessingDatabase, } from './DefaultProcessingDatabase'; -import { Entity, EntityRelationSpec } from '@backstage/catalog-model'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import * as uuid from 'uuid'; import { getVoidLogger } from '@backstage/backend-common'; +import { JsonObject } from '@backstage/config'; describe('Default Processing Database', () => { let db: Knex; let processingDatabase: DefaultProcessingDatabase; const logger = getVoidLogger(); + const insertRefRow = async (ref: DbRefreshStateReferencesRow) => { + return db('refresh_state_references').insert( + ref, + ); + }; + + const insertRefreshStateRow = async (ref: DbRefreshStateRow) => { + await db('refresh_state').insert(ref); + }; + beforeEach(async () => { db = await DatabaseManager.createTestDatabaseConnection(); await DatabaseManager.createDatabase(db); @@ -39,17 +50,162 @@ describe('Default Processing Database', () => { processingDatabase = new DefaultProcessingDatabase(db, logger); }); - describe('replaceUnprocessedEntities', () => { - const insertRefRow = async (ref: DbRefreshStateReferencesRow) => { - return db('refresh_state_references').insert( - ref, + describe('updateProcessedEntity', () => { + let id: string; + let processedEntity: Entity; + + beforeEach(() => { + id = uuid.v4(); + processedEntity = { + apiVersion: '1', + kind: 'Location', + metadata: { + name: 'fakelocation', + }, + spec: { + type: 'url', + target: 'somethingelse', + }, + }; + }); + + it('fails when there is no processing state for the entity', async () => { + await processingDatabase.transaction(async tx => { + await expect(() => + processingDatabase.updateProcessedEntity(tx, { + id, + processedEntity, + state: new Map(), + relations: [], + deferredEntities: [], + }), + ).rejects.toThrow(`Processing state not found for ${id}`); + }); + }); + + it('updates the refresh state entry with the cache, processed entity and errors', async () => { + await insertRefreshStateRow({ + entity_id: id, + entity_ref: 'location:default/fakelocation', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: '2021-04-01 13:37:00', + last_discovery_at: '2021-04-01 13:37:00', + }); + + const state = new Map(); + state.set('hello', { t: 'something' }); + + await processingDatabase.transaction(tx => + processingDatabase.updateProcessedEntity(tx, { + id, + processedEntity, + state, + relations: [], + deferredEntities: [], + errors: "['something broke']", + }), ); - }; - const insertRefreshStateRow = async (ref: DbRefreshStateRow) => { - await db('refresh_state').insert(ref); - }; + const entities = await db('refresh_state').select(); + expect(entities.length).toBe(1); + expect(entities[0].processed_entity).toEqual( + JSON.stringify(processedEntity), + ); + expect(entities[0].cache).toEqual(JSON.stringify(state)); + expect(entities[0].errors).toEqual("['something broke']"); + }); + it('removes old relations and stores the new relationships', async () => { + await insertRefreshStateRow({ + entity_id: id, + entity_ref: 'location:default/fakelocation', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: '2021-04-01 13:37:00', + last_discovery_at: '2021-04-01 13:37:00', + }); + + const relations = [ + { + source: { + kind: 'Component', + namespace: 'Default', + name: 'foo', + }, + target: { + kind: 'Component', + namespace: 'Default', + name: 'foo', + }, + type: 'memberOf', + }, + ]; + + await processingDatabase.transaction(tx => + processingDatabase.updateProcessedEntity(tx, { + id, + processedEntity, + state: new Map(), + relations: relations, + deferredEntities: [], + }), + ); + + const savedRelations = await db('relations') + .where({ originating_entity_id: id }) + .select(); + expect(savedRelations.length).toBe(1); + expect(savedRelations[0]).toEqual({ + originating_entity_id: id, + source_entity_ref: 'component:default/foo', + type: 'memberOf', + target_entity_ref: 'component:default/foo', + }); + }); + + it('adds deferred entities to the the refresh_state table to be picked up later', async () => { + await insertRefreshStateRow({ + entity_id: id, + entity_ref: 'location:default/fakelocation', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: '2021-04-01 13:37:00', + last_discovery_at: '2021-04-01 13:37:00', + }); + + const deferredEntities = [ + { + apiVersion: '1', + kind: 'Location', + metadata: { + name: 'next', + }, + }, + ]; + + await processingDatabase.transaction(tx => + processingDatabase.updateProcessedEntity(tx, { + id, + processedEntity, + state: new Map(), + relations: [], + deferredEntities, + }), + ); + + const refreshStateEntries = await db('refresh_state') + .where({ entity_ref: stringifyEntityRef(deferredEntities[0]) }) + .select(); + + expect(refreshStateEntries).toHaveLength(1); + }); + }); + + describe('replaceUnprocessedEntities', () => { const createLocations = async (entityRefs: string[]) => { for (const ref of entityRefs) { await insertRefreshStateRow({ @@ -57,9 +213,9 @@ describe('Default Processing Database', () => { entity_ref: ref, unprocessed_entity: '{}', processed_entity: '{}', - errors: '', - next_update_at: 'now()', - last_discovery_at: 'now()', + errors: '[]', + next_update_at: '2021-04-01 13:37:00', + last_discovery_at: '2021-04-01 13:37:00', }); } }; @@ -101,8 +257,8 @@ describe('Default Processing Database', () => { target_entity_ref: 'location:default/root-2', }); - await processingDatabase.transaction(async tx => { - await processingDatabase.replaceUnprocessedEntities(tx, { + await processingDatabase.transaction(tx => + processingDatabase.replaceUnprocessedEntities(tx, { type: 'full', sourceKey: 'config', items: [ @@ -114,8 +270,8 @@ describe('Default Processing Database', () => { kind: 'Location', } as Entity, ], - }); - }); + }), + ); const currentRefreshState = await db( 'refresh_state', @@ -431,86 +587,48 @@ describe('Default Processing Database', () => { }); }); - describe('updateProcessedEntity', () => { - it('should throw if the entity does not exist', async () => { - await processingDatabase.transaction(async tx => { - await expect( - processingDatabase.updateProcessedEntity(tx, { - id: '9', - processedEntity: { - apiVersion: '1.0.0', - metadata: { - name: 'new-root', - }, - kind: 'Location', - } as Entity, - deferredEntities: [], - relations: [], - }), - ).rejects.toThrow('Processing state not found for 9'); - }); - }); - - it('should update a processed entity', async () => { - await db('refresh_state').insert({ - entity_id: '321', - entity_ref: 'location:default/new-root', - unprocessed_entity: '', - errors: '', - next_update_at: 'now()', - last_discovery_at: 'now()', - }); - - const deferredEntity = { + describe('getProcessableEntities', () => { + it('should return entities to process', async () => { + const entity = JSON.stringify({ + kind: 'Location', apiVersion: '1.0.0', metadata: { - name: 'deferred', + name: 'xyz', }, - kind: 'Location', - } as Entity; + } as Entity); - const relation: EntityRelationSpec = { - source: { - kind: 'Component', - namespace: 'Default', - name: 'foo', - }, - target: { - kind: 'Component', - namespace: 'Default', - name: 'foo', - }, - type: 'url', - }; - - await processingDatabase.transaction(async tx => { - await processingDatabase.updateProcessedEntity(tx, { - id: '321', - processedEntity: { - apiVersion: '1.0.0', - metadata: { - name: 'new-root', - }, - kind: 'Location', - } as Entity, - deferredEntities: [deferredEntity], - relations: [relation], - }); + await db('refresh_state').insert({ + entity_id: '2', + entity_ref: 'location:default/new-root', + unprocessed_entity: entity, + errors: '[]', + next_update_at: '2019-01-01 23:00:00', + last_discovery_at: '2021-04-01 13:37:00', }); - const deferredResult = await db('refresh_state') - .where({ entity_ref: 'location:default/deferred' }) - .select(); - expect(deferredResult.length).toBe(1); + await db('refresh_state').insert({ + entity_id: '1', + entity_ref: 'location:default/foobar', + unprocessed_entity: entity, + errors: '[]', + next_update_at: '2042-01-01 23:00:00', + last_discovery_at: '2021-04-01 13:37:00', + }); - const [relations] = await db('relations') - .where({ originating_entity_id: '321' }) - .select(); - expect(relations).toEqual({ - originating_entity_id: '321', - source_entity_ref: 'component:default/foo', - type: 'url', - target_entity_ref: 'component:default/foo', + await processingDatabase.transaction(async tx => { + // request two items but only one can be processed. + const result = await processingDatabase.getProcessableEntities(tx, { + processBatchSize: 2, + }); + expect(result.items.length).toEqual(1); + expect(result.items[0].entityRef).toEqual('location:default/new-root'); + + // should not return the same item as there's nothing left to process. + await expect( + processingDatabase.getProcessableEntities(tx, { + processBatchSize: 2, + }), + ).resolves.toEqual({ items: [] }); }); }); }); diff --git a/plugins/catalog-backend/src/next/types.ts b/plugins/catalog-backend/src/next/types.ts index 202576fddd..410066eafd 100644 --- a/plugins/catalog-backend/src/next/types.ts +++ b/plugins/catalog-backend/src/next/types.ts @@ -80,37 +80,3 @@ export type EntityProcessingResult = export interface CatalogProcessingOrchestrator { process(request: EntityProcessingRequest): Promise; } - -export type ProcessingItemResult = { - id: string; - entity: Entity; - state: Map; - errors: Error[]; - relations: EntityRelationSpec[]; - deferredEntities: Entity[]; -}; - -export type ProcessingItem = { - id: string; - entity: Entity; - state: Map; -}; - -export type ReplaceProcessingItemsRequest = - | { - sourceKey: string; - items: Entity[]; - type: 'full'; - } - | { - sourceKey: string; - added: Entity[]; - removed: Entity[]; - type: 'delta'; - }; - -export interface ProcessingStateManager { - setProcessingItemResult(result: ProcessingItemResult): Promise; - getNextProcessingItem(): Promise; - replaceProcessingItems(request: ReplaceProcessingItemsRequest): Promise; -} diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md index 899bf8a406..8140724840 100644 --- a/plugins/catalog-graphql/CHANGELOG.md +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-graphql +## 0.2.8 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.2.7 ### Patch Changes diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index 3ab6d64dc8..969d4b7df4 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graphql", - "version": "0.2.7", + "version": "0.2.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.0", - "@backstage/catalog-model": "^0.7.1", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@graphql-modules/core": "^0.7.17", "apollo-server": "^2.16.1", "cross-fetch": "^3.0.6", @@ -42,7 +42,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.6.5", + "@backstage/cli": "^0.6.10", "@backstage/test-utils": "^0.1.9", "@graphql-codegen/cli": "^1.21.3", "@graphql-codegen/typescript": "^1.17.7", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 4b0d264a52..59baabfbc8 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-import +## 0.5.5 + +### Patch Changes + +- 38ca05168: The default `@octokit/rest` dependency was bumped to `"^18.5.3"`. +- 80888659b: Bump react-hook-form version to be the same for the entire project. +- 8aedbb4af: Fixes a typo and minor wording changes to the catalog import UI +- Updated dependencies [38ca05168] +- Updated dependencies [f65adcde7] +- Updated dependencies [81c54d1f2] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/integration@0.5.2 + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-react@0.1.5 + - @backstage/theme@0.2.7 + - @backstage/catalog-model@0.7.8 + - @backstage/catalog-client@0.3.11 + ## 0.5.4 ### Patch Changes diff --git a/plugins/catalog-import/README.md b/plugins/catalog-import/README.md index 5e773ad288..bad48a8486 100644 --- a/plugins/catalog-import/README.md +++ b/plugins/catalog-import/README.md @@ -18,8 +18,8 @@ Some features are not yet available for all supported Git providers. 1. Install the Catalog Import Plugin: ```bash -# packages/app - +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-catalog-import ``` diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index ca540fe8be..2277604dc2 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.5.4", + "version": "0.5.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,17 +30,17 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.6", - "@backstage/catalog-client": "^0.3.9", - "@backstage/core": "^0.7.7", - "@backstage/integration": "^0.5.0", + "@backstage/catalog-model": "^0.7.8", + "@backstage/catalog-client": "^0.3.11", + "@backstage/core": "^0.7.8", + "@backstage/integration": "^0.5.2", "@backstage/integration-react": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.1.5", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", - "@octokit/rest": "^18.0.12", + "@octokit/rest": "^18.5.3", "@types/react": "^16.9", "git-url-parse": "^11.4.4", "js-base64": "^3.6.0", @@ -53,7 +53,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-import/src/components/ImportStepper/defaults.tsx b/plugins/catalog-import/src/components/ImportStepper/defaults.tsx index 354ff6974f..a8c9b41abb 100644 --- a/plugins/catalog-import/src/components/ImportStepper/defaults.tsx +++ b/plugins/catalog-import/src/components/ImportStepper/defaults.tsx @@ -275,7 +275,7 @@ export function defaultGenerateStepper( } /> - WARNING: This may fail is no CODEOWNERS file is found at + WARNING: This may fail if no CODEOWNERS file is found at the target location. diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.tsx index c34d5f374b..d1f8be7065 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/StepPrepareCreatePullRequest.tsx @@ -176,10 +176,9 @@ export const StepPrepareCreatePullRequest = ({ return ( <> - You entered a link to a {analyzeResult.integrationType} repository but - we didn't found a catalog-info.yaml. Use this form to - create a Pull Request that creates an initial{' '} - catalog-info.yaml. + You entered a link to a {analyzeResult.integrationType} repository but a{' '} + catalog-info.yaml could not be found. Use this form to open + a Pull Request that creates one. diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 122647a67f..e5a857eca0 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-react +## 0.1.5 + +### Patch Changes + +- 81c54d1f2: Fetch relations in batches in `useRelatedEntities` +- Updated dependencies [f65adcde7] +- Updated dependencies [80888659b] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/core@0.7.8 + - @backstage/catalog-model@0.7.8 + - @backstage/catalog-client@0.3.11 + ## 0.1.4 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 91201a49a1..1d886478b8 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "0.1.4", + "version": "0.1.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,18 +28,19 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.3", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/core": "^0.7.8", "@material-ui/core": "^4.11.0", "@types/react": "^16.9", + "lodash": "^4.17.15", "react": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.9", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog-react/src/hooks/useRelatedEntities.ts b/plugins/catalog-react/src/hooks/useRelatedEntities.ts index baf6cf5fc7..b47d941ff6 100644 --- a/plugins/catalog-react/src/hooks/useRelatedEntities.ts +++ b/plugins/catalog-react/src/hooks/useRelatedEntities.ts @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import { Entity, EntityRelation } from '@backstage/catalog-model'; import { useApi } from '@backstage/core'; +import { chunk, groupBy } from 'lodash'; import { useAsync } from 'react-use'; import { catalogApiRef } from '../api'; +const BATCH_SIZE = 20; + export function useRelatedEntities( entity: Entity, { type, kind }: { type?: string; kind?: string }, @@ -40,16 +43,50 @@ export function useRelatedEntities( return []; } - // TODO: This code could be more efficient if there was an endpoint in the - // backend that either returns the relations of entity (filtered by type) - // or if there is a way to perform a batch request by entity name. However, - // such an implementation would probably be better placed in the graphql API. - const results = await Promise.all( - relations?.map(r => catalogApi.getEntityByName(r.target)), + // Group the relations by kind and namespace to reduce the size of the request query string. + // Without this grouping, the kind and namespace would need to be specified for each relation, e.g. + // `filter=kind=component,namespace=default,name=example1&filter=kind=component,namespace=default,name=example2` + // with grouping, we can generate a query a string like + // `filter=kind=component,namespace=default,name=example1,example2` + const relationsByKindAndNamespace: EntityRelation[][] = Object.values( + groupBy(relations, ({ target }) => { + return `${target.kind}:${target.namespace}`.toLowerCase(); + }), ); - // Skip entities that where not found, for example if a relation references - // an entity that doesn't exist. - return results.filter(e => e) as Entity[]; + + // Split the names within each group into batches to further reduce the query string length. + const batchedRelationsByKindAndNamespace: { + kind: string; + namespace: string; + nameBatches: string[][]; + }[] = []; + for (const rs of relationsByKindAndNamespace) { + batchedRelationsByKindAndNamespace.push({ + // All relations in a group have the same kind and namespace, so its arbitrary which we pick + kind: rs[0].target.kind, + namespace: rs[0].target.namespace, + nameBatches: chunk( + rs.map(r => r.target.name), + BATCH_SIZE, + ), + }); + } + + const results = await Promise.all( + batchedRelationsByKindAndNamespace.flatMap(rs => { + return rs.nameBatches.map(names => { + return catalogApi.getEntities({ + filter: { + kind: rs.kind, + 'metadata.namespace': rs.namespace, + 'metadata.name': names, + }, + }); + }); + }), + ); + + return results.flatMap(r => r.items); }, [entity, type]); return { diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 4f54735611..8277c2dcbc 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -32,12 +32,12 @@ "dependencies": { "@backstage/catalog-client": "^0.3.10", "@backstage/catalog-model": "^0.7.7", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.1", "@backstage/integration-react": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -52,7 +52,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@microsoft/microsoft-graph-types": "^1.25.0", diff --git a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx index 58df0a53f8..8e0a61b05a 100644 --- a/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx +++ b/plugins/catalog/src/components/HasComponentsCard/HasComponentsCard.test.tsx @@ -91,14 +91,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'Component', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx index 79b69d35ff..9ad69c9a0c 100644 --- a/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx +++ b/plugins/catalog/src/components/HasSubcomponentsCard/HasSubcomponentsCard.test.tsx @@ -91,14 +91,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'Component', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx index 08923a59c8..9b7fb0adae 100644 --- a/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx +++ b/plugins/catalog/src/components/HasSystemsCard/HasSystemsCard.test.tsx @@ -89,14 +89,18 @@ describe('', () => { }, ], }; - catalogApi.getEntityByName.mockResolvedValue({ - apiVersion: 'v1', - kind: 'System', - metadata: { - name: 'target-name', - namespace: 'my-namespace', - }, - spec: {}, + catalogApi.getEntities.mockResolvedValue({ + items: [ + { + apiVersion: 'v1', + kind: 'System', + metadata: { + name: 'target-name', + namespace: 'my-namespace', + }, + spec: {}, + }, + ], }); const { getByText } = await renderInTestApp( diff --git a/plugins/circleci/README.md b/plugins/circleci/README.md index 868ae6e26a..c878c66e56 100644 --- a/plugins/circleci/README.md +++ b/plugins/circleci/README.md @@ -7,34 +7,35 @@ Website: [https://circleci.com/](https://circleci.com/) ## Setup -1. If you have standalone app (you didn't clone this repo), then do +1. If you have a standalone app (you didn't clone this repo), then do ```bash +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-circleci ``` -2. Add the `EntityCircleCIContent` extension to the entity page in the app: +2. Add the `EntityCircleCIContent` extension to the entity page in your app: ```tsx -// packages/app/src/components/catalog/EntityPage.tsx -import { EntityCircleCIContent } from '@backstage/plugin-circleci'; +// In packages/app/src/components/catalog/EntityPage.tsx +import { + EntityCircleCIContent, + isCircleCIAvailable, +} from '@backstage/plugin-circleci'; -// ... -const serviceEntityPage = ( - - ... - +// For example in the CI/CD section +const cicdContent = ( + + - - ... - -); + ``` 4. Add proxy config: ```yaml -// app-config.yaml +# In app-config.yaml proxy: '/circleci/api': target: https://circleci.com/api/v1.1 @@ -42,10 +43,11 @@ proxy: Circle-Token: ${CIRCLECI_AUTH_TOKEN} ``` -5. Get and provide `CIRCLECI_AUTH_TOKEN` as env variable (https://circleci.com/docs/api/#add-an-api-token) -6. Add `circleci.com/project-slug` annotation to your catalog-info.yaml file in format // (https://backstage.io/docs/architecture-decisions/adrs-adr002#format) +5. Get and provide a `CIRCLECI_AUTH_TOKEN` as an environment variable (see the [CircleCI docs](https://circleci.com/docs/api/#add-an-api-token)). +6. Add a `circleci.com/project-slug` annotation to your respective `catalog-info.yaml` files, on the format // (https://backstage.io/docs/architecture-decisions/adrs-adr002#format). ```yaml +# Example catalog-info.yaml entity definition file apiVersion: backstage.io/v1alpha1 kind: Component metadata: diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 20c757eda0..b87fb4bbbf 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.2", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -50,7 +50,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index cb2a08fce1..98735f1c42 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -32,8 +32,8 @@ "dependencies": { "@backstage/catalog-model": "^0.7.3", "@backstage/plugin-catalog-react": "^0.1.2", - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/code-coverage-backend/CHANGELOG.md b/plugins/code-coverage-backend/CHANGELOG.md index d7a0f136f5..18e78c7454 100644 --- a/plugins/code-coverage-backend/CHANGELOG.md +++ b/plugins/code-coverage-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-code-coverage-backend +## 0.1.4 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/backend-common@0.7.0 + - @backstage/integration@0.5.2 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + ## 0.1.3 ### Patch Changes diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index c2bebbf293..db53096932 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-code-coverage-backend", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,12 +19,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.3", - "@backstage/catalog-client": "^0.3.10", - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.1", + "@backstage/integration": "^0.5.2", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", "express": "^4.17.1", @@ -36,7 +36,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@types/express-xml-bodyparser": "^0.3.2", "@types/supertest": "^2.0.8", "msw": "^0.21.2", diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index f5c36811cc..241b61b296 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -22,10 +22,10 @@ "dependencies": { "@backstage/catalog-model": "^0.7.7", "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/styles": "^4.11.0", @@ -39,7 +39,7 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index a017529400..7877668065 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -21,9 +21,9 @@ }, "dependencies": { "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/errors": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,7 +34,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index c86a71f8c9..e6a351f2a4 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-cost-insights +## 0.9.0 + +### Minor Changes + +- 6f1b82b14: make change ratio optional + +### Patch Changes + +- Updated dependencies [f65adcde7] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] + - @backstage/core@0.7.8 + - @backstage/theme@0.2.7 + - @backstage/config@0.1.5 + ## 0.8.5 ### Patch Changes diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md index f737c022f3..3fee89e77a 100644 --- a/plugins/cost-insights/README.md +++ b/plugins/cost-insights/README.md @@ -16,6 +16,8 @@ Learn more with the Backstage blog post [New Cost Insights plugin: The engineer' ## Install ```bash +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-cost-insights ``` diff --git a/plugins/cost-insights/contrib/aws-cost-explorer-api.md b/plugins/cost-insights/contrib/aws-cost-explorer-api.md index 10622b7917..d6c5250759 100644 --- a/plugins/cost-insights/contrib/aws-cost-explorer-api.md +++ b/plugins/cost-insights/contrib/aws-cost-explorer-api.md @@ -33,6 +33,8 @@ Cost Explorer permission policy: Install the AWS Cost Explorer SDK. The AWS docs recommend using the SDK over making calls to the API directly as it simplifies authentication and provides direct access to commands. ```bash +# From your Backstage root directory +cd packages/app yarn add @aws-sdk/client-cost-explorer ``` diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 39e3fe1791..5d780080d1 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cost-insights", - "version": "0.8.5", + "version": "0.9.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.3", - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/config": "^0.1.5", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -55,7 +55,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index be61bc1fd9..35a4a97349 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -31,10 +31,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.4", "@backstage/plugin-explore-react": "^0.0.4", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -45,7 +45,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/fossa/README.md b/plugins/fossa/README.md index 11f3e90e1c..6ca2e1a96f 100644 --- a/plugins/fossa/README.md +++ b/plugins/fossa/README.md @@ -9,8 +9,8 @@ The FOSSA Plugin displays code statistics from [FOSSA](https://fossa.com/). 1. Install the FOSSA Plugin: ```bash -# packages/app - +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-fossa ``` diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index ba7e1a381e..ee35b94c0b 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -32,10 +32,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 0930c791e4..96102332d9 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 6896a7f877..aa2fec5512 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-github-actions +## 0.4.5 + +### Patch Changes + +- 38ca05168: The default `@octokit/rest` dependency was bumped to `"^18.5.3"`. +- Updated dependencies [38ca05168] +- Updated dependencies [f65adcde7] +- Updated dependencies [81c54d1f2] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] + - @backstage/integration@0.5.2 + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-react@0.1.5 + - @backstage/theme@0.2.7 + - @backstage/catalog-model@0.7.8 + ## 0.4.4 ### Patch Changes diff --git a/plugins/github-actions/README.md b/plugins/github-actions/README.md index 87c68839c1..d027368c8b 100644 --- a/plugins/github-actions/README.md +++ b/plugins/github-actions/README.md @@ -11,15 +11,15 @@ TBD ### Generic Requirements 1. Provide OAuth credentials: - 1. [Create an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/) with callback URL set to `http://localhost:7000/api/auth/github`. - 2. Take Client ID and Client Secret from the newly created app's settings page and put them into `AUTH_GITHUB_CLIENT_ID` and `AUTH_GITHUB_CLIENT_SECRET` env variables. + 1. [Create an OAuth App](https://developer.github.com/apps/building-oauth-apps/creating-an-oauth-app/) with the callback URL set to `http://localhost:7000/api/auth/github`. + 2. Take the Client ID and Client Secret from the newly created app's settings page and put them into `AUTH_GITHUB_CLIENT_ID` and `AUTH_GITHUB_CLIENT_SECRET` environment variables. 2. Annotate your component with a correct GitHub Actions repository and owner: The annotation key is `github.com/project-slug`. Example: - ``` + ```yaml apiVersion: backstage.io/v1alpha1 kind: Component metadata: @@ -38,6 +38,7 @@ TBD 1. Install the plugin dependency in your Backstage app package: ```bash +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-github-actions ``` @@ -45,22 +46,24 @@ yarn add @backstage/plugin-github-actions 2. Add to the app `EntityPage` component: ```tsx -// packages/app/src/components/catalog/EntityPage.tsx -import { EntityGithubActionsContent } from '@backstage/plugin-github-actions'; +// In packages/app/src/components/catalog/EntityPage.tsx +import { + EntityGithubActionsContent, + isGithubActionsAvailable, +} from '@backstage/plugin-github-actions'; -// ... +// You can add the tab to any number of pages, the service page is shown as an +// example here const serviceEntityPage = ( - - ... + + {/* other tabs... */} - ... - -); ``` -2. Run the app with `yarn start` and the backend with `yarn --cwd packages/backend start`, navigate to `/github-actions/`. +3. Run the app with `yarn start` and the backend with `yarn start-backend`. + Then navigate to `/github-actions/` under any entity. ## Features diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 2c26061cf3..61de1a1f7d 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.4.4", + "version": "0.4.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,15 +32,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.5", - "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/core": "^0.7.7", - "@backstage/integration": "^0.5.1", - "@backstage/theme": "^0.2.6", + "@backstage/catalog-model": "^0.7.8", + "@backstage/plugin-catalog-react": "^0.1.5", + "@backstage/core": "^0.7.8", + "@backstage/integration": "^0.5.2", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", - "@octokit/rest": "^18.0.12", + "@octokit/rest": "^18.5.3", "moment": "^2.27.0", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -50,7 +50,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx index 38d84a4b6d..0624438ffe 100644 --- a/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx +++ b/plugins/github-actions/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx @@ -202,13 +202,13 @@ export const WorkflowRunDetails = ({ entity }: { entity: Entity }) => { Message - {details.value?.head_commit.message} + {details.value?.head_commit?.message} Commit ID - {details.value?.head_commit.id} + {details.value?.head_commit?.id} @@ -231,7 +231,7 @@ export const WorkflowRunDetails = ({ entity }: { entity: Entity }) => { Author - {`${details.value?.head_commit.author?.name} (${details.value?.head_commit.author?.email})`} + {`${details.value?.head_commit?.author?.name} (${details.value?.head_commit?.author?.email})`} diff --git a/plugins/github-deployments/README.md b/plugins/github-deployments/README.md index 1bb8846105..8284ceba56 100644 --- a/plugins/github-deployments/README.md +++ b/plugins/github-deployments/README.md @@ -13,8 +13,8 @@ The GitHub Deployments Plugin displays recent deployments from GitHub. 1. Install the GitHub Deployments Plugin. ```bash -# packages/app - +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-github-deployments ``` diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 049e02de4c..e67df926ea 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -21,9 +21,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.6", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.3", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -34,7 +34,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index d1897b018c..866ee571c0 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -42,7 +42,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/graphiql/README.md b/plugins/graphiql/README.md index 39372890d1..b95f055a56 100644 --- a/plugins/graphiql/README.md +++ b/plugins/graphiql/README.md @@ -12,6 +12,7 @@ By exposing GraphiQL as a plugin instead of a standalone app, it's possible to p Start out by installing the plugin in your Backstage app: ```bash +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-graphiql ``` diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 9ce1603b57..d03a5f39e9 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -43,7 +43,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/graphql/CHANGELOG.md b/plugins/graphql/CHANGELOG.md index dfcddfbcf9..fc31d831dc 100644 --- a/plugins/graphql/CHANGELOG.md +++ b/plugins/graphql/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-graphql-backend +## 0.1.7 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/config@0.1.5 + - @backstage/plugin-catalog-graphql@0.2.8 + ## 0.1.6 ### Patch Changes diff --git a/plugins/graphql/package.json b/plugins/graphql/package.json index b5046e13fc..0df78298d7 100644 --- a/plugins/graphql/package.json +++ b/plugins/graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-graphql-backend", - "version": "0.1.6", + "version": "0.1.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.0", - "@backstage/config": "^0.1.4", - "@backstage/plugin-catalog-graphql": "^0.2.7", + "@backstage/backend-common": "^0.7.0", + "@backstage/config": "^0.1.5", + "@backstage/plugin-catalog-graphql": "^0.2.8", "@graphql-modules/core": "^0.7.17", "@types/express": "^4.17.6", "apollo-server": "^2.16.1", @@ -45,7 +45,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.5", + "@backstage/cli": "^0.6.10", "@types/supertest": "^2.0.8", "eslint-plugin-graphql": "^4.0.0", "msw": "^0.20.5", diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index f406393190..69304cb847 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -11,25 +11,25 @@ Website: [https://jenkins.io/](https://jenkins.io/) 1. If you have a standalone app (you didn't clone this repo), then do ```bash +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-jenkins ``` 2. Add the `EntityJenkinsContent` extension to the entity page in the app: ```tsx -// packages/app/src/components/catalog/EntityPage.tsx -import { EntityJenkinsContent } from '@backstage/plugin-circleci'; +// In packages/app/src/components/catalog/EntityPage.tsx +import { EntityJenkinsContent } from '@backstage/plugin-jenkins'; -// ... +// You can add the tab to any number of pages, the service page is shown as an +// example here const serviceEntityPage = ( - - ... + + {/* other tabs... */} - ... - -); ``` 3. Add proxy configuration to `app-config.yaml` @@ -43,14 +43,18 @@ proxy: Authorization: Basic ${JENKINS_BASIC_AUTH_HEADER} ``` -4. Add an environment variable which contains the Jenkins credentials, (note: use an API token not your password). Here user is the name of the user created in Jenkins. +4. Add an environment variable which contains the Jenkins credentials (NOTE: + use an API token, not your password). Here `user` is the name of the user + created in Jenkins. ```shell export JENKINS_BASIC_AUTH_HEADER=$(echo -n user:api-token | base64) ``` -5. Run app with `yarn start` -6. Add the Jenkins folder annotation to your `catalog-info.yaml`, (note: currently this plugin only supports folders and Git SCM) +5. Run the app with `yarn start` + +6. Add the Jenkins folder annotation to your `catalog-info.yaml`, (NOTE: + currently this plugin only supports folders and Git SCM) ```yaml apiVersion: backstage.io/v1alpha1 @@ -68,11 +72,11 @@ spec: 7. Register your component -8. Click the component in the catalog you should now see Jenkins builds, and a last build result for your master build. +8. Click the component in the catalog. You should now see Jenkins builds, and a + last build result for your master build. -Note: - -If you are not using environment variable then you can directly type API token in app-config.yaml +Note: If you are not using environment variables, you can directly type the API +token into `app-config.yaml`. ```yaml proxy: @@ -83,7 +87,8 @@ proxy: Authorization: Basic YWRtaW46MTFlYzI1NmU0Mzg1MDFjM2Y1Yzc2Yjc1MWE3ZTQ3YWY4Mw== ``` -YWRtaW46MTFlYzI1NmU0Mzg1MDFjM2Y1Yzc2Yjc1MWE3ZTQ3YWY4Mw== is the base64 of user and it's API token e.g. admin:11ec256e438501c3f5c76b751a7e47af83 +The string starting with `YWR...` is the base64 encoding of the user and their +API token, e.g. `admin:11ec256e438501c3f5c76b751a7e47af83`. ## Features @@ -94,4 +99,5 @@ YWRtaW46MTFlYzI1NmU0Mzg1MDFjM2Y1Yzc2Yjc1MWE3ZTQ3YWY4Mw== is the base64 of user a ## Limitations - Only works with organization folder projects backed by GitHub -- No pagination support currently, limited to 50 projects - don't run this on a Jenkins with lots of builds +- No pagination support currently, limited to 50 projects - don't run this on a + Jenkins instance with lots of builds diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index d1cabd2143..45bd3c4bf8 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.3", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index f29fb62a91..ef006a360f 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kafka-backend +## 0.2.4 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.2.3 ### Patch Changes diff --git a/plugins/kafka-backend/README.md b/plugins/kafka-backend/README.md index 14914e5ba6..d18a232fa4 100644 --- a/plugins/kafka-backend/README.md +++ b/plugins/kafka-backend/README.md @@ -1,6 +1,7 @@ # Kafka Backend -This is the backend part of the Kafka plugin. It responds to Kafka requests from the frontend. +This is the backend part of the Kafka plugin. It responds to Kafka requests +from the frontend. ## Configuration @@ -16,7 +17,9 @@ A list of the brokers' host names and ports to connect to. ### `ssl` (optional) -Configure TLS connection to the Kafka cluster. The options are passed directly to [tls.connect] and used to create the TLS secure context. Normally these would include `key` and `cert`. +Configure TLS connection to the Kafka cluster. The options are passed directly +to [tls.connect] and used to create the TLS secure context. Normally these +would include `key` and `cert`. Example: @@ -39,7 +42,7 @@ kafka: clusters: - name: prod brokers: - - my-cluser:9092 + - my-cluster:9092 ssl: true sasl: mechanism: plain # or 'scram-sha-256' or 'scram-sha-512' diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index ef64ebfbf5..3e3f22654a 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kafka-backend", - "version": "0.2.3", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.1", - "@backstage/catalog-model": "^0.7.5", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -42,7 +42,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.6.5", + "@backstage/cli": "^0.6.10", "@types/jest-when": "^2.7.2", "@types/lodash": "^4.14.151", "jest-when": "^3.1.0", diff --git a/plugins/kafka/README.md b/plugins/kafka/README.md index a2ea01cf7d..7da44ae9b6 100644 --- a/plugins/kafka/README.md +++ b/plugins/kafka/README.md @@ -7,7 +7,9 @@ 1. Run: ```bash -yarn add @backstage/plugin-kafka @backstage/plugin-kafka-backend +# From your Backstage root directory +yarn --cwd packages/app add @backstage/plugin-kafka +yarn --cwd packages/backend add @backstage/plugin-kafka-backend ``` 2. Add the plugin backend: @@ -29,41 +31,31 @@ export default async function createPlugin({ And then add to `packages/backend/src/index.ts`: ```js -// ... +// In packages/backend/src/index.ts import kafka from './plugins/kafka'; // ... async function main() { // ... const kafkaEnv = useHotMemoize(module, () => createEnv('kafka')); // ... - - const apiRouter = Router(); - // ... apiRouter.use('/kafka', await kafka(kafkaEnv)); - // ... ``` -3. Add the plugin frontend to `packages/app/src/plugin.ts`: - -```js -export { plugin as Kafka } from '@backstage/plugin-kafka'; -``` - -4. Register the plugin frontend router in `packages/app/src/components/catalog/EntityPage.tsx`: +3. Add the plugin as a tab to your service entities: ```jsx -import { Router as KafkaRouter } from '@backstage/plugin-kafka'; +// In packages/app/src/components/catalog/EntityPage.tsx +import { EntityKafkaContent } from '@backstage/plugin-kafka'; -// Then, somewhere inside - -} -/>; +const serviceEntityPage = ( + + {/* other tabs... */} + + + ``` -5. Add broker configs for the backend in your `app-config.yaml` (see +4. Add broker configs for the backend in your `app-config.yaml` (see [kafka-backend](https://github.com/backstage/backstage/blob/master/plugins/kafka-backend/README.md) for more options): @@ -76,7 +68,7 @@ kafka: - localhost:9092 ``` -6. Add `kafka.apache.org/consumer-groups` annotation to your services: +5. Add the `kafka.apache.org/consumer-groups` annotation to your services: ```yaml apiVersion: backstage.io/v1alpha1 diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 0963d3178f..ce3e7561b1 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -21,9 +21,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.4", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -33,7 +33,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 8bcac8a60b..a9893925e2 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-backend +## 0.3.6 + +### Patch Changes + +- f53fba29f: Adds @backstage/plugin-kubernetes-common library to share types between kubernetes frontend and backend. +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.3.5 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 7a8b16bc8a..f73166d5fc 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.3.5", + "version": "0.3.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.3", - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", + "@backstage/plugin-kubernetes-common": "^0.1.0", "@google-cloud/container": "^2.2.0", "@kubernetes/client-node": "^0.14.0", "@types/express": "^4.17.6", @@ -52,7 +53,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@types/aws4": "^1.5.1", "supertest": "^6.1.3" }, diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts index 0dd2a4bcc2..9dc4519966 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/GoogleKubernetesAuthTranslator.ts @@ -15,7 +15,8 @@ */ import { KubernetesAuthTranslator } from './types'; -import { KubernetesRequestBody, ClusterDetails } from '../types/types'; +import { ClusterDetails } from '../types/types'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export class GoogleKubernetesAuthTranslator implements KubernetesAuthTranslator { diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts index 6433e41546..3610bd4d9f 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/ServiceAccountKubernetesAuthTranslator.ts @@ -15,7 +15,8 @@ */ import { KubernetesAuthTranslator } from './types'; -import { KubernetesRequestBody, ClusterDetails } from '../types/types'; +import { ClusterDetails } from '../types/types'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export class ServiceAccountKubernetesAuthTranslator implements KubernetesAuthTranslator { diff --git a/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts b/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts index c01a57889c..7a04e230c6 100644 --- a/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts +++ b/plugins/kubernetes-backend/src/kubernetes-auth-translator/types.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { KubernetesRequestBody, ClusterDetails } from '../types/types'; +import { ClusterDetails } from '../types/types'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export interface KubernetesAuthTranslator { decorateClusterDetailsWithAuth( diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index 6db6e2ad4a..75e2f8233c 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -20,9 +20,9 @@ import { CustomResource, KubernetesFetcher, KubernetesObjectTypes, - KubernetesRequestBody, KubernetesServiceLocator, } from '../types/types'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { KubernetesAuthTranslator } from '../kubernetes-auth-translator/types'; import { KubernetesAuthTranslatorGenerator } from '../kubernetes-auth-translator/KubernetesAuthTranslatorGenerator'; diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts index 10e5640bab..6958bd3387 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts @@ -32,15 +32,17 @@ import lodash, { Dictionary } from 'lodash'; import { Logger } from 'winston'; import { ClusterDetails, - FetchResponse, FetchResponseWrapper, - KubernetesErrorTypes, KubernetesFetcher, - KubernetesFetchError, KubernetesObjectTypes, ObjectFetchParams, CustomResource, } from '../types/types'; +import { + FetchResponse, + KubernetesFetchError, + KubernetesErrorTypes, +} from '@backstage/plugin-kubernetes-common'; import { KubernetesClientProvider } from './KubernetesClientProvider'; export interface Clients { diff --git a/plugins/kubernetes-backend/src/service/router.ts b/plugins/kubernetes-backend/src/service/router.ts index 189f2eb9c5..ab7bb5befa 100644 --- a/plugins/kubernetes-backend/src/service/router.ts +++ b/plugins/kubernetes-backend/src/service/router.ts @@ -23,11 +23,11 @@ import { MultiTenantServiceLocator } from '../service-locator/MultiTenantService import { ClusterDetails, KubernetesClustersSupplier, - KubernetesRequestBody, KubernetesServiceLocator, ServiceLocatorMethod, CustomResource, } from '../types/types'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { KubernetesClientProvider } from './KubernetesClientProvider'; import { KubernetesFanOutHandler } from './KubernetesFanOutHandler'; import { KubernetesClientBasedFetcher } from './KubernetesFetcher'; diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index c597c718c5..0be0b447af 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -14,146 +14,14 @@ * limitations under the License. */ -import { - ExtensionsV1beta1Ingress, - V1ConfigMap, - V1Deployment, - V1HorizontalPodAutoscaler, - V1Pod, - V1ReplicaSet, - V1Service, -} from '@kubernetes/client-node'; -import { Entity } from '@backstage/catalog-model'; +import type { + FetchResponse, + KubernetesFetchError, +} from '@backstage/plugin-kubernetes-common'; -export interface ClusterDetails { - name: string; - url: string; - authProvider: string; - serviceAccountToken?: string | undefined; - skipTLSVerify?: boolean; -} - -export interface KubernetesRequestBody { - auth?: { - google?: string; - }; - entity: Entity; -} - -export interface ClusterObjects { - cluster: { name: string }; - resources: FetchResponse[]; - errors: KubernetesFetchError[]; -} - -export interface ObjectsByEntityResponse { - items: ClusterObjects[]; -} - -export interface FetchResponseWrapper { - errors: KubernetesFetchError[]; - responses: FetchResponse[]; -} - -export type FetchResponse = - | PodFetchResponse - | ServiceFetchResponse - | ConfigMapFetchResponse - | DeploymentFetchResponse - | ReplicaSetsFetchResponse - | HorizontalPodAutoscalersFetchResponse - | IngressesFetchResponse - | CustomResourceFetchResponse; - -// TODO fairly sure there's a easier way to do this - -export type KubernetesObjectTypes = - | 'pods' - | 'services' - | 'configmaps' - | 'deployments' - | 'replicasets' - | 'horizontalpodautoscalers' - | 'ingresses' - | 'customresources'; - -export interface PodFetchResponse { - type: 'pods'; - resources: Array; -} - -export interface ServiceFetchResponse { - type: 'services'; - resources: Array; -} - -export interface ConfigMapFetchResponse { - type: 'configmaps'; - resources: Array; -} - -export interface DeploymentFetchResponse { - type: 'deployments'; - resources: Array; -} - -export interface ReplicaSetsFetchResponse { - type: 'replicasets'; - resources: Array; -} - -export interface HorizontalPodAutoscalersFetchResponse { - type: 'horizontalpodautoscalers'; - resources: Array; -} - -export interface IngressesFetchResponse { - type: 'ingresses'; - resources: Array; -} - -export interface CustomResourceFetchResponse { - type: 'customresources'; - resources: Array; -} - -export interface ObjectFetchParams { - serviceId: string; - clusterDetails: ClusterDetails; - objectTypesToFetch: Set; - labelSelector: string; - customResources: CustomResource[]; -} - -// Fetches information from a kubernetes cluster using the cluster details object -// to target a specific cluster -export interface KubernetesFetcher { - fetchObjectsForService( - params: ObjectFetchParams, - ): Promise; -} - -// Used to locate which cluster(s) a service is running on -export interface KubernetesServiceLocator { - getClustersByServiceId(serviceId: string): Promise; -} - -// Used to load cluster details from different sources -export interface KubernetesClustersSupplier { - getClusters(): Promise; -} - -export type KubernetesErrorTypes = - | 'BAD_REQUEST' - | 'UNAUTHORIZED_ERROR' - | 'SYSTEM_ERROR' - | 'UNKNOWN_ERROR'; - -export interface KubernetesFetchError { - errorType: KubernetesErrorTypes; - statusCode?: number; - resourcePath?: string; -} +export type ClusterLocatorMethod = + | ConfigClusterLocatorMethod + | GKEClusterLocatorMethod; export interface ConfigClusterLocatorMethod { /** @@ -195,15 +63,61 @@ export interface GKEClusterLocatorMethod { region?: string; } -export type ClusterLocatorMethod = - | ConfigClusterLocatorMethod - | GKEClusterLocatorMethod; - -export type ServiceLocatorMethod = 'multiTenant' | 'http'; // TODO implement http -export type AuthProviderType = 'google' | 'serviceAccount' | 'aws'; - export interface CustomResource { group: string; apiVersion: string; plural: string; } + +export interface ObjectFetchParams { + serviceId: string; + clusterDetails: ClusterDetails; + objectTypesToFetch: Set; + labelSelector: string; + customResources: CustomResource[]; +} + +// Fetches information from a kubernetes cluster using the cluster details object +// to target a specific cluster +export interface KubernetesFetcher { + fetchObjectsForService( + params: ObjectFetchParams, + ): Promise; +} + +export interface FetchResponseWrapper { + errors: KubernetesFetchError[]; + responses: FetchResponse[]; +} + +// TODO fairly sure there's a easier way to do this + +export type KubernetesObjectTypes = + | 'pods' + | 'services' + | 'configmaps' + | 'deployments' + | 'replicasets' + | 'horizontalpodautoscalers' + | 'ingresses' + | 'customresources'; + +// Used to load cluster details from different sources +export interface KubernetesClustersSupplier { + getClusters(): Promise; +} + +// Used to locate which cluster(s) a service is running on +export interface KubernetesServiceLocator { + getClustersByServiceId(serviceId: string): Promise; +} + +export type ServiceLocatorMethod = 'multiTenant' | 'http'; // TODO implement http + +export interface ClusterDetails { + name: string; + url: string; + authProvider: string; + serviceAccountToken?: string | undefined; + skipTLSVerify?: boolean; +} diff --git a/plugins/kubernetes-common/.eslintrc.js b/plugins/kubernetes-common/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/kubernetes-common/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md new file mode 100644 index 0000000000..44c9066ada --- /dev/null +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -0,0 +1,7 @@ +# @backstage/plugin-kubernetes-common + +## 0.1.0 + +### Minor Changes + +- Adds types to be shared by the backend and the front end. diff --git a/plugins/kubernetes-common/README.md b/plugins/kubernetes-common/README.md new file mode 100644 index 0000000000..c366d0493c --- /dev/null +++ b/plugins/kubernetes-common/README.md @@ -0,0 +1,3 @@ +# @backstage/plugin-kubernetes-common + +Common types and functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend. diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json new file mode 100644 index 0000000000..3551eb6413 --- /dev/null +++ b/plugins/kubernetes-common/package.json @@ -0,0 +1,49 @@ +{ + "name": "@backstage/plugin-kubernetes-common", + "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugin/kubernetes-common" + }, + "keywords": [ + "techdocs", + "kubernetes" + ], + "files": [ + "dist" + ], + "scripts": { + "build": "backstage-cli build", + "lint": "backstage-cli lint", + "test": "backstage-cli test --passWithNoTests", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "bugs": { + "url": "https://github.com/backstage/backstage/issues" + }, + "dependencies": { + "@backstage/catalog-model": "^0.7.6", + "@kubernetes/client-node": "^0.14.0" + }, + "devDependencies": { + "@backstage/cli": "^0.6.5" + }, + "jest": { + "roots": [ + ".." + ] + } +} diff --git a/plugins/kubernetes-common/src/index.ts b/plugins/kubernetes-common/src/index.ts new file mode 100644 index 0000000000..50e9534751 --- /dev/null +++ b/plugins/kubernetes-common/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './types'; diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts new file mode 100644 index 0000000000..23dc4f0f3c --- /dev/null +++ b/plugins/kubernetes-common/src/types.ts @@ -0,0 +1,107 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { + ExtensionsV1beta1Ingress, + V1ConfigMap, + V1Deployment, + V1HorizontalPodAutoscaler, + V1Pod, + V1ReplicaSet, + V1Service, +} from '@kubernetes/client-node'; +import { Entity } from '@backstage/catalog-model'; + +export interface KubernetesRequestBody { + auth?: { + google?: string; + }; + entity: Entity; +} + +export interface ClusterObjects { + cluster: { name: string }; + resources: FetchResponse[]; + errors: KubernetesFetchError[]; +} + +export interface ObjectsByEntityResponse { + items: ClusterObjects[]; +} + +export type AuthProviderType = 'google' | 'serviceAccount' | 'aws'; + +export type FetchResponse = + | PodFetchResponse + | ServiceFetchResponse + | ConfigMapFetchResponse + | DeploymentFetchResponse + | ReplicaSetsFetchResponse + | HorizontalPodAutoscalersFetchResponse + | IngressesFetchResponse + | CustomResourceFetchResponse; + +export interface PodFetchResponse { + type: 'pods'; + resources: Array; +} + +export interface ServiceFetchResponse { + type: 'services'; + resources: Array; +} + +export interface ConfigMapFetchResponse { + type: 'configmaps'; + resources: Array; +} + +export interface DeploymentFetchResponse { + type: 'deployments'; + resources: Array; +} + +export interface ReplicaSetsFetchResponse { + type: 'replicasets'; + resources: Array; +} + +export interface HorizontalPodAutoscalersFetchResponse { + type: 'horizontalpodautoscalers'; + resources: Array; +} + +export interface IngressesFetchResponse { + type: 'ingresses'; + resources: Array; +} + +export interface CustomResourceFetchResponse { + type: 'customresources'; + resources: Array; +} + +export interface KubernetesFetchError { + errorType: KubernetesErrorTypes; + statusCode?: number; + resourcePath?: string; +} + +export type KubernetesErrorTypes = + | 'BAD_REQUEST' + | 'UNAUTHORIZED_ERROR' + | 'SYSTEM_ERROR' + | 'UNKNOWN_ERROR'; diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 13f1a03d8e..d78842f351 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-kubernetes +## 0.4.3 + +### Patch Changes + +- f53fba29f: Adds @backstage/plugin-kubernetes-common library to share types between kubernetes frontend and backend. +- Updated dependencies [f65adcde7] +- Updated dependencies [81c54d1f2] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-react@0.1.5 + - @backstage/theme@0.2.7 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.4.2 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 8d21c79a24..8028c3a339 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.4.2", + "version": "0.4.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,7 +19,6 @@ "backstage", "kubernetes" ], - "configSchema": "schema.d.ts", "scripts": { "build": "backstage-cli plugin:build", "start": "backstage-cli plugin:serve", @@ -31,12 +30,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.4", - "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.7", - "@backstage/plugin-catalog-react": "^0.1.3", - "@backstage/plugin-kubernetes-backend": "^0.3.2", - "@backstage/theme": "^0.2.6", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", + "@backstage/core": "^0.7.8", + "@backstage/plugin-catalog-react": "^0.1.5", + "@backstage/plugin-kubernetes-common": "^0.1.0", + "@backstage/theme": "^0.2.7", "@kubernetes/client-node": "^0.14.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -50,7 +49,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", @@ -63,7 +62,6 @@ "msw": "^0.21.2" }, "files": [ - "dist", - "schema.d.ts" + "dist" ] } diff --git a/plugins/kubernetes/src/api/KubernetesBackendClient.ts b/plugins/kubernetes/src/api/KubernetesBackendClient.ts index 1eadec3fb0..19f1602143 100644 --- a/plugins/kubernetes/src/api/KubernetesBackendClient.ts +++ b/plugins/kubernetes/src/api/KubernetesBackendClient.ts @@ -19,7 +19,7 @@ import { KubernetesApi } from './types'; import { KubernetesRequestBody, ObjectsByEntityResponse, -} from '@backstage/plugin-kubernetes-backend'; +} from '@backstage/plugin-kubernetes-common'; export class KubernetesBackendClient implements KubernetesApi { private readonly discoveryApi: DiscoveryApi; diff --git a/plugins/kubernetes/src/api/types.ts b/plugins/kubernetes/src/api/types.ts index da1b36989a..2e91783132 100644 --- a/plugins/kubernetes/src/api/types.ts +++ b/plugins/kubernetes/src/api/types.ts @@ -18,7 +18,7 @@ import { createApiRef } from '@backstage/core'; import { KubernetesRequestBody, ObjectsByEntityResponse, -} from '@backstage/plugin-kubernetes-backend'; +} from '@backstage/plugin-kubernetes-common'; export const kubernetesApiRef = createApiRef({ id: 'plugin.kubernetes.service', diff --git a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx index 650e6ddff1..ba0026ac47 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { WarningPanel } from '@backstage/core'; import { Typography } from '@material-ui/core'; -import { ClusterObjects } from '@backstage/plugin-kubernetes-backend'; +import { ClusterObjects } from '@backstage/plugin-kubernetes-common'; const clustersWithErrorsToErrorMessage = ( clustersWithErrors: ClusterObjects[], diff --git a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx index cda8dd7e6b..1461f52112 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx @@ -31,7 +31,7 @@ import { StatusOK, } from '@backstage/core'; import { Entity } from '@backstage/catalog-model'; -import { ClusterObjects } from '@backstage/plugin-kubernetes-backend'; +import { ClusterObjects } from '@backstage/plugin-kubernetes-common'; import { ErrorPanel } from './ErrorPanel'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { DeploymentsAccordions } from '../DeploymentsAccordions'; diff --git a/plugins/kubernetes/src/error-detection/error-detection.test.ts b/plugins/kubernetes/src/error-detection/error-detection.test.ts index 8a4575c8be..5079ed9245 100644 --- a/plugins/kubernetes/src/error-detection/error-detection.test.ts +++ b/plugins/kubernetes/src/error-detection/error-detection.test.ts @@ -30,7 +30,7 @@ import * as maxedOutHpa from './__fixtures__/hpa-maxed-out.json'; import { FetchResponse, ObjectsByEntityResponse, -} from '@backstage/plugin-kubernetes-backend'; +} from '@backstage/plugin-kubernetes-common'; const CLUSTER_NAME = 'cluster-a'; diff --git a/plugins/kubernetes/src/error-detection/error-detection.ts b/plugins/kubernetes/src/error-detection/error-detection.ts index a44747969d..91544fe623 100644 --- a/plugins/kubernetes/src/error-detection/error-detection.ts +++ b/plugins/kubernetes/src/error-detection/error-detection.ts @@ -15,7 +15,7 @@ */ import { DetectedError, DetectedErrorsByCluster } from './types'; -import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-backend'; +import { ObjectsByEntityResponse } from '@backstage/plugin-kubernetes-common'; import { groupResponses } from '../utils/response'; import { detectErrorsInPods } from './pods'; import { detectErrorsInDeployments } from './deployments'; diff --git a/plugins/kubernetes/src/hooks/useKubernetesObjects.ts b/plugins/kubernetes/src/hooks/useKubernetesObjects.ts index 7c4de9ff1a..1dfe85fe38 100644 --- a/plugins/kubernetes/src/hooks/useKubernetesObjects.ts +++ b/plugins/kubernetes/src/hooks/useKubernetesObjects.ts @@ -22,7 +22,7 @@ import { useEffect, useState } from 'react'; import { KubernetesRequestBody, ObjectsByEntityResponse, -} from '@backstage/plugin-kubernetes-backend'; +} from '@backstage/plugin-kubernetes-common'; export interface KubernetesObjects { kubernetesObjects: ObjectsByEntityResponse | undefined; diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/AwsKubernetesAuthProvider.ts b/plugins/kubernetes/src/kubernetes-auth-provider/AwsKubernetesAuthProvider.ts index cab81f3622..ef541b0173 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/AwsKubernetesAuthProvider.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/AwsKubernetesAuthProvider.ts @@ -15,7 +15,7 @@ */ import { KubernetesAuthProvider } from './types'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-backend'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export class AwsKubernetesAuthProvider implements KubernetesAuthProvider { async decorateRequestBodyForAuth( diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/GoogleKubernetesAuthProvider.ts b/plugins/kubernetes/src/kubernetes-auth-provider/GoogleKubernetesAuthProvider.ts index 570806a1a4..9c9db0b9fc 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/GoogleKubernetesAuthProvider.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/GoogleKubernetesAuthProvider.ts @@ -16,7 +16,7 @@ import { OAuthApi } from '@backstage/core'; import { KubernetesAuthProvider } from './types'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-backend'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export class GoogleKubernetesAuthProvider implements KubernetesAuthProvider { authProvider: OAuthApi; diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts index dc9d9b5e0d..a96565f0c0 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/KubernetesAuthProviders.ts @@ -15,7 +15,7 @@ */ import { OAuthApi } from '@backstage/core'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-backend'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; import { KubernetesAuthProvider, KubernetesAuthProvidersApi } from './types'; import { GoogleKubernetesAuthProvider } from './GoogleKubernetesAuthProvider'; import { ServiceAccountKubernetesAuthProvider } from './ServiceAccountKubernetesAuthProvider'; diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/ServiceAccountKubernetesAuthProvider.ts b/plugins/kubernetes/src/kubernetes-auth-provider/ServiceAccountKubernetesAuthProvider.ts index 5d33521ae4..b88fd7679e 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/ServiceAccountKubernetesAuthProvider.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/ServiceAccountKubernetesAuthProvider.ts @@ -15,7 +15,7 @@ */ import { KubernetesAuthProvider } from './types'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-backend'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export class ServiceAccountKubernetesAuthProvider implements KubernetesAuthProvider { diff --git a/plugins/kubernetes/src/kubernetes-auth-provider/types.ts b/plugins/kubernetes/src/kubernetes-auth-provider/types.ts index 2bed2838b9..8d4cead11a 100644 --- a/plugins/kubernetes/src/kubernetes-auth-provider/types.ts +++ b/plugins/kubernetes/src/kubernetes-auth-provider/types.ts @@ -15,7 +15,7 @@ */ import { createApiRef } from '@backstage/core'; -import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-backend'; +import { KubernetesRequestBody } from '@backstage/plugin-kubernetes-common'; export interface KubernetesAuthProvider { decorateRequestBodyForAuth( diff --git a/plugins/kubernetes/src/utils/response.ts b/plugins/kubernetes/src/utils/response.ts index a9b10e7759..1d860ca226 100644 --- a/plugins/kubernetes/src/utils/response.ts +++ b/plugins/kubernetes/src/utils/response.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { FetchResponse } from '@backstage/plugin-kubernetes-backend'; +import { FetchResponse } from '@backstage/plugin-kubernetes-common'; import { GroupedResponses } from '../types/types'; // TODO this could probably be a lodash groupBy diff --git a/plugins/lighthouse/README.md b/plugins/lighthouse/README.md index 028547ea3e..06c9a1da12 100644 --- a/plugins/lighthouse/README.md +++ b/plugins/lighthouse/README.md @@ -28,20 +28,21 @@ _It's likely you will need to [enable CORS](https://developer.mozilla.org/en-US/ When you have an instance running that Backstage can hook into, first install the plugin into your app: ```sh -$ yarn add @backstage/plugin-lighthouse +# From your Backstage root directory +cd packages/app +yarn add @backstage/plugin-lighthouse ``` Modify your app routes in `App.tsx` to include the `LighthousePage` component exported from the plugin, for example: ```tsx -// At the top imports +// In packages/app/src/App.tsx import { LighthousePage } from '@backstage/plugin-lighthouse'; - - // ... - } /> - // ... -; +const routes = ( + + {/* ...other routes */} + } /> ``` Then configure the `lighthouse-audit-service` URL in your [`app-config.yaml`](https://github.com/backstage/backstage/blob/master/app-config.yaml). @@ -63,55 +64,45 @@ kind: Component metadata: # ... annotations: - # ... lighthouse.com/website-url: # A single website url e.g. https://backstage.io/ ``` > NOTE: The plugin only supports one website URL per component at this time. -Add a **Lighthouse tab** to the EntityPage: +Add a Lighthouse tab to the entity page: ```tsx -// packages/app/src/components/catalog/EntityPage.tsx -import { EmbeddedRouter as LighthouseRouter } from '@backstage/plugin-lighthouse'; +// In packages/app/src/components/catalog/EntityPage.tsx +import { EntityLighthouseContent } from '@backstage/plugin-lighthouse'; -// ... -const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( - - // ... - } - /> - -); +const websiteEntityPage = ( + + {/* other tabs... */} + + + ``` -> NOTE: The embedded router renders page content without a header section allowing it to be rendered within a -> catalog plugin page. +> NOTE: The embedded router renders page content without a header section +> allowing it to be rendered within a catalog plugin page. Add a **Lighthouse card** to the overview tab on the EntityPage: ```tsx -// packages/app/src/components/catalog/EntityPage.tsx +// In packages/app/src/components/catalog/EntityPage.tsx import { - LastLighthouseAuditCard, - isPluginApplicableToEntity as isLighthouseAvailable, + EntityLastLighthouseAuditCard, + isLighthouseAvailable, } from '@backstage/plugin-lighthouse'; -// ... - -const OverviewContent = ({ entity }: { entity: Entity }) => ( - - // ... - {isLighthouseAvailable(entity) && ( - - - - )} - -); +const overviewContent = ( + + {/* ...other content */} + + + + + + + ``` - -Link Lighthouse diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 2fce507756..a6ae0102bf 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -33,9 +33,9 @@ "dependencies": { "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.2", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -46,7 +46,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/lighthouse/src/components/Intro/index.tsx b/plugins/lighthouse/src/components/Intro/index.tsx index a89518a5ed..6b0307f9ec 100644 --- a/plugins/lighthouse/src/components/Intro/index.tsx +++ b/plugins/lighthouse/src/components/Intro/index.tsx @@ -46,7 +46,8 @@ with the environment variable \`LAS_CORS\` set to \`true\`._ When you have an instance running that Backstage can hook into, first install the plugin into your app: \`\`\`sh -$ yarn add @backstage/plugin-lighthouse +cd packages/app +yarn add @backstage/plugin-lighthouse \`\`\` Modify your app routes in \`App.tsx\` to include the \`LighthousePage\` component exported from the plugin, for example: diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 2dbb2a7eb8..dfe5217f9d 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/org/package.json b/plugins/org/package.json index 9ad4704630..2b802510f4 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -21,10 +21,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.6", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/core-api": "^0.2.16", "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -35,7 +35,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/pagerduty/README.md b/plugins/pagerduty/README.md index 36f380fdd5..e1ec7f89c7 100644 --- a/plugins/pagerduty/README.md +++ b/plugins/pagerduty/README.md @@ -19,6 +19,8 @@ This plugin provides: Install the plugin: ```bash +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-pagerduty ``` diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 821b89a5b1..0005b9aa3b 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -46,7 +46,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 0fada2e81f..06a96576e6 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-proxy-backend +## 0.2.7 + +### Patch Changes + +- cdb3426e5: Prefix proxy routes with `/` if not present in configuration +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/config@0.1.5 + ## 0.2.6 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 39a6e128bd..2c6fc9e08d 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.2.6", + "version": "0.2.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,8 +28,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.0", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", @@ -42,7 +42,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.5", + "@backstage/cli": "^0.6.10", "@types/http-proxy-middleware": "^0.19.3", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", diff --git a/plugins/register-component/README.md b/plugins/register-component/README.md index 9c5304385e..47db1a7a55 100644 --- a/plugins/register-component/README.md +++ b/plugins/register-component/README.md @@ -19,6 +19,7 @@ When installed it is accessible on [localhost:3000/register-component](localhost 1. Install plugin and its dependency `plugin-catalog` ```bash +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-register-component ``` diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 38d1e3202e..5052fd3342 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -31,21 +31,21 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", "react": "^16.13.1", "react-dom": "^16.13.1", - "react-hook-form": "^6.6.0", + "react-hook-form": "^6.15.4", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index 8e20e93555..9aa0d77e69 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-rollbar-backend +## 0.1.10 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] + - @backstage/backend-common@0.7.0 + - @backstage/config@0.1.5 + ## 0.1.9 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index 05b02b2c39..c7eb819dc1 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-rollbar-backend", - "version": "0.1.9", + "version": "0.1.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.3", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/config": "^0.1.5", "@types/express": "^4.17.6", "axios": "^0.21.1", "camelcase-keys": "^6.2.2", @@ -47,7 +47,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/rollbar/README.md b/plugins/rollbar/README.md index 3a33fe223c..bdf0541fa0 100644 --- a/plugins/rollbar/README.md +++ b/plugins/rollbar/README.md @@ -9,25 +9,23 @@ Website: [https://rollbar.com/](https://rollbar.com/) 2. If you have standalone app (you didn't clone this repo), then do ```bash +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-rollbar ``` 3. Add to the app `EntityPage` component: ```tsx -// packages/app/src/components/catalog/EntityPage.tsx +// In packages/app/src/components/catalog/EntityPage.tsx import { EntityRollbarContent } from '@backstage/plugin-rollbar'; -// ... const serviceEntityPage = ( - - ... + + {/* other tabs... */} - ... - -); ``` 4. Setup the `app-config.yaml` and account token environment variable diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 64ea010b54..5563ad0e40 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index f591568d44..b4aa856606 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,78 @@ # @backstage/plugin-scaffolder-backend +## 0.11.0 + +### Minor Changes + +- e0bfd3d44: Migrate the plugin to use the `ContainerRunner` interface instead of `runDockerContainer(…)`. + It also provides the `ContainerRunner` to the individual templaters instead of to the `createRouter` function. + + To apply this change to an existing backend application, add the following to `src/plugins/scaffolder.ts`: + + ```diff + - import { SingleHostDiscovery } from '@backstage/backend-common'; + + import { + + DockerContainerRunner, + + SingleHostDiscovery, + + } from '@backstage/backend-common'; + + + export default async function createPlugin({ + logger, + config, + database, + reader, + }: PluginEnvironment): Promise { + + const dockerClient = new Docker(); + + const containerRunner = new DockerContainerRunner({ dockerClient }); + + + const cookiecutterTemplater = new CookieCutter({ containerRunner }); + - const cookiecutterTemplater = new CookieCutter(); + + const craTemplater = new CreateReactAppTemplater({ containerRunner }); + - const craTemplater = new CreateReactAppTemplater(); + const templaters = new Templaters(); + + templaters.register('cookiecutter', cookiecutterTemplater); + templaters.register('cra', craTemplater); + + const preparers = await Preparers.fromConfig(config, { logger }); + const publishers = await Publishers.fromConfig(config, { logger }); + + - const dockerClient = new Docker(); + + const discovery = SingleHostDiscovery.fromConfig(config); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + preparers, + templaters, + publishers, + logger, + config, + - dockerClient, + database, + catalogClient, + reader, + }); + } + ``` + +### Patch Changes + +- 38ca05168: The default `@octokit/rest` dependency was bumped to `"^18.5.3"`. +- 69eefb5ae: Fix GithubPR built-in action `credentialsProvider.getCredentials` URL. + Adding Documentation for GitHub PR built-in action. +- 75c8cec39: bump `jsonschema` from 1.2.7 to 1.4.0 +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/backend-common@0.7.0 + - @backstage/integration@0.5.2 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + ## 0.10.1 ### Patch Changes diff --git a/plugins/scaffolder-backend/README.md b/plugins/scaffolder-backend/README.md index 185938b006..304d43a750 100644 --- a/plugins/scaffolder-backend/README.md +++ b/plugins/scaffolder-backend/README.md @@ -1,7 +1,25 @@ -# Title +# Scaffolder Backend Welcome to the scaffolder plugin! -## Sub-section 1 +## Jobs -## Sub-section 2 +Documentation for `Jobs` here + +## Stages + +Documentation for `Stages` here + +## Tasks + +Documentation for `Tasks` here + +## Actions + +### Built-in: + +- #### GitHub Pull Request + - Minimum permissions required for GitHub App for creating a Pull Request with the built-in action: + - Read and Write permissions for `Contents`. + - Read and write permissions for `Pull Requests` and `Issues`. + - Read permissions on `Metadata`. diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 355bed4de0..9cd951f7d8 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.10.1", + "version": "0.11.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,16 +29,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.3", - "@backstage/catalog-client": "^0.3.10", - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.1", + "@backstage/integration": "^0.5.2", "@gitbeaker/core": "^28.0.2", "@gitbeaker/node": "^28.0.2", - "@octokit/rest": "^18.0.12", - "@types/dockerode": "^3.2.1", + "@octokit/rest": "^18.5.3", "@types/express": "^4.17.6", "@types/git-url-parse": "^9.0.0", "azure-devops-node-api": "^10.1.1", @@ -46,7 +45,6 @@ "compression": "^1.7.4", "cors": "^2.8.5", "cross-fetch": "^3.0.6", - "dockerode": "^3.2.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "^9.0.0", @@ -67,7 +65,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/test-utils": "^0.1.10", "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 2d4b8fb4c5..0209fc50dd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -17,6 +17,7 @@ import { UrlReader } from '@backstage/backend-common'; import { CatalogApi } from '@backstage/catalog-client'; import { ScmIntegrations } from '@backstage/integration'; +import { TemplaterBuilder } from '../../stages'; import { createCatalogRegisterAction } from './catalog'; import { createFetchCookiecutterAction, createFetchPlainAction } from './fetch'; import { @@ -26,23 +27,14 @@ import { createPublishGithubPullRequestAction, createPublishGitlabAction, } from './publish'; -import Docker from 'dockerode'; -import { TemplaterBuilder } from '../../stages'; export const createBuiltinActions = (options: { reader: UrlReader; integrations: ScmIntegrations; - dockerClient: Docker; catalogClient: CatalogApi; templaters: TemplaterBuilder; }) => { - const { - reader, - integrations, - dockerClient, - templaters, - catalogClient, - } = options; + const { reader, integrations, templaters, catalogClient } = options; return [ createFetchPlainAction({ @@ -52,7 +44,6 @@ export const createBuiltinActions = (options: { createFetchCookiecutterAction({ reader, integrations, - dockerClient, templaters, }), createPublishGithubAction({ diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts index 628107155d..a0f94816bd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts @@ -15,16 +15,16 @@ */ jest.mock('./helpers'); +import { getVoidLogger, UrlReader } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import mock from 'mock-fs'; import os from 'os'; import { resolve as resolvePath } from 'path'; -import { createFetchCookiecutterAction } from './cookiecutter'; -import { ScmIntegrations } from '@backstage/integration'; -import { ConfigReader } from '@backstage/config'; -import { Templaters } from '../../../stages/templater'; import { PassThrough } from 'stream'; -import { getVoidLogger, UrlReader } from '@backstage/backend-common'; +import { Templaters } from '../../../stages/templater'; +import { createFetchCookiecutterAction } from './cookiecutter'; import { fetchContents } from './helpers'; -import mock from 'mock-fs'; describe('fetch:cookiecutter', () => { const integrations = ScmIntegrations.fromConfig( @@ -40,7 +40,6 @@ describe('fetch:cookiecutter', () => { const templaters = new Templaters(); const cookiecutterTemplater = { run: jest.fn() }; - const mockDockerClient = {}; const mockTmpDir = os.tmpdir(); const mockContext = { input: { @@ -67,7 +66,6 @@ describe('fetch:cookiecutter', () => { const action = createFetchCookiecutterAction({ integrations, templaters, - dockerClient: mockDockerClient as any, reader: mockReader, }); @@ -102,7 +100,6 @@ describe('fetch:cookiecutter', () => { expect(cookiecutterTemplater.run).toHaveBeenCalledWith({ workspacePath: mockTmpDir, - dockerClient: mockDockerClient, logStream: mockContext.logStream, values: mockContext.input.values, }); @@ -123,7 +120,6 @@ describe('fetch:cookiecutter', () => { expect(cookiecutterTemplater.run).toHaveBeenCalledWith({ workspacePath: mockTmpDir, - dockerClient: mockDockerClient, logStream: mockContext.logStream, values: { ...mockContext.input.values, @@ -166,7 +162,6 @@ describe('fetch:cookiecutter', () => { const newAction = createFetchCookiecutterAction({ integrations, templaters: templatersWithoutCookiecutter, - dockerClient: mockDockerClient as any, reader: mockReader, }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts index fca791c7bd..83b373c3de 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts @@ -14,24 +14,22 @@ * limitations under the License. */ -import fs from 'fs-extra'; -import { resolve as resolvePath } from 'path'; -import Docker from 'dockerode'; import { UrlReader } from '@backstage/backend-common'; +import { JsonObject } from '@backstage/config'; import { InputError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; -import { JsonObject } from '@backstage/config'; +import fs from 'fs-extra'; +import { resolve as resolvePath } from 'path'; import { TemplaterBuilder, TemplaterValues } from '../../../stages/templater'; -import { fetchContents } from './helpers'; import { createTemplateAction } from '../../createTemplateAction'; +import { fetchContents } from './helpers'; export function createFetchCookiecutterAction(options: { - dockerClient: Docker; reader: UrlReader; integrations: ScmIntegrations; templaters: TemplaterBuilder; }) { - const { dockerClient, reader, templaters, integrations } = options; + const { reader, templaters, integrations } = options; return createTemplateAction<{ url: string; @@ -134,7 +132,6 @@ export function createFetchCookiecutterAction(options: { // Will execute the template in ./template and put the result in ./result await cookiecutter.run({ workspacePath: workDir, - dockerClient, logStream: ctx.logStream, values, }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts index e2e4f17efb..5dd4046811 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -87,7 +87,9 @@ export const defaultClientFactory = async ({ } const { token } = await credentialsProvider.getCredentials({ - url: `${host}/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, + url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent( + repo, + )}`, }); if (!token) { diff --git a/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts b/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts index 425fc07946..005df09fec 100644 --- a/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts +++ b/plugins/scaffolder-backend/src/scaffolder/jobs/processor.ts @@ -19,20 +19,12 @@ import fs from 'fs-extra'; import { Processor, Job, StageContext, StageInput } from './types'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import * as uuid from 'uuid'; -import Docker from 'dockerode'; import path from 'path'; -import { TemplaterValues, TemplaterBase } from '../stages/templater'; -import { PreparerBuilder } from '../stages/prepare'; +import { TemplaterValues } from '../stages/templater'; import { makeLogStream } from './logger'; import { Logger } from 'winston'; import { Config } from '@backstage/config'; -export type JobProcessorArguments = { - preparers: PreparerBuilder; - templater: TemplaterBase; - dockerClient: Docker; -}; - export type JobAndDirectoryTuple = { job: Job; directory: string; diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/legacy.ts b/plugins/scaffolder-backend/src/scaffolder/stages/legacy.ts index c7c52fa320..bb7ab40d57 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/legacy.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/legacy.ts @@ -14,21 +14,19 @@ * limitations under the License. */ -import { FilePreparer, PreparerBuilder } from './prepare'; -import Docker from 'dockerode'; -import { TemplaterBuilder, TemplaterValues } from './templater'; -import { PublisherBuilder } from './publish'; import { createTemplateAction } from '../actions'; +import { FilePreparer, PreparerBuilder } from './prepare'; +import { PublisherBuilder } from './publish'; +import { TemplaterBuilder, TemplaterValues } from './templater'; type Options = { - dockerClient: Docker; preparers: PreparerBuilder; templaters: TemplaterBuilder; publishers: PublisherBuilder; }; export function createLegacyActions(options: Options) { - const { dockerClient, preparers, templaters, publishers } = options; + const { preparers, templaters, publishers } = options; return [ createTemplateAction({ @@ -55,7 +53,6 @@ export function createLegacyActions(options: Options) { const templater = templaters.get(ctx.input.templater as string); await templater.run({ workspacePath: ctx.workspacePath, - dockerClient, logStream: ctx.logStream, values: ctx.input.values as TemplaterValues, }); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts index 7806f089ff..59fb72d515 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts @@ -14,16 +14,14 @@ * limitations under the License. */ -const runDockerContainer = jest.fn(); const runCommand = jest.fn(); const commandExists = jest.fn(); jest.mock('./helpers', () => ({ runCommand })); -jest.mock('@backstage/backend-common', () => ({ runDockerContainer })); jest.mock('command-exists-promise', () => commandExists); jest.mock('fs-extra'); -import Docker from 'dockerode'; +import { ContainerRunner } from '@backstage/backend-common'; import fs from 'fs-extra'; import parseGitUrl from 'git-url-parse'; import path from 'path'; @@ -31,7 +29,9 @@ import { PassThrough } from 'stream'; import { CookieCutter } from './cookiecutter'; describe('CookieCutter Templater', () => { - const mockDocker = {} as Docker; + const containerRunner: jest.Mocked = { + runContainer: jest.fn(), + }; beforeEach(() => { jest.clearAllMocks(); @@ -50,11 +50,10 @@ describe('CookieCutter Templater', () => { jest.spyOn(fs, 'readdir').mockResolvedValueOnce(['newthing'] as any); - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await templater.run({ workspacePath: 'tempdir', values, - dockerClient: mockDocker, }); expect(fs.ensureDir).toBeCalledWith(path.join('tempdir', 'intermediate')); @@ -83,11 +82,10 @@ describe('CookieCutter Templater', () => { }, }; - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await templater.run({ workspacePath: 'tempdir', values, - dockerClient: mockDocker, }); expect(fs.writeJSON).toBeCalledWith( @@ -115,12 +113,11 @@ describe('CookieCutter Templater', () => { }, }; - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await expect( templater.run({ workspacePath: 'tempdir', values, - dockerClient: mockDocker, }), ).rejects.toThrow('BAM'); }); @@ -140,23 +137,16 @@ describe('CookieCutter Templater', () => { .spyOn(fs, 'realpath') .mockImplementation(x => Promise.resolve(x.toString())); - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await templater.run({ workspacePath: 'tempdir', values, - dockerClient: mockDocker, }); - expect(runDockerContainer).toHaveBeenCalledWith({ + expect(containerRunner.runContainer).toHaveBeenCalledWith({ imageName: 'spotify/backstage-cookiecutter', - args: [ - 'cookiecutter', - '--no-input', - '-o', - '/output', - '/input', - '--verbose', - ], + command: 'cookiecutter', + args: ['--no-input', '-o', '/output', '/input', '--verbose'], envVars: { HOME: '/tmp' }, mountDirs: { [path.join('tempdir', 'template')]: '/input', @@ -164,7 +154,6 @@ describe('CookieCutter Templater', () => { }, workingDir: '/input', logStream: undefined, - dockerClient: mockDocker, }); }); @@ -177,14 +166,13 @@ describe('CookieCutter Templater', () => { jest.spyOn(fs, 'readdir').mockResolvedValueOnce(['newthing'] as any); - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await templater.run({ workspacePath: 'tempdir', values, - dockerClient: mockDocker, }); - expect(runDockerContainer).toHaveBeenCalledWith( + expect(containerRunner.runContainer).toHaveBeenCalledWith( expect.objectContaining({ imageName: 'foo/cookiecutter-image-with-extensions', }), @@ -205,24 +193,17 @@ describe('CookieCutter Templater', () => { jest.spyOn(fs, 'readdir').mockResolvedValueOnce(['newthing'] as any); - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await templater.run({ workspacePath: 'tempdir', values, logStream: stream, - dockerClient: mockDocker, }); - expect(runDockerContainer).toHaveBeenCalledWith({ + expect(containerRunner.runContainer).toHaveBeenCalledWith({ imageName: 'spotify/backstage-cookiecutter', - args: [ - 'cookiecutter', - '--no-input', - '-o', - '/output', - '/input', - '--verbose', - ], + command: 'cookiecutter', + args: ['--no-input', '-o', '/output', '/input', '--verbose'], envVars: { HOME: '/tmp' }, mountDirs: { [path.join('tempdir', 'template')]: '/input', @@ -230,7 +211,6 @@ describe('CookieCutter Templater', () => { }, workingDir: '/input', logStream: stream, - dockerClient: mockDocker, }); }); @@ -250,12 +230,11 @@ describe('CookieCutter Templater', () => { jest.spyOn(fs, 'readdir').mockResolvedValueOnce(['newthing'] as any); commandExists.mockImplementationOnce(() => () => true); - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await templater.run({ workspacePath: 'tempdir', values, logStream: stream, - dockerClient: mockDocker, }); expect(runCommand).toHaveBeenCalledWith({ @@ -280,7 +259,7 @@ describe('CookieCutter Templater', () => { .spyOn(fs, 'readdir') .mockImplementationOnce(() => Promise.resolve([])); - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); await expect( templater.run({ workspacePath: 'tempdir', @@ -292,7 +271,6 @@ describe('CookieCutter Templater', () => { }, }, logStream: stream, - dockerClient: mockDocker, }), ).rejects.toThrow(/No data generated by cookiecutter/); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts index e60c5efb4d..8819a90d63 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { runDockerContainer } from '@backstage/backend-common'; +import { ContainerRunner } from '@backstage/backend-common'; import { JsonValue } from '@backstage/config'; import fs from 'fs-extra'; import path from 'path'; @@ -24,6 +24,12 @@ import { TemplaterBase, TemplaterRunOptions } from './types'; const commandExists = require('command-exists-promise'); export class CookieCutter implements TemplaterBase { + private readonly containerRunner: ContainerRunner; + + constructor({ containerRunner }: { containerRunner: ContainerRunner }) { + this.containerRunner = containerRunner; + } + private async fetchTemplateCookieCutter( directory: string, ): Promise> { @@ -40,7 +46,6 @@ export class CookieCutter implements TemplaterBase { public async run({ workspacePath, - dockerClient, values, logStream, }: TemplaterRunOptions): Promise { @@ -74,23 +79,16 @@ export class CookieCutter implements TemplaterBase { logStream, }); } else { - await runDockerContainer({ + await this.containerRunner.runContainer({ imageName: imageName || 'spotify/backstage-cookiecutter', - args: [ - 'cookiecutter', - '--no-input', - '-o', - '/output', - '/input', - '--verbose', - ], + command: 'cookiecutter', + args: ['--no-input', '-o', '/output', '/input', '--verbose'], mountDirs, workingDir: '/input', // Set the home directory inside the container as something that applications can // write to, otherwise they will just fail trying to write to / envVars: { HOME: '/tmp' }, logStream, - dockerClient, }); } diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts index 3d03b002c0..36716b6c99 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { runDockerContainer } from '@backstage/backend-common'; +import { ContainerRunner } from '@backstage/backend-common'; import fs from 'fs-extra'; import path from 'path'; import * as yaml from 'yaml'; @@ -25,11 +25,16 @@ import { TemplaterBase, TemplaterRunOptions } from '../types'; const GITHUB_ACTIONS_ANNOTATION = 'github.com/project-slug'; export class CreateReactAppTemplater implements TemplaterBase { + private readonly containerRunner: ContainerRunner; + + constructor({ containerRunner }: { containerRunner: ContainerRunner }) { + this.containerRunner = containerRunner; + } + public async run({ workspacePath, values, logStream, - dockerClient, }: TemplaterRunOptions): Promise { const { component_id: componentName, @@ -46,23 +51,20 @@ export class CreateReactAppTemplater implements TemplaterBase { [intermediateDir]: '/result', }; - await runDockerContainer({ + await this.containerRunner.runContainer({ imageName: 'node:lts-alpine', + command: ['npx'], args: [ 'create-react-app', componentName as string, withTypescript ? ' --template typescript' : '', ], mountDirs, + workingDir: '/result', logStream: logStream, - dockerClient: dockerClient, // Set the home directory inside the container as something that applications can // write to, otherwise they will just fail trying to write to / envVars: { HOME: '/tmp' }, - createOptions: { - Entrypoint: ['npx'], - WorkingDir: '/result', - }, }); // if cookiecutter was successful, intermediateDir will contain diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts index d390bfbd9d..713ec514c0 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/templaters.test.ts @@ -14,10 +14,15 @@ * limitations under the License. */ +import { ContainerRunner } from '@backstage/backend-common'; import { CookieCutter } from './cookiecutter'; import { Templaters } from './templaters'; describe('Templaters', () => { + const containerRunner: jest.Mocked = { + runContainer: jest.fn(), + }; + it('should throw an error when the templater is not registered', () => { const templaters = new Templaters(); @@ -29,7 +34,7 @@ describe('Templaters', () => { }); it('should return the correct templater when the templater matches', () => { const templaters = new Templaters(); - const templater = new CookieCutter(); + const templater = new CookieCutter({ containerRunner }); templaters.register('cookiecutter', templater); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts index fa14788d22..c5fe454e33 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { Writable } from 'stream'; -import Docker from 'dockerode'; + import gitUrlParse from 'git-url-parse'; +import type { Writable } from 'stream'; /** * Currently the required template values. The owner @@ -48,7 +48,6 @@ export type TemplaterRunOptions = { workspacePath: string; values: TemplaterValues; logStream?: Writable; - dockerClient: Docker; }; export type TemplaterBase = { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index 9e4deab31a..1d0d5ad3a3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -143,9 +143,10 @@ export class TaskWorker { }); if (action.schema?.input) { - const validateResult = validateJsonSchema(input, action.schema, { - propertyName: 'input', - }); + const validateResult = validateJsonSchema( + input, + action.schema.input, + ); if (!validateResult.valid) { const errors = validateResult.errors.join(', '); throw new InputError( diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index bb52edaaea..e8aa76a63a 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -29,20 +29,17 @@ jest.doMock('fs-extra', () => ({ })); import { - SingleConnectionDatabaseManager, - PluginDatabaseManager, getVoidLogger, + PluginDatabaseManager, + SingleConnectionDatabaseManager, UrlReaders, } from '@backstage/backend-common'; +import { CatalogApi } from '@backstage/catalog-client'; import { ConfigReader } from '@backstage/config'; import express from 'express'; import request from 'supertest'; +import { Preparers, Publishers, Templaters } from '../scaffolder'; import { createRouter } from './router'; -import { Templaters, Preparers, Publishers } from '../scaffolder'; -import Docker from 'dockerode'; -import { CatalogApi } from '@backstage/catalog-client'; - -jest.mock('dockerode'); const createCatalogClient = (templates: any[] = []) => ({ @@ -115,7 +112,6 @@ describe('createRouter - working directory', () => { templaters: new Templaters(), publishers: new Publishers(), config: new ConfigReader(workDirConfig('/path')), - dockerClient: new Docker(), database: createDatabase(), catalogClient: createCatalogClient([template]), reader: mockUrlReader, @@ -130,7 +126,6 @@ describe('createRouter - working directory', () => { templaters: new Templaters(), publishers: new Publishers(), config: new ConfigReader(workDirConfig('/path')), - dockerClient: new Docker(), database: createDatabase(), catalogClient: createCatalogClient([template]), reader: mockUrlReader, @@ -160,7 +155,6 @@ describe('createRouter - working directory', () => { templaters: new Templaters(), publishers: new Publishers(), config: new ConfigReader({}), - dockerClient: new Docker(), database: createDatabase(), catalogClient: createCatalogClient([template]), reader: mockUrlReader, @@ -234,7 +228,6 @@ describe('createRouter', () => { templaters: new Templaters(), publishers: new Publishers(), config: new ConfigReader({}), - dockerClient: new Docker(), database: createDatabase(), catalogClient: createCatalogClient([template]), reader: mockUrlReader, diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 35a57da6a2..5e0790526e 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -15,7 +15,6 @@ */ import { Config } from '@backstage/config'; -import Docker from 'dockerode'; import express from 'express'; import { resolve as resolvePath, dirname } from 'path'; import Router from 'express-promise-router'; @@ -62,7 +61,6 @@ export interface RouterOptions { logger: Logger; config: Config; reader: UrlReader; - dockerClient: Docker; database: PluginDatabaseManager; catalogClient: CatalogApi; actions?: TemplateAction[]; @@ -96,7 +94,6 @@ export async function createRouter( logger: parentLogger, config, reader, - dockerClient, database, catalogClient, actions, @@ -124,13 +121,11 @@ export async function createRouter( ? actions : [ ...createLegacyActions({ - dockerClient, preparers, publishers, templaters, }), ...createBuiltinActions({ - dockerClient, integrations, catalogClient, templaters, @@ -243,7 +238,6 @@ export async function createRouter( const templater = templaters.get(ctx.entity.spec.templater); await templater.run({ workspacePath: ctx.workspacePath, - dockerClient, logStream: ctx.logStream, values: ctx.values, }); diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 1d0bbcede9..b67740ecc1 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-scaffolder +## 0.9.3 + +### Patch Changes + +- 9314a8592: Close eventSource upon completion of a scaffolder task +- d8b81fd28: Bump `json-schema` dependency from `0.2.5` to `0.3.0`. +- Updated dependencies [38ca05168] +- Updated dependencies [f65adcde7] +- Updated dependencies [81c54d1f2] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/integration@0.5.2 + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-react@0.1.5 + - @backstage/theme@0.2.7 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + ## 0.9.2 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 32229ccbc6..f2f69c029b 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.9.2", + "version": "0.9.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,14 +30,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.10", - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.7", - "@backstage/integration": "^0.5.1", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", + "@backstage/core": "^0.7.8", + "@backstage/integration": "^0.5.2", "@backstage/integration-react": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/plugin-catalog-react": "^0.1.5", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -59,7 +59,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index 6caa2a4446..0c5eb2b268 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -214,6 +214,7 @@ export class ScaffolderClient implements ScaffolderApi { subscriber.error(ex); } } + eventSource.close(); subscriber.complete(); }); eventSource.addEventListener('error', event => { diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index e39fbb5579..99077b9b62 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -25,8 +25,8 @@ "@types/lunr": "^2.3.3" }, "devDependencies": { - "@backstage/backend-common": "^0.6.3", - "@backstage/cli": "^0.6.9" + "@backstage/backend-common": "^0.7.0", + "@backstage/cli": "^0.6.10" }, "files": [ "dist" diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 9313e2e83a..fd429aae62 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-search-backend +## 0.1.4 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] + - @backstage/backend-common@0.7.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index a94fed35b9..4de8ada9bc 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,7 +19,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.3", + "@backstage/backend-common": "^0.7.0", "@backstage/search-common": "^0.1.1", "@backstage/plugin-search-backend-node": "^0.1.3", "@types/express": "^4.17.6", @@ -29,7 +29,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/search/package.json b/plugins/search/package.json index 2b9db3444e..b135d184f6 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/catalog-model": "^0.7.3", "@backstage/plugin-catalog-react": "^0.1.2", "@backstage/search-common": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -44,7 +44,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sentry/README.md b/plugins/sentry/README.md index 9bdb2dc16d..e0c56e842c 100644 --- a/plugins/sentry/README.md +++ b/plugins/sentry/README.md @@ -9,8 +9,8 @@ The Sentry Plugin displays issues from [Sentry](https://sentry.io). 1. Install the Sentry Plugin: ```bash -# packages/app - +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-sentry ``` diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index e34d8bcef9..a5b1ac7d68 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -32,9 +32,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -46,7 +46,7 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sonarqube/README.md b/plugins/sonarqube/README.md index 5c12832e57..d0251e91ef 100644 --- a/plugins/sonarqube/README.md +++ b/plugins/sonarqube/README.md @@ -9,7 +9,8 @@ The SonarQube Plugin displays code statistics from [SonarCloud](https://sonarclo 1. Install the SonarQube Plugin: ```bash -# packages/app +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-sonarqube ``` diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index c31b99bac8..648f945e03 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -34,8 +34,8 @@ "dependencies": { "@backstage/catalog-model": "^0.7.7", "@backstage/plugin-catalog-react": "^0.1.3", - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/splunk-on-call/README.md b/plugins/splunk-on-call/README.md index 5c1dda33fc..33cfa091ca 100644 --- a/plugins/splunk-on-call/README.md +++ b/plugins/splunk-on-call/README.md @@ -20,6 +20,8 @@ This plugin provides: Install the plugin: ```bash +# From your Backstage root directory +cd packages/app yarn add @backstage/plugin-splunk-on-call ``` diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 9e93004bf5..63fb68c0b2 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.4", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/plugin-catalog-react": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -45,7 +45,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index 54cefe487f..9f0a5b87c3 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-tech-radar +## 0.3.10 + +### Patch Changes + +- b2e2ec753: Update README for composability +- Updated dependencies [f65adcde7] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] + - @backstage/core@0.7.8 + - @backstage/theme@0.2.7 + ## 0.3.9 ### Patch Changes diff --git a/plugins/tech-radar/README.md b/plugins/tech-radar/README.md index 7f97f88ed4..814ab6cba5 100644 --- a/plugins/tech-radar/README.md +++ b/plugins/tech-radar/README.md @@ -26,6 +26,7 @@ The Tech Radar can be used in two ways: For either simple or advanced installations, you'll need to add the dependency using Yarn: ```sh +# From your Backstage root directory cd packages/app yarn add @backstage/plugin-tech-radar ``` @@ -35,12 +36,12 @@ yarn add @backstage/plugin-tech-radar Modify your app routes to include the Router component exported from the tech radar, for example: ```tsx -// in packages/app/src/App.tsx +// In packages/app/src/App.tsx import { TechRadarPage } from '@backstage/plugin-tech-radar'; const routes = ( - {/* ... */} + {/* ...other routes */} } diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 37ad7d8211..2a2619bbc4 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-radar", - "version": "0.3.9", + "version": "0.3.10", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -43,7 +43,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 2923e452a7..59a116f653 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,79 @@ # @backstage/plugin-techdocs-backend +## 0.8.0 + +### Minor Changes + +- e0bfd3d44: Migrate the plugin to use the `ContainerRunner` interface instead of `runDockerContainer(…)`. + It also provides the `ContainerRunner` to the generators instead of to the `createRouter` function. + + To apply this change to an existing backend application, add the following to `src/plugins/techdocs.ts`: + + ```diff + + import { DockerContainerRunner } from '@backstage/backend-common'; + + // ... + + export default async function createPlugin({ + logger, + config, + discovery, + reader, + }: PluginEnvironment): Promise { + // Preparers are responsible for fetching source files for documentation. + const preparers = await Preparers.fromConfig(config, { + logger, + reader, + }); + + + // Docker client (conditionally) used by the generators, based on techdocs.generators config. + + const dockerClient = new Docker(); + + const containerRunner = new DockerContainerRunner({ dockerClient }); + + // Generators are used for generating documentation sites. + const generators = await Generators.fromConfig(config, { + logger, + + containerRunner, + }); + + // Publisher is used for + // 1. Publishing generated files to storage + // 2. Fetching files from storage and passing them to TechDocs frontend. + const publisher = await Publisher.fromConfig(config, { + logger, + discovery, + }); + + // checks if the publisher is working and logs the result + await publisher.getReadiness(); + + - // Docker client (conditionally) used by the generators, based on techdocs.generators config. + - const dockerClient = new Docker(); + + return await createRouter({ + preparers, + generators, + publisher, + - dockerClient, + logger, + config, + discovery, + }); + } + ``` + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [e0bfd3d44] +- Updated dependencies [d8b81fd28] +- Updated dependencies [e9e56b01a] + - @backstage/backend-common@0.7.0 + - @backstage/techdocs-common@0.6.0 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.7.1 ### Patch Changes diff --git a/plugins/techdocs-backend/examples/documented-component/docs/code/code-sample.md b/plugins/techdocs-backend/examples/documented-component/docs/code/code-sample.md index 9f8e1bd739..f41ab1d506 100644 --- a/plugins/techdocs-backend/examples/documented-component/docs/code/code-sample.md +++ b/plugins/techdocs-backend/examples/documented-component/docs/code/code-sample.md @@ -5,19 +5,19 @@ This page provides some sample code which may be used in your example component. This code uses TypeScript, and the Markdown code fence to wrap the code. ```typescript -const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - +const serviceEntityPage = ( + + + + + + + + + + + + ); ``` diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 53e0865430..1424381f30 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.7.1", + "version": "0.8.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,12 +30,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.0", - "@backstage/catalog-model": "^0.7.7", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/techdocs-common": "^0.5.0", - "@types/dockerode": "^3.2.1", + "@backstage/techdocs-common": "^0.6.0", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", "dockerode": "^3.2.1", @@ -46,7 +45,8 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.6.8", + "@backstage/cli": "^0.6.10", + "@types/dockerode": "^3.2.1", "supertest": "^6.1.3" }, "files": [ diff --git a/plugins/techdocs-backend/src/DocsBuilder/builder.ts b/plugins/techdocs-backend/src/DocsBuilder/builder.ts index 9dd53f159e..d1aa1e08be 100644 --- a/plugins/techdocs-backend/src/DocsBuilder/builder.ts +++ b/plugins/techdocs-backend/src/DocsBuilder/builder.ts @@ -28,7 +28,6 @@ import { PublisherBase, UrlPreparer, } from '@backstage/techdocs-common'; -import Docker from 'dockerode'; import fs from 'fs-extra'; import os from 'os'; import path from 'path'; @@ -41,7 +40,6 @@ type DocsBuilderArguments = { publisher: PublisherBase; entity: Entity; logger: Logger; - dockerClient: Docker; }; export class DocsBuilder { @@ -50,7 +48,6 @@ export class DocsBuilder { private publisher: PublisherBase; private entity: Entity; private logger: Logger; - private dockerClient: Docker; constructor({ preparers, @@ -58,14 +55,12 @@ export class DocsBuilder { publisher, entity, logger, - dockerClient, }: DocsBuilderArguments) { this.preparer = preparers.get(entity); this.generator = generators.get(entity); this.publisher = publisher; this.entity = entity; this.logger = logger; - this.dockerClient = dockerClient; } public async build(): Promise { @@ -157,7 +152,6 @@ export class DocsBuilder { await this.generator.run({ inputDir: preparedDir, outputDir, - dockerClient: this.dockerClient, parsedLocationAnnotation, etag: newEtag, }); diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index 121ddddef5..9f383ea31d 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -24,7 +24,6 @@ import { PublisherBase, } from '@backstage/techdocs-common'; import fetch from 'cross-fetch'; -import Docker from 'dockerode'; import express from 'express'; import Router from 'express-promise-router'; import { Knex } from 'knex'; @@ -40,7 +39,6 @@ type RouterOptions = { discovery: PluginEndpointDiscovery; database?: Knex; // TODO: Make database required when we're implementing database stuff. config: Config; - dockerClient: Docker; }; export async function createRouter({ @@ -48,7 +46,6 @@ export async function createRouter({ generators, publisher, config, - dockerClient, logger, discovery, }: RouterOptions): Promise { @@ -165,7 +162,6 @@ export async function createRouter({ preparers, generators, publisher, - dockerClient, logger, entity, }); diff --git a/plugins/techdocs-backend/src/service/standaloneServer.ts b/plugins/techdocs-backend/src/service/standaloneServer.ts index 0fd23a4f7d..af03987870 100644 --- a/plugins/techdocs-backend/src/service/standaloneServer.ts +++ b/plugins/techdocs-backend/src/service/standaloneServer.ts @@ -16,21 +16,22 @@ import { createServiceBuilder, + DockerContainerRunner, SingleHostDiscovery, UrlReader, } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { + DirectoryPreparer, + Generators, + Preparers, + Publisher, + TechdocsGenerator, +} from '@backstage/techdocs-common'; +import Docker from 'dockerode'; import { Server } from 'http'; import { Logger } from 'winston'; import { createRouter } from './router'; -import Docker from 'dockerode'; -import { - Preparers, - DirectoryPreparer, - Generators, - TechdocsGenerator, - Publisher, -} from '@backstage/techdocs-common'; -import { ConfigReader } from '@backstage/config'; export interface ServerOptions { port: number; @@ -65,21 +66,25 @@ export async function startStandaloneServer( ); preparers.register('dir', directoryPreparer); + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(logger, config); + const techdocsGenerator = new TechdocsGenerator({ + logger, + containerRunner, + config, + }); generators.register('techdocs', techdocsGenerator); const publisher = await Publisher.fromConfig(config, { logger, discovery }); - const dockerClient = new Docker(); - logger.debug('Starting application server...'); const router = await createRouter({ preparers, generators, logger, publisher, - dockerClient, config, discovery, }); diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 847121c0cf..69e988a433 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-techdocs +## 0.9.1 + +### Patch Changes + +- 2e05277e0: Fix navigation in a page using the table of contents. +- 4075c6367: Make git config optional for techdocs feedback links +- Updated dependencies [38ca05168] +- Updated dependencies [f65adcde7] +- Updated dependencies [81c54d1f2] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] + - @backstage/integration@0.5.2 + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-react@0.1.5 + - @backstage/theme@0.2.7 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + ## 0.9.0 ### Minor Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 632feac3c1..380eae2737 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.9.0", + "version": "0.9.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,11 +31,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/config": "^0.1.4", - "@backstage/catalog-model": "^0.7.7", - "@backstage/core": "^0.7.7", - "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/theme": "^0.2.6", + "@backstage/config": "^0.1.5", + "@backstage/catalog-model": "^0.7.8", + "@backstage/core": "^0.7.8", + "@backstage/integration": "^0.5.2", + "@backstage/integration-react": "^0.1.1", + "@backstage/plugin-catalog-react": "^0.1.5", + "@backstage/theme": "^0.2.7", "@backstage/errors": "^0.1.1", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -49,7 +51,7 @@ "sanitize-html": "^2.3.2" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts index 97dd6f2150..e25f245fb4 100644 --- a/plugins/techdocs/src/plugin.ts +++ b/plugins/techdocs/src/plugin.ts @@ -13,21 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Copyright 2020 Spotify AB - * - * 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 { configApiRef, diff --git a/plugins/techdocs/src/reader/components/Reader.test.tsx b/plugins/techdocs/src/reader/components/Reader.test.tsx index b39134b0cd..fbdac95658 100644 --- a/plugins/techdocs/src/reader/components/Reader.test.tsx +++ b/plugins/techdocs/src/reader/components/Reader.test.tsx @@ -14,7 +14,12 @@ * limitations under the License. */ +import { ConfigReader } from '@backstage/config'; import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { + ScmIntegrationsApi, + scmIntegrationsApiRef, +} from '@backstage/integration-react'; import { wrapInTestApp } from '@backstage/test-utils'; import { act, render } from '@testing-library/react'; import React from 'react'; @@ -39,9 +44,15 @@ describe('', () => { entityId: 'Component::backstage', }); + const scmIntegrationsApi: ScmIntegrationsApi = ScmIntegrationsApi.fromConfig( + new ConfigReader({ + integrations: {}, + }), + ); const techdocsStorageApi: Partial = {}; const apiRegistry = ApiRegistry.from([ + [scmIntegrationsApiRef, scmIntegrationsApi], [techdocsStorageApiRef, techdocsStorageApi], ]); diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index b16a566afa..a67443e40f 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import { EntityName } from '@backstage/catalog-model'; -import { configApiRef, useApi } from '@backstage/core'; +import { useApi } from '@backstage/core'; +import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { BackstageTheme } from '@backstage/theme'; import { useTheme } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; @@ -54,7 +55,7 @@ export const Reader = ({ entityId, onReady }: Props) => { const [loadedPath, setLoadedPath] = useState(''); const [atInitialLoad, setAtInitialLoad] = useState(true); const [newerDocsExist, setNewerDocsExist] = useState(false); - const configApi = useApi(configApiRef); + const scmIntegrationsApi = useApi(scmIntegrationsApiRef); const { value: isSynced, @@ -145,7 +146,7 @@ export const Reader = ({ entityId, onReady }: Props) => { rewriteDocLinks(), removeMkdocsHeader(), simplifyMkdocsFooter(), - addGitFeedbackLink(configApi), + addGitFeedbackLink(scmIntegrationsApi), injectCss({ css: ` body { @@ -263,10 +264,14 @@ export const Reader = ({ entityId, onReady }: Props) => { ); shadowRoot.appendChild(transformedElement); + // Scroll to top after render + window.scroll({ top: 0 }); + // Post-render transformer(shadowRoot.children[0], [ dom => { setTimeout(() => { + // Scoll to the desired anchor on initial navigation if (window.location.hash) { const hash = window.location.hash.slice(1); shadowRoot?.getElementById(hash)?.scrollIntoView(); @@ -277,14 +282,19 @@ export const Reader = ({ entityId, onReady }: Props) => { addLinkClickListener({ baseUrl: window.location.origin, onClick: (_: MouseEvent, url: string) => { - window.scroll({ top: 0 }); const parsedUrl = new URL(url); if (newerDocsExist && isSynced) { // link navigation will load newer docs setNewerDocsExist(false); } + if (parsedUrl.hash) { navigate(`${parsedUrl.pathname}${parsedUrl.hash}`); + + // Scroll to hash if it's on the current page + shadowRoot + ?.getElementById(parsedUrl.hash.slice(1)) + ?.scrollIntoView(); } else { navigate(parsedUrl.pathname); } @@ -327,7 +337,7 @@ export const Reader = ({ entityId, onReady }: Props) => { theme.palette.background.default, newerDocsExist, isSynced, - configApi, + scmIntegrationsApi, ]); // docLoadError not considered an error state if sync request is still ongoing diff --git a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx index 52ddd3794e..5b638ff668 100644 --- a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx @@ -16,6 +16,11 @@ import React from 'react'; import { TechDocsPage } from './TechDocsPage'; import { render, act } from '@testing-library/react'; +import { ConfigReader } from '@backstage/config'; +import { + ScmIntegrationsApi, + scmIntegrationsApiRef, +} from '@backstage/integration-react'; import { wrapInTestApp } from '@backstage/test-utils'; import { ApiRegistry, ApiProvider } from '@backstage/core'; import { @@ -50,6 +55,11 @@ describe('', () => { entityId: 'Component::backstage', }); + const scmIntegrationsApi: ScmIntegrationsApi = ScmIntegrationsApi.fromConfig( + new ConfigReader({ + integrations: {}, + }), + ); const techdocsApi: Partial = { getEntityMetadata: () => Promise.resolve({ @@ -72,6 +82,7 @@ describe('', () => { }; const apiRegistry = ApiRegistry.from([ + [scmIntegrationsApiRef, scmIntegrationsApi], [techdocsApiRef, techdocsApi], [techdocsStorageApiRef, techdocsStorageApi], ]); diff --git a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts index 4c9ea04fe9..1b53b0141e 100644 --- a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts +++ b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.test.ts @@ -14,16 +14,18 @@ * limitations under the License. */ +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; import { createTestShadowDom } from '../../test-utils'; import { addGitFeedbackLink } from './addGitFeedbackLink'; -const configApi = { - getConfigArray: function getConfigArray(key: string) { - return key === 'integrations.github' - ? [{ data: { host: 'self-hosted-git-hub-provider.com' } }] - : []; - }, -}; +const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + github: [{ host: 'self-hosted-git-hub-provider.com' }], + }, + }), +); describe('addGitFeedbackLink', () => { it('adds a feedback link when a Gitlab source edit link is available', () => { @@ -38,7 +40,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -63,7 +65,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -87,7 +89,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -107,7 +109,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); @@ -127,7 +129,7 @@ describe('addGitFeedbackLink', () => { `, { - preTransformers: [addGitFeedbackLink(configApi)], + preTransformers: [addGitFeedbackLink(integrations)], postTransformers: [], }, ); diff --git a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts index d19d90228c..0fc25d8cf3 100644 --- a/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts +++ b/plugins/techdocs/src/reader/transformers/addGitFeedbackLink.ts @@ -15,12 +15,15 @@ */ import type { Transformer } from './index'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import FeedbackOutlinedIcon from '@material-ui/icons/FeedbackOutlined'; import React from 'react'; import ReactDOM from 'react-dom'; // requires repo -export const addGitFeedbackLink = (configApi: any): Transformer => { +export const addGitFeedbackLink = ( + scmIntegrationsApi: ScmIntegrationRegistry, +): Transformer => { return dom => { // attempting to use selectors that are more likely to be static as MkDocs updates over time const sourceAnchor = dom.querySelector( @@ -31,28 +34,12 @@ export const addGitFeedbackLink = (configApi: any): Transformer => { if (!sourceAnchor || !sourceAnchor.href) { return dom; } - let gitHost = ''; const sourceURL = new URL(sourceAnchor.href); - const githubHosts = configApi - .getConfigArray('integrations.github') - .map((integration: any) => integration.data.host); - const gitlabHosts = configApi - .getConfigArray('integrations.gitlab') - .map((integration: any) => integration.data.host); + const integration = scmIntegrationsApi.byUrl(sourceURL); // don't show if can't identify edit link hostname as a gitlab/github hosting - if ( - githubHosts.includes(sourceURL.hostname) || - sourceURL.origin.includes('github') - ) { - gitHost = 'github'; - } else if ( - gitlabHosts.includes(sourceURL.hostname) || - sourceURL.origin.includes('gitlab') - ) { - gitHost = 'gitlab'; - } else { + if (integration?.type !== 'github' && integration?.type !== 'gitlab') { return dom; } @@ -66,7 +53,7 @@ export const addGitFeedbackLink = (configApi: any): Transformer => { const repoPath = sourceURL.pathname.split('/').slice(0, 3).join('/'); const feedbackLink = sourceAnchor.cloneNode() as HTMLAnchorElement; - switch (gitHost) { + switch (integration?.type) { case 'gitlab': feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?issue[title]=${issueTitle}&issue[description]=${issueDesc}`; break; diff --git a/plugins/todo-backend/CHANGELOG.md b/plugins/todo-backend/CHANGELOG.md index 8468bcd500..b42f631a98 100644 --- a/plugins/todo-backend/CHANGELOG.md +++ b/plugins/todo-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-todo-backend +## 0.1.4 + +### Patch Changes + +- Updated dependencies [e0bfd3d44] +- Updated dependencies [38ca05168] +- Updated dependencies [d8b81fd28] +- Updated dependencies [d1b1306d9] + - @backstage/backend-common@0.7.0 + - @backstage/integration@0.5.2 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + - @backstage/catalog-client@0.3.11 + ## 0.1.3 ### Patch Changes diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index 2f7348e054..1af9f9b40c 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-todo-backend", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,12 +24,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.1", - "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.5", - "@backstage/config": "^0.1.4", + "@backstage/backend-common": "^0.7.0", + "@backstage/catalog-client": "^0.3.11", + "@backstage/catalog-model": "^0.7.8", + "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.0", + "@backstage/integration": "^0.5.2", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", "express": "^4.17.1", @@ -39,7 +39,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.10", "@types/supertest": "^2.0.8", "msw": "^0.21.2", "supertest": "^6.1.3" diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 92390857ef..aac15e9ad6 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -27,10 +27,10 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.7", + "@backstage/core": "^0.7.8", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.1", - "@backstage/theme": "^0.2.6", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -39,7 +39,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 08ae87d3ec..3349319442 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 189b6eeb59..90cdb3dc0f 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -30,8 +30,8 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.7.7", - "@backstage/theme": "^0.2.6", + "@backstage/core": "^0.7.8", + "@backstage/theme": "^0.2.7", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.9", + "@backstage/cli": "^0.6.10", "@backstage/dev-utils": "^0.1.13", "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", diff --git a/yarn.lock b/yarn.lock index 8a73d19c38..72d98dabea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1608,10 +1608,10 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/runtime-corejs3@^7.10.2", "@babel/runtime-corejs3@^7.11.2", "@babel/runtime-corejs3@^7.13.10": - version "7.13.10" - resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.13.10.tgz#14c3f4c85de22ba88e8e86685d13e8861a82fe86" - integrity sha512-x/XYVQ1h684pp1mJwOV4CyvqZXqbc8CMsMGUnAbuc82ZCdv1U63w5RSUzgDSXQHG5Rps/kiksH6g2D5BuaKyXg== +"@babel/runtime-corejs3@^7.10.2", "@babel/runtime-corejs3@^7.11.2", "@babel/runtime-corejs3@^7.13.17": + version "7.14.0" + resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.0.tgz#6bf5fbc0b961f8e3202888cb2cd0fb7a0a9a3f66" + integrity sha512-0R0HTZWHLk6G8jIk0FtoX+AatCtKnswS98VhXwGImFc759PJRp4Tru0PQYZofyijTFUr+gT8Mu7sgXVJLQ0ceg== dependencies: core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" @@ -1925,6 +1925,15 @@ debug "^3.1.0" lodash.once "^4.1.1" +"@dabh/diagnostics@^2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" + integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + "@date-io/core@1.x", "@date-io/core@^1.3.13": version "1.3.13" resolved "https://registry.npmjs.org/@date-io/core/-/core-1.3.13.tgz#90c71da493f20204b7a972929cc5c482d078b3fa" @@ -3955,11 +3964,13 @@ puka "^1.0.1" read-package-json-fast "^2.0.1" -"@octokit/auth-app@^2.10.5": - version "2.10.5" - resolved "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-2.10.5.tgz#85d69cb96818f5da34bf0b81bb637d3675ad4e9a" - integrity sha512-6yXyjtcBWpuPYSdZN8z8IIjGSqkPmiJzdmCdod8at41ANB1FtaKbUIDL5+IkG+svv68NIYs+XORbhBRFXYB3bw== +"@octokit/auth-app@^3.4.0": + version "3.4.0" + resolved "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-3.4.0.tgz#af9f68512e7b8dd071b49e1470a1ddf88ff6a3a3" + integrity sha512-zBVgTnLJb0uoNMGCpcDkkAbPeavHX7oAjJkaDv2nqMmsXSsCw4AbUhjl99EtJQG/JqFY/kLFHM9330Wn0k70+g== dependencies: + "@octokit/auth-oauth-app" "^4.1.0" + "@octokit/auth-oauth-user" "^1.2.3" "@octokit/request" "^5.4.11" "@octokit/request-error" "^2.0.0" "@octokit/types" "^6.0.3" @@ -3969,6 +3980,41 @@ universal-github-app-jwt "^1.0.1" universal-user-agent "^6.0.0" +"@octokit/auth-oauth-app@^4.1.0": + version "4.1.2" + resolved "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-4.1.2.tgz#bf3ff30c260e6e9f10b950386f279befb8fe907d" + integrity sha512-bdNGNRmuDJjKoHla3mUGtkk/xcxKngnQfBEnyk+7VwMqrABKvQB1wQRSrwSWkPPUX7Lcj2ttkPAPG7+iBkMRnw== + dependencies: + "@octokit/auth-oauth-device" "^3.1.1" + "@octokit/auth-oauth-user" "^1.2.1" + "@octokit/request" "^5.3.0" + "@octokit/types" "^6.0.3" + "@types/btoa-lite" "^1.0.0" + btoa-lite "^1.0.0" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-device@^3.1.1": + version "3.1.1" + resolved "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-3.1.1.tgz#380499f9a850425e2c7bdeb62afc070181c536a9" + integrity sha512-ykDZROilszXZJ6pYdl6SZ15UZniCs0zDcKgwOZpMz3U0QDHPUhFGXjHToBCAIHwbncMu+jLt4/Nw4lq3FwAw/w== + dependencies: + "@octokit/oauth-methods" "^1.1.0" + "@octokit/request" "^5.4.14" + "@octokit/types" "^6.10.0" + universal-user-agent "^6.0.0" + +"@octokit/auth-oauth-user@^1.2.1", "@octokit/auth-oauth-user@^1.2.3": + version "1.2.4" + resolved "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-1.2.4.tgz#3594eb7d40cb462240e7e90849781dfa0045aed5" + integrity sha512-efOajupCZBP1veqx5w59Qey0lIud1rDUgxTRjjkQDU3eOBmkAasY1pXemDsQwW0I85jb1P/gn2dMejedVxf9kw== + dependencies: + "@octokit/auth-oauth-device" "^3.1.1" + "@octokit/oauth-methods" "^1.1.0" + "@octokit/request" "^5.4.14" + "@octokit/types" "^6.12.2" + btoa-lite "^1.0.0" + universal-user-agent "^6.0.0" + "@octokit/auth-token@^2.4.4": version "2.4.4" resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56" @@ -4006,20 +4052,31 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" +"@octokit/oauth-authorization-url@^4.3.1": + version "4.3.1" + resolved "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-4.3.1.tgz#008d09bf427a7f61c70b5283040d60a456011a51" + integrity sha512-sI/SOEAvzRhqdzj+kJl+2ifblRve2XU6ZB36Lq25Su8R31zE3GoKToSLh64nWFnKePNi2RrdcMm94UEIQZslOw== + +"@octokit/oauth-methods@^1.1.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-1.2.2.tgz#3d98c548aa2ace36ad8d0ce6593fd49dcbe103cc" + integrity sha512-CFMUMn9DdPLMcpffhKgkwIIClfv0ZToJM4qcg4O0egCoHMYkVlxl22bBoo9qCnuF1U/xn871KEXuozKIX+bA2w== + dependencies: + "@octokit/oauth-authorization-url" "^4.3.1" + "@octokit/request" "^5.4.14" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.12.2" + btoa-lite "^1.0.0" + "@octokit/openapi-types@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.2.0.tgz#123e0438a0bc718ccdac3b5a2e69b3dd00daa85b" integrity sha512-274lNUDonw10kT8wHg8fCcUc1ZjZHbWv0/TbAwb0ojhBQqZYc1cQ/4yqTVTtPMDeZ//g7xVEYe/s3vURkRghPg== -"@octokit/openapi-types@^5.3.2": - version "5.3.2" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-5.3.2.tgz#b8ac43c5c3d00aef61a34cf744e315110c78deb4" - integrity sha512-NxF1yfYOUO92rCx3dwvA2onF30Vdlg7YUkMVXkeptqpzA3tRLplThhFleV/UKWFgh7rpKu1yYRbvNDUtzSopKA== - -"@octokit/openapi-types@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d" - integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ== +"@octokit/openapi-types@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.0.0.tgz#0f6992db9854af15eca77d71ab0ec7fad2f20411" + integrity sha512-gV/8DJhAL/04zjTI95a7FhQwS6jlEE0W/7xeYAzuArD0KVAVWDLP2f3vi98hs3HLTczxXdRK/mF0tRoQPpolEw== "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" @@ -4038,64 +4095,44 @@ resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44" integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg== -"@octokit/plugin-rest-endpoint-methods@4.13.5": - version "4.13.5" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.5.tgz#ad76285b82fe05fbb4adf2774a9c887f3534a880" - integrity sha512-kYKcWkFm4Ldk8bZai2RVEP1z97k1C/Ay2FN9FNTBg7JIyKoiiJjks4OtT6cuKeZX39tqa+C3J9xeYc6G+6g8uQ== +"@octokit/plugin-rest-endpoint-methods@5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.1.tgz#631b8d4edc6798b03489911252a25f2a4e58c594" + integrity sha512-vvWbPtPqLyIzJ7A4IPdTl+8IeuKAwMJ4LjvmqWOOdfSuqWQYZXq2CEd0hsnkidff2YfKlguzujHs/reBdAx8Sg== dependencies: - "@octokit/types" "^6.12.2" + "@octokit/types" "^6.13.1" deprecation "^2.3.1" -"@octokit/plugin-rest-endpoint-methods@5.0.0": - version "5.0.0" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98" - integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg== +"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" + integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== dependencies: - "@octokit/types" "^6.13.0" - deprecation "^2.3.1" - -"@octokit/request-error@^2.0.0": - version "2.0.2" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz#0e76b83f5d8fdda1db99027ea5f617c2e6ba9ed0" - integrity sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw== - dependencies: - "@octokit/types" "^5.0.1" + "@octokit/types" "^6.0.3" deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.12": - version "5.4.13" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.13.tgz#eec5987b3e96f984fc5f41967e001170c6d23a18" - integrity sha512-WcNRH5XPPtg7i1g9Da5U9dvZ6YbTffw9BN2rVezYiE7couoSyaRsw0e+Tl8uk1fArHE7Dn14U7YqUDy59WaqEw== +"@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.12", "@octokit/request@^5.4.14": + version "5.4.15" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128" + integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.0.0" - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" + "@octokit/types" "^6.7.1" is-plain-object "^5.0.0" node-fetch "^2.6.1" - once "^1.4.0" universal-user-agent "^6.0.0" -"@octokit/rest@^18.0.12", "@octokit/rest@^18.1.0": - version "18.3.5" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.3.5.tgz#a89903d46e0b4273bd3234674ec2777a651d68ab" - integrity sha512-ZPeRms3WhWxQBEvoIh0zzf8xdU2FX0Capa7+lTca8YHmRsO3QNJzf1H3PcuKKsfgp91/xVDRtX91sTe1kexlbw== +"@octokit/rest@^18.1.0", "@octokit/rest@^18.1.1", "@octokit/rest@^18.5.3": + version "18.5.3" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.5.3.tgz#6a2e6006a87ebbc34079c419258dd29ec9ff659d" + integrity sha512-KPAsUCr1DOdLVbZJgGNuE/QVLWEaVBpFQwDAz/2Cnya6uW2wJ/P5RVGk0itx7yyN1aGa8uXm2pri4umEqG1JBA== dependencies: "@octokit/core" "^3.2.3" "@octokit/plugin-paginate-rest" "^2.6.2" "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "4.13.5" - -"@octokit/rest@^18.1.1": - version "18.5.2" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b" - integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw== - dependencies: - "@octokit/core" "^3.2.3" - "@octokit/plugin-paginate-rest" "^2.6.2" - "@octokit/plugin-request-log" "^1.0.2" - "@octokit/plugin-rest-endpoint-methods" "5.0.0" + "@octokit/plugin-rest-endpoint-methods" "5.0.1" "@octokit/types@^5.0.0", "@octokit/types@^5.0.1": version "5.5.0" @@ -4104,19 +4141,12 @@ dependencies: "@types/node" ">= 8" -"@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.12.2": - version "6.12.2" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.12.2.tgz#5b44add079a478b8eb27d78cf384cc47e4411362" - integrity sha512-kCkiN8scbCmSq+gwdJV0iLgHc0O/GTPY1/cffo9kECu1MvatLPh9E+qFhfRIktKfHEA6ZYvv6S1B4Wnv3bi3pA== +"@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.12.2", "@octokit/types@^6.13.0", "@octokit/types@^6.13.1", "@octokit/types@^6.7.1", "@octokit/types@^6.8.2": + version "6.14.2" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.14.2.tgz#64c9457f38fb8522bdbba3c8cc814590a2d61bf5" + integrity sha512-wiQtW9ZSy4OvgQ09iQOdyXYNN60GqjCL/UdMsepDr1Gr0QzpW6irIKbH3REuAHXAhxkEk9/F2a3Gcs1P6kW5jA== dependencies: - "@octokit/openapi-types" "^5.3.2" - -"@octokit/types@^6.13.0", "@octokit/types@^6.8.2": - version "6.13.0" - resolved "https://registry.npmjs.org/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0" - integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA== - dependencies: - "@octokit/openapi-types" "^6.0.0" + "@octokit/openapi-types" "^7.0.0" "@open-draft/until@^1.0.3": version "1.0.3" @@ -5711,6 +5741,11 @@ resolved "https://registry.npmjs.org/@types/braces/-/braces-3.0.0.tgz#7da1c0d44ff1c7eb660a36ec078ea61ba7eb42cb" integrity sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw== +"@types/btoa-lite@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.0.tgz#e190a5a548e0b348adb0df9ac7fa5f1151c7cca4" + integrity sha512-wJsiX1tosQ+J5+bY5LrSahHxr2wT+uME5UDwdN1kg4frt40euqA+wzECkmq4t5QbveHiJepfdThgQrPw6KiSlg== + "@types/cacheable-request@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" @@ -6268,9 +6303,9 @@ "@types/node" "*" "@types/ldapjs@^1.0.9": - version "1.0.9" - resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.9.tgz#1224192d14cc5ab5218fcea72ebb04489c52cb95" - integrity sha512-3PvY7Drp1zoLbcGlothCAkoc5o6Jp9KvUPwHadlHyKp3yPvyeIh7w2zQc9UXMzgDRkoeGXUEODtbEs5XCh9ZyA== + version "1.0.10" + resolved "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.10.tgz#bac705c9e154b97d69496b5213cc28dbe9715a37" + integrity sha512-AMkMxkK/wjYtWebNH2O+rARfo7scBpW3T23g6zmGCwDgbyDbR79XWpcSqhPWdU+fChaF+I3dVyl9X2dT1CyI9w== dependencies: "@types/node" "*" @@ -6566,14 +6601,14 @@ "@types/reactcss" "*" "@types/react-dev-utils@^9.0.4": - version "9.0.4" - resolved "https://registry.npmjs.org/@types/react-dev-utils/-/react-dev-utils-9.0.4.tgz#3e4bee79b7536777cef219427ab1d38adc24f3f2" - integrity sha512-8cv9rAeSP1EmyRQAbZ/i6uYtai1VoKHGSBwDyCLM82wCkqoh3WPjJgI1pfi2kiLc0C5hNU7DLo7/c4hylfHLWg== + version "9.0.5" + resolved "https://registry.npmjs.org/@types/react-dev-utils/-/react-dev-utils-9.0.5.tgz#3265699edaba0078256d91b7793f98c4fb9bf6cf" + integrity sha512-uUsdRdc4LU1WQrfw3htnRIRchyOOsBnmnByK6H3oZMBdjmeyH1f7Cfy64YixQ3T3Dzy5gwGilcJVSDyOFjVBWQ== dependencies: "@types/eslint" "*" "@types/express" "*" "@types/html-webpack-plugin" "*" - "@types/webpack" "*" + "@types/webpack" "^4" "@types/webpack-dev-server" "*" "@types/react-dom@^16.9.8": @@ -8133,7 +8168,7 @@ async@^2.0.1, async@^2.6.1, async@^2.6.2: dependencies: lodash "^4.17.14" -async@^3.2.0: +async@^3.1.0, async@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== @@ -9032,6 +9067,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= + btoa@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" @@ -9677,10 +9717,10 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.5, classnames@^2.2.6: - version "2.2.6" - resolved "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" - integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== +classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== clean-css@^4.2.3: version "4.2.3" @@ -9993,10 +10033,10 @@ colorette@1.2.1, colorette@^1.2.1: resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== -colornames@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz#f8889030685c7c4ff9e2a559f5077eb76a816f96" - integrity sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y= +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== colors@^1.1.2, colors@^1.2.1: version "1.4.0" @@ -10451,9 +10491,9 @@ core-js-pure@^3.0.0, core-js-pure@^3.0.1: integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== core-js@3, core-js@^3.0.1, core-js@^3.0.4, core-js@^3.5.0, core-js@^3.6.0, core-js@^3.6.5: - version "3.11.0" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.11.0.tgz#05dac6aa70c0a4ad842261f8957b961d36eb8926" - integrity sha512-bd79DPpx+1Ilh9+30aT5O1sgpQd4Ttg8oqkqi51ZzhedMM1omD2e6IOF48Z/DzDCZ2svp49tN/3vneTK6ZBkXw== + version "3.11.2" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.11.2.tgz#af087a43373fc6e72942917c4a4c3de43ed574d6" + integrity sha512-3tfrrO1JpJSYGKnd9LKTBPqgUES/UYiCzMKeqwR1+jF16q4kD1BY2NvqkfuzXwQ6+CIWm55V9cjD7PQd+hijdw== core-js@^2.4.0, core-js@^2.5.7, core-js@^2.6.10, core-js@^2.6.5: version "2.6.11" @@ -11591,15 +11631,6 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diagnostics@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" - integrity sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ== - dependencies: - colorspace "1.1.x" - enabled "1.0.x" - kuler "1.0.x" - dicer@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" @@ -11832,10 +11863,10 @@ domhandler@^4.0.0: dependencies: domelementtype "^2.1.0" -dompurify@^2.0.12, dompurify@^2.1.1, dompurify@^2.2.7: - version "2.2.7" - resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.2.7.tgz#a5f055a2a471638680e779bd08fc334962d11fd8" - integrity sha512-jdtDffdGNY+C76jvodNTu9jt5yYj59vuTUyx+wXdzcSwAGTYZDAQkQ7Iwx9zcGrA4ixC1syU4H3RZROqRxokxg== +dompurify@^2.0.12, dompurify@^2.1.1, dompurify@^2.2.8: + version "2.2.8" + resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.2.8.tgz#ce88e395f6d00b6dc53f80d6b2a6fdf5446873c6" + integrity sha512-9H0UL59EkDLgY3dUFjLV6IEUaHm5qp3mxSqWw7Yyx4Zhk2Jn2cmLe+CNPP3xy13zl8Bqg+0NehQzkdMoVhGRww== domutils@1.5.1: version "1.5.1" @@ -12119,12 +12150,10 @@ emotion-theming@^10.0.19: "@emotion/weak-memoize" "0.2.5" hoist-non-react-statics "^3.3.0" -enabled@1.0.x: - version "1.0.2" - resolved "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" - integrity sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M= - dependencies: - env-variable "0.0.x" +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== encodeurl@~1.0.2: version "1.0.2" @@ -12190,11 +12219,6 @@ env-paths@^2.2.0: resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== -env-variable@0.0.x: - version "0.0.6" - resolved "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz#74ab20b3786c545b62b4a4813ab8cf22726c9808" - integrity sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg== - envinfo@^7.7.4: version "7.7.4" resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.7.4.tgz#c6311cdd38a0e86808c1c9343f667e4267c4a320" @@ -13491,6 +13515,11 @@ fn-name@~3.0.0: resolved "https://registry.npmjs.org/fn-name/-/fn-name-3.0.0.tgz#0596707f635929634d791f452309ab41558e3c5c" integrity sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA== +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + follow-redirects@^1.0.0, follow-redirects@^1.10.0: version "1.13.0" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" @@ -17071,9 +17100,9 @@ jsonpointer@^4.0.1: integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= jsonschema@^1.2.6: - version "1.2.7" - resolved "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.7.tgz#4e6d6dc4d83dc80707055ba22c00ec6152c0e6e9" - integrity sha512-3dFMg9hmI9LdHag/BRIhMefCfbq1hicvYMy8YhZQorAdzOzWz7NjniSpn39yjpzUAMIWtGyyZuH2KNBloH7ZLw== + version "1.4.0" + resolved "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" + integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== jsonwebtoken@^8.5.1: version "8.5.1" @@ -17341,12 +17370,10 @@ knex@^0.95.1: tarn "^3.0.1" tildify "2.0.0" -kuler@1.0.x: - version "1.0.1" - resolved "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz#ef7c784f36c9fb6e16dd3150d152677b2b0228a6" - integrity sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ== - dependencies: - colornames "^1.1.1" +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== language-subtag-registry@~0.3.2: version "0.3.21" @@ -17946,7 +17973,7 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -logform@^2.1.1: +logform@^2.1.1, logform@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg== @@ -19080,10 +19107,10 @@ nanoid@^2.1.0: resolved "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== -nanoid@^3.1.20: - version "3.1.20" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@^3.1.22: + version "3.1.22" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" + integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ== nanomatch@^1.2.9: version "1.2.13" @@ -19824,10 +19851,12 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -one-time@0.0.4: - version "0.0.4" - resolved "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e" - integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4= +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" onetime@^1.0.0: version "1.1.0" @@ -21303,13 +21332,13 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^ source-map "^0.6.1" supports-color "^6.1.0" -postcss@^8.0.2: - version "8.2.6" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe" - integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg== +postcss@^8.0.2, postcss@^8.1.0: + version "8.2.13" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz#dbe043e26e3c068e45113b1ed6375d2d37e2129f" + integrity sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ== dependencies: - colorette "^1.2.1" - nanoid "^3.1.20" + colorette "^1.2.2" + nanoid "^3.1.22" source-map "^0.6.1" postgres-array@~1.0.0: @@ -22075,7 +22104,7 @@ react-helmet@6.1.0: react-fast-compare "^3.1.1" react-side-effect "^2.1.0" -react-hook-form@^6.15.4, react-hook-form@^6.6.0: +react-hook-form@^6.15.4: version "6.15.4" resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-6.15.4.tgz#328003e1ccc096cd158899ffe7e3b33735a9b024" integrity sha512-K+Sw33DtTMengs8OdqFJI3glzNl1wBzSefD/ksQw/hJf9CnOHQAU6qy82eOrh0IRNt2G53sjr7qnnw1JDjvx1w== @@ -22596,7 +22625,7 @@ read@1, read@~1.0.1: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -23589,12 +23618,7 @@ sentence-case@^3.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -serialize-error@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" - integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= - -serialize-error@^8.0.1: +serialize-error@^8.0.1, serialize-error@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== @@ -24865,10 +24889,10 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -swagger-client@^3.13.1: - version "3.13.1" - resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.13.1.tgz#d2531ede4a498911aa084384e387e5008e7b6a70" - integrity sha512-Hmy4+wVVa3kveWzC7PIeUwiAY5qcYbm4XlC4uZ7e5kAePfB2cprXImiqrZHIzL+ndU0YTN7I+9w/ZayTisn3Jg== +swagger-client@^3.13.2: + version "3.13.2" + resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.13.2.tgz#df1a1fe92e6db87ab5f76cee6381dcb7da1ad117" + integrity sha512-kamtyXtmbZiA2C5YTVqJYgoPJgzqtM5RbeP23Rt/YPYjMArTKZ2fjx1UTsI0aSbws0GluU5pVHiGp8YMciSUfw== dependencies: "@babel/runtime-corejs3" "^7.11.2" btoa "^1.2.1" @@ -24886,19 +24910,19 @@ swagger-client@^3.13.1: url "~0.11.0" swagger-ui-react@^3.37.2: - version "3.45.1" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.45.1.tgz#6c8e551c5c1d443e7195acd9614f2f6bc4557daf" - integrity sha512-ATXMVIb5YaxxmHJdvPq1nnXxsJ1FCo02fQI2gExEtZCQkouMThZh50DRxdKYNae53Ovz3w8glxxwmsMuWtmKaQ== + version "3.48.0" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.48.0.tgz#f53e1b7de5bc2628635a2d832e254043badb3a91" + integrity sha512-39cibLdJYeujP5CAzBg+LiNFyIuHomi4Blru/1hyFyiTkFx9yYGvcFpZrKFKq1Me4GSUCQJ1DzUyUMblo+VFcA== dependencies: - "@babel/runtime-corejs3" "^7.13.10" + "@babel/runtime-corejs3" "^7.13.17" "@braintree/sanitize-url" "^5.0.0" "@kyleshockey/object-assign-deep" "^0.4.2" "@kyleshockey/xml" "^1.0.2" base64-js "^1.5.1" - classnames "^2.2.6" + classnames "^2.3.1" css.escape "1.5.1" deep-extend "0.6.0" - dompurify "^2.2.7" + dompurify "^2.2.8" ieee754 "^1.2.1" immutable "^3.x.x" js-file-download "^0.4.12" @@ -24919,9 +24943,9 @@ swagger-ui-react@^3.37.2: redux-immutable "3.1.0" remarkable "^2.0.1" reselect "^4.0.0" - serialize-error "^2.1.0" + serialize-error "^8.1.0" sha.js "^2.4.11" - swagger-client "^3.13.1" + swagger-client "^3.13.2" url-parse "^1.5.1" xml-but-prettier "^1.0.1" zenscroll "^4.0.2" @@ -26426,6 +26450,11 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +wait-for-expect@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463" + integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag== + wait-on@5.3.0: version "5.3.0" resolved "https://registry.npmjs.org/wait-on/-/wait-on-5.3.0.tgz#584e17d4b3fe7b46ac2b9f8e5e102c005c2776c7" @@ -26792,28 +26821,28 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" -winston-transport@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz#df68c0c202482c448d9b47313c07304c2d7c2c66" - integrity sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A== +winston-transport@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59" + integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw== dependencies: - readable-stream "^2.3.6" + readable-stream "^2.3.7" triple-beam "^1.2.0" winston@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz#63061377976c73584028be2490a1846055f77f07" - integrity sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw== + version "3.3.3" + resolved "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" + integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw== dependencies: - async "^2.6.1" - diagnostics "^1.1.1" - is-stream "^1.1.0" - logform "^2.1.1" - one-time "0.0.4" - readable-stream "^3.1.1" + "@dabh/diagnostics" "^2.0.2" + async "^3.1.0" + is-stream "^2.0.0" + logform "^2.2.0" + one-time "^1.0.0" + readable-stream "^3.4.0" stack-trace "0.0.x" triple-beam "^1.3.0" - winston-transport "^4.3.0" + winston-transport "^4.4.0" word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" @@ -27120,9 +27149,9 @@ yaml-ast-parser@0.0.43, yaml-ast-parser@^0.0.43: integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== yaml@^1.10.0, yaml@^1.7.2, yaml@^1.9.2: - version "1.10.0" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" - integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + version "1.10.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yargs-parser@20.2.4, yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.4"