From 09a37042643341a4f6f001554748e438573fb67f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 30 Dec 2020 13:52:48 +0100 Subject: [PATCH 001/144] backend-common: remove deprecated HTTPS config --- .changeset/wise-mice-invite.md | 5 + packages/backend-common/config.d.ts | 35 ++---- .../backend-common/src/service/lib/config.ts | 36 +----- .../src/service/lib/hostFactory.ts | 117 +++++++++--------- 4 files changed, 82 insertions(+), 111 deletions(-) create mode 100644 .changeset/wise-mice-invite.md diff --git a/.changeset/wise-mice-invite.md b/.changeset/wise-mice-invite.md new file mode 100644 index 0000000000..9021336c07 --- /dev/null +++ b/.changeset/wise-mice-invite.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': minor +--- + +Remove support for HTTPS certificate generation parameters. Use `backend.https = true` instead. diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index b7241bcc03..96dd71d41b 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -41,31 +41,16 @@ export interface Config { https?: | true | { - /** - * Certificate configuration or parameters for generating a self-signed certificate - * - * Setting parameters for self-signed certificates is deprecated and will be removed in - * the future, set `backend.https = true` instead. - */ - certificate?: - | { - /** Algorithm to use to generate a self-signed certificate */ - algorithm?: string; - keySize?: number; - days?: number; - attributes: { - commonName: string; - }; - } - | { - /** PEM encoded certificate. Use $file to load in a file */ - cert: string; - /** - * PEM encoded certificate key. Use $file to load in a file. - * @visibility secret - */ - key: string; - }; + /** Certificate configuration */ + certificate?: { + /** PEM encoded certificate. Use $file to load in a file */ + cert: string; + /** + * PEM encoded certificate key. Use $file to load in a file. + * @visibility secret + */ + key: string; + }; }; /** Database connection configuration, select database type using the `client` field */ diff --git a/packages/backend-common/src/service/lib/config.ts b/packages/backend-common/src/service/lib/config.ts index 6abea97454..5d9d12658d 100644 --- a/packages/backend-common/src/service/lib/config.ts +++ b/packages/backend-common/src/service/lib/config.ts @@ -22,23 +22,8 @@ export type BaseOptions = { listenHost?: string; }; -export type CertificateOptions = { - key?: CertificateKeyOptions; - attributes?: CertificateAttributeOptions; -}; - -export type CertificateKeyOptions = { - size?: number; - algorithm?: string; - days?: number; -}; - -export type CertificateAttributeOptions = { - commonName?: string; -}; - export type HttpsSettings = { - certificate: CertificateSigningOptions | CertificateReferenceOptions; + certificate: CertificateGenerationOptions | CertificateReferenceOptions; }; export type CertificateReferenceOptions = { @@ -46,11 +31,8 @@ export type CertificateReferenceOptions = { cert: string; }; -export type CertificateSigningOptions = { - algorithm?: string; - size?: number; - days?: number; - attributes: CertificateAttributes; +export type CertificateGenerationOptions = { + hostname: string; }; export type CertificateAttributes = { @@ -196,20 +178,14 @@ export function readHttpsSettings(config: Config): HttpsSettings | undefined { const https = config.get('https'); if (https === true) { const baseUrl = config.getString('baseUrl'); - let commonName; + let hostname; try { - commonName = new URL(baseUrl).hostname; + hostname = new URL(baseUrl).hostname; } catch (error) { throw new Error(`Invalid backend.baseUrl "${baseUrl}"`); } - return { - certificate: { - attributes: { - commonName, - }, - }, - }; + return { certificate: { hostname } }; } const cc = config.getOptionalConfig('https'); diff --git a/packages/backend-common/src/service/lib/hostFactory.ts b/packages/backend-common/src/service/lib/hostFactory.ts index 656f160c31..db202a84ab 100644 --- a/packages/backend-common/src/service/lib/hostFactory.ts +++ b/packages/backend-common/src/service/lib/hostFactory.ts @@ -20,10 +20,12 @@ import express from 'express'; import * as http from 'http'; import * as https from 'https'; import { Logger } from 'winston'; -import { CertificateSigningOptions, HttpsSettings } from './config'; +import { HttpsSettings } from './config'; const ALMOST_MONTH_IN_MS = 25 * 24 * 60 * 60 * 1000; +const IP_HOSTNAME_REGEX = /:|^\d+\.\d+\.\d+\.\d+$/; + /** * Creates a Http server instance based on an Express application. * @@ -59,17 +61,17 @@ export async function createHttpsServer( let credentials: { key: string | Buffer; cert: string | Buffer }; - const signingOptions: any = httpsSettings?.certificate; - - // TODO(Rugvip): remove support for generated certificate params and make this a more straightforward check - if (signingOptions?.attributes) { - credentials = await getGeneratedCertificate(signingOptions, logger); + if ('hostname' in httpsSettings?.certificate) { + credentials = await getGeneratedCertificate( + httpsSettings.certificate.hostname, + logger, + ); } else { logger?.info('Loading certificate from config'); credentials = { - key: signingOptions?.key, - cert: signingOptions?.cert, + key: httpsSettings?.certificate?.key, + cert: httpsSettings?.certificate?.cert, }; } @@ -80,16 +82,7 @@ export async function createHttpsServer( return https.createServer(credentials, app) as http.Server; } -async function getGeneratedCertificate( - options: CertificateSigningOptions, - logger?: Logger, -) { - if (options?.algorithm) { - logger?.warn( - 'Certificate generation configuration with parameters in backend.https.certificate is deprecated, set backend.https = true instead', - ); - } - +async function getGeneratedCertificate(hostname: string, logger?: Logger) { const hasModules = await fs.pathExists('node_modules'); let certPath; if (hasModules) { @@ -119,20 +112,61 @@ async function getGeneratedCertificate( } logger?.info('Generating new self-signed certificate'); - const newCert = await createCertificate(options); + const newCert = await createCertificate(hostname); await fs.writeFile(certPath, newCert.cert + newCert.key, 'utf8'); return newCert; } -async function createCertificate(options: CertificateSigningOptions) { - const attributes: Array = Object.entries( - options.attributes, - ).map(([name, value]) => ({ name, value })); +async function createCertificate(hostname: string) { + const attributes = [ + { + name: 'commonName', + value: 'dev-cert', + }, + ]; + + const sans = [ + { + type: 2, // DNS + value: 'localhost', + }, + { + type: 2, + value: 'localhost.localdomain', + }, + { + type: 2, + value: '[::1]', + }, + { + type: 7, // IP + ip: '127.0.0.1', + }, + { + type: 7, + ip: 'fe80::1', + }, + ]; + + // Add hostname from backend.baseUrl if it doesn't already exist in our list of SANs + if (!sans.find(({ value, ip }) => value === hostname || ip === hostname)) { + sans.push( + IP_HOSTNAME_REGEX.test(hostname) + ? { + type: 7, + ip: hostname, + } + : { + type: 2, + value: hostname, + }, + ); + } const params = { - algorithm: options?.algorithm || 'sha256', - keySize: options?.size || 2048, - days: options?.days || 30, + algorithm: 'sha256', + keySize: 2048, + days: 30, extensions: [ { name: 'keyUsage', @@ -151,36 +185,7 @@ async function createCertificate(options: CertificateSigningOptions) { }, { name: 'subjectAltName', - altNames: [ - { - type: 2, // DNS - value: 'localhost', - }, - { - type: 2, - value: 'localhost.localdomain', - }, - { - type: 2, - value: '[::1]', - }, - { - type: 7, // IP - ip: '127.0.0.1', - }, - { - type: 7, - ip: 'fe80::1', - }, - ...(options.attributes.commonName - ? [ - { - type: 2, // DNS - value: options.attributes.commonName, - }, - ] - : []), - ], + altNames: sans, }, ], }; From c2386e9e860325f43775d700a016b91ecd7053d0 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 08:57:15 +0100 Subject: [PATCH 002/144] Modifying auth tutorial to contain different providers. Adding a better example repo link. --- docs/tutorials/quickstart-app-auth.md | 265 +++++++++++++++++++++++--- 1 file changed, 239 insertions(+), 26 deletions(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 1dcca9d7e0..ecafd47b8c 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -3,20 +3,17 @@ id: quickstart-app-auth title: Monorepo App Setup With Authentication --- -###### September 15th 2020 - @backstage/create-app - v0.1.1-alpha.21 +###### January 8th 2021 - @backstage/create-app - v0.4.5
> This document takes you through setting up a Backstage app that runs in your > own environment. It starts with a skeleton install and verifying of the -> monorepo's functionality. Next, GitHub authentication is added and tested. +> monorepo's functionality. Next, authentication is added and tested. > > This document assumes you have Node.js 12 active along with Yarn and Python. -> Please note, that at the time of this writing, the current version is -> 0.1.1-alpha.21. This guide can still be used with future versions, just, -> verify as you go. If you run into issues, you can compare your setup with mine -> here > -> [simple-backstage-app](https://github.com/johnson-jesse/simple-backstage-app). +> Please note, that at the time of this writing, the current version is v0.4.5 +> This guide can still be used with future versions, just, verify as you go. # The Skeleton Application @@ -55,6 +52,16 @@ guest. Let's fix that now and add auth. # The Auth Configuration +Default Backstage installation includes multiple authentication providers out of +the box. The steps to enable new authentication provider in Backstage are very +similar to each other, the biggest difference is usually configuring the +external authentication provider. Please see a subset of possible providers and +instructions to integrate them below. Steps 1 & 2 are described separately for +each provider and steps beyond that are common for all. + +
Github +

+ 1. Open `app-config.yaml` and change it as follows _from:_ @@ -75,23 +82,224 @@ auth: $env: AUTH_GITHUB_CLIENT_ID clientSecret: $env: AUTH_GITHUB_CLIENT_SECRET - ## uncomment the following three lines if using enterprise + ## uncomment the following two lines if using enterprise # enterpriseInstanceUrl: # $env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL ``` -2. Set environment variables in whatever fashion is easiest for you. I chose to +2. Generate Github client id and secret + +- Log into http://github.com +- Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth + App)[https://github.com/settings/applications/new] +- Set Homepage URL = http://localhost:3000 +- Set Callback URL = http://localhost:7000/api/auth/github +- Click [Register application] +- On the next page, copy and paste your new Client ID and Client Secret to + environment variables defined in the `app-config.yaml` file, + `AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET` + +

+
+ +
Gitlab +

+ +1. Open `app-config.yaml` and change it as follows + +_from:_ + +```yaml +auth: + providers: {} +``` + +_to:_ + +```yaml +auth: + providers: + gitlab: + development: + clientId: + $env: AUTH_GITLAB_CLIENT_ID + clientSecret: + $env: AUTH_GITLAB_CLIENT_SECRET + audience: https://gitlab.com # Or your self-hosted Gitlab instance URL +``` + +2. Generate Gitlab Application for client id and secret + +- Log into Gitlab +- Navigate to (Profile > Settings > + Applications)[https://gitlab.com/-/profile/applications] +- Name your application +- Set Callback URL = http://localhost:7000/api/auth/gitlab/handler/frame +- Select the following values: + - `read_user (Read the authenticated user's personal information)` + - `read_repository (Allows read-only access to the repository)` + - `write_repository (Allows read-write access to the repository)` + - `openid (Authenticate using OpenID Connect)` + - `profile (Allows read-only access to the user's personal information using OpenID Connect)` + - `email (Allows read-only access to the user's primary email address using OpenID Connect)` +- Click [Save application] +- On the next page, copy and paste your new Application ID and Secret to + environment variables defined in the `app-config.yaml` file, + `AUTH_GITLAB_CLIENT_ID` & `AUTH_GITLAB_CLIENT_SECRET` + +

+
+ +
Google +

+ +1. Open `app-config.yaml` and change it as follows + +_from:_ + +```yaml +auth: + providers: {} +``` + +_to:_ + +```yaml +auth: + providers: + google: + development: + clientId: + $env: AUTH_GOOGLE_CLIENT_ID + clientSecret: + $env: AUTH_GOOGLE_CLIENT_SECRET +``` + +2. Generate Google Application in Google Cloud console + +- Log into https://console.cloud.google.com +- Select or create a new project from the dropdown on the top bar +- Navigate to (APIs & Services - > + Credentials)[https://console.cloud.google.com/apis/credentials] +- Add new Authorised JavaScript origin = `http://localhost:3000` +- Add new Authorised redirect URI = + `http://localhost:7000/api/auth/google/handler/frame` +- Click [Save application] +- Google should display a modal with your Client ID and Secret. Copy and paste + those to environment variables defined in the `app-config.yaml` file, + `AUTH_GOOGLE_CLIENT_ID` & `AUTH_GOOGLE_CLIENT_SECRET` + +

+
+ +
Microsoft +

+ +1. Open `app-config.yaml` and change it as follows + +_from:_ + +```yaml +auth: + providers: {} +``` + +_to:_ + +```yaml +auth: + providers: + microsoft: + development: + clientId: + $env: AUTH_MICROSOFT_CLIENT_ID + clientSecret: + $env: AUTH_MICROSOFT_CLIENT_SECRET + tenantId: + $env: AUTH_MICROSOFT_TENANT_ID +``` + +2. Create Microsoft Directory in Microsoft Portal + +- Log into https://portal.azure.com +- Navigate to (Azure Active Directory -> App + Registrations)[https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps] +- Create a New Registration +- Add new Redirect URI = `http://localhost:3000` +- Add new Authorised redirect URI = + `http://localhost:7000/api/auth/microsoft/handler/frame` +- Click [Save application] +- Set environment variable `AUTH_MICROSOFT_CLIENT_ID` from + `Application (client) Id` displayed on the directory page +- Set environment variable `AUTH_MICROSOFT_TENANT_ID` from + `Directory (tenant) ID` displayed on the directory page +- Navigate to Certificates & Secrets section and click [Create a new secret] +- Set environment variable `AUTH_MICROSOFT_CLIENT_SECRET` from the `value` field + created. + +

+
+ +
Auth0 +

+ +1. Open `app-config.yaml` and change it as follows + +_from:_ + +```yaml +auth: + providers: {} +``` + +_to:_ + +```yaml +auth: + providers: + auth0: + development: + clientId: + $env: AUTH_AUTH0_CLIENT_ID + clientSecret: + $env: AUTH_AUTH0_CLIENT_SECRET + domain: + $env: AUTH_AUTH0_DOMAIN_ID +``` + +2. Create Auth0 application in Auth0 management console + +- Log into https://manage.auth0.com/dashboard/ +- Navigate to Applications +- Create a New Application + - Select Single Page Web Application +- Go to Settings tab +- Add new line to Allowed Callback URLs = + `http://localhost:7000/api/auth/auth0/handler/frame` +- Click [Save Changes] +- Set environment variables displayed on the Basic Information page + - `AUTH_AUTH0_CLIENT_ID` from `Client ID` displayed on Auth0 application page + - `AUTH_AUTH0_CLIENT_SECRET` from `Client Secret` displayed on Auth0 + application page + - `AUTH_AUTH0_DOMAIN_ID` from `Domain` displayed on Auth0 application page + +

+
+ +3. Set environment variables in whatever fashion is easiest for you. I chose to add mine to my `.zshrc` profile. ```zsh # For macOS Catalina & Z Shell # ------ simple-backstage-app GitHub +# +# (Change the name of the environment variables based on your auth setup above export AUTH_GITHUB_CLIENT_ID=xxx export AUTH_GITHUB_CLIENT_SECRET=xxx # export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://github.{MY_BIZ}.com ``` -3. And of course I need to source that file. +4. And of course I need to source that file. ```zsh # Loading the new variables @@ -107,26 +315,26 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx > ... ``` -4. The values to replace `xxx` above come from your oauth app setup. - -``` -> Log into http://github.com -> Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth App)[https://github.com/settings/applications/new] -> Set Homepage URL = http://localhost:3000 -> Set Callback URL = http://localhost:7000/api/auth/github -> Click [Register application] -> On the next page, copy and paste your new Client ID and Client Secret to the environment variables above, `AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET` -> Don't forget to `source` that profile file again if necessary. -``` - -5. Open and change _root > packages > app > src >_`App.tsx` as follows +6. Open and change _root > packages > app > src >_`App.tsx` to use correct + authentication provider reference ```tsx -// Add the following imports to the existing list from core import { githubAuthApiRef, SignInPage } from '@backstage/core'; ``` -6. In the same file, change the createApp function as follows +Modify the imported reference based on authentication method selected above + +| Auth Provider | Import Name | +| ------------- | ------------------- | +| Github | githubAuthApiRef | +| Gitlab | gitlabAuthApiRef | +| Google | googleAuthApiRef | +| Microsoft | microsoftAuthApiRef | +| Auth0 | googleAuthApiRef | + +7. In the same file, modify createApp + +Remeber to modify the provider information based on the table above. ```tsx const app = createApp({ @@ -153,12 +361,17 @@ const app = createApp({ }); ``` -7. Start the backend and frontend as before +After finishing setting up one (or multiple) authentication providers defined +above you can start the backend and frontend as before When the browser loads, you should be presented with a login page for GitHub. Login as usual with your GitHub account. If this is your first time, you will be asked to authorize and then are redirected to the catalog page if all is well. +For more information you can clone the repository: +https://github.com/RoadieHQ/backstage-auth-example Each authentication setting +is set up there on a branch named after the authentication provider. + # Where to go from here > You're probably eager to write your first custom plugin. Follow this next From e9c75d64a3aff49109d8f5851d17c0f7d02455df Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 09:02:26 +0100 Subject: [PATCH 003/144] Fix typos. --- docs/tutorials/quickstart-app-auth.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index ecafd47b8c..5991f626e7 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -293,7 +293,7 @@ auth: # For macOS Catalina & Z Shell # ------ simple-backstage-app GitHub # -# (Change the name of the environment variables based on your auth setup above +# (Change the name of the environment variables based on your auth setup above) export AUTH_GITHUB_CLIENT_ID=xxx export AUTH_GITHUB_CLIENT_SECRET=xxx # export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://github.{MY_BIZ}.com @@ -315,7 +315,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx > ... ``` -6. Open and change _root > packages > app > src >_`App.tsx` to use correct +5. Open and change _root > packages > app > src >`App.tsx` to use correct authentication provider reference ```tsx @@ -332,9 +332,9 @@ Modify the imported reference based on authentication method selected above | Microsoft | microsoftAuthApiRef | | Auth0 | googleAuthApiRef | -7. In the same file, modify createApp +6. In the same file, modify createApp -Remeber to modify the provider information based on the table above. +Remember to modify the provider information based on the table above. ```tsx const app = createApp({ From b62bc928de9693d2af8e7cd131018f6d522021ed Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 09:41:06 +0100 Subject: [PATCH 004/144] Run prettier. --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 5991f626e7..b6370fd1f9 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -315,7 +315,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx > ... ``` -5. Open and change _root > packages > app > src >`App.tsx` to use correct +5. Open and change \_root > packages > app > src >`App.tsx` to use correct authentication provider reference ```tsx From 34206f81f05459a675c40e0351cdd0b0de8b97ed Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 13:47:47 +0100 Subject: [PATCH 005/144] Adding reference to latest node LTS as well. Resolves: * https://github.com/backstage/backstage/pull/4003#discussion_r554920635 --- docs/tutorials/quickstart-app-auth.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index b6370fd1f9..48b5d2b3a7 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -11,9 +11,10 @@ title: Monorepo App Setup With Authentication > own environment. It starts with a skeleton install and verifying of the > monorepo's functionality. Next, authentication is added and tested. > -> This document assumes you have Node.js 12 active along with Yarn and Python. -> Please note, that at the time of this writing, the current version is v0.4.5 -> This guide can still be used with future versions, just, verify as you go. +> This document assumes you have Node.js 12 or 14 active along with Yarn and +> Python. Please note, that at the time of this writing, the current version is +> v0.4.5 This guide can still be used with future versions, just, verify as you +> go. # The Skeleton Application From 82e35cd6531b9b62d4b264dd831a5585fc2e24b0 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 16:57:58 +0100 Subject: [PATCH 006/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 48b5d2b3a7..88ccce6ae5 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -13,7 +13,7 @@ title: Monorepo App Setup With Authentication > > This document assumes you have Node.js 12 or 14 active along with Yarn and > Python. Please note, that at the time of this writing, the current version is -> v0.4.5 This guide can still be used with future versions, just, verify as you +> v0.4.5. This guide can still be used with future versions, just, verify as you > go. # The Skeleton Application From ff4e53bd4ebb257732b0374cc6b2d19988e1cfe7 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 16:58:08 +0100 Subject: [PATCH 007/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 88ccce6ae5..2affcec7fc 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -53,7 +53,7 @@ guest. Let's fix that now and add auth. # The Auth Configuration -Default Backstage installation includes multiple authentication providers out of +A default Backstage installation includes multiple authentication providers out of the box. The steps to enable new authentication provider in Backstage are very similar to each other, the biggest difference is usually configuring the external authentication provider. Please see a subset of possible providers and From 322b04bff7191e0f31591a9306888e140b48f7fb Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 16:58:14 +0100 Subject: [PATCH 008/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 2affcec7fc..12d3767791 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -54,7 +54,7 @@ guest. Let's fix that now and add auth. # The Auth Configuration A default Backstage installation includes multiple authentication providers out of -the box. The steps to enable new authentication provider in Backstage are very +the box. The steps to enable new authentication providers in Backstage are very similar to each other, the biggest difference is usually configuring the external authentication provider. Please see a subset of possible providers and instructions to integrate them below. Steps 1 & 2 are described separately for From f3165cce9535a17d7698a584c90397d8ba351dfe Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 16:58:24 +0100 Subject: [PATCH 009/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 12d3767791..7e05a5a63d 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -88,7 +88,7 @@ auth: # $env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL ``` -2. Generate Github client id and secret +2. Generate a GitHub client ID and secret - Log into http://github.com - Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth From 642bf0be1a47df153704e32e8304fe98ef94ce65 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 16:59:17 +0100 Subject: [PATCH 010/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 7e05a5a63d..0f5e452aa5 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -103,7 +103,7 @@ auth:

-
Gitlab +
GitLab

1. Open `app-config.yaml` and change it as follows From 72399d57e5612b6198bce30efd2910ab67effef4 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 16:59:38 +0100 Subject: [PATCH 011/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 0f5e452aa5..2d23402edd 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -129,7 +129,7 @@ auth: audience: https://gitlab.com # Or your self-hosted Gitlab instance URL ``` -2. Generate Gitlab Application for client id and secret +2. Generate a GitLab Application client ID and secret - Log into Gitlab - Navigate to (Profile > Settings > From cc64069374adea4d24e4781f2ca64cb4fb8d0da5 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 16:59:51 +0100 Subject: [PATCH 012/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 2d23402edd..b58596b434 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -131,7 +131,7 @@ auth: 2. Generate a GitLab Application client ID and secret -- Log into Gitlab +- Log into GitLab - Navigate to (Profile > Settings > Applications)[https://gitlab.com/-/profile/applications] - Name your application From 163e263d8eb8e06868f7d464ba86b3adfaf76a23 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 17:00:35 +0100 Subject: [PATCH 013/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index b58596b434..00c97cfe4b 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -180,7 +180,7 @@ auth: - Log into https://console.cloud.google.com - Select or create a new project from the dropdown on the top bar -- Navigate to (APIs & Services - > +- Navigate to (APIs & Services > Credentials)[https://console.cloud.google.com/apis/credentials] - Add new Authorised JavaScript origin = `http://localhost:3000` - Add new Authorised redirect URI = From ecba810c98a37c72a915061f5822eec338db62a2 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 17:01:32 +0100 Subject: [PATCH 014/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 00c97cfe4b..1cae03469d 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -323,7 +323,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx import { githubAuthApiRef, SignInPage } from '@backstage/core'; ``` -Modify the imported reference based on authentication method selected above +Modify the imported reference based on the authentication method you selected above: | Auth Provider | Import Name | | ------------- | ------------------- | From 33267d1cb9f398153634a2dfb0851b5edfc488c8 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 17:01:39 +0100 Subject: [PATCH 015/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 1cae03469d..900c52c484 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -331,7 +331,7 @@ Modify the imported reference based on the authentication method you selected ab | Gitlab | gitlabAuthApiRef | | Google | googleAuthApiRef | | Microsoft | microsoftAuthApiRef | -| Auth0 | googleAuthApiRef | +| Auth0 | auth0AuthApiRef | 6. In the same file, modify createApp From 39b45b62b4889953dcf8676cf61b75d5f3a51876 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Mon, 11 Jan 2021 17:01:49 +0100 Subject: [PATCH 016/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 900c52c484..db97ae5ed4 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -369,8 +369,8 @@ When the browser loads, you should be presented with a login page for GitHub. Login as usual with your GitHub account. If this is your first time, you will be asked to authorize and then are redirected to the catalog page if all is well. -For more information you can clone the repository: -https://github.com/RoadieHQ/backstage-auth-example Each authentication setting +For more information you can clone [the backstage-auth-example repository](https://github.com/RoadieHQ/backstage-auth-example). + Each authentication setting is set up there on a branch named after the authentication provider. # Where to go from here From dc48cfc9f2d862f8ec805ffc4fc67d0ae33b0e27 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Tue, 12 Jan 2021 08:27:31 +0100 Subject: [PATCH 017/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index db97ae5ed4..364e4a434a 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -268,7 +268,7 @@ auth: $env: AUTH_AUTH0_DOMAIN_ID ``` -2. Create Auth0 application in Auth0 management console +2. Create an Auth0 application in the Auth0 management console - Log into https://manage.auth0.com/dashboard/ - Navigate to Applications From 4f0993407b406037d0468a77638d82da6993ed51 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Tue, 12 Jan 2021 08:31:32 +0100 Subject: [PATCH 018/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 364e4a434a..0fd48dc7bf 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -223,7 +223,7 @@ auth: 2. Create Microsoft Directory in Microsoft Portal - Log into https://portal.azure.com -- Navigate to (Azure Active Directory -> App +- Navigate to (Azure Active Directory > App Registrations)[https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps] - Create a New Registration - Add new Redirect URI = `http://localhost:3000` From 80619474d854e3f1e1f8205920fa43cae513fc28 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Tue, 12 Jan 2021 08:46:22 +0100 Subject: [PATCH 019/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 0fd48dc7bf..2ad2d7c900 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -220,7 +220,7 @@ auth: $env: AUTH_MICROSOFT_TENANT_ID ``` -2. Create Microsoft Directory in Microsoft Portal +2. Create a Microsoft App Registration in Microsoft Portal - Log into https://portal.azure.com - Navigate to (Azure Active Directory > App From 0fed686aed8791e0ce24911df04193bb44b34e3e Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Tue, 12 Jan 2021 08:47:27 +0100 Subject: [PATCH 020/144] Update docs/tutorials/quickstart-app-auth.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 2ad2d7c900..7475061b9e 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -176,7 +176,7 @@ auth: $env: AUTH_GOOGLE_CLIENT_SECRET ``` -2. Generate Google Application in Google Cloud console +2. Generate Google Credentials in Google Cloud console - Log into https://console.cloud.google.com - Select or create a new project from the dropdown on the top bar From 6d74776e1214f1e9ffe187f89cf909cbfdd027f1 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Tue, 12 Jan 2021 08:55:45 +0100 Subject: [PATCH 021/144] Adds in more modifications based on PR comments Run prettier. Add heading level for list items. Fix styling and nomenclature. --- docs/tutorials/quickstart-app-auth.md | 68 +++++++++++++++------------ 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 7475061b9e..205fcddb65 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -53,9 +53,9 @@ guest. Let's fix that now and add auth. # The Auth Configuration -A default Backstage installation includes multiple authentication providers out of -the box. The steps to enable new authentication providers in Backstage are very -similar to each other, the biggest difference is usually configuring the +A default Backstage installation includes multiple authentication providers out +of the box. The steps to enable new authentication providers in Backstage are +very similar to each other, the biggest difference is usually configuring the external authentication provider. Please see a subset of possible providers and instructions to integrate them below. Steps 1 & 2 are described separately for each provider and steps beyond that are common for all. @@ -63,7 +63,7 @@ each provider and steps beyond that are common for all.

Github

-1. Open `app-config.yaml` and change it as follows +### 1. Open `app-config.yaml` and change it as follows _from:_ @@ -88,7 +88,7 @@ auth: # $env: AUTH_GITHUB_ENTERPRISE_INSTANCE_URL ``` -2. Generate a GitHub client ID and secret +### 2. Generate a GitHub client ID and secret - Log into http://github.com - Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth @@ -106,7 +106,7 @@ auth:

GitLab

-1. Open `app-config.yaml` and change it as follows +### 1. Open `app-config.yaml` and change it as follows _from:_ @@ -129,7 +129,7 @@ auth: audience: https://gitlab.com # Or your self-hosted Gitlab instance URL ``` -2. Generate a GitLab Application client ID and secret +### 2. Generate a Gitlab Application client ID and secret - Log into GitLab - Navigate to (Profile > Settings > @@ -137,12 +137,14 @@ auth: - Name your application - Set Callback URL = http://localhost:7000/api/auth/gitlab/handler/frame - Select the following values: - - `read_user (Read the authenticated user's personal information)` - - `read_repository (Allows read-only access to the repository)` - - `write_repository (Allows read-write access to the repository)` - - `openid (Authenticate using OpenID Connect)` - - `profile (Allows read-only access to the user's personal information using OpenID Connect)` - - `email (Allows read-only access to the user's primary email address using OpenID Connect)` + - `read_user` (Read the authenticated user's personal information) + - `read_repository` (Allows read-only access to the repository) + - `write_repository` (Allows read-write access to the repository) + - `openid` (Authenticate using OpenID Connect) + - `profile` (Allows read-only access to the user's personal information using + OpenID Connect) + - `email` (Allows read-only access to the user's primary email address using + OpenID Connect) - Click [Save application] - On the next page, copy and paste your new Application ID and Secret to environment variables defined in the `app-config.yaml` file, @@ -154,7 +156,7 @@ auth:

Google

-1. Open `app-config.yaml` and change it as follows +### 1. Open `app-config.yaml` and change it as follows _from:_ @@ -176,12 +178,14 @@ auth: $env: AUTH_GOOGLE_CLIENT_SECRET ``` -2. Generate Google Credentials in Google Cloud console +### 2. Generate Google Credentials in Google Cloud console - Log into https://console.cloud.google.com - Select or create a new project from the dropdown on the top bar - Navigate to (APIs & Services > Credentials)[https://console.cloud.google.com/apis/credentials] +- Click Create Credentials and select [OAuth client ID] +- Select Web Application as the application type - Add new Authorised JavaScript origin = `http://localhost:3000` - Add new Authorised redirect URI = `http://localhost:7000/api/auth/google/handler/frame` @@ -196,7 +200,7 @@ auth:

Microsoft

-1. Open `app-config.yaml` and change it as follows +### 1. Open `app-config.yaml` and change it as follows _from:_ @@ -220,7 +224,7 @@ auth: $env: AUTH_MICROSOFT_TENANT_ID ``` -2. Create a Microsoft App Registration in Microsoft Portal +### 2. Create a Microsoft App Registration in Microsoft Portal - Log into https://portal.azure.com - Navigate to (Azure Active Directory > App @@ -244,7 +248,7 @@ auth:

Auth0

-1. Open `app-config.yaml` and change it as follows +### 1. Open `app-config.yaml` and change it as follows _from:_ @@ -268,7 +272,7 @@ auth: $env: AUTH_AUTH0_DOMAIN_ID ``` -2. Create an Auth0 application in the Auth0 management console +### 2. Create an Auth0 application in the Auth0 management console - Log into https://manage.auth0.com/dashboard/ - Navigate to Applications @@ -287,8 +291,9 @@ auth:

-3. Set environment variables in whatever fashion is easiest for you. I chose to - add mine to my `.zshrc` profile. +### 3. Set environment variables in whatever fashion is easiest for you. I chose to + +add mine to my `.zshrc` profile. ```zsh # For macOS Catalina & Z Shell @@ -300,7 +305,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx # export AUTH_GITHUB_ENTERPRISE_INSTANCE_URL=https://github.{MY_BIZ}.com ``` -4. And of course I need to source that file. +### 4. And of course I need to source that file. ```zsh # Loading the new variables @@ -316,14 +321,16 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx > ... ``` -5. Open and change \_root > packages > app > src >`App.tsx` to use correct - authentication provider reference +### 5. Open and change _root > packages > app > src >_ `App.tsx` to use correct + +authentication provider reference ```tsx import { githubAuthApiRef, SignInPage } from '@backstage/core'; ``` -Modify the imported reference based on the authentication method you selected above: +Modify the imported reference based on the authentication method you selected +above: | Auth Provider | Import Name | | ------------- | ------------------- | @@ -331,9 +338,9 @@ Modify the imported reference based on the authentication method you selected ab | Gitlab | gitlabAuthApiRef | | Google | googleAuthApiRef | | Microsoft | microsoftAuthApiRef | -| Auth0 | auth0AuthApiRef | +| Auth0 | auth0AuthApiRef | -6. In the same file, modify createApp +### 6. In the same file, modify createApp Remember to modify the provider information based on the table above. @@ -369,9 +376,10 @@ When the browser loads, you should be presented with a login page for GitHub. Login as usual with your GitHub account. If this is your first time, you will be asked to authorize and then are redirected to the catalog page if all is well. -For more information you can clone [the backstage-auth-example repository](https://github.com/RoadieHQ/backstage-auth-example). - Each authentication setting -is set up there on a branch named after the authentication provider. +For more information you can clone +[the backstage-auth-example repository](https://github.com/RoadieHQ/backstage-auth-example). +Each authentication setting is set up there on a branch named after the +authentication provider. # Where to go from here From 4e4dc71b2c2dd68a1ba54db1a9da85fdeb1a70ca Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 12 Jan 2021 21:33:33 +0100 Subject: [PATCH 022/144] microsite: set UR redirects for core features We already have a redirect in place i.e. /docs to /docs/overview/what-is-backstage/ This PR adds 3 new redirects /docs/features/software-catalog -> /docs/features/software-catalog/software-catalog-overview /docs/features/techdocs -> /docs/features/techdocs/techdocs-overview /docs/features/software-templates -> /docs/features/software-templates/software-templates-index --- .../pages/en/docs/features/software-catalog/index.js | 12 ++++++++++++ .../en/docs/features/software-templates/index.js | 12 ++++++++++++ microsite/pages/en/docs/features/techdocs/index.js | 12 ++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 microsite/pages/en/docs/features/software-catalog/index.js create mode 100644 microsite/pages/en/docs/features/software-templates/index.js create mode 100644 microsite/pages/en/docs/features/techdocs/index.js diff --git a/microsite/pages/en/docs/features/software-catalog/index.js b/microsite/pages/en/docs/features/software-catalog/index.js new file mode 100644 index 0000000000..619641ced1 --- /dev/null +++ b/microsite/pages/en/docs/features/software-catalog/index.js @@ -0,0 +1,12 @@ +const React = require('react'); +const Redirect = require('../../../../../core/Redirect.js'); + +const siteConfig = require(process.cwd() + '/siteConfig.js'); + +function Docs() { + return ( + + ); +} + +module.exports = Docs; diff --git a/microsite/pages/en/docs/features/software-templates/index.js b/microsite/pages/en/docs/features/software-templates/index.js new file mode 100644 index 0000000000..c5592844e5 --- /dev/null +++ b/microsite/pages/en/docs/features/software-templates/index.js @@ -0,0 +1,12 @@ +const React = require('react'); +const Redirect = require('../../../../../core/Redirect.js'); + +const siteConfig = require(process.cwd() + '/siteConfig.js'); + +function Docs() { + return ( + + ); +} + +module.exports = Docs; diff --git a/microsite/pages/en/docs/features/techdocs/index.js b/microsite/pages/en/docs/features/techdocs/index.js new file mode 100644 index 0000000000..e92f6bf82e --- /dev/null +++ b/microsite/pages/en/docs/features/techdocs/index.js @@ -0,0 +1,12 @@ +const React = require('react'); +const Redirect = require('../../../../../core/Redirect.js'); + +const siteConfig = require(process.cwd() + '/siteConfig.js'); + +function Docs() { + return ( + + ); +} + +module.exports = Docs; From 50e063f4955715f2f5af75ddc55e72614b778c3f Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Tue, 12 Jan 2021 21:44:47 +0100 Subject: [PATCH 023/144] microsite prettier has a different version than backstage root package.json --- microsite/pages/en/docs/features/software-catalog/index.js | 5 ++++- microsite/pages/en/docs/features/software-templates/index.js | 5 ++++- microsite/pages/en/docs/features/techdocs/index.js | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/microsite/pages/en/docs/features/software-catalog/index.js b/microsite/pages/en/docs/features/software-catalog/index.js index 619641ced1..cffc91af21 100644 --- a/microsite/pages/en/docs/features/software-catalog/index.js +++ b/microsite/pages/en/docs/features/software-catalog/index.js @@ -5,7 +5,10 @@ const siteConfig = require(process.cwd() + '/siteConfig.js'); function Docs() { return ( - + ); } diff --git a/microsite/pages/en/docs/features/software-templates/index.js b/microsite/pages/en/docs/features/software-templates/index.js index c5592844e5..79d3f0659e 100644 --- a/microsite/pages/en/docs/features/software-templates/index.js +++ b/microsite/pages/en/docs/features/software-templates/index.js @@ -5,7 +5,10 @@ const siteConfig = require(process.cwd() + '/siteConfig.js'); function Docs() { return ( - + ); } diff --git a/microsite/pages/en/docs/features/techdocs/index.js b/microsite/pages/en/docs/features/techdocs/index.js index e92f6bf82e..c45cde24f5 100644 --- a/microsite/pages/en/docs/features/techdocs/index.js +++ b/microsite/pages/en/docs/features/techdocs/index.js @@ -5,7 +5,10 @@ const siteConfig = require(process.cwd() + '/siteConfig.js'); function Docs() { return ( - + ); } From abbee6fff46a6ffc866df086063a4bf41877999f Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 12 Jan 2021 17:05:49 +0100 Subject: [PATCH 024/144] Add system, domain and resource entity kinds --- .changeset/thin-icons-kick.md | 6 + app-config.yaml | 2 +- .../src/kinds/ApiEntityV1alpha1.test.ts | 16 ++ .../src/kinds/ApiEntityV1alpha1.ts | 2 + .../src/kinds/ComponentEntityV1alpha1.test.ts | 16 ++ .../src/kinds/ComponentEntityV1alpha1.ts | 2 + .../src/kinds/DomainEntityV1alpha1.test.ts | 71 ++++++++ .../src/kinds/DomainEntityV1alpha1.ts | 46 +++++ .../src/kinds/ResourceEntityV1alpha1.test.ts | 103 +++++++++++ .../src/kinds/ResourceEntityV1alpha1.ts | 50 ++++++ .../src/kinds/SystemEntityV1alpha1.test.ts | 87 +++++++++ .../src/kinds/SystemEntityV1alpha1.ts | 48 +++++ packages/catalog-model/src/kinds/index.ts | 15 ++ packages/catalog-model/src/kinds/relations.ts | 7 +- .../BuiltinKindsEntityProcessor.test.ts | 169 +++++++++++++++++- .../processors/BuiltinKindsEntityProcessor.ts | 79 +++++++- 16 files changed, 713 insertions(+), 6 deletions(-) create mode 100644 .changeset/thin-icons-kick.md create mode 100644 packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts create mode 100644 packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts create mode 100644 packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts create mode 100644 packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts create mode 100644 packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts create mode 100644 packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts diff --git a/.changeset/thin-icons-kick.md b/.changeset/thin-icons-kick.md new file mode 100644 index 0000000000..348f55a8ad --- /dev/null +++ b/.changeset/thin-icons-kick.md @@ -0,0 +1,6 @@ +--- +'@backstage/catalog-model': patch +'@backstage/plugin-catalog-backend': patch +--- + +Implement System, Domain and Resource entity kinds. diff --git a/app-config.yaml b/app-config.yaml index 48869cc0a0..b67e9525bf 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -127,7 +127,7 @@ integrations: catalog: rules: - - allow: [Component, API, Group, User, Template, Location] + - allow: [Component, API, Resource, Group, User, Template, System, Domain, Location] processors: githubOrg: diff --git a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts index a5d5152fab..a4d7d904cd 100644 --- a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.test.ts @@ -70,6 +70,7 @@ components: items: $ref: "#/components/schemas/Pet" `, + system: 'system', }, }; }); @@ -152,4 +153,19 @@ components: (entity as any).spec.definition = ''; await expect(validator.check(entity)).rejects.toThrow(/definition/); }); + + it('accepts missing system', async () => { + delete (entity as any).spec.system; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('rejects wrong system', async () => { + (entity as any).spec.system = 7; + await expect(validator.check(entity)).rejects.toThrow(/system/); + }); + + it('rejects empty system', async () => { + (entity as any).spec.system = ''; + await expect(validator.check(entity)).rejects.toThrow(/system/); + }); }); diff --git a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts index 660cd71cd8..2c634ff091 100644 --- a/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ApiEntityV1alpha1.ts @@ -30,6 +30,7 @@ const schema = yup.object>({ lifecycle: yup.string().required().min(1), owner: yup.string().required().min(1), definition: yup.string().required().min(1), + system: yup.string().notRequired().min(1), }) .required(), }); @@ -42,6 +43,7 @@ export interface ApiEntityV1alpha1 extends Entity { lifecycle: string; owner: string; definition: string; + system?: string; }; } diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts index 10d66ac880..9284a5d5b1 100644 --- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.test.ts @@ -36,6 +36,7 @@ describe('ComponentV1alpha1Validator', () => { subcomponentOf: 'monolith', providesApis: ['api-0'], consumesApis: ['api-0'], + system: 'system', }, }; }); @@ -158,4 +159,19 @@ describe('ComponentV1alpha1Validator', () => { (entity as any).spec.consumesApis = []; await expect(validator.check(entity)).resolves.toBe(true); }); + + it('accepts missing system', async () => { + delete (entity as any).spec.system; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('rejects wrong system', async () => { + (entity as any).spec.system = 7; + await expect(validator.check(entity)).rejects.toThrow(/system/); + }); + + it('rejects empty system', async () => { + (entity as any).spec.system = ''; + await expect(validator.check(entity)).rejects.toThrow(/system/); + }); }); diff --git a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts index 97519ad403..c55c48055a 100644 --- a/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/ComponentEntityV1alpha1.ts @@ -32,6 +32,7 @@ const schema = yup.object>({ subcomponentOf: yup.string().notRequired().min(1), providesApis: yup.array(yup.string().required()).notRequired(), consumesApis: yup.array(yup.string().required()).notRequired(), + system: yup.string().notRequired().min(1), }) .required(), }); @@ -46,6 +47,7 @@ export interface ComponentEntityV1alpha1 extends Entity { subcomponentOf?: string; providesApis?: string[]; consumesApis?: string[]; + system?: string; }; } diff --git a/packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts new file mode 100644 index 0000000000..0e989f22ca --- /dev/null +++ b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.test.ts @@ -0,0 +1,71 @@ +/* + * 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 { + DomainEntityV1alpha1, + domainEntityV1alpha1Validator as validator, +} from './DomainEntityV1alpha1'; + +describe('DomainV1alpha1Validator', () => { + let entity: DomainEntityV1alpha1; + + beforeEach(() => { + entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Domain', + metadata: { + name: 'test', + }, + spec: { + owner: 'me', + }, + }; + }); + + it('happy path: accepts valid data', async () => { + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('silently accepts v1beta1 as well', async () => { + (entity as any).apiVersion = 'backstage.io/v1beta1'; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('ignores unknown apiVersion', async () => { + (entity as any).apiVersion = 'backstage.io/v1beta0'; + await expect(validator.check(entity)).resolves.toBe(false); + }); + + it('ignores unknown kind', async () => { + (entity as any).kind = 'Wizard'; + await expect(validator.check(entity)).resolves.toBe(false); + }); + + it('rejects missing owner', async () => { + delete (entity as any).spec.owner; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('rejects wrong owner', async () => { + (entity as any).spec.owner = 7; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('rejects empty owner', async () => { + (entity as any).spec.owner = ''; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); +}); diff --git a/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts new file mode 100644 index 0000000000..60b11aa124 --- /dev/null +++ b/packages/catalog-model/src/kinds/DomainEntityV1alpha1.ts @@ -0,0 +1,46 @@ +/* + * 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 * as yup from 'yup'; +import type { Entity } from '../entity/Entity'; +import { schemaValidator } from './util'; + +const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const; +const KIND = 'Domain' as const; + +const schema = yup.object>({ + apiVersion: yup.string().required().oneOf(API_VERSION), + kind: yup.string().required().equals([KIND]), + spec: yup + .object({ + owner: yup.string().required().min(1), + }) + .required(), +}); + +export interface DomainEntityV1alpha1 extends Entity { + apiVersion: typeof API_VERSION[number]; + kind: typeof KIND; + spec: { + owner: string; + }; +} + +export const domainEntityV1alpha1Validator = schemaValidator( + KIND, + API_VERSION, + schema, +); diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts new file mode 100644 index 0000000000..ad8ea5cdf3 --- /dev/null +++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.test.ts @@ -0,0 +1,103 @@ +/* + * 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 { + ResourceEntityV1alpha1, + resourceEntityV1alpha1Validator as validator, +} from './ResourceEntityV1alpha1'; + +describe('ResourceV1alpha1Validator', () => { + let entity: ResourceEntityV1alpha1; + + beforeEach(() => { + entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { + name: 'test', + }, + spec: { + type: 'database', + owner: 'me', + system: 'system', + }, + }; + }); + + it('happy path: accepts valid data', async () => { + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('silently accepts v1beta1 as well', async () => { + (entity as any).apiVersion = 'backstage.io/v1beta1'; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('ignores unknown apiVersion', async () => { + (entity as any).apiVersion = 'backstage.io/v1beta0'; + await expect(validator.check(entity)).resolves.toBe(false); + }); + + it('ignores unknown kind', async () => { + (entity as any).kind = 'Wizard'; + await expect(validator.check(entity)).resolves.toBe(false); + }); + + it('rejects missing type', async () => { + delete (entity as any).spec.type; + await expect(validator.check(entity)).rejects.toThrow(/type/); + }); + + it('rejects wrong type', async () => { + (entity as any).spec.type = 7; + await expect(validator.check(entity)).rejects.toThrow(/type/); + }); + + it('rejects empty type', async () => { + (entity as any).spec.type = ''; + await expect(validator.check(entity)).rejects.toThrow(/type/); + }); + + it('rejects missing owner', async () => { + delete (entity as any).spec.owner; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('rejects wrong owner', async () => { + (entity as any).spec.owner = 7; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('rejects empty owner', async () => { + (entity as any).spec.owner = ''; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('accepts missing system', async () => { + delete (entity as any).spec.system; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('rejects wrong system', async () => { + (entity as any).spec.system = 7; + await expect(validator.check(entity)).rejects.toThrow(/system/); + }); + + it('rejects empty system', async () => { + (entity as any).spec.system = ''; + await expect(validator.check(entity)).rejects.toThrow(/system/); + }); +}); diff --git a/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts new file mode 100644 index 0000000000..12df7f6664 --- /dev/null +++ b/packages/catalog-model/src/kinds/ResourceEntityV1alpha1.ts @@ -0,0 +1,50 @@ +/* + * 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 * as yup from 'yup'; +import type { Entity } from '../entity/Entity'; +import { schemaValidator } from './util'; + +const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const; +const KIND = 'Resource' as const; + +const schema = yup.object>({ + apiVersion: yup.string().required().oneOf(API_VERSION), + kind: yup.string().required().equals([KIND]), + spec: yup + .object({ + type: yup.string().required().min(1), + owner: yup.string().required().min(1), + system: yup.string().notRequired().min(1), + }) + .required(), +}); + +export interface ResourceEntityV1alpha1 extends Entity { + apiVersion: typeof API_VERSION[number]; + kind: typeof KIND; + spec: { + type: string; + owner: string; + system?: string; + }; +} + +export const resourceEntityV1alpha1Validator = schemaValidator( + KIND, + API_VERSION, + schema, +); diff --git a/packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts new file mode 100644 index 0000000000..7d744b7d0d --- /dev/null +++ b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.test.ts @@ -0,0 +1,87 @@ +/* + * 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 { + SystemEntityV1alpha1, + systemEntityV1alpha1Validator as validator, +} from './SystemEntityV1alpha1'; + +describe('SystemV1alpha1Validator', () => { + let entity: SystemEntityV1alpha1; + + beforeEach(() => { + entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { + name: 'test', + }, + spec: { + owner: 'me', + domain: 'domain', + }, + }; + }); + + it('happy path: accepts valid data', async () => { + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('silently accepts v1beta1 as well', async () => { + (entity as any).apiVersion = 'backstage.io/v1beta1'; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('ignores unknown apiVersion', async () => { + (entity as any).apiVersion = 'backstage.io/v1beta0'; + await expect(validator.check(entity)).resolves.toBe(false); + }); + + it('ignores unknown kind', async () => { + (entity as any).kind = 'Wizard'; + await expect(validator.check(entity)).resolves.toBe(false); + }); + + it('rejects missing owner', async () => { + delete (entity as any).spec.owner; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('rejects wrong owner', async () => { + (entity as any).spec.owner = 7; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('rejects empty owner', async () => { + (entity as any).spec.owner = ''; + await expect(validator.check(entity)).rejects.toThrow(/owner/); + }); + + it('accepts missing domain', async () => { + delete (entity as any).spec.domain; + await expect(validator.check(entity)).resolves.toBe(true); + }); + + it('rejects wrong domain', async () => { + (entity as any).spec.domain = 7; + await expect(validator.check(entity)).rejects.toThrow(/domain/); + }); + + it('rejects empty domain', async () => { + (entity as any).spec.domain = ''; + await expect(validator.check(entity)).rejects.toThrow(/domain/); + }); +}); diff --git a/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts new file mode 100644 index 0000000000..764514efdd --- /dev/null +++ b/packages/catalog-model/src/kinds/SystemEntityV1alpha1.ts @@ -0,0 +1,48 @@ +/* + * 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 * as yup from 'yup'; +import type { Entity } from '../entity/Entity'; +import { schemaValidator } from './util'; + +const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const; +const KIND = 'System' as const; + +const schema = yup.object>({ + apiVersion: yup.string().required().oneOf(API_VERSION), + kind: yup.string().required().equals([KIND]), + spec: yup + .object({ + owner: yup.string().required().min(1), + domain: yup.string().notRequired().min(1), + }) + .required(), +}); + +export interface SystemEntityV1alpha1 extends Entity { + apiVersion: typeof API_VERSION[number]; + kind: typeof KIND; + spec: { + owner: string; + domain?: string; + }; +} + +export const systemEntityV1alpha1Validator = schemaValidator( + KIND, + API_VERSION, + schema, +); diff --git a/packages/catalog-model/src/kinds/index.ts b/packages/catalog-model/src/kinds/index.ts index e00a49acb5..bc157c79df 100644 --- a/packages/catalog-model/src/kinds/index.ts +++ b/packages/catalog-model/src/kinds/index.ts @@ -26,6 +26,11 @@ export type { ComponentEntityV1alpha1 as ComponentEntity, ComponentEntityV1alpha1, } from './ComponentEntityV1alpha1'; +export { domainEntityV1alpha1Validator } from './DomainEntityV1alpha1'; +export type { + DomainEntityV1alpha1 as DomainEntity, + DomainEntityV1alpha1, +} from './DomainEntityV1alpha1'; export { groupEntityV1alpha1Validator } from './GroupEntityV1alpha1'; export type { GroupEntityV1alpha1 as GroupEntity, @@ -37,6 +42,16 @@ export type { LocationEntityV1alpha1, } from './LocationEntityV1alpha1'; export * from './relations'; +export { resourceEntityV1alpha1Validator } from './ResourceEntityV1alpha1'; +export type { + ResourceEntityV1alpha1 as ResourceEntity, + ResourceEntityV1alpha1, +} from './ResourceEntityV1alpha1'; +export { systemEntityV1alpha1Validator } from './SystemEntityV1alpha1'; +export type { + SystemEntityV1alpha1 as SystemEntity, + SystemEntityV1alpha1, +} from './SystemEntityV1alpha1'; export { templateEntityV1alpha1Validator } from './TemplateEntityV1alpha1'; export type { TemplateEntityV1alpha1 as TemplateEntity, diff --git a/packages/catalog-model/src/kinds/relations.ts b/packages/catalog-model/src/kinds/relations.ts index 78bbc61df2..ed40a7e9c6 100644 --- a/packages/catalog-model/src/kinds/relations.ts +++ b/packages/catalog-model/src/kinds/relations.ts @@ -30,7 +30,7 @@ export const RELATION_OWNED_BY = 'ownedBy'; export const RELATION_OWNER_OF = 'ownerOf'; /** - * A relation with an API entity, typically from a component or system + * A relation with an API entity, typically from a component */ export const RELATION_CONSUMES_API = 'consumesApi'; export const RELATION_API_CONSUMED_BY = 'apiConsumedBy'; @@ -57,8 +57,13 @@ export const RELATION_MEMBER_OF = 'memberOf'; export const RELATION_HAS_MEMBER = 'hasMember'; /** +<<<<<<< HEAD * A part/whole relation, typically for components in a system and systems * in a domain. +======= + * A grouping relation, typically for components, resources or APIs in a + * system, or for systems inside a domain. +>>>>>>> Add system, domain and resource entity kinds */ export const RELATION_PART_OF = 'partOf'; export const RELATION_HAS_PART = 'hasPart'; diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts index 1d2562c25b..feb4791477 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.test.ts @@ -17,7 +17,10 @@ import { ApiEntity, ComponentEntity, + DomainEntity, GroupEntity, + ResourceEntity, + SystemEntity, UserEntity, } from '@backstage/catalog-model'; import { BuiltinKindsEntityProcessor } from './BuiltinKindsEntityProcessor'; @@ -42,12 +45,13 @@ describe('BuiltinKindsEntityProcessor', () => { lifecycle: 'l', providesApis: ['b'], consumesApis: ['c'], + system: 's', }, }; await processor.postProcessEntity(entity, location, emit); - expect(emit).toBeCalledTimes(8); + expect(emit).toBeCalledTimes(10); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -112,6 +116,22 @@ describe('BuiltinKindsEntityProcessor', () => { target: { kind: 'Component', namespace: 'default', name: 's' }, }, }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'System', namespace: 'default', name: 's' }, + type: 'hasPart', + target: { kind: 'Component', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Component', namespace: 'default', name: 'n' }, + type: 'partOf', + target: { kind: 'System', namespace: 'default', name: 's' }, + }, + }); }); it('generates relations for api entities', async () => { @@ -124,12 +144,13 @@ describe('BuiltinKindsEntityProcessor', () => { owner: 'o', lifecycle: 'l', definition: 'd', + system: 's', }, }; await processor.postProcessEntity(entity, location, emit); - expect(emit).toBeCalledTimes(2); + expect(emit).toBeCalledTimes(4); expect(emit).toBeCalledWith({ type: 'relation', relation: { @@ -146,6 +167,150 @@ describe('BuiltinKindsEntityProcessor', () => { target: { kind: 'Group', namespace: 'default', name: 'o' }, }, }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'System', namespace: 'default', name: 's' }, + type: 'hasPart', + target: { kind: 'API', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'API', namespace: 'default', name: 'n' }, + type: 'partOf', + target: { kind: 'System', namespace: 'default', name: 's' }, + }, + }); + }); + + it('generates relations for resource entities', async () => { + const entity: ResourceEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Resource', + metadata: { name: 'n' }, + spec: { + type: 'database', + owner: 'o', + system: 's', + }, + }; + + await processor.postProcessEntity(entity, location, emit); + + expect(emit).toBeCalledTimes(4); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Group', namespace: 'default', name: 'o' }, + type: 'ownerOf', + target: { kind: 'Resource', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Resource', namespace: 'default', name: 'n' }, + type: 'ownedBy', + target: { kind: 'Group', namespace: 'default', name: 'o' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'System', namespace: 'default', name: 's' }, + type: 'hasPart', + target: { kind: 'Resource', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Resource', namespace: 'default', name: 'n' }, + type: 'partOf', + target: { kind: 'System', namespace: 'default', name: 's' }, + }, + }); + }); + + it('generates relations for system entities', async () => { + const entity: SystemEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'System', + metadata: { name: 'n' }, + spec: { + owner: 'o', + domain: 'd', + }, + }; + + await processor.postProcessEntity(entity, location, emit); + + expect(emit).toBeCalledTimes(4); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Group', namespace: 'default', name: 'o' }, + type: 'ownerOf', + target: { kind: 'System', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'System', namespace: 'default', name: 'n' }, + type: 'ownedBy', + target: { kind: 'Group', namespace: 'default', name: 'o' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Domain', namespace: 'default', name: 'd' }, + type: 'hasPart', + target: { kind: 'System', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'System', namespace: 'default', name: 'n' }, + type: 'partOf', + target: { kind: 'Domain', namespace: 'default', name: 'd' }, + }, + }); + }); + + it('generates relations for domain entities', async () => { + const entity: DomainEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Domain', + metadata: { name: 'n' }, + spec: { + owner: 'o', + }, + }; + + await processor.postProcessEntity(entity, location, emit); + + expect(emit).toBeCalledTimes(2); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Group', namespace: 'default', name: 'o' }, + type: 'ownerOf', + target: { kind: 'Domain', namespace: 'default', name: 'n' }, + }, + }); + expect(emit).toBeCalledWith({ + type: 'relation', + relation: { + source: { kind: 'Domain', namespace: 'default', name: 'n' }, + type: 'ownedBy', + target: { kind: 'Group', namespace: 'default', name: 'o' }, + }, + }); }); it('generates relations for user entities', async () => { diff --git a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts index 67e89ac52c..c75a46874d 100644 --- a/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/BuiltinKindsEntityProcessor.ts @@ -19,6 +19,8 @@ import { apiEntityV1alpha1Validator, ComponentEntity, componentEntityV1alpha1Validator, + DomainEntity, + domainEntityV1alpha1Validator, Entity, getEntityName, GroupEntity, @@ -31,13 +33,17 @@ import { RELATION_CHILD_OF, RELATION_CONSUMES_API, RELATION_HAS_MEMBER, - RELATION_MEMBER_OF, RELATION_HAS_PART, - RELATION_PART_OF, + RELATION_MEMBER_OF, RELATION_OWNED_BY, RELATION_OWNER_OF, RELATION_PARENT_OF, + RELATION_PART_OF, RELATION_PROVIDES_API, + ResourceEntity, + resourceEntityV1alpha1Validator, + SystemEntity, + systemEntityV1alpha1Validator, templateEntityV1alpha1Validator, UserEntity, userEntityV1alpha1Validator, @@ -49,10 +55,13 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { private readonly validators = [ apiEntityV1alpha1Validator, componentEntityV1alpha1Validator, + resourceEntityV1alpha1Validator, groupEntityV1alpha1Validator, locationEntityV1alpha1Validator, templateEntityV1alpha1Validator, userEntityV1alpha1Validator, + systemEntityV1alpha1Validator, + domainEntityV1alpha1Validator, ]; async validateEntityKind(entity: Entity): Promise { @@ -135,6 +144,12 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { RELATION_CONSUMES_API, RELATION_API_CONSUMED_BY, ); + doEmit( + component.spec.system, + { defaultKind: 'System', defaultNamespace: selfRef.namespace }, + RELATION_PART_OF, + RELATION_HAS_PART, + ); } /* @@ -149,6 +164,32 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { RELATION_OWNED_BY, RELATION_OWNER_OF, ); + doEmit( + api.spec.system, + { defaultKind: 'System', defaultNamespace: selfRef.namespace }, + RELATION_PART_OF, + RELATION_HAS_PART, + ); + } + + /* + * Emit relations for the Resource kind + */ + + if (entity.kind === 'Resource') { + const resource = entity as ResourceEntity; + doEmit( + resource.spec.owner, + { defaultKind: 'Group', defaultNamespace: selfRef.namespace }, + RELATION_OWNED_BY, + RELATION_OWNER_OF, + ); + doEmit( + resource.spec.system, + { defaultKind: 'System', defaultNamespace: selfRef.namespace }, + RELATION_PART_OF, + RELATION_HAS_PART, + ); } /* @@ -185,6 +226,40 @@ export class BuiltinKindsEntityProcessor implements CatalogProcessor { ); } + /* + * Emit relations for the System kind + */ + + if (entity.kind === 'System') { + const system = entity as SystemEntity; + doEmit( + system.spec.owner, + { defaultKind: 'Group', defaultNamespace: selfRef.namespace }, + RELATION_OWNED_BY, + RELATION_OWNER_OF, + ); + doEmit( + system.spec.domain, + { defaultKind: 'Domain', defaultNamespace: selfRef.namespace }, + RELATION_PART_OF, + RELATION_HAS_PART, + ); + } + + /* + * Emit relations for the Domain kind + */ + + if (entity.kind === 'Domain') { + const domain = entity as DomainEntity; + doEmit( + domain.spec.owner, + { defaultKind: 'Group', defaultNamespace: selfRef.namespace }, + RELATION_OWNED_BY, + RELATION_OWNER_OF, + ); + } + return entity; } } From 6dee39ebe285f4f4013fbafad683c5da0a57b68d Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Wed, 13 Jan 2021 09:16:58 +0100 Subject: [PATCH 025/144] Fix spelling. --- docs/tutorials/quickstart-app-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 205fcddb65..14ed856c96 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -103,7 +103,7 @@ auth:

-
GitLab +
Gitlab

### 1. Open `app-config.yaml` and change it as follows From e921fb2ec7428029748ad4d5a109a32ebde437ea Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Wed, 13 Jan 2021 09:25:25 +0100 Subject: [PATCH 026/144] Fix GitHub stylization --- docs/tutorials/quickstart-app-auth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 14ed856c96..4218940ef7 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -60,7 +60,7 @@ external authentication provider. Please see a subset of possible providers and instructions to integrate them below. Steps 1 & 2 are described separately for each provider and steps beyond that are common for all. -

Github +
GitHub

### 1. Open `app-config.yaml` and change it as follows @@ -334,7 +334,7 @@ above: | Auth Provider | Import Name | | ------------- | ------------------- | -| Github | githubAuthApiRef | +| GitHub | githubAuthApiRef | | Gitlab | gitlabAuthApiRef | | Google | googleAuthApiRef | | Microsoft | microsoftAuthApiRef | From 37e4bf3473a1f399f572eb7bdcb636f57204ebd8 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Wed, 13 Jan 2021 10:12:40 +0100 Subject: [PATCH 027/144] Modifying GitLab text to be stylized --- docs/tutorials/quickstart-app-auth.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/tutorials/quickstart-app-auth.md b/docs/tutorials/quickstart-app-auth.md index 4218940ef7..1c661d274c 100644 --- a/docs/tutorials/quickstart-app-auth.md +++ b/docs/tutorials/quickstart-app-auth.md @@ -93,8 +93,8 @@ auth: - Log into http://github.com - Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth App)[https://github.com/settings/applications/new] -- Set Homepage URL = http://localhost:3000 -- Set Callback URL = http://localhost:7000/api/auth/github +- Set Homepage URL = `http://localhost:3000` +- Set Callback URL = `http://localhost:7000/api/auth/github` - Click [Register application] - On the next page, copy and paste your new Client ID and Client Secret to environment variables defined in the `app-config.yaml` file, @@ -103,7 +103,7 @@ auth:

-
Gitlab +
GitLab

### 1. Open `app-config.yaml` and change it as follows @@ -126,16 +126,16 @@ auth: $env: AUTH_GITLAB_CLIENT_ID clientSecret: $env: AUTH_GITLAB_CLIENT_SECRET - audience: https://gitlab.com # Or your self-hosted Gitlab instance URL + audience: https://gitlab.com # Or your self-hosted GitLab instance URL ``` -### 2. Generate a Gitlab Application client ID and secret +### 2. Generate a GitLab Application client ID and secret - Log into GitLab - Navigate to (Profile > Settings > Applications)[https://gitlab.com/-/profile/applications] - Name your application -- Set Callback URL = http://localhost:7000/api/auth/gitlab/handler/frame +- Set Callback URL = `http://localhost:7000/api/auth/gitlab/handler/frame` - Select the following values: - `read_user` (Read the authenticated user's personal information) - `read_repository` (Allows read-only access to the repository) @@ -335,7 +335,7 @@ above: | Auth Provider | Import Name | | ------------- | ------------------- | | GitHub | githubAuthApiRef | -| Gitlab | gitlabAuthApiRef | +| GitLab | gitlabAuthApiRef | | Google | googleAuthApiRef | | Microsoft | microsoftAuthApiRef | | Auth0 | auth0AuthApiRef | From 371f67ecd0473fee596d5832fc8931371c5e164e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 13 Jan 2021 10:37:02 +0100 Subject: [PATCH 028/144] techdocs-common: fix to-string breakage of binary files --- .changeset/funny-snails-cry.md | 5 +++++ packages/techdocs-common/src/stages/publish/awsS3.ts | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .changeset/funny-snails-cry.md diff --git a/.changeset/funny-snails-cry.md b/.changeset/funny-snails-cry.md new file mode 100644 index 0000000000..161a386a8c --- /dev/null +++ b/.changeset/funny-snails-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/techdocs-common': patch +--- + +fix to-string breakage of binary files diff --git a/packages/techdocs-common/src/stages/publish/awsS3.ts b/packages/techdocs-common/src/stages/publish/awsS3.ts index 7a21ae6475..3f7a0e3d81 100644 --- a/packages/techdocs-common/src/stages/publish/awsS3.ts +++ b/packages/techdocs-common/src/stages/publish/awsS3.ts @@ -24,13 +24,13 @@ import { PublisherBase, PublishRequest } from './types'; import fs from 'fs-extra'; import { Readable } from 'stream'; -const streamToString = (stream: Readable): Promise => { +const streamToBuffer = (stream: Readable): Promise => { return new Promise((resolve, reject) => { try { const chunks: any[] = []; stream.on('data', chunk => chunks.push(chunk)); stream.on('error', reject); - stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); + stream.on('end', () => resolve(Buffer.concat(chunks))); } catch (e) { throw new Error(`Unable to parse the response data, ${e.message}`); } @@ -173,7 +173,7 @@ export class AwsS3Publish implements PublisherBase { Key: `${entityRootDir}/techdocs_metadata.json`, }) .then(async file => { - const techdocsMetadataJson = await streamToString( + const techdocsMetadataJson = await streamToBuffer( file.Body as Readable, ); @@ -183,7 +183,7 @@ export class AwsS3Publish implements PublisherBase { ); } - resolve(techdocsMetadataJson); + resolve(techdocsMetadataJson.toString('utf-8')); }) .catch(err => { this.logger.error(err.message); @@ -211,7 +211,7 @@ export class AwsS3Publish implements PublisherBase { this.storageClient .getObject({ Bucket: this.bucketName, Key: filePath }) .then(async object => { - const fileContent = await streamToString(object.Body as Readable); + const fileContent = await streamToBuffer(object.Body as Readable); if (!fileContent) { throw new Error(`Unable to parse the file ${filePath}.`); } From 9560a1a4ab42a329124bf3daccf43e4e431dd5a0 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 13 Jan 2021 15:24:22 +0100 Subject: [PATCH 029/144] chore: Move Dockerfile at root to contrib/ In deployment docs https://backstage.io/docs/getting-started/deployment-other, we suggest doing a `yarn docker-build` and I thought the root Dockerfile was being used to build the image. Hence I modified it for some needs, but no changes were reflected. Later I found that `yarn docker-build` uses the `Dockerfile` present inside `packages/backend` https://github.com/backstage/backstage/blob/master/packages/backend/Dockerfile. So, I think the Dockerfile at the root is a bit misleading, and should be moved to contrib. Signed-off-by: Himanshu Mishra --- Dockerfile => contrib/docker/frontend-with-nginx/Dockerfile | 0 .../docker/frontend-with-nginx/docker}/default.conf.template | 0 {docker => contrib/docker/frontend-with-nginx/docker}/run.sh | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename Dockerfile => contrib/docker/frontend-with-nginx/Dockerfile (100%) rename {docker => contrib/docker/frontend-with-nginx/docker}/default.conf.template (100%) rename {docker => contrib/docker/frontend-with-nginx/docker}/run.sh (100%) diff --git a/Dockerfile b/contrib/docker/frontend-with-nginx/Dockerfile similarity index 100% rename from Dockerfile rename to contrib/docker/frontend-with-nginx/Dockerfile diff --git a/docker/default.conf.template b/contrib/docker/frontend-with-nginx/docker/default.conf.template similarity index 100% rename from docker/default.conf.template rename to contrib/docker/frontend-with-nginx/docker/default.conf.template diff --git a/docker/run.sh b/contrib/docker/frontend-with-nginx/docker/run.sh similarity index 100% rename from docker/run.sh rename to contrib/docker/frontend-with-nginx/docker/run.sh From 71fb4e1281b57754ed8cb9765bba2018678d98a4 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 13 Jan 2021 16:58:11 +0100 Subject: [PATCH 030/144] cli: Remove api url from github app configuration --- .../src/commands/create-github-app/GithubCreateAppServer.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts index 406e563ebc..45671c2ead 100644 --- a/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts +++ b/packages/cli/src/commands/create-github-app/GithubCreateAppServer.ts @@ -47,7 +47,6 @@ const FORM_PAGE = ` type GithubAppConfig = { appId: number; - apiUrl: string; slug?: string; name?: string; webhookUrl?: string; @@ -88,14 +87,11 @@ export class GithubCreateAppServer { `POST /app-manifests/${encodeURIComponent( req.query.code as string, )}/conversions`, - ).then(({ data, url }) => { - // url = https://api.github.com/app-manifests//conversions - const apiUrl = url.replace(/(?:\/[^\/]+){3}$/, ''); + ).then(({ data }) => { resolve({ name: data.name, slug: data.slug, appId: data.id, - apiUrl, webhookUrl: this.webhookUrl, clientId: data.client_id, clientSecret: data.client_secret, From 8277fe6f77094d3254c268ec8e5c64c28221242c Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 13 Jan 2021 17:04:10 +0100 Subject: [PATCH 031/144] Add changeset --- .changeset/real-vans-provide.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/real-vans-provide.md diff --git a/.changeset/real-vans-provide.md b/.changeset/real-vans-provide.md new file mode 100644 index 0000000000..9d83804bd6 --- /dev/null +++ b/.changeset/real-vans-provide.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Remove `apiUrl` from the output of the create-github-app because apiUrl already exist in the GitHub integration config. From 4f78ee3a69571d0fed625a5120f64ffb940ddbdf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Jan 2021 16:35:56 +0000 Subject: [PATCH 032/144] chore(deps): bump azure-devops-node-api from 10.1.1 to 10.2.1 Bumps [azure-devops-node-api](https://github.com/Microsoft/azure-devops-node-api) from 10.1.1 to 10.2.1. - [Release notes](https://github.com/Microsoft/azure-devops-node-api/releases) - [Commits](https://github.com/Microsoft/azure-devops-node-api/commits) Signed-off-by: dependabot[bot] --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index b10819d4f2..1981e1f886 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8560,12 +8560,12 @@ axobject-query@^2.0.2: integrity sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ== azure-devops-node-api@^10.1.1: - version "10.1.1" - resolved "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-10.1.1.tgz#9016d8935316fff260f5f8fafd81d0caff90a19e" - integrity sha512-P4Hyrh/+Nzc2KXQk73z72/GsenSWIH5o8uiyELqykJYs9TWTVCxVwghoR7lPeiY6QVoXkq2S2KtvAgi5fyjl9w== + version "10.2.1" + resolved "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-10.2.1.tgz#835080164f8c30cec6506c47198b044c053f1f36" + integrity sha512-XuSiUaYpk0tQpd9fD8qfRa5y1IdavupKNVmwxy0w/RhmxG2Wl8uAYnNJchUoWd3Rn9On0mYTCCZSn+UlYdYFSg== dependencies: tunnel "0.0.6" - typed-rest-client "^1.7.3" + typed-rest-client "^1.8.0" underscore "1.8.3" babel-code-frame@^6.22.0: @@ -24977,10 +24977,10 @@ type@^2.0.0: resolved "https://registry.npmjs.org/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== -typed-rest-client@^1.7.3: - version "1.7.3" - resolved "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.7.3.tgz#1beb263b86b14d34596f6127c6172dd5fd652e7b" - integrity sha512-CwTpx/TkRHGZoHkJhBcp4X8K3/WtlzSHVQR0OIFnt10j4tgy4ypgq/SrrgVpA1s6tAL49Q6J3R5C0Cgfh2ddqA== +typed-rest-client@^1.8.0: + version "1.8.0" + resolved "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.0.tgz#3b6c22a7cc31b665ec1e4bedb3482ebe12e2fbe6" + integrity sha512-Nu1MrdH6ECrRW5gHoRAdubgCs4oH6q5/J76jsEC8bVDfvVoVPkigukPalhMHPwb7ZvpsZqPptd5zpt/QdtrdBw== dependencies: qs "^6.9.1" tunnel "0.0.6" From cb7af51e7367e57af5c555b49ceda8a92a490c72 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 13 Jan 2021 21:08:03 +0100 Subject: [PATCH 033/144] techdocs: cache docs site when built using urlReader for 30 minutes This caching makes it usable experience, so that docs are not built on every load. In future readTree will support a method to fetch the timestamp of the latest HEAD. And it should be used to invalidate the cache. --- .changeset/techdocs-rotten-crabs-ring.md | 5 +++++ plugins/techdocs-backend/src/DocsBuilder/builder.ts | 12 ++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 .changeset/techdocs-rotten-crabs-ring.md diff --git a/.changeset/techdocs-rotten-crabs-ring.md b/.changeset/techdocs-rotten-crabs-ring.md new file mode 100644 index 0000000000..7eddd8ba6c --- /dev/null +++ b/.changeset/techdocs-rotten-crabs-ring.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-backend': patch +--- + +If using Url Reader, cache downloaded source files for 30 minutes. diff --git a/plugins/techdocs-backend/src/DocsBuilder/builder.ts b/plugins/techdocs-backend/src/DocsBuilder/builder.ts index 3d40f0a870..33d3120c47 100644 --- a/plugins/techdocs-backend/src/DocsBuilder/builder.ts +++ b/plugins/techdocs-backend/src/DocsBuilder/builder.ts @@ -144,6 +144,18 @@ export class DocsBuilder { } } + // Cache downloaded source files for 30 minutes. + // TODO: When urlReader/readTree supports some way to get latest commit timestamp, + // it should be used to invalidate cache. + if (type === 'url') { + const builtAt = buildMetadataStorage.getTimestamp(); + const now = Date.now(); + + if (builtAt > now - 1800000) { + return true; + } + } + this.logger.debug( `Docs for entity ${getEntityId(this.entity)} was outdated.`, ); From dc33c518904549716a8af0cc5cc73fbe2f15388d Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 13 Jan 2021 21:35:36 +0100 Subject: [PATCH 034/144] TechDocs: Use URL Reader in the out-of-the-box experience It is time to start using URL Reader for the exmaple docs components we provide in the out-of-the-box experience (i.e. when users experience TechDocs by doing a git clone of this repository). URL Reader makes the prepare step 8x faster. --- app-config.yaml | 4 ++-- catalog-info.yaml | 2 +- .../{documented-component.yaml => catalog-info.yaml} | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename plugins/techdocs-backend/examples/documented-component/{documented-component.yaml => catalog-info.yaml} (61%) diff --git a/app-config.yaml b/app-config.yaml index d2100801ae..ac9cf700df 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -178,9 +178,9 @@ catalog: # Example component for github-actions - type: url target: https://github.com/backstage/backstage/blob/master/plugins/github-actions/examples/sample.yaml - # Example component for techdocs + # Example component for TechDocs - type: url - target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/documented-component.yaml + target: https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml # Backstage example APIs - type: url target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml diff --git a/catalog-info.yaml b/catalog-info.yaml index 617d01093e..2144d1709c 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -6,7 +6,7 @@ metadata: Backstage is an open-source developer portal that puts the developer experience first. annotations: github.com/project-slug: backstage/backstage - backstage.io/techdocs-ref: github:https://github.com/backstage/backstage.git + backstage.io/techdocs-ref: url:https://github.com/backstage/backstage/tree/master lighthouse.com/website-url: https://backstage.io spec: type: library diff --git a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml similarity index 61% rename from plugins/techdocs-backend/examples/documented-component/documented-component.yaml rename to plugins/techdocs-backend/examples/documented-component/catalog-info.yaml index 800116344f..d609866f9b 100644 --- a/plugins/techdocs-backend/examples/documented-component/documented-component.yaml +++ b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml @@ -4,7 +4,7 @@ metadata: name: documented-component description: A Service with TechDocs documentation annotations: - backstage.io/techdocs-ref: 'github:https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component' + backstage.io/techdocs-ref: 'url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component' spec: type: service lifecycle: experimental From 77c8a9af2106396f1538379142a2d7dbe44c8bb6 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 13 Jan 2021 23:16:19 +0100 Subject: [PATCH 035/144] docs: Update project structure page to remove docker/ --- docs/support/project-structure.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/support/project-structure.md b/docs/support/project-structure.md index bb2c02ec76..5c8a8cd3bb 100644 --- a/docs/support/project-structure.md +++ b/docs/support/project-structure.md @@ -32,10 +32,6 @@ the code. better control over our `yarn.lock` file and hopefully avoid problems due to yarn versioning differences. -- [`docker/`](https://github.com/backstage/backstage/tree/master/docker) - Files - related to our root Dockerfile. We are planning to refactor this, so expect - this folder to be moved in the future. - - [`contrib/`](https://github.com/backstage/backstage/tree/master/contrib) - Collection of examples or resources provided by the community. We really appreciate contributions in here and encourage them being kept up to date. From 3e9b38e288231d070777cd651cd3727b5705b4b6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 10 Jan 2021 18:11:23 +0100 Subject: [PATCH 036/144] docs: initial incomplete composability docs --- docs/plugins/composability.md | 86 +++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 docs/plugins/composability.md diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md new file mode 100644 index 0000000000..d85a557772 --- /dev/null +++ b/docs/plugins/composability.md @@ -0,0 +1,86 @@ +--- +id: composability +title: New Composability System +description: + Documentation and migration instructions for new composability APIs. +--- + +## Summary + +This page describes the new composability system that was recently introduced in +Backstage. It describes the new system from the perspective of the existing +patterns and APIs. As the new system is solidified and existing code is ported, +this page will removed and replaced with a more direct description of the +composability system. + +The core principle of the new composability system is that plugins should have +clear boundaries and connections. It should isolate crashes within a plugin, but +allow navigation between them. It should allow for plugins to be loaded only +when needed, and enable plugins to provide extension point for other plugins to +build upon. The composability system is also built with an app-first mindset, +prioritizing simplicity and clarity in the app over plugins and core APIs. + +The new composability system isn't a single new API surface. It is a collection +of patterns, new primitives, new APIs, and old APIs used in new ways. At the +core is the new concept of Extensions, are exported by plugins for use in the +app. There is a new primitive called component data, which is used to connect +plugin and the app, and a new hook that provides a practical use of . + +## Component Data + +Component data is a new composability primitive that is introduced as a way to +provide a new data dimension for React components. Data is attached to React +components using a key, and is then readable from any JSX elements created with +those components using the same key, as illustrated by the following example: + +```tsx +const MyComponent = () =>

This is my component

; +attachComponentData(MyComponent, 'my.data', 5); + +const element = ; +const myData = getComponentData(element, 'my.data'); +// myData === 5 +``` + +The purpose of component data is to provide a method for embedding data that can +be inspected before rendering elements. It's a pattern that is quite common +among React libraries, and used for example by `react-router` and `material-ui` +to discover properties of the child elements before rendering. Although in those +libraries only the element type and props are typically inspected, while our +component data adds more structured access and simplifies evolution by allowing +for multiple different versions of a piece of data to be used at once. + +The main use-case + +## Extensions + +Extensions are what plugins export for use in an app. Most typically they are +React components, but in practice they can be any kind of value. They are +created using `create*Extension` functions, and wrapped with `plugin.provide()` +in order to create the actual exported extension. + +The Backstage core API currently provides two different types of extension +creators, `createComponentExtension`, and `createRoutableExtension`. + +### Extensions from a plugin's point of view + +Extensions are one of the primary methods to traverse the plugin boundary, and +the way that plugins provide concrete content for use within an app. They +replace existing component export concepts such as `Router` or `*Card`s for +display on entity overview pages. + +### Using Extensions in an app + +TODO + +## RouteRefs, useRouteRef, and plugin routes and externalRoutes + +TODO + +## Binding external routes in the app + +TODO + +## New catalog components, EntitySwitch & EntityLayout, and how to use those in the app + +TODO From 492258d2a10d83fba72d38e5101dc81b0f8795c7 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 12 Jan 2021 20:23:27 +0100 Subject: [PATCH 037/144] docs: lotsa more composability docs --- .github/styles/vocab.txt | 2 + docs/plugins/composability.md | 255 +++++++++++++++++++++++++++++++--- 2 files changed, 234 insertions(+), 23 deletions(-) diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index a71887f796..6c38cf638a 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -183,6 +183,8 @@ rollbar Rollbar Rollup Rosaceae +routable +Routable rst rsync rugvip diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index d85a557772..5c96b88cee 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -26,7 +26,12 @@ core is the new concept of Extensions, are exported by plugins for use in the app. There is a new primitive called component data, which is used to connect plugin and the app, and a new hook that provides a practical use of . -## Component Data +## New Concepts + +This section is a brief look into all the new and updated concepts that were put +in place to support the new composability system. + +### Component Data Component data is a new composability primitive that is introduced as a way to provide a new data dimension for React components. Data is attached to React @@ -43,44 +48,248 @@ const myData = getComponentData(element, 'my.data'); ``` The purpose of component data is to provide a method for embedding data that can -be inspected before rendering elements. It's a pattern that is quite common -among React libraries, and used for example by `react-router` and `material-ui` -to discover properties of the child elements before rendering. Although in those -libraries only the element type and props are typically inspected, while our -component data adds more structured access and simplifies evolution by allowing -for multiple different versions of a piece of data to be used at once. +be inspected before rendering elements. Element inspection is a pattern that is +quite common among React libraries, and used for example by `react-router` and +`material-ui` to discover properties of the child elements before rendering. +Though in those libraries only the element type and props are typically +inspected, while our component data adds more structured access and simplifies +evolution by allowing for multiple different versions of a piece of data to be +used at once. -The main use-case +The initial use-cases for component data is support route and plugin discovery +through elements in the app. Through this we allow for the React element tree in +the app to be the source of truth, both for which plugins are used and all +top-level plugin routes in the app. The use of component data is not limited to +these use-cases though, as it can be used as a primitive to create new +abstractions as well. -## Extensions +### Extensions Extensions are what plugins export for use in an app. Most typically they are React components, but in practice they can be any kind of value. They are created using `create*Extension` functions, and wrapped with `plugin.provide()` in order to create the actual exported extension. -The Backstage core API currently provides two different types of extension -creators, `createComponentExtension`, and `createRoutableExtension`. +The extension type is dead simple: -### Extensions from a plugin's point of view +```ts +export type Extension = { + expose(plugin: BackstagePlugin): T; +}; +``` + +The power of extensions comes from the ability of various actors to hook into +their usage. The creation and plugin wrapping is controlled by whoever owns the +creation function, the Backstage core is able to hook into the process of +exposing the extension outside the plugin, and in the end the app controls the +usage of the extension. + +The Backstage core API currently provides two different types of extension +creators, `createComponentExtension`, and `createRoutableExtension`. Component +extensions are plain react component with no particular requirements, such as +cards for entity overview pages. The component will be exported more or less as +is, but is wrapped up to provide things like an error boundary, lazy loading, +and a plugin context. + +Routable extensions build on top of component extensions and are used for any +component that should be rendered at a specific route path, such as full pages +or entity page tab content. When creating a routable extension you need to +supply a `RouteRef` as `mountPoint`. The mount point will be the handle of the +component for the outside world, and is used by other components and plugins +that wish to link to the routable component. + +As of now there are only two extension creation functions, but it is possible to +add more of them in the future, both in the core library and in plugins that +wish to provide an extension point for other plugins to build upon. Extensions +are also not tied to React, and can both be used to model generic JavaScript +concepts, as well as potentially bridge to rendering libraries and web +frameworks other than React. + +### Extensions from a Plugin's Point of View Extensions are one of the primary methods to traverse the plugin boundary, and the way that plugins provide concrete content for use within an app. They replace existing component export concepts such as `Router` or `*Card`s for display on entity overview pages. -### Using Extensions in an app +It is recommended to create the exported extensions either in the top-level +`plugin.ts` file, or in a dedicated `extensions.ts` (or `.tsx`) file. That file +should not contain the bulk of the implementation though, and in fact, if the +extension is a React component it is recommended to lazy-load the actual +component. Component extensions support lazy loading out of the box using the +`lazy` component declaration, for example: + +```ts +export const EntityFooCard = plugin.provide( + createComponentExtension({ + component: { + lazy: () => import('./components/FooCard').then(m => m.FooCard), + }, + }), +); +``` + +Routable extensions even enforce lazy loading, for example: + +```ts +export const FooPage = plugin.provide( + createRoutableExtension({ + component: () => import('./components/FooPage').then(m => m.FooPage), + mountPoint: fooRouteRef, + }), +); +``` + +### Using Extensions in an App + +Right now all extensions are modelled as React components. The usage of these +extension is like regular usage of any React components, with one important +difference. Extensions must be all be part of a single React element tree +spanning from the root `AppProvider`. + +For example, the following app code does **NOT** work: + +```tsx +const AppRoutes = () => ( + + } /> + } /> + +); + +const App = () => ( + + + + + + + +); +``` + +But it is simple to fix! Simply make sure that you don't create any intermediate +components in the app, for example like this: + +```tsx +const appRoutes = ( + + } /> + } /> + +); + +const App = () => ( + + + {appRoutes} + + +); +``` + +### New Routing System + +A big piece of what is enabled by moving over to this new composability system +is to make `RouteRef`s useful. The `RouteRef`s no longer have their own path, in +fact the only required parameter is currently a `title`. Instead of assigning a +path to each `RouteRef` and possibly overriding these paths in the app, the +concrete `path` for each `RouteRef` is discovered based on the element tree in +the app. Let's consider the following example: + +```tsx + + } /> + } /> + +``` + +We'll assume that `FooPage` and `BarPage` are routable extensions, exported by +the `fooPlugin` and `barPlugin` respectively. Since the `FooPage` is a routable +extension it has a `RouteRef` assigned as its mount point, which we'll refer to +as `fooRootRouteRef`. + +Given the above example, the `fooRootRouteRef` will be associated with the +`'/foo'` route. The path is no longer accessible via the `path` property of the +`RouteRef` though, as the routing structure is tied to the app's react tree. We +instead use the new `useRouteRef` hook if we want to create a concrete link to +the page. The `useRouteRef` hook takes a single `RouteRef` as its only +parameter, and returns a function that is called to create the URL. + +Now let's assume that we want to link from the `BarPage` to the `FooPage`. +Before the introduction of the new composability system, we would do this by +importing the `fooRootRouteRef` from the `fooPlugin`. This created an +unnecessary dependency on the plugin, and also provided little flexibility +allowing the app to tie plugins together rather than the plugins themselves. To +handle this, we introduce the concept of `ExternalRouteRef`s. Much like regular +route refs, they can be passed to `useRouteRef` to create concrete URLs, but +they can not be used as mount points in routable component and instead have to +be associated with an actual using route bindings in the app. + +The `ExternalRouteRef` inside the `barPlugin` should also not be opinionated +about what it is linking to either, allowing the app to decide the final target. +It should however provide context in how the link is presented or used, to make +it easier to understand the flow of the app. If the `BarPage` for example wants +to link to an external page in the header, it might declare an +`ExternalRouteRef` similar to this: + +```ts +const headerLinkRouteRef = createExternalRouteRef(); +``` + +### Binding External Routes in the App + +The association of external routes are controlled by the app. Each +`ExternalRouteRef` of a plugin is bound to an actual `RouteRef`, usually from +another plugin. The binding process happens once att app startup, and is then +used through the lifetime of the app to help resolve concrete route paths. + +Using the above example of the `BarPage` linking to the `FooPage`, we might do +something like this in the app: + +```ts +createApp({ + bindRoutes({ bind }) { + bind(barPlugin.externalRoutes, { + headerLink: fooPlugin.routes.root, + }); + }, +}); +``` + +Given the above binding, using `useRouteRef(external)` + +Note that we are not importing and using the `RouteRef`s directly, and instead +rely on the plugin instance to access routes of the plugins. This is a new +convention that was introduced to provide better namespacing and discoverability +of routes, as well as reduce the number of different things exported from each +plugin package. The route references would be supplied to `createPlugin` like +this: + +```ts +// In foo-plugin +export const fooPlugin = createPlugin({ + routes: { + root: fooRootRouteRef, + }, + ... +}) + +// In bar-plugin +export const barPlugin = createPlugin({ + externalRoutes: { + headerLink: headerLinkRouteRef, + }, + ... +}) +``` + +### New Catalog Components + +EntitySwitch & EntityLayout, and how to use those in the app TODO -## RouteRefs, useRouteRef, and plugin routes and externalRoutes +## Porting Existing Plugins -TODO - -## Binding external routes in the app - -TODO - -## New catalog components, EntitySwitch & EntityLayout, and how to use those in the app - -TODO +## Porting Existing Apps From 70d8653bdad5649399bcae8d146922bdc3d7b2d5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 13 Jan 2021 23:52:10 +0100 Subject: [PATCH 038/144] docs/composability: the rest of the f***ing owl --- docs/plugins/composability.md | 403 +++++++++++++++++++++++++++++----- 1 file changed, 346 insertions(+), 57 deletions(-) diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index 5c96b88cee..3e38f3376e 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -16,15 +16,17 @@ composability system. The core principle of the new composability system is that plugins should have clear boundaries and connections. It should isolate crashes within a plugin, but allow navigation between them. It should allow for plugins to be loaded only -when needed, and enable plugins to provide extension point for other plugins to +when needed, and enable plugins to provide extension points for other plugins to build upon. The composability system is also built with an app-first mindset, -prioritizing simplicity and clarity in the app over plugins and core APIs. +prioritizing simplicity and clarity in the app over that in the plugins and core +APIs. The new composability system isn't a single new API surface. It is a collection -of patterns, new primitives, new APIs, and old APIs used in new ways. At the -core is the new concept of Extensions, are exported by plugins for use in the -app. There is a new primitive called component data, which is used to connect -plugin and the app, and a new hook that provides a practical use of . +of patterns, primitives, new APIs, and old APIs used in new ways. At the core is +the new concept of extensions, which are exported by plugins for use in the app. +There is also a new primitive called component data, which assists in the +conversion to a more declarative app. The `RouteRef`s now have a clear purpose +as well, and can be used route to pages in a flexible way. ## New Concepts @@ -36,7 +38,7 @@ in place to support the new composability system. Component data is a new composability primitive that is introduced as a way to provide a new data dimension for React components. Data is attached to React components using a key, and is then readable from any JSX elements created with -those components using the same key, as illustrated by the following example: +those components, using the same key, as illustrated by the following example: ```tsx const MyComponent = () =>

This is my component

; @@ -51,26 +53,26 @@ The purpose of component data is to provide a method for embedding data that can be inspected before rendering elements. Element inspection is a pattern that is quite common among React libraries, and used for example by `react-router` and `material-ui` to discover properties of the child elements before rendering. -Though in those libraries only the element type and props are typically +Although in those libraries only the element type and props are typically inspected, while our component data adds more structured access and simplifies evolution by allowing for multiple different versions of a piece of data to be -used at once. +used and interpreted at once. The initial use-cases for component data is support route and plugin discovery through elements in the app. Through this we allow for the React element tree in -the app to be the source of truth, both for which plugins are used and all -top-level plugin routes in the app. The use of component data is not limited to -these use-cases though, as it can be used as a primitive to create new +the app to be the source of truth, both for which plugins are used, as well as +all top-level plugin routes in the app. The use of component data is not limited +to these use-cases though, as it can be used as a primitive to create new abstractions as well. ### Extensions Extensions are what plugins export for use in an app. Most typically they are -React components, but in practice they can be any kind of value. They are -created using `create*Extension` functions, and wrapped with `plugin.provide()` -in order to create the actual exported extension. +React components, but in practice they can be any kind of JavaScript value. They +are created using `create*Extension` functions, and wrapped with +`plugin.provide()` in order to create the actual exported extension. -The extension type is dead simple: +The extension type is a simple one: ```ts export type Extension = { @@ -86,14 +88,14 @@ usage of the extension. The Backstage core API currently provides two different types of extension creators, `createComponentExtension`, and `createRoutableExtension`. Component -extensions are plain react component with no particular requirements, such as -cards for entity overview pages. The component will be exported more or less as -is, but is wrapped up to provide things like an error boundary, lazy loading, -and a plugin context. +extensions are plain React component with no particular requirements, for +example a card for an entity overview page. The component will be exported more +or less as is, but is wrapped to provide things like an error boundary, lazy +loading, and a plugin context. Routable extensions build on top of component extensions and are used for any -component that should be rendered at a specific route path, such as full pages -or entity page tab content. When creating a routable extension you need to +component that should be rendered at a specific route path, such as top-level +pages or entity page tab content. When creating a routable extension you need to supply a `RouteRef` as `mountPoint`. The mount point will be the handle of the component for the outside world, and is used by other components and plugins that wish to link to the routable component. @@ -129,7 +131,8 @@ export const EntityFooCard = plugin.provide( ); ``` -Routable extensions even enforce lazy loading, for example: +Routable extensions even enforce lazy loading, as it is the only way to provide +a component: ```ts export const FooPage = plugin.provide( @@ -144,8 +147,8 @@ export const FooPage = plugin.provide( Right now all extensions are modelled as React components. The usage of these extension is like regular usage of any React components, with one important -difference. Extensions must be all be part of a single React element tree -spanning from the root `AppProvider`. +difference. Extensions must all be part of a single React element tree spanning +from the root `AppProvider`. For example, the following app code does **NOT** work: @@ -168,8 +171,8 @@ const App = () => ( ); ``` -But it is simple to fix! Simply make sure that you don't create any intermediate -components in the app, for example like this: +But in this case it is simple to fix! Simply be sure to not create any +intermediate components in the app, for example like this: ```tsx const appRoutes = ( @@ -198,10 +201,12 @@ concrete `path` for each `RouteRef` is discovered based on the element tree in the app. Let's consider the following example: ```tsx - - } /> - } /> - +const appRoutes = ( + + } /> + } /> + +); ``` We'll assume that `FooPage` and `BarPage` are routable extensions, exported by @@ -214,24 +219,32 @@ Given the above example, the `fooRootRouteRef` will be associated with the `RouteRef` though, as the routing structure is tied to the app's react tree. We instead use the new `useRouteRef` hook if we want to create a concrete link to the page. The `useRouteRef` hook takes a single `RouteRef` as its only -parameter, and returns a function that is called to create the URL. +parameter, and returns a function that is called to create the URL. For example +like this: + +```tsx +const MyComponent = () => { + const fooRoute = useRouteRef(fooRouteRef); + return Link to Foo; +}; +``` Now let's assume that we want to link from the `BarPage` to the `FooPage`. Before the introduction of the new composability system, we would do this by importing the `fooRootRouteRef` from the `fooPlugin`. This created an -unnecessary dependency on the plugin, and also provided little flexibility -allowing the app to tie plugins together rather than the plugins themselves. To -handle this, we introduce the concept of `ExternalRouteRef`s. Much like regular -route refs, they can be passed to `useRouteRef` to create concrete URLs, but -they can not be used as mount points in routable component and instead have to -be associated with an actual using route bindings in the app. +unnecessary dependency on the plugin, and also provided little flexibility in +allowing the app to tie plugins together, with the links instead being dictated +by the plugins themselves. To solve this, we introduce `ExternalRouteRef`s. Much +like regular route references, they can be passed to `useRouteRef` to create +concrete URLs, but they can not be used as mount points in routable component +and instead have to be associated with a target route using route bindings in +the app. -The `ExternalRouteRef` inside the `barPlugin` should also not be opinionated -about what it is linking to either, allowing the app to decide the final target. -It should however provide context in how the link is presented or used, to make -it easier to understand the flow of the app. If the `BarPage` for example wants -to link to an external page in the header, it might declare an -`ExternalRouteRef` similar to this: +We create a new `ExternalRouteRef` inside the `barPlugin`, using a neutral name +that describes its role in the plugin rather than a specific plugin page that it +might be linking to, allowing the app to decide the final target. If the +`BarPage` for example wants to link to an external page in the header, it might +declare an `ExternalRouteRef` similar to this: ```ts const headerLinkRouteRef = createExternalRouteRef(); @@ -239,10 +252,11 @@ const headerLinkRouteRef = createExternalRouteRef(); ### Binding External Routes in the App -The association of external routes are controlled by the app. Each -`ExternalRouteRef` of a plugin is bound to an actual `RouteRef`, usually from -another plugin. The binding process happens once att app startup, and is then -used through the lifetime of the app to help resolve concrete route paths. +The association of external routes is controlled by the app. Each +`ExternalRouteRef` of a plugin should be<- bound to an actual `RouteRef`, +usually from another plugin. The binding process happens once att app startup, +and is then used through the lifetime of the app to help resolve concrete route +paths. Using the above example of the `BarPage` linking to the `FooPage`, we might do something like this in the app: @@ -257,14 +271,15 @@ createApp({ }); ``` -Given the above binding, using `useRouteRef(external)` +Given the above binding, using `useRouteRef(headerLinkRouteRef)` within the +`barPlugin` will let us create a link whatever path the `FooPage` is mounted at. -Note that we are not importing and using the `RouteRef`s directly, and instead -rely on the plugin instance to access routes of the plugins. This is a new -convention that was introduced to provide better namespacing and discoverability -of routes, as well as reduce the number of different things exported from each -plugin package. The route references would be supplied to `createPlugin` like -this: +Note that we are not importing and using the `RouteRef`s directly in the app, +and instead rely on the plugin instance to access routes of the plugins. This is +a new convention that was introduced to provide better namespacing and +discoverability of routes, as well as reduce the number of separate exports from +each plugin package. The route references would be supplied to `createPlugin` +like this: ```ts // In foo-plugin @@ -284,12 +299,286 @@ export const barPlugin = createPlugin({ }) ``` +Also note that you almost always want to create the route references themselves +in a different file than the one that creates the plugin instance, for example a +top-level `routes.ts`. This is to avoid circular imports when you use the route +references from other parts of the app. + +### Parameterized Routes + +A new addition to `RouteRef`s is the possibility of adding named and typed +parameters. Parameters are declared at creation, and will enforce presence of +the parameters in the path in the app, and require them as a parameter when +using `useRouteRef`. + +The following is an example of creation and usage of a parameterized route: + +```tsx +// Creation of a parameterized route +const myRouteRef = createRouteRef({ + title: 'My Named Route', + params: ['name'] +}) + +// In the app, where MyPage is a routable extension with myRouteRef set as mountPoint +}/> + +// Usage within a component +const myRoute = useRouteRef(myRouteRef) +return ( +
+ A + B +
+) +``` + +It is currently not possible to have parameterized `ExternalRouteRef`s, or to +bind an external route to a parameterized route, although this may be added in +the future if needed. + ### New Catalog Components -EntitySwitch & EntityLayout, and how to use those in the app +The established pattern for selecting what plugins should be available on each +catalog page is to use custom components in the app, with logic embedded in the +render function. Typically this takes form as a component that either receives +the entity via props or uses the `useEntity` hook to retrieve the selected +entity. A `switch` or `if` / `else if` chain is then used to select what +children should be rendered based on information in the entity. -TODO +This pattern will no longer work with the new composability system, and in +general is very difficult to build any form declarative model around, as it +depends on runtime execution. To help replace existing code, a new +`EntitySwitch` component has been added to the `@backstage/catalog` plugin, +which grabs the selected entity from context, and selects at most one element to +render using a list of `EntitySwitch.Case`s children. + +For example, if you want all entities of kind `"Template"` to be rendered with a +`MyTemplate` component, and all other entities to be rendered with a `MyOther` +component, you would do the following: + +```tsx + + + + + + + + + + +// Shorter form if desired: + + }/> + }/> + +``` + +The `EntitySwitch` component will render the children of the first +`EntitySwitch.Case` that returns `true` when the selected entity is passed to +the function of the `if` prop. If none of the cases match, no children will be +rendered, and if a case doesn't specify an `if` filter function, it will always +match. The `if` property is simply a function of the type +`(entity: Entity) => boolean`, for example, `isKind` can be implemented like +this: + +```ts +function isKind(kind: string) { + return (entity: Entity) => entity.kind.toLowerCase() === kind.toLowerCase(); +} +``` + +The `@backstage/catalog` plugin provides a couple of built-in conditions, +`isKind`, `isComponentType`, and `isNamespace`. + +In addition to the `EntitySwitch` component, the catalog plugin also exports a +new `EntityLayout` component. It is a tweaked version and replacement for the +`EntityPageLayout` component, and is introduced more in depth in the app +migration section below. ## Porting Existing Plugins +There are a couple of high-level steps to porting an existing plugin to the new +composability system: + +- Remove usage of `router.addRoute` or `router.registerRoute` within + `createPlugin`, and export the page components as routable extensions instead. +- Switch any `Router` export to instead be a routable extension. +- Change any plain component exports, such as catalog overview cards, to be + component extensions. +- Stop exporting `RouteRef`s and instead pass them to `createPlugin`. +- Stop accepting `RouteRef`s as props or importing them from other plugins, + instead create an `ExternalRouteRef` as a replacement, and pass it to + `createPlugin.` +- Rename any other exported symbols according to the naming pattern table below. + +Note that removing the existing exports and configuration is a breaking change +in any plugin. If backwards compatibility is needed the existing code be +deprecated while making the new additions, to then be removed at a later point. + +### Naming Patterns + +Many export naming patterns have been changed to avoid import aliases and to +clarify intent. Refer to the following table to formulate the new name: + +| Description | Existing Pattern | New Pattern | Examples | +| -------------------- | -------------------------- | --------------- | ---------------------------------------------- | +| Top-level Pages | Router | \*Page | CatalogIndexPage, SettingsPage, LighthousePage | +| Entity Tab Content | Router | Entity\*Content | EntityJenkinsContent, EntityKubernetesContent | +| Entity Overview Card | \*Card | Entity\*Card | EntitySentryCard, EntityPagerDutyCard | +| Entity Conditional | isPluginApplicableToEntity | is\*Available | isPagerDutyAvailable, isJenkinsAvailable | +| Plugin Instance | plugin | \*Plugin | jenkinsPlugin, catalogPlugin | + ## Porting Existing Apps + +The first step of porting any app is to replace the root `Routes` component with +`FlatRoutes` from `@backstage/core`. As opposed to the `Routes` component, +`FlatRoutes` only considers the first level of `Route` components in its +children, and provides any additional children to the outlet of the route. It +also removes the need to append `"/*"` to paths, as it is added automatically. + +```diff +const AppRoutes = () => ( +- ++ + ... +- } /> ++ } /> + ... +- ++ +); +``` + +The next step should be to switch from using `EntityPageLayout` to +`EntityLayout`, as this can also be done without waiting for plugins to be +ported. You should also replace the top-level `Router` from the catalog plugin +with the separate `CatalogIndexPage` and `CatalogEntityPage` extensions that +have been added to the catalog: + +```diff +-} +-/> ++} /> ++} ++> ++ ++ +``` + +At that point you should flatten out the element tree as much as possible in the +app, removing any intermediate components. At the top level this should usually +be straightforward, but when reaching the catalog entity pages you may need to +wait for some plugins to be migrated. This is because it is no longer possible +to pass in the selected entity through component props, and it should be picked +up from context inside the plugin instead. See the sections below for how to +carry out migrations of some common entity page patterns. + +Once the app element tree doesn't contain any intermediate components, and all +plugin imports have been switched to extensions rather than plain components, +the app has been fully ported. + +### Switching from EntityPageLayout to EntityLayout + +The existing `EntityPageLayout` is replaced by the new `EntityLayout` component, +which has a slightly different pattern for expressing the contents and paths. + +Porting from the old to the new API is just a matter of moving some things +around. For example, given the following existing code: + +```tsx + + } + /> + } + /> + } + /> + +``` + +It would be ported to this: + +```tsx + + + } + + + + } + + + + } + + +``` + +In addition to the renaming, the `element` prop has been moved to `children`. +Also note that the `/*` suffix has been remove from the `"/kubernetes"` path, as +it's now added automatically. + +Usage of the `EntityLayout` component is required to be able to properly +discover routes, and so it is required to apply this change before you can start +using routable entity content extensions from plugins. + +### Porting Entity Pages + +The established pattern in the app is to use custom components in order to +select what plugin components to render for a given entity. The new +`EntitySwitch` component introduced above is what is intended to replace this +pattern, now that the entire app needs to be rendered as a single element tree. +For example, given the following existing code: + +```tsx +export const EntityPage = () => { + const { entity } = useEntity(); + + switch (entity?.kind?.toLowerCase()) { + case 'component': + return ; + case 'api': + return ; + case 'group': + return ; + case 'user': + return ; + default: + return ; + } +}; +``` + +It would be migrated to this: + +```tsx +export const entityPage = ( + + + + + + + +); +``` + +Note that for example `` has been changed to simply +`componentPage`, that is because just like the `EntityPage` component, the +`ComponentEntityPage` also needs to be ported to be an element rather a +component in a similar way. From 72ce0f8b163b4d01a309064cad99f6f5af20e36e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Jan 2021 02:04:57 +0100 Subject: [PATCH 039/144] docs/composability: include in sidebar and add note about purpose --- docs/plugins/composability.md | 11 ++++++----- microsite/sidebars.json | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index 3e38f3376e..e6f00d2374 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -1,6 +1,6 @@ --- id: composability -title: New Composability System +title: Composability System Migration description: Documentation and migration instructions for new composability APIs. --- @@ -8,10 +8,11 @@ description: ## Summary This page describes the new composability system that was recently introduced in -Backstage. It describes the new system from the perspective of the existing -patterns and APIs. As the new system is solidified and existing code is ported, -this page will removed and replaced with a more direct description of the -composability system. +Backstage, and it does so from the perspective of the existing patterns and +APIs. As the new system is solidified and existing code is ported, this page +will removed and replaced with a more direct description of the composability +system. For now, the primary purpose of this documentation is to aid in the +migration of existing plugins, but it does cover the migration of apps as well. The core principle of the new composability system is that plugins should have clear boundaries and connections. It should isolate crashes within a plugin, but diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 3d6417cc65..05e421cb19 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -95,6 +95,7 @@ "plugins/plugin-development", "plugins/structure-of-a-plugin", "plugins/integrating-plugin-into-service-catalog", + "plugins/composability", { "type": "subcategory", "label": "Backends and APIs", From 1f383bbb7c07e5b5e2f7892731bb0c68fc79e749 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 00:24:40 -0500 Subject: [PATCH 040/144] Clarity of error vs problem --- .../src/components/KubernetesContent/ErrorPanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx index 85060fc11b..650e6ddff1 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.tsx @@ -52,8 +52,8 @@ export const ErrorPanel = ({ clustersWithErrors, }: ErrorPanelProps) => ( {clustersWithErrors && (
Errors: {clustersWithErrorsToErrorMessage(clustersWithErrors)}
From bfa6e1ad0a150e08b2da0217e3a98c38ebf00aee Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 00:25:30 -0500 Subject: [PATCH 041/144] Change grid to add spacing for ErrorPanel --- .../KubernetesContent/KubernetesContent.tsx | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx index 293e7a4833..39e73782f1 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/KubernetesContent.tsx @@ -106,51 +106,55 @@ export const KubernetesContent = ({ entity }: KubernetesContentProps) => { return ( - - {kubernetesObjects === undefined && error === undefined && ( - - )} + {kubernetesObjects === undefined && error === undefined && } - {/* errors retrieved from the kubernetes clusters */} - {clustersWithErrors.length > 0 && ( - - )} + {/* errors retrieved from the kubernetes clusters */} + {clustersWithErrors.length > 0 && ( + + + + + + )} - {/* other errors */} - {error !== undefined && ( - - )} + {/* other errors */} + {error !== undefined && ( + + + + + + )} - {kubernetesObjects && ( - <> - - - - - - - - Your Clusters - - - {kubernetesObjects?.items.map((item, i) => ( - - - - ))} - - - )} - + {kubernetesObjects && ( + + + + + + + + + Your Clusters + + + {kubernetesObjects?.items.map((item, i) => ( + + + + ))} + + + )} ); From 3f54eab6051ff93064a1b85431784e66d0ac2249 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 00:26:46 -0500 Subject: [PATCH 042/144] Compress no error display --- .../src/components/ErrorReporting/ErrorReporting.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx b/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx index c986c0158d..8243d48263 100644 --- a/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx +++ b/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx @@ -113,17 +113,17 @@ export const ErrorEmptyState = () => { return ( - + Nice! There are no errors to report! - + EmptyState Date: Thu, 14 Jan 2021 00:27:55 -0500 Subject: [PATCH 043/144] Add changeset --- .changeset/giant-geckos-tickle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/giant-geckos-tickle.md diff --git a/.changeset/giant-geckos-tickle.md b/.changeset/giant-geckos-tickle.md new file mode 100644 index 0000000000..a31ebafa55 --- /dev/null +++ b/.changeset/giant-geckos-tickle.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes': patch +--- + +Minor updates to display of errors From dea1cc3b52f9a2c0935fdcf61bd2066a1eb38e77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Jan 2021 05:48:15 +0000 Subject: [PATCH 044/144] chore(deps): bump @kubernetes/client-node from 0.12.2 to 0.13.2 Bumps [@kubernetes/client-node](https://github.com/kubernetes-client/javascript) from 0.12.2 to 0.13.2. - [Release notes](https://github.com/kubernetes-client/javascript/releases) - [Changelog](https://github.com/kubernetes-client/javascript/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes-client/javascript/compare/0.12.2...0.13.2) Signed-off-by: dependabot[bot] --- plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes/package.json | 2 +- yarn.lock | 59 ++++--------------------- 3 files changed, 11 insertions(+), 52 deletions(-) diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 66a36f8395..23767e01ac 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -34,7 +34,7 @@ "@backstage/backend-common": "^0.4.1", "@backstage/catalog-model": "^0.6.0", "@backstage/config": "^0.1.2", - "@kubernetes/client-node": "^0.12.1", + "@kubernetes/client-node": "^0.13.2", "@types/express": "^4.17.6", "compression": "^1.7.4", "cors": "^2.8.5", diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index f00c846163..72adab0b7b 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -36,7 +36,7 @@ "@backstage/core": "^0.4.3", "@backstage/plugin-kubernetes-backend": "^0.2.3", "@backstage/theme": "^0.2.2", - "@kubernetes/client-node": "^0.12.1", + "@kubernetes/client-node": "^0.13.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", diff --git a/yarn.lock b/yarn.lock index b10819d4f2..88c099a5b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3732,10 +3732,10 @@ resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== -"@kubernetes/client-node@^0.12.1": - version "0.12.2" - resolved "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.12.2.tgz#8728684bd57d1cbcbe14fe742e79be7021403eea" - integrity sha512-J0UwyFl1Iv/IZ6WMP7LaizBEoKPnqwtc8tIO2q/X+EuDT7eGpPPAMHXSEOC/EI9JGIf0FaJEcDHhB/Dio/mKhw== +"@kubernetes/client-node@^0.13.2": + version "0.13.2" + resolved "https://registry.npmjs.org/@kubernetes/client-node/-/client-node-0.13.2.tgz#881eb407afbd5b499e5daf898ffcc40f839709e7" + integrity sha512-ufvGfjBXuy5LbZTJZ8bd1eRVgWUd9rRDCryMWNfxb4372Q60R1oG8qy7svUB9NqBmwFgKHuaXkrfq3rFFbGrew== dependencies: "@types/js-yaml" "^3.12.1" "@types/node" "^10.12.0" @@ -14257,23 +14257,6 @@ got@^11.5.2: p-cancelable "^2.0.0" responselike "^2.0.0" -got@^11.6.2: - version "11.7.0" - resolved "https://registry.npmjs.org/got/-/got-11.7.0.tgz#a386360305571a74548872e674932b4ef70d3b24" - integrity sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg== - dependencies: - "@sindresorhus/is" "^3.1.1" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.1" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - got@^11.7.0, got@^11.8.0: version "11.8.0" resolved "https://registry.npmjs.org/got/-/got-11.8.0.tgz#be0920c3586b07fd94add3b5b27cb28f49e6545f" @@ -19355,21 +19338,7 @@ opencollective-postinstall@^2.0.2: resolved "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== -openid-client@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/openid-client/-/openid-client-4.1.1.tgz#3e8a25584c4292e9b9b03e60358f5549fb85197a" - integrity sha512-/qch3I3v8UtO0A7wVgyXJJjGX/knR8bv06DQpLuKQqLG5u4AHcgusGuVKPKAcneLZvHKbKovF2+3e2ngXyuudA== - dependencies: - base64url "^3.0.1" - got "^11.6.2" - jose "^2.0.2" - lru-cache "^6.0.0" - make-error "^1.3.6" - object-hash "^2.0.1" - oidc-token-hash "^5.0.0" - p-any "^3.0.0" - -openid-client@^4.2.1: +openid-client@^4.1.1, openid-client@^4.2.1: version "4.2.1" resolved "https://registry.npmjs.org/openid-client/-/openid-client-4.2.1.tgz#8200c0ab6a3b8e954727dfa790847dc5cb8999c2" integrity sha512-07eOcJeMH3ZHNvx5DVMZQmy3vZSTQqKSSunbtM1pXb+k5LBPi5hMum1vJCFReXlo4wuLEqZ/OgbsZvXPhbGRtA== @@ -24854,22 +24823,17 @@ tslib@2.0.0: resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== -tslib@2.0.1, tslib@^2.0.0, tslib@~2.0.0, tslib@~2.0.1: +tslib@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== -tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: - version "1.13.0" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== - -tslib@^1.8.0: +tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.1: +tslib@^2.0.0, tslib@^2.0.1, tslib@~2.0.0, tslib@~2.0.1: version "2.0.3" resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== @@ -26112,12 +26076,7 @@ ws@^6.0.0, ws@^6.1.2, ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.2.3: - version "7.3.0" - resolved "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" - integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w== - -ws@^7.3.1: +ws@^7.2.3, ws@^7.3.1: version "7.3.1" resolved "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== From 3e4da2518bd035d526cd02ffc0c4a1a9fa8c3288 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 00:58:23 -0500 Subject: [PATCH 045/144] Align tests --- .../src/components/KubernetesContent/ErrorPanel.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.test.tsx b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.test.tsx index 6fa6b3114c..8dff742502 100644 --- a/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.test.tsx +++ b/plugins/kubernetes/src/components/KubernetesContent/ErrorPanel.test.tsx @@ -33,7 +33,7 @@ describe('ErrorPanel', () => { // title expect( getByText( - 'There was an error retrieving some Kubernetes resources for the entity: THIS_ENTITY', + 'There was a problem retrieving some Kubernetes resources for the entity: THIS_ENTITY. This could mean that the Error Reporting card is not completely accurate.', ), ).toBeInTheDocument(); @@ -67,7 +67,7 @@ describe('ErrorPanel', () => { // title expect( getByText( - 'There was an error retrieving some Kubernetes resources for the entity: THIS_ENTITY', + 'There was a problem retrieving some Kubernetes resources for the entity: THIS_ENTITY. This could mean that the Error Reporting card is not completely accurate.', ), ).toBeInTheDocument(); From a1f587c8614adc9d3b81089b28e60595875c7f9e Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 01:01:37 -0500 Subject: [PATCH 046/144] Support HTTP 400 Bad Request --- plugins/kubernetes-backend/src/service/KubernetesFetcher.ts | 2 ++ plugins/kubernetes-backend/src/types/types.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts index d9c6ee6d1a..991174e78a 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.ts @@ -74,6 +74,8 @@ function fetchResultsToResponseWrapper( const statusCodeToErrorType = (statusCode: number): KubernetesErrorTypes => { switch (statusCode) { + case 400: + return 'BAD_REQUEST'; case 401: return 'UNAUTHORIZED_ERROR'; case 500: diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 9f0b90c032..cf6576153b 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -135,6 +135,7 @@ export interface KubernetesClustersSupplier { } export type KubernetesErrorTypes = + | 'BAD_REQUEST' | 'UNAUTHORIZED_ERROR' | 'SYSTEM_ERROR' | 'UNKNOWN_ERROR'; From a6f9dca0dc8701382304b7fe2f8f84d804028192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 14 Jan 2021 08:03:51 +0100 Subject: [PATCH 047/144] plugins should not depend on core-api --- .changeset/olive-dodos-hammer.md | 7 +++++++ .../quickstart-app-plugin/ExampleComponent.md | 2 +- .../ExampleFetchComponent.md | 2 +- docs/tutorials/quickstart-app-plugin.md | 5 ++--- plugins/github-actions/package.json | 1 - .../Cards/RecentWorkflowRunsCard.test.tsx | 14 +++++++------- .../components/Cards/RecentWorkflowRunsCard.tsx | 15 ++++++++++----- plugins/lighthouse/package.json | 1 - .../src/hooks/useWebsiteForEntity.test.tsx | 2 +- .../lighthouse/src/hooks/useWebsiteForEntity.ts | 2 +- plugins/techdocs/package.json | 1 - .../src/reader/components/TechDocsHome.test.tsx | 2 +- .../src/reader/components/TechDocsPage.test.tsx | 2 +- 13 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 .changeset/olive-dodos-hammer.md diff --git a/.changeset/olive-dodos-hammer.md b/.changeset/olive-dodos-hammer.md new file mode 100644 index 0000000000..4901d10cbb --- /dev/null +++ b/.changeset/olive-dodos-hammer.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-github-actions': patch +'@backstage/plugin-lighthouse': patch +'@backstage/plugin-techdocs': patch +--- + +Remove dependency on `@backstage/core-api`. No plugin should ever depend on that package; it's an internal concern whose important bits are re-exported by `@backstage/core` which is the public facing dependency to use. diff --git a/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md b/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md index 9b5d77bc7c..77b820d921 100644 --- a/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md +++ b/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md @@ -14,8 +14,8 @@ import { HeaderLabel, SupportButton, identityApiRef, + useApi, } from '@backstage/core'; -import { useApi } from '@backstage/core-api'; import ExampleFetchComponent from '../ExampleFetchComponent'; const ExampleComponent = () => { diff --git a/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md b/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md index 6061b69e93..6992d05866 100644 --- a/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md +++ b/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md @@ -11,8 +11,8 @@ import { TableColumn, Progress, githubAuthApiRef, + useApi, } from '@backstage/core'; -import { useApi } from '@backstage/core-api'; import { graphql } from '@octokit/graphql'; const query = `{ diff --git a/docs/tutorials/quickstart-app-plugin.md b/docs/tutorials/quickstart-app-plugin.md index 045062fdcb..6208fbe30d 100644 --- a/docs/tutorials/quickstart-app-plugin.md +++ b/docs/tutorials/quickstart-app-plugin.md @@ -72,8 +72,7 @@ Our first modification will be to extract information from the Identity API. ```tsx // Add identityApiRef to the list of imported from core -import { identityApiRef } from '@backstage/core'; -import { useApi } from '@backstage/core-api'; +import { identityApiRef, useApi } from '@backstage/core'; ``` 3. Adjust the ExampleComponent from inline to block @@ -143,8 +142,8 @@ import { TableColumn, Progress, githubAuthApiRef, + useApi, } from '@backstage/core'; -import { useApi } from '@backstage/core-api'; import { graphql } from '@octokit/graphql'; const ExampleFetchComponent = () => { diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index f876eb9e7c..771cbcfdd5 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -34,7 +34,6 @@ "dependencies": { "@backstage/catalog-model": "^0.6.0", "@backstage/core": "^0.4.3", - "@backstage/core-api": "^0.2.7", "@backstage/plugin-catalog": "^0.2.8", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", diff --git a/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.test.tsx b/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.test.tsx index 1d6001e038..0fd77c0443 100644 --- a/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.test.tsx +++ b/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.test.tsx @@ -14,15 +14,15 @@ * limitations under the License. */ -import type { Props as RecentWorkflowRunsCardProps } from './RecentWorkflowRunsCard'; -import React from 'react'; -import { render } from '@testing-library/react'; -import { RecentWorkflowRunsCard } from './RecentWorkflowRunsCard'; -import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core-api'; -import { useWorkflowRuns } from '../useWorkflowRuns'; -import { ThemeProvider } from '@material-ui/core'; +import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core'; import { lightTheme } from '@backstage/theme'; +import { ThemeProvider } from '@material-ui/core'; +import { render } from '@testing-library/react'; +import React from 'react'; import { MemoryRouter } from 'react-router'; +import { useWorkflowRuns } from '../useWorkflowRuns'; +import type { Props as RecentWorkflowRunsCardProps } from './RecentWorkflowRunsCard'; +import { RecentWorkflowRunsCard } from './RecentWorkflowRunsCard'; jest.mock('../useWorkflowRuns', () => ({ useWorkflowRuns: jest.fn(), diff --git a/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx b/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx index 70348ece0a..9bb52b9ab1 100644 --- a/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx +++ b/plugins/github-actions/src/components/Cards/RecentWorkflowRunsCard.tsx @@ -14,14 +14,19 @@ * limitations under the License. */ import { Entity } from '@backstage/catalog-model'; -import { errorApiRef, useApi } from '@backstage/core-api'; +import { + EmptyState, + errorApiRef, + InfoCard, + Table, + useApi, +} from '@backstage/core'; +import { Button, Link } from '@material-ui/core'; +import React, { useEffect } from 'react'; +import { generatePath, Link as RouterLink } from 'react-router-dom'; import { GITHUB_ACTIONS_ANNOTATION } from '../useProjectName'; import { useWorkflowRuns } from '../useWorkflowRuns'; -import React, { useEffect } from 'react'; -import { EmptyState, InfoCard, Table } from '@backstage/core'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; -import { Button, Link } from '@material-ui/core'; -import { generatePath, Link as RouterLink } from 'react-router-dom'; const firstLine = (message: string): string => message.split('\n')[0]; diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index f688f9c1db..569277054e 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -34,7 +34,6 @@ "@backstage/catalog-model": "^0.6.0", "@backstage/config": "^0.1.2", "@backstage/core": "^0.4.3", - "@backstage/core-api": "^0.2.6", "@backstage/plugin-catalog": "^0.2.7", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", diff --git a/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx index 7f5925abf8..c1773921cf 100644 --- a/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx +++ b/plugins/lighthouse/src/hooks/useWebsiteForEntity.test.tsx @@ -15,7 +15,7 @@ */ import React, { PropsWithChildren } from 'react'; import { renderHook } from '@testing-library/react-hooks'; -import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core-api'; +import { ApiProvider, ApiRegistry, errorApiRef } from '@backstage/core'; import { lighthouseApiRef, WebsiteListResponse } from '../api'; import { useWebsiteForEntity } from './useWebsiteForEntity'; import { EntityContext } from '@backstage/plugin-catalog'; diff --git a/plugins/lighthouse/src/hooks/useWebsiteForEntity.ts b/plugins/lighthouse/src/hooks/useWebsiteForEntity.ts index c52e38f473..08d9925b7a 100644 --- a/plugins/lighthouse/src/hooks/useWebsiteForEntity.ts +++ b/plugins/lighthouse/src/hooks/useWebsiteForEntity.ts @@ -15,7 +15,7 @@ */ import { useEntity } from '@backstage/plugin-catalog'; import { LIGHTHOUSE_WEBSITE_URL_ANNOTATION } from '../../constants'; -import { errorApiRef, useApi } from '@backstage/core-api'; +import { errorApiRef, useApi } from '@backstage/core'; import { lighthouseApiRef } from '../api'; import { useAsync } from 'react-use'; diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 955b0f798c..db26ee122d 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -33,7 +33,6 @@ "dependencies": { "@backstage/catalog-model": "^0.6.0", "@backstage/core": "^0.4.3", - "@backstage/core-api": "^0.2.8", "@backstage/plugin-catalog": "^0.2.9", "@backstage/test-utils": "^0.1.6", "@backstage/theme": "^0.2.2", diff --git a/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx b/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx index 0924b90fc1..4979f027a6 100644 --- a/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsHome.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ApiProvider, ApiRegistry } from '@backstage/core-api'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog'; import { wrapInTestApp } from '@backstage/test-utils'; import { render } from '@testing-library/react'; diff --git a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx index c05c0488ff..bc84f792a7 100644 --- a/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsPage.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { TechDocsPage } from './TechDocsPage'; import { render, act } from '@testing-library/react'; import { wrapInTestApp } from '@backstage/test-utils'; -import { ApiRegistry, ApiProvider } from '@backstage/core-api'; +import { ApiRegistry, ApiProvider } from '@backstage/core'; import { techdocsApiRef, TechDocsApi, From 0c6e3b21a78be733cdec0b74d18a4c8838fc2747 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 12 Jan 2021 17:42:53 +0100 Subject: [PATCH 048/144] Add example data for system, domains and resources --- app-config.yaml | 20 ++++++++++++++++++- .../catalog-model/examples/all-domains.yaml | 9 +++++++++ .../catalog-model/examples/all-resources.yaml | 8 ++++++++ .../catalog-model/examples/all-systems.yaml | 10 ++++++++++ .../components/artist-lookup-component.yaml | 1 + .../components/playback-lib-component.yaml | 1 + .../components/playback-order-component.yaml | 1 + .../components/podcast-api-component.yaml | 1 + .../components/queue-proxy-component.yaml | 1 + .../components/shuffle-api-component.yaml | 1 + .../components/www-artist-component.yaml | 1 + .../examples/domains/artists-domain.yaml | 7 +++++++ .../examples/domains/playback-domain.yaml | 7 +++++++ .../resources/artists-db-resource.yaml | 9 +++++++++ .../artist-engagement-portal-system.yaml | 10 ++++++++++ .../systems/audio-playback-system.yaml | 8 ++++++++ .../examples/systems/podcast-system.yaml | 8 ++++++++ packages/catalog-model/src/kinds/relations.ts | 5 ----- 18 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 packages/catalog-model/examples/all-domains.yaml create mode 100644 packages/catalog-model/examples/all-resources.yaml create mode 100644 packages/catalog-model/examples/all-systems.yaml create mode 100644 packages/catalog-model/examples/domains/artists-domain.yaml create mode 100644 packages/catalog-model/examples/domains/playback-domain.yaml create mode 100644 packages/catalog-model/examples/resources/artists-db-resource.yaml create mode 100644 packages/catalog-model/examples/systems/artist-engagement-portal-system.yaml create mode 100644 packages/catalog-model/examples/systems/audio-playback-system.yaml create mode 100644 packages/catalog-model/examples/systems/podcast-system.yaml diff --git a/app-config.yaml b/app-config.yaml index b67e9525bf..93c49a5319 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -127,7 +127,16 @@ integrations: catalog: rules: - - allow: [Component, API, Resource, Group, User, Template, System, Domain, Location] + - allow: + - Component + - API + - Resource + - Group + - User + - Template + - System + - Domain + - Location processors: githubOrg: @@ -184,6 +193,15 @@ catalog: # Backstage example APIs - type: url target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml + # Backstage example resources + - type: url + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-resources.yaml + # Backstage example systems + - type: url + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-systems.yaml + # Backstage example domains + - type: url + target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-domains.yaml # Backstage example templates - type: url target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/all-templates.yaml diff --git a/packages/catalog-model/examples/all-domains.yaml b/packages/catalog-model/examples/all-domains.yaml new file mode 100644 index 0000000000..91a8a5b76d --- /dev/null +++ b/packages/catalog-model/examples/all-domains.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: example-domains + description: A collection of all Backstage example domains +spec: + targets: + - ./domains/artists-domain.yaml + - ./domains/playback-domain.yaml diff --git a/packages/catalog-model/examples/all-resources.yaml b/packages/catalog-model/examples/all-resources.yaml new file mode 100644 index 0000000000..d0986e3fe2 --- /dev/null +++ b/packages/catalog-model/examples/all-resources.yaml @@ -0,0 +1,8 @@ +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: example-resources + description: A collection of all Backstage example resources +spec: + targets: + - ./resources/artists-db-resource.yaml diff --git a/packages/catalog-model/examples/all-systems.yaml b/packages/catalog-model/examples/all-systems.yaml new file mode 100644 index 0000000000..165bee54e5 --- /dev/null +++ b/packages/catalog-model/examples/all-systems.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Location +metadata: + name: example-systems + description: A collection of all Backstage example systems +spec: + targets: + - ./systems/artist-engagement-portal-system.yaml + - ./systems/audio-playback-system.yaml + - ./systems/podcast-system.yaml diff --git a/packages/catalog-model/examples/components/artist-lookup-component.yaml b/packages/catalog-model/examples/components/artist-lookup-component.yaml index 257344be3d..3fc516ece9 100644 --- a/packages/catalog-model/examples/components/artist-lookup-component.yaml +++ b/packages/catalog-model/examples/components/artist-lookup-component.yaml @@ -10,3 +10,4 @@ spec: type: service lifecycle: experimental owner: team-a + system: artist-engagement-portal diff --git a/packages/catalog-model/examples/components/playback-lib-component.yaml b/packages/catalog-model/examples/components/playback-lib-component.yaml index f7d7670b5d..de7e93d38d 100644 --- a/packages/catalog-model/examples/components/playback-lib-component.yaml +++ b/packages/catalog-model/examples/components/playback-lib-component.yaml @@ -7,3 +7,4 @@ spec: type: library lifecycle: experimental owner: team-c + system: audio-playback diff --git a/packages/catalog-model/examples/components/playback-order-component.yaml b/packages/catalog-model/examples/components/playback-order-component.yaml index c4f41b2b58..9146063886 100644 --- a/packages/catalog-model/examples/components/playback-order-component.yaml +++ b/packages/catalog-model/examples/components/playback-order-component.yaml @@ -10,3 +10,4 @@ spec: type: service lifecycle: production owner: user:guest + system: audio-playback diff --git a/packages/catalog-model/examples/components/podcast-api-component.yaml b/packages/catalog-model/examples/components/podcast-api-component.yaml index b89ff48c48..30d254a00f 100644 --- a/packages/catalog-model/examples/components/podcast-api-component.yaml +++ b/packages/catalog-model/examples/components/podcast-api-component.yaml @@ -9,3 +9,4 @@ spec: type: service lifecycle: experimental owner: team-b + system: podcast diff --git a/packages/catalog-model/examples/components/queue-proxy-component.yaml b/packages/catalog-model/examples/components/queue-proxy-component.yaml index 7f7fcbd527..a2d5ae5ea4 100644 --- a/packages/catalog-model/examples/components/queue-proxy-component.yaml +++ b/packages/catalog-model/examples/components/queue-proxy-component.yaml @@ -10,3 +10,4 @@ spec: type: website lifecycle: production owner: team-b + system: podcast diff --git a/packages/catalog-model/examples/components/shuffle-api-component.yaml b/packages/catalog-model/examples/components/shuffle-api-component.yaml index 1c2da03511..6328ebdf3b 100644 --- a/packages/catalog-model/examples/components/shuffle-api-component.yaml +++ b/packages/catalog-model/examples/components/shuffle-api-component.yaml @@ -9,3 +9,4 @@ spec: type: service lifecycle: production owner: user:guest + system: audio-playback diff --git a/packages/catalog-model/examples/components/www-artist-component.yaml b/packages/catalog-model/examples/components/www-artist-component.yaml index c333eb8c09..3acb6fc6a6 100644 --- a/packages/catalog-model/examples/components/www-artist-component.yaml +++ b/packages/catalog-model/examples/components/www-artist-component.yaml @@ -7,3 +7,4 @@ spec: type: website lifecycle: production owner: team-a + system: artist-engagement-portal diff --git a/packages/catalog-model/examples/domains/artists-domain.yaml b/packages/catalog-model/examples/domains/artists-domain.yaml new file mode 100644 index 0000000000..7bcc4329dd --- /dev/null +++ b/packages/catalog-model/examples/domains/artists-domain.yaml @@ -0,0 +1,7 @@ +apiVersion: backstage.io/v1alpha1 +kind: Domain +metadata: + name: artists + description: Everything related to artists +spec: + owner: team-a diff --git a/packages/catalog-model/examples/domains/playback-domain.yaml b/packages/catalog-model/examples/domains/playback-domain.yaml new file mode 100644 index 0000000000..c9933ebf5e --- /dev/null +++ b/packages/catalog-model/examples/domains/playback-domain.yaml @@ -0,0 +1,7 @@ +apiVersion: backstage.io/v1alpha1 +kind: Domain +metadata: + name: playback + description: Everything related to audio playback +spec: + owner: user:frank.tiernan diff --git a/packages/catalog-model/examples/resources/artists-db-resource.yaml b/packages/catalog-model/examples/resources/artists-db-resource.yaml new file mode 100644 index 0000000000..a666e9b3fd --- /dev/null +++ b/packages/catalog-model/examples/resources/artists-db-resource.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Resource +metadata: + name: artists-db + description: Stores artist details +spec: + type: database + owner: team-a + system: artist-engagement-portal diff --git a/packages/catalog-model/examples/systems/artist-engagement-portal-system.yaml b/packages/catalog-model/examples/systems/artist-engagement-portal-system.yaml new file mode 100644 index 0000000000..8de3c00880 --- /dev/null +++ b/packages/catalog-model/examples/systems/artist-engagement-portal-system.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: System +metadata: + name: artist-engagement-portal + description: Everything related to artists + tags: + - portal +spec: + owner: team-a + domain: artists diff --git a/packages/catalog-model/examples/systems/audio-playback-system.yaml b/packages/catalog-model/examples/systems/audio-playback-system.yaml new file mode 100644 index 0000000000..7430ae2ff5 --- /dev/null +++ b/packages/catalog-model/examples/systems/audio-playback-system.yaml @@ -0,0 +1,8 @@ +apiVersion: backstage.io/v1alpha1 +kind: System +metadata: + name: audio-playback + description: Audio playback system +spec: + owner: team-c + domain: playback diff --git a/packages/catalog-model/examples/systems/podcast-system.yaml b/packages/catalog-model/examples/systems/podcast-system.yaml new file mode 100644 index 0000000000..47a2f7ac9f --- /dev/null +++ b/packages/catalog-model/examples/systems/podcast-system.yaml @@ -0,0 +1,8 @@ +apiVersion: backstage.io/v1alpha1 +kind: System +metadata: + name: podcast + description: Podcast playback +spec: + owner: team-b + domain: playback diff --git a/packages/catalog-model/src/kinds/relations.ts b/packages/catalog-model/src/kinds/relations.ts index ed40a7e9c6..8ad5017fba 100644 --- a/packages/catalog-model/src/kinds/relations.ts +++ b/packages/catalog-model/src/kinds/relations.ts @@ -57,13 +57,8 @@ export const RELATION_MEMBER_OF = 'memberOf'; export const RELATION_HAS_MEMBER = 'hasMember'; /** -<<<<<<< HEAD * A part/whole relation, typically for components in a system and systems * in a domain. -======= - * A grouping relation, typically for components, resources or APIs in a - * system, or for systems inside a domain. ->>>>>>> Add system, domain and resource entity kinds */ export const RELATION_PART_OF = 'partOf'; export const RELATION_HAS_PART = 'hasPart'; From b2fc74ea168004dde06f76a144c01fd26b7122a9 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 13 Jan 2021 23:07:59 +0100 Subject: [PATCH 049/144] docs: Write a HOW TO guide for using URL Reader --- docs/features/techdocs/how-to-guides.md | 54 +++++++++++++++++++++++++ microsite/sidebars.json | 1 + mkdocs.yml | 1 + 3 files changed, 56 insertions(+) create mode 100644 docs/features/techdocs/how-to-guides.md diff --git a/docs/features/techdocs/how-to-guides.md b/docs/features/techdocs/how-to-guides.md new file mode 100644 index 0000000000..b32ff40589 --- /dev/null +++ b/docs/features/techdocs/how-to-guides.md @@ -0,0 +1,54 @@ +--- +id: how-to-guides +title: TechDocs "HOW TO" guides +sidebar_label: "HOW TO" guides +description: TechDocs "HOW TO" guides related to TechDocs +--- + +## How to use URL Reader in TechDocs Prepare step? + +If TechDocs is configured to generate docs, it will first download the +repository associated with the `backstage.io/techdocs-ref` annotation defined in +the Entity's `catalog-info.yaml` file. This is also called the +[Prepare](./concepts.md#techdocs-preparer) step. + +There are two kinds of preparers or two ways of downloading these source files + +- Preparer 1: Doing a `git clone` of the repository (also known as Common Git + Preparer) +- Preparer 2: Downloading an archive.zip or equivalent of the repository (also + known as URL Reader) + +If `backstage.io/techdocs-ref` is equal to any of these - + +1. `github:https://githubhost.com/org/repo` +2. `gitlab:https://gitlabhost.com/org/repo` +3. `bitbucket:https://bitbuckethost.com/project/repo` +4. `azure/api:https://azurehost.com/org/project` + +Then Common Git Preparer will be used i.e. a `git clone`. But the URL Reader is +a much faster way to do this step. Convert the `backstage.io/techdocs-ref` +values to the following - + +1. `url:https://githubhost.com/org/repo/tree/` +2. `url:https://gitlabhost.com/org/repo/tree/` +3. `url:https://bitbuckethost.com/project/repo/src/` +4. `url:https://azurehost.com/organization/project/_git/repository` + +Note that you can also provide a path to a non-root directory inside the +repository which contains the `docs/` directory. + +e.g. +`url:https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend/examples/documented-component` + +### Why is URL Reader faster than a git clone? + +URL Reader uses the source code hosting provider to download a zip or tarball of +the repository. The archive does not have any git history attached to it. Also +it is a compressed file. Hence the file size is significantly smaller than how +much data git clone has to transfer. + +Caveat: Currently TechDocs sites built using URL Reader will be cached for 30 +minutes which means they will not be re-built if new changes are made within 30 +minutes. This cache invalidation will be replaced by commit timestamp based +implementation very soon. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 3d6417cc65..0dc4628c1a 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -83,6 +83,7 @@ "features/techdocs/creating-and-publishing", "features/techdocs/configuration", "features/techdocs/using-cloud-storage", + "features/techdocs/how-to-guides", "features/techdocs/troubleshooting", "features/techdocs/faqs" ] diff --git a/mkdocs.yml b/mkdocs.yml index 29198963e3..c7ca02432b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,6 +56,7 @@ nav: - Creating and Publishing Documentation: 'features/techdocs/creating-and-publishing.md' - Configuration: 'features/techdocs/configuration.md' - Using Cloud Storage: 'features/techdocs/using-cloud-storage.md' + - HOW TO guides: 'features/techdocs/how-to-guides.md' - Troubleshooting: 'features/techdocs/troubleshooting.md' - FAQ: 'features/techdocs/FAQ.md' - Kubernetes: From 22d2a523475b6e817cd221fc508171afa15a008c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Jan 2021 12:25:58 +0100 Subject: [PATCH 050/144] Apply suggestions from code review Co-authored-by: Himanshu Mishra --- docs/plugins/composability.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index e6f00d2374..5f1be7a561 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -1,8 +1,8 @@ --- id: composability title: Composability System Migration -description: - Documentation and migration instructions for new composability APIs. +# prettier-ignore +description: Documentation and migration instructions for new composability APIs. --- ## Summary @@ -59,7 +59,7 @@ inspected, while our component data adds more structured access and simplifies evolution by allowing for multiple different versions of a piece of data to be used and interpreted at once. -The initial use-cases for component data is support route and plugin discovery +The initial use-case for component data is to support route and plugin discovery through elements in the app. Through this we allow for the React element tree in the app to be the source of truth, both for which plugins are used, as well as all top-level plugin routes in the app. The use of component data is not limited @@ -254,8 +254,8 @@ const headerLinkRouteRef = createExternalRouteRef(); ### Binding External Routes in the App The association of external routes is controlled by the app. Each -`ExternalRouteRef` of a plugin should be<- bound to an actual `RouteRef`, -usually from another plugin. The binding process happens once att app startup, +`ExternalRouteRef` of a plugin should be bound to an actual `RouteRef`, +usually from another plugin. The binding process happens once at app startup, and is then used through the lifetime of the app to help resolve concrete route paths. @@ -517,15 +517,15 @@ It would be ported to this: ```tsx - } + - } + - } + ``` From 151e36504020a747a1e3072781cf2237888c7e60 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Jan 2021 12:53:25 +0100 Subject: [PATCH 051/144] docs/composability: ran prettier + added to mkdocs.yml --- docs/plugins/composability.md | 7 +++---- mkdocs.yml | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index 5f1be7a561..c23ab2882d 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -254,10 +254,9 @@ const headerLinkRouteRef = createExternalRouteRef(); ### Binding External Routes in the App The association of external routes is controlled by the app. Each -`ExternalRouteRef` of a plugin should be bound to an actual `RouteRef`, -usually from another plugin. The binding process happens once at app startup, -and is then used through the lifetime of the app to help resolve concrete route -paths. +`ExternalRouteRef` of a plugin should be bound to an actual `RouteRef`, usually +from another plugin. The binding process happens once at app startup, and is +then used through the lifetime of the app to help resolve concrete route paths. Using the above example of the `BarPage` linking to the `FooPage`, we might do something like this in the app: diff --git a/mkdocs.yml b/mkdocs.yml index 29198963e3..105ad721d5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -66,6 +66,7 @@ nav: - Creating a new plugin: 'plugins/create-a-plugin.md' - Developing a plugin: 'plugins/plugin-development.md' - Structure of a plugin: 'plugins/structure-of-a-plugin.md' + - Composability System Migration: 'plugins/composability.md' - Backends and APIs: - Proxying: 'plugins/proxying.md' - Backstage backend plugin: 'plugins/backend-plugin.md' From e8a0506344d2d8966f7d459d58126c986e0be984 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 13 Jan 2021 11:46:02 +0100 Subject: [PATCH 052/144] scripts/verify-links: refactor to not use any dependencies --- scripts/verify-links.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/scripts/verify-links.js b/scripts/verify-links.js index 75e7a8c1d0..5ad14ead4d 100755 --- a/scripts/verify-links.js +++ b/scripts/verify-links.js @@ -18,8 +18,27 @@ /* eslint-disable import/no-extraneous-dependencies */ const { resolve: resolvePath, join: joinPath, dirname } = require('path'); -const fs = require('fs-extra'); -const recursive = require('recursive-readdir'); +const fs = require('fs').promises; +const { existsSync } = require('fs'); + +const IGNORED_DIRS = ['node_modules', 'dist', 'bin', '.git']; + +async function listFiles(dir) { + const files = await fs.readdir(dir); + const paths = await Promise.all( + files + .filter(file => !IGNORED_DIRS.includes(file)) + .map(async file => { + const path = joinPath(dir, file); + + if ((await fs.stat(path)).isDirectory()) { + return listFiles(path); + } + return path; + }), + ); + return paths.flat(); +} const projectRoot = resolvePath(__dirname, '..'); @@ -65,7 +84,7 @@ async function verifyUrl(basePath, absUrl, docPages) { } const staticPath = resolvePath(projectRoot, 'microsite/static', `.${url}`); - if (await fs.pathExists(staticPath)) { + if (existsSync(staticPath)) { return undefined; } @@ -82,8 +101,7 @@ async function verifyUrl(basePath, absUrl, docPages) { return { url, basePath, problem: 'out-of-docs' }; } - const exists = await fs.pathExists(path); - if (!exists) { + if (!existsSync(path)) { return { url, basePath, problem: 'missing' }; } @@ -110,7 +128,7 @@ async function verifyFile(filePath, docPages) { // It is used to validate microsite links from outside /docs/, as those // are not transformed from the markdown file representation by docusaurus. async function findExternalDocsLinks(dir) { - const allFiles = await recursive(dir); + const allFiles = await listFiles(dir); const mdFiles = allFiles.filter(p => p.endsWith('.md')); const paths = new Map(); @@ -138,14 +156,15 @@ async function findExternalDocsLinks(dir) { async function main() { process.chdir(projectRoot); - const files = await recursive('.', ['node_modules', 'dist', 'bin']); + const files = await listFiles('.'); const mdFiles = files.filter(f => f.endsWith('.md')); const badUrls = []; const docPages = await findExternalDocsLinks('docs'); + const docPageSet = new Set(docPages.values()); for (const mdFile of mdFiles) { - const badFileUrls = await verifyFile(mdFile, new Set(docPages.values())); + const badFileUrls = await verifyFile(mdFile, docPageSet); badUrls.push(...badFileUrls); } From 9595e2e166553e72eb3b1714b16e55b4777c96c2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 13 Jan 2021 11:47:51 +0100 Subject: [PATCH 053/144] workflows: run link verification as part of microsite CI --- .github/workflows/microsite-build-check.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/microsite-build-check.yml b/.github/workflows/microsite-build-check.yml index 118ba942bb..45182229c9 100644 --- a/.github/workflows/microsite-build-check.yml +++ b/.github/workflows/microsite-build-check.yml @@ -27,6 +27,9 @@ jobs: with: node-version: ${{ matrix.node-version }} + - name: verify doc links + run: node scripts/verify-links.js + # Skip caching of microsite dependencies, it keeps the global cache size # smaller, which make Windows builds a lot faster for the rest of the project. - name: yarn install From c09215095b1fdc29f4a9919b93f33286c26ef574 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 14 Jan 2021 13:18:56 +0100 Subject: [PATCH 054/144] chore: remove the changeset as we will do it in this release --- .changeset/real-vans-provide.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/real-vans-provide.md diff --git a/.changeset/real-vans-provide.md b/.changeset/real-vans-provide.md deleted file mode 100644 index 9d83804bd6..0000000000 --- a/.changeset/real-vans-provide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Remove `apiUrl` from the output of the create-github-app because apiUrl already exist in the GitHub integration config. From 42a1591b6597113b62be9285136626157b58f203 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 7 Jan 2021 17:19:23 +0100 Subject: [PATCH 055/144] WIP: Github App manager --- packages/integration/src/github/githubApps.ts | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 packages/integration/src/github/githubApps.ts diff --git a/packages/integration/src/github/githubApps.ts b/packages/integration/src/github/githubApps.ts new file mode 100644 index 0000000000..bd2d6853a8 --- /dev/null +++ b/packages/integration/src/github/githubApps.ts @@ -0,0 +1,245 @@ +/* + * 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 { GithubAppConfig, GitHubIntegrationConfig } from './config'; +import { createAppAuth } from '@octokit/auth-app'; +import { Octokit } from '@octokit/rest'; +import gitUrlParse from 'git-url-parse'; + +type InstallationData = { + installationId: number; + suspended: boolean; + repositorySelection: 'selected' | 'all'; +}; + +type InstallationRepoData = { + etag: string; + repos: Set; +}; + +// for each app +class GithubAppManager { + private readonly appClient: Octokit; + private readonly baseAuthConfig: { appId: number; privateKey: string }; + private readonly installationDatas = new Map(); + private readonly installationRepoDatas = new Map< + number, + InstallationRepoData + >(); + + private installationsEtag?: string; + + constructor(config: GithubAppConfig) { + this.baseAuthConfig = { + appId: config.appId, + privateKey: config.privateKey, + }; + this.appClient = new Octokit({ + authStrategy: createAppAuth, + auth: this.baseAuthConfig, + }); + } + + getInstallationClient(installationId: number): Octokit { + return new Octokit({ + authStrategy: createAppAuth, + auth: { + ...this.baseAuthConfig, + installationId, + }, + }); + } + + async getInstallationCredentials( + owner: string, + repo: string, + ): Promise<{ accessToken: string }> { + const { + installationId, + suspended, + repositorySelection, + } = await this.getInstallationData(owner); + if (suspended) { + throw new Error(`The app for ${owner}/${repo} is suspended`); + } + + const auth = createAppAuth({ ...this.baseAuthConfig, installationId }); + + const { token } = await auth({ type: 'installation' }); + + if (repositorySelection === 'all') { + return { accessToken: token }; + } + + // const octokit = new Octokit({ auth: token }); + const res = await this.getInstallationClient( + installationId, + ).apps.createInstallationAccessToken({ + installation_id: installationId, + repositories: [repo], + }); + // token: 'v1.186839cac1afb9236d3b41dffee02ffbcf17861b', + // expires_at: '2021-01-07T17:12:50Z', + // permissions: { contents: 'read', metadata: 'read' }, + // repository_selection: 'selected', + // repositories: [ [Object] ] + return { accessToken: res.data.token }; + + // const hasRepo = await this.installationHasRepo(installationId, repo, token); + // if (!hasRepo) { + // const error = new Error( + // `No app installation found for ${owner}/${repo} in ${this.baseAuthConfig.appId}`, + // ); + // error.name = 'NotFoundError'; + // throw error; + // } + + // return { accessToken: token }; + } + + private async installationHasRepo(id: number, repo: string, token: string) { + const octokit = new Octokit({ auth: token }); + + const installationRepoData = this.installationRepoDatas.get(id); + + let repos: Set; + try { + const res = await octokit.apps.listReposAccessibleToInstallation({ + headers: { + 'If-None-Match': installationRepoData?.etag, + }, + }); + repos = new Set(res.data.repositories.map(repo => repo.name)); + this.installationRepoDatas.set(id, { + etag: res.headers.etag, + repos, + }); + } catch (error) { + if (error.status !== 304) { + throw error; + } + repos = installationRepoData!.repos; + } + + return repos.has(repo); + } + + private async getInstallationData(owner: string): Promise { + try { + const installations = await this.appClient.apps.listInstallations({ + headers: { + 'If-None-Match': this.installationsEtag, + }, + }); + this.installationsEtag = installations.headers.etag; + + const installation = installations.data.find( + inst => inst.account?.login === owner, + ); + + if (installation) { + const data = { + installationId: installation.id, + suspended: Boolean(installation.suspended_by), + repositorySelection: installation.repository_selection, + }; + this.installationDatas.set(owner, data); + return data; + } + } catch (error) { + if (error.status !== 304) { + throw error; + } + const data = this.installationDatas.get(owner); + if (data) { + return data; + } + } + + const notFoundError = new Error( + `No app installation found for ${owner} in ${this.baseAuthConfig.appId}`, + ); + notFoundError.name = 'NotFoundError'; + throw notFoundError; + } +} + +// for each github installation +class GithubIntegration { + private readonly apps: GithubAppManager[]; + + constructor(config: GitHubIntegrationConfig) { + this.apps = config.apps?.map(ac => new GithubAppManager(ac)) ?? []; + } + + async getCredentialsForAppInstallation( + owner: string, + repo: string, + ): Promise<{ accessToken: string }> { + const results = await Promise.all( + this.apps.map(app => + app.getInstallationCredentials(owner, repo).then( + credentials => ({ credentials, error: undefined }), + error => ({ credentials: undefined, error }), + ), + ), + ); + const result = results.find(result => result.credentials); + if (result) { + return result.credentials; + } + + const errors = results.map(r => r.error); + const notNotFoundError = errors.find(err => err.name !== 'NotFoundError'); + if (notNotFoundError) { + throw notNotFoundError; + } + const notFoundError = new Error( + `No app installation found for ${owner}/${repo}`, + ); + notFoundError.name = 'NotFoundError'; + throw notFoundError; + } +} + +export class GithubAppAuthProvider { + private readonly integrations: Map; + + constructor(configs: GitHubIntegrationConfig[]) { + this.integrations = new Map( + configs.map(config => [config.host, new GithubIntegration(config)]), + ); + } + + // getCredentials('github.com/backstage/somerepo') + async getCredentials(url: string): Promise<{ accessToken: string }> { + const parsed = gitUrlParse(url); + + const host = parsed.source; + const owner = parsed.owner; + const repo = parsed.name; + + const integration = await this.integrations.get(host); + const credentials = await integration?.getCredentialsForAppInstallation( + owner, + repo, + ); + if (!credentials) { + throw new Error(`No app installation found for ${owner}/${repo}`); + } + return credentials; + } +} From 8c6f35528ccc111c4b6ce9bb7aa6bedd4b5dba3f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 8 Jan 2021 09:33:23 +0100 Subject: [PATCH 056/144] Remove installationHasRepo --- packages/integration/src/github/githubApps.ts | 70 ++++--------------- 1 file changed, 14 insertions(+), 56 deletions(-) diff --git a/packages/integration/src/github/githubApps.ts b/packages/integration/src/github/githubApps.ts index bd2d6853a8..fca380f51c 100644 --- a/packages/integration/src/github/githubApps.ts +++ b/packages/integration/src/github/githubApps.ts @@ -25,21 +25,20 @@ type InstallationData = { repositorySelection: 'selected' | 'all'; }; -type InstallationRepoData = { - etag: string; - repos: Set; -}; +class Cache { + private readonly entries = new Map(); + getToken(key: string) {} +} // for each app class GithubAppManager { private readonly appClient: Octokit; private readonly baseAuthConfig: { appId: number; privateKey: string }; private readonly installationDatas = new Map(); - private readonly installationRepoDatas = new Map< - number, - InstallationRepoData - >(); + // private readonly repoTokenCache = new Cache<{ token: string; exp: Date }>( + // ({ exp }) => isInThePast(exp), + // ); private installationsEtag?: string; constructor(config: GithubAppConfig) { @@ -76,18 +75,16 @@ class GithubAppManager { throw new Error(`The app for ${owner}/${repo} is suspended`); } - const auth = createAppAuth({ ...this.baseAuthConfig, installationId }); - - const { token } = await auth({ type: 'installation' }); - if (repositorySelection === 'all') { + const auth = createAppAuth({ ...this.baseAuthConfig, installationId }); + + const { token } = await auth({ type: 'installation' }); + console.log('DEBUG: token =', token); + return { accessToken: token }; } - // const octokit = new Octokit({ auth: token }); - const res = await this.getInstallationClient( - installationId, - ).apps.createInstallationAccessToken({ + const res = await this.appClient.apps.createInstallationAccessToken({ installation_id: installationId, repositories: [repo], }); @@ -97,44 +94,6 @@ class GithubAppManager { // repository_selection: 'selected', // repositories: [ [Object] ] return { accessToken: res.data.token }; - - // const hasRepo = await this.installationHasRepo(installationId, repo, token); - // if (!hasRepo) { - // const error = new Error( - // `No app installation found for ${owner}/${repo} in ${this.baseAuthConfig.appId}`, - // ); - // error.name = 'NotFoundError'; - // throw error; - // } - - // return { accessToken: token }; - } - - private async installationHasRepo(id: number, repo: string, token: string) { - const octokit = new Octokit({ auth: token }); - - const installationRepoData = this.installationRepoDatas.get(id); - - let repos: Set; - try { - const res = await octokit.apps.listReposAccessibleToInstallation({ - headers: { - 'If-None-Match': installationRepoData?.etag, - }, - }); - repos = new Set(res.data.repositories.map(repo => repo.name)); - this.installationRepoDatas.set(id, { - etag: res.headers.etag, - repos, - }); - } catch (error) { - if (error.status !== 304) { - throw error; - } - repos = installationRepoData!.repos; - } - - return repos.has(repo); } private async getInstallationData(owner: string): Promise { @@ -177,7 +136,6 @@ class GithubAppManager { } } -// for each github installation class GithubIntegration { private readonly apps: GithubAppManager[]; @@ -199,7 +157,7 @@ class GithubIntegration { ); const result = results.find(result => result.credentials); if (result) { - return result.credentials; + return result.credentials!; } const errors = results.map(r => r.error); From 6437f0adf8153b77249597744f8cf83149803cc5 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 8 Jan 2021 11:17:39 +0100 Subject: [PATCH 057/144] wip --- packages/integration/src/github/githubApps.ts | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/integration/src/github/githubApps.ts b/packages/integration/src/github/githubApps.ts index fca380f51c..cd4002ef13 100644 --- a/packages/integration/src/github/githubApps.ts +++ b/packages/integration/src/github/githubApps.ts @@ -18,6 +18,7 @@ import { GithubAppConfig, GitHubIntegrationConfig } from './config'; import { createAppAuth } from '@octokit/auth-app'; import { Octokit } from '@octokit/rest'; import gitUrlParse from 'git-url-parse'; +import moment from 'moment'; type InstallationData = { installationId: number; @@ -25,20 +26,16 @@ type InstallationData = { repositorySelection: 'selected' | 'all'; }; -class Cache { - private readonly entries = new Map(); - - getToken(key: string) {} -} -// for each app +// GithubAppManager issues tokens for a speicifc GitHub App class GithubAppManager { private readonly appClient: Octokit; private readonly baseAuthConfig: { appId: number; privateKey: string }; private readonly installationDatas = new Map(); + private readonly tokenCache = new Map< + string, + { accessToken: string; exp: Date } + >(); - // private readonly repoTokenCache = new Cache<{ token: string; exp: Date }>( - // ({ exp }) => isInThePast(exp), - // ); private installationsEtag?: string; constructor(config: GithubAppConfig) { @@ -52,15 +49,8 @@ class GithubAppManager { }); } - getInstallationClient(installationId: number): Octokit { - return new Octokit({ - authStrategy: createAppAuth, - auth: { - ...this.baseAuthConfig, - installationId, - }, - }); - } + private lessThanOneHourAgo = (date: Date) => + moment(date).isAfter(moment().subtract(1, 'hours')); async getInstallationCredentials( owner: string, @@ -75,28 +65,38 @@ class GithubAppManager { throw new Error(`The app for ${owner}/${repo} is suspended`); } + // App is installed in the entire org if (repositorySelection === 'all') { const auth = createAppAuth({ ...this.baseAuthConfig, installationId }); - const { token } = await auth({ type: 'installation' }); - console.log('DEBUG: token =', token); - return { accessToken: token }; } + // App is not installed org wide which requires a specific app token. + const cacheKey = `${owner}/${repo}`; + if (this.tokenCache.has(cacheKey)) { + const item = this.tokenCache.get(cacheKey); + if (this.lessThanOneHourAgo(item?.exp!)) { + return { + accessToken: item?.accessToken!, + }; + } + } + const res = await this.appClient.apps.createInstallationAccessToken({ installation_id: installationId, repositories: [repo], }); - // token: 'v1.186839cac1afb9236d3b41dffee02ffbcf17861b', - // expires_at: '2021-01-07T17:12:50Z', - // permissions: { contents: 'read', metadata: 'read' }, - // repository_selection: 'selected', - // repositories: [ [Object] ] + this.tokenCache.set(cacheKey, { + accessToken: res.data.token, + exp: new Date(res.data.expires_at), + }); return { accessToken: res.data.token }; } private async getInstallationData(owner: string): Promise { + // List all installations using the last used etag. + // Return cached InstallationData if error with status 304 is thrown. try { const installations = await this.appClient.apps.listInstallations({ headers: { @@ -136,6 +136,7 @@ class GithubAppManager { } } +// GithubIntegration corresponds to a Github installation which internally could hold several GitHub Apps. class GithubIntegration { private readonly apps: GithubAppManager[]; From 5d20d4bad8cb813151d0750ac87329c133b1d4e6 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 8 Jan 2021 13:11:16 +0100 Subject: [PATCH 058/144] Store installations response --- packages/integration/src/github/githubApps.ts | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/packages/integration/src/github/githubApps.ts b/packages/integration/src/github/githubApps.ts index cd4002ef13..daf96429b9 100644 --- a/packages/integration/src/github/githubApps.ts +++ b/packages/integration/src/github/githubApps.ts @@ -16,7 +16,7 @@ import { GithubAppConfig, GitHubIntegrationConfig } from './config'; import { createAppAuth } from '@octokit/auth-app'; -import { Octokit } from '@octokit/rest'; +import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'; import gitUrlParse from 'git-url-parse'; import moment from 'moment'; @@ -30,14 +30,12 @@ type InstallationData = { class GithubAppManager { private readonly appClient: Octokit; private readonly baseAuthConfig: { appId: number; privateKey: string }; - private readonly installationDatas = new Map(); + private installations?: RestEndpointMethodTypes['apps']['listInstallations']['response']; private readonly tokenCache = new Map< string, { accessToken: string; exp: Date } >(); - private installationsEtag?: string; - constructor(config: GithubAppConfig) { this.baseAuthConfig = { appId: config.appId, @@ -67,7 +65,10 @@ class GithubAppManager { // App is installed in the entire org if (repositorySelection === 'all') { - const auth = createAppAuth({ ...this.baseAuthConfig, installationId }); + const auth = createAppAuth({ + ...this.baseAuthConfig, + installationId, + }); const { token } = await auth({ type: 'installation' }); return { accessToken: token }; } @@ -97,35 +98,31 @@ class GithubAppManager { private async getInstallationData(owner: string): Promise { // List all installations using the last used etag. // Return cached InstallationData if error with status 304 is thrown. + let installation; try { - const installations = await this.appClient.apps.listInstallations({ + this.installations = await this.appClient.apps.listInstallations({ headers: { - 'If-None-Match': this.installationsEtag, + 'If-None-Match': this.installations?.headers.etag, }, }); - this.installationsEtag = installations.headers.etag; - const installation = installations.data.find( + installation = this.installations.data.find( inst => inst.account?.login === owner, ); - - if (installation) { - const data = { - installationId: installation.id, - suspended: Boolean(installation.suspended_by), - repositorySelection: installation.repository_selection, - }; - this.installationDatas.set(owner, data); - return data; - } } catch (error) { if (error.status !== 304) { throw error; } - const data = this.installationDatas.get(owner); - if (data) { - return data; - } + installation = this.installations?.data.find( + inst => inst.account?.login === owner, + ); + } + if (installation) { + return { + installationId: installation.id, + suspended: Boolean(installation.suspended_by), + repositorySelection: installation.repository_selection, + }; } const notFoundError = new Error( From 25a5d6bf2f7dd0d240bb482ae984b1fc53d79e5e Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 11 Jan 2021 14:28:56 +0100 Subject: [PATCH 059/144] Refactor GitHubAppManager, add deps & config --- packages/integration/config.d.ts | 10 ++ packages/integration/package.json | 5 +- packages/integration/src/github/config.ts | 14 ++ packages/integration/src/github/githubApps.ts | 77 ++++++----- yarn.lock | 120 +++++------------- 5 files changed, 109 insertions(+), 117 deletions(-) diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index a03a07409b..6a9fd62995 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -35,6 +35,16 @@ export interface Config { apiBaseUrl?: string; /** @visibility frontend */ rawBaseUrl?: string; + apps?: Array<{ + appId: number; + /** @visiblity secret */ + privateKey: string; + /** @visiblity secret */ + webhookSecret: string; + clientId: string; + /** @visiblity secret */ + clientSecret: string; + }>; }>; gitlab?: Array<{ diff --git a/packages/integration/package.json b/packages/integration/package.json index 258c5c61c6..52dc52a658 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -31,7 +31,10 @@ "dependencies": { "@backstage/config": "^0.1.2", "cross-fetch": "^3.0.6", - "git-url-parse": "^11.4.3" + "git-url-parse": "^11.4.3", + "@octokit/rest": "^18.0.12", + "@octokit/auth-app": "^2.10.5", + "moment": "^2.29.1" }, "devDependencies": { "@backstage/cli": "^0.4.5", diff --git a/packages/integration/src/github/config.ts b/packages/integration/src/github/config.ts index 22e8ad57d8..527e287419 100644 --- a/packages/integration/src/github/config.ts +++ b/packages/integration/src/github/config.ts @@ -58,8 +58,22 @@ export type GitHubIntegrationConfig = { * If no token is specified, anonymous access is used. */ token?: string; + + /** + * The GitHub Apps configuration to use for requests to this provider. + * + * If no apps is specified, token or anonymous is used. + */ + apps?: GithubAppConfig[]; }; +export type GithubAppConfig = { + appId: number; + privateKey: string; + webhookSecret: string; + clientId: string; + clientSecret: string; +}; /** * Reads a single GitHub integration config. * diff --git a/packages/integration/src/github/githubApps.ts b/packages/integration/src/github/githubApps.ts index daf96429b9..75f83e8662 100644 --- a/packages/integration/src/github/githubApps.ts +++ b/packages/integration/src/github/githubApps.ts @@ -19,6 +19,7 @@ import { createAppAuth } from '@octokit/auth-app'; import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'; import gitUrlParse from 'git-url-parse'; import moment from 'moment'; +import { InstallationAccessTokenAuthentication } from '@octokit/auth-app/dist-types/types'; type InstallationData = { installationId: number; @@ -26,15 +27,37 @@ type InstallationData = { repositorySelection: 'selected' | 'all'; }; +class Cache { + private readonly tokenCache = new Map< + string, + { token: string; expiresAt: Date } + >(); + + async getToken( + key: string, + fn: () => Promise<{ token: string; expiresAt: Date }>, + ): Promise<{ accessToken: string }> { + const item = this.tokenCache.get(key); + if (item && this.isNotExpired(item.expiresAt)) { + return { accessToken: item.token }; + } + + const result = await fn(); + this.tokenCache.set(key, result); + return { accessToken: result.token }; + } + + // consider timestamps older than 50 minutes to be expired. + private isNotExpired = (date: Date) => + moment(date).isAfter(moment().subtract(50, 'minutes')); +} + // GithubAppManager issues tokens for a speicifc GitHub App class GithubAppManager { private readonly appClient: Octokit; private readonly baseAuthConfig: { appId: number; privateKey: string }; private installations?: RestEndpointMethodTypes['apps']['listInstallations']['response']; - private readonly tokenCache = new Map< - string, - { accessToken: string; exp: Date } - >(); + private readonly cache = new Cache(); constructor(config: GithubAppConfig) { this.baseAuthConfig = { @@ -47,9 +70,6 @@ class GithubAppManager { }); } - private lessThanOneHourAgo = (date: Date) => - moment(date).isAfter(moment().subtract(1, 'hours')); - async getInstallationCredentials( owner: string, repo: string, @@ -65,34 +85,31 @@ class GithubAppManager { // App is installed in the entire org if (repositorySelection === 'all') { - const auth = createAppAuth({ - ...this.baseAuthConfig, - installationId, + return this.cache.getToken(owner, async () => { + const auth = createAppAuth({ + ...this.baseAuthConfig, + installationId, + }); + const result = await auth({ type: 'installation' }); + const { + token, + expiresAt, + } = result as InstallationAccessTokenAuthentication; + return { token, expiresAt: new Date(expiresAt) }; }); - const { token } = await auth({ type: 'installation' }); - return { accessToken: token }; } // App is not installed org wide which requires a specific app token. - const cacheKey = `${owner}/${repo}`; - if (this.tokenCache.has(cacheKey)) { - const item = this.tokenCache.get(cacheKey); - if (this.lessThanOneHourAgo(item?.exp!)) { - return { - accessToken: item?.accessToken!, - }; - } - } - - const res = await this.appClient.apps.createInstallationAccessToken({ - installation_id: installationId, - repositories: [repo], + return this.cache.getToken(`${owner}/${repo}`, async () => { + const result = await this.appClient.apps.createInstallationAccessToken({ + installation_id: installationId, + repositories: [repo], + }); + return { + token: result.data.token, + expiresAt: new Date(result.data.expires_at), + }; }); - this.tokenCache.set(cacheKey, { - accessToken: res.data.token, - exp: new Date(res.data.expires_at), - }); - return { accessToken: res.data.token }; } private async getInstallationData(owner: string): Promise { diff --git a/yarn.lock b/yarn.lock index 5182e6abb0..ea4d11c6d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4661,32 +4661,27 @@ dependencies: mkdirp "^1.0.4" -"@octokit/auth-token@^2.4.0": - version "2.4.0" - resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz#b64178975218b99e4dfe948253f0673cbbb59d9f" - integrity sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg== +"@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== dependencies: - "@octokit/types" "^2.0.0" + "@octokit/request" "^5.4.11" + "@octokit/request-error" "^2.0.0" + "@octokit/types" "^6.0.3" + "@types/lru-cache" "^5.1.0" + deprecation "^2.3.1" + lru-cache "^6.0.0" + universal-github-app-jwt "^1.0.1" + universal-user-agent "^6.0.0" -"@octokit/auth-token@^2.4.4": +"@octokit/auth-token@^2.4.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" integrity sha512-LNfGu3Ro9uFAYh10MUZVaT7X2CnNm2C8IDQmabx+3DygYIQjs9FwzFAHN/0t6mu5HEPhxcb1XOuxdpY82vCg2Q== dependencies: "@octokit/types" "^6.0.0" -"@octokit/core@^3.0.0": - version "3.1.0" - resolved "https://registry.npmjs.org/@octokit/core/-/core-3.1.0.tgz#9c3c9b23f7504668cfa057f143ccbf0c645a0ac9" - integrity sha512-yPyQSmxIXLieEIRikk2w8AEtWkFdfG/LXcw1KvEtK3iP0ENZLW/WYQmdzOKqfSaLhooz4CJ9D+WY79C8ZliACw== - dependencies: - "@octokit/auth-token" "^2.4.0" - "@octokit/graphql" "^4.3.1" - "@octokit/request" "^5.4.0" - "@octokit/types" "^5.0.0" - before-after-hook "^2.1.0" - universal-user-agent "^5.0.0" - "@octokit/core@^3.2.3": version "3.2.4" resolved "https://registry.npmjs.org/@octokit/core/-/core-3.2.4.tgz#5791256057a962eca972e31818f02454897fd106" @@ -4708,15 +4703,6 @@ is-plain-object "^3.0.0" universal-user-agent "^5.0.0" -"@octokit/graphql@^4.3.1": - version "4.5.1" - resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.1.tgz#162aed1490320b88ce34775b3f6b8de945529fa9" - integrity sha512-qgMsROG9K2KxDs12CO3bySJaYoUu2aic90qpFrv7A8sEBzZ7UFGvdgPKiLw5gOPYEYbS0Xf8Tvf84tJutHPulQ== - dependencies: - "@octokit/request" "^5.3.0" - "@octokit/types" "^5.0.0" - universal-user-agent "^5.0.0" - "@octokit/graphql@^4.5.8": version "4.5.8" resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.8.tgz#d42373633c3015d0eafce64a8ce196be167fdd9b" @@ -4743,13 +4729,6 @@ dependencies: "@octokit/types" "^2.0.1" -"@octokit/plugin-paginate-rest@^2.2.0": - version "2.2.3" - resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.3.tgz#a6ad4377e7e7832fb4bdd9d421e600cb7640ac27" - integrity sha512-eKTs91wXnJH8Yicwa30jz6DF50kAh7vkcqCQ9D7/tvBAP5KKkg6I2nNof8Mp/65G0Arjsb4QcOJcIEQY+rK1Rg== - dependencies: - "@octokit/types" "^5.0.0" - "@octokit/plugin-paginate-rest@^2.6.2": version "2.7.0" resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.7.0.tgz#6bb7b043c246e0654119a6ec4e72a172c9e2c7f3" @@ -4757,12 +4736,7 @@ dependencies: "@octokit/types" "^6.0.1" -"@octokit/plugin-request-log@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e" - integrity sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw== - -"@octokit/plugin-request-log@^1.0.2": +"@octokit/plugin-request-log@^1.0.0", "@octokit/plugin-request-log@^1.0.2": version "1.0.2" resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44" integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg== @@ -4775,14 +4749,6 @@ "@octokit/types" "^2.0.1" deprecation "^2.3.1" -"@octokit/plugin-rest-endpoint-methods@4.1.4": - version "4.1.4" - resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.1.4.tgz#ca60736d761b304fec02a2608caaec2d822e9835" - integrity sha512-Y2tVpSa7HjV3DGIQrQOJcReJ2JtcN9FaGr9jDa332Flro923/h3/Iu9e7Y4GilnzfLclHEh5iCQoCkHm7tWOcg== - dependencies: - "@octokit/types" "^5.4.1" - deprecation "^2.3.1" - "@octokit/plugin-rest-endpoint-methods@4.4.1": version "4.4.1" resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.4.1.tgz#105cf93255432155de078c9efc33bd4e14d1cd63" @@ -4809,21 +4775,7 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.2.0", "@octokit/request@^5.3.0", "@octokit/request@^5.4.0": - version "5.4.5" - resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.5.tgz#8df65bd812047521f7e9db6ff118c06ba84ac10b" - integrity sha512-atAs5GAGbZedvJXXdjtKljin+e2SltEs48B3naJjqWupYl2IUBbB/CJisyjbNHcKpHzb3E+OYEZ46G8eakXgQg== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.0.0" - "@octokit/types" "^5.0.0" - deprecation "^2.0.0" - is-plain-object "^3.0.0" - node-fetch "^2.3.0" - once "^1.4.0" - universal-user-agent "^5.0.0" - -"@octokit/request@^5.4.12": +"@octokit/request@^5.2.0", "@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.12": version "5.4.12" resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.12.tgz#b04826fa934670c56b135a81447be2c1723a2ffc" integrity sha512-MvWYdxengUWTGFpfpefBBpVmmEYfkwMoxonIB3sUGp5rhdgwjXL1ejo6JbgzG/QD9B/NYt/9cJX1pxXeSIUCkg== @@ -4859,17 +4811,7 @@ once "^1.4.0" universal-user-agent "^4.0.0" -"@octokit/rest@^18.0.0": - version "18.0.5" - resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.5.tgz#1f1498dcdc2d85d0f86b8168e4ff5779842b2742" - integrity sha512-SPKI24tQXrr1XsnaIjv2x0rl4M5eF1+hj8+vMe3d/exZ7NnL5sTe1BuFyCyJyrc+j1HkXankvgGN9zT0rwBwtg== - dependencies: - "@octokit/core" "^3.0.0" - "@octokit/plugin-paginate-rest" "^2.2.0" - "@octokit/plugin-request-log" "^1.0.0" - "@octokit/plugin-rest-endpoint-methods" "4.1.4" - -"@octokit/rest@^18.0.12": +"@octokit/rest@^18.0.0", "@octokit/rest@^18.0.12": version "18.0.12" resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.12.tgz#278bd41358c56d87c201e787e8adc0cac132503a" integrity sha512-hNRCZfKPpeaIjOVuNJzkEL6zacfZlBPV8vw8ReNeyUkVvbuCvvrrx8K8Gw2eyHHsmd4dPlAxIXIZ9oHhJfkJpw== @@ -4887,16 +4829,9 @@ "@types/node" ">= 8" "@octokit/types@^5.0.0", "@octokit/types@^5.0.1": - version "5.0.1" - resolved "https://registry.npmjs.org/@octokit/types/-/types-5.0.1.tgz#5459e9a5e9df8565dcc62c17a34491904d71971e" - integrity sha512-GorvORVwp244fGKEt3cgt/P+M0MGy4xEDbckw+K5ojEezxyMDgCaYPKVct+/eWQfZXOT7uq0xRpmrl/+hliabA== - dependencies: - "@types/node" ">= 8" - -"@octokit/types@^5.4.1": - version "5.4.1" - resolved "https://registry.npmjs.org/@octokit/types/-/types-5.4.1.tgz#d5d5f2b70ffc0e3f89467c3db749fa87fc3b7031" - integrity sha512-OlMlSySBJoJ6uozkr/i03nO5dlYQyE05vmQNZhAh9MyO4DPBP88QlwsDVLmVjIMFssvIZB6WO0ctIGMRG+xsJQ== + version "5.5.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" + integrity sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ== dependencies: "@types/node" ">= 8" @@ -6631,7 +6566,7 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= -"@types/jsonwebtoken@^8.5.0": +"@types/jsonwebtoken@^8.3.3", "@types/jsonwebtoken@^8.5.0": version "8.5.0" resolved "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5" integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== @@ -6694,6 +6629,11 @@ resolved "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== +"@types/lru-cache@^5.1.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" + integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== + "@types/markdown-to-jsx@^6.11.0": version "6.11.2" resolved "https://registry.npmjs.org/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.2.tgz#05d1aaffbf15be7be12c70535fa4fed65cc7c64f" @@ -18484,7 +18424,7 @@ modify-values@^1.0.0: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@^2.25.3, moment@^2.26.0, moment@^2.27.0: +moment@^2.25.3, moment@^2.26.0, moment@^2.27.0, moment@^2.29.1: version "2.29.1" resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -25155,6 +25095,14 @@ unist-util-visit@^2.0.0: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" +universal-github-app-jwt@^1.0.1: + version "1.1.0" + resolved "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.0.tgz#0abaa876101cdf1d3e4c546be2768841c0c1b514" + integrity sha512-3b+ocAjjz4JTyqaOT+NNBd5BtTuvJTxWElIoeHSVelUV9J3Jp7avmQTdLKCaoqi/5Ox2o/q+VK19TJ233rVXVQ== + dependencies: + "@types/jsonwebtoken" "^8.3.3" + jsonwebtoken "^8.5.1" + universal-user-agent@^4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz#fd8d6cb773a679a709e967ef8288a31fcc03e557" From 04ad29ca4e0f274bdb8cfc004077ebf39e88e377 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Jan 2021 16:42:39 +0100 Subject: [PATCH 060/144] Make GithubUrlReader use GithubCredentialsProvider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- .../src/reading/GithubUrlReader.ts | 34 +++++-- packages/integration/package.json | 3 +- ...ubApps.ts => GithubCredentialsProvider.ts} | 88 ++++++++++--------- packages/integration/src/github/config.ts | 9 +- packages/integration/src/github/index.ts | 1 + yarn.lock | 10 +++ 6 files changed, 94 insertions(+), 51 deletions(-) rename packages/integration/src/github/{githubApps.ts => GithubCredentialsProvider.ts} (74%) diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 5ca2a99692..255319dd20 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -18,7 +18,7 @@ import { GitHubIntegrationConfig, readGitHubIntegrationConfigs, getGitHubFileFetchUrl, - getGitHubRequestOptions, + GithubCredentialsProvider, } from '@backstage/integration'; import fetch from 'cross-fetch'; import parseGitUri from 'git-url-parse'; @@ -42,7 +42,11 @@ export class GithubUrlReader implements UrlReader { config.getOptionalConfigArray('integrations.github') ?? [], ); return configs.map(provider => { - const reader = new GithubUrlReader(provider, { treeResponseFactory }); + const credentialsProvider = GithubCredentialsProvider.create(provider); + const reader = new GithubUrlReader(provider, { + treeResponseFactory, + credentialsProvider, + }); const predicate = (url: URL) => url.host === provider.host; return { reader, predicate }; }); @@ -50,7 +54,10 @@ export class GithubUrlReader implements UrlReader { constructor( private readonly config: GitHubIntegrationConfig, - private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + credentialsProvider: GithubCredentialsProvider; + }, ) { if (!config.apiBaseUrl && !config.rawBaseUrl) { throw new Error( @@ -61,11 +68,17 @@ export class GithubUrlReader implements UrlReader { async read(url: string): Promise { const ghUrl = getGitHubFileFetchUrl(url, this.config); - const options = getGitHubRequestOptions(this.config); - + const { headers } = await this.deps.credentialsProvider.getCredentials({ + url, + }); let response: Response; try { - response = await fetch(ghUrl.toString(), options); + response = await fetch(ghUrl.toString(), { + headers: { + ...headers, + Accept: 'application/vnd.github.v3.raw', + }, + }); } catch (e) { throw new Error(`Unable to read ${url}, ${e}`); } @@ -101,12 +114,19 @@ export class GithubUrlReader implements UrlReader { ); } + const { headers } = await this.deps.credentialsProvider.getCredentials({ + url, + }); // TODO(Rugvip): use API to fetch URL instead const response = await fetch( new URL( `${protocol}://${resource}/${full_name}/archive/${ref}.tar.gz`, ).toString(), - getGitHubRequestOptions(this.config), + { + headers: { + ...headers, + }, + }, ); if (!response.ok) { const message = `Failed to read tree from ${url}, ${response.status} ${response.statusText}`; diff --git a/packages/integration/package.json b/packages/integration/package.json index 52dc52a658..58c97ae4e5 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -34,12 +34,13 @@ "git-url-parse": "^11.4.3", "@octokit/rest": "^18.0.12", "@octokit/auth-app": "^2.10.5", - "moment": "^2.29.1" + "luxon": "^1.25.0" }, "devDependencies": { "@backstage/cli": "^0.4.5", "@backstage/test-utils": "^0.1.5", "@types/jest": "^26.0.7", + "@types/luxon": "^1.25.0", "msw": "^0.21.2" }, "files": [ diff --git a/packages/integration/src/github/githubApps.ts b/packages/integration/src/github/GithubCredentialsProvider.ts similarity index 74% rename from packages/integration/src/github/githubApps.ts rename to packages/integration/src/github/GithubCredentialsProvider.ts index 75f83e8662..490e062415 100644 --- a/packages/integration/src/github/githubApps.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -14,11 +14,11 @@ * limitations under the License. */ +import gitUrlParse from 'git-url-parse'; import { GithubAppConfig, GitHubIntegrationConfig } from './config'; import { createAppAuth } from '@octokit/auth-app'; import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'; -import gitUrlParse from 'git-url-parse'; -import moment from 'moment'; +import { DateTime } from 'luxon'; import { InstallationAccessTokenAuthentication } from '@octokit/auth-app/dist-types/types'; type InstallationData = { @@ -30,26 +30,26 @@ type InstallationData = { class Cache { private readonly tokenCache = new Map< string, - { token: string; expiresAt: Date } + { token: string; expiresAt: DateTime } >(); - async getToken( + async getOrCreateToken( key: string, - fn: () => Promise<{ token: string; expiresAt: Date }>, + supplier: () => Promise<{ token: string; expiresAt: DateTime }>, ): Promise<{ accessToken: string }> { const item = this.tokenCache.get(key); if (item && this.isNotExpired(item.expiresAt)) { return { accessToken: item.token }; } - const result = await fn(); + const result = await supplier(); this.tokenCache.set(key, result); return { accessToken: result.token }; } // consider timestamps older than 50 minutes to be expired. - private isNotExpired = (date: Date) => - moment(date).isAfter(moment().subtract(50, 'minutes')); + private isNotExpired = (date: DateTime) => + date.diff(DateTime.local(), 'minutes').minutes > 50; } // GithubAppManager issues tokens for a speicifc GitHub App @@ -85,7 +85,7 @@ class GithubAppManager { // App is installed in the entire org if (repositorySelection === 'all') { - return this.cache.getToken(owner, async () => { + return this.cache.getOrCreateToken(owner, async () => { const auth = createAppAuth({ ...this.baseAuthConfig, installationId, @@ -95,19 +95,19 @@ class GithubAppManager { token, expiresAt, } = result as InstallationAccessTokenAuthentication; - return { token, expiresAt: new Date(expiresAt) }; + return { token, expiresAt: DateTime.fromISO(expiresAt) }; }); } // App is not installed org wide which requires a specific app token. - return this.cache.getToken(`${owner}/${repo}`, async () => { + return this.cache.getOrCreateToken(`${owner}/${repo}`, async () => { const result = await this.appClient.apps.createInstallationAccessToken({ installation_id: installationId, repositories: [repo], }); return { token: result.data.token, - expiresAt: new Date(result.data.expires_at), + expiresAt: DateTime.fromISO(result.data.expires_at), }; }); } @@ -150,18 +150,19 @@ class GithubAppManager { } } -// GithubIntegration corresponds to a Github installation which internally could hold several GitHub Apps. -class GithubIntegration { +// GithubAppCredentialsMux corresponds to a Github installation which internally could hold several GitHub Apps. +export class GithubAppCredentialsMux { private readonly apps: GithubAppManager[]; constructor(config: GitHubIntegrationConfig) { this.apps = config.apps?.map(ac => new GithubAppManager(ac)) ?? []; } - async getCredentialsForAppInstallation( - owner: string, - repo: string, - ): Promise<{ accessToken: string }> { + async getAppToken(owner: string, repo: string): Promise { + if (this.apps.length === 0) { + return undefined; + } + const results = await Promise.all( this.apps.map(app => app.getInstallationCredentials(owner, repo).then( @@ -172,7 +173,7 @@ class GithubIntegration { ); const result = results.find(result => result.credentials); if (result) { - return result.credentials!; + return result.credentials!.accessToken; } const errors = results.map(r => r.error); @@ -180,39 +181,42 @@ class GithubIntegration { if (notNotFoundError) { throw notNotFoundError; } - const notFoundError = new Error( - `No app installation found for ${owner}/${repo}`, - ); - notFoundError.name = 'NotFoundError'; - throw notFoundError; + + return undefined; } } -export class GithubAppAuthProvider { - private readonly integrations: Map; - - constructor(configs: GitHubIntegrationConfig[]) { - this.integrations = new Map( - configs.map(config => [config.host, new GithubIntegration(config)]), +// TODO: Possibly move this to a backend only package so that it's not used in the frontend by mistake +export class GithubCredentialsProvider { + static create(config: GitHubIntegrationConfig): GithubCredentialsProvider { + return new GithubCredentialsProvider( + new GithubAppCredentialsMux(config), + config.token, ); } - // getCredentials('github.com/backstage/somerepo') - async getCredentials(url: string): Promise<{ accessToken: string }> { - const parsed = gitUrlParse(url); + private constructor( + private readonly githubAppCredentialsMux: GithubAppCredentialsMux, + private readonly token?: string, + ) {} - const host = parsed.source; + async getCredentials(opts: { url: string }) { + const parsed = gitUrlParse(opts.url); const owner = parsed.owner; const repo = parsed.name; - const integration = await this.integrations.get(host); - const credentials = await integration?.getCredentialsForAppInstallation( - owner, - repo, - ); - if (!credentials) { - throw new Error(`No app installation found for ${owner}/${repo}`); + let token = await this.githubAppCredentialsMux.getAppToken(owner, repo); + if (!token) { + token = this.token; } - return credentials; + + return { + headers: token + ? { + Authorization: `Bearer ${token}`, + } + : undefined, + token, + }; } } diff --git a/packages/integration/src/github/config.ts b/packages/integration/src/github/config.ts index 527e287419..eced7584a4 100644 --- a/packages/integration/src/github/config.ts +++ b/packages/integration/src/github/config.ts @@ -86,6 +86,13 @@ export function readGitHubIntegrationConfig( let apiBaseUrl = config.getOptionalString('apiBaseUrl'); let rawBaseUrl = config.getOptionalString('rawBaseUrl'); const token = config.getOptionalString('token'); + const apps = config.getOptionalConfigArray('apps')?.map(c => ({ + appId: c.getNumber('appId'), + clientId: c.getString('clientId'), + clientSecret: c.getString('clientSecret'), + webhookSecret: c.getString('webhookSecret'), + privateKey: c.getString('privateKey'), + })); if (!isValidHost(host)) { throw new Error( @@ -105,7 +112,7 @@ export function readGitHubIntegrationConfig( rawBaseUrl = GITHUB_RAW_BASE_URL; } - return { host, apiBaseUrl, rawBaseUrl, token }; + return { host, apiBaseUrl, rawBaseUrl, token, apps }; } /** diff --git a/packages/integration/src/github/index.ts b/packages/integration/src/github/index.ts index 5f97f6980a..6491e8dcc5 100644 --- a/packages/integration/src/github/index.ts +++ b/packages/integration/src/github/index.ts @@ -20,3 +20,4 @@ export { } from './config'; export type { GitHubIntegrationConfig } from './config'; export { getGitHubFileFetchUrl, getGitHubRequestOptions } from './core'; +export { GithubCredentialsProvider } from './GithubCredentialsProvider'; diff --git a/yarn.lock b/yarn.lock index ea4d11c6d3..6986104789 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6634,6 +6634,11 @@ resolved "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== +"@types/luxon@^1.25.0": + version "1.25.0" + resolved "https://registry.npmjs.org/@types/luxon/-/luxon-1.25.0.tgz#3d6fe591fac874f48dd225cb5660b2b785a21a05" + integrity sha512-iIJp2CP6C32gVqI08HIYnzqj55tlLnodIBMCcMf28q9ckqMfMzocCmIzd9JWI/ALLPMUiTkCu1JGv3FFtu6t3g== + "@types/markdown-to-jsx@^6.11.0": version "6.11.2" resolved "https://registry.npmjs.org/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.2.tgz#05d1aaffbf15be7be12c70535fa4fed65cc7c64f" @@ -17662,6 +17667,11 @@ lru-queue@0.1: dependencies: es5-ext "~0.10.2" +luxon@^1.25.0: + version "1.25.0" + resolved "https://registry.npmjs.org/luxon/-/luxon-1.25.0.tgz#d86219e90bc0102c0eb299d65b2f5e95efe1fe72" + integrity sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ== + macos-release@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" From e64acdad765f113e42d21cb0c5665f2d90d66ce4 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Wed, 13 Jan 2021 16:19:01 +0100 Subject: [PATCH 061/144] Added tests and refactored provider Co-authored-by: blam --- .../github/GithubCredentialsProvider.test.ts | 265 ++++++++++++++++++ .../src/github/GithubCredentialsProvider.ts | 54 ++-- 2 files changed, 295 insertions(+), 24 deletions(-) create mode 100644 packages/integration/src/github/GithubCredentialsProvider.test.ts diff --git a/packages/integration/src/github/GithubCredentialsProvider.test.ts b/packages/integration/src/github/GithubCredentialsProvider.test.ts new file mode 100644 index 0000000000..34243009b1 --- /dev/null +++ b/packages/integration/src/github/GithubCredentialsProvider.test.ts @@ -0,0 +1,265 @@ +/* + * 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. + */ + +const octokit = { + apps: { + listInstallations: jest.fn(), + createInstallationAccessToken: jest.fn(), + }, +}; + +jest.doMock('@octokit/rest', () => { + class Octokit { + constructor() { + return octokit; + } + } + return { Octokit }; +}); + +import { GithubCredentialsProvider } from './GithubCredentialsProvider'; +import { RestEndpointMethodTypes } from '@octokit/rest'; +import { DateTime } from 'luxon'; + +const github = GithubCredentialsProvider.create({ + host: 'github.com', + apps: [ + { + appId: 1, + privateKey: 'privateKey', + webhookSecret: '123', + clientId: 'CLIENT_ID', + clientSecret: 'CLIENT_SECRET', + }, + ], + token: 'hardcoded_token', +}); + +describe('GithubCredentialsProvider tests', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + it('create repository specific tokens', async () => { + octokit.apps.listInstallations.mockResolvedValueOnce({ + headers: { + etag: '123', + }, + data: [ + { + id: 1, + repository_selection: 'selected', + account: null, + }, + { + id: 2, + repository_selection: 'selected', + account: { + login: 'backstage', + }, + }, + ], + } as RestEndpointMethodTypes['apps']['listInstallations']['response']); + octokit.apps.listInstallations.mockRejectedValue({ status: 304 }); + + octokit.apps.createInstallationAccessToken.mockResolvedValueOnce({ + data: { + expires_at: DateTime.local().plus({ hour: 1 }).toString(), + token: 'secret_token', + }, + } as RestEndpointMethodTypes['apps']['createInstallationAccessToken']['response']); + + const { token, headers } = await github.getCredentials({ + url: 'https://github.com/backstage/foobar', + }); + const { token: accessToken2 } = await github.getCredentials({ + url: 'https://github.com/backstage/foobar', + }); + + expect(token).toEqual('secret_token'); + expect(token).toEqual(accessToken2); + expect(headers).toEqual({ Authorization: 'Bearer secret_token' }); + + // fallback to the configured token if no applicatin is matching + await expect( + github.getCredentials({ + url: 'https://github.com/404/foobar', + }), + ).resolves.toEqual({ + headers: { + Authorization: 'Bearer hardcoded_token', + }, + token: 'hardcoded_token', + }); + }); + + it('creates tokens for an organization', async () => { + octokit.apps.listInstallations.mockResolvedValueOnce({ + headers: { + etag: '123', + }, + data: [ + { + id: 1, + repository_selection: 'all', + account: { + login: 'backstage', + }, + }, + ], + } as RestEndpointMethodTypes['apps']['listInstallations']['response']); + octokit.apps.listInstallations.mockRejectedValue({ status: 304 }); + + octokit.apps.createInstallationAccessToken.mockResolvedValueOnce({ + data: { + expires_at: DateTime.local().plus({ hour: 1 }).toString(), + token: 'secret_token', + }, + } as RestEndpointMethodTypes['apps']['createInstallationAccessToken']['response']); + + const { token, headers } = await github.getCredentials({ + url: 'https://github.com/backstage', + }); + const { token: accessToken2 } = await github.getCredentials({ + url: 'https://github.com/backstage', + }); + + expect(headers).toEqual({ Authorization: 'Bearer secret_token' }); + expect(token).toEqual('secret_token'); + expect(token).toEqual(accessToken2); + }); + + it('should fail to issue tokens for an organization when the app is installed for a single repo', async () => { + octokit.apps.listInstallations.mockResolvedValueOnce({ + headers: { + etag: '123', + }, + data: [ + { + id: 1, + repository_selection: 'selected', + account: { + login: 'backstage', + }, + }, + ], + } as RestEndpointMethodTypes['apps']['listInstallations']['response']); + octokit.apps.listInstallations.mockRejectedValue({ status: 304 }); + + octokit.apps.createInstallationAccessToken.mockResolvedValueOnce({ + data: { + expires_at: DateTime.local().plus({ hour: 1 }).toString(), + token: 'secret_token', + }, + } as RestEndpointMethodTypes['apps']['createInstallationAccessToken']['response']); + + await expect( + github.getCredentials({ + url: 'https://github.com/backstage', + }), + ).rejects.toThrow( + 'Application must be installed for the entire organization', + ); + }); + + it('should throw if the app is suspended', async () => { + octokit.apps.listInstallations.mockResolvedValueOnce({ + headers: { + etag: '123', + }, + data: [ + { + id: 1, + suspended_by: { + login: 'admin', + }, + repository_selection: 'all', + account: { + login: 'backstage', + }, + }, + ], + } as RestEndpointMethodTypes['apps']['listInstallations']['response']); + octokit.apps.listInstallations.mockRejectedValue({ status: 304 }); + + await expect( + github.getCredentials({ + url: 'https://github.com/backstage', + }), + ).rejects.toThrow('The app for backstage is suspended'); + }); + + it('should return the default token when the call to github return a status that is not recognized', async () => { + octokit.apps.listInstallations.mockRejectedValue({ + status: 404, + message: 'NotFound', + }); + + await expect( + github.getCredentials({ + url: 'https://github.com/backstage', + }), + ).rejects.toEqual({ status: 404, message: 'NotFound' }); + }); + + it('should return the default token if no app is configured', async () => { + const github = GithubCredentialsProvider.create({ + host: 'github.com', + apps: [], + token: 'fallback_token', + }); + + await expect( + github.getCredentials({ + url: 'https://github.com/404/foobar', + }), + ).resolves.toEqual(expect.objectContaining({ token: 'fallback_token' })); + }); + + it('should return the configured token if listing installations throws', async () => { + const github = GithubCredentialsProvider.create({ + host: 'github.com', + apps: [ + { + appId: 1, + privateKey: 'privateKey', + webhookSecret: '123', + clientId: 'CLIENT_ID', + clientSecret: 'CLIENT_SECRET', + }, + ], + token: 'hardcoded_token', + }); + octokit.apps.listInstallations.mockRejectedValue({ status: 304 }); + + await expect( + github.getCredentials({ + url: 'https://github.com/backstage', + }), + ).resolves.toEqual(expect.objectContaining({ token: 'hardcoded_token' })); + }); + + it('should return undefined if no token or apps are configured', async () => { + const github = GithubCredentialsProvider.create({ + host: 'github.com', + }); + + await expect( + github.getCredentials({ + url: 'https://github.com/backstage', + }), + ).resolves.toEqual({ headers: undefined, token: undefined }); + }); +}); diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index 490e062415..a1f21d0e69 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -19,7 +19,6 @@ import { GithubAppConfig, GitHubIntegrationConfig } from './config'; import { createAppAuth } from '@octokit/auth-app'; import { Octokit, RestEndpointMethodTypes } from '@octokit/rest'; import { DateTime } from 'luxon'; -import { InstallationAccessTokenAuthentication } from '@octokit/auth-app/dist-types/types'; type InstallationData = { installationId: number; @@ -72,7 +71,7 @@ class GithubAppManager { async getInstallationCredentials( owner: string, - repo: string, + repo?: string, ): Promise<{ accessToken: string }> { const { installationId, @@ -80,31 +79,27 @@ class GithubAppManager { repositorySelection, } = await this.getInstallationData(owner); if (suspended) { - throw new Error(`The app for ${owner}/${repo} is suspended`); + throw new Error( + `The app for ${[owner, repo].filter(Boolean).join('/')} is suspended`, + ); } - // App is installed in the entire org - if (repositorySelection === 'all') { - return this.cache.getOrCreateToken(owner, async () => { - const auth = createAppAuth({ - ...this.baseAuthConfig, - installationId, - }); - const result = await auth({ type: 'installation' }); - const { - token, - expiresAt, - } = result as InstallationAccessTokenAuthentication; - return { token, expiresAt: DateTime.fromISO(expiresAt) }; - }); + if (repositorySelection !== 'all' && !repo) { + throw new Error( + 'Application must be installed for the entire organization', + ); } - // App is not installed org wide which requires a specific app token. - return this.cache.getOrCreateToken(`${owner}/${repo}`, async () => { + const cacheKey = !repo ? owner : `${owner}/${repo}`; + const repositories = repositorySelection !== 'all' ? [repo!] : undefined; + + // Go and grab an access token for the app scoped to a repository if provided, if not use the organisation installation. + return this.cache.getOrCreateToken(cacheKey, async () => { const result = await this.appClient.apps.createInstallationAccessToken({ installation_id: installationId, - repositories: [repo], + repositories, }); + return { token: result.data.token, expiresAt: DateTime.fromISO(result.data.expires_at), @@ -158,7 +153,7 @@ export class GithubAppCredentialsMux { this.apps = config.apps?.map(ac => new GithubAppManager(ac)) ?? []; } - async getAppToken(owner: string, repo: string): Promise { + async getAppToken(owner: string, repo?: string): Promise { if (this.apps.length === 0) { return undefined; } @@ -171,6 +166,7 @@ export class GithubAppCredentialsMux { ), ), ); + const result = results.find(result => result.credentials); if (result) { return result.credentials!.accessToken; @@ -186,6 +182,11 @@ export class GithubAppCredentialsMux { } } +export type GithubCredentials = { + headers?: { [name: string]: string }; + token?: string; +}; + // TODO: Possibly move this to a backend only package so that it's not used in the frontend by mistake export class GithubCredentialsProvider { static create(config: GitHubIntegrationConfig): GithubCredentialsProvider { @@ -200,10 +201,15 @@ export class GithubCredentialsProvider { private readonly token?: string, ) {} - async getCredentials(opts: { url: string }) { + /** + * @returns GithubCredentials. + * @param opts + */ + async getCredentials(opts: { url: string }): Promise { const parsed = gitUrlParse(opts.url); - const owner = parsed.owner; - const repo = parsed.name; + + const owner = parsed.owner || parsed.name; + const repo = parsed.owner ? parsed.name : undefined; let token = await this.githubAppCredentialsMux.getAppToken(owner, repo); if (!token) { From d5189471b266836bb23565f38a06c0b6a0b4a524 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 09:04:14 +0100 Subject: [PATCH 062/144] Update packages/integration/src/github/GithubCredentialsProvider.test.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- .../integration/src/github/GithubCredentialsProvider.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration/src/github/GithubCredentialsProvider.test.ts b/packages/integration/src/github/GithubCredentialsProvider.test.ts index 34243009b1..1bf5a11b9b 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.test.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.test.ts @@ -92,7 +92,7 @@ describe('GithubCredentialsProvider tests', () => { expect(token).toEqual(accessToken2); expect(headers).toEqual({ Authorization: 'Bearer secret_token' }); - // fallback to the configured token if no applicatin is matching + // fallback to the configured token if no application is matching await expect( github.getCredentials({ url: 'https://github.com/404/foobar', From e3ae3c29c5788c3130b6b670e8dd91c95bf0b520 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 09:08:19 +0100 Subject: [PATCH 063/144] Update packages/integration/src/github/config.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- packages/integration/src/github/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration/src/github/config.ts b/packages/integration/src/github/config.ts index eced7584a4..607033739f 100644 --- a/packages/integration/src/github/config.ts +++ b/packages/integration/src/github/config.ts @@ -62,7 +62,7 @@ export type GitHubIntegrationConfig = { /** * The GitHub Apps configuration to use for requests to this provider. * - * If no apps is specified, token or anonymous is used. + * If no apps are specified, token or anonymous is used. */ apps?: GithubAppConfig[]; }; From 779734303b22ad8266183f4942a11d42bbba04d9 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 10:20:44 +0100 Subject: [PATCH 064/144] Improve error for suspended app --- .../integration/src/github/GithubCredentialsProvider.test.ts | 2 +- packages/integration/src/github/GithubCredentialsProvider.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/integration/src/github/GithubCredentialsProvider.test.ts b/packages/integration/src/github/GithubCredentialsProvider.test.ts index 1bf5a11b9b..a272714887 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.test.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.test.ts @@ -198,7 +198,7 @@ describe('GithubCredentialsProvider tests', () => { github.getCredentials({ url: 'https://github.com/backstage', }), - ).rejects.toThrow('The app for backstage is suspended'); + ).rejects.toThrow('The GitHub application for backstage is suspended'); }); it('should return the default token when the call to github return a status that is not recognized', async () => { diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index a1f21d0e69..c572dd3473 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -80,7 +80,9 @@ class GithubAppManager { } = await this.getInstallationData(owner); if (suspended) { throw new Error( - `The app for ${[owner, repo].filter(Boolean).join('/')} is suspended`, + `The GitHub application for ${[owner, repo] + .filter(Boolean) + .join('/')} is suspended`, ); } From 820430b2206655de1d40f4150030bc0b927770b6 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 10:22:09 +0100 Subject: [PATCH 065/144] Add docs for getCredentials --- .../integration/src/github/GithubCredentialsProvider.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index c572dd3473..ae1e73c3b8 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -204,8 +204,13 @@ export class GithubCredentialsProvider { ) {} /** - * @returns GithubCredentials. - * @param opts + * Returns GithubCredentials for requested url. + * Consecutive calls to this method with the same url will return cached credentials. + * The shortest lifetime for a token returned is 10 minutes. + * @param opts containing the organization or repository url + * @returns {Promise} of @type {GithubCredentials}. + * @example + * const { token, headers } = await getCredentials({url: 'github.com/backstage/foobar'}) */ async getCredentials(opts: { url: string }): Promise { const parsed = gitUrlParse(opts.url); From ca875e665881dec30c2b192e5172e3bb6fc7afd8 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 10:44:52 +0100 Subject: [PATCH 066/144] Add docs for GithubAppConfig --- packages/integration/src/github/config.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/integration/src/github/config.ts b/packages/integration/src/github/config.ts index 607033739f..94ed00731e 100644 --- a/packages/integration/src/github/config.ts +++ b/packages/integration/src/github/config.ts @@ -67,13 +67,34 @@ export type GitHubIntegrationConfig = { apps?: GithubAppConfig[]; }; +/** + * The configuration parameters for authenticating a GitHub Application. + * A Github Apps configuration can be generated using the `backstage-cli create-github-app` command. + */ export type GithubAppConfig = { + /** + * Unique app identifier, found at https://github.com/organizations/$org/settings/apps/$AppName + */ appId: number; + /** + * The private key is used by the GitHub App integration to authenticate the app. + * A private key can be generated from the app at https://github.com/organizations/$org/settings/apps/$AppName + */ privateKey: string; + /** + * Webhook secret can be configured at https://github.com/organizations/$org/settings/apps/$AppName + */ webhookSecret: string; + /** + * Found at https://github.com/organizations/$org/settings/apps/$AppName + */ clientId: string; + /** + * Client secrets can be generated at https://github.com/organizations/$org/settings/apps/$AppName + */ clientSecret: string; }; + /** * Reads a single GitHub integration config. * From 58cb92f91eb779376f3911e00274a76d0607a311 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 13:46:01 +0100 Subject: [PATCH 067/144] Improve installation error message --- .../integration/src/github/GithubCredentialsProvider.test.ts | 2 +- packages/integration/src/github/GithubCredentialsProvider.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/integration/src/github/GithubCredentialsProvider.test.ts b/packages/integration/src/github/GithubCredentialsProvider.test.ts index a272714887..f708f75184 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.test.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.test.ts @@ -170,7 +170,7 @@ describe('GithubCredentialsProvider tests', () => { url: 'https://github.com/backstage', }), ).rejects.toThrow( - 'Application must be installed for the entire organization', + 'The Backstage GitHub application used in the backstage organization must be installed for the entire organization to be able to issue credentials without a specified repository.', ); }); diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index ae1e73c3b8..04b38d6e27 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -88,7 +88,7 @@ class GithubAppManager { if (repositorySelection !== 'all' && !repo) { throw new Error( - 'Application must be installed for the entire organization', + `The Backstage GitHub application used in the ${owner} organization must be installed for the entire organization to be able to issue credentials without a specified repository.`, ); } From 4e623b7bf24bea5fcd1d35fa1fdb3f400bdb6de2 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 13:47:47 +0100 Subject: [PATCH 068/144] Add GitHub Apps documentation Co-authored-by: blam --- docs/plugins/github-apps.md | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 docs/plugins/github-apps.md diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md new file mode 100644 index 0000000000..0710f59158 --- /dev/null +++ b/docs/plugins/github-apps.md @@ -0,0 +1,78 @@ +# Using GithubApps for backend authentication + +Backstage can be configured to use GitHub Apps for backend authentication. This +come with advantages such as higher rate limits and that Backstage can act as an +application instead of a user or bot account. + +It also provides a much clearer and better authorization model as a opposed to +the oauth apps and their respective scopes. + +## Caveats + +- It's not possible to have multiple backstage GitHub Apps installed in the same + Github organization be managed by Backstage. We currently don't check through + all the registered GitHub Apps to see which ones are installed for a + particular repository. We just respect global Organization installs right now. +- App permissions is not managed by Backstage. They're created with some simple + default permissions which you are free to change as you need, but you will + need to update them in the GitHub web console, not in Backstage right now. The + permissions that are defaulted are `metadata +- The created GitHub App is private by default, this is most likely what you + want for github.com but it's recommended to make your application public for + GitHub Enterprise in order to share application across your GHE organizations. + +A GitHub app created with `backstage-cli create-github-app` will have read +access by default. You have to manually update the GitHub App settings in GitHub +to grant the app more permissions if needed. + +### Using the CLI (public GitHub only) + +You can use the `backstage-cli` to create GitHub App' using a manifest file that +we provide. This gives us a way to automate some of the work required to create +a github app. + +You can read more about the `backstage-cli create-github-app` method +[here](../cli/commands.md#create-github-app) + +Once you've gone through the CLI command, it should produce a `yaml` file in the +root of the project which you can then use as an `include` in your +`app-config.yaml`. You can go ahead and skip to +[here](#including-in-integrations-config) if you've got to this part. + +### GitHub Enterprise + +You have to create the GitHub Application manually using these +[instructions](https://docs.github.com/en/free-pro-team@latest/developers/apps/creating-a-github-app) +as GitHub Enterprise does not support creation of apps from manifests. + +Once the application is created you have to generate a private key for the +application it in a `yaml` file. + +The yaml file must include the following information. Please note that the +indentation for the `privateKey` is required. + +```yaml +appId: 1 +clientId: client id +clientSecret: client secret +webhookSecret: webhook secret +privateKey: | + -----BEGIN RSA PRIVATE KEY----- + ...Key content... + -----END RSA PRIVATE KEY----- +``` + +### Including in Integrations Config + +Once the credentials are stored in a `yaml` file generated by +`create-github-app` or manually by following the +[Github Enterprise](#gitHub-enterprise) instructions they can be included in the +`app-config.yaml` under the integrations section. + +```yaml +integrations: + github: + - host: github.com + apps: + - $include: example-backstage-app-credentials.yaml +``` From be332e13ea18d5bbf548a576429f6c2575a799a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Jan 2021 13:05:31 +0000 Subject: [PATCH 069/144] Version Packages --- .changeset/brave-boats-greet.md | 5 --- .changeset/eighty-rats-smash.md | 5 --- .changeset/eleven-badgers-wink.md | 9 ----- .changeset/flat-cycles-lay.md | 6 ---- .changeset/flat-walls-burn.md | 5 --- .changeset/friendly-masks-dress.md | 5 --- .changeset/friendly-rats-wonder.md | 5 --- .changeset/funny-snails-cry.md | 5 --- .../generic-catalog-import-descriptions.md | 5 --- .changeset/giant-geckos-tickle.md | 5 --- .changeset/gorgeous-poems-wash.md | 5 --- .changeset/great-vans-happen.md | 11 ------ .changeset/grumpy-trains-juggle.md | 5 --- .changeset/heavy-owls-float.md | 12 ------- .changeset/many-dodos-scream.md | 5 --- .changeset/olive-dodos-hammer.md | 7 ---- .changeset/quiet-trainers-study.md | 12 ------- .changeset/quote-plastic-monk.md | 5 --- .changeset/rare-paws-listen.md | 5 --- .changeset/red-baboons-rhyme.md | 9 ----- .changeset/rich-games-yawn.md | 5 --- .changeset/short-badgers-collect.md | 5 --- .changeset/sixty-ants-give.md | 5 --- .changeset/smooth-pigs-deny.md | 5 --- .changeset/spicy-feet-sparkle.md | 5 --- .changeset/stale-cougars-wink.md | 10 ------ .changeset/techdocs-rotten-crabs-ring.md | 5 --- .changeset/techdocs-small-berries-travel.md | 5 --- .changeset/thin-icons-kick.md | 6 ---- .changeset/twelve-gorillas-give.md | 6 ---- .changeset/wild-dolls-rest.md | 5 --- packages/backend-common/CHANGELOG.md | 7 ++++ packages/backend-common/package.json | 6 ++-- packages/backend/CHANGELOG.md | 30 ++++++++++++++++ packages/backend/package.json | 18 +++++----- packages/catalog-model/CHANGELOG.md | 8 +++++ packages/catalog-model/package.json | 4 +-- packages/cli/CHANGELOG.md | 8 +++++ packages/cli/package.json | 6 ++-- packages/core/CHANGELOG.md | 6 ++++ packages/core/package.json | 4 +-- packages/create-app/CHANGELOG.md | 12 +++++++ packages/create-app/package.json | 28 +++++++-------- packages/integration/CHANGELOG.md | 6 ++++ packages/integration/package.json | 4 +-- packages/techdocs-common/CHANGELOG.md | 33 +++++++++++++++++ packages/techdocs-common/package.json | 10 +++--- plugins/api-docs/package.json | 4 +-- plugins/auth-backend/CHANGELOG.md | 11 ++++++ plugins/auth-backend/package.json | 8 ++--- plugins/catalog-backend/CHANGELOG.md | 14 ++++++++ plugins/catalog-backend/package.json | 8 ++--- plugins/catalog-import/CHANGELOG.md | 19 ++++++++++ plugins/catalog-import/package.json | 12 +++---- plugins/catalog/CHANGELOG.md | 17 +++++++++ plugins/catalog/package.json | 8 ++--- plugins/circleci/package.json | 4 +-- plugins/cloudbuild/CHANGELOG.md | 14 ++++++++ plugins/cloudbuild/package.json | 10 +++--- plugins/cost-insights/package.json | 4 +-- plugins/explore/package.json | 4 +-- plugins/fossa/package.json | 4 +-- plugins/gcp-projects/package.json | 4 +-- plugins/github-actions/CHANGELOG.md | 15 ++++++++ plugins/github-actions/package.json | 10 +++--- plugins/gitops-profiles/package.json | 4 +-- plugins/graphiql/CHANGELOG.md | 8 +++++ plugins/graphiql/package.json | 6 ++-- plugins/jenkins/CHANGELOG.md | 14 ++++++++ plugins/jenkins/package.json | 10 +++--- plugins/kubernetes-backend/CHANGELOG.md | 11 ++++++ plugins/kubernetes-backend/package.json | 8 ++--- plugins/kubernetes/CHANGELOG.md | 15 ++++++++ plugins/kubernetes/package.json | 10 +++--- plugins/lighthouse/CHANGELOG.md | 15 ++++++++ plugins/lighthouse/package.json | 10 +++--- plugins/newrelic/package.json | 4 +-- plugins/org/CHANGELOG.md | 14 ++++++++ plugins/org/package.json | 10 +++--- plugins/pagerduty/package.json | 4 +-- plugins/register-component/package.json | 4 +-- plugins/rollbar/package.json | 4 +-- plugins/scaffolder-backend/CHANGELOG.md | 21 +++++++++++ plugins/scaffolder-backend/package.json | 10 +++--- plugins/scaffolder/package.json | 4 +-- plugins/search/package.json | 4 +-- plugins/sentry/package.json | 4 +-- plugins/sonarqube/package.json | 4 +-- plugins/tech-radar/package.json | 4 +-- plugins/techdocs-backend/CHANGELOG.md | 22 ++++++++++++ plugins/techdocs-backend/package.json | 10 +++--- plugins/techdocs/CHANGELOG.md | 36 +++++++++++++++++++ plugins/techdocs/package.json | 12 +++---- plugins/user-settings/package.json | 4 +-- plugins/welcome/package.json | 4 +-- 95 files changed, 503 insertions(+), 340 deletions(-) delete mode 100644 .changeset/brave-boats-greet.md delete mode 100644 .changeset/eighty-rats-smash.md delete mode 100644 .changeset/eleven-badgers-wink.md delete mode 100644 .changeset/flat-cycles-lay.md delete mode 100644 .changeset/flat-walls-burn.md delete mode 100644 .changeset/friendly-masks-dress.md delete mode 100644 .changeset/friendly-rats-wonder.md delete mode 100644 .changeset/funny-snails-cry.md delete mode 100644 .changeset/generic-catalog-import-descriptions.md delete mode 100644 .changeset/giant-geckos-tickle.md delete mode 100644 .changeset/gorgeous-poems-wash.md delete mode 100644 .changeset/great-vans-happen.md delete mode 100644 .changeset/grumpy-trains-juggle.md delete mode 100644 .changeset/heavy-owls-float.md delete mode 100644 .changeset/many-dodos-scream.md delete mode 100644 .changeset/olive-dodos-hammer.md delete mode 100644 .changeset/quiet-trainers-study.md delete mode 100644 .changeset/quote-plastic-monk.md delete mode 100644 .changeset/rare-paws-listen.md delete mode 100644 .changeset/red-baboons-rhyme.md delete mode 100644 .changeset/rich-games-yawn.md delete mode 100644 .changeset/short-badgers-collect.md delete mode 100644 .changeset/sixty-ants-give.md delete mode 100644 .changeset/smooth-pigs-deny.md delete mode 100644 .changeset/spicy-feet-sparkle.md delete mode 100644 .changeset/stale-cougars-wink.md delete mode 100644 .changeset/techdocs-rotten-crabs-ring.md delete mode 100644 .changeset/techdocs-small-berries-travel.md delete mode 100644 .changeset/thin-icons-kick.md delete mode 100644 .changeset/twelve-gorillas-give.md delete mode 100644 .changeset/wild-dolls-rest.md diff --git a/.changeset/brave-boats-greet.md b/.changeset/brave-boats-greet.md deleted file mode 100644 index e9d3debc35..0000000000 --- a/.changeset/brave-boats-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-lighthouse': patch ---- - -Strip trailing slash from url when creating a new audit. This change prevents duplicate audits from being displayed in the audit list. diff --git a/.changeset/eighty-rats-smash.md b/.changeset/eighty-rats-smash.md deleted file mode 100644 index 4d49d0f203..0000000000 --- a/.changeset/eighty-rats-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-import': patch ---- - -Improve how URLs are analyzed for add/import diff --git a/.changeset/eleven-badgers-wink.md b/.changeset/eleven-badgers-wink.md deleted file mode 100644 index a08c11d257..0000000000 --- a/.changeset/eleven-badgers-wink.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/techdocs-common': patch -'@backstage/plugin-techdocs-backend': patch ---- - -Improve techdocs-common Generator API for it to be used by techdocs-cli. TechDocs generator.run function now takes -an input AND an output directory. Most probably you use techdocs-common via plugin-techdocs-backend, and so there -is no breaking change for you. -But if you use techdocs-common separately, you need to create an output directory and pass into the generator. diff --git a/.changeset/flat-cycles-lay.md b/.changeset/flat-cycles-lay.md deleted file mode 100644 index 2f84b38ffb..0000000000 --- a/.changeset/flat-cycles-lay.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-kubernetes-backend': patch ---- - -Revamped Kubernetes UI and added error reporting/detection diff --git a/.changeset/flat-walls-burn.md b/.changeset/flat-walls-burn.md deleted file mode 100644 index 370b651a4b..0000000000 --- a/.changeset/flat-walls-burn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/integration': minor ---- - -Build out the `ScmIntegrations` class, as well as the individual `*Integration` classes diff --git a/.changeset/friendly-masks-dress.md b/.changeset/friendly-masks-dress.md deleted file mode 100644 index 31405f88eb..0000000000 --- a/.changeset/friendly-masks-dress.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': patch ---- - -Export the `schemaValidator` helper function. diff --git a/.changeset/friendly-rats-wonder.md b/.changeset/friendly-rats-wonder.md deleted file mode 100644 index 5a5b30f98a..0000000000 --- a/.changeset/friendly-rats-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-jenkins': patch ---- - -Handle missing ObjectMetadataAction in Jenkins API diff --git a/.changeset/funny-snails-cry.md b/.changeset/funny-snails-cry.md deleted file mode 100644 index 161a386a8c..0000000000 --- a/.changeset/funny-snails-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -fix to-string breakage of binary files diff --git a/.changeset/generic-catalog-import-descriptions.md b/.changeset/generic-catalog-import-descriptions.md deleted file mode 100644 index 8f28ce4afd..0000000000 --- a/.changeset/generic-catalog-import-descriptions.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-import': patch ---- - -Add more generic descriptions for the catalog-import form. diff --git a/.changeset/giant-geckos-tickle.md b/.changeset/giant-geckos-tickle.md deleted file mode 100644 index a31ebafa55..0000000000 --- a/.changeset/giant-geckos-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Minor updates to display of errors diff --git a/.changeset/gorgeous-poems-wash.md b/.changeset/gorgeous-poems-wash.md deleted file mode 100644 index 602dca79fb..0000000000 --- a/.changeset/gorgeous-poems-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-org': patch ---- - -Fixed - normalizing strings for comparison when ignoring when one is in low case. diff --git a/.changeset/great-vans-happen.md b/.changeset/great-vans-happen.md deleted file mode 100644 index 45d6eb2ee9..0000000000 --- a/.changeset/great-vans-happen.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/cli': patch -'@backstage/create-app': patch -'@backstage/plugin-catalog-backend': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-cloudbuild': patch -'@backstage/plugin-github-actions': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version diff --git a/.changeset/grumpy-trains-juggle.md b/.changeset/grumpy-trains-juggle.md deleted file mode 100644 index 91ba37b72a..0000000000 --- a/.changeset/grumpy-trains-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Fix bug where binary files (`png`, etc.) could not load when using AWS or GCS publisher. diff --git a/.changeset/heavy-owls-float.md b/.changeset/heavy-owls-float.md deleted file mode 100644 index 274876e723..0000000000 --- a/.changeset/heavy-owls-float.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'@backstage/techdocs-common': patch -'@backstage/plugin-techdocs': patch ---- - -Google Cloud authentication in TechDocs has been improved. - -1. `techdocs.publisher.googleGcs.credentials` is now optional. If it is missing, `GOOGLE_APPLICATION_CREDENTIALS` - environment variable (and some other methods) will be used to authenticate. - Read more here https://cloud.google.com/docs/authentication/production - -2. `techdocs.publisher.googleGcs.projectId` is no longer used. You can remove it from your `app-config.yaml`. diff --git a/.changeset/many-dodos-scream.md b/.changeset/many-dodos-scream.md deleted file mode 100644 index 2cae9a3713..0000000000 --- a/.changeset/many-dodos-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-graphiql': patch ---- - -Updated README diff --git a/.changeset/olive-dodos-hammer.md b/.changeset/olive-dodos-hammer.md deleted file mode 100644 index 4901d10cbb..0000000000 --- a/.changeset/olive-dodos-hammer.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-github-actions': patch -'@backstage/plugin-lighthouse': patch -'@backstage/plugin-techdocs': patch ---- - -Remove dependency on `@backstage/core-api`. No plugin should ever depend on that package; it's an internal concern whose important bits are re-exported by `@backstage/core` which is the public facing dependency to use. diff --git a/.changeset/quiet-trainers-study.md b/.changeset/quiet-trainers-study.md deleted file mode 100644 index 2e2b8f3566..0000000000 --- a/.changeset/quiet-trainers-study.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -'example-backend': patch -'@backstage/create-app': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -Bump the gitbeaker dependencies to 28.x. - -To update your own installation, go through the `package.json` files of all of -your packages, and ensure that all dependencies on `@gitbeaker/node` or -`@gitbeaker/core` are at version `^28.0.2`. Then run `yarn install` at the root -of your repo. diff --git a/.changeset/quote-plastic-monk.md b/.changeset/quote-plastic-monk.md deleted file mode 100644 index 231afa33e7..0000000000 --- a/.changeset/quote-plastic-monk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Handle no npm info diff --git a/.changeset/rare-paws-listen.md b/.changeset/rare-paws-listen.md deleted file mode 100644 index 1946cd7f2f..0000000000 --- a/.changeset/rare-paws-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-import': patch ---- - -Remove dependency to `@backstage/plugin-catalog-backend`. diff --git a/.changeset/red-baboons-rhyme.md b/.changeset/red-baboons-rhyme.md deleted file mode 100644 index dcf924e912..0000000000 --- a/.changeset/red-baboons-rhyme.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Enable catalog table actions for all location types. - -The edit button has had support for other providers for a while and there is -no specific reason the View in GitHub cannot work for all locations. This -change also replaces the GitHub icon with the OpenInNew icon. diff --git a/.changeset/rich-games-yawn.md b/.changeset/rich-games-yawn.md deleted file mode 100644 index 1106a8b3de..0000000000 --- a/.changeset/rich-games-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core': patch ---- - -Fix issue where `SidebarItem` with `onClick` and without `to` renders an inaccessible div. It now renders a button. diff --git a/.changeset/short-badgers-collect.md b/.changeset/short-badgers-collect.md deleted file mode 100644 index 25f5a4044f..0000000000 --- a/.changeset/short-badgers-collect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -AWS SDK version bump for TechDocs. diff --git a/.changeset/sixty-ants-give.md b/.changeset/sixty-ants-give.md deleted file mode 100644 index f5cb46bdb7..0000000000 --- a/.changeset/sixty-ants-give.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -AWS SDK version bump for Catalog Backend. diff --git a/.changeset/smooth-pigs-deny.md b/.changeset/smooth-pigs-deny.md deleted file mode 100644 index 702c6ab2de..0000000000 --- a/.changeset/smooth-pigs-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -Allow blank certificates and support logout URLs in the SAML provider. diff --git a/.changeset/spicy-feet-sparkle.md b/.changeset/spicy-feet-sparkle.md deleted file mode 100644 index dcc8a7c5fb..0000000000 --- a/.changeset/spicy-feet-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added experimental `create-github-app` command. diff --git a/.changeset/stale-cougars-wink.md b/.changeset/stale-cougars-wink.md deleted file mode 100644 index 306d92ef8f..0000000000 --- a/.changeset/stale-cougars-wink.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/techdocs-common': patch -'@backstage/plugin-techdocs': patch ---- - -AWS S3 authentication in TechDocs has been improved. - -1. `techdocs.publisher.awsS3.bucketName` is now the only required config. `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are optional. - -2. If `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are missing, the AWS environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be used. There are more better ways of setting up AWS authentication. Read the guide at https://backstage.io/docs/features/techdocs/using-cloud-storage diff --git a/.changeset/techdocs-rotten-crabs-ring.md b/.changeset/techdocs-rotten-crabs-ring.md deleted file mode 100644 index 7eddd8ba6c..0000000000 --- a/.changeset/techdocs-rotten-crabs-ring.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs-backend': patch ---- - -If using Url Reader, cache downloaded source files for 30 minutes. diff --git a/.changeset/techdocs-small-berries-travel.md b/.changeset/techdocs-small-berries-travel.md deleted file mode 100644 index af77ee2a17..0000000000 --- a/.changeset/techdocs-small-berries-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs': patch ---- - -Use `history.pushState` for hash link navigation. diff --git a/.changeset/thin-icons-kick.md b/.changeset/thin-icons-kick.md deleted file mode 100644 index 348f55a8ad..0000000000 --- a/.changeset/thin-icons-kick.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/catalog-model': patch -'@backstage/plugin-catalog-backend': patch ---- - -Implement System, Domain and Resource entity kinds. diff --git a/.changeset/twelve-gorillas-give.md b/.changeset/twelve-gorillas-give.md deleted file mode 100644 index ed13fc773c..0000000000 --- a/.changeset/twelve-gorillas-give.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/catalog-model': patch -'@backstage/plugin-catalog-backend': patch ---- - -Add subcomponentOf to Component kind to represent subsystems of larger components. diff --git a/.changeset/wild-dolls-rest.md b/.changeset/wild-dolls-rest.md deleted file mode 100644 index 33298dbb52..0000000000 --- a/.changeset/wild-dolls-rest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Export all preparers and publishers properly diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 454ec81bfd..0814347c71 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/backend-common +## 0.4.3 + +### Patch Changes + +- Updated dependencies [466354aaa] + - @backstage/integration@0.2.0 + ## 0.4.2 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index ef91389330..c311a140f6 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.4.2", + "version": "0.4.3", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -32,7 +32,7 @@ "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.2", "@backstage/config-loader": "^0.4.1", - "@backstage/integration": "^0.1.5", + "@backstage/integration": "^0.2.0", "@types/cors": "^2.8.6", "@types/express": "^4.17.6", "archiver": "^5.0.2", @@ -66,7 +66,7 @@ } }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/test-utils": "^0.1.5", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 2740b98937..e9246ab533 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,35 @@ # example-backend +## 0.2.11 + +### Patch Changes + +- cc068c0d6: Bump the gitbeaker dependencies to 28.x. + + To update your own installation, go through the `package.json` files of all of + your packages, and ensure that all dependencies on `@gitbeaker/node` or + `@gitbeaker/core` are at version `^28.0.2`. Then run `yarn install` at the root + of your repo. + +- Updated dependencies [68ad5af51] +- Updated dependencies [5a9a7e7c2] +- Updated dependencies [f3b064e1c] +- Updated dependencies [94fdf4955] +- Updated dependencies [cc068c0d6] +- Updated dependencies [ade6b3bdf] +- Updated dependencies [468579734] +- Updated dependencies [cb7af51e7] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] +- Updated dependencies [711ba55a2] + - @backstage/plugin-techdocs-backend@0.5.3 + - @backstage/plugin-kubernetes-backend@0.2.4 + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog-backend@0.5.3 + - @backstage/plugin-scaffolder-backend@0.4.1 + - @backstage/plugin-auth-backend@0.2.10 + - @backstage/backend-common@0.4.3 + ## 0.2.10 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 1e6819b7b8..92b14698b6 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.10", + "version": "0.2.11", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,18 +27,18 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.4.1", - "@backstage/catalog-model": "^0.6.0", + "@backstage/backend-common": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", "@backstage/plugin-app-backend": "^0.3.3", - "@backstage/plugin-auth-backend": "^0.2.7", - "@backstage/plugin-catalog-backend": "^0.5.1", + "@backstage/plugin-auth-backend": "^0.2.10", + "@backstage/plugin-catalog-backend": "^0.5.3", "@backstage/plugin-graphql-backend": "^0.1.4", - "@backstage/plugin-kubernetes-backend": "^0.2.3", + "@backstage/plugin-kubernetes-backend": "^0.2.4", "@backstage/plugin-proxy-backend": "^0.2.3", "@backstage/plugin-rollbar-backend": "^0.1.5", - "@backstage/plugin-scaffolder-backend": "^0.4.0", - "@backstage/plugin-techdocs-backend": "^0.5.0", + "@backstage/plugin-scaffolder-backend": "^0.4.1", + "@backstage/plugin-techdocs-backend": "^0.5.3", "@gitbeaker/node": "^28.0.2", "@octokit/rest": "^18.0.12", "azure-devops-node-api": "^10.1.1", @@ -53,7 +53,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.4.3", + "@backstage/cli": "^0.4.6", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index f6c2477d6c..97f54bbdd2 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/catalog-model +## 0.6.1 + +### Patch Changes + +- f3b064e1c: Export the `schemaValidator` helper function. +- abbee6fff: Implement System, Domain and Resource entity kinds. +- 147fadcb9: Add subcomponentOf to Component kind to represent subsystems of larger components. + ## 0.6.0 ### Minor Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 4fad95e122..01ae2e45b7 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.6.0", + "version": "0.6.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -38,7 +38,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.4.2", + "@backstage/cli": "^0.4.6", "@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 adf041b4bb..738b56d82d 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/cli +## 0.4.6 + +### Patch Changes + +- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version +- 08e9893d2: Handle no npm info +- 9cf71f8bf: Added experimental `create-github-app` command. + ## 0.4.5 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index f6512b6261..4605a6bdff 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.4.5", + "version": "0.4.6", "private": false, "publishConfig": { "access": "public" @@ -113,9 +113,9 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.4.2", + "@backstage/backend-common": "^0.4.3", "@backstage/config": "^0.1.2", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@backstage/theme": "^0.2.2", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index bb13447f94..b9ea855c83 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/core +## 0.4.4 + +### Patch Changes + +- 265a7ab30: Fix issue where `SidebarItem` with `onClick` and without `to` renders an inaccessible div. It now renders a button. + ## 0.4.3 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index b84eb4ff7b..d86ea3b240 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.4.3", + "version": "0.4.4", "private": false, "publishConfig": { "access": "public", @@ -65,7 +65,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.4.4", + "@backstage/cli": "^0.4.6", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^10.4.1", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 5c9b44fd07..8dba8d8fc1 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/create-app +## 0.3.5 + +### Patch Changes + +- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version +- cc068c0d6: Bump the gitbeaker dependencies to 28.x. + + To update your own installation, go through the `package.json` files of all of + your packages, and ensure that all dependencies on `@gitbeaker/node` or + `@gitbeaker/core` are at version `^28.0.2`. Then run `yarn install` at the root + of your repo. + ## 0.3.4 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 9dec615bb7..0c703bcdac 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.4", + "version": "0.3.5", "private": false, "publishConfig": { "access": "public" @@ -37,29 +37,29 @@ "recursive-readdir": "^2.2.2" }, "devDependencies": { - "@backstage/backend-common": "^0.4.2", - "@backstage/catalog-model": "^0.6.0", - "@backstage/cli": "^0.4.5", + "@backstage/backend-common": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", + "@backstage/cli": "^0.4.6", "@backstage/config": "^0.1.2", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-api-docs": "^0.4.2", "@backstage/plugin-app-backend": "^0.3.3", - "@backstage/plugin-auth-backend": "^0.2.9", - "@backstage/plugin-catalog": "^0.2.10", - "@backstage/plugin-catalog-backend": "^0.5.2", - "@backstage/plugin-catalog-import": "^0.3.3", + "@backstage/plugin-auth-backend": "^0.2.10", + "@backstage/plugin-catalog": "^0.2.11", + "@backstage/plugin-catalog-backend": "^0.5.3", + "@backstage/plugin-catalog-import": "^0.3.4", "@backstage/plugin-circleci": "^0.2.5", "@backstage/plugin-explore": "^0.2.2", - "@backstage/plugin-github-actions": "^0.2.6", - "@backstage/plugin-lighthouse": "^0.2.6", + "@backstage/plugin-github-actions": "^0.2.7", + "@backstage/plugin-lighthouse": "^0.2.7", "@backstage/plugin-proxy-backend": "^0.2.3", "@backstage/plugin-rollbar-backend": "^0.1.6", "@backstage/plugin-scaffolder": "^0.3.6", "@backstage/plugin-search": "^0.2.5", - "@backstage/plugin-scaffolder-backend": "^0.4.0", + "@backstage/plugin-scaffolder-backend": "^0.4.1", "@backstage/plugin-tech-radar": "^0.3.2", - "@backstage/plugin-techdocs": "^0.5.2", - "@backstage/plugin-techdocs-backend": "^0.5.2", + "@backstage/plugin-techdocs": "^0.5.3", + "@backstage/plugin-techdocs-backend": "^0.5.3", "@backstage/plugin-user-settings": "^0.2.3", "@backstage/test-utils": "^0.1.6", "@backstage/theme": "^0.2.2", diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 10b30a11ad..2310c7d020 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/integration +## 0.2.0 + +### Minor Changes + +- 466354aaa: Build out the `ScmIntegrations` class, as well as the individual `*Integration` classes + ## 0.1.5 ### Patch Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 258c5c61c6..eff9e541ce 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "0.1.5", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,7 +34,7 @@ "git-url-parse": "^11.4.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/test-utils": "^0.1.5", "@types/jest": "^26.0.7", "msw": "^0.21.2" diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index 6a2ec6cf97..6cf9481327 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/techdocs-common +## 0.3.3 + +### Patch Changes + +- 68ad5af51: Improve techdocs-common Generator API for it to be used by techdocs-cli. TechDocs generator.run function now takes + an input AND an output directory. Most probably you use techdocs-common via plugin-techdocs-backend, and so there + is no breaking change for you. + But if you use techdocs-common separately, you need to create an output directory and pass into the generator. +- 371f67ecd: fix to-string breakage of binary files +- f1e74777a: Fix bug where binary files (`png`, etc.) could not load when using AWS or GCS publisher. +- dbe4450c3: Google Cloud authentication in TechDocs has been improved. + + 1. `techdocs.publisher.googleGcs.credentials` is now optional. If it is missing, `GOOGLE_APPLICATION_CREDENTIALS` + environment variable (and some other methods) will be used to authenticate. + Read more here https://cloud.google.com/docs/authentication/production + + 2. `techdocs.publisher.googleGcs.projectId` is no longer used. You can remove it from your `app-config.yaml`. + +- 5826d0973: AWS SDK version bump for TechDocs. +- b3b9445df: AWS S3 authentication in TechDocs has been improved. + + 1. `techdocs.publisher.awsS3.bucketName` is now the only required config. `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are optional. + + 2. If `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are missing, the AWS environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be used. There are more better ways of setting up AWS authentication. Read the guide at https://backstage.io/docs/features/techdocs/using-cloud-storage + +- Updated dependencies [466354aaa] +- Updated dependencies [f3b064e1c] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/integration@0.2.0 + - @backstage/catalog-model@0.6.1 + - @backstage/backend-common@0.4.3 + ## 0.3.2 ### Patch Changes diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index c95f415ad3..387c75241c 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.3.2", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -37,10 +37,10 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.1.0", - "@backstage/backend-common": "^0.4.2", - "@backstage/catalog-model": "^0.6.0", + "@backstage/backend-common": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", - "@backstage/integration": "^0.1.5", + "@backstage/integration": "^0.2.0", "@google-cloud/storage": "^5.6.0", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", @@ -57,7 +57,7 @@ }, "devDependencies": { "@aws-sdk/types": "3.1.0", - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@types/fs-extra": "^9.0.5", "@types/git-url-parse": "^9.0.0", "@types/js-yaml": "^3.12.5", diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 0bbd96f1a8..39e202eef8 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-catalog": "^0.2.9", "@backstage/theme": "^0.2.2", "@kyma-project/asyncapi-react": "^0.14.2", @@ -49,7 +49,7 @@ "swagger-ui-react": "^3.37.2" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index ce029cded2..f21224eb65 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend +## 0.2.10 + +### Patch Changes + +- 468579734: Allow blank certificates and support logout URLs in the SAML provider. +- Updated dependencies [f3b064e1c] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/backend-common@0.4.3 + ## 0.2.9 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 38d074f7fb..8f88c69487 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.2.9", + "version": "0.2.10", "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.4.2", + "@backstage/backend-common": "^0.4.3", "@backstage/catalog-client": "^0.3.4", - "@backstage/catalog-model": "^0.6.0", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", "@types/express": "^4.17.6", "compression": "^1.7.4", @@ -64,7 +64,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index b60555037c..a3ee4b7eb3 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend +## 0.5.3 + +### Patch Changes + +- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version +- ade6b3bdf: AWS SDK version bump for Catalog Backend. +- abbee6fff: Implement System, Domain and Resource entity kinds. +- 147fadcb9: Add subcomponentOf to Component kind to represent subsystems of larger components. +- Updated dependencies [f3b064e1c] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/backend-common@0.4.3 + ## 0.5.2 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 9a782ac1e5..0ce2e8d37a 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.5.2", + "version": "0.5.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ "dependencies": { "@aws-sdk/client-organizations": "^3.2.0", "@azure/msal-node": "^1.0.0-alpha.8", - "@backstage/backend-common": "^0.4.2", - "@backstage/catalog-model": "^0.6.0", + "@backstage/backend-common": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", "@octokit/graphql": "^4.5.8", "@types/express": "^4.17.6", @@ -57,7 +57,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/test-utils": "^0.1.6", "@types/core-js": "^2.5.4", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index f494949aab..a307095014 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog-import +## 0.3.4 + +### Patch Changes + +- 34a01a171: Improve how URLs are analyzed for add/import +- bc40ccecf: Add more generic descriptions for the catalog-import form. +- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version +- be5ac7fde: Remove dependency to `@backstage/plugin-catalog-backend`. +- Updated dependencies [466354aaa] +- Updated dependencies [f3b064e1c] +- Updated dependencies [c00488983] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/integration@0.2.0 + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + ## 0.3.3 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 1a7d22d551..5e601ad896 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.3.3", + "version": "0.3.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,10 +30,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", - "@backstage/plugin-catalog": "^0.2.10", - "@backstage/integration": "^0.1.5", + "@backstage/catalog-model": "^0.6.1", + "@backstage/core": "^0.4.4", + "@backstage/plugin-catalog": "^0.2.11", + "@backstage/integration": "^0.2.0", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -49,7 +49,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 57a8a9d205..b826346b39 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog +## 0.2.11 + +### Patch Changes + +- c00488983: Enable catalog table actions for all location types. + + The edit button has had support for other providers for a while and there is + no specific reason the View in GitHub cannot work for all locations. This + change also replaces the GitHub icon with the OpenInNew icon. + +- Updated dependencies [f3b064e1c] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/core@0.4.4 + ## 0.2.10 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index a5fa16ed6f..fa6d757f0a 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "0.2.10", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ }, "dependencies": { "@backstage/catalog-client": "^0.3.4", - "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", + "@backstage/core": "^0.4.4", "@backstage/plugin-scaffolder": "^0.3.6", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", @@ -51,7 +51,7 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@microsoft/microsoft-graph-types": "^1.25.0", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 8e0b4743a0..7e3124375a 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-catalog": "^0.2.7", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", @@ -50,7 +50,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index 64e1fe2937..95eaf4a391 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-cloudbuild +## 0.2.6 + +### Patch Changes + +- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version +- Updated dependencies [f3b064e1c] +- Updated dependencies [c00488983] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + ## 0.2.5 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 9434ec70c8..5b55f1df5a 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-cloudbuild", - "version": "0.2.5", + "version": "0.2.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", - "@backstage/plugin-catalog": "^0.2.7", + "@backstage/catalog-model": "^0.6.1", + "@backstage/core": "^0.4.4", + "@backstage/plugin-catalog": "^0.2.11", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index fd0b0e8e7d..f0538720ab 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/config": "^0.1.2", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -55,7 +55,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 5c109d6a9e..613c0cb098 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -42,7 +42,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 3036554fad..81e047e7ab 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -43,7 +43,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index f2202054db..797b63bda9 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index f2537742f4..7aead61ed2 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-github-actions +## 0.2.7 + +### Patch Changes + +- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version +- a6f9dca0d: Remove dependency on `@backstage/core-api`. No plugin should ever depend on that package; it's an internal concern whose important bits are re-exported by `@backstage/core` which is the public facing dependency to use. +- Updated dependencies [f3b064e1c] +- Updated dependencies [c00488983] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + ## 0.2.6 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 771cbcfdd5..9b2d199b1f 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-actions", - "version": "0.2.6", + "version": "0.2.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,9 +32,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", - "@backstage/plugin-catalog": "^0.2.8", + "@backstage/catalog-model": "^0.6.1", + "@backstage/core": "^0.4.4", + "@backstage/plugin-catalog": "^0.2.11", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -49,7 +49,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 28fead7796..61adfe5d67 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -42,7 +42,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index 9c1289febc..fa33128f45 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-graphiql +## 0.2.5 + +### Patch Changes + +- 5a1368ba1: Updated README +- Updated dependencies [265a7ab30] + - @backstage/core@0.4.4 + ## 0.2.4 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 341c4c14f7..f995dc4ebc 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.4", + "version": "0.2.5", "private": false, "publishConfig": { "access": "public", @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -43,7 +43,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index 78792789cf..bcddeedf29 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-jenkins +## 0.3.5 + +### Patch Changes + +- feabc7f0c: Handle missing ObjectMetadataAction in Jenkins API +- Updated dependencies [f3b064e1c] +- Updated dependencies [c00488983] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + ## 0.3.4 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 99d69cf50e..f8c0b3fd76 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins", - "version": "0.3.4", + "version": "0.3.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", - "@backstage/plugin-catalog": "^0.2.7", + "@backstage/catalog-model": "^0.6.1", + "@backstage/core": "^0.4.4", + "@backstage/plugin-catalog": "^0.2.11", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -46,7 +46,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 9b247c174f..eeb710fce4 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes-backend +## 0.2.4 + +### Patch Changes + +- 5a9a7e7c2: Revamped Kubernetes UI and added error reporting/detection +- Updated dependencies [f3b064e1c] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/backend-common@0.4.3 + ## 0.2.3 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 23767e01ac..56c6707427 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.2.3", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,8 +31,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.4.1", - "@backstage/catalog-model": "^0.6.0", + "@backstage/backend-common": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", "@kubernetes/client-node": "^0.13.2", "@types/express": "^4.17.6", @@ -49,7 +49,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.4.2", + "@backstage/cli": "^0.4.6", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index ef5b51cfb9..99373e9ec6 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-kubernetes +## 0.3.4 + +### Patch Changes + +- 5a9a7e7c2: Revamped Kubernetes UI and added error reporting/detection +- 3e7c09c84: Minor updates to display of errors +- Updated dependencies [5a9a7e7c2] +- Updated dependencies [f3b064e1c] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/plugin-kubernetes-backend@0.2.4 + - @backstage/catalog-model@0.6.1 + - @backstage/core@0.4.4 + ## 0.3.3 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 72adab0b7b..41e1954bbb 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.3.3", + "version": "0.3.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", - "@backstage/core": "^0.4.3", - "@backstage/plugin-kubernetes-backend": "^0.2.3", + "@backstage/core": "^0.4.4", + "@backstage/plugin-kubernetes-backend": "^0.2.4", "@backstage/theme": "^0.2.2", "@kubernetes/client-node": "^0.13.2", "@material-ui/core": "^4.11.0", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 42035d1209..b70539f6fa 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-lighthouse +## 0.2.7 + +### Patch Changes + +- cf7df3b1f: Strip trailing slash from url when creating a new audit. This change prevents duplicate audits from being displayed in the audit list. +- a6f9dca0d: Remove dependency on `@backstage/core-api`. No plugin should ever depend on that package; it's an internal concern whose important bits are re-exported by `@backstage/core` which is the public facing dependency to use. +- Updated dependencies [f3b064e1c] +- Updated dependencies [c00488983] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + ## 0.2.6 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 569277054e..6f4892da6c 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-lighthouse", - "version": "0.2.6", + "version": "0.2.7", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", - "@backstage/core": "^0.4.3", - "@backstage/plugin-catalog": "^0.2.7", + "@backstage/core": "^0.4.4", + "@backstage/plugin-catalog": "^0.2.11", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -46,7 +46,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index 629dec635b..6d6eb4ac5c 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 13025d8e83..e1bcf20114 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-org +## 0.3.3 + +### Patch Changes + +- f573cf368: Fixed - normalizing strings for comparison when ignoring when one is in low case. +- Updated dependencies [f3b064e1c] +- Updated dependencies [c00488983] +- Updated dependencies [265a7ab30] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + ## 0.3.2 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 9db6fff348..90cbf2aa57 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.3.2", + "version": "0.3.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,9 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", - "@backstage/plugin-catalog": "^0.2.7", + "@backstage/catalog-model": "^0.6.1", + "@backstage/core": "^0.4.4", + "@backstage/plugin-catalog": "^0.2.11", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -33,7 +33,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 1db99822a2..6bfcf86a89 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -44,7 +44,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 56451b7958..955092c2ee 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-catalog": "^0.2.9", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", @@ -45,7 +45,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 37c9cec4c0..e7bfd4f379 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-catalog": "^0.2.7", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", @@ -47,7 +47,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index ee54a5c794..80330bbce3 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-scaffolder-backend +## 0.4.1 + +### Patch Changes + +- 94fdf4955: Get rid of all usages of @octokit/types, and bump the rest of the octokit dependencies to the latest version +- cc068c0d6: Bump the gitbeaker dependencies to 28.x. + + To update your own installation, go through the `package.json` files of all of + your packages, and ensure that all dependencies on `@gitbeaker/node` or + `@gitbeaker/core` are at version `^28.0.2`. Then run `yarn install` at the root + of your repo. + +- 711ba55a2: Export all preparers and publishers properly +- Updated dependencies [466354aaa] +- Updated dependencies [f3b064e1c] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/integration@0.2.0 + - @backstage/catalog-model@0.6.1 + - @backstage/backend-common@0.4.3 + ## 0.4.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 7855dcd349..134f63f96a 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.4.0", + "version": "0.4.1", "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.4.2", - "@backstage/catalog-model": "^0.6.0", + "@backstage/backend-common": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", - "@backstage/integration": "^0.1.5", + "@backstage/integration": "^0.2.0", "@gitbeaker/core": "^28.0.2", "@gitbeaker/node": "^28.0.2", "@octokit/rest": "^18.0.12", @@ -58,7 +58,7 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/test-utils": "^0.1.5", "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 0d6c1bdb37..655b7f324c 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-catalog": "^0.2.10", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", @@ -50,7 +50,7 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/search/package.json b/plugins/search/package.json index aae095dafe..eb9512e52c 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-catalog": "^0.2.10", "@backstage/catalog-model": "^0.6.0", "@backstage/theme": "^0.2.2", @@ -43,7 +43,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 6a1ef07436..5bf8fe1045 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/plugin-catalog": "^0.2.10", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", @@ -46,7 +46,7 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 8ca178f9fe..78c6343685 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -46,7 +46,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 326b0fe732..6e0663e159 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -43,7 +43,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index d83ff92fd4..9993456436 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-techdocs-backend +## 0.5.3 + +### Patch Changes + +- 68ad5af51: Improve techdocs-common Generator API for it to be used by techdocs-cli. TechDocs generator.run function now takes + an input AND an output directory. Most probably you use techdocs-common via plugin-techdocs-backend, and so there + is no breaking change for you. + But if you use techdocs-common separately, you need to create an output directory and pass into the generator. +- cb7af51e7: If using Url Reader, cache downloaded source files for 30 minutes. +- Updated dependencies [68ad5af51] +- Updated dependencies [f3b064e1c] +- Updated dependencies [371f67ecd] +- Updated dependencies [f1e74777a] +- Updated dependencies [dbe4450c3] +- Updated dependencies [5826d0973] +- Updated dependencies [b3b9445df] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/techdocs-common@0.3.3 + - @backstage/catalog-model@0.6.1 + - @backstage/backend-common@0.4.3 + ## 0.5.2 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 6b0aeec177..7c756641e2 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.5.2", + "version": "0.5.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.4.2", - "@backstage/catalog-model": "^0.6.0", + "@backstage/backend-common": "^0.4.3", + "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", - "@backstage/techdocs-common": "^0.3.2", + "@backstage/techdocs-common": "^0.3.3", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", @@ -45,7 +45,7 @@ "winston": "^3.2.1" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "supertest": "^4.0.2" }, "files": [ diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 8bd79e4139..ee8860da46 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-techdocs +## 0.5.3 + +### Patch Changes + +- dbe4450c3: Google Cloud authentication in TechDocs has been improved. + + 1. `techdocs.publisher.googleGcs.credentials` is now optional. If it is missing, `GOOGLE_APPLICATION_CREDENTIALS` + environment variable (and some other methods) will be used to authenticate. + Read more here https://cloud.google.com/docs/authentication/production + + 2. `techdocs.publisher.googleGcs.projectId` is no longer used. You can remove it from your `app-config.yaml`. + +- a6f9dca0d: Remove dependency on `@backstage/core-api`. No plugin should ever depend on that package; it's an internal concern whose important bits are re-exported by `@backstage/core` which is the public facing dependency to use. +- b3b9445df: AWS S3 authentication in TechDocs has been improved. + + 1. `techdocs.publisher.awsS3.bucketName` is now the only required config. `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are optional. + + 2. If `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are missing, the AWS environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be used. There are more better ways of setting up AWS authentication. Read the guide at https://backstage.io/docs/features/techdocs/using-cloud-storage + +- e5d12f705: Use `history.pushState` for hash link navigation. +- Updated dependencies [68ad5af51] +- Updated dependencies [f3b064e1c] +- Updated dependencies [371f67ecd] +- Updated dependencies [f1e74777a] +- Updated dependencies [dbe4450c3] +- Updated dependencies [c00488983] +- Updated dependencies [265a7ab30] +- Updated dependencies [5826d0973] +- Updated dependencies [b3b9445df] +- Updated dependencies [abbee6fff] +- Updated dependencies [147fadcb9] + - @backstage/techdocs-common@0.3.3 + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + ## 0.5.2 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index db26ee122d..fb26087790 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.5.2", + "version": "0.5.3", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,12 +31,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.6.0", - "@backstage/core": "^0.4.3", - "@backstage/plugin-catalog": "^0.2.9", + "@backstage/catalog-model": "^0.6.1", + "@backstage/core": "^0.4.4", + "@backstage/plugin-catalog": "^0.2.11", "@backstage/test-utils": "^0.1.6", "@backstage/theme": "^0.2.2", - "@backstage/techdocs-common": "^0.3.1", + "@backstage/techdocs-common": "^0.3.3", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", @@ -49,7 +49,7 @@ "sanitize-html": "^1.27.0" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 38b26dd94c..33b1d1aa46 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index d215028960..7ac7dd43b8 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.4.3", + "@backstage/core": "^0.4.4", "@backstage/theme": "^0.2.2", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,7 +41,7 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.4.5", + "@backstage/cli": "^0.4.6", "@backstage/dev-utils": "^0.1.7", "@backstage/test-utils": "^0.1.6", "@testing-library/jest-dom": "^5.10.1", From ed82e83a84b2b639b510a6af9daa26f94393374f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 14:42:48 +0100 Subject: [PATCH 070/144] Add GitHubUrlReader test using CredentialsProvider Co-authored-by: blam --- .../src/reading/GithubUrlReader.test.ts | 116 ++++++++++++++++-- yarn.lock | 2 +- 2 files changed, 108 insertions(+), 10 deletions(-) diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index f842adcf90..1a0f27d28f 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -15,6 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; +import { GithubCredentialsProvider } from '@backstage/integration'; import { msw } from '@backstage/test-utils'; import fs from 'fs'; import { rest } from 'msw'; @@ -28,6 +29,18 @@ const treeResponseFactory = ReadTreeResponseFactory.create({ }); describe('GithubUrlReader', () => { + const mockCredentialsProvider = ({ + getCredentials: jest.fn().mockResolvedValue({ headers: {} }), + } as unknown) as GithubCredentialsProvider; + + const worker = setupServer(); + + msw.setupDefaultHandlers(worker); + + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('implementation', () => { it('rejects unknown targets', async () => { const processor = new GithubUrlReader( @@ -35,7 +48,7 @@ describe('GithubUrlReader', () => { host: 'github.com', apiBaseUrl: 'https://api.github.com', }, - { treeResponseFactory }, + { treeResponseFactory, credentialsProvider: mockCredentialsProvider }, ); await expect( processor.read('https://not.github.com/apa'), @@ -45,11 +58,52 @@ describe('GithubUrlReader', () => { }); }); + describe('read', () => { + it('should use the headers from the credentials provider to the fetch request when doing read', async () => { + expect.assertions(2); + + const mockHeaders = { + Authorization: 'bearer blah', + otherheader: 'something', + }; + + (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + headers: mockHeaders, + }); + + worker.use( + rest.get( + 'https://api.github.com/repos/backstage/mock/tree/contents/?ref=repo', + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBe( + mockHeaders.Authorization, + ); + expect(req.headers.get('otherheader')).toBe( + mockHeaders.otherheader, + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/x-gzip'), + ctx.body('foo'), + ); + }, + ), + ); + + const processor = new GithubUrlReader( + { + host: 'ghe.github.com', + apiBaseUrl: 'https://api.github.com', + }, + { treeResponseFactory, credentialsProvider: mockCredentialsProvider }, + ); + await processor.read( + 'https://ghe.github.com/backstage/mock/tree/blob/repo', + ); + }); + }); + describe('readTree', () => { - const worker = setupServer(); - - msw.setupDefaultHandlers(worker); - const repoBuffer = fs.readFileSync( path.resolve('src', 'reading', '__fixtures__', 'repo.tar.gz'), ); @@ -74,7 +128,7 @@ describe('GithubUrlReader', () => { host: 'github.com', apiBaseUrl: 'https://api.github.com', }, - { treeResponseFactory }, + { treeResponseFactory, credentialsProvider: mockCredentialsProvider }, ); const response = await processor.readTree( @@ -110,7 +164,7 @@ describe('GithubUrlReader', () => { host: 'ghe.github.com', apiBaseUrl: 'https://api.github.com', }, - { treeResponseFactory }, + { treeResponseFactory, credentialsProvider: mockCredentialsProvider }, ); const response = await processor.readTree( @@ -125,13 +179,57 @@ describe('GithubUrlReader', () => { expect(indexMarkdownFile.toString()).toBe('# Test\n'); }); + it('should use the headers from the credentials provider to the fetch request', async () => { + expect.assertions(2); + + const mockHeaders = { + Authorization: 'bearer blah', + otherheader: 'something', + }; + + (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + headers: mockHeaders, + }); + + worker.use( + rest.get( + 'https://ghe.github.com/backstage/mock/archive/repo.tar.gz', + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBe( + mockHeaders.Authorization, + ); + expect(req.headers.get('otherheader')).toBe( + mockHeaders.otherheader, + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/x-gzip'), + ctx.body(repoBuffer), + ); + }, + ), + ); + + const processor = new GithubUrlReader( + { + host: 'ghe.github.com', + apiBaseUrl: 'https://api.github.com', + }, + { treeResponseFactory, credentialsProvider: mockCredentialsProvider }, + ); + + await processor.readTree( + 'https://ghe.github.com/backstage/mock/tree/repo/docs', + ); + }); + it('must specify a branch', async () => { const processor = new GithubUrlReader( { host: 'github.com', apiBaseUrl: 'https://api.github.com', }, - { treeResponseFactory }, + { treeResponseFactory, credentialsProvider: mockCredentialsProvider }, ); await expect( @@ -147,7 +245,7 @@ describe('GithubUrlReader', () => { host: 'github.com', apiBaseUrl: 'https://api.github.com', }, - { treeResponseFactory }, + { treeResponseFactory, credentialsProvider: mockCredentialsProvider }, ); const response = await processor.readTree( diff --git a/yarn.lock b/yarn.lock index 6986104789..e179b16698 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18434,7 +18434,7 @@ modify-values@^1.0.0: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@^2.25.3, moment@^2.26.0, moment@^2.27.0, moment@^2.29.1: +moment@^2.25.3, moment@^2.26.0, moment@^2.27.0: version "2.29.1" resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== From 8b800caaf93f96649777b89b6b5c1142b82b81bc Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 14 Jan 2021 13:32:09 +0100 Subject: [PATCH 071/144] Removes the @types/hemlet dependency We are using hemlet 4.0.0+ which has tpying included. The typing dependency is not needed anymore. --- packages/backend/package.json | 3 +-- .../default-app/packages/backend/package.json.hbs | 3 +-- yarn.lock | 7 ------- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 1e6819b7b8..09a4195a96 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -56,8 +56,7 @@ "@backstage/cli": "^0.4.3", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", - "@types/express-serve-static-core": "^4.17.5", - "@types/helmet": "^0.0.48" + "@types/express-serve-static-core": "^4.17.5" }, "files": [ "dist" 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 7b7429d838..3fed72f07b 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 @@ -45,8 +45,7 @@ "@backstage/cli": "^{{version '@backstage/cli'}}", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", - "@types/express-serve-static-core": "^4.17.5", - "@types/helmet": "^0.0.47" + "@types/express-serve-static-core": "^4.17.5" }, "files": [ "dist" diff --git a/yarn.lock b/yarn.lock index 88c099a5b5..a58546f126 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6479,13 +6479,6 @@ dependencies: "@types/unist" "*" -"@types/helmet@^0.0.48": - version "0.0.48" - resolved "https://registry.npmjs.org/@types/helmet/-/helmet-0.0.48.tgz#e754399d2f4672ba63962e8490efd3edd31d9799" - integrity sha512-C7MpnvSDrunS1q2Oy1VWCY7CDWHozqSnM8P4tFeRTuzwqni+PYOjEredwcqWG+kLpYcgLsgcY3orHB54gbx2Jw== - dependencies: - "@types/express" "*" - "@types/history@*": version "4.7.5" resolved "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860" From 8eaeb326aab74f1373ce533b370cbd80fba018dc Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 14:49:38 +0100 Subject: [PATCH 072/144] Fix spelling --- docs/plugins/github-apps.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 0710f59158..def9e32f4a 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -5,12 +5,12 @@ come with advantages such as higher rate limits and that Backstage can act as an application instead of a user or bot account. It also provides a much clearer and better authorization model as a opposed to -the oauth apps and their respective scopes. +the OAuth apps and their respective scopes. ## Caveats - It's not possible to have multiple backstage GitHub Apps installed in the same - Github organization be managed by Backstage. We currently don't check through + GitHub organization be managed by Backstage. We currently don't check through all the registered GitHub Apps to see which ones are installed for a particular repository. We just respect global Organization installs right now. - App permissions is not managed by Backstage. They're created with some simple @@ -29,7 +29,7 @@ to grant the app more permissions if needed. You can use the `backstage-cli` to create GitHub App' using a manifest file that we provide. This gives us a way to automate some of the work required to create -a github app. +a GitHub app. You can read more about the `backstage-cli create-github-app` method [here](../cli/commands.md#create-github-app) @@ -66,7 +66,7 @@ privateKey: | Once the credentials are stored in a `yaml` file generated by `create-github-app` or manually by following the -[Github Enterprise](#gitHub-enterprise) instructions they can be included in the +[GitHub Enterprise](#gitHub-enterprise) instructions they can be included in the `app-config.yaml` under the integrations section. ```yaml From eb29c605f31389685e631ac17e83cba5839fb939 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 15:00:24 +0100 Subject: [PATCH 073/144] Document default permissions --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index def9e32f4a..6941d76ed4 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -16,7 +16,7 @@ the OAuth apps and their respective scopes. - App permissions is not managed by Backstage. They're created with some simple default permissions which you are free to change as you need, but you will need to update them in the GitHub web console, not in Backstage right now. The - permissions that are defaulted are `metadata + permissions that are defaulted are `metadata:read` and `contents:read`. - The created GitHub App is private by default, this is most likely what you want for github.com but it's recommended to make your application public for GitHub Enterprise in order to share application across your GHE organizations. From db05f7a35862642905a4ef09bae6fa5a647f732f Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Thu, 14 Jan 2021 15:29:21 +0100 Subject: [PATCH 074/144] Add changeset --- .changeset/unlucky-cougars-grin.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/unlucky-cougars-grin.md diff --git a/.changeset/unlucky-cougars-grin.md b/.changeset/unlucky-cougars-grin.md new file mode 100644 index 0000000000..3c48a612e4 --- /dev/null +++ b/.changeset/unlucky-cougars-grin.md @@ -0,0 +1,9 @@ +--- +'@backstage/create-app': patch +--- + +Remove the `@types/helmet` dev dependency from the app template. This +dependency is now unused as the package `helmet` brings its own types. + +To update your existing app, simply remove the `@types/helmet` dependency from +the `package.json` of your backend package. From 1fea88fd05d3ecc3ccd83c723370b1d4e6f38478 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Jan 2021 16:04:56 +0100 Subject: [PATCH 075/144] plugin/kubernetes: fix assets location to make sure they're included in the output bundle --- .changeset/late-rings-decide.md | 5 +++++ plugins/kubernetes/{ => src}/assets/emptystate.svg | 2 +- .../src/components/ErrorReporting/ErrorReporting.tsx | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/late-rings-decide.md rename plugins/kubernetes/{ => src}/assets/emptystate.svg (98%) diff --git a/.changeset/late-rings-decide.md b/.changeset/late-rings-decide.md new file mode 100644 index 0000000000..327d9698f8 --- /dev/null +++ b/.changeset/late-rings-decide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes': patch +--- + +Fixed an issue where assets weren't properly bundled in the published package. diff --git a/plugins/kubernetes/assets/emptystate.svg b/plugins/kubernetes/src/assets/emptystate.svg similarity index 98% rename from plugins/kubernetes/assets/emptystate.svg rename to plugins/kubernetes/src/assets/emptystate.svg index fa7f19123e..f01a74f374 100644 --- a/plugins/kubernetes/assets/emptystate.svg +++ b/plugins/kubernetes/src/assets/emptystate.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx b/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx index 8243d48263..21f66f28c0 100644 --- a/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx +++ b/plugins/kubernetes/src/components/ErrorReporting/ErrorReporting.tsx @@ -17,7 +17,7 @@ import * as React from 'react'; import { Table, TableColumn, InfoCard } from '@backstage/core'; import { DetectedError, DetectedErrorsByCluster } from '../../error-detection'; import { Chip, Typography, Grid } from '@material-ui/core'; -import EmptyStateImage from '../../../assets/emptystate.svg'; +import EmptyStateImage from '../../assets/emptystate.svg'; type ErrorReportingProps = { detectedErrors: DetectedErrorsByCluster; From 0b135e7e01f2e8cabfad9deb3b9e80c4ca265c8d Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 16:22:08 +0100 Subject: [PATCH 076/144] Add changeset for GitHub apps support --- .changeset/clever-timers-thank.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .changeset/clever-timers-thank.md diff --git a/.changeset/clever-timers-thank.md b/.changeset/clever-timers-thank.md new file mode 100644 index 0000000000..ffe39749de --- /dev/null +++ b/.changeset/clever-timers-thank.md @@ -0,0 +1,25 @@ +--- +'@backstage/backend-common': patch +'@backstage/integration': patch +--- + +Add support for Github Apps authentication for backend plugins. + +`GithubCredentialsProvider` requests and caches GitHub credentials based on a repository or organization url. + +The `GithubCredentialsProvider` class should be considered stateful since tokens will be cached internally. +Consecutive calls to get credentials will return the same token, tokens older than 50 minutes will be considered expired and reissued. +`GithubCredentialsProvider` will default to the configured access token if no GitHub Apps are configured. + +More information on how to create and configure a GitHub App to use with backstage can be found in the documentation. + +Usage: + +```javascript +const credentialsProvider = new GithubCredentialsProvider(config); +const { token, headers } = await credentialsProvider.getCredentials({ + url: 'https://github.com/', +}); +``` + +Updates `GithubUrlReader` to use the GithubCredentialsProvider. From 83a9bb335feca14c4bbf266870ce8d462043686c Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 16:28:32 +0100 Subject: [PATCH 077/144] Fix spelling error --- .changeset/clever-timers-thank.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/clever-timers-thank.md b/.changeset/clever-timers-thank.md index ffe39749de..b3b8ad62ad 100644 --- a/.changeset/clever-timers-thank.md +++ b/.changeset/clever-timers-thank.md @@ -22,4 +22,4 @@ const { token, headers } = await credentialsProvider.getCredentials({ }); ``` -Updates `GithubUrlReader` to use the GithubCredentialsProvider. +Updates `GithubUrlReader` to use the `GithubCredentialsProvider`. From 12e166655dd3a762515a88a5df9d62323ca5ac62 Mon Sep 17 00:00:00 2001 From: Guillermo Manzo Date: Thu, 14 Jan 2021 07:29:27 -0800 Subject: [PATCH 078/144] Update ADOPTERS.md --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index 493bf499af..b0567155bb 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -17,3 +17,4 @@ | [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | | [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | | [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com)| [Mike Turner](miturner@expediagroup.com), [Sneha Kumar](snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo) | EG Common Developer Toolkit From 38545afbfa36606d964847eba0929dcf6f591b8a Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 10:30:55 -0500 Subject: [PATCH 079/144] Add Bad Request test --- .../src/service/KubernetesFetcher.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts b/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts index 29233cf7fd..8d29439539 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFetcher.test.ts @@ -197,6 +197,27 @@ describe('KubernetesClientProvider', () => { }); // they're in testErrorResponse // eslint-disable-next-line jest/expect-expect + it('should return pods, bad request error', async () => { + await testErrorResponse( + { + response: { + statusCode: 400, + request: { + uri: { + pathname: '/some/path', + }, + }, + }, + }, + { + errorType: 'BAD_REQUEST', + resourcePath: '/some/path', + statusCode: 400, + }, + ); + }); + // they're in testErrorResponse + // eslint-disable-next-line jest/expect-expect it('should return pods, unauthorized error', async () => { await testErrorResponse( { From 63a706aed2ef27a117926c05e5fd81e8c39f18f0 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 16:31:54 +0100 Subject: [PATCH 080/144] Fix spelling error --- .changeset/clever-timers-thank.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/clever-timers-thank.md b/.changeset/clever-timers-thank.md index b3b8ad62ad..b5b75c0409 100644 --- a/.changeset/clever-timers-thank.md +++ b/.changeset/clever-timers-thank.md @@ -3,7 +3,7 @@ '@backstage/integration': patch --- -Add support for Github Apps authentication for backend plugins. +Add support for GitHub Apps authentication for backend plugins. `GithubCredentialsProvider` requests and caches GitHub credentials based on a repository or organization url. From 3bb5113a928316702989bb9017d29f02717248f4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Jan 2021 15:32:34 +0000 Subject: [PATCH 081/144] Version Packages --- .changeset/late-rings-decide.md | 5 ----- plugins/kubernetes/CHANGELOG.md | 6 ++++++ plugins/kubernetes/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/late-rings-decide.md diff --git a/.changeset/late-rings-decide.md b/.changeset/late-rings-decide.md deleted file mode 100644 index 327d9698f8..0000000000 --- a/.changeset/late-rings-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes': patch ---- - -Fixed an issue where assets weren't properly bundled in the published package. diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 99373e9ec6..997676bef7 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-kubernetes +## 0.3.5 + +### Patch Changes + +- 1fea88fd0: Fixed an issue where assets weren't properly bundled in the published package. + ## 0.3.4 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 41e1954bbb..554bfe69ab 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.3.4", + "version": "0.3.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", From 93be0f55e32d5aadb2ac5daa5b64adb4d882879d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Jan 2021 16:34:59 +0100 Subject: [PATCH 082/144] docs/composability: fooRootRouteRef -> fooPageRouteRef --- docs/plugins/composability.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index c23ab2882d..207540b989 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -139,7 +139,7 @@ a component: export const FooPage = plugin.provide( createRoutableExtension({ component: () => import('./components/FooPage').then(m => m.FooPage), - mountPoint: fooRouteRef, + mountPoint: fooPageRouteRef, }), ); ``` @@ -213,9 +213,9 @@ const appRoutes = ( We'll assume that `FooPage` and `BarPage` are routable extensions, exported by the `fooPlugin` and `barPlugin` respectively. Since the `FooPage` is a routable extension it has a `RouteRef` assigned as its mount point, which we'll refer to -as `fooRootRouteRef`. +as `fooPageRouteRef`. -Given the above example, the `fooRootRouteRef` will be associated with the +Given the above example, the `fooPageRouteRef` will be associated with the `'/foo'` route. The path is no longer accessible via the `path` property of the `RouteRef` though, as the routing structure is tied to the app's react tree. We instead use the new `useRouteRef` hook if we want to create a concrete link to @@ -225,14 +225,14 @@ like this: ```tsx const MyComponent = () => { - const fooRoute = useRouteRef(fooRouteRef); + const fooRoute = useRouteRef(fooPageRouteRef); return Link to Foo; }; ``` Now let's assume that we want to link from the `BarPage` to the `FooPage`. Before the introduction of the new composability system, we would do this by -importing the `fooRootRouteRef` from the `fooPlugin`. This created an +importing the `fooPageRouteRef` from the `fooPlugin`. This created an unnecessary dependency on the plugin, and also provided little flexibility in allowing the app to tie plugins together, with the links instead being dictated by the plugins themselves. To solve this, we introduce `ExternalRouteRef`s. Much @@ -285,7 +285,7 @@ like this: // In foo-plugin export const fooPlugin = createPlugin({ routes: { - root: fooRootRouteRef, + root: fooPageRouteRef, }, ... }) From 065498367d1310d36afbbbee2f0fec41662afb3d Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 14 Jan 2021 16:37:31 +0100 Subject: [PATCH 083/144] Find installation after try/catch --- .../src/github/GithubCredentialsProvider.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index 04b38d6e27..2dba69c392 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -112,25 +112,20 @@ class GithubAppManager { private async getInstallationData(owner: string): Promise { // List all installations using the last used etag. // Return cached InstallationData if error with status 304 is thrown. - let installation; try { this.installations = await this.appClient.apps.listInstallations({ headers: { 'If-None-Match': this.installations?.headers.etag, }, }); - - installation = this.installations.data.find( - inst => inst.account?.login === owner, - ); } catch (error) { if (error.status !== 304) { throw error; } - installation = this.installations?.data.find( - inst => inst.account?.login === owner, - ); } + const installation = this.installations?.data.find( + inst => inst.account?.login === owner, + ); if (installation) { return { installationId: installation.id, From 487eb593217be97857833887c9b1937f185a53b2 Mon Sep 17 00:00:00 2001 From: Guillermo Manzo Date: Thu, 14 Jan 2021 07:44:26 -0800 Subject: [PATCH 084/144] Update ADOPTERS.md --- ADOPTERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index b0567155bb..37bd7a57f1 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -17,4 +17,4 @@ | [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | | [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | | [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com)| [Mike Turner](miturner@expediagroup.com), [Sneha Kumar](snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo) | EG Common Developer Toolkit +| [Expedia Group](https://www.expediagroup.com)| [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo) | EG Common Developer Toolkit From 68cf50c52372d1ce4964465abb108f9ae96285ee Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Jan 2021 16:36:41 +0100 Subject: [PATCH 085/144] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/composability.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index 207540b989..a496217875 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -10,7 +10,7 @@ description: Documentation and migration instructions for new composability APIs This page describes the new composability system that was recently introduced in Backstage, and it does so from the perspective of the existing patterns and APIs. As the new system is solidified and existing code is ported, this page -will removed and replaced with a more direct description of the composability +will be removed and replaced with a more direct description of the composability system. For now, the primary purpose of this documentation is to aid in the migration of existing plugins, but it does cover the migration of apps as well. @@ -232,7 +232,7 @@ const MyComponent = () => { Now let's assume that we want to link from the `BarPage` to the `FooPage`. Before the introduction of the new composability system, we would do this by -importing the `fooPageRouteRef` from the `fooPlugin`. This created an +importing the `fooPageRouteRef` exported by the `fooPlugin`. This created an unnecessary dependency on the plugin, and also provided little flexibility in allowing the app to tie plugins together, with the links instead being dictated by the plugins themselves. To solve this, we introduce `ExternalRouteRef`s. Much @@ -272,7 +272,8 @@ createApp({ ``` Given the above binding, using `useRouteRef(headerLinkRouteRef)` within the -`barPlugin` will let us create a link whatever path the `FooPage` is mounted at. +`barPlugin` will let us create a link to whatever path the `FooPage` is mounted +at. Note that we are not importing and using the `RouteRef`s directly in the app, and instead rely on the plugin instance to access routes of the plugins. This is @@ -302,7 +303,7 @@ export const barPlugin = createPlugin({ Also note that you almost always want to create the route references themselves in a different file than the one that creates the plugin instance, for example a top-level `routes.ts`. This is to avoid circular imports when you use the route -references from other parts of the app. +references from other parts of the same plugin. ### Parameterized Routes @@ -347,11 +348,11 @@ entity. A `switch` or `if` / `else if` chain is then used to select what children should be rendered based on information in the entity. This pattern will no longer work with the new composability system, and in -general is very difficult to build any form declarative model around, as it +general is very difficult to build any form of declarative model around, as it depends on runtime execution. To help replace existing code, a new `EntitySwitch` component has been added to the `@backstage/catalog` plugin, -which grabs the selected entity from context, and selects at most one element to -render using a list of `EntitySwitch.Case`s children. +which grabs the selected entity from a context, and selects at most one element +to render using a list of `EntitySwitch.Case` children. For example, if you want all entities of kind `"Template"` to be rendered with a `MyTemplate` component, and all other entities to be rendered with a `MyOther` @@ -530,8 +531,8 @@ It would be ported to this: ``` In addition to the renaming, the `element` prop has been moved to `children`. -Also note that the `/*` suffix has been remove from the `"/kubernetes"` path, as -it's now added automatically. +Also note that the `/*` suffix has been removed from the `"/kubernetes"` path, +as it's now added automatically. Usage of the `EntityLayout` component is required to be able to properly discover routes, and so it is required to apply this change before you can start From d54857099dfcb49e2aa3664cd1efb96c611bbfcb Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 10:52:53 -0500 Subject: [PATCH 086/144] Add changeset --- .changeset/bright-icons-repair.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/bright-icons-repair.md diff --git a/.changeset/bright-icons-repair.md b/.changeset/bright-icons-repair.md new file mode 100644 index 0000000000..8c3f43403a --- /dev/null +++ b/.changeset/bright-icons-repair.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Support HTTP 400 Bad Request from Kubernetes API From 68a8c6204722e0fbe0ec7e301d221eafed6f52f6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 14 Jan 2021 17:10:06 +0100 Subject: [PATCH 087/144] format ADOPTERS.md + add vocab --- .github/styles/vocab.txt | 3 +++ ADOPTERS.md | 40 ++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index a71887f796..cb0f487bfd 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -70,6 +70,7 @@ Ek env Env eslint +Expedia facto failover Figma @@ -108,6 +109,7 @@ Kaewkasi Knex kubectl kubernetes +Kumar learnings lerna Lerna @@ -195,6 +197,7 @@ semlas semver Serverless Sinon +Sneha Snyk sourcemaps sparklines diff --git a/ADOPTERS.md b/ADOPTERS.md index 37bd7a57f1..f7d1114008 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,20 +1,20 @@ -| Organization | Contact | Description of Use | -| -------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | -| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | -| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. | -| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | -| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | -| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | -| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | -| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | -| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | -| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | -| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | -| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | -| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | -| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | -| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | -| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | -| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | -| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | -| [Expedia Group](https://www.expediagroup.com)| [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo) | EG Common Developer Toolkit +| Organization | Contact | Description of Use | +| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| [Spotify](https://www.spotify.com) | [@leemills83](https://github.com/leemills83) | Main interface towards all of Spotify's infrastructure and technical documentation. | +| [bol.com](https://www.bol.com) | [@RoyJacobs](https://github.com/RoyJacobs) | Initial work being done to unify platform tooling. | +| [DFDS](https://www.dfds.com) | [@carlsendk](https://github.com/carlsendk) | V2 self-service platform. | +| [Roadie](https://roadie.io) | [@dtuite](https://github.com/dtuite) | Hosted, managed Backstage with easy set-up | +| [Roku](https://www.roku.com) | [@timurista](https://github.com/timurista) | Initial work on Cloud engineering service platform. | +| [SDA SE](https://sda.se) | [@Fox32](https://github.com/Fox32) | Central place for developing and sharing services in our insurance ecosystem. | +| [H-E-B](https://www.heb.com) | [@german-j-rodriguez](https://github.com/german-j-rodriguez) | Initial work on Engineering Portal service platform. | +| [American Airlines](https://www.aa.com) | [@paulpach](https://github.com/paulpach) | Central place for developers to develop and maintain applications | +| [Kiwi.com](https://kiwi.com) | [@aexvir](https://github.com/aexvir) | Replacing the frontend of [The Zoo](https://github.com/kiwicom/the-zoo), their service registry. | +| [Voi](https://www.voiscooters.com/) | [@K-Phoen](https://github.com/K-Phoen) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | +| [Talkdesk](https://www.talkdesk.com) | [@jaime-talkdesk](https://github.com/jaime-talkdesk) | Initial work for Engineering Portal and Self Provisioning to R&D | +| [Wealthsimple](https://www.wealthsimple.com) | [@andrewthauer](https://github.com/andrewthauer) | Developer portal, service catalog, documentation and tooling | +| [Grab](https://www.grab.com) | [@althafh](https://github.com/althafh) | Initial work as a unified interface for all of Grab's internal tooling | +| [Telenor Sweden](https://www.telenor.se) | [@O5ten](https://github.com/O5ten) | Building a developer portal for scaffolding projects towards our unified build environment and microservice stacks | +| [Fiverr](https://www.fiverr.com) | [@nirga](https://github.com/nirga) | Unifying separate tools that developers are using today (i.e. monitoring, dead letter queues management, etc.) into a single platform. | +| [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | +| [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | +| [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo) | EG Common Developer Toolkit | From be1e8af024e21f96b8124c4f25e54258bd7b8c75 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 22:17:12 -0500 Subject: [PATCH 088/144] Reduce log noise --- plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts index b7e9c48fe5..ba97232810 100644 --- a/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts +++ b/plugins/catalog-backend/src/ingestion/HigherOrderOperations.ts @@ -186,7 +186,7 @@ export class HigherOrderOperations implements HigherOrderOperation { throw e; } - this.logger.info(`Posting update success markers`); + this.logger.debug(`Posting update success markers`); await this.locationsCatalog.logUpdateSuccess( location.id, From e4b4740095ac67fc775d6b57c4c01f1ad42f67db Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 22:23:15 -0500 Subject: [PATCH 089/144] Add changeset --- .changeset/spotty-moons-tap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-moons-tap.md diff --git a/.changeset/spotty-moons-tap.md b/.changeset/spotty-moons-tap.md new file mode 100644 index 0000000000..facb76a296 --- /dev/null +++ b/.changeset/spotty-moons-tap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Reduce log noise on locations refresh From ad838c02f73f7dcec230988f96472a89467931eb Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Thu, 14 Jan 2021 22:25:30 -0500 Subject: [PATCH 090/144] Add changeset --- .changeset/spotty-moons-tap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-moons-tap.md diff --git a/.changeset/spotty-moons-tap.md b/.changeset/spotty-moons-tap.md new file mode 100644 index 0000000000..facb76a296 --- /dev/null +++ b/.changeset/spotty-moons-tap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Reduce log noise on locations refresh From bd18e6528ef5824eaacf74aec38155d3e9dcfb36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Jan 2021 04:59:23 +0000 Subject: [PATCH 091/144] chore(deps): bump @rjsf/material-ui from 2.4.0 to 2.4.1 Bumps [@rjsf/material-ui](https://github.com/rjsf-team/react-jsonschema-form) from 2.4.0 to 2.4.1. - [Release notes](https://github.com/rjsf-team/react-jsonschema-form/releases) - [Commits](https://github.com/rjsf-team/react-jsonschema-form/compare/v2.4.0...v2.4.1) Signed-off-by: dependabot[bot] --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 955b6305c3..9abad80835 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2436,7 +2436,7 @@ to-fast-properties "^2.0.0" "@backstage/catalog-model@^0.2.0": - version "0.6.0" + version "0.6.1" dependencies: "@backstage/config" "^0.1.2" "@types/json-schema" "^7.0.5" @@ -2447,7 +2447,7 @@ yup "^0.29.3" "@backstage/catalog-model@^0.3.0": - version "0.6.0" + version "0.6.1" dependencies: "@backstage/config" "^0.1.2" "@types/json-schema" "^7.0.5" @@ -2458,7 +2458,7 @@ yup "^0.29.3" "@backstage/core@^0.3.0": - version "0.4.3" + version "0.4.4" dependencies: "@backstage/config" "^0.1.2" "@backstage/core-api" "^0.2.8" @@ -5024,9 +5024,9 @@ shortid "^2.2.14" "@rjsf/material-ui@^2.4.0": - version "2.4.0" - resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-2.4.0.tgz#1b5859298bf3f61137d7b05084f058a775d6fd73" - integrity sha512-U8F/suzg4MuV+8mK1/ufs0Y6c3O8hc1wnuD2IKoOVJvegGfz5JCafyoyGAW6iyuT1DZBMPzVWEqfiuYPmoE7pw== + version "2.4.1" + resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-2.4.1.tgz#b0dedff8877b114147e298966ca3faba895a7a62" + integrity sha512-pZaWL5Dw+km8S0hFLIK1lRHaeNtheMxTF2mZrWhf6HlGWCTGkQJnXta2UgshJN/nKtZPgO1L4FKz42Eun9nnhg== "@roadiehq/backstage-plugin-buildkite@^0.1.3": version "0.1.3" From a594a725767dd9a9c4ebe0fd282a07c023eaf23b Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Fri, 15 Jan 2021 09:26:26 +0100 Subject: [PATCH 092/144] techdocs-common: Allow techdocs-cli to import in a non-backstage environment techdocs-common has a dependency on @backstage/plugin-techdocs-backend. This prohibits techdocs-cli to import it and generate/publish docs on a CI/CD environment to an external storage --- .changeset/techdocs-mean-items-behave.md | 5 ++++ .../src/stages/publish/local.ts | 28 ++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 .changeset/techdocs-mean-items-behave.md diff --git a/.changeset/techdocs-mean-items-behave.md b/.changeset/techdocs-mean-items-behave.md new file mode 100644 index 0000000000..77679c39d4 --- /dev/null +++ b/.changeset/techdocs-mean-items-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/techdocs-common': patch +--- + +@backstage/techdocs-common can now be imported in an environment without @backstage/plugin-techdocs-backend being installed. diff --git a/packages/techdocs-common/src/stages/publish/local.ts b/packages/techdocs-common/src/stages/publish/local.ts index 2739940530..de670cfba5 100644 --- a/packages/techdocs-common/src/stages/publish/local.ts +++ b/packages/techdocs-common/src/stages/publish/local.ts @@ -16,6 +16,8 @@ import fetch from 'cross-fetch'; import express from 'express'; import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; import { Logger } from 'winston'; import { Entity, EntityName } from '@backstage/catalog-model'; import { @@ -25,10 +27,20 @@ import { import { Config } from '@backstage/config'; import { PublisherBase, PublishRequest, PublishResponse } from './types'; -const staticDocsDir = resolvePackagePath( - '@backstage/plugin-techdocs-backend', - 'static/docs', -); +// TODO: Use a more persistent storage than node_modules or /tmp directory. +// Make it configurable with techdocs.publisher.local.publishDirectory +let staticDocsDir = ''; +try { + staticDocsDir = resolvePackagePath( + '@backstage/plugin-techdocs-backend', + 'static/docs', + ); +} catch (err) { + // This will most probably never be used. + // The try/catch is introduced so that techdocs-cli can import @backstage/techdocs-common + // on CI/CD without installing techdocs backend plugin. + staticDocsDir = os.tmpdir(); +} /** * Local publisher which uses the local filesystem to store the generated static files. It uses a directory @@ -39,6 +51,9 @@ export class LocalPublish implements PublisherBase { private readonly logger: Logger; private readonly discovery: PluginEndpointDiscovery; + // TODO: Use a static fromConfig method to create a LocalPublish instance, similar to aws/gcs publishers. + // Move the logic of setting staticDocsDir based on config over to fromConfig, + // and set the value as a class parameter. constructor( config: Config, logger: Logger, @@ -52,9 +67,8 @@ export class LocalPublish implements PublisherBase { publish({ entity, directory }: PublishRequest): Promise { const entityNamespace = entity.metadata.namespace ?? 'default'; - const publishDir = resolvePackagePath( - '@backstage/plugin-techdocs-backend', - 'static/docs', + const publishDir = path.join( + staticDocsDir, entityNamespace, entity.kind, entity.metadata.name, From 6037fa75c58bfe7ffc789ddddb1680c99b8344f6 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 10:41:07 +0100 Subject: [PATCH 093/144] Set accept header and api base url --- .../src/github/GithubCredentialsProvider.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index 2dba69c392..cb928a4a6c 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -51,6 +51,15 @@ class Cache { date.diff(DateTime.local(), 'minutes').minutes > 50; } +/** + * This accept header is required when calling App APIs in GitHub Enterprise. + * It has no effect on calls to github.com and can probably be removed entierly + * once GitHub Apps is out of preview. + */ +const HEADERS = { + Accept: 'application/vnd.github.machine-man-preview+json', +}; + // GithubAppManager issues tokens for a speicifc GitHub App class GithubAppManager { private readonly appClient: Octokit; @@ -58,12 +67,14 @@ class GithubAppManager { private installations?: RestEndpointMethodTypes['apps']['listInstallations']['response']; private readonly cache = new Cache(); - constructor(config: GithubAppConfig) { + constructor(config: GithubAppConfig, baseUrl?: string) { this.baseAuthConfig = { appId: config.appId, privateKey: config.privateKey, }; this.appClient = new Octokit({ + baseUrl, + headers: HEADERS, authStrategy: createAppAuth, auth: this.baseAuthConfig, }); @@ -85,7 +96,6 @@ class GithubAppManager { .join('/')} is suspended`, ); } - if (repositorySelection !== 'all' && !repo) { throw new Error( `The Backstage GitHub application used in the ${owner} organization must be installed for the entire organization to be able to issue credentials without a specified repository.`, @@ -99,9 +109,9 @@ class GithubAppManager { return this.cache.getOrCreateToken(cacheKey, async () => { const result = await this.appClient.apps.createInstallationAccessToken({ installation_id: installationId, + headers: HEADERS, repositories, }); - return { token: result.data.token, expiresAt: DateTime.fromISO(result.data.expires_at), @@ -116,6 +126,7 @@ class GithubAppManager { this.installations = await this.appClient.apps.listInstallations({ headers: { 'If-None-Match': this.installations?.headers.etag, + Accept: HEADERS.Accept, }, }); } catch (error) { @@ -133,7 +144,6 @@ class GithubAppManager { repositorySelection: installation.repository_selection, }; } - const notFoundError = new Error( `No app installation found for ${owner} in ${this.baseAuthConfig.appId}`, ); @@ -147,7 +157,8 @@ export class GithubAppCredentialsMux { private readonly apps: GithubAppManager[]; constructor(config: GitHubIntegrationConfig) { - this.apps = config.apps?.map(ac => new GithubAppManager(ac)) ?? []; + this.apps = + config.apps?.map(ac => new GithubAppManager(ac, config.apiBaseUrl)) ?? []; } async getAppToken(owner: string, repo?: string): Promise { From 057cb8e84fe235dcf00e64f733e2d948032ef83f Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Fri, 15 Jan 2021 10:45:39 +0100 Subject: [PATCH 094/144] Backstage Glossary Documentation (#4073) * add Backstage Glossary documentation * Update docs/glossary.md Co-authored-by: Himanshu Mishra * Update docs/glossary.md Co-authored-by: Adam Harvey * Update docs/glossary.md Co-authored-by: Adam Harvey * updates from feedback Co-authored-by: Himanshu Mishra Co-authored-by: Adam Harvey --- docs/glossary.md | 21 +++++++++++++++++++++ microsite/sidebars.json | 1 + mkdocs.yml | 1 + 3 files changed, 23 insertions(+) create mode 100644 docs/glossary.md diff --git a/docs/glossary.md b/docs/glossary.md new file mode 100644 index 0000000000..dc67b2aae2 --- /dev/null +++ b/docs/glossary.md @@ -0,0 +1,21 @@ +--- +id: Glossary +title: Backstage Glossary +# prettier-ignore +description: List of all the terms, abbreviations, and phrases used in Backstage, together with their explanations. +--- + +The Backstage Glossary lists all the terms, abbreviations, and phrases used in +Backstage, together with their explanations. We encourage you to use the +terminology below for clarity and consistency when discussing Backstage. + +### Backstage User Profiles + +There are three main user profiles for Backstage: the integrator, the +contributor, and the software engineer. + +| Term | Explanation | +| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Integrator | The **integrator** hosts the Backstage app and configures which plugins are available to use in the app. | +| Contributor | The **contributor** adds functionality to the app by writing plugins. | +| Software Engineer | The **software engineer** uses the app's functionality and interacts with its plugins. In practice, this profile covers the various roles that help deliver software, from the Software Engineer themselves, to Designers, Data Scientists, Product Owners, Engineering Managers, etc. | diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 3d6417cc65..07245e75e5 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -176,6 +176,7 @@ ], "Contribute": ["../CONTRIBUTING"], "Support": ["support/support", "support/project-structure"], + "Glossary": ["glossary"], "FAQ": ["FAQ"] } } diff --git a/mkdocs.yml b/mkdocs.yml index 29198963e3..fce5fc1a42 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -117,4 +117,5 @@ nav: - Support: - 'support/support.md' - 'support/project-structure.md' + - Glossary: glossary.md - FAQ: FAQ.md From 33846acfcb4f085d8bd05f372220248a61e56f06 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 15 Jan 2021 11:13:58 +0100 Subject: [PATCH 095/144] Display the owner and system as links to the entity pages in the about card --- .changeset/healthy-comics-drive.md | 5 ++ .../src/components/AboutCard/AboutContent.tsx | 43 ++++++---- .../EntityRefLink/EntityRefLink.test.tsx | 86 +++++++++++++++++++ .../EntityRefLink/EntityRefLink.tsx | 75 ++++++++++++++++ .../src/components/EntityRefLink/index.ts | 16 ++++ 5 files changed, 206 insertions(+), 19 deletions(-) create mode 100644 .changeset/healthy-comics-drive.md create mode 100644 plugins/catalog/src/components/EntityRefLink/EntityRefLink.test.tsx create mode 100644 plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx create mode 100644 plugins/catalog/src/components/EntityRefLink/index.ts diff --git a/.changeset/healthy-comics-drive.md b/.changeset/healthy-comics-drive.md new file mode 100644 index 0000000000..3847274eda --- /dev/null +++ b/.changeset/healthy-comics-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Display the owner and system as links to the entity pages in the about card. diff --git a/plugins/catalog/src/components/AboutCard/AboutContent.tsx b/plugins/catalog/src/components/AboutCard/AboutContent.tsx index b8d5d84316..725217db9a 100644 --- a/plugins/catalog/src/components/AboutCard/AboutContent.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutContent.tsx @@ -14,15 +14,16 @@ * limitations under the License. */ -import React from 'react'; -import { Grid, Typography, Chip, makeStyles } from '@material-ui/core'; -import { AboutField } from './AboutField'; import { Entity, - ENTITY_DEFAULT_NAMESPACE, RELATION_OWNED_BY, - serializeEntityRef, + RELATION_PART_OF, } from '@backstage/catalog-model'; +import { Chip, Grid, makeStyles, Typography } from '@material-ui/core'; +import React from 'react'; +import { EntityRefLink } from '../EntityRefLink'; +import { getEntityRelations } from '../getEntityRelations'; +import { AboutField } from './AboutField'; const useStyles = makeStyles({ description: { @@ -36,6 +37,11 @@ type Props = { export const AboutContent = ({ entity }: Props) => { const classes = useStyles(); + const [partOfSystemRelation] = getEntityRelations(entity, RELATION_PART_OF, { + kind: 'system', + }); + const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); + return ( @@ -43,22 +49,21 @@ export const AboutContent = ({ entity }: Props) => { {entity?.metadata?.description || 'No description'} + + {ownedByRelations.map((t, i) => [ + i > 0 && ', ', + , + ])} + r.type === RELATION_OWNED_BY) - .map(({ target: { kind, name, namespace } }) => - // TODO(Rugvip): we want to provide some utils for this - serializeEntityRef({ - kind, - name, - namespace: - namespace === ENTITY_DEFAULT_NAMESPACE ? undefined : namespace, - }), - ) - .join(', ')} + label="System" + value="No System" gridSizes={{ xs: 12, sm: 6, lg: 4 }} - /> + > + {partOfSystemRelation && ( + + )} + ', () => { + it('renders link for entity in default namespace', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'software', + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + const { getByText } = render(, { + wrapper: MemoryRouter, + }); + + expect(getByText('component:software')).toBeInTheDocument(); + }); + + it('renders link for entity in other namespace', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'software', + namespace: 'test', + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + const { getByText } = render(, { + wrapper: MemoryRouter, + }); + expect(getByText('component:test/software')).toBeInTheDocument(); + }); + + it('renders link for entity name in default namespace', () => { + const entityName = { + kind: 'Component', + namespace: 'default', + name: 'software', + }; + const { getByText } = render(, { + wrapper: MemoryRouter, + }); + expect(getByText('component:software')).toBeInTheDocument(); + }); + + it('renders link for entity name in other namespace', () => { + const entityName = { + kind: 'Component', + namespace: 'test', + name: 'software', + }; + const { getByText } = render(, { + wrapper: MemoryRouter, + }); + expect(getByText('component:test/software')).toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx b/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx new file mode 100644 index 0000000000..1084fbd37e --- /dev/null +++ b/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx @@ -0,0 +1,75 @@ +/* + * 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 { + Entity, + EntityName, + ENTITY_DEFAULT_NAMESPACE, + serializeEntityRef, +} from '@backstage/catalog-model'; +import { Link } from '@material-ui/core'; +import React from 'react'; +import { generatePath } from 'react-router'; +import { Link as RouterLink } from 'react-router-dom'; +import { entityRoute } from '../../routes'; + +type EntityRefLinkProps = { + entityRef: Entity | EntityName; +}; + +// TODO: This component is private for now, as it should probably belong into +// some kind of helper module for the catalog plugin to avoid a dependency on +// the catalog plugin itself. +export const EntityRefLink = ({ entityRef }: EntityRefLinkProps) => { + let kind; + let namespace; + let name; + + if ('metadata' in entityRef) { + kind = entityRef.kind; + namespace = entityRef.metadata.namespace; + name = entityRef.metadata.name; + } else { + kind = entityRef.kind; + namespace = entityRef.namespace; + name = entityRef.name; + } + + if (namespace === ENTITY_DEFAULT_NAMESPACE) { + namespace = undefined; + } + + kind = kind.toLowerCase(); + + const title = `${serializeEntityRef({ + kind, + name, + namespace, + })}`; + const routeParams = { + kind, + namespace: namespace?.toLowerCase() ?? ENTITY_DEFAULT_NAMESPACE, + name, + }; + + return ( + + {title} + + ); +}; diff --git a/plugins/catalog/src/components/EntityRefLink/index.ts b/plugins/catalog/src/components/EntityRefLink/index.ts new file mode 100644 index 0000000000..aa2c6641ef --- /dev/null +++ b/plugins/catalog/src/components/EntityRefLink/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { EntityRefLink } from './EntityRefLink'; From be2f9d8049f1accf14d4412f7ecf36b19f6d1580 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 15 Jan 2021 11:43:16 +0100 Subject: [PATCH 096/144] microsite: fix display of summary elements --- microsite/static/css/custom.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/microsite/static/css/custom.css b/microsite/static/css/custom.css index 46b2aa3470..01bb3c89eb 100644 --- a/microsite/static/css/custom.css +++ b/microsite/static/css/custom.css @@ -48,6 +48,11 @@ h6 { color: $textColor; } +summary { + color: $textColor; + cursor: pointer; +} + h2:hover .hash-link { opacity: 1; } From ac7be581a1c67177caaf89a3b5b3dad6c6b6666c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 15 Jan 2021 12:03:50 +0100 Subject: [PATCH 097/144] catalog-backend: Refuse to remove the bootstrap location --- .changeset/orange-avocados-work.md | 5 +++++ .../src/database/CommonDatabase.test.ts | 16 ++++++++++++++++ .../src/database/CommonDatabase.ts | 18 ++++++++++++------ 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 .changeset/orange-avocados-work.md diff --git a/.changeset/orange-avocados-work.md b/.changeset/orange-avocados-work.md new file mode 100644 index 0000000000..a1f3597302 --- /dev/null +++ b/.changeset/orange-avocados-work.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Refuse to remove the bootstrap location diff --git a/plugins/catalog-backend/src/database/CommonDatabase.test.ts b/plugins/catalog-backend/src/database/CommonDatabase.test.ts index 130ed41eb1..b319bb832d 100644 --- a/plugins/catalog-backend/src/database/CommonDatabase.test.ts +++ b/plugins/catalog-backend/src/database/CommonDatabase.test.ts @@ -137,6 +137,22 @@ describe('CommonDatabase', () => { await expect(db.location(location.id)).rejects.toThrow(/Found no location/); }); + it('refuses to remove the bootstrap location', async () => { + const input: Location = { + id: 'dd12620d-0436-422f-93bd-929aa0788123', + type: 'bootstrap', + target: 'bootstrap', + }; + + const output = await db.transaction( + async tx => await db.addLocation(tx, input), + ); + + await expect( + db.transaction(async tx => await db.removeLocation(tx, output.id)), + ).rejects.toThrow(ConflictError); + }); + describe('addEntities', () => { it('happy path: adds entities to empty database', async () => { const result = await db.transaction(tx => diff --git a/plugins/catalog-backend/src/database/CommonDatabase.ts b/plugins/catalog-backend/src/database/CommonDatabase.ts index d641cbb7b6..2edc0ca5b8 100644 --- a/plugins/catalog-backend/src/database/CommonDatabase.ts +++ b/plugins/catalog-backend/src/database/CommonDatabase.ts @@ -330,15 +330,21 @@ export class CommonDatabase implements Database { async removeLocation(txOpaque: Transaction, id: string): Promise { const tx = txOpaque as Knex.Transaction; + const locations = await tx('locations') + .where({ id }) + .select(); + if (!locations.length) { + throw new NotFoundError(`Found no location with ID ${id}`); + } + + if (locations[0].type === 'bootstrap') { + throw new ConflictError('You may not delete the bootstrap location.'); + } + await tx('entities') .where({ location_id: id }) .update({ location_id: null }); - - const result = await tx('locations').where({ id }).del(); - - if (!result) { - throw new NotFoundError(`Found no location with ID ${id}`); - } + await tx('locations').where({ id }).del(); } async location(id: string): Promise { From bbad7b3e9c718d5ddb41ba0d6d7e5c89140c2e31 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 15 Jan 2021 12:23:50 +0100 Subject: [PATCH 098/144] bug(create-app): updating create-app to have support for url protocol for the scaffolder --- .../templates/default-app/app-config.yaml.hbs | 10 +-- .../backend/src/plugins/scaffolder.ts | 80 +------------------ 2 files changed, 8 insertions(+), 82 deletions(-) diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index 90f272270a..d253102f71 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -88,23 +88,23 @@ catalog: target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/all-apis.yaml # Backstage example templates - - type: github + - type: url target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml rules: - allow: [Template] - - type: github + - type: url target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/template.yaml rules: - allow: [Template] - - type: github + - type: url target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/create-react-app/template.yaml rules: - allow: [Template] - - type: github + - type: url target: https://github.com/spotify/cookiecutter-golang/blob/master/template.yaml rules: - allow: [Template] - - type: github + - type: url target: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/sample-templates/docs-template/template.yaml rules: - allow: [Template] 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 2dc69feb45..e6b8ad96fc 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,21 +1,13 @@ import { CookieCutter, createRouter, - FilePreparer, - GithubPreparer, - GitlabPreparer, Preparers, Publishers, - GithubPublisher, - GitlabPublisher, CreateReactAppTemplater, Templaters, - RepoVisibilityOptions, CatalogEntityClient, } from '@backstage/plugin-scaffolder-backend'; import { SingleHostDiscovery } from '@backstage/backend-common'; -import { Octokit } from '@octokit/rest'; -import { Gitlab } from '@gitbeaker/node'; import type { PluginEnvironment } from '../types'; import Docker from 'dockerode'; @@ -29,74 +21,8 @@ export default async function createPlugin({ templaters.register('cookiecutter', cookiecutterTemplater); templaters.register('cra', craTemplater); - const filePreparer = new FilePreparer(); - const githubPreparer = new GithubPreparer(); - const gitlabPreparer = new GitlabPreparer(config); - const preparers = new Preparers(); - - preparers.register('file', filePreparer); - preparers.register('github', githubPreparer); - preparers.register('gitlab', gitlabPreparer); - preparers.register('gitlab/api', gitlabPreparer); - - const publishers = new Publishers(); - - const githubConfig = config.getOptionalConfig('scaffolder.github'); - - if (githubConfig) { - try { - const repoVisibility = githubConfig.getString( - 'visibility', - ) as RepoVisibilityOptions; - - const githubToken = githubConfig.getString('token'); - const githubHost = githubConfig.getOptionalString('host'); - const githubClient = new Octokit({ auth: githubToken, baseUrl: githubHost }); - const githubPublisher = new GithubPublisher({ - client: githubClient, - token: githubToken, - repoVisibility, - }); - publishers.register('file', githubPublisher); - publishers.register('github', githubPublisher); - } catch (e) { - const providerName = 'github'; - if (process.env.NODE_ENV !== 'development') { - throw new Error( - `Failed to initialize ${providerName} scaffolding provider, ${e.message}`, - ); - } - - logger.warn( - `Skipping ${providerName} scaffolding provider, ${e.message}`, - ); - } - } - - const gitLabConfig = config.getOptionalConfig('scaffolder.gitlab.api'); - if (gitLabConfig) { - try { - const gitLabToken = gitLabConfig.getString('token'); - const gitLabClient = new Gitlab({ - host: gitLabConfig.getOptionalString('baseUrl'), - token: gitLabToken, - }); - const gitLabPublisher = new GitlabPublisher(gitLabClient, gitLabToken); - publishers.register('gitlab', gitLabPublisher); - publishers.register('gitlab/api', gitLabPublisher); - } catch (e) { - const providerName = 'gitlab'; - if (process.env.NODE_ENV !== 'development') { - throw new Error( - `Failed to initialize ${providerName} scaffolding provider, ${e.message}`, - ); - } - - logger.warn( - `Skipping ${providerName} scaffolding provider, ${e.message}`, - ); - } - } + const preparers = await Preparers.fromConfig(config, { logger }); + const publishers = await Publishers.fromConfig(config, { logger }); const dockerClient = new Docker(); @@ -112,4 +38,4 @@ export default async function createPlugin({ dockerClient, entityClient, }); -} +} \ No newline at end of file From 549a254e0b1a3ca393b3061fb32a8fa72d236219 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Fri, 15 Jan 2021 12:24:01 +0100 Subject: [PATCH 099/144] fixup so glossary shows up in sidebar on microsite --- docs/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/glossary.md b/docs/glossary.md index dc67b2aae2..e5a9882909 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -1,5 +1,5 @@ --- -id: Glossary +id: glossary title: Backstage Glossary # prettier-ignore description: List of all the terms, abbreviations, and phrases used in Backstage, together with their explanations. From d176671d1a8c341e53ce75d7c10745ed7522689f Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 15 Jan 2021 12:25:12 +0100 Subject: [PATCH 100/144] chore(create-app): added changeset --- .changeset/swift-baboons-refuse.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/swift-baboons-refuse.md diff --git a/.changeset/swift-baboons-refuse.md b/.changeset/swift-baboons-refuse.md new file mode 100644 index 0000000000..529d2d1258 --- /dev/null +++ b/.changeset/swift-baboons-refuse.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +use `fromConfig` for all scaffolder helpers, and use the url protocol for app-config location entries From 6507f83070bbd58b52bc3da4a8a4b45e7865f54b Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 15 Jan 2021 12:34:47 +0100 Subject: [PATCH 101/144] Hide the entity kind for systems --- .../src/components/AboutCard/AboutContent.tsx | 15 +++++--- .../EntityRefLink/EntityRefLink.test.tsx | 38 +++++++++++++++++++ .../EntityRefLink/EntityRefLink.tsx | 9 ++++- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/plugins/catalog/src/components/AboutCard/AboutContent.tsx b/plugins/catalog/src/components/AboutCard/AboutContent.tsx index 725217db9a..d0c25c69b6 100644 --- a/plugins/catalog/src/components/AboutCard/AboutContent.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutContent.tsx @@ -50,10 +50,12 @@ export const AboutContent = ({ entity }: Props) => { - {ownedByRelations.map((t, i) => [ - i > 0 && ', ', - , - ])} + {ownedByRelations.map((t, i) => ( + + {i > 0 && ', '} + + + ))} { gridSizes={{ xs: 12, sm: 6, lg: 4 }} > {partOfSystemRelation && ( - + )} ', () => { expect(getByText('component:test/software')).toBeInTheDocument(); }); + it('renders link for entity and hides default kind', () => { + const entity = { + apiVersion: 'v1', + kind: 'Component', + metadata: { + name: 'software', + namespace: 'test', + }, + spec: { + owner: 'guest', + type: 'service', + lifecycle: 'production', + }, + }; + const { getByText } = render( + , + { + wrapper: MemoryRouter, + }, + ); + expect(getByText('test/software')).toBeInTheDocument(); + }); + it('renders link for entity name in default namespace', () => { const entityName = { kind: 'Component', @@ -83,4 +106,19 @@ describe('', () => { }); expect(getByText('component:test/software')).toBeInTheDocument(); }); + + it('renders link for entity name and hides default kind', () => { + const entityName = { + kind: 'Component', + namespace: 'test', + name: 'software', + }; + const { getByText } = render( + , + { + wrapper: MemoryRouter, + }, + ); + expect(getByText('test/software')).toBeInTheDocument(); + }); }); diff --git a/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx b/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx index 1084fbd37e..a06ba69fbe 100644 --- a/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx +++ b/plugins/catalog/src/components/EntityRefLink/EntityRefLink.tsx @@ -27,12 +27,16 @@ import { entityRoute } from '../../routes'; type EntityRefLinkProps = { entityRef: Entity | EntityName; + defaultKind?: string; }; // TODO: This component is private for now, as it should probably belong into // some kind of helper module for the catalog plugin to avoid a dependency on // the catalog plugin itself. -export const EntityRefLink = ({ entityRef }: EntityRefLinkProps) => { +export const EntityRefLink = ({ + entityRef, + defaultKind, +}: EntityRefLinkProps) => { let kind; let namespace; let name; @@ -54,7 +58,7 @@ export const EntityRefLink = ({ entityRef }: EntityRefLinkProps) => { kind = kind.toLowerCase(); const title = `${serializeEntityRef({ - kind, + kind: defaultKind && defaultKind.toLowerCase() === kind ? undefined : kind, name, namespace, })}`; @@ -64,6 +68,7 @@ export const EntityRefLink = ({ entityRef }: EntityRefLinkProps) => { name, }; + // TODO: Use useRouteRef here to generate the path return ( Date: Fri, 15 Jan 2021 12:41:52 +0100 Subject: [PATCH 102/144] Fix invalid changeset file format I guess changesets are not tracked if they are not an `.md` file in the `.changeset` directory. @backstage/silver-lining --- ...less-coins-pretend => cost-insights-careless-coins-pretend.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changeset/{cost-insights-careless-coins-pretend => cost-insights-careless-coins-pretend.md} (100%) diff --git a/.changeset/cost-insights-careless-coins-pretend b/.changeset/cost-insights-careless-coins-pretend.md similarity index 100% rename from .changeset/cost-insights-careless-coins-pretend rename to .changeset/cost-insights-careless-coins-pretend.md From 09d211bff20c592a5a4c56f080e5afbd92594a11 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Fri, 15 Jan 2021 12:45:47 +0100 Subject: [PATCH 103/144] techdocs-common: release v0.3.4 --- .changeset/techdocs-mean-items-behave.md | 5 ----- packages/techdocs-common/CHANGELOG.md | 6 ++++++ packages/techdocs-common/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/techdocs-mean-items-behave.md diff --git a/.changeset/techdocs-mean-items-behave.md b/.changeset/techdocs-mean-items-behave.md deleted file mode 100644 index 77679c39d4..0000000000 --- a/.changeset/techdocs-mean-items-behave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -@backstage/techdocs-common can now be imported in an environment without @backstage/plugin-techdocs-backend being installed. diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index 6cf9481327..c26164bbb6 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/techdocs-common +## 0.3.4 + +### Patch Changes + +- a594a7257: @backstage/techdocs-common can now be imported in an environment without @backstage/plugin-techdocs-backend being installed. + ## 0.3.3 ### Patch Changes diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index 387c75241c..a069326375 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.3.3", + "version": "0.3.4", "main": "src/index.ts", "types": "src/index.ts", "private": false, From f9ba00a1cad3f3cc5f73a815ea7d1f1e795f5c6a Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 15 Jan 2021 13:34:04 +0100 Subject: [PATCH 104/144] Update @azure/msal-node to 1.0.0-beta.3 The new version introduced a breaking change and requires manual changes. --- .changeset/wild-cats-end.md | 5 ++++ plugins/catalog-backend/package.json | 2 +- .../processors/microsoftGraph/client.ts | 4 +++ yarn.lock | 27 +++++++++---------- 4 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 .changeset/wild-cats-end.md diff --git a/.changeset/wild-cats-end.md b/.changeset/wild-cats-end.md new file mode 100644 index 0000000000..d2c9e6f245 --- /dev/null +++ b/.changeset/wild-cats-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Update the @azure/msal-node dependency to 1.0.0-beta.3. diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 0ce2e8d37a..e6b6713e20 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -30,7 +30,7 @@ }, "dependencies": { "@aws-sdk/client-organizations": "^3.2.0", - "@azure/msal-node": "^1.0.0-alpha.8", + "@azure/msal-node": "^1.0.0-beta.3", "@backstage/backend-common": "^0.4.3", "@backstage/catalog-model": "^0.6.1", "@backstage/config": "^0.1.2", diff --git a/plugins/catalog-backend/src/ingestion/processors/microsoftGraph/client.ts b/plugins/catalog-backend/src/ingestion/processors/microsoftGraph/client.ts index bdb5918b2f..3dfc58e773 100644 --- a/plugins/catalog-backend/src/ingestion/processors/microsoftGraph/client.ts +++ b/plugins/catalog-backend/src/ingestion/processors/microsoftGraph/client.ts @@ -96,6 +96,10 @@ export class MicrosoftGraphClient { scopes: ['https://graph.microsoft.com/.default'], }); + if (!token) { + throw new Error('Error while requesting token for Microsoft Graph'); + } + return await fetch(url, { headers: { Authorization: `Bearer ${token.accessToken}`, diff --git a/yarn.lock b/yarn.lock index 955b6305c3..7fdc2c3a62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -879,21 +879,20 @@ dependencies: tslib "^1.8.0" -"@azure/msal-common@^1.6.2": - version "1.6.2" - resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-1.6.2.tgz#91f3732866d727e20f1e142e6e88a981268fbff2" - integrity sha512-GShzp1q7Ld8SwYiDEjQZ9PmFOY4x+2stE86maiguylE9/d/c2muqKjc8aepmEqyjbV7o/omDvEf2Sr9QcIqkSA== +"@azure/msal-common@^2.1.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@azure/msal-common/-/msal-common-2.1.0.tgz#a4bc17e254d6ec524016f13267947dd4ff4a624d" + integrity sha512-Y1Id+jG59S3eY2ZQQtUA/lxwbRcgjcWaiib9YX+SwV3zeRauKfEiZT7l3z+lwV+T+Sst20F6l1mJsfQcfE7CEQ== dependencies: debug "^4.1.1" -"@azure/msal-node@^1.0.0-alpha.8": - version "1.0.0-alpha.12" - resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.0.0-alpha.12.tgz#09d8d52f5cea90b133c3d48fe4ec477693040c91" - integrity sha512-uGLOJRWiEhfJIrTv/lwdm4RxQFm++00h83zNgDn0O3NkXlzAoCCq9QFYW84PjMR/Q2PUvVy7uW+6yKL/Nq3gBA== +"@azure/msal-node@^1.0.0-beta.3": + version "1.0.0-beta.3" + resolved "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.0.0-beta.3.tgz#c84c7948028b39e48b901f5fac35bdedcbc8772e" + integrity sha512-/KfYRfrsOIrZONvo/0Vi5umuqbPBtCWNtmRvkse64uI0C4CP/W4WXwRD42VMws/8LtKvr1I5rYlYgFzt5zDz/A== dependencies: - "@azure/msal-common" "^1.6.2" - axios "^0.19.2" - debug "^4.1.1" + "@azure/msal-common" "^2.1.0" + axios "^0.21.1" jsonwebtoken "^8.5.1" uuid "^8.3.0" @@ -2436,7 +2435,7 @@ to-fast-properties "^2.0.0" "@backstage/catalog-model@^0.2.0": - version "0.6.0" + version "0.6.1" dependencies: "@backstage/config" "^0.1.2" "@types/json-schema" "^7.0.5" @@ -2447,7 +2446,7 @@ yup "^0.29.3" "@backstage/catalog-model@^0.3.0": - version "0.6.0" + version "0.6.1" dependencies: "@backstage/config" "^0.1.2" "@types/json-schema" "^7.0.5" @@ -2458,7 +2457,7 @@ yup "^0.29.3" "@backstage/core@^0.3.0": - version "0.4.3" + version "0.4.4" dependencies: "@backstage/config" "^0.1.2" "@backstage/core-api" "^0.2.8" From 532b788679c634dbb70dbdc43c4574d2f4d87bed Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Fri, 15 Jan 2021 15:39:13 +0100 Subject: [PATCH 105/144] chore(create-app): new line at the end of the file! --- .../default-app/packages/backend/src/plugins/scaffolder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e6b8ad96fc..196d48ec78 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 @@ -38,4 +38,4 @@ export default async function createPlugin({ dockerClient, entityClient, }); -} \ No newline at end of file +} From fc15529cf4855fabd949fa340eaaae356f02688f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:40:46 +0100 Subject: [PATCH 106/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 6941d76ed4..6cf6ac5d2a 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -1,7 +1,7 @@ # Using GithubApps for backend authentication Backstage can be configured to use GitHub Apps for backend authentication. This -come with advantages such as higher rate limits and that Backstage can act as an +comes with advantages such as higher rate limits and that Backstage can act as an application instead of a user or bot account. It also provides a much clearer and better authorization model as a opposed to From 57b542c73691105f435c771bd7a10fd614f1ff4c Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:41:03 +0100 Subject: [PATCH 107/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 6cf6ac5d2a..7738cae624 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -9,7 +9,7 @@ the OAuth apps and their respective scopes. ## Caveats -- It's not possible to have multiple backstage GitHub Apps installed in the same +- It's not possible to have multiple Backstage GitHub Apps installed in the same GitHub organization be managed by Backstage. We currently don't check through all the registered GitHub Apps to see which ones are installed for a particular repository. We just respect global Organization installs right now. From 084e2450d181d956f6f9f7f4eadb5934ecbe710c Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:41:23 +0100 Subject: [PATCH 108/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 7738cae624..a94ad212f9 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -12,7 +12,7 @@ the OAuth apps and their respective scopes. - It's not possible to have multiple Backstage GitHub Apps installed in the same GitHub organization be managed by Backstage. We currently don't check through all the registered GitHub Apps to see which ones are installed for a - particular repository. We just respect global Organization installs right now. + particular repository. We only respect global Organization installs right now. - App permissions is not managed by Backstage. They're created with some simple default permissions which you are free to change as you need, but you will need to update them in the GitHub web console, not in Backstage right now. The From b8f87997867b2844401e0dbc257d3a307dc9fe31 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:41:50 +0100 Subject: [PATCH 109/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index a94ad212f9..a9758afb57 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -67,7 +67,7 @@ privateKey: | Once the credentials are stored in a `yaml` file generated by `create-github-app` or manually by following the [GitHub Enterprise](#gitHub-enterprise) instructions they can be included in the -`app-config.yaml` under the integrations section. +`app-config.yaml` under the `integrations` section. ```yaml integrations: From fffe91dba5d904f39926e5c033af59447b3a7303 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:42:06 +0100 Subject: [PATCH 110/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index a9758afb57..675b3c91a7 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -66,7 +66,7 @@ privateKey: | Once the credentials are stored in a `yaml` file generated by `create-github-app` or manually by following the -[GitHub Enterprise](#gitHub-enterprise) instructions they can be included in the +[GitHub Enterprise](#gitHub-enterprise) instructions, they can be included in the `app-config.yaml` under the `integrations` section. ```yaml From 27745f651af014fc7109e5187ef32aa142bb2cf1 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:42:13 +0100 Subject: [PATCH 111/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 675b3c91a7..0503b7c9f0 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -64,7 +64,7 @@ privateKey: | ### Including in Integrations Config -Once the credentials are stored in a `yaml` file generated by +Once the credentials are stored in a yaml file generated by `create-github-app` or manually by following the [GitHub Enterprise](#gitHub-enterprise) instructions, they can be included in the `app-config.yaml` under the `integrations` section. From b5ed171a1ac218c09b690aba2f690dbbef8b3595 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:42:25 +0100 Subject: [PATCH 112/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 0503b7c9f0..b07eaeacc2 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -10,7 +10,7 @@ the OAuth apps and their respective scopes. ## Caveats - It's not possible to have multiple Backstage GitHub Apps installed in the same - GitHub organization be managed by Backstage. We currently don't check through + GitHub organization, to be handled by Backstage. We currently don't check through all the registered GitHub Apps to see which ones are installed for a particular repository. We only respect global Organization installs right now. - App permissions is not managed by Backstage. They're created with some simple From 50b933d9ac66b41363c0ede684be1caed9572e9e Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 15:42:39 +0100 Subject: [PATCH 113/144] Update docs/plugins/github-apps.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/plugins/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index b07eaeacc2..1bb2af8524 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -1,4 +1,4 @@ -# Using GithubApps for backend authentication +# Using GitHub Apps for Backend Authentication Backstage can be configured to use GitHub Apps for backend authentication. This comes with advantages such as higher rate limits and that Backstage can act as an From 6e02373449c58b751417be8099685bd4b1949c94 Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Fri, 15 Jan 2021 15:42:39 +0100 Subject: [PATCH 114/144] Update swift-baboons-refuse.md --- .changeset/swift-baboons-refuse.md | 50 +++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/.changeset/swift-baboons-refuse.md b/.changeset/swift-baboons-refuse.md index 529d2d1258..093fd727be 100644 --- a/.changeset/swift-baboons-refuse.md +++ b/.changeset/swift-baboons-refuse.md @@ -2,4 +2,52 @@ '@backstage/create-app': patch --- -use `fromConfig` for all scaffolder helpers, and use the url protocol for app-config location entries +use `fromConfig` for all scaffolder helpers, and use the url protocol for app-config location entries. + +To apply this change to your local installation, replace the contents of your `packages/backend/src/plugins/scaffolder.ts` with the following contents: + +```ts +import { + CookieCutter, + createRouter, + Preparers, + Publishers, + CreateReactAppTemplater, + Templaters, + CatalogEntityClient, +} from '@backstage/plugin-scaffolder-backend'; +import { SingleHostDiscovery } from '@backstage/backend-common'; +import type { PluginEnvironment } from '../types'; +import Docker from 'dockerode'; + +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { + const cookiecutterTemplater = new CookieCutter(); + 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 entityClient = new CatalogEntityClient({ discovery }); + + return await createRouter({ + preparers, + templaters, + publishers, + logger, + config, + dockerClient, + entityClient, + }); +} +``` + +This will ensure that the `scaffolder-backend` backage can add handlers for the `url` protocol which is becoming the standard when registering entities in the `catalog` From 48f3a5dcae4eeddf3916132a75caf69361aadc52 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 16:13:19 +0100 Subject: [PATCH 115/144] Update docs --- docs/plugins/github-apps.md | 20 +++++++++++-------- .../src/github/GithubCredentialsProvider.ts | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index 1bb2af8524..d3b0e36cd9 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -1,8 +1,8 @@ # Using GitHub Apps for Backend Authentication Backstage can be configured to use GitHub Apps for backend authentication. This -comes with advantages such as higher rate limits and that Backstage can act as an -application instead of a user or bot account. +comes with advantages such as higher rate limits and that Backstage can act as +an application instead of a user or bot account. It also provides a much clearer and better authorization model as a opposed to the OAuth apps and their respective scopes. @@ -10,8 +10,8 @@ the OAuth apps and their respective scopes. ## Caveats - It's not possible to have multiple Backstage GitHub Apps installed in the same - GitHub organization, to be handled by Backstage. We currently don't check through - all the registered GitHub Apps to see which ones are installed for a + GitHub organization, to be handled by Backstage. We currently don't check + through all the registered GitHub Apps to see which ones are installed for a particular repository. We only respect global Organization installs right now. - App permissions is not managed by Backstage. They're created with some simple default permissions which you are free to change as you need, but you will @@ -64,10 +64,14 @@ privateKey: | ### Including in Integrations Config -Once the credentials are stored in a yaml file generated by -`create-github-app` or manually by following the -[GitHub Enterprise](#gitHub-enterprise) instructions, they can be included in the -`app-config.yaml` under the `integrations` section. +Once the credentials are stored in a yaml file generated by `create-github-app` +or manually by following the [GitHub Enterprise](#gitHub-enterprise) +instructions, they can be included in the `app-config.yaml` under the +`integrations` section. + +Please note that the credentials file is highly sensitive and should NOT be +checked into any kind of version control. Instead use your preferred secure +method of distributing secrets. ```yaml integrations: diff --git a/packages/integration/src/github/GithubCredentialsProvider.ts b/packages/integration/src/github/GithubCredentialsProvider.ts index cb928a4a6c..843ef629ab 100644 --- a/packages/integration/src/github/GithubCredentialsProvider.ts +++ b/packages/integration/src/github/GithubCredentialsProvider.ts @@ -60,7 +60,9 @@ const HEADERS = { Accept: 'application/vnd.github.machine-man-preview+json', }; -// GithubAppManager issues tokens for a speicifc GitHub App +/** + * GithubAppManager issues and caches tokens for a specific GitHub App. + */ class GithubAppManager { private readonly appClient: Octokit; private readonly baseAuthConfig: { appId: number; privateKey: string }; From d1ccd52a9d58d1dcc312a8738fa3a1b79cded8c1 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 16:17:11 +0100 Subject: [PATCH 116/144] cli: Append credentials to create-github-app output name --- packages/cli/src/commands/create-github-app/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/create-github-app/index.ts b/packages/cli/src/commands/create-github-app/index.ts index b8d232674e..2dd0d31232 100644 --- a/packages/cli/src/commands/create-github-app/index.ts +++ b/packages/cli/src/commands/create-github-app/index.ts @@ -26,7 +26,7 @@ import { GithubCreateAppServer } from './GithubCreateAppServer'; export default async (org: string) => { const { slug, name, ...config } = await GithubCreateAppServer.run({ org }); - const fileName = `github-app-${slug}.yaml`; + const fileName = `github-app-${slug}-credentials.yaml`; const content = `# Name: ${name}\n${stringifyYaml(config)}`; await fs.writeFile(paths.resolveTargetRoot(fileName), content); console.log(`GitHub App configuration written to ${chalk.cyan(fileName)}`); From c48119248846e45d572e3065ae2ad0b78e46572f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 16:25:29 +0100 Subject: [PATCH 117/144] gitignore: Ignore *-credentials.yaml --- .gitignore | 3 +++ packages/create-app/templates/default-app/.gitignore.hbs | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3334bf956d..57ad74c5cc 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,6 @@ site # Local configuration files *.local.yaml + +# Sensitive credentials +*-credentials.yaml diff --git a/packages/create-app/templates/default-app/.gitignore.hbs b/packages/create-app/templates/default-app/.gitignore.hbs index 5f5cc739f4..4adebc5adc 100644 --- a/packages/create-app/templates/default-app/.gitignore.hbs +++ b/packages/create-app/templates/default-app/.gitignore.hbs @@ -30,4 +30,7 @@ dist-types site # Local configuration files -*.local.yaml \ No newline at end of file +*.local.yaml + +# Sensitive credentials +*-credentials.yaml From b604a9d41192b643436f36283b8fdd43235f14b5 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 16:35:06 +0100 Subject: [PATCH 118/144] Add changeset --- .changeset/loud-kids-dance.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/loud-kids-dance.md diff --git a/.changeset/loud-kids-dance.md b/.changeset/loud-kids-dance.md new file mode 100644 index 0000000000..035e038f58 --- /dev/null +++ b/.changeset/loud-kids-dance.md @@ -0,0 +1,8 @@ +--- +'@backstage/cli': patch +'@backstage/create-app': patch +--- + +Append `-credentials.yaml` to credentials file generated by `backstage-cli create-github-app`. + +Add `*-credentials.yaml` to gitignore to prevent accidental commits. From 7d2a390a4f27107c23eabdecc95bfee7a8286a39 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 17:21:12 +0100 Subject: [PATCH 119/144] Warn user about sensitive credentials --- .changeset/loud-kids-dance.md | 5 +---- packages/cli/src/commands/create-github-app/index.ts | 5 +++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.changeset/loud-kids-dance.md b/.changeset/loud-kids-dance.md index 035e038f58..4fd32ac348 100644 --- a/.changeset/loud-kids-dance.md +++ b/.changeset/loud-kids-dance.md @@ -1,8 +1,5 @@ --- '@backstage/cli': patch -'@backstage/create-app': patch --- -Append `-credentials.yaml` to credentials file generated by `backstage-cli create-github-app`. - -Add `*-credentials.yaml` to gitignore to prevent accidental commits. +Append `-credentials.yaml` to credentials file generated by `backstage-cli create-github-app` and display warning about sensitive contents. diff --git a/packages/cli/src/commands/create-github-app/index.ts b/packages/cli/src/commands/create-github-app/index.ts index 2dd0d31232..cd9e8dbe09 100644 --- a/packages/cli/src/commands/create-github-app/index.ts +++ b/packages/cli/src/commands/create-github-app/index.ts @@ -30,5 +30,10 @@ export default async (org: string) => { const content = `# Name: ${name}\n${stringifyYaml(config)}`; await fs.writeFile(paths.resolveTargetRoot(fileName), content); console.log(`GitHub App configuration written to ${chalk.cyan(fileName)}`); + console.log( + chalk.yellow( + 'This file contains sensitive credentials, it should not be committed to version control and handled with care!', + ), + ); // TODO: log instructions on how to use the newly created app configuration. }; From 92dbbcedd760bbab615bf5a2fabf4d988e4dc42b Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 17:21:44 +0100 Subject: [PATCH 120/144] Add changeset for create-app --- .changeset/spoon-fork.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .changeset/spoon-fork.md diff --git a/.changeset/spoon-fork.md b/.changeset/spoon-fork.md new file mode 100644 index 0000000000..a9d5e42237 --- /dev/null +++ b/.changeset/spoon-fork.md @@ -0,0 +1,11 @@ +--- +'@backstage/create-app': patch +--- + +Add `*-credentials.yaml` to gitignore to prevent accidental commits of sensitive credential information. + +To apply this change to an existing installation, add this line to your `.gitignore` + +```gitignore +*-credentials.yaml +``` From d69d5d5ca8018f8efa8ee0f6414dab4107508d4f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 15 Jan 2021 17:25:33 +0100 Subject: [PATCH 121/144] Add comment to changeset --- .changeset/spoon-fork.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/spoon-fork.md b/.changeset/spoon-fork.md index a9d5e42237..5b8620f94e 100644 --- a/.changeset/spoon-fork.md +++ b/.changeset/spoon-fork.md @@ -4,8 +4,9 @@ Add `*-credentials.yaml` to gitignore to prevent accidental commits of sensitive credential information. -To apply this change to an existing installation, add this line to your `.gitignore` +To apply this change to an existing installation, add these lines to your `.gitignore` ```gitignore +# Sensitive credentials *-credentials.yaml ``` From 1012fcfb5d9f3701abe1d1aa3cbb8b18613f48ad Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Fri, 15 Jan 2021 17:26:40 +0100 Subject: [PATCH 122/144] Update .changeset/swift-baboons-refuse.md Co-authored-by: Himanshu Mishra --- .changeset/swift-baboons-refuse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/swift-baboons-refuse.md b/.changeset/swift-baboons-refuse.md index 093fd727be..4cefb534ff 100644 --- a/.changeset/swift-baboons-refuse.md +++ b/.changeset/swift-baboons-refuse.md @@ -50,4 +50,4 @@ export default async function createPlugin({ } ``` -This will ensure that the `scaffolder-backend` backage can add handlers for the `url` protocol which is becoming the standard when registering entities in the `catalog` +This will ensure that the `scaffolder-backend` package can add handlers for the `url` protocol which is becoming the standard when registering entities in the `catalog` From 177f7984d870cafeeff4f3e40206ba5e5761f2c0 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Fri, 15 Jan 2021 14:10:09 -0500 Subject: [PATCH 123/144] Add TechDocs CLI for mkdocs build errors --- docs/features/techdocs/troubleshooting.md | 50 +++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/features/techdocs/troubleshooting.md b/docs/features/techdocs/troubleshooting.md index 3a9b2fcfd0..f2dc65704a 100644 --- a/docs/features/techdocs/troubleshooting.md +++ b/docs/features/techdocs/troubleshooting.md @@ -5,6 +5,50 @@ sidebar_label: Troubleshooting description: Troubleshooting for TechDocs --- -- TechDocs will fail to clone your docs if you have a git config which overrides - the `https` protocol with `ssh` or something else. Make sure to remove your - git config locally when you try TechDocs. +## Failure to clone + +TechDocs will fail to clone your docs if you have a git config which overrides +the `https` protocol with `ssh` or something else. Make sure to remove your git +config locally when you try TechDocs. + +## MkDocs Build Errors + +Using the [TechDocs CLI](https://github.com/backstage/techdocs-cli), you can +troubleshoot MkDocs build issues locally. Note this requires you have Docker +available to launch images. First, `git clone` the target repository locally, +then in the root of the repository, run: + +``` +npx @techdocs/cli serve +``` + +For example, if you have forgotten to put an MkDocs configuration file in your +repo, the resulting error will be: + +``` +npx: installed 278 in 9.089s +[techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000 +INFO - Building documentation... + +Config file '/content/mkdocs.yml' does not exist. +``` + +When it works, a local copy of both Backstage and your site will be launched +locally: + +``` +npx: installed 278 in 9.682s +[techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000 +INFO - Building documentation... +WARNING - Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0' suggests a production environment or the use of a proxy to connect to the MkDocs server. However, the MkDocs' server is intended for local development purposes only. Please use a third party production-ready server instead. +INFO - Cleaning site directory +DEBUG - Successfully imported extension module "plantuml_markdown". +DEBUG - Successfully loaded extension "plantuml_markdown.PlantUMLMarkdownExtension". +INFO - Documentation built in 0.23 seconds +[I 210115 19:00:45 server:335] Serving on http://0.0.0.0:8000 +INFO - Serving on http://0.0.0.0:8000 +[I 210115 19:00:45 handlers:62] Start watching changes +INFO - Start watching changes +[I 210115 19:00:45 handlers:64] Start detecting changes +INFO - Start detecting changes +``` From 33f7fbc5b20c8514eab2931555b125cebba8bd29 Mon Sep 17 00:00:00 2001 From: Adam Harvey Date: Fri, 15 Jan 2021 14:12:23 -0500 Subject: [PATCH 124/144] Fix wrapping --- docs/features/techdocs/troubleshooting.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/features/techdocs/troubleshooting.md b/docs/features/techdocs/troubleshooting.md index f2dc65704a..e6efefc578 100644 --- a/docs/features/techdocs/troubleshooting.md +++ b/docs/features/techdocs/troubleshooting.md @@ -40,7 +40,10 @@ locally: npx: installed 278 in 9.682s [techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000 INFO - Building documentation... -WARNING - Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0' suggests a production environment or the use of a proxy to connect to the MkDocs server. However, the MkDocs' server is intended for local development purposes only. Please use a third party production-ready server instead. +WARNING - Config value: 'dev_addr'. Warning: The use of the IP address '0.0.0.0' + suggests a production environment or the use of a proxy to connect to the MkDocs + server. However, the MkDocs' server is intended for local development purposes only. + Please use a third party production-ready server instead. INFO - Cleaning site directory DEBUG - Successfully imported extension module "plantuml_markdown". DEBUG - Successfully loaded extension "plantuml_markdown.PlantUMLMarkdownExtension". From ce3e20403885565bc086ad7f2b7cb751f4dd0576 Mon Sep 17 00:00:00 2001 From: Kiran Date: Tue, 12 Jan 2021 02:56:05 +0000 Subject: [PATCH 125/144] fix(create-app): fix user-settings routing in create-app --- .../create-app/templates/default-app/packages/app/src/App.tsx | 2 ++ .../templates/default-app/packages/app/src/plugins.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 693e66e0c6..13880f9ee3 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -15,6 +15,7 @@ import { Router as DocsRouter } from '@backstage/plugin-techdocs'; import { Router as ImportComponentRouter } from '@backstage/plugin-catalog-import'; import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; import { SearchPage as SearchRouter } from '@backstage/plugin-search'; +import { Router as SettingsRouter } from '@backstage/plugin-user-settings'; import { EntityPage } from './components/catalog/EntityPage'; @@ -59,6 +60,7 @@ const App = () => ( path="/search" element={} /> + } /> {deprecatedAppRoutes} diff --git a/packages/create-app/templates/default-app/packages/app/src/plugins.ts b/packages/create-app/templates/default-app/packages/app/src/plugins.ts index d3c9d6e2f3..28b42d5be2 100644 --- a/packages/create-app/templates/default-app/packages/app/src/plugins.ts +++ b/packages/create-app/templates/default-app/packages/app/src/plugins.ts @@ -6,3 +6,5 @@ export { plugin as GithubActions } from '@backstage/plugin-github-actions'; export { plugin as ScaffolderPlugin } from '@backstage/plugin-scaffolder'; export { plugin as TechDocsPlugin } from '@backstage/plugin-techdocs'; export { plugin as TechRadar } from '@backstage/plugin-tech-radar'; +export { plugin as UserSettings } from '@backstage/plugin-user-settings'; + From b87c7447409d018177cd185a771a114f69f3a8c3 Mon Sep 17 00:00:00 2001 From: Kiran Date: Tue, 12 Jan 2021 04:51:50 +0000 Subject: [PATCH 126/144] feat(create-app): synch backend/scaffolder.js with master --- .../backend/src/plugins/scaffolder.ts | 78 +------------------ 1 file changed, 2 insertions(+), 76 deletions(-) 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 2dc69feb45..196d48ec78 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,21 +1,13 @@ import { CookieCutter, createRouter, - FilePreparer, - GithubPreparer, - GitlabPreparer, Preparers, Publishers, - GithubPublisher, - GitlabPublisher, CreateReactAppTemplater, Templaters, - RepoVisibilityOptions, CatalogEntityClient, } from '@backstage/plugin-scaffolder-backend'; import { SingleHostDiscovery } from '@backstage/backend-common'; -import { Octokit } from '@octokit/rest'; -import { Gitlab } from '@gitbeaker/node'; import type { PluginEnvironment } from '../types'; import Docker from 'dockerode'; @@ -29,74 +21,8 @@ export default async function createPlugin({ templaters.register('cookiecutter', cookiecutterTemplater); templaters.register('cra', craTemplater); - const filePreparer = new FilePreparer(); - const githubPreparer = new GithubPreparer(); - const gitlabPreparer = new GitlabPreparer(config); - const preparers = new Preparers(); - - preparers.register('file', filePreparer); - preparers.register('github', githubPreparer); - preparers.register('gitlab', gitlabPreparer); - preparers.register('gitlab/api', gitlabPreparer); - - const publishers = new Publishers(); - - const githubConfig = config.getOptionalConfig('scaffolder.github'); - - if (githubConfig) { - try { - const repoVisibility = githubConfig.getString( - 'visibility', - ) as RepoVisibilityOptions; - - const githubToken = githubConfig.getString('token'); - const githubHost = githubConfig.getOptionalString('host'); - const githubClient = new Octokit({ auth: githubToken, baseUrl: githubHost }); - const githubPublisher = new GithubPublisher({ - client: githubClient, - token: githubToken, - repoVisibility, - }); - publishers.register('file', githubPublisher); - publishers.register('github', githubPublisher); - } catch (e) { - const providerName = 'github'; - if (process.env.NODE_ENV !== 'development') { - throw new Error( - `Failed to initialize ${providerName} scaffolding provider, ${e.message}`, - ); - } - - logger.warn( - `Skipping ${providerName} scaffolding provider, ${e.message}`, - ); - } - } - - const gitLabConfig = config.getOptionalConfig('scaffolder.gitlab.api'); - if (gitLabConfig) { - try { - const gitLabToken = gitLabConfig.getString('token'); - const gitLabClient = new Gitlab({ - host: gitLabConfig.getOptionalString('baseUrl'), - token: gitLabToken, - }); - const gitLabPublisher = new GitlabPublisher(gitLabClient, gitLabToken); - publishers.register('gitlab', gitLabPublisher); - publishers.register('gitlab/api', gitLabPublisher); - } catch (e) { - const providerName = 'gitlab'; - if (process.env.NODE_ENV !== 'development') { - throw new Error( - `Failed to initialize ${providerName} scaffolding provider, ${e.message}`, - ); - } - - logger.warn( - `Skipping ${providerName} scaffolding provider, ${e.message}`, - ); - } - } + const preparers = await Preparers.fromConfig(config, { logger }); + const publishers = await Publishers.fromConfig(config, { logger }); const dockerClient = new Docker(); From 10b22d6c706a456a9f60e672c6f4a62d2e3276c2 Mon Sep 17 00:00:00 2001 From: Kiran Date: Wed, 13 Jan 2021 02:28:24 +0000 Subject: [PATCH 127/144] Revert "feat(create-app): synch backend/scaffolder.js with master" This reverts commit 087e90cf8baeeff60d4cee7d503a84a88ee87fd1. --- .../backend/src/plugins/scaffolder.ts | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) 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 196d48ec78..2dc69feb45 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,13 +1,21 @@ import { CookieCutter, createRouter, + FilePreparer, + GithubPreparer, + GitlabPreparer, Preparers, Publishers, + GithubPublisher, + GitlabPublisher, CreateReactAppTemplater, Templaters, + RepoVisibilityOptions, CatalogEntityClient, } from '@backstage/plugin-scaffolder-backend'; import { SingleHostDiscovery } from '@backstage/backend-common'; +import { Octokit } from '@octokit/rest'; +import { Gitlab } from '@gitbeaker/node'; import type { PluginEnvironment } from '../types'; import Docker from 'dockerode'; @@ -21,8 +29,74 @@ export default async function createPlugin({ templaters.register('cookiecutter', cookiecutterTemplater); templaters.register('cra', craTemplater); - const preparers = await Preparers.fromConfig(config, { logger }); - const publishers = await Publishers.fromConfig(config, { logger }); + const filePreparer = new FilePreparer(); + const githubPreparer = new GithubPreparer(); + const gitlabPreparer = new GitlabPreparer(config); + const preparers = new Preparers(); + + preparers.register('file', filePreparer); + preparers.register('github', githubPreparer); + preparers.register('gitlab', gitlabPreparer); + preparers.register('gitlab/api', gitlabPreparer); + + const publishers = new Publishers(); + + const githubConfig = config.getOptionalConfig('scaffolder.github'); + + if (githubConfig) { + try { + const repoVisibility = githubConfig.getString( + 'visibility', + ) as RepoVisibilityOptions; + + const githubToken = githubConfig.getString('token'); + const githubHost = githubConfig.getOptionalString('host'); + const githubClient = new Octokit({ auth: githubToken, baseUrl: githubHost }); + const githubPublisher = new GithubPublisher({ + client: githubClient, + token: githubToken, + repoVisibility, + }); + publishers.register('file', githubPublisher); + publishers.register('github', githubPublisher); + } catch (e) { + const providerName = 'github'; + if (process.env.NODE_ENV !== 'development') { + throw new Error( + `Failed to initialize ${providerName} scaffolding provider, ${e.message}`, + ); + } + + logger.warn( + `Skipping ${providerName} scaffolding provider, ${e.message}`, + ); + } + } + + const gitLabConfig = config.getOptionalConfig('scaffolder.gitlab.api'); + if (gitLabConfig) { + try { + const gitLabToken = gitLabConfig.getString('token'); + const gitLabClient = new Gitlab({ + host: gitLabConfig.getOptionalString('baseUrl'), + token: gitLabToken, + }); + const gitLabPublisher = new GitlabPublisher(gitLabClient, gitLabToken); + publishers.register('gitlab', gitLabPublisher); + publishers.register('gitlab/api', gitLabPublisher); + } catch (e) { + const providerName = 'gitlab'; + if (process.env.NODE_ENV !== 'development') { + throw new Error( + `Failed to initialize ${providerName} scaffolding provider, ${e.message}`, + ); + } + + logger.warn( + `Skipping ${providerName} scaffolding provider, ${e.message}`, + ); + } + } const dockerClient = new Docker(); From 5899e5ca3a9a1877e6e1b0c50c0e7ecf15966a97 Mon Sep 17 00:00:00 2001 From: Kiran Patel Date: Wed, 13 Jan 2021 16:33:39 +1100 Subject: [PATCH 128/144] fix(create-app): add changeset for user-settings routing fix --- .changeset/ninety-turtles-fix.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ninety-turtles-fix.md diff --git a/.changeset/ninety-turtles-fix.md b/.changeset/ninety-turtles-fix.md new file mode 100644 index 0000000000..d0e16360ee --- /dev/null +++ b/.changeset/ninety-turtles-fix.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +fix routing and config for user-settings plugin From 26d3b24f3f3ffd781dfe511c63eb77f98317630e Mon Sep 17 00:00:00 2001 From: Kiran Patel Date: Sat, 16 Jan 2021 12:08:46 +1100 Subject: [PATCH 129/144] doc(create-app):enhance changelog for create-app settings routing --- .changeset/healthy-crews-remember.md | 19 +++++++++++++++++++ .changeset/ninety-turtles-fix.md | 5 ----- 2 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 .changeset/healthy-crews-remember.md delete mode 100644 .changeset/ninety-turtles-fix.md diff --git a/.changeset/healthy-crews-remember.md b/.changeset/healthy-crews-remember.md new file mode 100644 index 0000000000..c7cfdaaa6d --- /dev/null +++ b/.changeset/healthy-crews-remember.md @@ -0,0 +1,19 @@ +--- +'@backstage/create-app': patch +--- + +fix routing and config for user-settings plugin + +To make the corresponding change in your local app, add the following in your App.tsx + +``` +import { Router as SettingsRouter } from '@backstage/plugin-user-settings'; +... +} /> +``` + +and the following to your plugins.ts: + +``` +export { plugin as UserSettings } from '@backstage/plugin-user-settings'; +``` diff --git a/.changeset/ninety-turtles-fix.md b/.changeset/ninety-turtles-fix.md deleted file mode 100644 index d0e16360ee..0000000000 --- a/.changeset/ninety-turtles-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -fix routing and config for user-settings plugin From f5cc804ca30d7e2ef825b28fe64e3588e2731afe Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 16 Jan 2021 15:44:40 +0100 Subject: [PATCH 130/144] docs: add docs for how to handle type checking issues with linked in packages --- docs/getting-started/create-an-app.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/getting-started/create-an-app.md b/docs/getting-started/create-an-app.md index 52ea90db5b..0663d3faa1 100644 --- a/docs/getting-started/create-an-app.md +++ b/docs/getting-started/create-an-app.md @@ -74,6 +74,22 @@ those plugins in your backend. This is because the transformation of backend module tree stops whenever a non-local package is encountered, and from that point node will `require` packages directly for that entire module subtree. +Type checking can also have issues when linking in external packages, since the +linked in packages will use the types in the external project and dependency +version mismatches between the two projects may cause errors. To fix any of +those errors you need to sync versions of the dependencies in the two projects. +A simple way to do this can be to copy over `yarn.lock` from the external +project and run `yarn install`, although this is quite intrusive and can cause +other issues in existing projects, so use this method with care. It can often be +best to simply ignore the type errors, as app serving will work just fine +anyway. + +Another issue with type checking is that the incremental type cache doesn't +invalidate correctly for the linked in packages, causing type checking to not +reflect changes made to types. You can work around this by either setting +`compilerOptions.incremental = false` in `tsconfig.json`, or by deleting the +types cache folder `dist-types` before running `yarn tsc`. + ### Troubleshooting The create app command doesn't always work as expected, this is a collection of From bc4424b9d23403b7a1b42201ce8440b48642ce20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 16 Jan 2021 15:48:38 +0100 Subject: [PATCH 131/144] chore: silence code quality warning about backslash escape --- packages/e2e-test/src/lib/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e-test/src/lib/helpers.ts b/packages/e2e-test/src/lib/helpers.ts index 0b05eaf4b4..9242384d7d 100644 --- a/packages/e2e-test/src/lib/helpers.ts +++ b/packages/e2e-test/src/lib/helpers.ts @@ -152,7 +152,7 @@ export async function waitForPageWithText( // The page may not be fully loaded and hence we need to retry. let findTextAttempts = 0; - const escapedText = text.replace(/"/g, '\\"'); + const escapedText = text.replace(/(["\\])/g, '\\$1'); for (;;) { try { browser.assert.evaluate( From 09dd4d5aea789ce49aae7db6df13d1aea62af6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 16 Jan 2021 16:01:04 +0100 Subject: [PATCH 132/144] Update packages/e2e-test/src/lib/helpers.ts Co-authored-by: Patrik Oldsberg --- packages/e2e-test/src/lib/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e-test/src/lib/helpers.ts b/packages/e2e-test/src/lib/helpers.ts index 9242384d7d..190ff403d9 100644 --- a/packages/e2e-test/src/lib/helpers.ts +++ b/packages/e2e-test/src/lib/helpers.ts @@ -152,7 +152,7 @@ export async function waitForPageWithText( // The page may not be fully loaded and hence we need to retry. let findTextAttempts = 0; - const escapedText = text.replace(/(["\\])/g, '\\$1'); + const escapedText = text.replace(/"|\\/g, '\\$&'); for (;;) { try { browser.assert.evaluate( From 125da9d0ed27b520475293a2b0b5fe7672717476 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 15 Jan 2021 16:27:48 +0100 Subject: [PATCH 133/144] scripts: add check-if-release --- scripts/check-if-release.js | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100755 scripts/check-if-release.js diff --git a/scripts/check-if-release.js b/scripts/check-if-release.js new file mode 100755 index 0000000000..57a0ad792c --- /dev/null +++ b/scripts/check-if-release.js @@ -0,0 +1,101 @@ +#!/usr/bin/env node +/* eslint-disable import/no-extraneous-dependencies */ +/* + * 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. + */ + +// This script is used to determine whether a particular commit has changes +// that should lead to a release. It is run as part of the main master build +// to determine whether the release flow should be run as well. +// +// It has the following output which can be used later in GitHub actions: +// +// needs_release = 'true' | 'false' + +const { execFile: execFileCb } = require('child_process'); +const { resolve: resolvePath } = require('path'); +const { promises: fs } = require('fs'); +const { promisify } = require('util'); + +const parentRef = process.env.COMMIT_SHA_BEFORE || 'HEAD^'; + +const execFile = promisify(execFileCb); + +async function runPlain(cmd, ...args) { + try { + const { stdout } = await execFile(cmd, args, { shell: true }); + return stdout.trim(); + } catch (error) { + if (error.stderr) { + process.stderr.write(error.stderr); + } + if (!error.code) { + throw error; + } + throw new Error( + `Command '${[cmd, ...args].join(' ')}' failed with code ${error.code}`, + ); + } +} + +async function main() { + process.cwd(resolvePath(__dirname, '..')); + + const diff = await runPlain( + 'git', + 'diff', + '--name-only', + parentRef, + 'packages/*/package.json', + 'plugins/*/package.json', + ); + const packageList = diff.split(/^(.*)$/gm).filter(s => s.trim()); + + const packageVersions = await Promise.all( + packageList.map(async path => { + const { name, version: newVersion } = JSON.parse( + await fs.readFile(path, 'utf8'), + ); + const { version: oldVersion } = JSON.parse( + await runPlain('git', 'show', `${parentRef}:${path}`), + ); + return { name, oldVersion, newVersion }; + }), + ); + + const newVersions = packageVersions.filter( + ({ oldVersion, newVersion }) => oldVersion !== newVersion, + ); + + if (newVersions.length === 0) { + console.log('No package version bumps detected, no release needed'); + console.log(`::set-output name=needs_release::false`); + return; + } + + console.log('Package version bumps detected, a new release is needed'); + const maxLength = Math.max(...newVersions.map(_ => _.name.length)); + for (const { name, oldVersion, newVersion } of newVersions) { + console.log( + ` ${name.padEnd(maxLength, ' ')} ${oldVersion} -> ${newVersion}`, + ); + } + console.log(`::set-output name=needs_release::true`); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); From 5fcb150cd475373d11241e1cbde42f191ebd48ea Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 15 Jan 2021 16:28:11 +0100 Subject: [PATCH 134/144] github/workflows: update main master build to use check-if-release script --- .github/workflows/master.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 6f45968002..68b08224ac 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -8,6 +8,9 @@ jobs: build: runs-on: ubuntu-latest + outputs: + needs_release: ${{ steps.release_check.outputs.needs_release }} + strategy: matrix: node-version: [12.x, 14.x] @@ -47,6 +50,15 @@ jobs: run: yarn install --frozen-lockfile # End of yarn setup + - name: Fetch previous commit for release check + run: git fetch origin '${{ github.event.before }}' + + - name: Check if release + id: release_check + run: node scripts/check-if-release.js + env: + COMMIT_SHA_BEFORE: '${{ github.event.before }}' + - name: validate config run: yarn backstage-cli config:check @@ -82,9 +94,10 @@ jobs: # We can't re-use the output from the above step, but we'll have a guaranteed node_modules cache and # only run the build steps that are necessary for publishing release: - if: contains(github.event.commits.*.author.username, 'github-actions[bot]') && contains(github.event.head_commit.message, 'from backstage/changeset-release/master') needs: build + if: needs.build.outputs.needs_release == 'true' + runs-on: ubuntu-latest strategy: From ecd214c4cac66bd33890ab80331d8c72d501ea7f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 17 Jan 2021 19:10:18 +0100 Subject: [PATCH 135/144] scripts/create-github-release: fall back release message to PR contents if not a release PR --- scripts/create-github-release.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/scripts/create-github-release.js b/scripts/create-github-release.js index a9bfb9f5b3..1d6a493db3 100755 --- a/scripts/create-github-release.js +++ b/scripts/create-github-release.js @@ -53,7 +53,8 @@ if (!BOOL_CREATE_RELEASE) { const GH_OWNER = 'backstage'; const GH_REPO = 'backstage'; -const EXPECTED_COMMIT_MESSAGE = /^Merge pull request #(?[0-9]+) from backstage\/changeset-release\/master\n\nVersion Packages$/; +const EXPECTED_COMMIT_MESSAGE = /^Merge pull request #(?[0-9]+) from/; +const CHANGESET_RELEASE_BRANCH = 'backstage/changeset-release/master'; // Initialize a GitHub client const octokit = new Octokit({ @@ -116,7 +117,7 @@ async function getCommitMessageUsingTagName(tagName) { } // There is a PR number in our expected commit message. Get the description of that PR. -async function getPrDescriptionFromCommitMessage(commitMessage) { +async function getReleaseDescriptionFromCommitMessage(commitMessage) { // It should exactly match the pattern of changeset commit message, or else will abort. const expectedMessage = RegExp(EXPECTED_COMMIT_MESSAGE); if (!expectedMessage.test(commitMessage)) { @@ -131,20 +132,19 @@ async function getPrDescriptionFromCommitMessage(commitMessage) { `Identified the changeset Pull request - https://github.com/backstage/backstage/pull/${prNumber}`, ); - const prData = await octokit.pulls.get({ + const { data } = await octokit.pulls.get({ owner: GH_OWNER, repo: GH_REPO, pull_number: prNumber, }); - return prData.data.body; -} + // Use the PR description to prepare for the release description + const isChangesetRelease = commitMessage.includes(CHANGESET_RELEASE_BRANCH); + if (isChangesetRelease) { + return data.body.split('\n').slice(3).join('\n'); + } -// Use the PR description to prepare for the release description -async function prepareReleaseDescription(prDescription) { - // TODO: Refine prDescription to remove the lines containing "Update Dependencies" - // Remove everything in the beginning until changelogs. - return prDescription.split('\n').slice(3).join('\n'); + return data.body; } // Create Release on GitHub. @@ -178,8 +178,9 @@ async function createRelease(releaseDescription) { async function main() { const commitMessage = await getCommitMessageUsingTagName(TAG_NAME); - const prDescription = await getPrDescriptionFromCommitMessage(commitMessage); - const releaseDescription = await prepareReleaseDescription(prDescription); + const releaseDescription = await getReleaseDescriptionFromCommitMessage( + commitMessage, + ); await createRelease(releaseDescription); } From e6c06db86330578ca1d3e22f9809fe9125f4981e Mon Sep 17 00:00:00 2001 From: Ioannis Georgoulas Date: Sun, 17 Jan 2021 19:56:34 +0000 Subject: [PATCH 136/144] Adopters: Add Paddle.com --- .github/styles/vocab.txt | 2 ++ ADOPTERS.md | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 3188d059f6..c3c67ab66e 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -78,6 +78,7 @@ Firekube Fiverr freben Fredrik +Georgoulas gitbeaker GitHub GitLab @@ -100,6 +101,7 @@ incentivised inlined inlinehilite interop +Ioannis JavaScript jq js diff --git a/ADOPTERS.md b/ADOPTERS.md index f7d1114008..363c092198 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -18,3 +18,4 @@ | [Zalando SE](https://www.zalando.de) | [@leviferreira](https://github.com/leviferreira) | Building V2 of the Internal Development Portal. | | [LegalZoom](https://legalzoom.com) | [@backjo](https://github.com/backjo) | Developer portal - hub for all engineering projects and metadata. | | [Expedia Group](https://www.expediagroup.com) | [Mike Turner](mailto:miturner@expediagroup.com), [Sneha Kumar](mailto:snkumar@expediagroup.com), [@guillermomanzo](https://github.com/guillermomanzo) | EG Common Developer Toolkit | +| [Paddle.com](https://paddle.com) | [Ioannis Georgoulas](https://github.com/geototti21) | Developer portal (Tech Docs, Service Catalog, Internal Tooling), we use vanilla Backstage FE and custom BE implementation in Go | From cca03162bcc9a54b01a891e8256101b9e5a16a45 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Sun, 17 Jan 2021 21:53:37 +0100 Subject: [PATCH 137/144] chore: Remove yarn docker-build:app command and related docs --- contrib/docker/frontend-with-nginx/Dockerfile | 2 -- docs/overview/architecture-overview.md | 11 ----------- package.json | 1 - 3 files changed, 14 deletions(-) diff --git a/contrib/docker/frontend-with-nginx/Dockerfile b/contrib/docker/frontend-with-nginx/Dockerfile index 174548a90c..a444c9de83 100644 --- a/contrib/docker/frontend-with-nginx/Dockerfile +++ b/contrib/docker/frontend-with-nginx/Dockerfile @@ -8,8 +8,6 @@ FROM nginx:mainline # This dockerfile requires the app to be built on the host first, as it # simply copies in the build output into the image. -# The safest way to build this image is to use `yarn docker-build:app` - RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/* COPY packages/app/dist /usr/share/nginx/html diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index d8069c8665..f2f2bc72c8 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -185,17 +185,6 @@ separate Docker images. ![Boxes around the architecture to indicate how it is containerised](../assets/architecture-overview/containerised.png) -The frontend container can be built with a provided command. - -```bash -yarn install -yarn tsc -yarn run docker-build:app -``` - -Running this will simply generate a Docker container containing the contents of -the UIs `dist` directory. - The backend container can be built by running the following command: ```bash diff --git a/package.json b/package.json index 8376915729..71a54ceee5 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "lint:all": "lerna run lint --", "lint:type-deps": "node scripts/check-type-dependencies.js", "docgen": "lerna run docgen", - "docker-build:app": "yarn workspace example-app build && docker build . -t spotify/backstage", "docker-build": "yarn tsc && yarn workspace example-backend build-image", "create-plugin": "backstage-cli create-plugin --scope backstage --no-private", "remove-plugin": "backstage-cli remove-plugin", From 614ee0d1db49b4c7549dbe61d99aa5a5b4712e1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Jan 2021 05:02:30 +0000 Subject: [PATCH 138/144] chore(deps-dev): bump @storybook/react from 6.1.11 to 6.1.14 Bumps [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/app/react) from 6.1.11 to 6.1.14. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v6.1.14/app/react) Signed-off-by: dependabot[bot] --- yarn.lock | 288 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 199 insertions(+), 89 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7fdc2c3a62..4f10347ff1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1989,20 +1989,6 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.12.1" -"@babel/plugin-transform-react-jsx-self@^7.12.1": - version "7.12.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.1.tgz#ef43cbca2a14f1bd17807dbe4376ff89d714cf28" - integrity sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-transform-react-jsx-source@^7.12.1": - version "7.12.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.1.tgz#d07de6863f468da0809edcf79a1aa8ce2a82a26b" - integrity sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-react-jsx@^7.0.0": version "7.10.4" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz#673c9f913948764a4421683b2bef2936968fddf2" @@ -2023,16 +2009,6 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.12.1" -"@babel/plugin-transform-react-jsx@^7.12.7": - version "7.12.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.7.tgz#8b14d45f6eccd41b7f924bcb65c021e9f0a06f7f" - integrity sha512-YFlTi6MEsclFAPIDNZYiCRbneg1MFGao9pPG9uD5htwE0vDbPaMUMeYd6itWjw7K4kro4UbdQf3ljmFl9y48dQ== - dependencies: - "@babel/helper-builder-react-jsx" "^7.10.4" - "@babel/helper-builder-react-jsx-experimental" "^7.12.4" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-syntax-jsx" "^7.12.1" - "@babel/plugin-transform-react-pure-annotations@^7.12.1": version "7.12.1" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz#05d46f0ab4d1339ac59adf20a1462c91b37a1a42" @@ -2229,7 +2205,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.12.1": +"@babel/preset-react@^7.12.1", "@babel/preset-react@^7.12.5", "@babel/preset-react@^7.9.4": version "7.12.10" resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.10.tgz#4fed65f296cbb0f5fb09de6be8cddc85cc909be9" integrity sha512-vtQNjaHRl4DUpp+t+g4wvTHsLQuye+n0H/wsXIZRn69oz/fvNC7gQ4IK73zGJBaxvHoxElDvnYCthMcT7uzFoQ== @@ -2240,19 +2216,6 @@ "@babel/plugin-transform-react-jsx-development" "^7.12.7" "@babel/plugin-transform-react-pure-annotations" "^7.12.1" -"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.9.4": - version "7.12.7" - resolved "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.7.tgz#36d61d83223b07b6ac4ec55cf016abb0f70be83b" - integrity sha512-wKeTdnGUP5AEYCYQIMeXMMwU7j+2opxrG0WzuZfxuuW9nhKvvALBjl67653CWamZJVefuJGI219G591RSldrqQ== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/plugin-transform-react-display-name" "^7.12.1" - "@babel/plugin-transform-react-jsx" "^7.12.7" - "@babel/plugin-transform-react-jsx-development" "^7.12.7" - "@babel/plugin-transform-react-jsx-self" "^7.12.1" - "@babel/plugin-transform-react-jsx-source" "^7.12.1" - "@babel/plugin-transform-react-pure-annotations" "^7.12.1" - "@babel/preset-typescript@^7.12.1": version "7.12.7" resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz#fc7df8199d6aae747896f1e6c61fc872056632a3" @@ -5292,7 +5255,7 @@ react-syntax-highlighter "^13.5.0" regenerator-runtime "^0.13.7" -"@storybook/addons@6.1.11", "@storybook/addons@^6.1.11": +"@storybook/addons@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.1.11.tgz#cb4578411ca00ccb206b484df5a171ccaca34719" integrity sha512-OZXsdmn60dVe482l9zWxzOqqJApD2jggk/8QJKn3/Ub9posmqdqg712bW6v71BBe0UXXG/QfkZA7gcyiyEENbw== @@ -5307,6 +5270,21 @@ global "^4.3.2" regenerator-runtime "^0.13.7" +"@storybook/addons@6.1.14", "@storybook/addons@^6.1.11": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/addons/-/addons-6.1.14.tgz#2b81304bbe696923df95cdcf85cfc592d10f4065" + integrity sha512-HlpmV7aejp/MeW8bo/WKME3i71gi0men9qcwoovjDjnSF6jXoNLT336a5udKXdHqYSZgzdyURlgLtilCWkWaJQ== + dependencies: + "@storybook/api" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/router" "6.1.14" + "@storybook/theming" "6.1.14" + core-js "^3.0.1" + global "^4.3.2" + regenerator-runtime "^0.13.7" + "@storybook/api@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/api/-/api-6.1.11.tgz#1e0b798203df823ac21184386258cf8b5f17f440" @@ -5332,6 +5310,31 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/api@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/api/-/api-6.1.14.tgz#20035dd336aba1c5a0f8c83c8c14a2edaf4db891" + integrity sha512-gWcC/xEW8HL5DsocLujHBUdoNsl4YW1Zx1Y4SBbLCyrhj8v4JudJpylwJpOUBDe/GESXq1zqvNKvUPtI8DQNyw== + dependencies: + "@reach/router" "^1.3.3" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/csf" "0.0.1" + "@storybook/router" "6.1.14" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.1.14" + "@types/reach__router" "^1.3.5" + core-js "^3.0.1" + fast-deep-equal "^3.1.1" + global "^4.3.2" + lodash "^4.17.15" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + store2 "^2.7.1" + telejson "^5.0.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/channel-postmessage@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.1.11.tgz#62c1079f04870dd27925bd538a2020e7380daa2e" @@ -5345,6 +5348,19 @@ qs "^6.6.0" telejson "^5.0.2" +"@storybook/channel-postmessage@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.1.14.tgz#41f3115895010dad9fb30f4ac381e4f904b1e50c" + integrity sha512-If83dXXW9mKIRuvuWhWa/zkEw/F0FDgikp33x8436J3rWCh3recp27kffFRrKG0YDMpFSk/Ci5G47E9zn9SCjw== + dependencies: + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" + core-js "^3.0.1" + global "^4.3.2" + qs "^6.6.0" + telejson "^5.0.2" + "@storybook/channels@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.1.11.tgz#a93a83746ad78dd40e1c056029f6d93b17bb66bc" @@ -5354,6 +5370,15 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/channels@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/channels/-/channels-6.1.14.tgz#c479190ebb853a603f3ed90fc470534a02eb46eb" + integrity sha512-vP19IB2FXj8SiFbQ9ETljEBienL+KRMLgMzz3Ta3nZj/OfjJJbIuj42ZfexQGV4mS0Bo+OW+qT7VMIY6fulnFw== + dependencies: + core-js "^3.0.1" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/client-api@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.1.11.tgz#d25aac484ca84a1acb01d450e756a62408f00c1a" @@ -5378,6 +5403,30 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" +"@storybook/client-api@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.1.14.tgz#6daf56743cc72e13f05fff3d2ac554897cc9f9fd" + integrity sha512-pIDSlS48bhJdtgNg7sXV1NmLJtB0ebRHJI9htIiqtL7EGQenb4+Bbwflhj1j51OEkuM+bQsAAZxq5deiUQEGVw== + dependencies: + "@storybook/addons" "6.1.14" + "@storybook/channel-postmessage" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/csf" "0.0.1" + "@types/qs" "^6.9.0" + "@types/webpack-env" "^1.15.3" + core-js "^3.0.1" + global "^4.3.2" + lodash "^4.17.15" + memoizerific "^1.11.3" + qs "^6.6.0" + regenerator-runtime "^0.13.7" + stable "^0.1.8" + store2 "^2.7.1" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + "@storybook/client-logger@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.1.11.tgz#5dd092e4293e5f58f7e89ddbc6eb2511b7d60954" @@ -5386,6 +5435,14 @@ core-js "^3.0.1" global "^4.3.2" +"@storybook/client-logger@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.1.14.tgz#216b9c1332ffa3a3473dad837780a3b14f686bae" + integrity sha512-NSO8nVsp6o0eoQ1Drlu66KXpl6DPuq02Kj8AhttGzvqSYB50SV4CV+wceBcg77tIVu5QmQ+71hAEVXhx7sjRHA== + dependencies: + core-js "^3.0.1" + global "^4.3.2" + "@storybook/components@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/components/-/components-6.1.11.tgz#edd5db7fe43f47b5a7ab515840795a89d931512e" @@ -5412,6 +5469,32 @@ react-textarea-autosize "^8.1.1" ts-dedent "^2.0.0" +"@storybook/components@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/components/-/components-6.1.14.tgz#4ea47edfa0a3e4a26882aa5a1eb90c1ec86e6f71" + integrity sha512-Nxsp/9o1tqfY8s6RBWNHyM03A5D9k56Kr/4VNa++CbDrz1+TIxpYlDgS4sllUlXyvICLfk3sUtg3KS5CPl2iZA== + dependencies: + "@popperjs/core" "^2.5.4" + "@storybook/client-logger" "6.1.14" + "@storybook/csf" "0.0.1" + "@storybook/theming" "6.1.14" + "@types/overlayscrollbars" "^1.9.0" + "@types/react-color" "^3.0.1" + "@types/react-syntax-highlighter" "11.0.4" + core-js "^3.0.1" + fast-deep-equal "^3.1.1" + global "^4.3.2" + lodash "^4.17.15" + markdown-to-jsx "^6.11.4" + memoizerific "^1.11.3" + overlayscrollbars "^1.10.2" + polished "^3.4.4" + react-color "^2.17.0" + react-popper-tooltip "^3.1.1" + react-syntax-highlighter "^13.5.0" + react-textarea-autosize "^8.1.1" + ts-dedent "^2.0.0" + "@storybook/core-events@6.1.11": version "6.1.11" resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.1.11.tgz#d50e8ec90490f9a7180a8c8a83afb6dcfe47ed66" @@ -5419,10 +5502,17 @@ dependencies: core-js "^3.0.1" -"@storybook/core@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/core/-/core-6.1.11.tgz#ed9d3b513794c604ab11180f6a014924b871179e" - integrity sha512-pYOOQwiNJ5myLRn6p6nnLUjjjISHK/N55vS4HFnETYSaRLA++h1coN1jk7Zwt89dOQTdF0EsTJn+6snYOC+lxQ== +"@storybook/core-events@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.1.14.tgz#a3165e32cefd6be7326bbad4b8140653bdfa0426" + integrity sha512-tpM3VDvzqgRY7S17CRglgt1625rxNoyEwrLQiNcZkUPyO0rpaacPqVEbPCtcTmUeboI1bLdnSQIjT9B0/Y2Pww== + dependencies: + core-js "^3.0.1" + +"@storybook/core@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/core/-/core-6.1.14.tgz#17e724a5b94d6e1bb557e213b8176660d2d14762" + integrity sha512-lHKZmfLAo2VGtF/yrZkkWMYgmFRNKbzIDxYJGp8USyUQyTfEpz2qqJlBdoD6rxr1hFPM2954tIKwh8iPhT2PFQ== dependencies: "@babel/core" "^7.12.3" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -5446,20 +5536,20 @@ "@babel/preset-react" "^7.12.1" "@babel/preset-typescript" "^7.12.1" "@babel/register" "^7.12.1" - "@storybook/addons" "6.1.11" - "@storybook/api" "6.1.11" - "@storybook/channel-postmessage" "6.1.11" - "@storybook/channels" "6.1.11" - "@storybook/client-api" "6.1.11" - "@storybook/client-logger" "6.1.11" - "@storybook/components" "6.1.11" - "@storybook/core-events" "6.1.11" + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/channel-postmessage" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-api" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core-events" "6.1.14" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.1.11" - "@storybook/router" "6.1.11" + "@storybook/node-logger" "6.1.14" + "@storybook/router" "6.1.14" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.1.11" - "@storybook/ui" "6.1.11" + "@storybook/theming" "6.1.14" + "@storybook/ui" "6.1.14" "@types/glob-base" "^0.3.0" "@types/micromatch" "^4.0.1" "@types/node-fetch" "^2.5.4" @@ -5533,10 +5623,10 @@ dependencies: lodash "^4.17.15" -"@storybook/node-logger@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.1.11.tgz#8e0d058b4804f2fea03c9d7d331b8e2d02f3b7ff" - integrity sha512-MASonXDWpSMU9HF9mqbGOR1Ps/DTJ8AVmYD50+OnB9kXl4M42Dliobeq7JwKFMnZ42RelUCCSXdWW80hGrUKKA== +"@storybook/node-logger@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.1.14.tgz#e5294f986e3ec5c67b2738895b9d16c9a2b667fa" + integrity sha512-3jrw7coAwFXZu4qK1vm54bCPhNRvxjG+7jISbhhocDoNIv0nLWL3+tJyrC5/k/XHQiUlLkhEzpMaASADmkttNw== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.0.0" @@ -5545,16 +5635,16 @@ pretty-hrtime "^1.0.3" "@storybook/react@^6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/react/-/react-6.1.11.tgz#e94403cd878c66b445df993bad9bec9023db3ebe" - integrity sha512-EmR7yvVW6z6AYhfzAgJMGR/5+igeBGa1EePaEIibn51r5uboSB72N12NaADyF2OaycIdV+0sW6vP9Zvlvexa/w== + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/react/-/react-6.1.14.tgz#436e9b90096b1d7c83f7f073b5baf47212b2e425" + integrity sha512-M99wHjc/5z+Wz1FdFaScVs6dyAi/6PdcIx5Fyip6Qd8aKwm1XyYoOMql5Vu3Cf560feDYCKS4phzyEZ7EJy+EQ== dependencies: "@babel/preset-flow" "^7.12.1" "@babel/preset-react" "^7.12.1" "@pmmmwh/react-refresh-webpack-plugin" "^0.4.2" - "@storybook/addons" "6.1.11" - "@storybook/core" "6.1.11" - "@storybook/node-logger" "6.1.11" + "@storybook/addons" "6.1.14" + "@storybook/core" "6.1.14" + "@storybook/node-logger" "6.1.14" "@storybook/semver" "^7.3.2" "@types/webpack-env" "^1.15.3" babel-plugin-add-react-displayname "^0.0.5" @@ -5583,6 +5673,18 @@ memoizerific "^1.11.3" qs "^6.6.0" +"@storybook/router@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/router/-/router-6.1.14.tgz#f6aef8c9dabf19bf06dddd80907e66369261fdde" + integrity sha512-rMaUCYzgfVLwFWo3A1Q/weSv8FBqCLmHY+3+t6ao7OV6NYjR0XgLKRzHrXq1uYdbMxWeIKhN2tIt/LR43bmDjQ== + dependencies: + "@reach/router" "^1.3.3" + "@types/reach__router" "^1.3.5" + core-js "^3.0.1" + global "^4.3.2" + memoizerific "^1.11.3" + qs "^6.6.0" + "@storybook/semver@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0" @@ -5626,21 +5728,39 @@ resolve-from "^5.0.0" ts-dedent "^2.0.0" -"@storybook/ui@6.1.11": - version "6.1.11" - resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.1.11.tgz#2e5a5df010f2bb75a09a0fd0439fc8e62f8c89e5" - integrity sha512-Qth2dxS5+VbKHcqgkiKpeD+xr/hRUuUIDUA/2Ierh/BaA8Up/krlso/mCLaQOa5E8Og9WJAdDFO0cUbt939c2Q== +"@storybook/theming@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/theming/-/theming-6.1.14.tgz#fecb66cab22d3b3218b4a98a9c210eb8a7be91e8" + integrity sha512-S+t30y4FqBTXWoVr+dtxVJ/ywiQGHBclBd9aUunbdCV4mMFra5InNo2CWn+RJlNEauLZ93gRIEzSFchIbzLk1A== dependencies: "@emotion/core" "^10.1.1" - "@storybook/addons" "6.1.11" - "@storybook/api" "6.1.11" - "@storybook/channels" "6.1.11" - "@storybook/client-logger" "6.1.11" - "@storybook/components" "6.1.11" - "@storybook/core-events" "6.1.11" - "@storybook/router" "6.1.11" + "@emotion/is-prop-valid" "^0.8.6" + "@emotion/styled" "^10.0.23" + "@storybook/client-logger" "6.1.14" + core-js "^3.0.1" + deep-object-diff "^1.1.0" + emotion-theming "^10.0.19" + global "^4.3.2" + memoizerific "^1.11.3" + polished "^3.4.4" + resolve-from "^5.0.0" + ts-dedent "^2.0.0" + +"@storybook/ui@6.1.14": + version "6.1.14" + resolved "https://registry.npmjs.org/@storybook/ui/-/ui-6.1.14.tgz#766d696480ee6f6a5a0454ccb2f101c38a0eb9d2" + integrity sha512-DTW2TM05jTMKxh8LzUGk3g5a528PgJxrtgODFU6zzwSg2+LwdmSDtd1HAxopt2vpfTyQyX+6WN2H+lMNwfQTAQ== + dependencies: + "@emotion/core" "^10.1.1" + "@storybook/addons" "6.1.14" + "@storybook/api" "6.1.14" + "@storybook/channels" "6.1.14" + "@storybook/client-logger" "6.1.14" + "@storybook/components" "6.1.14" + "@storybook/core-events" "6.1.14" + "@storybook/router" "6.1.14" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.1.11" + "@storybook/theming" "6.1.14" "@types/markdown-to-jsx" "^6.11.0" copy-to-clipboard "^3.0.8" core-js "^3.0.1" @@ -7257,12 +7377,7 @@ "@types/serve-static" "*" "@types/webpack" "*" -"@types/webpack-env@^1.15.2": - version "1.15.3" - resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.15.3.tgz#fb602cd4c2f0b7c0fb857e922075fdf677d25d84" - integrity sha512-5oiXqR7kwDGZ6+gmzIO2lTC+QsriNuQXZDWNYRV3l2XRN/zmPgnC21DLSx2D05zvD8vnXW6qUg7JnXZ4I6qLVQ== - -"@types/webpack-env@^1.15.3": +"@types/webpack-env@^1.15.2", "@types/webpack-env@^1.15.3": version "1.16.0" resolved "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.0.tgz#8c0a9435dfa7b3b1be76562f3070efb3f92637b4" integrity sha512-Fx+NpfOO0CpeYX2g9bkvX8O5qh9wrU1sOF4g8sft4Mu7z+qfe387YlyY8w8daDyDsKY5vUxM0yxkAYnbkRbZEw== @@ -22121,12 +22236,7 @@ regenerator-runtime@^0.11.0: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.5: - version "0.13.5" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== - -regenerator-runtime@^0.13.7: +regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.5, regenerator-runtime@^0.13.7: version "0.13.7" resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== From d5b92637bcc854b786d3237d50ba2544ddad728f Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 18 Jan 2021 09:41:36 +0100 Subject: [PATCH 139/144] Show domain for systems and hide other fields if the don't apply for the kind --- .changeset/healthy-comics-drive.md | 3 +- .../src/components/AboutCard/AboutContent.tsx | 70 +++++++++++++------ 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/.changeset/healthy-comics-drive.md b/.changeset/healthy-comics-drive.md index 3847274eda..4b532dc853 100644 --- a/.changeset/healthy-comics-drive.md +++ b/.changeset/healthy-comics-drive.md @@ -2,4 +2,5 @@ '@backstage/plugin-catalog': patch --- -Display the owner and system as links to the entity pages in the about card. +Display the owner, system, and domain as links to the entity pages in the about card. +Only display fields in the about card that are applicable to the entity kind. diff --git a/plugins/catalog/src/components/AboutCard/AboutContent.tsx b/plugins/catalog/src/components/AboutCard/AboutContent.tsx index d0c25c69b6..178debb42a 100644 --- a/plugins/catalog/src/components/AboutCard/AboutContent.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutContent.tsx @@ -37,9 +37,15 @@ type Props = { export const AboutContent = ({ entity }: Props) => { const classes = useStyles(); + const isSystem = entity.kind.toLowerCase() === 'system'; + const isDomain = entity.kind.toLowerCase() === 'domain'; + const isResource = entity.kind.toLowerCase() === 'resource'; const [partOfSystemRelation] = getEntityRelations(entity, RELATION_PART_OF, { kind: 'system', }); + const [partOfDomainRelation] = getEntityRelations(entity, RELATION_PART_OF, { + kind: 'domain', + }); const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); return ( @@ -57,28 +63,48 @@ export const AboutContent = ({ entity }: Props) => { ))} - - {partOfSystemRelation && ( - - )} - - - + {isSystem && ( + + {partOfDomainRelation && ( + + )} + + )} + {!isSystem && !isDomain && ( + + {partOfSystemRelation && ( + + )} + + )} + {!isSystem && !isDomain && ( + + )} + {!isSystem && !isDomain && !isResource && ( + + )} Date: Mon, 18 Jan 2021 12:01:54 +0100 Subject: [PATCH 140/144] Search Roadmap and Architecture (#4030) * search documentation and roadmap * fixup * add link to architecture issue * formatting * add nav to mkdocs yaml and microsite sidebar * use backstage search instead of backstage global search * add file extension to app_config * Update docs/features/search/README.md Co-authored-by: Himanshu Mishra * replace Big Picture with Architecture * search architecture wip * changes to architecture and documentation to it * update used user profiles according to the glossary * prettier ignore description * clarify architecture bullet points with examples Co-authored-by: Himanshu Mishra --- docs/assets/search/architecture.drawio.svg | 541 +++++++++++++++++++++ docs/features/search/README.md | 100 ++++ docs/features/search/architecture.md | 39 ++ microsite/sidebars.json | 8 + mkdocs.yml | 3 + 5 files changed, 691 insertions(+) create mode 100644 docs/assets/search/architecture.drawio.svg create mode 100644 docs/features/search/README.md create mode 100644 docs/features/search/architecture.md diff --git a/docs/assets/search/architecture.drawio.svg b/docs/assets/search/architecture.drawio.svg new file mode 100644 index 0000000000..736d4545f0 --- /dev/null +++ b/docs/assets/search/architecture.drawio.svg @@ -0,0 +1,541 @@ + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ + + +
+
+ + + + +
+
+
+
+
+
+
+
+
+
+ + + +
+
+ + + + + + + +
+
+
+ App Package: <Route path="/search" element={<... />} /> +
+
+
+
+ + App Package: <Route path="/search" element={<... />}... + +
+
+ + + + +
+
+
+ @backstage/plugin-search-backend +
+
+
+
+ + @backstage/plugin-search-backend + +
+
+ + + + +
+
+
+ Other Plugins +
+ (TechDocs, Catalogue, Etc) +
+
+
+
+ + Other Plugins... + +
+
+ + + + +
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+ + + + +
+
+
+ @backstage/plugin-search +
+
+
+
+ + @backstage/plugin-search + +
+
+ + + + + + +
+
+
+ Other Backend Plugin (TechDocs, Catalogue, Etc) +
+
+
+
+ + Other Backend Plugin (TechDocs, Catalogue, Etc) + +
+
+ + + + +
+
+
+ Search Engine (Elastic, Solr, SaaS, etc.) +
+
+
+
+ + Search Engine (Elastic, Solr, SaaS, et... + +
+
+ + + + +
+
+
+ Search Engine Integration Layer +
+
+
+
+ + Search Engine Integration Layer + +
+
+ + + + + + + + + + + + +
+
+
+ + 1 2 3 + +
+
+
+
+ + 1 2 3 + +
+
+ + + + +
+
+
+ + X number of search results + +
+
+
+
+ + X number of sear... + +
+
+ + + + + + + + +
+
+
+ + Components + +
+
+
+
+ + Components + +
+
+ + + + + + +
+
+
+ Pass Search +
+ Term and Filters +
+ and then +
+ Return Results +
+
+
+
+ + Pass Search... + +
+
+ + + + +
+
+
+ + Search API + +
+
+
+
+ + Search API + +
+
+ + + + + + +
+
+
+ + Components + +
+
+
+
+ + Components + +
+
+ + + + + +
+
+
+ + Scheduler + +
+
+
+
+ + Scheduler + +
+
+ + + + +
+
+
+ + Gather Documents + +
+
+
+
+ + Gather D... + +
+
+ + + + + + + +
+
+
+ + Collate Documents +
+ Or Metadata +
+
+
+
+
+ + Collate Docum... + +
+
+ + + + +
+
+
+ + API Endpoint +
+
+
+
+
+
+ + API Endp... + +
+
+ + + + +
+
+
+ + Query Processing + +
+
+
+
+ + Query Processing + +
+
+ + + + +
+
+
+ + Index Processing + +
+
+
+
+ + Index Processi... + +
+
+ + + + +
+
+
+ + Index Processing + +
+
+
+
+ + Index Processi... + +
+
+ + + + +
+
+
+ + Query Processing + +
+
+
+
+ + Query Processing + +
+
+ + + + +
+
+
+ + + Manage Index + +
+ Create, Remove, Replace Documents and Indices +
+
+
+
+
+ + Manage Index... + +
+
+ + + + + + + +
+
+
+ + Compile and Execute Query from Term and Filters + +
+
+
+
+ + Compile and Execut... + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
diff --git a/docs/features/search/README.md b/docs/features/search/README.md new file mode 100644 index 0000000000..0b13b47979 --- /dev/null +++ b/docs/features/search/README.md @@ -0,0 +1,100 @@ +--- +id: search-overview +title: Search Documentation +sidebar_label: Overview +# prettier-ignore +description: Backstage Search lets you find the right information you are looking for in the Backstage ecosystem. +--- + +# Backstage Search + +## What is it? + +Backstage Search lets you find the right information you are looking for in the +Backstage ecosystem. + +## Features + +- A federated, faceted search, searching across all entities registered in your + Backstage instance. + +- A search that lets you plug in your own search engine of choice. + +- A standardized search API where you can choose to index other plugins data. + +## Project roadmap + +| Version | Description | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Backstage Search V.0 ✅ | Search Frontend letting you search through the entities of the software catalog. [See V.0 Use Cases.](#backstage-search-v0) | +| Backstage Search V.1 ⌛ | Basic “out-of-the-box” in-memory indexing process of entities, and their metadata, registered to the Software Catalog. [See V.1 Use Cases.](#backstage-search-v1) | +| Backstage Search V.2 ⌛ | Search Backend responsible for the indexing process of entities, and their metadata, registered to the Software Catalog. [See V.2 Use Cases.](#backstage-search-v2) | +| Backstage Search V.3 ⌛ | Standardized Search API lets you index other plugins data to the search engine of choice. [See V.3 Use Cases.](#backstage-search-v3) | + +## Use Cases + +#### Backstage Search V.0 + +- As a software engineer I should be able to navigate to a search page and + search for entities registered in the Software Catalog. +- As a software engineer I should be able to use the search input field in the + sidebar to search for entities registered in the Software Catalog. +- As a software engineer I should be able to see the number of results my search + returned. +- As a software engineer I should be able to filter on metadata (kind, + lifecycle) when I’ve performed a search. +- As a software engineer I should be able to hide the filters if I don’t need to + use them. + +#### Backstage Search V.1 + +- As a software engineer I should be able to get a match of a search on all + entity metadata (e.g. owner, name, description, kind). +- As an integrator I should not have to plug in any search engine, instead I can + use the out of the box in-memory indexing process to index entities and their + metadata registered in the Software Catalog. + +#### Backstage Search V.2 + +- As an integrator I should be able to spin up an instance of ElasticSearch. +- As an integrator I should be able to define a ElasticSearch cluster in my + app_config.yaml where my data gets indexed to. + +more to come... + +#### Backstage Search V.3 + +- As a contributor I should be able to integrate plugin data to the indexing + process of Backstage Search by using the standardized API. +- As a software engineer I should be able to search for all content (for + example, entities, metadata, documentation) in backstage search. + +more to come... + +## Search Engines Supported + +See [Backstage Search Architecture](architecture.md) to get an overview of how +the search engines are used. + +| Search Engine | Support Status | +| ------------- | -------------- | +| ElasticSearch | Not yet ❌ | + +[Reach out to us](#feedback) if you want to chat about support for more search +engines. + +## Tech Stack + +| Stack | Location | +| --------------- | ------------------------ | +| Frontend Plugin | @backstage/plugin-search | +| Backend Plugin | ⌛ | + +## Feedback + +For any questions of feedback, reach out to us in the `#search` channel of our +[Discord chatroom](https://github.com/backstage/backstage#community). + +We are still looking for feedback to improve the architecture to fit your +use-case, see +[this open issue](https://github.com/backstage/backstage/issues/4078). diff --git a/docs/features/search/architecture.md b/docs/features/search/architecture.md new file mode 100644 index 0000000000..3075719b9d --- /dev/null +++ b/docs/features/search/architecture.md @@ -0,0 +1,39 @@ +--- +id: architecture +title: Search Architecture +description: Documentation on Search Architecture +--- + +# Search Architecture + +> _This is a proposed architecture which has not been implemented yet. We are +> still looking for feedback to improve the architecture to fit your use-case, +> see [this open issue](https://github.com/backstage/backstage/issues/4078)._ + +Below you can explore the Search Architecture. Our aim with this architecture is +to support a wide variety of search engines, while providing a simple developer +experience for plugin developers, and a good out-of-the-box experience for +Backstage end-users. + +Search Architecture + +At a base-level, we want to support the following: + +- We aim to enable the capability to search across the entire Backstage + ecosystem by decoupling search from content management. +- We aim to enable the capability to deploy Backstage using any search engine, + by providing an integration and translation layer between the core search + plugin and search engine specific logic that can be extended for different + search engines. We may also introduce the ability to replace the backend API + endpoint with a custom endpoint for simpler customization. + +More advanced use-cases we hope to support with this architecture include: + +- It should be easy for any plugin to expose new content to search. (e.g. entity + metadata, documentation from TechDocs) +- It should be easy for any plugin to append relevant metadata to existing + content in search. (e.g. location (path) for TechDocs page) +- It should be easy to refine search queries (e.g. ranking, scoring, etc.) +- It should be easy to customize the search UI +- It should be easy to add search functionality to any Backstage plugin or + deployment diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 1148f774da..f5a433fc84 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -72,6 +72,14 @@ "features/software-templates/extending/extending-preparer" ] }, + { + "type": "subcategory", + "label": "Backstage Search", + "ids": [ + "features/search/search-overview", + "features/search/architecture" + ] + }, { "type": "subcategory", "label": "TechDocs", diff --git a/mkdocs.yml b/mkdocs.yml index bceabf942e..99a96d77dc 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,6 +48,9 @@ nav: - Create your own Templater: 'features/software-templates/extending/create-your-own-templater.md' - Create your own Publisher: 'features/software-templates/extending/create-your-own-publisher.md' - Create your own Preparer: 'features/software-templates/extending/create-your-own-preparer.md' + - Backstage Search: + - Overview: 'features/search/README.md' + - Architecture: 'features/search/architecture.md' - TechDocs: - Overview: 'features/techdocs/README.md' - Getting Started: 'features/techdocs/getting-started.md' From 0b4b755b91073fda7629bbb47d78621c3b7ec0db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 15 Jan 2021 14:52:03 +0100 Subject: [PATCH 141/144] chore: consistent naming of parseGitUrl --- .../backend-common/src/reading/BitbucketUrlReader.ts | 9 +++++---- packages/backend-common/src/reading/GithubUrlReader.ts | 4 ++-- packages/backend-common/src/reading/GitlabUrlReader.ts | 4 ++-- .../catalog-backend/src/ingestion/LocationAnalyzer.ts | 4 ++-- .../src/ingestion/processors/CodeOwnersProcessor.ts | 4 ++-- plugins/catalog-import/src/util/urls.ts | 4 ++-- plugins/catalog-import/src/util/useGithubRepos.ts | 4 ++-- plugins/catalog/src/components/createEditLink.ts | 6 +++--- .../src/scaffolder/stages/prepare/azure.ts | 4 ++-- .../src/scaffolder/stages/prepare/bitbucket.ts | 4 ++-- .../src/scaffolder/stages/prepare/github.ts | 4 ++-- .../src/scaffolder/stages/prepare/gitlab.ts | 4 ++-- 12 files changed, 28 insertions(+), 27 deletions(-) diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.ts b/packages/backend-common/src/reading/BitbucketUrlReader.ts index 4367424423..4578436521 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.ts @@ -23,7 +23,7 @@ import { readBitbucketIntegrationConfigs, } from '@backstage/integration'; import fetch from 'cross-fetch'; -import parseGitUri from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import { Readable } from 'stream'; import { NotFoundError } from '../errors'; import { ReadTreeResponseFactory } from './tree'; @@ -101,8 +101,9 @@ export class BitbucketUrlReader implements UrlReader { url: string, options?: ReadTreeOptions, ): Promise { - const gitUrl: parseGitUri.GitUrl = parseGitUri(url); - const { name: repoName, owner: project, resource, filepath } = gitUrl; + const { name: repoName, owner: project, resource, filepath } = parseGitUrl( + url, + ); const isHosted = resource === 'bitbucket.org'; @@ -142,7 +143,7 @@ export class BitbucketUrlReader implements UrlReader { } private async getLastCommitShortHash(url: string): Promise { - const { name: repoName, owner: project, ref } = parseGitUri(url); + const { name: repoName, owner: project, ref } = parseGitUrl(url); let branch = ref; if (!branch) { diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 5ca2a99692..27167f4242 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -21,7 +21,7 @@ import { getGitHubRequestOptions, } from '@backstage/integration'; import fetch from 'cross-fetch'; -import parseGitUri from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import { Readable } from 'stream'; import { InputError, NotFoundError } from '../errors'; import { ReadTreeResponseFactory } from './tree'; @@ -92,7 +92,7 @@ export class GithubUrlReader implements UrlReader { resource, full_name, filepath, - } = parseGitUri(url); + } = parseGitUrl(url); if (!ref) { // TODO(Rugvip): We should add support for defaulting to the default branch diff --git a/packages/backend-common/src/reading/GitlabUrlReader.ts b/packages/backend-common/src/reading/GitlabUrlReader.ts index d51e8dc090..299bbc783b 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.ts @@ -29,7 +29,7 @@ import { ReadTreeResponse, UrlReader, } from './types'; -import parseGitUri from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import { Readable } from 'stream'; export class GitlabUrlReader implements UrlReader { @@ -85,7 +85,7 @@ export class GitlabUrlReader implements UrlReader { resource, full_name, filepath, - } = parseGitUri(url); + } = parseGitUrl(url); if (!ref) { throw new InputError( diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index 97df326fc5..bc81cf14f8 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -15,7 +15,7 @@ */ import { Logger } from 'winston'; -import parseGitUri from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import { AnalyzeLocationRequest, AnalyzeLocationResponse, @@ -31,7 +31,7 @@ export class RepoLocationAnalyzer implements LocationAnalyzer { async analyzeLocation( request: AnalyzeLocationRequest, ): Promise { - const { owner, name, source } = parseGitUri(request.location.target); + const { owner, name, source } = parseGitUrl(request.location.target); const entity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', diff --git a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts index e2b8a8b62c..e1d7e22ccf 100644 --- a/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts +++ b/plugins/catalog-backend/src/ingestion/processors/CodeOwnersProcessor.ts @@ -20,7 +20,7 @@ import * as codeowners from 'codeowners-utils'; import { CodeOwnersEntry } from 'codeowners-utils'; // NOTE: This can be removed when ES2021 is implemented import 'core-js/features/promise'; -import parseGitUri from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import { filter, get, head, pipe, reverse } from 'lodash/fp'; import { Logger } from 'winston'; import { CatalogProcessor } from './types'; @@ -123,7 +123,7 @@ export function buildCodeOwnerUrl( basePath: string, codeOwnersPath: string, ): string { - return buildUrl({ ...parseGitUri(basePath), codeOwnersPath }); + return buildUrl({ ...parseGitUrl(basePath), codeOwnersPath }); } export function parseCodeOwners(ownersText: string) { diff --git a/plugins/catalog-import/src/util/urls.ts b/plugins/catalog-import/src/util/urls.ts index d142263734..c51dc0a999 100644 --- a/plugins/catalog-import/src/util/urls.ts +++ b/plugins/catalog-import/src/util/urls.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import parseGitUri from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; export type UrlType = 'file' | 'tree'; export function urlType(url: string): UrlType { - const { filepathtype, filepath } = parseGitUri(url); + const { filepathtype, filepath } = parseGitUrl(url); if (filepathtype === 'tree' || filepathtype === 'file') { return filepathtype; diff --git a/plugins/catalog-import/src/util/useGithubRepos.ts b/plugins/catalog-import/src/util/useGithubRepos.ts index 30e77e18a8..89448a1be1 100644 --- a/plugins/catalog-import/src/util/useGithubRepos.ts +++ b/plugins/catalog-import/src/util/useGithubRepos.ts @@ -18,7 +18,7 @@ import * as YAML from 'yaml'; import { useApi, configApiRef } from '@backstage/core'; import { catalogImportApiRef } from '../api/CatalogImportApi'; import { ConfigSpec } from '../components/ImportComponentPage'; -import parseGitUri from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; // TODO: (O5ten) Refactor into a core API instead of direct usage like this // https://github.com/backstage/backstage/pull/3613#issuecomment-7408929430 @@ -33,7 +33,7 @@ export function useGithubRepos() { name: repoName, owner: ownerName, resource: hostname, - } = parseGitUri(selectedRepo.location); + } = parseGitUrl(selectedRepo.location); const configs = readGitHubIntegrationConfigs( config.getOptionalConfigArray('integrations.github') ?? [], diff --git a/plugins/catalog/src/components/createEditLink.ts b/plugins/catalog/src/components/createEditLink.ts index cc04d605f1..57fc876704 100644 --- a/plugins/catalog/src/components/createEditLink.ts +++ b/plugins/catalog/src/components/createEditLink.ts @@ -15,7 +15,7 @@ */ import { LocationSpec } from '@backstage/catalog-model'; -import gitUrlParse from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; /** * Creates the edit link for components yaml file @@ -26,7 +26,7 @@ import gitUrlParse from 'git-url-parse'; export const createEditLink = (location: LocationSpec): string | undefined => { try { - const urlData = gitUrlParse(location.target); + const urlData = parseGitUrl(location.target); const url = new URL(location.target); switch (location.type) { case 'github': @@ -64,7 +64,7 @@ export const createEditLink = (location: LocationSpec): string | undefined => { * @returns string representing type of icon to be used */ export const determineUrlType = (url: string): string => { - const urlData = gitUrlParse(url); + const urlData = parseGitUrl(url); if (urlData.source === 'github.com') { return 'github'; diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/azure.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/azure.ts index a38c5f4fdc..51763c6291 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/azure.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/azure.ts @@ -20,7 +20,7 @@ import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { parseLocationAnnotation } from '../helpers'; import { InputError, Git } from '@backstage/backend-common'; import { PreparerBase, PreparerOptions } from './types'; -import GitUriParser from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import { Config } from '@backstage/config'; export class AzurePreparer implements PreparerBase { @@ -46,7 +46,7 @@ export class AzurePreparer implements PreparerBase { } const templateId = template.metadata.name; - const parsedGitLocation = GitUriParser(location); + const parsedGitLocation = parseGitUrl(location); const repositoryCheckoutUrl = parsedGitLocation.toString('https'); const tempDir = await fs.promises.mkdtemp( path.join(workingDirectory, templateId), diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/bitbucket.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/bitbucket.ts index 5b244d24ce..278b481da2 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/bitbucket.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/bitbucket.ts @@ -20,7 +20,7 @@ import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { parseLocationAnnotation } from '../helpers'; import { InputError, Git } from '@backstage/backend-common'; import { PreparerBase, PreparerOptions } from './types'; -import GitUriParser from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import { Config } from '@backstage/config'; export class BitbucketPreparer implements PreparerBase { @@ -49,7 +49,7 @@ export class BitbucketPreparer implements PreparerBase { } const templateId = template.metadata.name; - const repo = GitUriParser(location); + const repo = parseGitUrl(location); const repositoryCheckoutUrl = repo.toString('https'); const tempDir = await fs.promises.mkdtemp( diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts index e7fa0c9b72..c157263894 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts @@ -20,7 +20,7 @@ import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { parseLocationAnnotation } from '../helpers'; import { InputError, Git } from '@backstage/backend-common'; import { PreparerBase, PreparerOptions } from './types'; -import GitUriParser from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; export class GithubPreparer implements PreparerBase { token?: string; @@ -44,7 +44,7 @@ export class GithubPreparer implements PreparerBase { } const templateId = template.metadata.name; - const parsedGitLocation = GitUriParser(location); + const parsedGitLocation = parseGitUrl(location); const repositoryCheckoutUrl = parsedGitLocation.toString('https'); const tempDir = await fs.promises.mkdtemp( path.join(workingDirectory, templateId), diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts index 6e169baa98..9b0d468431 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/gitlab.ts @@ -21,7 +21,7 @@ import { readGitLabIntegrationConfigs, } from '@backstage/integration'; import fs from 'fs-extra'; -import GitUriParser from 'git-url-parse'; +import parseGitUrl from 'git-url-parse'; import os from 'os'; import path from 'path'; import { parseLocationAnnotation } from '../helpers'; @@ -55,7 +55,7 @@ export class GitlabPreparer implements PreparerBase { } const templateId = template.metadata.name; - const parsedGitLocation = GitUriParser(location); + const parsedGitLocation = parseGitUrl(location); const repositoryCheckoutUrl = parsedGitLocation.toString('https'); const tempDir = await fs.promises.mkdtemp( path.join(workingDirectory, templateId), From 5345a1f983ca48cf82cfad5f46e0b29fc46c5dcf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 18 Jan 2021 13:17:50 +0100 Subject: [PATCH 142/144] backend-common: lock down UrlReader to only read from allowed URLs --- .changeset/rich-turtles-roll.md | 19 ++++++ app-config.yaml | 4 ++ .../software-catalog/descriptor-format.md | 13 +++++ packages/backend-common/config.d.ts | 19 ++++++ .../src/reading/FetchUrlReader.test.ts | 58 +++++++++++++++++++ .../src/reading/FetchUrlReader.ts | 29 +++++++++- .../src/reading/UrlReaderPredicateMux.ts | 27 ++------- .../backend-common/src/reading/UrlReaders.ts | 19 ++---- .../processors/UrlReaderProcessor.test.ts | 16 ++++- 9 files changed, 163 insertions(+), 41 deletions(-) create mode 100644 .changeset/rich-turtles-roll.md create mode 100644 packages/backend-common/src/reading/FetchUrlReader.test.ts diff --git a/.changeset/rich-turtles-roll.md b/.changeset/rich-turtles-roll.md new file mode 100644 index 0000000000..9015161369 --- /dev/null +++ b/.changeset/rich-turtles-roll.md @@ -0,0 +1,19 @@ +--- +'@backstage/backend-common': minor +--- + +Remove fallback option from `UrlReaders.create` and `UrlReaders.default`, as well as the default fallback reader. + +To be able to read data from endpoints outside of the configured integrations, you now need to explicitly allow it by +adding an entry in the `backend.reading.allow` list. For example: + +```yml +backend: + baseUrl: ... + reading: + allow: + - host: example.com + - host: '*.examples.org' +``` + +Apart from adding the above configuration, most projects should not need to take any action to migrate existing code. If you do happen to have your own fallback reader configured, this needs to be replaced with a reader factory that selects a specific set of URLs to work with. If you where wrapping the existing fallback reader, the new one that handles the allow list is created using `FetchUrlReader.factory`. diff --git a/app-config.yaml b/app-config.yaml index 5866354a87..741991877e 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -16,6 +16,10 @@ backend: credentials: true csp: connect-src: ["'self'", 'http:', 'https:'] + reading: + allow: + - host: example.com + - host: '*.mozilla.org' # workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir # See README.md in the proxy-backend plugin for information on the configuration format diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 77feef45e2..5b7c82152d 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -131,6 +131,19 @@ spec: $text: https://petstore.swagger.io/v2/swagger.json ``` +Note that to be able to read from targets that are outside of the normal +integration points such as `github.com`, you'll need to explicitly allow it by +adding an entry in the `backend.reading.allow` list. For example: + +```yml +backend: + baseUrl: ... + reading: + allow: + - host: example.com + - host: '*.examples.org' +``` + ## Common to All Kinds: The Envelope The root envelope object has the following structure. diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index b7241bcc03..e5d9b294f8 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -94,6 +94,25 @@ export interface Config { optionsSuccessStatus?: number; }; + /** + * Configuration related to URL reading, used for example for reading catalog info + * files, scaffolder templates, and techdocs content. + */ + reading?: { + /** + * A list of targets to allow outgoing requests to. Users will be able to make + * requests on behalf of the backend to the targets that are allowed by this list. + */ + allow?: Array<{ + /** + * A hostname to allow outgoing requests to, being either a full hostname or + * a subdomain wildcard pattern with a leading `*`. For example `example.com` + * and `*.example.com` are valid values, `prod.*.example.com` is not. + */ + host: string; + }>; + }; + /** * Content Security Policy options. * diff --git a/packages/backend-common/src/reading/FetchUrlReader.test.ts b/packages/backend-common/src/reading/FetchUrlReader.test.ts new file mode 100644 index 0000000000..6579926e3b --- /dev/null +++ b/packages/backend-common/src/reading/FetchUrlReader.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { ConfigReader } from '@backstage/config'; +import { msw } from '@backstage/test-utils'; +import { setupServer } from 'msw/node'; +import { getVoidLogger } from '../logging'; +import { FetchUrlReader } from './FetchUrlReader'; +import { ReadTreeResponseFactory } from './tree'; + +describe('FetchUrlReader', () => { + const worker = setupServer(); + + msw.setupDefaultHandlers(worker); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('factory should create a single entry with a predicate that matches config', async () => { + const entries = FetchUrlReader.factory({ + config: new ConfigReader({ + backend: { + reading: { + allow: [{ host: 'example.com' }, { host: '*.examples.org' }], + }, + }, + }), + logger: getVoidLogger(), + treeResponseFactory: ReadTreeResponseFactory.create({ + config: new ConfigReader({}), + }), + }); + + expect(entries.length).toBe(1); + const [{ predicate }] = entries; + + expect(predicate(new URL('https://example.com/test'))).toBe(true); + expect(predicate(new URL('https://a.example.com/test'))).toBe(false); + expect(predicate(new URL('https://other.com/test'))).toBe(false); + expect(predicate(new URL('https://examples.org/test'))).toBe(false); + expect(predicate(new URL('https://a.examples.org/test'))).toBe(true); + expect(predicate(new URL('https://a.b.examples.org/test'))).toBe(true); + }); +}); diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts index 1d1784590c..aec399a06c 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.ts @@ -16,12 +16,39 @@ import fetch from 'cross-fetch'; import { NotFoundError } from '../errors'; -import { ReadTreeResponse, UrlReader } from './types'; +import { ReaderFactory, ReadTreeResponse, UrlReader } from './types'; /** * A UrlReader that does a plain fetch of the URL. */ export class FetchUrlReader implements UrlReader { + /** + * The factory creates a single reader that will be used for reading any URL that's listed + * in configuration at `backend.reading.allow`. The allow list contains a list of objects describing + * targets to allow, containing the following fields: + * + * `host`: + * Either full hostnames to match, or subdomain wildcard matchers with a leading `*`. + * For example `example.com` and `*.example.com` are valid values, `prod.*.example.com` is not. + */ + static factory: ReaderFactory = ({ config }) => { + const predicates = + config + .getOptionalConfigArray('backend.reading.allow') + ?.map(allowConfig => { + const host = allowConfig.getString('host'); + if (host.startsWith('*.')) { + const suffix = host.slice(1); + return (url: URL) => url.hostname.endsWith(suffix); + } + return (url: URL) => url.hostname === host; + }) ?? []; + + const reader = new FetchUrlReader(); + const predicate = (url: URL) => predicates.some(p => p(url)); + return [{ reader, predicate }]; + }; + async read(url: string): Promise { let response: Response; try { diff --git a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts index 465c125fda..ca11400e4a 100644 --- a/packages/backend-common/src/reading/UrlReaderPredicateMux.ts +++ b/packages/backend-common/src/reading/UrlReaderPredicateMux.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { NotAllowedError } from '../errors'; import { ReadTreeOptions, ReadTreeResponse, @@ -21,22 +22,12 @@ import { UrlReaderPredicateTuple, } from './types'; -type Options = { - // UrlReader to fall back to if no other reader is matched - fallback?: UrlReader; -}; - /** * A UrlReader implementation that selects from a set of UrlReaders * based on a predicate tied to each reader. */ export class UrlReaderPredicateMux implements UrlReader { private readonly readers: UrlReaderPredicateTuple[] = []; - private readonly fallback?: UrlReader; - - constructor({ fallback }: Options) { - this.fallback = fallback; - } register(tuple: UrlReaderPredicateTuple): void { this.readers.push(tuple); @@ -51,11 +42,7 @@ export class UrlReaderPredicateMux implements UrlReader { } } - if (this.fallback) { - return this.fallback.read(url); - } - - throw new Error(`No reader found that could handle '${url}'`); + throw new NotAllowedError(`Reading from '${url}' is not allowed`); } readTree(url: string, options?: ReadTreeOptions): Promise { @@ -67,16 +54,10 @@ export class UrlReaderPredicateMux implements UrlReader { } } - if (this.fallback) { - return this.fallback.readTree(url, options); - } - - throw new Error(`No reader found that could handle '${url}'`); + throw new NotAllowedError(`Reading from '${url}' is not allowed`); } toString() { - return `predicateMux{readers=${this.readers - .map(t => t.reader) - .join(',')},fallback=${this.fallback}}`; + return `predicateMux{readers=${this.readers.map(t => t.reader).join(',')}`; } } diff --git a/packages/backend-common/src/reading/UrlReaders.ts b/packages/backend-common/src/reading/UrlReaders.ts index 2bb5617907..2f233463bb 100644 --- a/packages/backend-common/src/reading/UrlReaders.ts +++ b/packages/backend-common/src/reading/UrlReaders.ts @@ -22,8 +22,8 @@ import { AzureUrlReader } from './AzureUrlReader'; import { BitbucketUrlReader } from './BitbucketUrlReader'; import { GithubUrlReader } from './GithubUrlReader'; import { GitlabUrlReader } from './GitlabUrlReader'; -import { FetchUrlReader } from './FetchUrlReader'; import { ReadTreeResponseFactory } from './tree'; +import { FetchUrlReader } from './FetchUrlReader'; type CreateOptions = { /** Root config object */ @@ -32,8 +32,6 @@ type CreateOptions = { logger: Logger; /** A list of factories used to construct individual readers that match on URLs */ factories?: ReaderFactory[]; - /** Fallback reader to use if none of the readers created by the factories match */ - fallback?: UrlReader; }; /** @@ -43,13 +41,8 @@ export class UrlReaders { /** * Creates a UrlReader without any known types. */ - static create({ - logger, - config, - factories, - fallback, - }: CreateOptions): UrlReader { - const mux = new UrlReaderPredicateMux({ fallback: fallback }); + static create({ logger, config, factories }: CreateOptions): UrlReader { + const mux = new UrlReaderPredicateMux(); const treeResponseFactory = ReadTreeResponseFactory.create({ config }); for (const factory of factories ?? []) { @@ -67,10 +60,8 @@ export class UrlReaders { * Creates a UrlReader that includes all the default factories from this package. * * Any additional factories passed will be loaded before the default ones. - * - * If no fallback reader is passed, a plain fetch reader will be used. */ - static default({ logger, config, factories = [], fallback }: CreateOptions) { + static default({ logger, config, factories = [] }: CreateOptions) { return UrlReaders.create({ logger, config, @@ -79,8 +70,8 @@ export class UrlReaders { BitbucketUrlReader.factory, GithubUrlReader.factory, GitlabUrlReader.factory, + FetchUrlReader.factory, ]), - fallback: fallback ?? new FetchUrlReader(), }); } } diff --git a/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.test.ts index 9bf72d620b..aac4235e3a 100644 --- a/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/UrlReaderProcessor.test.ts @@ -27,13 +27,18 @@ import { } from './types'; describe('UrlReaderProcessor', () => { - const mockApiOrigin = 'http://localhost:23000'; + const mockApiOrigin = 'http://localhost'; const server = setupServer(); msw.setupDefaultHandlers(server); it('should load from url', async () => { const logger = getVoidLogger(); - const reader = UrlReaders.default({ logger, config: new ConfigReader({}) }); + const reader = UrlReaders.default({ + logger, + config: new ConfigReader({ + backend: { reading: { allow: [{ host: 'localhost' }] } }, + }), + }); const processor = new UrlReaderProcessor({ reader, logger }); const spec = { type: 'url', @@ -57,7 +62,12 @@ describe('UrlReaderProcessor', () => { it('should fail load from url with error', async () => { const logger = getVoidLogger(); - const reader = UrlReaders.default({ logger, config: new ConfigReader({}) }); + const reader = UrlReaders.default({ + logger, + config: new ConfigReader({ + backend: { reading: { allow: [{ host: 'localhost' }] } }, + }), + }); const processor = new UrlReaderProcessor({ reader, logger }); const spec = { type: 'url', From a283dcc43b2912e89e80b96e49abbf95fedaae6d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 18 Jan 2021 14:21:34 +0100 Subject: [PATCH 143/144] backend-common: include port in url reader allow list check --- packages/backend-common/config.d.ts | 3 ++- .../src/reading/FetchUrlReader.test.ts | 17 ++++++++++++++++- .../src/reading/FetchUrlReader.ts | 4 ++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index e5d9b294f8..08b746f96d 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -105,9 +105,10 @@ export interface Config { */ allow?: Array<{ /** - * A hostname to allow outgoing requests to, being either a full hostname or + * A host to allow outgoing requests to, being either a full host or * a subdomain wildcard pattern with a leading `*`. For example `example.com` * and `*.example.com` are valid values, `prod.*.example.com` is not. + * The host may also contain a port, for example `example.com:8080`. */ host: string; }>; diff --git a/packages/backend-common/src/reading/FetchUrlReader.test.ts b/packages/backend-common/src/reading/FetchUrlReader.test.ts index 6579926e3b..8dc4aba29b 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.test.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.test.ts @@ -35,7 +35,12 @@ describe('FetchUrlReader', () => { config: new ConfigReader({ backend: { reading: { - allow: [{ host: 'example.com' }, { host: '*.examples.org' }], + allow: [ + { host: 'example.com' }, + { host: 'example.com:700' }, + { host: '*.examples.org' }, + { host: '*.examples.org:700' }, + ], }, }, }), @@ -50,9 +55,19 @@ describe('FetchUrlReader', () => { expect(predicate(new URL('https://example.com/test'))).toBe(true); expect(predicate(new URL('https://a.example.com/test'))).toBe(false); + expect(predicate(new URL('https://example.com:600/test'))).toBe(false); + expect(predicate(new URL('https://a.example.com:600/test'))).toBe(false); + expect(predicate(new URL('https://example.com:700/test'))).toBe(true); + expect(predicate(new URL('https://a.example.com:700/test'))).toBe(false); expect(predicate(new URL('https://other.com/test'))).toBe(false); expect(predicate(new URL('https://examples.org/test'))).toBe(false); expect(predicate(new URL('https://a.examples.org/test'))).toBe(true); expect(predicate(new URL('https://a.b.examples.org/test'))).toBe(true); + expect(predicate(new URL('https://examples.org:600/test'))).toBe(false); + expect(predicate(new URL('https://a.examples.org:600/test'))).toBe(false); + expect(predicate(new URL('https://a.b.examples.org:600/test'))).toBe(false); + expect(predicate(new URL('https://examples.org:700/test'))).toBe(false); + expect(predicate(new URL('https://a.examples.org:700/test'))).toBe(true); + expect(predicate(new URL('https://a.b.examples.org:700/test'))).toBe(true); }); }); diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts index aec399a06c..81f1dfa90e 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.ts @@ -39,9 +39,9 @@ export class FetchUrlReader implements UrlReader { const host = allowConfig.getString('host'); if (host.startsWith('*.')) { const suffix = host.slice(1); - return (url: URL) => url.hostname.endsWith(suffix); + return (url: URL) => url.host.endsWith(suffix); } - return (url: URL) => url.hostname === host; + return (url: URL) => url.host === host; }) ?? []; const reader = new FetchUrlReader(); From a93f42213210c0bc35790728b07731d5f5900830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 18 Jan 2021 16:50:03 +0100 Subject: [PATCH 144/144] catalog-model: remove special merge treatment of annotations --- .changeset/yellow-ties-switch.md | 5 ++ .../catalog-model/src/entity/util.test.ts | 10 +--- packages/catalog-model/src/entity/util.ts | 51 +++++-------------- 3 files changed, 19 insertions(+), 47 deletions(-) create mode 100644 .changeset/yellow-ties-switch.md diff --git a/.changeset/yellow-ties-switch.md b/.changeset/yellow-ties-switch.md new file mode 100644 index 0000000000..784d2a18ae --- /dev/null +++ b/.changeset/yellow-ties-switch.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': minor +--- + +The catalog no longer attempts to merge old and new annotations, when updating an entity from a remote location. This was a behavior that was copied from kubernetes, and catered to use cases where you wanted to use HTTP POST to update an entity in-place, outside of what the refresh loop does. This has proved to be a mistake, because as a side effect, the refresh loop effectively is unable to ever delete annotations when they are removed from source YAML. This is obviously a breaking change, but we believe that this is not a behavior that is relied upon in the wild, and it has never been an actually supported use flow of the catalog. We therefore choose to break the behavior outright, and instead just store updated annotations verbatim - just like we already do for example for labels diff --git a/packages/catalog-model/src/entity/util.test.ts b/packages/catalog-model/src/entity/util.test.ts index e4399bbe05..c7e2c036b5 100644 --- a/packages/catalog-model/src/entity/util.test.ts +++ b/packages/catalog-model/src/entity/util.test.ts @@ -96,18 +96,12 @@ describe('util', () => { b = lodash.cloneDeep(a); b.metadata.labels.labelKey += 'a'; expect(entityHasChanges(a, b)).toBe(true); - }); - - it('detects annotation changes, but not removals', () => { - let b: any = lodash.cloneDeep(a); + b = lodash.cloneDeep(a); b.metadata.annotations.annotationKey += 'a'; expect(entityHasChanges(a, b)).toBe(true); b = lodash.cloneDeep(a); - b.metadata.annotations.n = 'n'; - expect(entityHasChanges(a, b)).toBe(true); - b = lodash.cloneDeep(a); delete b.metadata.annotations.annotationKey; - expect(entityHasChanges(a, b)).toBe(false); + expect(entityHasChanges(a, b)).toBe(true); }); it('detects spec changes', () => { diff --git a/packages/catalog-model/src/entity/util.ts b/packages/catalog-model/src/entity/util.ts index ed68339a99..2c63cc4c49 100644 --- a/packages/catalog-model/src/entity/util.ts +++ b/packages/catalog-model/src/entity/util.ts @@ -54,10 +54,6 @@ export function generateEntityEtag(): string { * @param next The new state of the entity */ export function entityHasChanges(previous: Entity, next: Entity): boolean { - if (entityHasAnnotationChanges(previous, next)) { - return true; - } - const e1 = lodash.cloneDeep(previous); const e2 = lodash.cloneDeep(next); @@ -67,6 +63,18 @@ export function entityHasChanges(previous: Entity, next: Entity): boolean { if (!e2.metadata.labels) { e2.metadata.labels = {}; } + if (!e1.metadata.annotations) { + e1.metadata.annotations = {}; + } + if (!e2.metadata.annotations) { + e2.metadata.annotations = {}; + } + if (!e1.metadata.tags) { + e1.metadata.tags = []; + } + if (!e2.metadata.tags) { + e2.metadata.tags = []; + } // Remove generated fields delete e1.metadata.uid; @@ -76,10 +84,6 @@ export function entityHasChanges(previous: Entity, next: Entity): boolean { delete e2.metadata.etag; delete e2.metadata.generation; - // Remove already compared things - delete e1.metadata.annotations; - delete e2.metadata.annotations; - // Remove things that we explicitly do not compare delete e1.relations; delete e2.relations; @@ -106,14 +110,6 @@ export function generateUpdatedEntity(previous: Entity, next: Entity): Entity { const result = lodash.cloneDeep(next); - // Annotations are merged, with the new ones taking precedence - if (previous.metadata.annotations) { - next.metadata.annotations = { - ...previous.metadata.annotations, - ...next.metadata.annotations, - }; - } - // Generated fields are copied and updated const bumpEtag = entityHasChanges(previous, result); const bumpGeneration = !lodash.isEqual(previous.spec, result.spec); @@ -123,26 +119,3 @@ export function generateUpdatedEntity(previous: Entity, next: Entity): Entity { return result; } - -function entityHasAnnotationChanges(previous: Entity, next: Entity): boolean { - // Since the next annotations get merged into the previous, extract only - // the overlapping keys and check if their values match. - if (next.metadata.annotations) { - if (!previous.metadata.annotations) { - return true; - } - if ( - !lodash.isEqual( - next.metadata.annotations, - lodash.pick( - previous.metadata.annotations, - Object.keys(next.metadata.annotations), - ), - ) - ) { - return true; - } - } - - return false; -}