From 16c41c290cdd88bcb9e8a6843de5ad6309ef590b Mon Sep 17 00:00:00 2001 From: MarceloLeite2604 Date: Wed, 17 Mar 2021 11:25:15 -0300 Subject: [PATCH 01/61] Replace "ESNext.Promise" with "ES2020.Promise" on tsc lib property Signed-off-by: MarceloLeite2604 --- packages/cli/config/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/config/tsconfig.json b/packages/cli/config/tsconfig.json index a55b215f74..d249d53f87 100644 --- a/packages/cli/config/tsconfig.json +++ b/packages/cli/config/tsconfig.json @@ -11,7 +11,7 @@ "incremental": true, "isolatedModules": true, "jsx": "react", - "lib": ["DOM", "DOM.Iterable", "ScriptHost", "ES2020", "ESNext.Promise"], + "lib": ["DOM", "DOM.Iterable", "ScriptHost", "ES2020", "ES2020.Promise"], "module": "ESNext", "moduleResolution": "node", "noEmit": false, From 802b41b65b760e990bca4dcc862bedb0065200ec Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Thu, 18 Mar 2021 15:43:50 -0700 Subject: [PATCH 02/61] feat: allow custom directory to be specified for github publish action Signed-off-by: Jonah Back --- .changeset/bright-lions-camp.md | 5 +++++ .../src/scaffolder/actions/builtin/publish/github.ts | 12 ++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .changeset/bright-lions-camp.md diff --git a/.changeset/bright-lions-camp.md b/.changeset/bright-lions-camp.md new file mode 100644 index 0000000000..ccb812b01e --- /dev/null +++ b/.changeset/bright-lions-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Allow custom directory to be specified for github publish action diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index 4f9b6038a1..5979136488 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { resolve as resolvePath } from 'path'; import { InputError } from '@backstage/errors'; import { GithubCredentialsProvider, @@ -40,6 +40,7 @@ export function createPublishGithubAction(options: { repoUrl: string; description?: string; access?: string; + repoPath?: string; repoVisibility: 'private' | 'internal' | 'public'; }>({ id: 'publish:github', @@ -67,6 +68,10 @@ export function createPublishGithubAction(options: { type: 'string', enum: ['private', 'public', 'internal'], }, + repoPath: { + title: 'Repository Path', + type: 'string', + }, }, }, output: { @@ -158,9 +163,12 @@ export function createPublishGithubAction(options: { const remoteUrl = data.clone_url; const repoContentsUrl = `${data.html_url}/blob/master`; + const outputPath = ctx.input.repoPath + ? resolvePath(ctx.workspacePath, ctx.input.repoPath) + : ctx.workspacePath; await initRepoAndPush({ - dir: ctx.workspacePath, + dir: outputPath, remoteUrl, auth: { username: 'x-access-token', From d0b4ebf22c25fe0262f640d6347b19efb1a53c40 Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Sun, 21 Mar 2021 08:24:34 +0100 Subject: [PATCH 03/61] fix: support auth in badge plugin Signed-off-by: Erik Larsson --- .changeset/eight-files-rhyme.md | 6 ++++++ plugins/badges-backend/src/service/router.ts | 18 ++++++++++++++++-- plugins/badges/src/api/BadgesClient.ts | 18 +++++++++++++++--- plugins/badges/src/plugin.ts | 6 ++++-- 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 .changeset/eight-files-rhyme.md diff --git a/.changeset/eight-files-rhyme.md b/.changeset/eight-files-rhyme.md new file mode 100644 index 0000000000..c45556366d --- /dev/null +++ b/.changeset/eight-files-rhyme.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-badges': minor +'@backstage/plugin-badges-backend': patch +--- + +Support auth in badge plugin diff --git a/plugins/badges-backend/src/service/router.ts b/plugins/badges-backend/src/service/router.ts index bfbb10fc94..8548476f5f 100644 --- a/plugins/badges-backend/src/service/router.ts +++ b/plugins/badges-backend/src/service/router.ts @@ -46,7 +46,12 @@ export async function createRouter( router.get('/entity/:namespace/:kind/:name/badge-specs', async (req, res) => { const { namespace, kind, name } = req.params; - const entity = await catalog.getEntityByName({ namespace, kind, name }); + const entity = await catalog.getEntityByName( + { namespace, kind, name }, + { + token: getBearerToken(req.headers.authorization), + }, + ); if (!entity) { throw new NotFoundError( `No ${kind} entity in ${namespace} named "${name}"`, @@ -81,7 +86,12 @@ export async function createRouter( '/entity/:namespace/:kind/:name/badge/:badgeId', async (req, res) => { const { namespace, kind, name, badgeId } = req.params; - const entity = await catalog.getEntityByName({ namespace, kind, name }); + const entity = await catalog.getEntityByName( + { namespace, kind, name }, + { + token: getBearerToken(req.headers.authorization), + }, + ); if (!entity) { throw new NotFoundError( `No ${kind} entity in ${namespace} named "${name}"`, @@ -134,3 +144,7 @@ async function getBadgeUrl( const baseUrl = await options.discovery.getExternalBaseUrl('badges'); return `${baseUrl}/entity/${namespace}/${kind}/${name}/badge/${badgeId}`; } + +function getBearerToken(header?: string): string | undefined { + return header?.match(/Bearer\s+(\S+)/i)?.[1]; +} diff --git a/plugins/badges/src/api/BadgesClient.ts b/plugins/badges/src/api/BadgesClient.ts index 927a16a7eb..c015a59703 100644 --- a/plugins/badges/src/api/BadgesClient.ts +++ b/plugins/badges/src/api/BadgesClient.ts @@ -15,7 +15,7 @@ */ import { generatePath } from 'react-router'; -import { DiscoveryApi } from '@backstage/core'; +import { DiscoveryApi, IdentityApi } from '@backstage/core'; import { ResponseError } from '@backstage/errors'; import { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model'; import { entityRoute } from '@backstage/plugin-catalog-react'; @@ -23,14 +23,26 @@ import { BadgesApi, BadgeSpec } from './types'; export class BadgesClient implements BadgesApi { private readonly discoveryApi: DiscoveryApi; + private readonly identityApi: IdentityApi; - constructor(options: { discoveryApi: DiscoveryApi }) { + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + }) { this.discoveryApi = options.discoveryApi; + this.identityApi = options.identityApi; } public async getEntityBadgeSpecs(entity: Entity): Promise { const entityBadgeSpecsUrl = await this.getEntityBadgeSpecsUrl(entity); - const response = await fetch(entityBadgeSpecsUrl); + const token = await this.identityApi.getIdToken(); + const response = await fetch(entityBadgeSpecsUrl, { + headers: token + ? { + Authorization: `Bearer ${token}`, + } + : undefined, + }); if (!response.ok) { throw await ResponseError.fromResponse(response); diff --git a/plugins/badges/src/plugin.ts b/plugins/badges/src/plugin.ts index 92932aa356..a247c93a4b 100644 --- a/plugins/badges/src/plugin.ts +++ b/plugins/badges/src/plugin.ts @@ -18,6 +18,7 @@ import { createComponentExtension, createPlugin, discoveryApiRef, + identityApiRef, } from '@backstage/core'; import { badgesApiRef, BadgesClient } from './api'; @@ -26,8 +27,9 @@ export const badgesPlugin = createPlugin({ apis: [ createApiFactory({ api: badgesApiRef, - deps: { discoveryApi: discoveryApiRef }, - factory: ({ discoveryApi }) => new BadgesClient({ discoveryApi }), + deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, + factory: ({ discoveryApi, identityApi }) => + new BadgesClient({ discoveryApi, identityApi }), }), ], }); From 839743bf3ed2eb9bc472edc7c2bb770eaaea0eda Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Sun, 21 Mar 2021 12:20:44 +0100 Subject: [PATCH 04/61] fix tests Signed-off-by: Erik Larsson --- .../badges-backend/src/service/router.test.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/plugins/badges-backend/src/service/router.test.ts b/plugins/badges-backend/src/service/router.test.ts index e8de5dfd42..1be0923e0b 100644 --- a/plugins/badges-backend/src/service/router.test.ts +++ b/plugins/badges-backend/src/service/router.test.ts @@ -112,11 +112,14 @@ describe('createRouter', () => { expect(response.text).toEqual(JSON.stringify([badge], null, 2)); expect(catalog.getEntityByName).toHaveBeenCalledTimes(1); - expect(catalog.getEntityByName).toHaveBeenCalledWith({ - namespace: 'default', - kind: 'service', - name: 'test', - }); + expect(catalog.getEntityByName).toHaveBeenCalledWith( + { + namespace: 'default', + kind: 'service', + name: 'test', + }, + { token: undefined }, + ); expect(badgeBuilder.getBadges).toHaveBeenCalledTimes(1); expect(badgeBuilder.createBadgeJson).toHaveBeenCalledTimes(1); @@ -148,11 +151,14 @@ describe('createRouter', () => { expect(response.body).toEqual(Buffer.from(image)); expect(catalog.getEntityByName).toHaveBeenCalledTimes(1); - expect(catalog.getEntityByName).toHaveBeenCalledWith({ - namespace: 'default', - kind: 'service', - name: 'test', - }); + expect(catalog.getEntityByName).toHaveBeenCalledWith( + { + namespace: 'default', + kind: 'service', + name: 'test', + }, + { token: undefined }, + ); expect(badgeBuilder.getBadges).toHaveBeenCalledTimes(0); expect(badgeBuilder.createBadgeSvg).toHaveBeenCalledTimes(1); From d20e2993849515dad830d453d6f3ab4e09f2d0db Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Sun, 21 Mar 2021 21:01:00 -0700 Subject: [PATCH 05/61] Use 'sourcePath' for custom git source directory Signed-off-by: Jonah Back --- .../src/scaffolder/actions/builtin/publish/github.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index 5979136488..a791f3ad39 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -40,7 +40,7 @@ export function createPublishGithubAction(options: { repoUrl: string; description?: string; access?: string; - repoPath?: string; + sourcePath?: string; repoVisibility: 'private' | 'internal' | 'public'; }>({ id: 'publish:github', @@ -68,7 +68,7 @@ export function createPublishGithubAction(options: { type: 'string', enum: ['private', 'public', 'internal'], }, - repoPath: { + sourcePath: { title: 'Repository Path', type: 'string', }, @@ -163,8 +163,8 @@ export function createPublishGithubAction(options: { const remoteUrl = data.clone_url; const repoContentsUrl = `${data.html_url}/blob/master`; - const outputPath = ctx.input.repoPath - ? resolvePath(ctx.workspacePath, ctx.input.repoPath) + const outputPath = ctx.input.sourcePath + ? resolvePath(ctx.workspacePath, ctx.input.sourcePath) : ctx.workspacePath; await initRepoAndPush({ From 4449b497f95a92eea6641111ecc9af7aaf54f3ef Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Tue, 23 Mar 2021 10:05:47 -0300 Subject: [PATCH 06/61] #3497 Created an example of how to use the Dialog component Signed-off-by: Mateus Marquezini --- .changeset/warm-hotels-happen.md | 5 + .../src/components/Dialog/Dialog.stories.tsx | 134 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 .changeset/warm-hotels-happen.md create mode 100644 packages/core/src/components/Dialog/Dialog.stories.tsx diff --git a/.changeset/warm-hotels-happen.md b/.changeset/warm-hotels-happen.md new file mode 100644 index 0000000000..89ff4ea432 --- /dev/null +++ b/.changeset/warm-hotels-happen.md @@ -0,0 +1,5 @@ +--- +'storybook': minor +--- + +Created a new example of how to use the Dialog component in Storybook diff --git a/packages/core/src/components/Dialog/Dialog.stories.tsx b/packages/core/src/components/Dialog/Dialog.stories.tsx new file mode 100644 index 0000000000..528b266e47 --- /dev/null +++ b/packages/core/src/components/Dialog/Dialog.stories.tsx @@ -0,0 +1,134 @@ +/* + * 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 { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + Typography, +} from '@material-ui/core'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import CloseIcon from '@material-ui/icons/Close'; +import React, { useState } from 'react'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + leftAlignButtonsDialog: { + justifyContent: 'flex-start', + paddingLeft: 24, + }, + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.grey[500], + }, + }), +); + +export default { + title: 'Layout/Dialog', + component: Dialog, +}; + +export const Default = () => { + const [open, setOpen] = useState(false); + const classes = useStyles(); + + const openDialog = () => { + setOpen(true); + }; + + const closeDialog = () => { + setOpen(false); + }; + + const dialogContent = () => { + return ( + <> + + This is an example of how to use the Dialog component. + + + This component is used whenever confirmation of some sort is needed, + such as: + +
    +
  • + + Consent to sensitive matters like GDPR, access, etc; + +
  • +
  • + + Save, submit, cancel after a form is completed; + +
  • +
  • + Alert message; +
  • +
  • + Buttons are optional. +
  • +
+ + The color for the secondary button is the same as the primary. For the + primary action button, use: + +
variant="contained"
+ For the secondary action button, use: +
variant="outlined"
+ + ); + }; + + return ( + <> + + + + Dialog Box Title + + + + + {dialogContent()} + + + + + + + ); +}; From f1ded32d41eaa8e9d8ad811a974f9a22dc3d7e82 Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Tue, 23 Mar 2021 13:22:36 -0300 Subject: [PATCH 07/61] #3497 changeset file deleted Signed-off-by: Mateus Marquezini --- .changeset/warm-hotels-happen.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/warm-hotels-happen.md diff --git a/.changeset/warm-hotels-happen.md b/.changeset/warm-hotels-happen.md deleted file mode 100644 index 89ff4ea432..0000000000 --- a/.changeset/warm-hotels-happen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'storybook': minor ---- - -Created a new example of how to use the Dialog component in Storybook From 5a572c4c83fe06679718835b0c1c57530e5e4b05 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Wed, 24 Mar 2021 15:55:16 -0700 Subject: [PATCH 08/61] Update .changeset/bright-lions-camp.md Co-authored-by: Patrik Oldsberg Signed-off-by: Jonah Back --- .changeset/bright-lions-camp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/bright-lions-camp.md b/.changeset/bright-lions-camp.md index ccb812b01e..fee5d5b5fc 100644 --- a/.changeset/bright-lions-camp.md +++ b/.changeset/bright-lions-camp.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': patch --- -Allow custom directory to be specified for github publish action +Allow custom directory to be specified for GitHub publish action From d3accca86001d6c367e58dfd89c1ee7b14054964 Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Wed, 24 Mar 2021 21:09:28 -0700 Subject: [PATCH 09/61] Sanitize input to avoid directory traversal Signed-off-by: Jonah Back --- .../src/scaffolder/actions/builtin/publish/github.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index a791f3ad39..ca1b0f3d82 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { resolve as resolvePath } from 'path'; +import { join as joinPath, normalize as normalizePath } from 'path'; import { InputError } from '@backstage/errors'; import { GithubCredentialsProvider, @@ -69,7 +69,7 @@ export function createPublishGithubAction(options: { enum: ['private', 'public', 'internal'], }, sourcePath: { - title: 'Repository Path', + title: 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the respository.', type: 'string', }, }, @@ -163,9 +163,11 @@ export function createPublishGithubAction(options: { const remoteUrl = data.clone_url; const repoContentsUrl = `${data.html_url}/blob/master`; - const outputPath = ctx.input.sourcePath - ? resolvePath(ctx.workspacePath, ctx.input.sourcePath) - : ctx.workspacePath; + let outputPath = ctx.input.sourcePath; + if (ctx.input.sourcePath) { + const safeSuffix = normalizePath(ctx.input.sourcePath).replace(/^(\.\.(\/|\\|$))+/, ''); + outputPath = joinPath(ctx.workspace, safeSuffix); + } await initRepoAndPush({ dir: outputPath, From 8419d669c717cc122139d0a5bf6310b0151f4d2f Mon Sep 17 00:00:00 2001 From: Jonah Back Date: Wed, 24 Mar 2021 21:20:58 -0700 Subject: [PATCH 10/61] Add sourcePath option to all git publishers Signed-off-by: Jonah Back --- .../scaffolder/actions/builtin/publish/azure.ts | 10 ++++++++-- .../actions/builtin/publish/bitbucket.ts | 10 ++++++++-- .../scaffolder/actions/builtin/publish/github.ts | 13 ++++--------- .../scaffolder/actions/builtin/publish/gitlab.ts | 10 ++++++++-- .../scaffolder/actions/builtin/publish/util.ts | 15 +++++++++++++++ 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts index 0141de3499..e1a064b3e6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/azure.ts @@ -19,7 +19,7 @@ import { ScmIntegrationRegistry } from '@backstage/integration'; import { initRepoAndPush } from '../../../stages/publish/helpers'; import { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api'; -import { parseRepoUrl } from './util'; +import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { createTemplateAction } from '../../createTemplateAction'; export function createPublishAzureAction(options: { @@ -30,6 +30,7 @@ export function createPublishAzureAction(options: { return createTemplateAction<{ repoUrl: string; description?: string; + sourcePath?: string; }>({ id: 'publish:azure', description: @@ -47,6 +48,11 @@ export function createPublishAzureAction(options: { title: 'Repository Description', type: 'string', }, + sourcePath: { + title: + 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the respository.', + type: 'string', + }, }, }, output: { @@ -112,7 +118,7 @@ export function createPublishAzureAction(options: { const repoContentsUrl = remoteUrl; await initRepoAndPush({ - dir: ctx.workspacePath, + dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, auth: { username: 'notempty', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts index a7370356cc..13f639d745 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/bitbucket.ts @@ -20,7 +20,7 @@ import { ScmIntegrationRegistry, } from '@backstage/integration'; import { initRepoAndPush } from '../../../stages/publish/helpers'; -import { parseRepoUrl } from './util'; +import { getRepoSourceDirectory, parseRepoUrl } from './util'; import fetch from 'cross-fetch'; import { createTemplateAction } from '../../createTemplateAction'; @@ -165,6 +165,7 @@ export function createPublishBitbucketAction(options: { repoUrl: string; description: string; repoVisibility: 'private' | 'public'; + sourcePath?: string; }>({ id: 'publish:bitbucket', description: @@ -187,6 +188,11 @@ export function createPublishBitbucketAction(options: { type: 'string', enum: ['private', 'public'], }, + sourcePath: { + title: + 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the respository.', + type: 'string', + }, }, }, output: { @@ -233,7 +239,7 @@ export function createPublishBitbucketAction(options: { }); await initRepoAndPush({ - dir: ctx.workspacePath, + dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, auth: { username: integrationConfig.config.username diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts index ca1b0f3d82..666c04f368 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { join as joinPath, normalize as normalizePath } from 'path'; import { InputError } from '@backstage/errors'; import { GithubCredentialsProvider, @@ -21,7 +20,7 @@ import { } from '@backstage/integration'; import { Octokit } from '@octokit/rest'; import { initRepoAndPush } from '../../../stages/publish/helpers'; -import { parseRepoUrl } from './util'; +import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { createTemplateAction } from '../../createTemplateAction'; export function createPublishGithubAction(options: { @@ -69,7 +68,8 @@ export function createPublishGithubAction(options: { enum: ['private', 'public', 'internal'], }, sourcePath: { - title: 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the respository.', + title: + 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the respository.', type: 'string', }, }, @@ -163,14 +163,9 @@ export function createPublishGithubAction(options: { const remoteUrl = data.clone_url; const repoContentsUrl = `${data.html_url}/blob/master`; - let outputPath = ctx.input.sourcePath; - if (ctx.input.sourcePath) { - const safeSuffix = normalizePath(ctx.input.sourcePath).replace(/^(\.\.(\/|\\|$))+/, ''); - outputPath = joinPath(ctx.workspace, safeSuffix); - } await initRepoAndPush({ - dir: outputPath, + dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, auth: { username: 'x-access-token', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts index 12fc90cc94..37074d6f57 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlab.ts @@ -18,7 +18,7 @@ import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { Gitlab } from '@gitbeaker/node'; import { initRepoAndPush } from '../../../stages/publish/helpers'; -import { parseRepoUrl } from './util'; +import { getRepoSourceDirectory, parseRepoUrl } from './util'; import { createTemplateAction } from '../../createTemplateAction'; export function createPublishGitlabAction(options: { @@ -29,6 +29,7 @@ export function createPublishGitlabAction(options: { return createTemplateAction<{ repoUrl: string; repoVisibility: 'private' | 'internal' | 'public'; + sourcePath?: string; }>({ id: 'publish:gitlab', description: @@ -47,6 +48,11 @@ export function createPublishGitlabAction(options: { type: 'string', enum: ['private', 'public', 'internal'], }, + sourcePath: { + title: + 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the respository.', + type: 'string', + }, }, }, output: { @@ -106,7 +112,7 @@ export function createPublishGitlabAction(options: { const repoContentsUrl = `${remoteUrl}/-/blob/master`; await initRepoAndPush({ - dir: ctx.workspacePath, + dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl: http_url_to_repo as string, auth: { username: 'oauth2', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts index 89bbf24574..07b3823e7b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts @@ -15,6 +15,21 @@ */ import { InputError } from '@backstage/errors'; +import { join as joinPath, normalize as normalizePath } from 'path'; + +export const getRepoSourceDirectory = ( + workspacePath: string, + sourcePath: string | undefined, +) => { + if (sourcePath) { + const safeSuffix = normalizePath(sourcePath).replace( + /^(\.\.(\/|\\|$))+/, + '', + ); + return joinPath(workspacePath, safeSuffix); + } + return workspacePath; +}; export const parseRepoUrl = (repoUrl: string) => { let parsed; From 2ae8cf460875c98864fa1af4459e251cb70b9b6d Mon Sep 17 00:00:00 2001 From: Nicolas Torres Date: Wed, 24 Mar 2021 18:51:57 +0100 Subject: [PATCH 11/61] Fixes untar not happening under certain occasions Signed-off-by: Nicolas Torres --- docs/cli/commands.md | 7 +++++-- packages/backend/Dockerfile | 4 +++- .../templates/default-app/packages/backend/Dockerfile | 6 ++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/cli/commands.md b/docs/cli/commands.md index ca89a43a6a..f5e63824a7 100644 --- a/docs/cli/commands.md +++ b/docs/cli/commands.md @@ -190,10 +190,13 @@ output of `backstage-cli backend:bundle` into an image: FROM node:14-buster-slim WORKDIR /app -ADD yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ +COPY yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ +RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz + RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" -ADD packages/backend/dist/bundle.tar.gz app-config.yaml ./ +COPY packages/backend/dist/bundle.tar.gz app-config.yaml ./ +RUN tar xzf bundle.tar.gz && rm bundle.tar.gz CMD ["node", "packages/backend"] ``` diff --git a/packages/backend/Dockerfile b/packages/backend/Dockerfile index acef405c8a..02aab45609 100644 --- a/packages/backend/Dockerfile +++ b/packages/backend/Dockerfile @@ -16,11 +16,13 @@ WORKDIR /app # Copy repo skeleton first, to avoid unnecessary docker cache invalidation. # The skeleton contains the package.json of each package in the monorepo, # and along with yarn.lock and the root package.json, that's enough to run yarn install. -ADD yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ +COPY yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ +RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" # Then copy the rest of the backend bundle, along with any other files we might want. ADD packages/backend/dist/bundle.tar.gz app-config.yaml ./ +RUN tar xzf bundle.tar.gz && rm bundle.tar.gz CMD ["node", "packages/backend", "--config", "app-config.yaml"] diff --git a/packages/create-app/templates/default-app/packages/backend/Dockerfile b/packages/create-app/templates/default-app/packages/backend/Dockerfile index acef405c8a..31231a3a4a 100644 --- a/packages/create-app/templates/default-app/packages/backend/Dockerfile +++ b/packages/create-app/templates/default-app/packages/backend/Dockerfile @@ -16,11 +16,13 @@ WORKDIR /app # Copy repo skeleton first, to avoid unnecessary docker cache invalidation. # The skeleton contains the package.json of each package in the monorepo, # and along with yarn.lock and the root package.json, that's enough to run yarn install. -ADD yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ +COPY yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ +RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)" # Then copy the rest of the backend bundle, along with any other files we might want. -ADD packages/backend/dist/bundle.tar.gz app-config.yaml ./ +COPY packages/backend/dist/bundle.tar.gz app-config.yaml ./ +RUN tar xzf bundle.tar.gz && rm bundle.tar.gz CMD ["node", "packages/backend", "--config", "app-config.yaml"] From f9c75f7a93698ce8ea8d1145aa87e66777cff978 Mon Sep 17 00:00:00 2001 From: Podge Date: Fri, 26 Mar 2021 20:13:05 +0000 Subject: [PATCH 12/61] Enable non unicode character in catalog import. replaced btoa with js-base64 so the code can gracefully handle non unicode characters. Signed-off-by: Podge --- .changeset/old-spies-love.md | 5 +++++ plugins/catalog-import/package.json | 1 + plugins/catalog-import/src/api/CatalogImportClient.ts | 3 ++- yarn.lock | 5 +++++ 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .changeset/old-spies-love.md diff --git a/.changeset/old-spies-love.md b/.changeset/old-spies-love.md new file mode 100644 index 0000000000..e02d3acb6f --- /dev/null +++ b/.changeset/old-spies-love.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-import': patch +--- + +When importing components you will now have the ability to use non unicode characters in the entity owner field diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 044483e2ba..ee1d40a590 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -43,6 +43,7 @@ "@octokit/rest": "^18.0.12", "@types/react": "^16.9", "git-url-parse": "^11.4.4", + "js-base64": "^3.6.0", "react": "^16.13.1", "react-dom": "^16.13.1", "react-hook-form": "^6.15.4", diff --git a/plugins/catalog-import/src/api/CatalogImportClient.ts b/plugins/catalog-import/src/api/CatalogImportClient.ts index f6e8cb69d4..6dd48d2aa7 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.ts @@ -21,6 +21,7 @@ import { GitHubIntegrationConfig, ScmIntegrationRegistry, } from '@backstage/integration'; +import { Base64 } from 'js-base64'; import { Octokit } from '@octokit/rest'; import { PartialEntity } from '../types'; import { AnalyzeResult, CatalogImportApi } from './CatalogImportApi'; @@ -300,7 +301,7 @@ export class CatalogImportClient implements CatalogImportApi { repo, path: fileName, message: title, - content: btoa(fileContent), + content: Base64.encode(fileContent), branch: branchName, }) .catch(e => { diff --git a/yarn.lock b/yarn.lock index 88f35fd4a4..fe9f7d5c2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16882,6 +16882,11 @@ joycon@^2.2.5: resolved "https://registry.npmjs.org/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615" integrity sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ== +js-base64@^3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/js-base64/-/js-base64-3.6.0.tgz#773e1de628f4f298d65a7e9842c50244751f5756" + integrity sha512-wVdUBYQeY2gY73RIlPrysvpYx+2vheGo8Y1SNQv/BzHToWpAZzJU7Z6uheKMAe+GLSBig5/Ps2nxg/8tRB73xg== + js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" From 5394703c36836535223168d3ea79ed3a2be8d25b Mon Sep 17 00:00:00 2001 From: Podge Date: Sat, 27 Mar 2021 21:53:40 +0000 Subject: [PATCH 13/61] Updating spelling of unicode to Unicode Signed-off-by: Podge --- .changeset/old-spies-love.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/old-spies-love.md b/.changeset/old-spies-love.md index e02d3acb6f..722d9d937e 100644 --- a/.changeset/old-spies-love.md +++ b/.changeset/old-spies-love.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-import': patch --- -When importing components you will now have the ability to use non unicode characters in the entity owner field +When importing components you will now have the ability to use non Unicode characters in the entity owner field From d8ffec739ea58b111bdfc8d0037f6e7f12214ed9 Mon Sep 17 00:00:00 2001 From: James Turley Date: Sat, 27 Mar 2021 12:18:32 +0000 Subject: [PATCH 14/61] Create builtin action for publishing to Github PR Signed-off-by: James Turley --- .changeset/clever-walls-bow.md | 5 + plugins/scaffolder-backend/package.json | 2 + .../sample-templates/local-templates.yaml | 1 + .../pull-request/template.yaml | 76 ++++++ .../pull-request/template/catalog-info.yaml | 8 + .../actions/builtin/createBuiltinActions.ts | 4 + .../builtin/publish/githubPullRequest.test.ts | 236 +++++++++++++++++ .../builtin/publish/githubPullRequest.ts | 243 ++++++++++++++++++ .../actions/builtin/publish/index.ts | 1 + yarn.lock | 7 + 10 files changed, 583 insertions(+) create mode 100644 .changeset/clever-walls-bow.md create mode 100644 plugins/scaffolder-backend/sample-templates/pull-request/template.yaml create mode 100644 plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts diff --git a/.changeset/clever-walls-bow.md b/.changeset/clever-walls-bow.md new file mode 100644 index 0000000000..36e56f6e37 --- /dev/null +++ b/.changeset/clever-walls-bow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add built-in publish action for creating GitHub pull requests. diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 9bd631a2e9..659ecdfe8a 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -57,8 +57,10 @@ "isomorphic-git": "^1.8.0", "jsonschema": "^1.2.6", "knex": "^0.95.1", + "lodash": "^4.17.21", "luxon": "^1.26.0", "morgan": "^1.10.0", + "octokit-plugin-create-pull-request": "^3.9.3", "uuid": "^8.2.0", "winston": "^3.2.1", "yaml": "^1.10.0" diff --git a/plugins/scaffolder-backend/sample-templates/local-templates.yaml b/plugins/scaffolder-backend/sample-templates/local-templates.yaml index b0e16f9dea..76c76bc97a 100644 --- a/plugins/scaffolder-backend/sample-templates/local-templates.yaml +++ b/plugins/scaffolder-backend/sample-templates/local-templates.yaml @@ -10,3 +10,4 @@ spec: - ./react-ssr-template/template.yaml - ./springboot-grpc-template/template.yaml - ./v1beta2-demo/template.yaml + - ./pull-request/template.yaml diff --git a/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml b/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml new file mode 100644 index 0000000000..cad2a58d1f --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/pull-request/template.yaml @@ -0,0 +1,76 @@ +apiVersion: backstage.io/v1beta2 +kind: Template +metadata: + name: pull-request + title: Pull Request Action template + description: scaffolder v1beta2 template demo publishing to PR on existing git repository +spec: + owner: backstage/techdocs-core + type: service + + parameters: + - title: Fill in some steps + required: + - name + properties: + name: + title: Name + type: string + description: Unique name of the component + ui:autofocus: true + ui:options: + rows: 5 + description: + title: Description + type: string + description: Description of the component + targetPath: + title: Target Path in repo + type: string + description: Name of the directory to create in the repository + - title: Choose a location + required: + - repoUrl + properties: + repoUrl: + title: Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - github.com + + steps: + - id: fetch-base + name: Fetch Base + action: fetch:cookiecutter + input: + url: ./template + values: + name: '{{parameters.name}}' + + - id: fetch-docs + name: Fetch Docs + action: fetch:plain + input: + targetPath: ./community + url: https://github.com/backstage/community/tree/main/backstage-community-sessions + + - id: publish + name: Publish + action: publish:github:pull-request + input: + repoUrl: '{{ parameters.repoUrl }}' + title: 'Create new project: {{parameters.name}}' + branchName: 'create-{{parameters.name}}' + description: | + # New project: {{parameters.name}} + + {{#if parameters.description}} + {{parameters.description}} + {{/if}} + host: '{{parameters.host}}' + targetPath: '{{#if parameters.targetPath}}{{parameters.targetPath}}{{else}}{{parameters.name}}{{/if}}' + + output: + remoteUrl: '{{steps.publish.output.remoteUrl}}' diff --git a/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml new file mode 100644 index 0000000000..dd1e0ebd09 --- /dev/null +++ b/plugins/scaffolder-backend/sample-templates/pull-request/template/catalog-info.yaml @@ -0,0 +1,8 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: {{cookiecutter.name | jsonify}} +spec: + type: website + lifecycle: experimental + owner: guest diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 0829a101f2..2d4b8fb4c5 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -23,6 +23,7 @@ import { createPublishAzureAction, createPublishBitbucketAction, createPublishGithubAction, + createPublishGithubPullRequestAction, createPublishGitlabAction, } from './publish'; import Docker from 'dockerode'; @@ -57,6 +58,9 @@ export const createBuiltinActions = (options: { createPublishGithubAction({ integrations, }), + createPublishGithubPullRequestAction({ + integrations, + }), createPublishGitlabAction({ integrations, }), diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts new file mode 100644 index 0000000000..fb9d3aeaa6 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts @@ -0,0 +1,236 @@ +/* + * 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 mockFs from 'mock-fs'; +import { Writable } from 'stream'; +import { + PullRequestCreator, + GithubPullRequestActionInput, + createPublishGithubPullRequestAction, + ClientFactoryInput, +} from './githubPullRequest'; +import { ActionContext, TemplateAction } from '../../types'; +import { getRootLogger } from '@backstage/backend-common'; + +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; + +const id = 'createPublishGithubPullRequestAction'; + +describe('createPublishGithubPullRequestAction', () => { + let instance: TemplateAction; + let fakeClient: PullRequestCreator; + + let clientFactory: (input: ClientFactoryInput) => Promise; + + beforeEach(() => { + const integrations = ScmIntegrations.fromConfig(new ConfigReader({})); + fakeClient = { + createPullRequest: jest.fn(async (_: any) => { + return { + url: 'https://api.github.com/myorg/myrepo/pull/123', + headers: {}, + status: 201, + data: { + html_url: 'https://github.com/myorg/myrepo/pull/123', + }, + }; + }), + }; + clientFactory = jest.fn(async () => fakeClient); + + instance = createPublishGithubPullRequestAction({ + integrations, + clientFactory, + }); + }); + + describe('with no sourcePath', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext; + + beforeEach(() => { + input = { + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + }; + + mockFs({ + [id]: { 'file.txt': 'Hello there!' }, + }); + + ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath: id, + }; + }); + it('creates a pull request', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + head: 'new-app', + body: 'This PR is really good', + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': 'Hello there!', + }, + }, + ], + }); + }); + + it('creates outputs for the url', async () => { + await instance.handler(ctx); + + expect(ctx.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + }); + afterEach(() => { + mockFs.restore(); + jest.resetAllMocks(); + }); + }); + + describe('with sourcePath', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext; + + beforeEach(() => { + input = { + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + sourcePath: 'source', + }; + + mockFs({ + [id]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + + ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath: id, + }; + }); + + afterEach(() => { + mockFs.restore(); + jest.resetAllMocks(); + }); + + it('creates a pull request with only relevant files', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + head: 'new-app', + body: 'This PR is really good', + changes: [ + { + commit: 'Create my new app', + files: { + 'foo.txt': 'Hello there!', + }, + }, + ], + }); + }); + }); + + describe('with repoUrl', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext; + + beforeEach(() => { + input = { + repoUrl: 'github.com?owner=myorg&repo=myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + }; + + mockFs({ + [id]: { 'file.txt': 'Hello there!' }, + }); + + ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath: id, + }; + }); + it('creates a pull request', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: 'Create my new app', + head: 'new-app', + body: 'This PR is really good', + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': 'Hello there!', + }, + }, + ], + }); + }); + + it('creates outputs for the url', async () => { + await instance.handler(ctx); + + expect(ctx.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + }); + afterEach(() => { + mockFs.restore(); + jest.resetAllMocks(); + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts new file mode 100644 index 0000000000..3a00610a92 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -0,0 +1,243 @@ +/* + * 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 { readFile } from 'fs-extra'; +import path from 'path'; +import { parseRepoUrl } from './util'; + +import { + GithubCredentialsProvider, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { zipObject } from 'lodash'; +import { createTemplateAction } from '../../createTemplateAction'; +import { Octokit } from '@octokit/rest'; +import { InputError, CustomErrorBase } from '@backstage/errors'; +import { createPullRequest } from 'octokit-plugin-create-pull-request'; +import globby from 'globby'; + +class GithubResponseError extends CustomErrorBase {} + +type CreatePullRequestResponse = { + data: { html_url: string }; +}; + +export interface PullRequestCreator { + createPullRequest( + options: createPullRequest.Options, + ): Promise; +} + +export type PullRequestCreatorConstructor = ( + octokit: Octokit, +) => PullRequestCreator; + +export type GithubPullRequestActionInput = { + title: string; + branchName: string; + description: string; + owner?: string; + repo?: string; + repoUrl?: string; + host?: string; + targetPath?: string; + sourcePath?: string; +}; + +export type ClientFactoryInput = { + integrations: ScmIntegrationRegistry; + host: string; + owner: string; + repo: string; +}; + +export const defaultClientFactory = async ({ + integrations, + owner, + repo, + host = 'github.com', +}: ClientFactoryInput): Promise => { + const integrationConfig = integrations.github.byHost(host)?.config; + + if (!integrationConfig) { + throw new InputError(`No integration for host ${host}`); + } + + const credentialsProvider = GithubCredentialsProvider.create( + integrationConfig, + ); + + if (!credentialsProvider) { + throw new InputError( + `No matching credentials for host ${host}, please check your integrations config`, + ); + } + + const { token } = await credentialsProvider.getCredentials({ + url: `${host}/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, + }); + + if (!token) { + throw new InputError( + `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`, + ); + } + + const OctokitPR = Octokit.plugin(createPullRequest); + + return new OctokitPR({ + auth: token, + baseUrl: integrationConfig.apiBaseUrl, + }); +}; + +interface CreateGithubPullRequestActionOptions { + integrations: ScmIntegrationRegistry; + clientFactory?: (input: ClientFactoryInput) => Promise; +} + +export const createPublishGithubPullRequestAction = ({ + integrations, + clientFactory = defaultClientFactory, +}: CreateGithubPullRequestActionOptions) => { + return createTemplateAction({ + id: 'publish:github:pull-request', + schema: { + input: { + required: ['owner', 'repo', 'title', 'description', 'branchName'], + type: 'object', + properties: { + owner: { + type: 'string', + title: 'Repository owner', + description: 'The owner of the target repository', + }, + repo: { + type: 'string', + title: 'Repository', + description: 'The github repository to create the file in', + }, + branchName: { + type: 'string', + title: 'Branch Name', + description: 'The name for the branch', + }, + title: { + type: 'string', + title: 'Pull Request Name', + description: 'The name for the pull request', + }, + description: { + type: 'string', + title: 'Pull Request Description', + description: 'The description of the pull request', + }, + sourcePath: { + type: 'string', + title: 'Working Subdirectory', + description: + 'Subdirectory of working directory to copy changes from', + }, + targetPath: { + type: 'string', + title: 'Repository Subdirectory', + description: 'Subdirectory of repository to apply changes to', + }, + }, + }, + output: { + required: ['remoteUrl'], + type: 'object', + properties: { + remoteUrl: { + type: 'string', + title: 'Pull Request URL', + description: 'Link to the pull request in Github', + }, + }, + }, + }, + async handler(ctx) { + let { owner, repo } = ctx.input; + let host = 'github.com'; + const { + repoUrl, + branchName, + title, + description, + targetPath, + sourcePath, + } = ctx.input; + + if (repoUrl) { + const parsed = parseRepoUrl(repoUrl); + host = parsed.host; + owner = parsed.owner; + repo = parsed.repo; + } + + if (!host || !owner || !repo) { + throw new InputError( + 'must provide either valid repo URL or owner and repo as parameters', + ); + } + + const client = await clientFactory({ integrations, host, owner, repo }); + const fileRoot = sourcePath + ? path.join(ctx.workspacePath, sourcePath) + : ctx.workspacePath; + const localFilePaths = await globby(`${fileRoot}/**/*.*`); + + const fileContents = await Promise.all( + localFilePaths.map(p => readFile(p)), + ); + + const repoFilePaths = localFilePaths.map(p => { + const relativePath = path.relative(fileRoot, p); + return targetPath ? `${targetPath}/${relativePath}` : relativePath; + }); + + const changes = [ + { + files: zipObject( + repoFilePaths, + fileContents.map(buf => buf.toString()), + ), + commit: title, + }, + ]; + + try { + const response = await client.createPullRequest({ + owner, + repo, + title, + changes, + body: description, + head: branchName, + }); + + if (!response) { + throw new GithubResponseError('null response from Github'); + } + + ctx.output('remoteUrl', response.data.html_url); + } catch (e) { + throw new GithubResponseError('Pull request creation failed', e); + } + }, + }); +}; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts index 419922704d..537a2b882d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/index.ts @@ -15,6 +15,7 @@ */ export { createPublishGithubAction } from './github'; +export { createPublishGithubPullRequestAction } from './githubPullRequest'; export { createPublishAzureAction } from './azure'; export { createPublishGitlabAction } from './gitlab'; export { createPublishBitbucketAction } from './bitbucket'; diff --git a/yarn.lock b/yarn.lock index ac7f53e651..af3ce6ab94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19885,6 +19885,13 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +octokit-plugin-create-pull-request@^3.9.3: + version "3.9.3" + resolved "https://registry.npmjs.org/octokit-plugin-create-pull-request/-/octokit-plugin-create-pull-request-3.9.3.tgz#f99f53907ac322a3494cc970514a023d7b659e2b" + integrity sha512-lTyNnCRoT4IvCQx2Cb4eFMqg8aIpsaDd59MNwf4OPnWAJM7hT6g7RW/icImvAzZLR4t5ENSLNzWarv2XqLL+Lg== + dependencies: + "@octokit/types" "^6.8.2" + oidc-token-hash@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.0.tgz#acdfb1f4310f58e64d5d74a4e8671a426986e888" From cba45d6f6c451b2fb1bf5dc808c2533099b5ad8a Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 31 Mar 2021 15:23:01 +0200 Subject: [PATCH 15/61] chore: fixing the docs based on @eXpire163 PR Signed-off-by: blam --- docs/getting-started/create-an-app.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/getting-started/create-an-app.md b/docs/getting-started/create-an-app.md index 837a27e592..fefb20b095 100644 --- a/docs/getting-started/create-an-app.md +++ b/docs/getting-started/create-an-app.md @@ -106,6 +106,12 @@ The install process may also fail if no Python installation is available. Python is commonly available in most systems already, but if it isn't you can head for example [here](https://www.python.org/downloads/) to install it. +#### Could not execute command yarn install + +Install yarn on your system with `npm install --global yarn` or for more details +refer to the +[prerequisites](https://backstage.io/docs/getting-started/running-backstage-locally#prerequisites) + ## Run the app When the installation is complete you can open the app folder and start the app. From 79fd3d863eab980f05f99311527b80781f2a9627 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 4 Mar 2021 22:58:15 +0100 Subject: [PATCH 16/61] example-app: migrate entity pages to composability api Signed-off-by: Patrik Oldsberg --- packages/app/package.json | 8 +- packages/app/src/App.tsx | 4 +- .../app/src/components/catalog/EntityPage.tsx | 857 ++++++++---------- yarn.lock | 299 ++---- 4 files changed, 478 insertions(+), 690 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index e5dbbaa5ff..d00ddefc76 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -42,10 +42,10 @@ "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@octokit/rest": "^18.0.12", - "@roadiehq/backstage-plugin-buildkite": "^0.1.3", - "@roadiehq/backstage-plugin-github-insights": "^0.3.2", - "@roadiehq/backstage-plugin-github-pull-requests": "^0.7.6", - "@roadiehq/backstage-plugin-travis-ci": "^0.4.5", + "@roadiehq/backstage-plugin-buildkite": "^0.3.0", + "@roadiehq/backstage-plugin-github-insights": "^0.3.3", + "@roadiehq/backstage-plugin-github-pull-requests": "^0.7.9", + "@roadiehq/backstage-plugin-travis-ci": "^0.4.7", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.12.0", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index defa70dbb7..379fc7e52d 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -48,8 +48,8 @@ import React from 'react'; import { hot } from 'react-hot-loader/root'; import { Navigate, Route } from 'react-router'; import { apis } from './apis'; -import { EntityPage } from './components/catalog/EntityPage'; import { Root } from './components/Root'; +import { entityPage } from './components/catalog/EntityPage'; import { providers } from './identityProviders'; import * as plugins from './plugins'; @@ -96,7 +96,7 @@ const routes = ( path="/catalog/:namespace/:kind/:name" element={} > - + {entityPage} } /> } /> diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 5dc4f4c570..76366a2ab8 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -14,174 +14,96 @@ * limitations under the License. */ -import { - ApiEntity, - DomainEntity, - Entity, - GroupEntity, - SystemEntity, - UserEntity, -} from '@backstage/catalog-model'; +import React, { ReactNode, useMemo, useState } from 'react'; +import BadgeIcon from '@material-ui/icons/CallToAction'; import { EmptyState } from '@backstage/core'; import { - ApiDefinitionCard, - ConsumedApisCard, - ConsumingComponentsCard, + EntityApiDefinitionCard, + EntityConsumingComponentsCard, EntityHasApisCard, - ProvidedApisCard, - ProvidingComponentsCard, + EntityProvidingComponentsCard, + EntityProvidedApisCard, + EntityConsumedApisCard, } from '@backstage/plugin-api-docs'; import { EntityBadgesDialog } from '@backstage/plugin-badges'; import { - AboutCard, + EntityAboutCard, EntityHasComponentsCard, EntityHasSubcomponentsCard, EntityHasSystemsCard, + EntityLayout, EntityLinksCard, - EntityPageLayout, EntitySystemDiagramCard, + EntitySwitch, + isComponentType, + isKind, } from '@backstage/plugin-catalog'; -import { EntityProvider, useEntity } from '@backstage/plugin-catalog-react'; import { - isPluginApplicableToEntity as isCircleCIAvailable, - Router as CircleCIRouter, + EntityCircleCIContent, + isCircleCIAvailable, } from '@backstage/plugin-circleci'; import { - isPluginApplicableToEntity as isCloudbuildAvailable, - Router as CloudbuildRouter, + EntityCloudbuildContent, + isCloudbuildAvailable, } from '@backstage/plugin-cloudbuild'; import { - isPluginApplicableToEntity as isGitHubActionsAvailable, - RecentWorkflowRunsCard, - Router as GitHubActionsRouter, + EntityGithubActionsContent, + EntityRecentGithubActionsRunsCard, + isGithubActionsAvailable, } from '@backstage/plugin-github-actions'; import { - isPluginApplicableToEntity as isJenkinsAvailable, - LatestRunCard as JenkinsLatestRunCard, - Router as JenkinsRouter, + EntityJenkinsContent, + EntityLatestJenkinsRunCard, + isJenkinsAvailable, } from '@backstage/plugin-jenkins'; -import { Router as KafkaRouter } from '@backstage/plugin-kafka'; -import { Router as KubernetesRouter } from '@backstage/plugin-kubernetes'; +import { EntityKafkaContent } from '@backstage/plugin-kafka'; +import { EntityKubernetesContent } from '@backstage/plugin-kubernetes'; import { - EmbeddedRouter as LighthouseRouter, - isPluginApplicableToEntity as isLighthouseAvailable, - LastLighthouseAuditCard, + EntityLastLighthouseAuditCard, + EntityLighthouseContent, + isLighthouseAvailable, } from '@backstage/plugin-lighthouse'; import { - GroupProfileCard, - MembersListCard, - OwnershipCard, - UserProfileCard, + EntityGroupProfileCard, + EntityMembersListCard, + EntityOwnershipCard, + EntityUserProfileCard, } from '@backstage/plugin-org'; import { - isPluginApplicableToEntity as isPagerDutyAvailable, - PagerDutyCard, + EntityPagerDutyCard, + isPagerDutyAvailable, } from '@backstage/plugin-pagerduty'; import { + EntityRollbarContent, isRollbarAvailable, - Router as RollbarRouter, } from '@backstage/plugin-rollbar'; -import { Router as SentryRouter } from '@backstage/plugin-sentry'; -import { EmbeddedDocsRouter as DocsRouter } from '@backstage/plugin-techdocs'; +import { EntitySentryContent } from '@backstage/plugin-sentry'; +import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; import { EntityTodoContent } from '@backstage/plugin-todo'; import { Button, Grid } from '@material-ui/core'; -import { - isPluginApplicableToEntity as isBuildkiteAvailable, - Router as BuildkiteRouter, -} from '@roadiehq/backstage-plugin-buildkite'; -import { - isPluginApplicableToEntity as isGitHubAvailable, - LanguagesCard, - ReadMeCard, - ReleasesCard, - Router as GitHubInsightsRouter, -} from '@roadiehq/backstage-plugin-github-insights'; -import { - isPluginApplicableToEntity as isPullRequestsAvailable, - PullRequestsStatsCard, - Router as PullRequestsRouter, -} from '@roadiehq/backstage-plugin-github-pull-requests'; -import { - isPluginApplicableToEntity as isTravisCIAvailable, - RecentTravisCIBuildsWidget, - Router as TravisCIRouter, -} from '@roadiehq/backstage-plugin-travis-ci'; -import React, { ReactNode, useMemo, useState } from 'react'; -import BadgeIcon from '@material-ui/icons/CallToAction'; +// import { +// EntityBuildkiteContent, +// isBuildkiteAvailable, +// } from '@roadiehq/backstage-plugin-buildkite'; +// import { +// EntityGitHubInsightsContent, +// EntityLanguagesCard, +// EntityReadMeCard, +// EntityReleasesCard, +// isGithubInsightsAvailable, +// } from '@roadiehq/backstage-plugin-github-insights'; +// import { +// EntityGithubPullRequestsContent, +// EntityGithubPullRequestsOverviewCard, +// isGithubPullRequestsAvailable, +// } from '@roadiehq/backstage-plugin-github-pull-requests'; +// import { +// EntityTravisCIContent, +// EntityTravisCIOverviewCard, +// isTravisciAvailable, +// } from '@roadiehq/backstage-plugin-travis-ci'; -export const CICDSwitcher = ({ entity }: { entity: Entity }) => { - // This component is just an example of how you can implement your company's logic in entity page. - // You can for example enforce that all components of type 'service' should use GitHubActions - switch (true) { - case isJenkinsAvailable(entity): - return ; - case isBuildkiteAvailable(entity): - return ; - case isCircleCIAvailable(entity): - return ; - case isCloudbuildAvailable(entity): - return ; - case isTravisCIAvailable(entity): - return ; - case isGitHubActionsAvailable(entity): - return ; - default: - return ( - - Read more - - } - /> - ); - } -}; - -const RecentCICDRunsSwitcher = ({ entity }: { entity: Entity }) => { - let content: ReactNode; - switch (true) { - case isJenkinsAvailable(entity): - content = ; - break; - case isTravisCIAvailable(entity): - content = ; - break; - case isGitHubActionsAvailable(entity): - content = ( - - ); - break; - default: - content = null; - } - if (!content) { - return null; - } - return ( - - {content} - - ); -}; - -export const ErrorsSwitcher = ({ entity }: { entity: Entity }) => { - switch (true) { - case isRollbarAvailable(entity): - return ; - default: - return ; - } -}; - -const EntityPageLayoutWrapper = (props: { children?: React.ReactNode }) => { +const EntityLayoutWrapper = (props: { children?: ReactNode }) => { const [badgesDialogOpen, setBadgesDialogOpen] = useState(false); const extraMenuItems = useMemo(() => { @@ -196,9 +118,9 @@ const EntityPageLayoutWrapper = (props: { children?: React.ReactNode }) => { return ( <> - + {props.children} - + setBadgesDialogOpen(false)} @@ -207,350 +129,367 @@ const EntityPageLayoutWrapper = (props: { children?: React.ReactNode }) => { ); }; -const ComponentOverviewContent = ({ entity }: { entity: Entity }) => ( +const cicdContent = ( + + + + + + {/* + + */} + + + + + + + + + + {/* + + */} + + + + + + + + Read more + + } + /> + + +); + +const cicdCard = ( + + + + + + + + {/* + + + + */} + + + + + + + +); + +const errorsContent = ( + + + + + + + + + +); + +const overviewContent = ( - - + + - {isPagerDutyAvailable(entity) && ( - - - - - - )} - - + + + + + + + + + + + - - {isGitHubAvailable(entity) && ( - <> - - - + + {cicdCard} + + {/* + Boolean(isGithubInsightsAvailable(e))}> + + + - - + + - - )} - {isLighthouseAvailable(entity) && ( - - - - )} - {isPullRequestsAvailable(entity) && ( - - - - )} - + + */} + + + + + + + + + + {/* + Boolean(isGithubPullRequestsAvailable(e))}> + + + + + */} + + ); -const ComponentApisContent = ({ entity }: { entity: Entity }) => ( - - - - - - - - -); +const serviceEntityPage = ( + + + {overviewContent} + -const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - -); + + {cicdContent} + -const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - -); + + {errorsContent} + -const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - } - /> - -); - -export const ComponentEntityPage = ({ entity }: { entity: Entity }) => { - switch (entity?.spec?.type) { - case 'service': - return ; - case 'website': - return ; - default: - return ; - } -}; - -const ApiOverviewContent = ({ entity }: { entity: Entity }) => ( - - - - - - - + + + + + + + + - - + + + + + + + + + + + {/* + + */} + + {/* + + */} + + + + + + + + + +); + +const websiteEntityPage = ( + + + {overviewContent} + + + + {cicdContent} + + + + + + + + {errorsContent} + + + + + + + + + + + {/* + + */} + + {/* + + */} + + + + + +); + +const defaultEntityPage = ( + + + {overviewContent} + + + + + + + + + + +); + +const componentPage = ( + + + {serviceEntityPage} + + + + {websiteEntityPage} + + + {defaultEntityPage} + +); + +const apiPage = ( + + + + + + + + + + + + + + - - + + + + + + + + + + ); -const ApiDefinitionContent = ({ entity }: { entity: ApiEntity }) => ( - - - - - +const userPage = ( + + + + + + + + + + + + ); -const ApiEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - +const groupPage = ( + + + + + + + + + + + + + + + + + + + ); -const UserOverviewContent = ({ entity }: { entity: UserEntity }) => ( - - - - - - - - +const systemPage = ( + + + + + + + + + + + + + + + ); -const UserEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - +const domainPage = ( + + + + + + + + + + + + ); -const GroupOverviewContent = ({ entity }: { entity: GroupEntity }) => ( - - - - - - - - - - - +export const entityPage = ( + + + + + + + + + {defaultEntityPage} + ); - -const GroupEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - -); - -const SystemOverviewContent = ({ entity }: { entity: SystemEntity }) => ( - - - - - - - - - - - -); - -const SystemEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - -); - -const DomainOverviewContent = ({ entity }: { entity: DomainEntity }) => ( - - - - - - - - -); - -const DomainEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - -); - -export const EntityPage = () => { - const { entity } = useEntity(); - - switch (entity?.kind?.toLocaleLowerCase('en-US')) { - case 'component': - return ; - case 'api': - return ; - case 'group': - return ; - case 'user': - return ; - case 'system': - return ; - case 'domain': - return ; - case 'location': - case 'resource': - case 'template': - default: - return ; - } -}; diff --git a/yarn.lock b/yarn.lock index 4fe945b334..2eef73e249 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1834,194 +1834,6 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@backstage/catalog-model@^0.2.0": - version "0.7.5" - dependencies: - "@backstage/config" "^0.1.3" - "@types/json-schema" "^7.0.5" - "@types/yup" "^0.29.8" - ajv "^7.0.3" - json-schema "^0.2.5" - lodash "^4.17.15" - uuid "^8.0.0" - yup "^0.29.3" - -"@backstage/core@^0.3.0": - version "0.7.3" - dependencies: - "@backstage/config" "^0.1.4" - "@backstage/core-api" "^0.2.15" - "@backstage/errors" "^0.1.1" - "@backstage/theme" "^0.2.5" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@testing-library/react-hooks" "^3.4.2" - "@types/dagre" "^0.7.44" - "@types/prop-types" "^15.7.3" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - "@types/react-text-truncate" "^0.14.0" - classnames "^2.2.6" - clsx "^1.1.0" - d3-selection "^2.0.0" - d3-shape "^2.0.0" - d3-zoom "^2.0.0" - dagre "^0.8.5" - immer "^9.0.1" - lodash "^4.17.15" - material-table "^1.69.1" - prop-types "^15.7.2" - qs "^6.9.4" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^6.6.0" - react-markdown "^5.0.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^15.4.3" - react-text-truncate "^0.16.0" - react-use "^15.3.3" - remark-gfm "^1.0.0" - zen-observable "^0.8.15" - -"@backstage/core@^0.6.0": - version "0.7.3" - dependencies: - "@backstage/config" "^0.1.4" - "@backstage/core-api" "^0.2.15" - "@backstage/errors" "^0.1.1" - "@backstage/theme" "^0.2.5" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@testing-library/react-hooks" "^3.4.2" - "@types/dagre" "^0.7.44" - "@types/prop-types" "^15.7.3" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - "@types/react-text-truncate" "^0.14.0" - classnames "^2.2.6" - clsx "^1.1.0" - d3-selection "^2.0.0" - d3-shape "^2.0.0" - d3-zoom "^2.0.0" - dagre "^0.8.5" - immer "^9.0.1" - lodash "^4.17.15" - material-table "^1.69.1" - prop-types "^15.7.2" - qs "^6.9.4" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^6.6.0" - react-markdown "^5.0.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^15.4.3" - react-text-truncate "^0.16.0" - react-use "^15.3.3" - remark-gfm "^1.0.0" - zen-observable "^0.8.15" - -"@backstage/core@^0.6.1": - version "0.7.3" - dependencies: - "@backstage/config" "^0.1.4" - "@backstage/core-api" "^0.2.15" - "@backstage/errors" "^0.1.1" - "@backstage/theme" "^0.2.5" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@testing-library/react-hooks" "^3.4.2" - "@types/dagre" "^0.7.44" - "@types/prop-types" "^15.7.3" - "@types/react" "^16.9" - "@types/react-sparklines" "^1.7.0" - "@types/react-text-truncate" "^0.14.0" - classnames "^2.2.6" - clsx "^1.1.0" - d3-selection "^2.0.0" - d3-shape "^2.0.0" - d3-zoom "^2.0.0" - dagre "^0.8.5" - immer "^9.0.1" - lodash "^4.17.15" - material-table "^1.69.1" - prop-types "^15.7.2" - qs "^6.9.4" - rc-progress "^3.0.0" - react "^16.12.0" - react-dom "^16.12.0" - react-helmet "6.1.0" - react-hook-form "^6.6.0" - react-markdown "^5.0.2" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-sparklines "^1.7.0" - react-syntax-highlighter "^15.4.3" - react-text-truncate "^0.16.0" - react-use "^15.3.3" - remark-gfm "^1.0.0" - zen-observable "^0.8.15" - -"@backstage/plugin-catalog@^0.2.1": - version "0.5.2" - dependencies: - "@backstage/catalog-client" "^0.3.9" - "@backstage/catalog-model" "^0.7.5" - "@backstage/core" "^0.7.3" - "@backstage/errors" "^0.1.1" - "@backstage/integration" "^0.5.1" - "@backstage/integration-react" "^0.1.1" - "@backstage/plugin-catalog-react" "^0.1.4" - "@backstage/theme" "^0.2.5" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - classnames "^2.2.6" - git-url-parse "^11.4.4" - react "^16.13.1" - react-dom "^16.13.1" - react-helmet "6.1.0" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - swr "^0.3.0" - -"@backstage/plugin-catalog@^0.3.1": - version "0.5.2" - dependencies: - "@backstage/catalog-client" "^0.3.9" - "@backstage/catalog-model" "^0.7.5" - "@backstage/core" "^0.7.3" - "@backstage/errors" "^0.1.1" - "@backstage/integration" "^0.5.1" - "@backstage/integration-react" "^0.1.1" - "@backstage/plugin-catalog-react" "^0.1.4" - "@backstage/theme" "^0.2.5" - "@material-ui/core" "^4.11.0" - "@material-ui/icons" "^4.9.1" - "@material-ui/lab" "4.0.0-alpha.45" - "@types/react" "^16.9" - classnames "^2.2.6" - git-url-parse "^11.4.4" - react "^16.13.1" - react-dom "^16.13.1" - react-helmet "6.1.0" - react-router "6.0.0-beta.0" - react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" - swr "^0.3.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -4302,6 +4114,11 @@ resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-5.3.2.tgz#b8ac43c5c3d00aef61a34cf744e315110c78deb4" integrity sha512-NxF1yfYOUO92rCx3dwvA2onF30Vdlg7YUkMVXkeptqpzA3tRLplThhFleV/UKWFgh7rpKu1yYRbvNDUtzSopKA== +"@octokit/openapi-types@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d" + integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ== + "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" @@ -4327,6 +4144,14 @@ "@octokit/types" "^6.12.2" deprecation "^2.3.1" +"@octokit/plugin-rest-endpoint-methods@5.0.0": + version "5.0.0" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98" + integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg== + dependencies: + "@octokit/types" "^6.13.0" + deprecation "^2.3.1" + "@octokit/request-error@^2.0.0": version "2.0.2" resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz#0e76b83f5d8fdda1db99027ea5f617c2e6ba9ed0" @@ -4350,7 +4175,7 @@ once "^1.4.0" universal-user-agent "^6.0.0" -"@octokit/rest@^18.0.0", "@octokit/rest@^18.0.12", "@octokit/rest@^18.1.0": +"@octokit/rest@^18.0.12", "@octokit/rest@^18.1.0": version "18.3.5" resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.3.5.tgz#a89903d46e0b4273bd3234674ec2777a651d68ab" integrity sha512-ZPeRms3WhWxQBEvoIh0zzf8xdU2FX0Capa7+lTca8YHmRsO3QNJzf1H3PcuKKsfgp91/xVDRtX91sTe1kexlbw== @@ -4360,6 +4185,16 @@ "@octokit/plugin-request-log" "^1.0.2" "@octokit/plugin-rest-endpoint-methods" "4.13.5" +"@octokit/rest@^18.1.1": + version "18.5.2" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b" + integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw== + dependencies: + "@octokit/core" "^3.2.3" + "@octokit/plugin-paginate-rest" "^2.6.2" + "@octokit/plugin-request-log" "^1.0.2" + "@octokit/plugin-rest-endpoint-methods" "5.0.0" + "@octokit/types@^5.0.0", "@octokit/types@^5.0.1": version "5.5.0" resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" @@ -4367,13 +4202,20 @@ dependencies: "@types/node" ">= 8" -"@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.12.2", "@octokit/types@^6.8.2": +"@octokit/types@^6.0.0", "@octokit/types@^6.0.1", "@octokit/types@^6.0.3", "@octokit/types@^6.12.2": version "6.12.2" resolved "https://registry.npmjs.org/@octokit/types/-/types-6.12.2.tgz#5b44add079a478b8eb27d78cf384cc47e4411362" integrity sha512-kCkiN8scbCmSq+gwdJV0iLgHc0O/GTPY1/cffo9kECu1MvatLPh9E+qFhfRIktKfHEA6ZYvv6S1B4Wnv3bi3pA== dependencies: "@octokit/openapi-types" "^5.3.2" +"@octokit/types@^6.13.0": + version "6.13.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0" + integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA== + dependencies: + "@octokit/openapi-types" "^6.0.0" + "@open-draft/until@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" @@ -4511,55 +4353,57 @@ 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" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-0.1.3.tgz#5a116bf677dfad22088212dde398cf3e9b48d6e4" - integrity sha512-q+cnAvZmjLu0DcqRKJZJhWTVcKaA9j7cM44tx6JcshEyb3UgzrlECLvx4ethnYyrmhM1/FSpMLa+t3N5A1Xz4g== +"@roadiehq/backstage-plugin-buildkite@^0.3.0": + version "0.3.0" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-0.3.0.tgz#60db5e712b88bc9cf3476737a1835f4fc26ae616" + integrity sha512-YeATwsR9G93/l3zZmzDN2k0vZzo0Tw0G0pp16B6p1Tz4lBUxiQ40D0anSsVrzep4NR3rxbzqzh7OGZ8Pihfcpw== dependencies: - "@backstage/catalog-model" "^0.2.0" - "@backstage/core" "^0.3.0" - "@backstage/plugin-catalog" "^0.2.1" + "@backstage/catalog-model" "^0.7.4" + "@backstage/core" "^0.7.3" + "@backstage/plugin-catalog" "^0.5.1" + "@backstage/theme" "^0.2.5" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" + history "^5.0.0" moment "^2.27.0" react "^16.13.1" react-dom "^16.13.1" react-lazylog "^4.5.2" react-router "6.0.0-beta.0" react-router-dom "6.0.0-beta.0" - react-use "^15.3.3" + react-use "^15.3.6" -"@roadiehq/backstage-plugin-github-insights@^0.3.2": - version "0.3.2" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.3.2.tgz#2db83e172ae34738677bd47c3b754c31bb2b5987" - integrity sha512-eIhqQS0jTRcdTCu+GxrRCH1tq+kDrTb6mMJpD/6sGnE9QXgj5nKHIm6KHmFYEfIiukQrGxXY4lABNWstvi7hgA== +"@roadiehq/backstage-plugin-github-insights@^0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-0.3.3.tgz#f2b4aeff76930d4ea4f5ca80bf1ad6f407dd6b74" + integrity sha512-sRczrC4JRli8Po1FXL5M6qiCJiUUWpin+kH7XSlo5JJoK5+UdNP05l1qFkMkeCyPdkJp/iVvXArkHPyWnm361g== dependencies: - "@backstage/catalog-model" "^0.7.1" - "@backstage/core" "^0.6.0" - "@backstage/theme" "^0.2.3" + "@backstage/catalog-model" "^0.7.4" + "@backstage/core" "^0.7.3" + "@backstage/theme" "^0.2.5" "@date-io/core" "2.10.7" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.57" - "@octokit/rest" "^18.0.0" - "@octokit/types" "^6.8.2" + "@octokit/rest" "^18.1.1" + "@octokit/types" "^6.13.0" history "^5.0.0" react "^16.13.1" react-dom "^16.13.1" react-router "^6.0.0-beta.0" - react-use "^17.1.0" + react-use "^17.2.1" -"@roadiehq/backstage-plugin-github-pull-requests@^0.7.6": - version "0.7.8" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-0.7.8.tgz#14a8e512b83de6cb5304b2a8e62c447b2bd6e311" - integrity sha512-sgBzIYC4GfZGbFpRixQBinf62laVqefRLHPLaoF2LuTMStXGcjUIGlqySmWNOXvpmGjCv3QIxh1NaxZBp6Z1Vw== +"@roadiehq/backstage-plugin-github-pull-requests@^0.7.9": + version "0.7.9" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-pull-requests/-/backstage-plugin-github-pull-requests-0.7.9.tgz#61ea6bc6a50defe0223ef8dff124379673f54c55" + integrity sha512-qUyCShhtV4x3QaZewiw13lQBmWLTjybOTsy7x+j4iY6IXkOhol5E2uWVTWifJLxDXg76FsXKWw9eRIqBSOrTRQ== dependencies: - "@backstage/catalog-model" "^0.7.1" - "@backstage/core" "^0.6.1" + "@backstage/catalog-model" "^0.7.4" + "@backstage/core" "^0.7.3" "@material-ui/core" "^4.11.0" "@material-ui/icons" "^4.9.1" - "@octokit/rest" "^18.0.0" + "@octokit/rest" "^18.1.1" "@octokit/types" "^5.0.1" "@types/node-fetch" "^2.5.7" history "^5.0.0" @@ -4570,20 +4414,20 @@ react-router "6.0.0-beta.0" react-use "^15.3.6" -"@roadiehq/backstage-plugin-travis-ci@^0.4.5": - version "0.4.5" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.4.5.tgz#c80eb972951badd110173f5521aae65b8a5cfc53" - integrity sha512-+y5kFtVaPjXgf6naGVU2E3C33nvoXuaBbfYEmCeByg2t0b5jHMHzsTv2tV5/8zeAcus/5+yIkRCq+F1Tcbwutw== +"@roadiehq/backstage-plugin-travis-ci@^0.4.7": + version "0.4.7" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-travis-ci/-/backstage-plugin-travis-ci-0.4.7.tgz#ce37ff8b3124975999f0cfe01170b8b5aee55fb3" + integrity sha512-hDyOND6/gpCy0GpHLwTDXwNfVWVIBpjh5Ipac0bBa7Yyyj0MKI4UVmLu1K99fRZk1aI6n6TIyLYAJTabLmuYuQ== dependencies: - "@backstage/catalog-model" "^0.7.0" - "@backstage/core" "^0.6.0" - "@backstage/plugin-catalog" "^0.3.1" - "@backstage/theme" "^0.2.1" + "@backstage/catalog-model" "^0.7.4" + "@backstage/core" "^0.7.3" + "@backstage/plugin-catalog" "^0.5.1" + "@backstage/theme" "^0.2.5" "@material-ui/core" "^4.9.1" "@material-ui/icons" "^4.9.1" "@material-ui/lab" "4.0.0-alpha.45" cross-fetch "^3.0.6" - date-fns "^2.16.1" + date-fns "^2.18.0" history "^5.0.0" moment "^2.29.1" react "^16.13.1" @@ -11413,6 +11257,11 @@ date-fns@^2.0.0-alpha.27, date-fns@^2.16.1: resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.18.0.tgz#08e50aee300ad0d2c5e054e3f0d10d8f9cdfe09e" integrity sha512-NYyAg4wRmGVU4miKq5ivRACOODdZRY3q5WLmOJSq8djyzftYphU7dTHLcEtLqEvfqMKQ0jVv91P4BAwIjsXIcw== +date-fns@^2.18.0: + version "2.19.0" + resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz#65193348635a28d5d916c43ec7ce6fbd145059e1" + integrity sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg== + dateformat@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -22483,7 +22332,7 @@ react-use@^15.3.3, react-use@^15.3.6: ts-easing "^0.2.0" tslib "^2.0.0" -react-use@^17.1.0: +react-use@^17.2.1: version "17.2.1" resolved "https://registry.npmjs.org/react-use/-/react-use-17.2.1.tgz#c81e12544115ed049c7deba1e3bb3d977dfee9b8" integrity sha512-9r51/at7/Nr/nEP4CsHz+pl800EAqhIY9R6O68m68kaWc8slDAfx1UrIedQqpsb4ImddFYb+6hF1i5Vj4u4Cnw== From 5386c168afa9f64784741e865f8d6e18da0b876c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 1 Apr 2021 13:24:19 +0200 Subject: [PATCH 17/61] create-app: fully migrate template to composability API Signed-off-by: Patrik Oldsberg --- .../default-app/packages/app/src/App.tsx | 4 +- .../app/src/components/catalog/EntityPage.tsx | 478 ++++++++---------- 2 files changed, 211 insertions(+), 271 deletions(-) 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 1cd7b77965..7323faeef2 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 @@ -19,7 +19,7 @@ import { TechRadarPage } from '@backstage/plugin-tech-radar'; import { TechdocsPage } from '@backstage/plugin-techdocs'; import { UserSettingsPage } from '@backstage/plugin-user-settings'; import { apis } from './apis'; -import { EntityPage } from './components/catalog/EntityPage'; +import { entityPage } from './components/catalog/EntityPage'; import { Root } from './components/Root'; import * as plugins from './plugins'; @@ -47,7 +47,7 @@ const routes = ( path="/catalog/:namespace/:kind/:name" element={} > - + {entityPage} } /> } /> diff --git a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx index d05f6bfa8d..f1aba46c52 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx @@ -15,308 +15,248 @@ */ import React from 'react'; import { Button, Grid } from '@material-ui/core'; -import { - ApiEntity, - DomainEntity, - Entity, - GroupEntity, - SystemEntity, - UserEntity, -} from '@backstage/catalog-model'; import { EmptyState } from '@backstage/core'; import { - ApiDefinitionCard, - ConsumedApisCard, - ConsumingComponentsCard, + EntityApiDefinitionCard, + EntityConsumedApisCard, + EntityConsumingComponentsCard, EntityHasApisCard, - ProvidedApisCard, - ProvidingComponentsCard, + EntityProvidedApisCard, + EntityProvidingComponentsCard, } from '@backstage/plugin-api-docs'; import { - AboutCard, + EntityAboutCard, + EntitySystemDiagramCard, EntityHasComponentsCard, EntityHasSystemsCard, - EntityPageLayout, + EntityLayout, + EntitySwitch, + isComponentType, + isKind, } from '@backstage/plugin-catalog'; -import { useEntity } from '@backstage/plugin-catalog-react'; import { - isPluginApplicableToEntity as isGitHubActionsAvailable, - Router as GitHubActionsRouter, + isGithubActionsAvailable, + EntityGithubActionsContent, } from '@backstage/plugin-github-actions'; import { - GroupProfileCard, - MembersListCard, - OwnershipCard, - UserProfileCard, + EntityUserProfileCard, + EntityGroupProfileCard, + EntityMembersListCard, + EntityOwnershipCard, } from '@backstage/plugin-org'; -import { EmbeddedDocsRouter as DocsRouter } from '@backstage/plugin-techdocs'; +import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; - -const CICDSwitcher = ({ entity }: { entity: Entity }) => { - // This component is just an example of how you can implement your company's logic in entity page. +const cicdContent = ( + // This is an example of how you can implement your company's logic in entity page. // You can for example enforce that all components of type 'service' should use GitHubActions - switch (true) { - case isGitHubActionsAvailable(entity): - return ; - default: - return ( - - Read more - - } - /> - ); - } -}; + + + + -const OverviewContent = ({ entity }: { entity: Entity }) => ( + + + Read more + + } + /> + + +); + +const overviewContent = ( - + ); -const ComponentApisContent = ({ entity }: { entity: Entity }) => ( - - - - - - - - -); +const serviceEntityPage = ( + + + {overviewContent} + -const ServiceEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - } - /> - } - /> - -); + + {cicdContent} + -const WebsiteEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - } - /> - -); - -const DefaultEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - -); - -export const ComponentEntityPage = ({ entity }: { entity: Entity }) => { - switch (entity?.spec?.type) { - case 'service': - return ; - case 'website': - return ; - default: - return ; - } -}; - -const ApiOverviewContent = ({ entity }: { entity: Entity }) => ( - - - - - - - + + + + + + + + - - + + + + + + +); + +const websiteEntityPage = ( + + + {overviewContent} + + + + {cicdContent} + + + + + + +); + +const defaultEntityPage = ( + + + {overviewContent} + + + + + + +); + +const componentPage = ( + + + {serviceEntityPage} + + + + {websiteEntityPage} + + + {defaultEntityPage} + +); + +const apiPage = ( + + + + + + + + + + + + + + - - + + + + + + + + + + ); -const ApiDefinitionContent = ({ entity }: { entity: ApiEntity }) => ( - - - - - +const userPage = ( + + + + + + + + + + + + ); -const ApiEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - } - /> - +const groupPage = ( + + + + + + + + + + + + + + + + + + + ); -const UserOverviewContent = ({ entity }: { entity: UserEntity }) => ( - - - - - - - - +const systemPage = ( + + + + + + + + + + + + + + + ); -const UserEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - +const domainPage = ( + + + + + + + + + + + + ); -const GroupOverviewContent = ({ entity }: { entity: GroupEntity }) => ( - - - - - - - - - - - +export const entityPage = ( + + + + + + + + + {defaultEntityPage} + ); - -const GroupEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - -); - -const SystemOverviewContent = ({ entity }: { entity: SystemEntity }) => ( - - - - - - - - - - - -); - -const SystemEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - -); - -const DomainOverviewContent = ({ entity }: { entity: DomainEntity }) => ( - - - - - - - - -); - -const DomainEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - -); - -export const EntityPage = () => { - const { entity } = useEntity(); - - switch (entity?.kind?.toLocaleLowerCase('en-US')) { - case 'component': - return ; - case 'api': - return ; - case 'group': - return ; - case 'user': - return ; - case 'system': - return ; - case 'domain': - return ; - case 'location': - case 'resource': - case 'template': - default: - return ; - } -}; From 3e7de08afc0438a9fb2894fea06cbd22de8cf987 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 1 Apr 2021 13:58:14 +0200 Subject: [PATCH 18/61] changesets: added changeset for composability migration Signed-off-by: Patrik Oldsberg --- .changeset/warm-rivers-dance.md | 188 ++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 .changeset/warm-rivers-dance.md diff --git a/.changeset/warm-rivers-dance.md b/.changeset/warm-rivers-dance.md new file mode 100644 index 0000000000..d128ddeb9e --- /dev/null +++ b/.changeset/warm-rivers-dance.md @@ -0,0 +1,188 @@ +-- +'@backstage/create-app': patch + +--- + +**Fully migrated the template to the new composability API** + +The `create-app` template is now fully migrated to the new composability API, see [Composability System Migration Documentation](https://backstage.io/docs/plugins/composability) for explanations and more details. The final change which is now done was to migrate the `EntityPage` from being a component built on top of the `EntityPageLayout` and several more custom components, to an element tree built with `EntitySwitch` and `EntityLayout`. + +To apply this change to an existing plugin, it is important that all plugins that you are using have already been migrated. In this case the most crucial piece is that no entity page cards of contents may require the `entity` prop, and they must instead consume the entity from context using `useEntity`. + +Since this change is large with a lot of repeated changes, we'll describe a couple of common cases rather than the entire change. If your entity pages are unchanged from the `create-app` template, you can also just bring in the latest version directly from the [template itself](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx). + +The first step of the change is to change the `packages/app/src/components/catalog/EntityPage.tsx` export to `entityPage` rather than `EntityPage`. This will require an update to `App.tsx`, which is the only change we need to do outside of `EntityPage.tsx`: + +```diff +-import { EntityPage } from './components/catalog/EntityPage'; ++import { entityPage } from './components/catalog/EntityPage'; + + } + > +- ++ {entityPage} + +``` + +The rest of the changes happen within `EntityPage.tsx`, and can be split into two broad categories, updating page components, and updating switch components. + +#### Migrating Page Components + +Let's start with an example of migrating a user page component. The following is the old code in the template: + +```tsx +const UserOverviewContent = ({ entity }: { entity: UserEntity }) => ( + + + + + + + + +); + +const UserEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + +); +``` + +There's the main `UserEntityPage` component, and the `UserOverviewContent` component. Let's start with migrating the page contents, which we do by rendering an element rather than creating a component, as well as replace the cards with their new composability compatible variants. The new cards and content components can be identified by the `Entity` prefix. + +```tsx +const userOverviewContent = ( + + + + + + + + +); +``` + +Now let's migrate the page component, again by converting it into a rendered element instead of a component, as well as replacing the use of `EntityPageLayout` with `EntityLayout`. + +```tsx +const userPage = ( + + + {userOverviewContent} + + +); +``` + +At this point the `userPage` is quite small, so throughout this migration we have inlined the page contents for all pages. This is an optional step, but may help reduce noise. The final page now looks like this: + +```tsx +const userPage = ( + + + + + + + + + + + + +); +``` + +#### Migrating Switch Components + +Switch components were used to select what entity page components or cards to render, based on for example the kind of entity. For this example we'll focus on the root `EntityPage` switch component, but the process is the same for example for the CI/CD switcher. + +The old `EntityPage` looked like this: + +```tsx +export const EntityPage = () => { + const { entity } = useEntity(); + + switch (entity?.kind?.toLocaleLowerCase('en-US')) { + case 'component': + return ; + case 'api': + return ; + case 'group': + return ; + case 'user': + return ; + case 'system': + return ; + case 'domain': + return ; + case 'location': + case 'resource': + case 'template': + default: + return ; + } +}; +``` + +In order to migrate to the composability API, we need to make this an element instead of a component, which means we're unable to keep the switch statement as is. To help with this, the catalog plugin provides an `EntitySwitch` component, which functions similar to a regular `switch` statement, which the first match being the one that is rendered. The catalog plugin also provides a number of built-in filter functions to use, such as `isKind` and `isComponentType`. + +To migrate the `EntityPage`, we convert the `switch` statement into an `EntitySwitch` element, and each `case` statement into an `EntitySwitch.Case` element. We also move over to use our new element version of the page components, with the result looking like this: + +```tsx +export const entityPage = ( + + + + + + + + + {defaultEntityPage} + +); +``` + +Another example is the `ComponentEntityPage`, which is migrated from this: + +```tsx +export const ComponentEntityPage = ({ entity }: { entity: Entity }) => { + switch (entity?.spec?.type) { + case 'service': + return ; + case 'website': + return ; + default: + return ; + } +}; +``` + +To this: + +```tsx +const componentPage = ( + + + {serviceEntityPage} + + + + {websiteEntityPage} + + + {defaultEntityPage} + +); +``` + +Note that if you want to conditionally render some piece of content, you can omit the default `EntitySwitch.Case`. If no case is matched in an `EntitySwitch`, nothing will be rendered. From 8818602117edcd2e2b5bdfc182d194ee178d049b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 1 Apr 2021 14:41:26 +0200 Subject: [PATCH 19/61] example-app: update EntityPage test Signed-off-by: Patrik Oldsberg --- .../components/catalog/EntityPage.test.tsx | 58 +++++++++++-------- .../app/src/components/catalog/EntityPage.tsx | 2 +- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/packages/app/src/components/catalog/EntityPage.test.tsx b/packages/app/src/components/catalog/EntityPage.test.tsx index 1392f77a6d..9860792ea0 100644 --- a/packages/app/src/components/catalog/EntityPage.test.tsx +++ b/packages/app/src/components/catalog/EntityPage.test.tsx @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; -import { CICDSwitcher } from './EntityPage'; -import { UrlPatternDiscovery, ApiProvider, ApiRegistry } from '@backstage/core'; -import { - buildKiteApiRef, - BuildkiteApi, -} from '@roadiehq/backstage-plugin-buildkite'; -import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; +import { ApiProvider, ApiRegistry } from '@backstage/core'; +import { EntityLayout } from '@backstage/plugin-catalog'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { cicdContent } from './EntityPage'; +import { githubActionsApiRef } from '@backstage/plugin-github-actions'; describe('EntityPage Test', () => { const entity = { @@ -29,7 +29,7 @@ describe('EntityPage Test', () => { metadata: { name: 'ExampleComponent', annotations: { - 'buildkite.com/project-slug': 'exampleProject/examplePipeline', + 'github.com/project-slug': 'example/project', }, }, spec: { @@ -39,24 +39,36 @@ describe('EntityPage Test', () => { }, }; - const discoveryApi = UrlPatternDiscovery.compile('http://exampleapi.com'); + const mockedApi = { + listWorkflowRuns: jest.fn().mockResolvedValue([]), + getWorkflow: jest.fn(), + getWorkflowRun: jest.fn(), + reRunWorkflow: jest.fn(), + downloadJobLogsForWorkflowRun: jest.fn(), + } as jest.Mocked; - const apis = ApiRegistry.from([ - [buildKiteApiRef, new BuildkiteApi({ discoveryApi })], - ]); + const apis = ApiRegistry.with(githubActionsApiRef, mockedApi); - describe('CICDSwitcher Test', () => { - it('Should render Buildkite View', async () => { - const renderedComponent = await renderWithEffects( - wrapInTestApp( - - - , - ), + describe('cicdContent', () => { + it('Should render GitHub Actions View', async () => { + const rendered = await renderInTestApp( + + + + + {cicdContent} + + + + , ); - expect( - renderedComponent.getByText(/exampleProject\/examplePipeline/), - ).toBeInTheDocument(); + + expect(rendered.getByText('ExampleComponent')).toBeInTheDocument(); + + await expect( + rendered.findByText('No Workflow Data'), + ).resolves.toBeInTheDocument(); + expect(rendered.getByText('Create new Workflow')).toBeInTheDocument(); }); }); }); diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 76366a2ab8..d8ee62a12c 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -129,7 +129,7 @@ const EntityLayoutWrapper = (props: { children?: ReactNode }) => { ); }; -const cicdContent = ( +export const cicdContent = ( From 3f96a9d5a7c2f4bef2410a45aa1549832b47946c Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 2 Apr 2021 03:28:06 +0200 Subject: [PATCH 20/61] fix: Support auth by sending cookies in scaffolder eventstream request Signed-off-by: Erik Larsson --- .changeset/mighty-bats-nail.md | 5 +++++ plugins/scaffolder/src/api.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/mighty-bats-nail.md diff --git a/.changeset/mighty-bats-nail.md b/.changeset/mighty-bats-nail.md new file mode 100644 index 0000000000..a74d8fa48c --- /dev/null +++ b/.changeset/mighty-bats-nail.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Support auth by sending cookies in eventstream request diff --git a/plugins/scaffolder/src/api.ts b/plugins/scaffolder/src/api.ts index e266d06a91..6caa2a4446 100644 --- a/plugins/scaffolder/src/api.ts +++ b/plugins/scaffolder/src/api.ts @@ -196,7 +196,7 @@ export class ScaffolderClient implements ScaffolderApi { const url = `${baseUrl}/v2/tasks/${encodeURIComponent( taskId, )}/eventstream`; - const eventSource = new EventSource(url); + const eventSource = new EventSource(url, { withCredentials: true }); eventSource.addEventListener('log', (event: any) => { if (event.data) { try { From a0e97966a51c6c288051f051be7d2adedf59dc87 Mon Sep 17 00:00:00 2001 From: Mateus Marquezini Date: Fri, 2 Apr 2021 08:00:02 -0300 Subject: [PATCH 21/61] #3497 improvement on Dialog component to follow MUI guidelines Signed-off-by: Mateus Marquezini --- .../src/components/Dialog/Dialog.stories.tsx | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/core/src/components/Dialog/Dialog.stories.tsx b/packages/core/src/components/Dialog/Dialog.stories.tsx index 528b266e47..c9388dcbec 100644 --- a/packages/core/src/components/Dialog/Dialog.stories.tsx +++ b/packages/core/src/components/Dialog/Dialog.stories.tsx @@ -29,10 +29,6 @@ import React, { useState } from 'react'; const useStyles = makeStyles((theme: Theme) => createStyles({ - leftAlignButtonsDialog: { - justifyContent: 'flex-start', - paddingLeft: 24, - }, closeButton: { position: 'absolute', right: theme.spacing(1), @@ -88,12 +84,9 @@ export const Default = () => { - The color for the secondary button is the same as the primary. For the - primary action button, use: + The color for the secondary button is the same as the primary. -
variant="contained"
- For the secondary action button, use: -
variant="outlined"
+
color="primary"
); }; @@ -120,13 +113,13 @@ export const Default = () => { {dialogContent()} - - - + From d710520dbedf26e57ecdd97129cb901c40a686ed Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Fri, 2 Apr 2021 19:26:27 +0200 Subject: [PATCH 22/61] vale Signed-off-by: Erik Larsson --- .changeset/mighty-bats-nail.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/mighty-bats-nail.md b/.changeset/mighty-bats-nail.md index a74d8fa48c..1fe2c0ee1d 100644 --- a/.changeset/mighty-bats-nail.md +++ b/.changeset/mighty-bats-nail.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder': patch --- -Support auth by sending cookies in eventstream request +Support auth by sending cookies in event stream request From 4ddd7b264225a7fe2a6b8f682b83888b25fcca29 Mon Sep 17 00:00:00 2001 From: Podge Date: Fri, 2 Apr 2021 18:38:04 +0100 Subject: [PATCH 23/61] modifying CatalogImportClient.test.ts last test to test for a non-Latin text by adding an emoji to the file content field Signed-off-by: Podge --- plugins/catalog-import/src/api/CatalogImportClient.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-import/src/api/CatalogImportClient.test.ts b/plugins/catalog-import/src/api/CatalogImportClient.test.ts index 9d3ca8d22a..ad856bd62b 100644 --- a/plugins/catalog-import/src/api/CatalogImportClient.test.ts +++ b/plugins/catalog-import/src/api/CatalogImportClient.test.ts @@ -307,7 +307,7 @@ describe('CatalogImportClient', () => { await expect( catalogImportClient.submitPullRequest({ repositoryUrl: 'https://github.com/backstage/backstage', - fileContent: 'some content', + fileContent: 'some content 🤖', title: 'A title/message', body: 'A body', }), @@ -333,7 +333,7 @@ describe('CatalogImportClient', () => { repo: 'backstage', path: 'catalog-info.yaml', message: 'A title/message', - content: 'c29tZSBjb250ZW50', + content: 'c29tZSBjb250ZW50IPCfpJY=', branch: 'backstage-integration', }); expect( From f3980ccd30fbc6a8a05c9c8f6d0c6445981f860f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Apr 2021 04:25:24 +0000 Subject: [PATCH 24/61] chore(deps): bump ora from 5.3.0 to 5.4.0 Bumps [ora](https://github.com/sindresorhus/ora) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/sindresorhus/ora/releases) - [Commits](https://github.com/sindresorhus/ora/compare/v5.3.0...v5.4.0) Signed-off-by: dependabot[bot] --- yarn.lock | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index b0b8c04313..dbcd3cb8db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8962,10 +8962,10 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -bl@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" - integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== +bl@^4.0.3, bl@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== dependencies: buffer "^5.5.0" inherits "^2.0.4" @@ -16166,6 +16166,11 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-upper-case@^2.0.1: version "2.0.2" resolved "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz#f1105ced1fe4de906a5f39553e7d3803fd804649" @@ -18003,12 +18008,13 @@ log-symbols@^3.0.0: dependencies: chalk "^2.4.2" -log-symbols@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== +log-symbols@^4.0.0, log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: - chalk "^4.0.0" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" log-update@^2.3.0: version "2.3.0" @@ -19991,16 +19997,17 @@ optionator@^0.9.1: word-wrap "^1.2.3" ora@^5.3.0: - version "5.3.0" - resolved "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" - integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g== + version "5.4.0" + resolved "https://registry.npmjs.org/ora/-/ora-5.4.0.tgz#42eda4855835b9cd14d33864c97a3c95a3f56bf4" + integrity sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg== dependencies: - bl "^4.0.3" + bl "^4.1.0" chalk "^4.1.0" cli-cursor "^3.1.0" cli-spinners "^2.5.0" is-interactive "^1.0.0" - log-symbols "^4.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" strip-ansi "^6.0.0" wcwidth "^1.0.1" From 454002eee082453af577ca8392d193a2e62741db Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Mon, 5 Apr 2021 09:10:55 -0600 Subject: [PATCH 25/61] Add contrib doc for AWS ECR + EKS deployment Signed-off-by: Tim Hansen --- .github/styles/vocab.txt | 1 + contrib/docs/tutorials/aws-deployment.md | 195 +++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 contrib/docs/tutorials/aws-deployment.md diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 6e3d69330a..96aa9d703e 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -9,6 +9,7 @@ Blackbox Chai Changesets Chanwit +Cloudformation Codecov Codehilite Config diff --git a/contrib/docs/tutorials/aws-deployment.md b/contrib/docs/tutorials/aws-deployment.md new file mode 100644 index 0000000000..5f94672b82 --- /dev/null +++ b/contrib/docs/tutorials/aws-deployment.md @@ -0,0 +1,195 @@ +# Deploying Backstage on AWS using ECR and EKS + +Backstage documentation shows how to build a [Docker +image](https://backstage.io/docs/getting-started/deployment-docker); this +tutorial shows how to deploy that Docker image to AWS using Elastic Container +Registry (ECR) and Elastic Kubernetes Service (EKS). Amazon also supports +deployments with Helm, covered in the [Helm +Kubernetes](../kubernetes/basic_kubernetes_example_with_helm) example. + +The basic workflow for this method is to build a Backstage Docker image, upload +the new version to a container registry, and update a Kubernetes deployment with +the new image. + +## Create a container registry + +To create an Elastic Container Registry on AWS, go to the [AWS ECR +console](https://console.aws.amazon.com/ecr/repositories). + +Click `Create repository` and give the repository a name, like `backstage`. + +## Add an ECR IAM user + +To push to this new ECR repository, there must be an IAM user with +`AmazonEC2ContainerRegistry` permission. + +Go to [AWS IAM console](https://console.aws.amazon.com/iam/home) and select +`Users`. + +1. Click `Add User` +2. Set the username to something like `ecr-publisher` and select `Programmatic access` only. +3. For `Permissions`, you can either create a new group or attach policies + directly to the user with `AmazonEC2ContainerRegistryFullAccess`. +4. Finish creating the user; an access key ID and secret access key will be + generated. + +## Publish a Backstage build + +Follow the [Docker +image](https://backstage.io/docs/getting-started/deployment-docker) +documentation to build a new Backstage Docker image: + +```shell +$ yarn build +$ docker image build . -f packages/backend/Dockerfile --tag backstage +``` + +This command builds a backend-only image, but you can similarly build a frontend +or combined Docker image. + +Next, configure the [AWS CLI](https://aws.amazon.com/cli/) to use the +`ecr-publisher` user you created: + +```shell +$ aws configure +AWS Access Key ID: +AWS Secret Access Key: +Default region name: +``` + +Now you can use the AWS CLI to push the built image to the ECR repository. It's +a good practice to use a specific version tag as well as `latest` when pushing; +more about [Semver tagging +here](https://medium.com/@mccode/using-semantic-versioning-for-docker-image-tags-dfde8be06699). + +Go to the [AWS ECR console](https://console.aws.amazon.com/ecr/repositories) and +click on your repository, then `View push commands`. This will show the +repository URL to push and some sample commands. + +```shell +# Copy from push command window +$ aws ecr get-login-password --region | docker login --username AWS --password-stdin +Login Succeeded + +$ docker tag backstage:latest /backstage:latest +$ docker push /backstage:latest + +$ docker tag backstage:latest /backstage:1.0.0 +$ docker push /backstage:1.0.0 +``` + +These will take a few minutes to upload the Docker images, then you can see +the image with `latest, 1.0.0` tags in the ECR repository. Now you can create a +Kubernetes deployment based on this image. + +## Deploy to an EKS cluster + +Creating an Elastic Kubernetes Service (EKS) cluster is beyond the scope of this +document, but it can be as easy as `eksctl create cluster` documented in the +[AWS EKS getting started +guide](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html), +which uses a Cloudformation template to create the necessary resources. + +To deploy the Docker image to EKS, create a `kubernetes` folder in your +Backstage source folder and add a Kubernetes `deployment.yaml`: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: backstage-backend + labels: + app: backstage-backend + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: backstage-backend + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: backstage-backend + spec: + containers: + - image: /backstage:1.0.0 + imagePullPolicy: Always + name: backstage-backend + ports: + - containerPort: 7000 + protocol: TCP +``` + +Note the `image` key in the container spec referencing the ECR repository. + +Now create a simple `service.yaml` to map the container ports: + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: backstage-backend +spec: + selector: + app: backstage-backend + ports: + - protocol: TCP + port: 80 + targetPort: 7000 +``` + +Apply these Kubernetes definitions to the EKS cluster to complete the Backstage +deployment: + +```shell +$ kubectl apply -f deployment.yaml +$ kubectl apply -f service.yaml +``` + +Now you can see your Backstage workload running from the [EKS +console](https://console.aws.amazon.com/eks/home). + +## Further steps + +### Exposing Backstage with a load balancer + +Backstage users need to query the backend, which means we need to expose +the workload with a load balancer. Follow the [Application load balancing on +EKS](https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html) guide to +set up a Load Balancer controller and Kubernetes ingress to your application. + +This is ultimately a `kubectl apply` with an ingress definition: + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: default + name: backstage-ingress + annotations: + kubernetes.io/ingress.class: alb + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: backstage-backend + port: + number: 80 +``` + +### Updating the deployment + +To update the Kubernetes deployment to a newly published version of your +Backstage Docker image, update the image tag reference in `deployment.yaml` and +then apply the changes to EKS with `kubectl apply -f deployment.yaml`. From ed134d9397872ce28d146cfa8c34946ce4abd0d8 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 6 Apr 2021 13:55:02 +0200 Subject: [PATCH 26/61] Fix example data link The old file was renamed. The new link points to an exact commit instead, so that it can't break again in the future. Signed-off-by: Oliver Sand --- packages/catalog-model/examples/apis/spotify-api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-model/examples/apis/spotify-api.yaml b/packages/catalog-model/examples/apis/spotify-api.yaml index 0b8e80b86e..1110627473 100644 --- a/packages/catalog-model/examples/apis/spotify-api.yaml +++ b/packages/catalog-model/examples/apis/spotify-api.yaml @@ -14,4 +14,4 @@ spec: lifecycle: production owner: team-a definition: - $text: https://github.com/APIs-guru/openapi-directory/blob/master/APIs/spotify.com/v1/swagger.yaml + $text: https://github.com/APIs-guru/openapi-directory/blob/dab6854d4d599aafb0eb36e6c7ae1fe0c37509b7/APIs/spotify.com/2021.4.2/openapi.yaml From 1279a33259a967b731605046fcbc1dfed9c0a835 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 6 Apr 2021 15:23:52 +0200 Subject: [PATCH 27/61] Handle chunk loading errors Signed-off-by: Oliver Sand --- .changeset/forty-queens-hear.md | 7 +++ packages/core-api/src/app/types.ts | 2 +- .../core-api/src/extensions/extensions.tsx | 56 +++++++++++-------- packages/core/src/api-wrappers/createApp.tsx | 34 ++++++++--- 4 files changed, 67 insertions(+), 32 deletions(-) create mode 100644 .changeset/forty-queens-hear.md diff --git a/.changeset/forty-queens-hear.md b/.changeset/forty-queens-hear.md new file mode 100644 index 0000000000..5e0b35162a --- /dev/null +++ b/.changeset/forty-queens-hear.md @@ -0,0 +1,7 @@ +--- +'@backstage/core': patch +'@backstage/core-api': patch +--- + +Introduce a `load-chunk` step in the `BootErrorPage` to show make chunk loading +errors visible to the user. diff --git a/packages/core-api/src/app/types.ts b/packages/core-api/src/app/types.ts index 5bdf51837d..cf3aaac931 100644 --- a/packages/core-api/src/app/types.ts +++ b/packages/core-api/src/app/types.ts @@ -24,7 +24,7 @@ import { AppConfig } from '@backstage/config'; import { SubRouteRef } from '../routing/types'; export type BootErrorPageProps = { - step: 'load-config'; + step: 'load-config' | 'load-chunk'; error: Error; }; diff --git a/packages/core-api/src/extensions/extensions.tsx b/packages/core-api/src/extensions/extensions.tsx index 9463e437e3..8ff4055e61 100644 --- a/packages/core-api/src/extensions/extensions.tsx +++ b/packages/core-api/src/extensions/extensions.tsx @@ -15,9 +15,10 @@ */ import React, { lazy, Suspense } from 'react'; +import { useApp } from '../app'; +import { BackstagePlugin, Extension } from '../plugin/types'; import { RouteRef, useRouteRef } from '../routing'; import { attachComponentData } from './componentData'; -import { Extension, BackstagePlugin } from '../plugin/types'; type ComponentLoader = | { @@ -40,30 +41,41 @@ export function createRoutableExtension< return createReactExtension({ component: { lazy: () => - component().then(InnerComponent => { - const RoutableExtensionWrapper: any = (props: any) => { - // Validate that the routing is wired up correctly in the App.tsx - try { - useRouteRef(mountPoint); - } catch { - throw new Error( - `Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` + - 'Routable extension components may not be rendered by other components and must be ' + - 'directly available as an element within the App provider component.', - ); - } - return ; - }; + component().then( + InnerComponent => { + const RoutableExtensionWrapper: any = (props: any) => { + // Validate that the routing is wired up correctly in the App.tsx + try { + useRouteRef(mountPoint); + } catch { + throw new Error( + `Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` + + 'Routable extension components may not be rendered by other components and must be ' + + 'directly available as an element within the App provider component.', + ); + } + return ; + }; - const componentName = - (InnerComponent as { displayName?: string }).displayName || - InnerComponent.name || - 'LazyComponent'; + const componentName = + (InnerComponent as { displayName?: string }).displayName || + InnerComponent.name || + 'LazyComponent'; - RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`; + RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`; - return RoutableExtensionWrapper as T; - }), + return RoutableExtensionWrapper as T; + }, + error => { + const RoutableExtensionWrapper: any = (_: any) => { + const app = useApp(); + const { BootErrorPage } = app.getComponents(); + + return ; + }; + return RoutableExtensionWrapper; + }, + ), }, data: { 'core.mountPoint': mountPoint, diff --git a/packages/core/src/api-wrappers/createApp.tsx b/packages/core/src/api-wrappers/createApp.tsx index db8d0d0636..b071c2d014 100644 --- a/packages/core/src/api-wrappers/createApp.tsx +++ b/packages/core/src/api-wrappers/createApp.tsx @@ -97,16 +97,32 @@ export function createApp(options?: AppOptions) { ); const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => { - let message = ''; - if (step === 'load-config') { - message = `The configuration failed to load, someone should have a look at this error: ${error.message}`; + switch (step) { + case 'load-config': + // TODO: figure out a nicer way to handle routing on the error page, when it can be done. + return ( + + + + ); + case 'load-chunk': + return ( + + ); + default: + // TODO: figure out a nicer way to handle routing on the error page, when it can be done. + return ( + + + + ); } - // TODO: figure out a nicer way to handle routing on the error page, when it can be done. - return ( - - - - ); }; const apis = options?.apis ?? []; From 3dabebb5fa35c43d18de7cc9ba343f93642d7d03 Mon Sep 17 00:00:00 2001 From: James Turley Date: Mon, 15 Mar 2021 13:11:05 +0000 Subject: [PATCH 28/61] Add links to TaskOutput type Signed-off-by: James Turley Signed-off-by: James Turley --- .../scaffolder/src/components/hooks/useEventStream.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/components/hooks/useEventStream.ts b/plugins/scaffolder/src/components/hooks/useEventStream.ts index 210fd8cb9b..958c3d192e 100644 --- a/plugins/scaffolder/src/components/hooks/useEventStream.ts +++ b/plugins/scaffolder/src/components/hooks/useEventStream.ts @@ -19,6 +19,11 @@ import { scaffolderApiRef, LogEvent } from '../../api'; import { ScaffolderTask, Status } from '../../types'; import { Subscription, useApi } from '@backstage/core'; +type OutputLink = { + title: string; + url: string; +}; + type Step = { id: string; status: Status; @@ -26,7 +31,10 @@ type Step = { startedAt?: string; }; -type TaskOutput = { entityRef?: string } & { [key in string]: string }; +type TaskOutput = { + entityRef?: string; + links?: OutputLink[]; +} & { [key in string]: string }; export type TaskStream = { loading: boolean; From b96d9c5677dcbcf275b623ac89de1aab4d71c0a0 Mon Sep 17 00:00:00 2001 From: James Turley Date: Tue, 16 Mar 2021 10:45:48 +0000 Subject: [PATCH 29/61] Move IconLink from catalog to core To reuse in scaffolder. Signed-off-by: James Turley Signed-off-by: James Turley --- .../components/IconLink}/IconLink.test.tsx | 0 .../src/components/IconLink}/IconLink.tsx | 28 ++++++++++--------- .../core/src/components/IconLink/index.ts | 17 +++++++++++ packages/core/src/components/index.ts | 1 + .../EntityLinksCard/LinksGridList.tsx | 11 ++++++-- 5 files changed, 41 insertions(+), 16 deletions(-) rename {plugins/catalog/src/components/EntityLinksCard => packages/core/src/components/IconLink}/IconLink.test.tsx (100%) rename {plugins/catalog/src/components/EntityLinksCard => packages/core/src/components/IconLink}/IconLink.tsx (74%) create mode 100644 packages/core/src/components/IconLink/index.ts diff --git a/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx b/packages/core/src/components/IconLink/IconLink.test.tsx similarity index 100% rename from plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx rename to packages/core/src/components/IconLink/IconLink.test.tsx diff --git a/plugins/catalog/src/components/EntityLinksCard/IconLink.tsx b/packages/core/src/components/IconLink/IconLink.tsx similarity index 74% rename from plugins/catalog/src/components/EntityLinksCard/IconLink.tsx rename to packages/core/src/components/IconLink/IconLink.tsx index 32ea085889..c2f0a5c5da 100644 --- a/plugins/catalog/src/components/EntityLinksCard/IconLink.tsx +++ b/packages/core/src/components/IconLink/IconLink.tsx @@ -14,10 +14,12 @@ * limitations under the License. */ -import { Link, IconComponent } from '@backstage/core'; -import { Grid, makeStyles, Typography } from '@material-ui/core'; -import LanguageIcon from '@material-ui/icons/Language'; import React from 'react'; +import { IconComponent } from '@backstage/core-api'; +import { Grid, LinkProps, makeStyles, Typography } from '@material-ui/core'; +import LanguageIcon from '@material-ui/icons/Language'; +import { omit } from 'lodash'; +import { Link } from '../Link'; const useStyles = makeStyles({ svgIcon: { @@ -30,15 +32,15 @@ const useStyles = makeStyles({ }, }); -export const IconLink = ({ - href, - text, - Icon, -}: { - href: string; - text?: string; - Icon?: IconComponent; -}) => { +export const IconLink = ( + props: { + href: string; + text?: string; + Icon?: IconComponent; + } & LinkProps, +) => { + const { href, text, Icon, ...linkProps } = props; + const classes = useStyles(); return ( @@ -49,7 +51,7 @@ export const IconLink = ({ - + {text || href} diff --git a/packages/core/src/components/IconLink/index.ts b/packages/core/src/components/IconLink/index.ts new file mode 100644 index 0000000000..b650b07279 --- /dev/null +++ b/packages/core/src/components/IconLink/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { IconLink } from './IconLink'; diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index c8797f576b..aec8cc9a64 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -26,6 +26,7 @@ export * from './ResponseErrorPanel'; export * from './FeatureDiscovery'; export * from './HeaderIconLinkRow'; export * from './HorizontalScrollGrid'; +export * from './IconLink'; export * from './Lifecycle'; export * from './Link'; export * from './MarkdownContent'; diff --git a/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx b/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx index 9ef77a4bbf..a2793f9aa4 100644 --- a/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx @@ -14,10 +14,9 @@ * limitations under the License. */ -import { IconComponent } from '@backstage/core'; +import { IconComponent, IconLink } from '@backstage/core'; import { GridList, GridListTile } from '@material-ui/core'; import React from 'react'; -import { IconLink } from './IconLink'; import { ColumnBreakpoints } from './types'; import { useDynamicColumns } from './useDynamicColumns'; @@ -39,7 +38,13 @@ export const LinksGridList = ({ items, cols = undefined }: Props) => { {items.map(({ text, href, Icon }, i) => ( - + ))} From baee14e5d624066eb0761b30c5d70ae95b035d93 Mon Sep 17 00:00:00 2001 From: James Turley Date: Tue, 16 Mar 2021 17:53:29 +0000 Subject: [PATCH 30/61] Add component for multiple links Signed-off-by: James Turley Signed-off-by: James Turley --- .../schema/kinds/Template.v1beta2.schema.json | 30 ++++++++ packages/core-api/src/icons/icons.tsx | 3 + .../src/components/TaskPage/TaskPage.tsx | 51 +++----------- .../src/components/TaskPage/TaskPageLinks.tsx | 69 +++++++++++++++++++ .../src/components/hooks/useEventStream.ts | 12 +--- plugins/scaffolder/src/types.ts | 13 ++++ 6 files changed, 126 insertions(+), 52 deletions(-) create mode 100644 plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx diff --git a/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json b/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json index bd9999f8bd..ba3ccb9d1d 100644 --- a/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json +++ b/packages/catalog-model/src/schema/kinds/Template.v1beta2.schema.json @@ -135,6 +135,36 @@ "output": { "type": "object", "description": "A templated object describing the outputs of the scaffolding task.", + "properties": { + "links": { + "type": "array", + "description": "A list of external hyperlinks, typically pointing to resources created or updated by the template", + "items": { + "type": "object", + "required": ["url"], + "properties": { + "url": { + "type": "string", + "description": "A url in a standard uri format.", + "examples": ["https://github.com/my-org/my-new-repo"], + "minLength": 1 + }, + "title": { + "type": "string", + "description": "A user friendly display name for the link.", + "examples": ["View new repo"], + "minLength": 1 + }, + "icon": { + "type": "string", + "description": "A key representing a visual icon to be displayed in the UI.", + "examples": ["dashboard"], + "minLength": 1 + } + } + } + } + }, "additionalProperties": { "type": "string" } diff --git a/packages/core-api/src/icons/icons.tsx b/packages/core-api/src/icons/icons.tsx index e638131a28..fd3d0dcdab 100644 --- a/packages/core-api/src/icons/icons.tsx +++ b/packages/core-api/src/icons/icons.tsx @@ -15,6 +15,7 @@ */ import { SvgIconProps } from '@material-ui/core'; +import MuiMenuBookIcon from '@material-ui/icons/MenuBook'; import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; import MuiChatIcon from '@material-ui/icons/Chat'; import MuiDashboardIcon from '@material-ui/icons/Dashboard'; @@ -32,6 +33,8 @@ import { IconComponent, IconComponentMap, SystemIconKey } from './types'; export const defaultSystemIcons: IconComponentMap = { brokenImage: MuiBrokenImageIcon, + // To be confirmed: see https://github.com/backstage/backstage/issues/4970 + catalog: MuiMenuBookIcon, chat: MuiChatIcon, dashboard: MuiDashboardIcon, email: MuiEmailIcon, diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index 96a857dbb5..cc1149ee61 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -22,28 +22,24 @@ import Step from '@material-ui/core/Step'; import StepLabel from '@material-ui/core/StepLabel'; import Grid from '@material-ui/core/Grid'; import Typography from '@material-ui/core/Typography'; -import { generatePath, useParams } from 'react-router'; +import { useParams } from 'react-router'; import { useTaskEventStream } from '../hooks/useEventStream'; import LazyLog from 'react-lazylog/build/LazyLog'; -import { Link } from 'react-router-dom'; import { - Box, - Button, CircularProgress, Paper, StepButton, StepIconProps, } from '@material-ui/core'; -import { Status } from '../../types'; +import { Status, TaskOutput } from '../../types'; import { DateTime, Interval } from 'luxon'; import { useInterval } from 'react-use'; import Check from '@material-ui/icons/Check'; import Cancel from '@material-ui/icons/Cancel'; import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord'; -import { entityRoute } from '@backstage/plugin-catalog-react'; -import { parseEntityName } from '@backstage/catalog-model'; import classNames from 'classnames'; import { BackstageTheme } from '@backstage/theme'; +import { TaskPageLinks } from './TaskPageLinks'; // typings are wrong for this library, so fallback to not parsing types. const humanizeDuration = require('humanize-duration'); @@ -211,6 +207,9 @@ const TaskLogger = memo(({ log }: { log: string }) => { ); }); +const hasLinks = ({ entityRef, remoteUrl, links = [] }: TaskOutput): boolean => + !!(entityRef || remoteUrl || links.length > 0); + export const TaskPage = () => { const [userSelectedStepId, setUserSelectedStepId] = useState< string | undefined @@ -261,8 +260,8 @@ export const TaskPage = () => { taskStream.loading === false && !taskStream.task; - const entityRef = taskStream.output?.entityRef; - const remoteUrl = taskStream.output?.remoteUrl; + const { output } = taskStream; + return (
{ currentStepId={currentStepId} onUserStepChange={setUserSelectedStepId} /> - {(entityRef || remoteUrl) && ( - - {entityRef && ( - - )} - {remoteUrl && ( - - )} - + {output && hasLinks(output) && ( + )} diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx new file mode 100644 index 0000000000..931482194e --- /dev/null +++ b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx @@ -0,0 +1,69 @@ +/* + * 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 React from 'react'; +import { parseEntityName } from '@backstage/catalog-model'; +import { IconComponent, IconKey, IconLink, useApp } from '@backstage/core'; +import { entityRoute } from '@backstage/plugin-catalog-react'; +import { Box } from '@material-ui/core'; +import LanguageIcon from '@material-ui/icons/Language'; +import { generatePath } from 'react-router'; +import { TaskOutput } from '../../types'; + +type TaskPageLinksProps = { + output: TaskOutput; +}; + +export const TaskPageLinks = ({ output }: TaskPageLinksProps) => { + const { entityRef, remoteUrl } = output; + let { links = [] } = output; + const app = useApp(); + + const iconResolver = (key: IconKey | undefined): IconComponent => + key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon; + + if (remoteUrl) { + links = [{ url: remoteUrl, title: 'Repo' }, ...links]; + } + + if (entityRef) { + links = [ + { + url: generatePath( + `/catalog/${entityRoute.path}`, + parseEntityName(entityRef), + ), + title: 'Open in catalog', + icon: 'catalog', + }, + ...links, + ]; + } + + return ( + + {links.map(({ url, title, icon }, i) => ( + + ))} + + ); +}; diff --git a/plugins/scaffolder/src/components/hooks/useEventStream.ts b/plugins/scaffolder/src/components/hooks/useEventStream.ts index 958c3d192e..4d28fabd34 100644 --- a/plugins/scaffolder/src/components/hooks/useEventStream.ts +++ b/plugins/scaffolder/src/components/hooks/useEventStream.ts @@ -16,14 +16,9 @@ import { useImmerReducer } from 'use-immer'; import { useEffect } from 'react'; import { scaffolderApiRef, LogEvent } from '../../api'; -import { ScaffolderTask, Status } from '../../types'; +import { ScaffolderTask, Status, TaskOutput } from '../../types'; import { Subscription, useApi } from '@backstage/core'; -type OutputLink = { - title: string; - url: string; -}; - type Step = { id: string; status: Status; @@ -31,11 +26,6 @@ type Step = { startedAt?: string; }; -type TaskOutput = { - entityRef?: string; - links?: OutputLink[]; -} & { [key in string]: string }; - export type TaskStream = { loading: boolean; error?: Error; diff --git a/plugins/scaffolder/src/types.ts b/plugins/scaffolder/src/types.ts index 6a2c2eede4..f96d103796 100644 --- a/plugins/scaffolder/src/types.ts +++ b/plugins/scaffolder/src/types.ts @@ -64,3 +64,16 @@ export type ListActionsResponse = Array<{ output?: JSONSchema; }; }>; + +type OutputLink = { + url: string; + title?: string; + icon?: string; +}; + +export type TaskOutput = { + entityRef?: string; + remoteUrl?: string; + links?: OutputLink[]; + [key: string]: any; +}; From b1ad7eae8a74aab1a0bb293c10aa248a0908fbea Mon Sep 17 00:00:00 2001 From: James Turley Date: Mon, 22 Mar 2021 15:27:13 +0000 Subject: [PATCH 31/61] Tests for TaskPageLinks Signed-off-by: James Turley Signed-off-by: James Turley --- .../core/src/components/IconLink/IconLink.tsx | 1 - .../TaskPage/TaskPageLinks.test.tsx | 76 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx diff --git a/packages/core/src/components/IconLink/IconLink.tsx b/packages/core/src/components/IconLink/IconLink.tsx index c2f0a5c5da..d7f9e5571b 100644 --- a/packages/core/src/components/IconLink/IconLink.tsx +++ b/packages/core/src/components/IconLink/IconLink.tsx @@ -18,7 +18,6 @@ import React from 'react'; import { IconComponent } from '@backstage/core-api'; import { Grid, LinkProps, makeStyles, Typography } from '@material-ui/core'; import LanguageIcon from '@material-ui/icons/Language'; -import { omit } from 'lodash'; import { Link } from '../Link'; const useStyles = makeStyles({ diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx new file mode 100644 index 0000000000..5ea170eae2 --- /dev/null +++ b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx @@ -0,0 +1,76 @@ +/* + * 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 React from 'react'; +import { TaskPageLinks } from './TaskPageLinks'; +import { renderInTestApp } from '@backstage/test-utils'; + +describe('TaskPageLinks', () => { + beforeEach(() => {}); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('renders the entityRef link', async () => { + const output = { entityRef: 'Component:default/my-app' }; + const { findByText } = await renderInTestApp( + , + ); + + const element = await findByText('Open in catalog'); + + expect(element).toBeInTheDocument(); + expect(element).toHaveAttribute( + 'href', + '/catalog/default/Component/my-app', + ); + }); + + it('renders the remoteUrl link', async () => { + const output = { remoteUrl: 'https://remote.url' }; + const { findByText } = await renderInTestApp( + , + ); + + const element = await findByText('Repo'); + + expect(element).toBeInTheDocument(); + expect(element).toHaveAttribute('href', 'https://remote.url'); + }); + + it('renders further links', async () => { + const output = { + links: [ + { url: 'https://first.url', title: 'Cool link 1' }, + { url: 'https://second.url', title: 'Cool link 2' }, + ], + }; + const { findByText } = await renderInTestApp( + , + ); + + let element = await findByText('Cool link 1'); + + expect(element).toBeInTheDocument(); + expect(element).toHaveAttribute('href', 'https://first.url'); + + element = await findByText('Cool link 2'); + + expect(element).toBeInTheDocument(); + expect(element).toHaveAttribute('href', 'https://second.url'); + }); +}); From 98dd5da71745c283687c0e9eac71fca414e11a7c Mon Sep 17 00:00:00 2001 From: James Turley Date: Mon, 22 Mar 2021 15:55:03 +0000 Subject: [PATCH 32/61] Changeset for task page links Signed-off-by: James Turley Signed-off-by: James Turley --- .changeset/sharp-rings-march.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/sharp-rings-march.md diff --git a/.changeset/sharp-rings-march.md b/.changeset/sharp-rings-march.md new file mode 100644 index 0000000000..81ea9aa9df --- /dev/null +++ b/.changeset/sharp-rings-march.md @@ -0,0 +1,8 @@ +--- +'@backstage/catalog-model': patch +'@backstage/core': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-scaffolder': patch +--- + +Add support for multiple links to post-scaffold task summary page From 9fdbc1e2166fe4d08af814a5591408accd3074eb Mon Sep 17 00:00:00 2001 From: James Turley Date: Tue, 6 Apr 2021 15:26:33 +0100 Subject: [PATCH 33/61] Deprecate remoteUrl Signed-off-by: James Turley --- plugins/scaffolder/src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/scaffolder/src/types.ts b/plugins/scaffolder/src/types.ts index f96d103796..233e1192e9 100644 --- a/plugins/scaffolder/src/types.ts +++ b/plugins/scaffolder/src/types.ts @@ -73,6 +73,7 @@ type OutputLink = { export type TaskOutput = { entityRef?: string; + /** @deprecated use the `links` property to link out to relevant resources */ remoteUrl?: string; links?: OutputLink[]; [key: string]: any; From 20ae6504c7b8099e612b243bc6f101fdef2411fa Mon Sep 17 00:00:00 2001 From: James Turley Date: Tue, 6 Apr 2021 15:37:32 +0100 Subject: [PATCH 34/61] Duplicate IconLink instead of moving to core Signed-off-by: James Turley --- .../core/src/components/IconLink/index.ts | 17 ------ packages/core/src/components/index.ts | 1 - .../EntityLinksCard}/IconLink.test.tsx | 0 .../components/EntityLinksCard/IconLink.tsx | 58 +++++++++++++++++++ .../EntityLinksCard/LinksGridList.tsx | 11 +--- .../src/components/TaskPage/IconLink.test.tsx | 38 ++++++++++++ .../src/components/TaskPage}/IconLink.tsx | 3 +- .../src/components/TaskPage/TaskPageLinks.tsx | 3 +- 8 files changed, 102 insertions(+), 29 deletions(-) delete mode 100644 packages/core/src/components/IconLink/index.ts rename {packages/core/src/components/IconLink => plugins/catalog/src/components/EntityLinksCard}/IconLink.test.tsx (100%) create mode 100644 plugins/catalog/src/components/EntityLinksCard/IconLink.tsx create mode 100644 plugins/scaffolder/src/components/TaskPage/IconLink.test.tsx rename {packages/core/src/components/IconLink => plugins/scaffolder/src/components/TaskPage}/IconLink.tsx (94%) diff --git a/packages/core/src/components/IconLink/index.ts b/packages/core/src/components/IconLink/index.ts deleted file mode 100644 index b650b07279..0000000000 --- a/packages/core/src/components/IconLink/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { IconLink } from './IconLink'; diff --git a/packages/core/src/components/index.ts b/packages/core/src/components/index.ts index aec8cc9a64..c8797f576b 100644 --- a/packages/core/src/components/index.ts +++ b/packages/core/src/components/index.ts @@ -26,7 +26,6 @@ export * from './ResponseErrorPanel'; export * from './FeatureDiscovery'; export * from './HeaderIconLinkRow'; export * from './HorizontalScrollGrid'; -export * from './IconLink'; export * from './Lifecycle'; export * from './Link'; export * from './MarkdownContent'; diff --git a/packages/core/src/components/IconLink/IconLink.test.tsx b/plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx similarity index 100% rename from packages/core/src/components/IconLink/IconLink.test.tsx rename to plugins/catalog/src/components/EntityLinksCard/IconLink.test.tsx diff --git a/plugins/catalog/src/components/EntityLinksCard/IconLink.tsx b/plugins/catalog/src/components/EntityLinksCard/IconLink.tsx new file mode 100644 index 0000000000..32ea085889 --- /dev/null +++ b/plugins/catalog/src/components/EntityLinksCard/IconLink.tsx @@ -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 { Link, IconComponent } from '@backstage/core'; +import { Grid, makeStyles, Typography } from '@material-ui/core'; +import LanguageIcon from '@material-ui/icons/Language'; +import React from 'react'; + +const useStyles = makeStyles({ + svgIcon: { + display: 'inline-block', + '& svg': { + display: 'inline-block', + fontSize: 'inherit', + verticalAlign: 'baseline', + }, + }, +}); + +export const IconLink = ({ + href, + text, + Icon, +}: { + href: string; + text?: string; + Icon?: IconComponent; +}) => { + const classes = useStyles(); + + return ( + + + + {Icon ? : } + + + + + {text || href} + + + + ); +}; diff --git a/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx b/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx index a2793f9aa4..9ef77a4bbf 100644 --- a/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/LinksGridList.tsx @@ -14,9 +14,10 @@ * limitations under the License. */ -import { IconComponent, IconLink } from '@backstage/core'; +import { IconComponent } from '@backstage/core'; import { GridList, GridListTile } from '@material-ui/core'; import React from 'react'; +import { IconLink } from './IconLink'; import { ColumnBreakpoints } from './types'; import { useDynamicColumns } from './useDynamicColumns'; @@ -38,13 +39,7 @@ export const LinksGridList = ({ items, cols = undefined }: Props) => { {items.map(({ text, href, Icon }, i) => ( - + ))} diff --git a/plugins/scaffolder/src/components/TaskPage/IconLink.test.tsx b/plugins/scaffolder/src/components/TaskPage/IconLink.test.tsx new file mode 100644 index 0000000000..8caf37e70d --- /dev/null +++ b/plugins/scaffolder/src/components/TaskPage/IconLink.test.tsx @@ -0,0 +1,38 @@ +/* + * 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 { lightTheme } from '@backstage/theme'; +import { ThemeProvider } from '@material-ui/core'; +import CloudIcon from '@material-ui/icons/Cloud'; +import { render } from '@testing-library/react'; +import React from 'react'; +import { IconLink } from './IconLink'; + +describe('IconLink', () => { + it('should render an icon link', () => { + const rendered = render( + + + , + ); + + expect(rendered.queryByText('I am Link')).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/components/IconLink/IconLink.tsx b/plugins/scaffolder/src/components/TaskPage/IconLink.tsx similarity index 94% rename from packages/core/src/components/IconLink/IconLink.tsx rename to plugins/scaffolder/src/components/TaskPage/IconLink.tsx index d7f9e5571b..9bede214c6 100644 --- a/packages/core/src/components/IconLink/IconLink.tsx +++ b/plugins/scaffolder/src/components/TaskPage/IconLink.tsx @@ -15,10 +15,9 @@ */ import React from 'react'; -import { IconComponent } from '@backstage/core-api'; +import { IconComponent, Link } from '@backstage/core'; import { Grid, LinkProps, makeStyles, Typography } from '@material-ui/core'; import LanguageIcon from '@material-ui/icons/Language'; -import { Link } from '../Link'; const useStyles = makeStyles({ svgIcon: { diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx index 931482194e..4eed65e5bb 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx @@ -16,7 +16,8 @@ import React from 'react'; import { parseEntityName } from '@backstage/catalog-model'; -import { IconComponent, IconKey, IconLink, useApp } from '@backstage/core'; +import { IconComponent, IconKey, useApp } from '@backstage/core'; +import { IconLink } from './IconLink'; import { entityRoute } from '@backstage/plugin-catalog-react'; import { Box } from '@material-ui/core'; import LanguageIcon from '@material-ui/icons/Language'; From 04eae5a8db2489cec696a5def769ace8f577cf2a Mon Sep 17 00:00:00 2001 From: James Turley Date: Tue, 6 Apr 2021 15:44:05 +0100 Subject: [PATCH 35/61] Use route ref hook for entity page link Signed-off-by: James Turley --- .../components/TaskPage/TaskPageLinks.test.tsx | 16 ++++++++++++++++ .../src/components/TaskPage/TaskPageLinks.tsx | 13 ++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx index 5ea170eae2..cd51488242 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.test.tsx @@ -17,6 +17,7 @@ import React from 'react'; import { TaskPageLinks } from './TaskPageLinks'; import { renderInTestApp } from '@backstage/test-utils'; +import { entityRouteRef } from '@backstage/plugin-catalog-react'; describe('TaskPageLinks', () => { beforeEach(() => {}); @@ -29,6 +30,11 @@ describe('TaskPageLinks', () => { const output = { entityRef: 'Component:default/my-app' }; const { findByText } = await renderInTestApp( , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, + }, + }, ); const element = await findByText('Open in catalog'); @@ -44,6 +50,11 @@ describe('TaskPageLinks', () => { const output = { remoteUrl: 'https://remote.url' }; const { findByText } = await renderInTestApp( , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, + }, + }, ); const element = await findByText('Repo'); @@ -61,6 +72,11 @@ describe('TaskPageLinks', () => { }; const { findByText } = await renderInTestApp( , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, + }, + }, ); let element = await findByText('Cool link 1'); diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx index 4eed65e5bb..c0b55193c2 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPageLinks.tsx @@ -16,12 +16,11 @@ import React from 'react'; import { parseEntityName } from '@backstage/catalog-model'; -import { IconComponent, IconKey, useApp } from '@backstage/core'; +import { IconComponent, IconKey, useApp, useRouteRef } from '@backstage/core'; import { IconLink } from './IconLink'; -import { entityRoute } from '@backstage/plugin-catalog-react'; +import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { Box } from '@material-ui/core'; import LanguageIcon from '@material-ui/icons/Language'; -import { generatePath } from 'react-router'; import { TaskOutput } from '../../types'; type TaskPageLinksProps = { @@ -32,6 +31,7 @@ export const TaskPageLinks = ({ output }: TaskPageLinksProps) => { const { entityRef, remoteUrl } = output; let { links = [] } = output; const app = useApp(); + const entityRoute = useRouteRef(entityRouteRef); const iconResolver = (key: IconKey | undefined): IconComponent => key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon; @@ -41,12 +41,11 @@ export const TaskPageLinks = ({ output }: TaskPageLinksProps) => { } if (entityRef) { + const entityName = parseEntityName(entityRef); + const target = entityRoute(entityName); links = [ { - url: generatePath( - `/catalog/${entityRoute.path}`, - parseEntityName(entityRef), - ), + url: target, title: 'Open in catalog', icon: 'catalog', }, From 26526f1962b7db7b933220b578abca072e162073 Mon Sep 17 00:00:00 2001 From: James Turley Date: Tue, 6 Apr 2021 15:46:42 +0100 Subject: [PATCH 36/61] Clean up TaskOutput typedef Signed-off-by: James Turley --- plugins/scaffolder/src/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/types.ts b/plugins/scaffolder/src/types.ts index 233e1192e9..9ba480a441 100644 --- a/plugins/scaffolder/src/types.ts +++ b/plugins/scaffolder/src/types.ts @@ -76,5 +76,6 @@ export type TaskOutput = { /** @deprecated use the `links` property to link out to relevant resources */ remoteUrl?: string; links?: OutputLink[]; - [key: string]: any; +} & { + [key: string]: unknown; }; From 21f80f2cfd59b42fbabeeac6b48776348f8af89a Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Tue, 6 Apr 2021 18:26:49 +0200 Subject: [PATCH 37/61] Inject a router if needed Signed-off-by: Oliver Sand --- .../core/src/api-wrappers/createApp.test.tsx | 25 ++++++++- packages/core/src/api-wrappers/createApp.tsx | 51 +++++++++---------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/packages/core/src/api-wrappers/createApp.test.tsx b/packages/core/src/api-wrappers/createApp.test.tsx index 31f584b68c..7eaec32b86 100644 --- a/packages/core/src/api-wrappers/createApp.test.tsx +++ b/packages/core/src/api-wrappers/createApp.test.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ -import { defaultConfigLoader } from './createApp'; +import { render } from '@testing-library/react'; +import React from 'react'; +import { MemoryRouter } from 'react-router'; +import { defaultConfigLoader, OptionallyWrapInRouter } from './createApp'; (process as any).env = { NODE_ENV: 'test' }; const anyEnv = process.env as any; @@ -92,3 +95,23 @@ describe('defaultConfigLoader', () => { ]); }); }); + +describe('OptionallyWrapInRouter', () => { + it('should wrap with router if not yet inside a router', async () => { + const { getByText } = render( + Test, + ); + + expect(getByText('Test')).toBeInTheDocument(); + }); + + it('should not wrap with router if already inside a router', async () => { + const { getByText } = render( + + Test + , + ); + + expect(getByText('Test')).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/api-wrappers/createApp.tsx b/packages/core/src/api-wrappers/createApp.tsx index b071c2d014..e79cdf509f 100644 --- a/packages/core/src/api-wrappers/createApp.tsx +++ b/packages/core/src/api-wrappers/createApp.tsx @@ -14,14 +14,18 @@ * limitations under the License. */ -import React from 'react'; +import React, { PropsWithChildren } from 'react'; import privateExports, { AppOptions, defaultSystemIcons, BootErrorPageProps, AppConfigLoader, } from '@backstage/core-api'; -import { BrowserRouter, MemoryRouter } from 'react-router-dom'; +import { + BrowserRouter, + MemoryRouter, + useInRouterContext, +} from 'react-router-dom'; import LightIcon from '@material-ui/icons/WbSunny'; import DarkIcon from '@material-ui/icons/Brightness2'; import { ErrorPage } from '../layout/ErrorPage'; @@ -89,6 +93,13 @@ export const defaultConfigLoader: AppConfigLoader = async ( // The actual implementation of the app class still lives in core-api, // as it needs to be used by dev- and test-utils. +export function OptionallyWrapInRouter({ children }: PropsWithChildren<{}>) { + if (useInRouterContext()) { + return <>{children}; + } + return {children}; +} + /** * Creates a new Backstage App. */ @@ -97,32 +108,18 @@ export function createApp(options?: AppOptions) { ); const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => { - switch (step) { - case 'load-config': - // TODO: figure out a nicer way to handle routing on the error page, when it can be done. - return ( - - - - ); - case 'load-chunk': - return ( - - ); - default: - // TODO: figure out a nicer way to handle routing on the error page, when it can be done. - return ( - - - - ); + let message = ''; + if (step === 'load-config') { + message = `The configuration failed to load, someone should have a look at this error: ${error.message}`; + } else if (step === 'load-chunk') { + message = `Lazy loaded chunk failed to load, try to reload the page: ${error.message}`; } + // TODO: figure out a nicer way to handle routing on the error page, when it can be done. + return ( + + + + ); }; const apis = options?.apis ?? []; From e3190971e7966d233cec04bfa1402ff8210490c4 Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Tue, 6 Apr 2021 11:49:08 -0600 Subject: [PATCH 38/61] Update auth index to be more "getting started" focused Signed-off-by: Tim Hansen --- docs/auth/auth0/provider.md | 28 +--- docs/auth/github/provider.md | 28 +--- docs/auth/gitlab/provider.md | 28 +--- docs/auth/google/provider.md | 28 +--- docs/auth/index.md | 172 +++++++++++--------- docs/auth/microsoft/provider.md | 28 +--- docs/auth/okta/provider.md | 28 +--- docs/auth/using-auth.md | 96 +++++++++++ docs/integrations/azure/locations.md | 4 +- docs/integrations/bitbucket/discovery.md | 4 +- docs/integrations/bitbucket/locations.md | 4 +- docs/integrations/github/discovery.md | 5 +- docs/integrations/index.md | 5 +- docs/tutorials/switching-sqlite-postgres.md | 4 +- microsite/sidebars.json | 7 +- 15 files changed, 221 insertions(+), 248 deletions(-) create mode 100644 docs/auth/using-auth.md diff --git a/docs/auth/auth0/provider.md b/docs/auth/auth0/provider.md index 383cda2014..b546285c94 100644 --- a/docs/auth/auth0/provider.md +++ b/docs/auth/auth0/provider.md @@ -46,29 +46,5 @@ The Auth0 provider is a structure with three configuration keys: ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `auth0AuthApi` reference and -`SignInPage` component to `createApp` in `packages/app/src/App.tsx`: - -```diff -+ import { auth0AuthApiRef, SignInConfig, SignInPage } from '@backstage/core'; - -+ const auth0Provider: SignInConfig = { -+ id: 'auth0-auth-provider', -+ title: 'Auth0', -+ message: 'Sign in using Auth0', -+ apiRef: auth0AuthApiRef, -+}; -+ -const app = createApp({ - apis, - plugins: Object.values(plugins), -+ components: { -+ SignInPage: props => ( -+ -+ ), -+ }, - bindRoutes({ bind }) { -``` +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/docs/auth/github/provider.md b/docs/auth/github/provider.md index 6e194f36f7..4b65c8c3c2 100644 --- a/docs/auth/github/provider.md +++ b/docs/auth/github/provider.md @@ -49,29 +49,5 @@ The GitHub provider is a structure with three configuration keys: ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `githubAuthApi` reference and -`SignInPage` component to `createApp` in `packages/app/src/App.tsx`: - -```diff -+ import { githubAuthApiRef, SignInConfig, SignInPage } from '@backstage/core'; - -+ const githubProvider: SignInConfig = { -+ id: 'github-auth-provider', -+ title: 'GitHub', -+ message: 'Sign in using GitHub', -+ apiRef: githubAuthApiRef, -+}; -+ -const app = createApp({ - apis, - plugins: Object.values(plugins), -+ components: { -+ SignInPage: props => ( -+ -+ ), -+ }, - bindRoutes({ bind }) { -``` +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/docs/auth/gitlab/provider.md b/docs/auth/gitlab/provider.md index e9cfdfa98e..8e9c499dc4 100644 --- a/docs/auth/gitlab/provider.md +++ b/docs/auth/gitlab/provider.md @@ -48,29 +48,5 @@ The GitLab provider is a structure with three configuration keys: ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `gitlabAuthApi` reference and -`SignInPage` component to `createApp` in `packages/app/src/App.tsx`: - -```diff -+ import { gitlabAuthApiRef, SignInConfig, SignInPage } from '@backstage/core'; - -+ const gitlabProvider: SignInConfig = { -+ id: 'gitlab-auth-provider', -+ title: 'GitLab', -+ message: 'Sign in using GitLab', -+ apiRef: gitlabAuthApiRef, -+}; -+ -const app = createApp({ - apis, - plugins: Object.values(plugins), -+ components: { -+ SignInPage: props => ( -+ -+ ), -+ }, - bindRoutes({ bind }) { -``` +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/docs/auth/google/provider.md b/docs/auth/google/provider.md index 2765b509f3..457224b2db 100644 --- a/docs/auth/google/provider.md +++ b/docs/auth/google/provider.md @@ -53,29 +53,5 @@ The Google provider is a structure with two configuration keys: ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `googleAuthApi` reference and -`SignInPage` component to `createApp` in `packages/app/src/App.tsx`: - -```diff -+ import { googleAuthApiRef, SignInConfig, SignInPage } from '@backstage/core'; - -+ const googleProvider: SignInConfig = { -+ id: 'google-auth-provider', -+ title: 'Google', -+ message: 'Sign in using Google', -+ apiRef: googleAuthApiRef, -+}; -+ -const app = createApp({ - apis, - plugins: Object.values(plugins), -+ components: { -+ SignInPage: props => ( -+ -+ ), -+ }, - bindRoutes({ bind }) { -``` +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/docs/auth/index.md b/docs/auth/index.md index a8ddf17c6b..0a6bb2c899 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -1,99 +1,121 @@ --- id: index -title: User Authentication and Authorization in Backstage -description: Documentation on User Authentication and Authorization in Backstage +title: Adding Authentication +description: How to add authentication to a Backstage application --- -## Summary +Authentication in Backstage identifies the user, and provides a way for plugins +to make requests on behalf of a user to third-party services. Backstage can have +zero (guest access), one, or many authentication providers. The default +`@backstage/create-app` template uses guest access for easy startup. -The purpose of the Auth APIs in Backstage is to identify the user, and to -provide a way for plugins to request access to 3rd party services on behalf of -the user (OAuth). This documentation focuses on the implementation of that -solution and how to extend it. For documentation on how to consume the Auth APIs -in a plugin, see [TODO](#TODO). +See [Using authentication and identity](using-auth.md) for tips on using +Backstage identity information in your app or plugins. -### Accessing Third Party Services +## Adding an authentication provider -The main pattern for talking to third party services in Backstage is -user-to-server requests, where short-lived OAuth Access Tokens are requested by -plugins to authenticate calls to external services. These calls can be made -either directly to the services or through a backend plugin or service. +Backstage comes with many common authentication providers in the core library: -By relying on user-to-server calls we keep the coupling between the frontend and -backend low, and provide a much lower barrier for plugins to make use of third -party services. This is in comparison to for example a session-based system, -where access tokens are stored server-side. Such a solution would require a much -deeper coupling between the auth backend plugin, its session storage, and other -backend plugins or separate services. A goal of Backstage is to make it as easy -as possible to create new plugins, and an auth solution based on user-to-server -OAuth helps in that regard. +- [Auth0](auth0/provider.md) +- [Azure](microsoft/provider.md) +- [GitHub](github/provider.md) +- [GitLab](gitlab/provider.md) +- [Google](google/provider.md) +- [Okta](okta/provider.md) +- OneLogin -The method with which frontend plugins request access to third party services is -through [Utility APIs](../api/utility-apis.md) for each service provider. For a -full list of providers, see the -[Utility API References](../reference/utility-apis/README.md). +These built-in providers handle the authentication flow for a particular service +including required scopes, callbacks, etc. These providers are each added to a +Backstage app in a similar way. -### Identity - WIP +### Adding provider configuration -> NOTE: Identity management and the `SignInPage` in Backstage is NOT a method -> for blocking access for unauthorized users, that either requires additional -> backend implementation or a separate service like Google's Identity-Aware -> Proxy. The identity system only serves to provide a personalized experience -> and access to a Backstage Identity Token, which can be passed to backend -> plugins. +Each built-in provider has a configuration block under the `auth` section of +`app-config.yaml`. For example, the GitHub provider: -Identity management is still work in progress, but there are already a couple of -pieces in place that can be used. +```yaml +auth: + environment: development + providers: + github: + development: + clientId: ${AUTH_GITHUB_CLIENT_ID} + clientSecret: ${AUTH_GITHUB_CLIENT_SECRET} +``` -#### Identity for Plugin Developers +See the documentation for a particular provider to see what configuration is +needed. -As a plugin developer, there are two main touchpoints for identities: the -`IdentityApi` exported by `@backstage/core` via the `identityApiRef`, and a not -yet existing middleware exported by `@backstage/backend-common`. +The `providers` key may have several authentication providers, if multiple +authentication methods are supported. Each provider may also have configuration +for different authentication environments (development, production, etc). This +allows a single auth backend to serve multiple environments, such as running a +local frontend against a deployed backend. The provider configuration matching +the local `auth.environment` setting will be selected. -The `IdentityApi` gives access to the signed-in user's identity in the frontend. -It provides access to the user's ID, lightweight profile information, and an ID -token used to make authenticated calls within Backstage. +### Adding the provider to the sign-in page -The middleware that will be provided by `@backstage/backend-common` allows -verification of Backstage ID tokens, and optionally loading additional -information about the user. The progress is tracked in -https://github.com/backstage/backstage/issues/1435. +After configuring an authentication provider, the `app` frontend package needs a +small update to show this provider as a login option. The `SignInPage` component +handles this, and takes either a `provider` or `providers` (array) prop of +`SignInConfig` definitions. -#### Identity for App Developers +These reference the [ApiRef](../reference/utility-apis/README.md) exported by +the provider. Again, an example using GitHub that can be adapted to any of the +built-in providers: -If you're setting up your own Backstage app, or want to add a new identity -provider, there are three touchpoints: the frontend auth APIs in -`@backstage/core-api`, the backend auth providers in `auth-backend`, and the -`SignInPage` component configured in the Backstage app via `createApp`. +```diff +# packages/app/src/App.tsx ++ import { githubAuthApiRef, SignInConfig, SignInPage } from '@backstage/core'; -The frontend APIs and backend providers are tightly coupled together for each -auth provider, and together they implement an e2e auth flow. Only some auth -providers also act as identity providers though. For example, at the moment of -writing, the Google Auth provider is able to act as a Backstage identity -provider, but the GitHub one can not. For an auth provider to also act as an -identity provider, it needs to implement the `BackstageIdentityApi` in the -frontend, and in the backend it needs to return a `BackstageIdentity` structure. ++ const githubProvider: SignInConfig = { ++ id: 'github-auth-provider', ++ title: 'GitHub', ++ message: 'Sign in using GitHub', ++ apiRef: githubAuthApiRef, ++}; ++ +const app = createApp({ + apis, + plugins: Object.values(plugins), ++ components: { ++ SignInPage: props => ( ++ ++ ), ++ }, + bindRoutes({ bind }) { +``` -It is up to each provider to implement the mapping between a provider identity -and the corresponding Backstage identity. That is currently still work in -progress, and as a stop-gap for example the Google provider returns the local -part of the user's email as the user ID. +To also allow unauthenticated guest access, use the `providers` prop for +`SignInPage`: -The final piece of the puzzle is the `SignInPage` component that can be -configured as part of the app. Without a sign-in page, Backstage will fall back -to a `guest` identity for all users, without any ID token. To enable sign-in, a -`SignInPage` needs to be configured, which in turn has to supply a user to the -app. The `@backstage/core` package provides a basic sign-in page that allows -both the user and the app developer to choose between a couple of different -sign-in methods, or to designate a single provider that may also be logged in to -automatically. +```diff +const app = createApp({ + apis, + plugins: Object.values(plugins), ++ components: { ++ SignInPage: props => ( ++ ++ ), ++ }, + bindRoutes({ bind }) { +``` -## Further Reading +## Adding a custom authentication provider -More details are provided in dedicated sections of the documentation. +There are generic authentication providers for OAuth2 and SAML. These can reduce +the amount of code needed to implement a custom authentication provider that +adheres to these standards. -- [OAuth](./oauth.md): Description of the generic OAuth flow implemented by the - [auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend). -- [Glossary](./glossary.md): Glossary of some common terms related to the auth - flows. +Backstage uses [Passport](http://www.passportjs.org/) under the hood, which has +a wide library of authentication strategies for different providers. See +[Add authentication provider](add-auth-provider.md) for details on adding a new +Passport-supported authentication method. diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index 3aa9de0df1..b64f9b1902 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -49,29 +49,5 @@ The Microsoft provider is a structure with three configuration keys: ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `microsoftAuthApi` reference and -`SignInPage` component to `createApp` in `packages/app/src/App.tsx`: - -```diff -+ import { microsoftAuthApiRef, SignInConfig, SignInPage } from '@backstage/core'; - -+ const microsoftProvider: SignInConfig = { -+ id: 'microsoft-auth-provider', -+ title: 'Microsoft Azure', -+ message: 'Sign in using Azure', -+ apiRef: microsoftAuthApiRef, -+}; -+ -const app = createApp({ - apis, - plugins: Object.values(plugins), -+ components: { -+ SignInPage: props => ( -+ -+ ), -+ }, - bindRoutes({ bind }) { -``` +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/docs/auth/okta/provider.md b/docs/auth/okta/provider.md index 091c5fffb6..da54a30add 100644 --- a/docs/auth/okta/provider.md +++ b/docs/auth/okta/provider.md @@ -62,29 +62,5 @@ The values referenced are found on the Application page on your Okta site. ## Adding the provider to the Backstage frontend To add the provider to the frontend, add the `oktaAuthApi` reference and -`SignInPage` component to `createApp` in `packages/app/src/App.tsx`: - -```diff -+ import { oktaAuthApiRef, SignInConfig, SignInPage } from '@backstage/core'; - -+ const oktaProvider: SignInConfig = { -+ id: 'okta-auth-provider', -+ title: 'Okta', -+ message: 'Sign in using Okta', -+ apiRef: oktaAuthApiRef, -+}; -+ -const app = createApp({ - apis, - plugins: Object.values(plugins), -+ components: { -+ SignInPage: props => ( -+ -+ ), -+ }, - bindRoutes({ bind }) { -``` +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/docs/auth/using-auth.md b/docs/auth/using-auth.md new file mode 100644 index 0000000000..fbc6017272 --- /dev/null +++ b/docs/auth/using-auth.md @@ -0,0 +1,96 @@ +--- +id: using-auth +title: Using authentication and identity +description: How to use authentication and identity in Backstage +--- + +The Auth APIs in Backstage identify the user, and provide a way for plugins to +request access to 3rd party services on behalf of the user (OAuth). This +documentation focuses on the implementation of that solution and how to extend +it. + +### Accessing Third Party Services + +The main pattern for talking to third party services in Backstage is +user-to-server requests, where short-lived OAuth Access Tokens are requested by +plugins to authenticate calls to external services. These calls can be made +either directly to the services or through a backend plugin or service. + +By relying on user-to-server calls we keep the coupling between the frontend and +backend low, and provide a much lower barrier for plugins to make use of third +party services. This is in comparison to for example a session-based system, +where access tokens are stored server-side. Such a solution would require a much +deeper coupling between the auth backend plugin, its session storage, and other +backend plugins or separate services. A goal of Backstage is to make it as easy +as possible to create new plugins, and an auth solution based on user-to-server +OAuth helps in that regard. + +The method with which frontend plugins request access to third party services is +through [Utility APIs](../api/utility-apis.md) for each service provider. For a +full list of providers, see the +[Utility API References](../reference/utility-apis/README.md). + +### Identity - WIP + +> NOTE: Identity management and the `SignInPage` in Backstage is NOT a method +> for blocking access for unauthorized users, that either requires additional +> backend implementation or a separate service like Google's Identity-Aware +> Proxy. The identity system only serves to provide a personalized experience +> and access to a Backstage Identity Token, which can be passed to backend +> plugins. + +Identity management is still work in progress, but there are already a couple of +pieces in place that can be used. + +#### Identity for Plugin Developers + +As a plugin developer, there are two main touchpoints for identities: the +`IdentityApi` exported by `@backstage/core` via the `identityApiRef`, and a not +yet existing middleware exported by `@backstage/backend-common`. + +The `IdentityApi` gives access to the signed-in user's identity in the frontend. +It provides access to the user's ID, lightweight profile information, and an ID +token used to make authenticated calls within Backstage. + +The middleware that will be provided by `@backstage/backend-common` allows +verification of Backstage ID tokens, and optionally loading additional +information about the user. The progress is tracked in +https://github.com/backstage/backstage/issues/1435. + +#### Identity for App Developers + +If you're setting up your own Backstage app, or want to add a new identity +provider, there are three touchpoints: the frontend auth APIs in +`@backstage/core-api`, the backend auth providers in `auth-backend`, and the +`SignInPage` component configured in the Backstage app via `createApp`. + +The frontend APIs and backend providers are tightly coupled together for each +auth provider, and together they implement an e2e auth flow. Only some auth +providers also act as identity providers though. For example, at the moment of +writing, the Google Auth provider is able to act as a Backstage identity +provider, but the GitHub one can not. For an auth provider to also act as an +identity provider, it needs to implement the `BackstageIdentityApi` in the +frontend, and in the backend it needs to return a `BackstageIdentity` structure. + +It is up to each provider to implement the mapping between a provider identity +and the corresponding Backstage identity. That is currently still work in +progress, and as a stop-gap for example the Google provider returns the local +part of the user's email as the user ID. + +The final piece of the puzzle is the `SignInPage` component that can be +configured as part of the app. Without a sign-in page, Backstage will fall back +to a `guest` identity for all users, without any ID token. To enable sign-in, a +`SignInPage` needs to be configured, which in turn has to supply a user to the +app. The `@backstage/core` package provides a basic sign-in page that allows +both the user and the app developer to choose between a couple of different +sign-in methods, or to designate a single provider that may also be logged in to +automatically. + +## Further Reading + +More details are provided in dedicated sections of the documentation. + +- [OAuth](./oauth.md): Description of the generic OAuth flow implemented by the + [auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend). +- [Glossary](./glossary.md): Glossary of some common terms related to the auth + flows. diff --git a/docs/integrations/azure/locations.md b/docs/integrations/azure/locations.md index 4ce19bad4e..163674f00a 100644 --- a/docs/integrations/azure/locations.md +++ b/docs/integrations/azure/locations.md @@ -2,8 +2,8 @@ id: locations title: Azure DevOps Locations sidebar_label: Locations -description: - Integrating source code stored in Azure DevOps into the Backstage catalog +# prettier-ignore +description: Integrating source code stored in Azure DevOps into the Backstage catalog --- The Azure integration supports loading catalog entities from Azure DevOps. diff --git a/docs/integrations/bitbucket/discovery.md b/docs/integrations/bitbucket/discovery.md index b31071be7f..b24734abde 100644 --- a/docs/integrations/bitbucket/discovery.md +++ b/docs/integrations/bitbucket/discovery.md @@ -2,8 +2,8 @@ id: discovery title: Bitbucket Discovery sidebar_label: Discovery -description: - Automatically discovering catalog entities from repositories in Bitbucket +# prettier-ignore +description: Automatically discovering catalog entities from repositories in Bitbucket --- The Bitbucket integration has a special discovery processor for discovering diff --git a/docs/integrations/bitbucket/locations.md b/docs/integrations/bitbucket/locations.md index 588d8b3bcc..992a240290 100644 --- a/docs/integrations/bitbucket/locations.md +++ b/docs/integrations/bitbucket/locations.md @@ -2,8 +2,8 @@ id: locations title: Bitbucket Locations sidebar_label: Locations -description: - Integrating source code stored in Bitbucket into the Backstage catalog +# prettier-ignore +description: Integrating source code stored in Bitbucket into the Backstage catalog --- The Bitbucket integration supports loading catalog entities from bitbucket.org diff --git a/docs/integrations/github/discovery.md b/docs/integrations/github/discovery.md index fe52feab6e..a578d0893d 100644 --- a/docs/integrations/github/discovery.md +++ b/docs/integrations/github/discovery.md @@ -2,9 +2,8 @@ id: discovery title: GitHub Discovery sidebar_label: Discovery -description: - Automatically discovering catalog entities from repositories in a GitHub - organization +# prettier-ignore +description: Automatically discovering catalog entities from repositories in a GitHub organization --- The GitHub integration has a special discovery processor for discovering catalog diff --git a/docs/integrations/index.md b/docs/integrations/index.md index 6c12aa8666..94f18daa09 100644 --- a/docs/integrations/index.md +++ b/docs/integrations/index.md @@ -2,9 +2,8 @@ id: index title: Integrations sidebar_label: Overview -description: - Configuring Backstage to read or publish data with external providers using - integrations +# prettier-ignore +description: Configuring Backstage to read or publish data with external providers using integrations --- Integrations allow Backstage to read or publish data using external providers diff --git a/docs/tutorials/switching-sqlite-postgres.md b/docs/tutorials/switching-sqlite-postgres.md index dfb82ddc51..72e5589baf 100644 --- a/docs/tutorials/switching-sqlite-postgres.md +++ b/docs/tutorials/switching-sqlite-postgres.md @@ -1,8 +1,8 @@ --- id: switching-sqlite-postgres title: Switching Backstage from SQLite to PostgreSQL -description: - How to get ready for deploying Backstage to production with PostgreSQL +# prettier-ignore +description: How to get ready for deploying Backstage to production with PostgreSQL --- The default `@backstage/create-app` database is SQLite, an in-memory database diff --git a/microsite/sidebars.json b/microsite/sidebars.json index fd21726271..0a438b7653 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -186,7 +186,6 @@ ], "Auth and identity": [ "auth/index", - "auth/add-auth-provider", { "type": "subcategory", "label": "Included providers", @@ -199,10 +198,12 @@ "auth/okta/provider" ] }, + "auth/add-auth-provider", + "auth/using-auth", "auth/auth-backend", "auth/oauth", - "auth/glossary", - "auth/auth-backend-classes" + "auth/auth-backend-classes", + "auth/glossary" ], "Designing for Backstage": [ "dls/design", From 0a0fa4ddedc2fcd0470c5903fbdbae712491aa7f Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Tue, 6 Apr 2021 12:05:47 -0600 Subject: [PATCH 39/61] No auto for you Signed-off-by: Tim Hansen --- docs/auth/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/auth/index.md b/docs/auth/index.md index 0a6bb2c899..6e3747dd25 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -101,7 +101,6 @@ const app = createApp({ + SignInPage: props => ( + + ), From 6db797a67c3f873546ecb4e82da2fdab6c2965c6 Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Tue, 6 Apr 2021 20:38:12 -0600 Subject: [PATCH 40/61] Add OneLogin auth documentation Signed-off-by: Tim Hansen --- docs/auth/index.md | 2 +- docs/auth/onelogin/provider.md | 54 ++++++++++++++++++++++++++++++++++ microsite/sidebars.json | 3 +- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 docs/auth/onelogin/provider.md diff --git a/docs/auth/index.md b/docs/auth/index.md index 6e3747dd25..fc6ce33e2b 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -22,7 +22,7 @@ Backstage comes with many common authentication providers in the core library: - [GitLab](gitlab/provider.md) - [Google](google/provider.md) - [Okta](okta/provider.md) -- OneLogin +- [OneLogin](onelogin/provider.md) These built-in providers handle the authentication flow for a particular service including required scopes, callbacks, etc. These providers are each added to a diff --git a/docs/auth/onelogin/provider.md b/docs/auth/onelogin/provider.md new file mode 100644 index 0000000000..62c1657fea --- /dev/null +++ b/docs/auth/onelogin/provider.md @@ -0,0 +1,54 @@ +--- +id: provider +title: OneLogin Authentication Provider +sidebar_label: OneLogin +description: Adding OneLogin OIDC as an authentication provider in Backstage +--- + +The Backstage `core-api` package comes with a OneLogin authentication provider +that can authenticate users using OpenID Connect. + +## Create an Application on OneLogin + +To support OneLogin authentication, you must create an Application: + +1. From the OneLogin Admin portal, choose Applications +2. Click `Add App` and select `OpenID Connect` + - Display Name: Backstage (or your custom app name) +3. Click Save +4. Go to the Configuration tab for the Application and set: + - `Login Url`: `http://localhost:3000` + - `Redirect URIs`: `http://localhost:7000/api/auth/onelogin/handler/frame` +5. Click Save +6. Go to the SSO tab for the Application and set: + - `Token Endpoint` > `Authentication Method`: `POST` +7. Click Save + +## Configuration + +The provider configuration can then be added to your `app-config.yaml` under the +root `auth` configuration: + +```yaml +auth: + environment: development + providers: + onelogin: + development: + clientId: ${AUTH_ONELOGIN_CLIENT_ID} + clientSecret: ${AUTH_ONELOGIN_CLIENT_SECRET} + issuer: https://.onelogin.com/oidc/2 +``` + +The OneLogin provider is a structure with three configuration keys; **these are +found on the SSO tab** for the OneLogin Application: + +- `clientId`: The client ID +- `clientSecret`: The client secret +- `issuer`: The issuer URL + +## Adding the provider to the Backstage frontend + +To add the provider to the frontend, add the `oneloginAuthApi` reference and +`SignInPage` component as shown in +[Adding the provider to the sign-in page](../index.md#adding-the-provider-to-the-sign-in-page). diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 0a438b7653..3eb887d357 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -195,7 +195,8 @@ "auth/github/provider", "auth/gitlab/provider", "auth/google/provider", - "auth/okta/provider" + "auth/okta/provider", + "auth/onelogin/provider" ] }, "auth/add-auth-provider", From eafb4f9f34a75c2bb43b8fa706e8501eb86f5879 Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Fri, 26 Feb 2021 18:01:06 +0100 Subject: [PATCH 41/61] Add GcsUrlReader Signed-off-by: Martina Iglesias Fernandez --- app-config.yaml | 7 ++ packages/backend-common/package.json | 1 + .../src/reading/GcsUrlReader.ts | 112 ++++++++++++++++++ .../backend-common/src/reading/UrlReaders.ts | 2 + yarn.lock | 60 ++++++++++ 5 files changed, 182 insertions(+) create mode 100644 packages/backend-common/src/reading/GcsUrlReader.ts diff --git a/app-config.yaml b/app-config.yaml index 465c7b0049..4d12e3e474 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -122,6 +122,13 @@ kafka: - localhost:9092 integrations: + gcs: + - host: 'storage.cloud.google.com' + clientEmail: + $env: GCS_CLIENT_EMAIL + privateKey: + $env: GCS_PRIVATE_KEY + github: - host: github.com token: diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index b860336d31..a720f9e3ab 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -34,6 +34,7 @@ "@backstage/config-loader": "^0.5.1", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.1", + "@google-cloud/storage": "^5.8.0", "@octokit/rest": "^18.0.12", "@types/cors": "^2.8.6", "@types/dockerode": "^3.2.1", diff --git a/packages/backend-common/src/reading/GcsUrlReader.ts b/packages/backend-common/src/reading/GcsUrlReader.ts new file mode 100644 index 0000000000..23c8f45fe7 --- /dev/null +++ b/packages/backend-common/src/reading/GcsUrlReader.ts @@ -0,0 +1,112 @@ +/* + * 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 { Storage } from '@google-cloud/storage'; +import { + ReaderFactory, + ReadTreeResponse, + SearchResponse, + UrlReader, +} from './types'; +import getRawBody from 'raw-body'; +import { Logger } from 'winston'; + +const parseURL = ( + url: string, +): { host: string; bucket: string; key: string } => { + const { host, pathname } = new URL(url); + + if (host !== 'storage.cloud.google.com') { + throw new Error(`not a valid GCS URL: ${url}`); + } + + const [, bucket, ...key] = pathname.split('/'); + return { + host: host, + bucket, + key: key.join('/'), + }; +}; + +export class GcsUrlReader implements UrlReader { + static factory: ReaderFactory = ({ config, logger }) => { + if (!config.has('integrations.gcs')) { + return []; + } + return config + .getConfigArray('integrations.gcs') + .filter(integration => { + if (!integration.has('clientEmail') || !integration.has('privateKey')) { + logger.warn( + "Skipping gcs integration, Missing required config value at 'integration.gcs.clientEmail' or 'integration.gcs.privateKey'", + ); + return false; + } + return true; + }) + .map(integration => { + const privKey = integration + .getOptionalString('privateKey') + ?.split('\\n') + .join('\n'); + + const storage = new Storage({ + credentials: { + client_email: integration.getOptionalString('clientEmail'), + private_key: privKey, + }, + }); + const reader = new GcsUrlReader(storage, logger); + const host = + integration.getOptionalString('host') || 'storage.cloud.google.com'; + + logger.info('Configuring integration, gcs'); + const predicate = (url: URL) => url.host === host; + return { reader, predicate }; + }); + }; + + constructor( + private readonly storage: Storage, + private readonly logger: Logger, + ) {} + + async read(url: string): Promise { + try { + const { bucket, key } = parseURL(url); + + this.logger.info('Reading GCS Location'); + return await getRawBody( + this.storage.bucket(bucket).file(key).createReadStream(), + ); + } catch (error) { + this.logger.warn(error.stack); + throw new Error(`unable to read gcs file from ${url}`); + } + } + + async readTree(): Promise { + throw new Error('GcsUrlReader does not implement readTree'); + } + + async search(): Promise { + throw new Error('GcsUrlReader does not implement readTree'); + } + + toString() { + return `gcs{host=storage.cloud.google.com,authed=true}}`; + } +} diff --git a/packages/backend-common/src/reading/UrlReaders.ts b/packages/backend-common/src/reading/UrlReaders.ts index 2f233463bb..785325faa7 100644 --- a/packages/backend-common/src/reading/UrlReaders.ts +++ b/packages/backend-common/src/reading/UrlReaders.ts @@ -24,6 +24,7 @@ import { GithubUrlReader } from './GithubUrlReader'; import { GitlabUrlReader } from './GitlabUrlReader'; import { ReadTreeResponseFactory } from './tree'; import { FetchUrlReader } from './FetchUrlReader'; +import { GcsUrlReader } from './GcsUrlReader'; type CreateOptions = { /** Root config object */ @@ -70,6 +71,7 @@ export class UrlReaders { BitbucketUrlReader.factory, GithubUrlReader.factory, GitlabUrlReader.factory, + GcsUrlReader.factory, FetchUrlReader.factory, ]), }); diff --git a/yarn.lock b/yarn.lock index b0b8c04313..d9dcd3f5b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2471,6 +2471,21 @@ retry-request "^4.1.1" teeny-request "^7.0.0" +"@google-cloud/common@^3.6.0": + version "3.6.0" + resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.6.0.tgz#c2f6da5f79279a4a9ac7c71fc02d582beab98e8b" + integrity sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q== + dependencies: + "@google-cloud/projectify" "^2.0.0" + "@google-cloud/promisify" "^2.0.0" + arrify "^2.0.1" + duplexify "^4.1.1" + ent "^2.2.0" + extend "^3.0.2" + google-auth-library "^7.0.2" + retry-request "^4.1.1" + teeny-request "^7.0.0" + "@google-cloud/container@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@google-cloud/container/-/container-2.2.0.tgz#e97ae1cee9040b6af09cc8199ed0aa2d4ae6238e" @@ -2570,6 +2585,33 @@ stream-events "^1.0.1" xdg-basedir "^4.0.0" +"@google-cloud/storage@^5.8.0": + version "5.8.0" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-5.8.0.tgz#1f580e276f1d453790b382156421d1bcc4bd3f4b" + integrity sha512-WOShvBPOfkDXUzXMO+3j8Bzus+PFI9r1Ey9dLG2Zf458/PVuFTtaRWntd9ZiDG8g90zl2LmnA1JkDCreGUKr5g== + dependencies: + "@google-cloud/common" "^3.6.0" + "@google-cloud/paginator" "^3.0.0" + "@google-cloud/promisify" "^2.0.0" + arrify "^2.0.0" + async-retry "^1.3.1" + compressible "^2.0.12" + date-and-time "^0.14.2" + duplexify "^4.0.0" + extend "^3.0.2" + gaxios "^4.0.0" + gcs-resumable-upload "^3.1.3" + get-stream "^6.0.0" + hash-stream-validation "^0.2.2" + mime "^2.2.0" + mime-types "^2.0.8" + onetime "^5.1.0" + p-limit "^3.0.1" + pumpify "^2.0.0" + snakeize "^0.1.0" + stream-events "^1.0.1" + xdg-basedir "^4.0.0" + "@graphiql/toolkit@^0.1.0": version "0.1.1" resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.1.1.tgz#a7da3ba460ceae27bcdc8f03831ca4f88f90f3d7" @@ -11393,6 +11435,11 @@ date-and-time@^0.14.2: resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.2.tgz#a4266c3dead460f6c231fe9674e585908dac354e" integrity sha512-EFTCh9zRSEpGPmJaexg7HTuzZHh6cnJj1ui7IGCFNXzd2QdpsNh05Db5TF3xzJm30YN+A8/6xHSuRcQqoc3kFA== +date-and-time@^0.14.2: + version "0.14.2" + resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.2.tgz#a4266c3dead460f6c231fe9674e585908dac354e" + integrity sha512-EFTCh9zRSEpGPmJaexg7HTuzZHh6cnJj1ui7IGCFNXzd2QdpsNh05Db5TF3xzJm30YN+A8/6xHSuRcQqoc3kFA== + date-and-time@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.6.3.tgz#2daee52df67c28bd93bce862756ac86b68cf4237" @@ -13979,6 +14026,19 @@ gcs-resumable-upload@^3.1.3: pumpify "^2.0.0" stream-events "^1.0.4" +gcs-resumable-upload@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.3.tgz#1e38e1339600b85812e6430a5ab455453c64cce3" + integrity sha512-LjVrv6YVH0XqBr/iBW0JgRA1ndxhK6zfEFFJR4im51QVTj/4sInOXimY2evDZuSZ75D3bHxTaQAdXRukMc1y+w== + dependencies: + abort-controller "^3.0.0" + configstore "^5.0.0" + extend "^3.0.2" + gaxios "^4.0.0" + google-auth-library "^7.0.0" + pumpify "^2.0.0" + stream-events "^1.0.4" + generic-names@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" From 7abb50d59ef22859bf7f3581195e0e635195d45d Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Fri, 12 Mar 2021 17:11:37 +0100 Subject: [PATCH 42/61] Add tests for GCSUrlReader Signed-off-by: Martina Iglesias Fernandez --- .../src/reading/GcsUrlReader.test.ts | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 packages/backend-common/src/reading/GcsUrlReader.test.ts diff --git a/packages/backend-common/src/reading/GcsUrlReader.test.ts b/packages/backend-common/src/reading/GcsUrlReader.test.ts new file mode 100644 index 0000000000..abd48d5b36 --- /dev/null +++ b/packages/backend-common/src/reading/GcsUrlReader.test.ts @@ -0,0 +1,113 @@ +/* + * 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, JsonObject } from '@backstage/config'; +import { getVoidLogger } from '../logging'; +import { ReadTreeResponseFactory } from './tree'; +import { GcsUrlReader } from './GcsUrlReader'; +import { UrlReaderPredicateTuple } from './types'; + +describe('GcsUrlReader', () => { + const createReader = (config: JsonObject): UrlReaderPredicateTuple[] => { + return GcsUrlReader.factory({ + config: new ConfigReader(config), + logger: getVoidLogger(), + treeResponseFactory: ReadTreeResponseFactory.create({ + config: new ConfigReader({}), + }), + }); + }; + + it('does not create a reader without the gcs field', () => { + const entries = createReader({ + integrations: {}, + }); + expect(entries).toHaveLength(0); + }); + + it('creates a reader with credentials correctly configured', () => { + const entries = createReader({ + integrations: { + gcs: [ + { + privateKey: '--- BEGIN KEY ---- fakekey --- END KEY ---', + clientEmail: 'someone@example.com', + }, + { + host: 'proxy.storage.cloud.google.com', + privateKey: '--- BEGIN KEY ---- fakekey2 --- END KEY ---', + clientEmail: 'someone2@example.com', + }, + ], + }, + }); + expect(entries).toHaveLength(2); + }); + + it('does not create a reader if the privateKey is missing', () => { + const entries = createReader({ + integrations: { + gcs: [ + { + clientEmail: 'someone@example.com', + }, + ], + }, + }); + expect(entries).toHaveLength(0); + }); + + it('does not create a reader if the clientEmail is missing', () => { + const entries = createReader({ + integrations: { + gcs: [ + { + privateKey: + '-----BEGIN PRIVATE KEY----- fakekey -----END PRIVATE KEY-----', + }, + ], + }, + }); + expect(entries).toHaveLength(0); + }); + + it('predicates', () => { + const readers = createReader({ + integrations: { + gcs: [ + { + privateKey: + '-----BEGIN PRIVATE KEY----- fakekey -----END PRIVATE KEY-----', + clientEmail: 'someone@example.com', + }, + ], + }, + }); + const predicate = readers[0].predicate; + expect(predicate(new URL('https://storage.cloud.google.com'))).toBe(true); + expect( + predicate( + new URL( + 'https://storage.cloud.google.com/team1/service1/catalog-info.yaml', + ), + ), + ).toBe(true); + expect(predicate(new URL('https://storage2.cloud.google.com'))).toBe(false); + expect(predicate(new URL('https://cloud.google.com'))).toBe(false); + expect(predicate(new URL('https://google.com'))).toBe(false); + expect(predicate(new URL('https://a.example.com/test'))).toBe(false); + }); +}); From b779b5fee035455d2c75687e4275ba23a1bfc8ae Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Mon, 15 Mar 2021 09:02:03 +0100 Subject: [PATCH 43/61] Add changeset Signed-off-by: Martina Iglesias Fernandez --- .changeset/sour-clocks-pay.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/sour-clocks-pay.md diff --git a/.changeset/sour-clocks-pay.md b/.changeset/sour-clocks-pay.md new file mode 100644 index 0000000000..79b7c95be3 --- /dev/null +++ b/.changeset/sour-clocks-pay.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': minor +--- + +Add UrlReader for Google Cloud Storage From 9c4cedf268a0ef95d75f3781a2a74b7527c4539b Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Thu, 18 Mar 2021 19:15:25 +0100 Subject: [PATCH 44/61] Fix some comments Signed-off-by: Martina Iglesias Fernandez --- .changeset/sour-clocks-pay.md | 2 +- app-config.yaml | 5 +- packages/backend-common/package.json | 1 + .../src/reading/GcsUrlReader.test.ts | 46 ++++++++----- .../src/reading/GcsUrlReader.ts | 21 +++--- yarn.lock | 65 ++++++------------- 6 files changed, 63 insertions(+), 77 deletions(-) diff --git a/.changeset/sour-clocks-pay.md b/.changeset/sour-clocks-pay.md index 79b7c95be3..11196aeebe 100644 --- a/.changeset/sour-clocks-pay.md +++ b/.changeset/sour-clocks-pay.md @@ -1,5 +1,5 @@ --- -'@backstage/backend-common': minor +'@backstage/backend-common': patch --- Add UrlReader for Google Cloud Storage diff --git a/app-config.yaml b/app-config.yaml index 4d12e3e474..49ac0b0d00 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -122,9 +122,8 @@ kafka: - localhost:9092 integrations: - gcs: - - host: 'storage.cloud.google.com' - clientEmail: + googleGcs: + - clientEmail: $env: GCS_CLIENT_EMAIL privateKey: $env: GCS_PRIVATE_KEY diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index a720f9e3ab..ba846e0f5f 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -57,6 +57,7 @@ "minimatch": "^3.0.4", "minimist": "^1.2.5", "morgan": "^1.10.0", + "raw-body": "^2.4.1", "selfsigned": "^1.10.7", "stoppable": "^1.1.0", "tar": "^6.0.5", diff --git a/packages/backend-common/src/reading/GcsUrlReader.test.ts b/packages/backend-common/src/reading/GcsUrlReader.test.ts index abd48d5b36..16102d2812 100644 --- a/packages/backend-common/src/reading/GcsUrlReader.test.ts +++ b/packages/backend-common/src/reading/GcsUrlReader.test.ts @@ -31,7 +31,7 @@ describe('GcsUrlReader', () => { }); }; - it('does not create a reader without the gcs field', () => { + it('does not create a reader without the googleGcs field', () => { const entries = createReader({ integrations: {}, }); @@ -41,7 +41,7 @@ describe('GcsUrlReader', () => { it('creates a reader with credentials correctly configured', () => { const entries = createReader({ integrations: { - gcs: [ + googleGcs: [ { privateKey: '--- BEGIN KEY ---- fakekey --- END KEY ---', clientEmail: 'someone@example.com', @@ -60,7 +60,7 @@ describe('GcsUrlReader', () => { it('does not create a reader if the privateKey is missing', () => { const entries = createReader({ integrations: { - gcs: [ + googleGcs: [ { clientEmail: 'someone@example.com', }, @@ -73,7 +73,7 @@ describe('GcsUrlReader', () => { it('does not create a reader if the clientEmail is missing', () => { const entries = createReader({ integrations: { - gcs: [ + googleGcs: [ { privateKey: '-----BEGIN PRIVATE KEY----- fakekey -----END PRIVATE KEY-----', @@ -84,10 +84,10 @@ describe('GcsUrlReader', () => { expect(entries).toHaveLength(0); }); - it('predicates', () => { + describe('predicates', () => { const readers = createReader({ integrations: { - gcs: [ + googleGcs: [ { privateKey: '-----BEGIN PRIVATE KEY----- fakekey -----END PRIVATE KEY-----', @@ -97,17 +97,29 @@ describe('GcsUrlReader', () => { }, }); const predicate = readers[0].predicate; - expect(predicate(new URL('https://storage.cloud.google.com'))).toBe(true); - expect( - predicate( - new URL( - 'https://storage.cloud.google.com/team1/service1/catalog-info.yaml', + + it('returns true for the correct google cloud storage host', () => { + expect(predicate(new URL('https://storage.cloud.google.com'))).toBe(true); + }); + it('returns true for a url with the full path and the correct host', () => { + expect( + predicate( + new URL( + 'https://storage.cloud.google.com/team1/service1/catalog-info.yaml', + ), ), - ), - ).toBe(true); - expect(predicate(new URL('https://storage2.cloud.google.com'))).toBe(false); - expect(predicate(new URL('https://cloud.google.com'))).toBe(false); - expect(predicate(new URL('https://google.com'))).toBe(false); - expect(predicate(new URL('https://a.example.com/test'))).toBe(false); + ).toBe(true); + }); + it('returns false for the wrong hostname under cloud.google.com', () => { + expect(predicate(new URL('https://storage2.cloud.google.com'))).toBe( + false, + ); + }); + it('returns false for a partially correct host', () => { + expect(predicate(new URL('https://cloud.google.com'))).toBe(false); + }); + it('returns false for a completely different host', () => { + expect(predicate(new URL('https://a.example.com/test'))).toBe(false); + }); }); }); diff --git a/packages/backend-common/src/reading/GcsUrlReader.ts b/packages/backend-common/src/reading/GcsUrlReader.ts index 23c8f45fe7..08891d7b49 100644 --- a/packages/backend-common/src/reading/GcsUrlReader.ts +++ b/packages/backend-common/src/reading/GcsUrlReader.ts @@ -22,7 +22,6 @@ import { UrlReader, } from './types'; import getRawBody from 'raw-body'; -import { Logger } from 'winston'; const parseURL = ( url: string, @@ -43,11 +42,14 @@ const parseURL = ( export class GcsUrlReader implements UrlReader { static factory: ReaderFactory = ({ config, logger }) => { - if (!config.has('integrations.gcs')) { + if (!config.has('integrations.googleGcs')) { return []; } - return config - .getConfigArray('integrations.gcs') + const configs = config.getOptionalConfigArray('integrations.googleGcs'); + if (!configs) { + return []; + } + return configs .filter(integration => { if (!integration.has('clientEmail') || !integration.has('privateKey')) { logger.warn( @@ -69,7 +71,7 @@ export class GcsUrlReader implements UrlReader { private_key: privKey, }, }); - const reader = new GcsUrlReader(storage, logger); + const reader = new GcsUrlReader(storage); const host = integration.getOptionalString('host') || 'storage.cloud.google.com'; @@ -79,21 +81,16 @@ export class GcsUrlReader implements UrlReader { }); }; - constructor( - private readonly storage: Storage, - private readonly logger: Logger, - ) {} + constructor(private readonly storage: Storage) {} async read(url: string): Promise { try { const { bucket, key } = parseURL(url); - this.logger.info('Reading GCS Location'); return await getRawBody( this.storage.bucket(bucket).file(key).createReadStream(), ); } catch (error) { - this.logger.warn(error.stack); throw new Error(`unable to read gcs file from ${url}`); } } @@ -103,7 +100,7 @@ export class GcsUrlReader implements UrlReader { } async search(): Promise { - throw new Error('GcsUrlReader does not implement readTree'); + throw new Error('GcsUrlReader does not implement search'); } toString() { diff --git a/yarn.lock b/yarn.lock index d9dcd3f5b1..1cb153d4e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2471,21 +2471,6 @@ retry-request "^4.1.1" teeny-request "^7.0.0" -"@google-cloud/common@^3.6.0": - version "3.6.0" - resolved "https://registry.npmjs.org/@google-cloud/common/-/common-3.6.0.tgz#c2f6da5f79279a4a9ac7c71fc02d582beab98e8b" - integrity sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q== - dependencies: - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - arrify "^2.0.1" - duplexify "^4.1.1" - ent "^2.2.0" - extend "^3.0.2" - google-auth-library "^7.0.2" - retry-request "^4.1.1" - teeny-request "^7.0.0" - "@google-cloud/container@^2.2.0": version "2.2.0" resolved "https://registry.npmjs.org/@google-cloud/container/-/container-2.2.0.tgz#e97ae1cee9040b6af09cc8199ed0aa2d4ae6238e" @@ -11435,11 +11420,6 @@ date-and-time@^0.14.2: resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.2.tgz#a4266c3dead460f6c231fe9674e585908dac354e" integrity sha512-EFTCh9zRSEpGPmJaexg7HTuzZHh6cnJj1ui7IGCFNXzd2QdpsNh05Db5TF3xzJm30YN+A8/6xHSuRcQqoc3kFA== -date-and-time@^0.14.2: - version "0.14.2" - resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.14.2.tgz#a4266c3dead460f6c231fe9674e585908dac354e" - integrity sha512-EFTCh9zRSEpGPmJaexg7HTuzZHh6cnJj1ui7IGCFNXzd2QdpsNh05Db5TF3xzJm30YN+A8/6xHSuRcQqoc3kFA== - date-and-time@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/date-and-time/-/date-and-time-0.6.3.tgz#2daee52df67c28bd93bce862756ac86b68cf4237" @@ -14026,19 +14006,6 @@ gcs-resumable-upload@^3.1.3: pumpify "^2.0.0" stream-events "^1.0.4" -gcs-resumable-upload@^3.1.3: - version "3.1.3" - resolved "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-3.1.3.tgz#1e38e1339600b85812e6430a5ab455453c64cce3" - integrity sha512-LjVrv6YVH0XqBr/iBW0JgRA1ndxhK6zfEFFJR4im51QVTj/4sInOXimY2evDZuSZ75D3bHxTaQAdXRukMc1y+w== - dependencies: - abort-controller "^3.0.0" - configstore "^5.0.0" - extend "^3.0.2" - gaxios "^4.0.0" - google-auth-library "^7.0.0" - pumpify "^2.0.0" - stream-events "^1.0.4" - generic-names@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" @@ -15159,6 +15126,17 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-errors@^1.7.3: version "1.8.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" @@ -15180,17 +15158,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - "http-parser-js@>=0.4.0 <0.4.11": version "0.4.10" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" @@ -21976,6 +21943,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + raw-loader@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" From e4cdec27e936359aad1a499a62ebac9405b44193 Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Fri, 19 Mar 2021 17:10:15 +0100 Subject: [PATCH 45/61] Fix comments on GoogleGcsUrlReader Signed-off-by: Martina Iglesias Fernandez --- ...der.test.ts => GoogleGcsUrlReader.test.ts} | 36 ++--------- ...{GcsUrlReader.ts => GoogleGcsUrlReader.ts} | 60 ++++++++---------- .../backend-common/src/reading/UrlReaders.ts | 4 +- packages/integration/config.d.ts | 14 +++++ .../integration/src/googleGcs/config.test.ts | 62 ++++++++++++++++++ packages/integration/src/googleGcs/config.ts | 63 +++++++++++++++++++ packages/integration/src/googleGcs/index.ts | 22 +++++++ packages/integration/src/index.ts | 1 + 8 files changed, 196 insertions(+), 66 deletions(-) rename packages/backend-common/src/reading/{GcsUrlReader.test.ts => GoogleGcsUrlReader.test.ts} (77%) rename packages/backend-common/src/reading/{GcsUrlReader.ts => GoogleGcsUrlReader.ts} (60%) create mode 100644 packages/integration/src/googleGcs/config.test.ts create mode 100644 packages/integration/src/googleGcs/config.ts create mode 100644 packages/integration/src/googleGcs/index.ts diff --git a/packages/backend-common/src/reading/GcsUrlReader.test.ts b/packages/backend-common/src/reading/GoogleGcsUrlReader.test.ts similarity index 77% rename from packages/backend-common/src/reading/GcsUrlReader.test.ts rename to packages/backend-common/src/reading/GoogleGcsUrlReader.test.ts index 16102d2812..c8262767e1 100644 --- a/packages/backend-common/src/reading/GcsUrlReader.test.ts +++ b/packages/backend-common/src/reading/GoogleGcsUrlReader.test.ts @@ -17,12 +17,12 @@ import { ConfigReader, JsonObject } from '@backstage/config'; import { getVoidLogger } from '../logging'; import { ReadTreeResponseFactory } from './tree'; -import { GcsUrlReader } from './GcsUrlReader'; +import { GoogleGcsUrlReader } from './GoogleGcsUrlReader'; import { UrlReaderPredicateTuple } from './types'; describe('GcsUrlReader', () => { const createReader = (config: JsonObject): UrlReaderPredicateTuple[] => { - return GcsUrlReader.factory({ + return GoogleGcsUrlReader.factory({ config: new ConfigReader(config), logger: getVoidLogger(), treeResponseFactory: ReadTreeResponseFactory.create({ @@ -57,43 +57,19 @@ describe('GcsUrlReader', () => { expect(entries).toHaveLength(2); }); - it('does not create a reader if the privateKey is missing', () => { + it('creates a reader with default credentials provider', () => { const entries = createReader({ integrations: { - googleGcs: [ - { - clientEmail: 'someone@example.com', - }, - ], + googleGcs: [{}], }, }); - expect(entries).toHaveLength(0); - }); - - it('does not create a reader if the clientEmail is missing', () => { - const entries = createReader({ - integrations: { - googleGcs: [ - { - privateKey: - '-----BEGIN PRIVATE KEY----- fakekey -----END PRIVATE KEY-----', - }, - ], - }, - }); - expect(entries).toHaveLength(0); + expect(entries).toHaveLength(1); }); describe('predicates', () => { const readers = createReader({ integrations: { - googleGcs: [ - { - privateKey: - '-----BEGIN PRIVATE KEY----- fakekey -----END PRIVATE KEY-----', - clientEmail: 'someone@example.com', - }, - ], + googleGcs: [{}], }, }); const predicate = readers[0].predicate; diff --git a/packages/backend-common/src/reading/GcsUrlReader.ts b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts similarity index 60% rename from packages/backend-common/src/reading/GcsUrlReader.ts rename to packages/backend-common/src/reading/GoogleGcsUrlReader.ts index 08891d7b49..a9100051a6 100644 --- a/packages/backend-common/src/reading/GcsUrlReader.ts +++ b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts @@ -22,13 +22,17 @@ import { UrlReader, } from './types'; import getRawBody from 'raw-body'; +import { + GOOGLE_GCS_HOST, + readGoogleGcsIntegrationConfigs, +} from '@backstage/integration'; const parseURL = ( url: string, ): { host: string; bucket: string; key: string } => { const { host, pathname } = new URL(url); - if (host !== 'storage.cloud.google.com') { + if (host !== GOOGLE_GCS_HOST) { throw new Error(`not a valid GCS URL: ${url}`); } @@ -40,45 +44,33 @@ const parseURL = ( }; }; -export class GcsUrlReader implements UrlReader { +export class GoogleGcsUrlReader implements UrlReader { static factory: ReaderFactory = ({ config, logger }) => { if (!config.has('integrations.googleGcs')) { return []; } - const configs = config.getOptionalConfigArray('integrations.googleGcs'); - if (!configs) { - return []; - } - return configs - .filter(integration => { - if (!integration.has('clientEmail') || !integration.has('privateKey')) { - logger.warn( - "Skipping gcs integration, Missing required config value at 'integration.gcs.clientEmail' or 'integration.gcs.privateKey'", - ); - return false; - } - return true; - }) - .map(integration => { - const privKey = integration - .getOptionalString('privateKey') - ?.split('\\n') - .join('\n'); - - const storage = new Storage({ + const configs = readGoogleGcsIntegrationConfigs( + config.getOptionalConfigArray('integrations.googleGcs') ?? [], + ); + return configs.map(integration => { + let storage: Storage; + if (!integration.clientEmail || !integration.privateKey) { + logger.warn( + 'googleGcs credentials not found in config. Using default credentials provider.', + ); + storage = new Storage(); + } else { + storage = new Storage({ credentials: { - client_email: integration.getOptionalString('clientEmail'), - private_key: privKey, + client_email: integration.clientEmail || undefined, + private_key: integration.privateKey || undefined, }, }); - const reader = new GcsUrlReader(storage); - const host = - integration.getOptionalString('host') || 'storage.cloud.google.com'; - - logger.info('Configuring integration, gcs'); - const predicate = (url: URL) => url.host === host; - return { reader, predicate }; - }); + } + const reader = new GoogleGcsUrlReader(storage); + const predicate = (url: URL) => url.host === GOOGLE_GCS_HOST; + return { reader, predicate }; + }); }; constructor(private readonly storage: Storage) {} @@ -104,6 +96,6 @@ export class GcsUrlReader implements UrlReader { } toString() { - return `gcs{host=storage.cloud.google.com,authed=true}}`; + return `gcs{host=${GOOGLE_GCS_HOST},authed=true}}`; } } diff --git a/packages/backend-common/src/reading/UrlReaders.ts b/packages/backend-common/src/reading/UrlReaders.ts index 785325faa7..06c6fdcdcf 100644 --- a/packages/backend-common/src/reading/UrlReaders.ts +++ b/packages/backend-common/src/reading/UrlReaders.ts @@ -24,7 +24,7 @@ import { GithubUrlReader } from './GithubUrlReader'; import { GitlabUrlReader } from './GitlabUrlReader'; import { ReadTreeResponseFactory } from './tree'; import { FetchUrlReader } from './FetchUrlReader'; -import { GcsUrlReader } from './GcsUrlReader'; +import { GoogleGcsUrlReader } from './GoogleGcsUrlReader'; type CreateOptions = { /** Root config object */ @@ -71,7 +71,7 @@ export class UrlReaders { BitbucketUrlReader.factory, GithubUrlReader.factory, GitlabUrlReader.factory, - GcsUrlReader.factory, + GoogleGcsUrlReader.factory, FetchUrlReader.factory, ]), }); diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index f0d72b7ceb..2947ba3889 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -149,5 +149,19 @@ export interface Config { */ baseUrl?: string; }>; + + /** Integration configuration for Google Cloud Storage */ + googleGcs?: Array<{ + /** + * Service account email used to authenticate requests. + * @visibility secret + */ + clientEmail?: string; + /** + * Service account private key used to authenticate requests. + * @visibility secret + */ + privateKey?: string; + }>; }; } diff --git a/packages/integration/src/googleGcs/config.test.ts b/packages/integration/src/googleGcs/config.test.ts new file mode 100644 index 0000000000..d612343b5e --- /dev/null +++ b/packages/integration/src/googleGcs/config.test.ts @@ -0,0 +1,62 @@ +/* + * 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 { Config, ConfigReader } from '@backstage/config'; +import { + GoogleGcsIntegrationConfig, + readGoogleGcsIntegrationConfig, + readGoogleGcsIntegrationConfigs, +} from './config'; + +describe('readGoogleGcsIntegrationConfig', () => { + function buildConfig(data: Partial): Config { + return new ConfigReader(data); + } + + it('reads all values', () => { + const output = readGoogleGcsIntegrationConfig( + buildConfig({ + privateKey: 'fake-key', + clientEmail: 'someone@example.com', + }), + ); + expect(output).toEqual({ + privateKey: 'fake-key', + token: 'someone@example.com', + }); + }); +}); + +describe('readGoogleGcsIntegrationConfigs', () => { + function buildConfig(data: Partial[]): Config[] { + return data.map(item => new ConfigReader(item)); + } + + it('reads all values', () => { + const output = readGoogleGcsIntegrationConfigs( + buildConfig([ + { + privateKey: 'fake-key', + clientEmail: 'someone@example.com', + }, + ]), + ); + expect(output).toContainEqual({ + privateKey: 'fake-key', + clientEmail: 'someone@example.com', + }); + }); +}); diff --git a/packages/integration/src/googleGcs/config.ts b/packages/integration/src/googleGcs/config.ts new file mode 100644 index 0000000000..5f70932638 --- /dev/null +++ b/packages/integration/src/googleGcs/config.ts @@ -0,0 +1,63 @@ +/* + * 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 { Config } from '@backstage/config'; + +/** + * The configuration parameters for a single Google Cloud Storage provider. + */ +export type GoogleGcsIntegrationConfig = { + /** + * Service account email used to authenticate requests. + */ + clientEmail?: string; + /** + * Service account private key used to authenticate requests. + */ + privateKey?: string; +}; + +/** + * Reads a single Google GCS integration config. + * + * @param config The config object of a single integration + */ +export function readGoogleGcsIntegrationConfig( + config: Config, +): GoogleGcsIntegrationConfig { + if (!config.has('clientEmail') || !config.has('privateKey')) { + return {}; + } + + const privateKey = config + .getOptionalString('privateKey') + ?.split('\\n') + .join('\n'); + + const clientEmail = config.getOptionalString('clientEmail'); + return { clientEmail: clientEmail, privateKey: privateKey }; +} + +/** + * Reads a set of Google Cloud Storage integration configs. + * + * @param configs All of the integration config objects + */ +export function readGoogleGcsIntegrationConfigs( + configs: Config[], +): GoogleGcsIntegrationConfig[] { + return configs.map(readGoogleGcsIntegrationConfig); +} diff --git a/packages/integration/src/googleGcs/index.ts b/packages/integration/src/googleGcs/index.ts new file mode 100644 index 0000000000..f120256d35 --- /dev/null +++ b/packages/integration/src/googleGcs/index.ts @@ -0,0 +1,22 @@ +/* + * 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 { + readGoogleGcsIntegrationConfig, + readGoogleGcsIntegrationConfigs, +} from './config'; +export type { GoogleGcsIntegrationConfig } from './config'; +export const GOOGLE_GCS_HOST = 'storage.cloud.google.com'; diff --git a/packages/integration/src/index.ts b/packages/integration/src/index.ts index e89cf6ef92..d15eea0832 100644 --- a/packages/integration/src/index.ts +++ b/packages/integration/src/index.ts @@ -18,6 +18,7 @@ export * from './azure'; export * from './bitbucket'; export * from './github'; export * from './gitlab'; +export * from './googleGcs'; export { defaultScmResolveUrl } from './helpers'; export { ScmIntegrations } from './ScmIntegrations'; export type { From 5bd790984e91fb200aa9d1c23ab5100bb381e1af Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Fri, 19 Mar 2021 17:29:43 +0100 Subject: [PATCH 46/61] Add integration documentation Signed-off-by: Martina Iglesias Fernandez --- docs/integrations/google-cloud-storage/gcs.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/integrations/google-cloud-storage/gcs.md diff --git a/docs/integrations/google-cloud-storage/gcs.md b/docs/integrations/google-cloud-storage/gcs.md new file mode 100644 index 0000000000..f9cba7a04c --- /dev/null +++ b/docs/integrations/google-cloud-storage/gcs.md @@ -0,0 +1,57 @@ +--- +id: googlegcs +title: Google Cloud Storage +sidebar_label: Google Cloud Storage Data +# prettier-ignore +description: Setting up an integration with Google Cloud Storage +--- + +The Backstage catalog can import entities from a yaml file stored in a GCS +(Google Cloud Storage) bucket. To enable the ingestion of said entities the +`GoogleGcs` integration must be enabled first. + +## Configuration + +To configure the integration add the appropriate credentials to the Backstage +backend. There are two main ways to do this: by explicitly setting a +`clientEmail` and a `privateKey` or by letting the Google Storage SDK discover +the credentials automatically. + +### Explicit credentials + +Explicit credentials can be set in the following format: + +```yaml +integrations: + googleGcs: + - clientEmail: + $env: GCS_CLIENT_EMAIL + privateKey: + $env: GCS_PRIVATE_KEY +``` + +Then make sure the environment variables `GCS_CLIENT_EMAIL` and +`GCS_PRIVATE_KEY` are set when you run Backstage. + +### Automatic discovery of Google credentials + +Since this integration uses the Google Storage SDK, you can also choose to not +provide any explicit credentials and let the SDK discover them automatically. + +One of these discovery methods is to provide an environment variable called +`GOOGLE_APPLICATION_CREDENTIALS` and set it to the file path of your JSON +service account key. + +For more details and methods to provide credentials to the Google Storage SDK +you can check [this documentation page][google gcs docs]. + +## Usage + +To use this integration to import entities from a GCS bucket go to the Google +console and browse the file you would like to import. Then copy the +`Authenticated URL` and paste it into the text box in the `register component` +form. This url should look like +`https://storage.cloud.google.com///catalog-info.yaml`. + +[google gcs docs]: + https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-nodejs From d5cb51331a42ae8483e2922b2dcad89712231746 Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Fri, 19 Mar 2021 17:50:25 +0100 Subject: [PATCH 47/61] Only allow one googleGcs config object Signed-off-by: Martina Iglesias Fernandez --- app-config.yaml | 8 ++-- docs/integrations/google-cloud-storage/gcs.md | 8 ++-- .../src/reading/GoogleGcsUrlReader.test.ts | 21 ++++------ .../src/reading/GoogleGcsUrlReader.ts | 42 +++++++++---------- packages/integration/config.d.ts | 4 +- .../integration/src/googleGcs/config.test.ts | 23 ++-------- packages/integration/src/googleGcs/config.ts | 15 ++----- packages/integration/src/googleGcs/index.ts | 5 +-- 8 files changed, 45 insertions(+), 81 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 49ac0b0d00..3dc071c85c 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -123,10 +123,10 @@ kafka: integrations: googleGcs: - - clientEmail: - $env: GCS_CLIENT_EMAIL - privateKey: - $env: GCS_PRIVATE_KEY + clientEmail: + $env: GCS_CLIENT_EMAIL + privateKey: + $env: GCS_PRIVATE_KEY github: - host: github.com diff --git a/docs/integrations/google-cloud-storage/gcs.md b/docs/integrations/google-cloud-storage/gcs.md index f9cba7a04c..1d46c57b10 100644 --- a/docs/integrations/google-cloud-storage/gcs.md +++ b/docs/integrations/google-cloud-storage/gcs.md @@ -24,10 +24,10 @@ Explicit credentials can be set in the following format: ```yaml integrations: googleGcs: - - clientEmail: - $env: GCS_CLIENT_EMAIL - privateKey: - $env: GCS_PRIVATE_KEY + clientEmail: + $env: GCS_CLIENT_EMAIL + privateKey: + $env: GCS_PRIVATE_KEY ``` Then make sure the environment variables `GCS_CLIENT_EMAIL` and diff --git a/packages/backend-common/src/reading/GoogleGcsUrlReader.test.ts b/packages/backend-common/src/reading/GoogleGcsUrlReader.test.ts index c8262767e1..e201361a91 100644 --- a/packages/backend-common/src/reading/GoogleGcsUrlReader.test.ts +++ b/packages/backend-common/src/reading/GoogleGcsUrlReader.test.ts @@ -41,26 +41,19 @@ describe('GcsUrlReader', () => { it('creates a reader with credentials correctly configured', () => { const entries = createReader({ integrations: { - googleGcs: [ - { - privateKey: '--- BEGIN KEY ---- fakekey --- END KEY ---', - clientEmail: 'someone@example.com', - }, - { - host: 'proxy.storage.cloud.google.com', - privateKey: '--- BEGIN KEY ---- fakekey2 --- END KEY ---', - clientEmail: 'someone2@example.com', - }, - ], + googleGcs: { + privateKey: '--- BEGIN KEY ---- fakekey --- END KEY ---', + clientEmail: 'someone@example.com', + }, }, }); - expect(entries).toHaveLength(2); + expect(entries).toHaveLength(1); }); it('creates a reader with default credentials provider', () => { const entries = createReader({ integrations: { - googleGcs: [{}], + googleGcs: {}, }, }); expect(entries).toHaveLength(1); @@ -69,7 +62,7 @@ describe('GcsUrlReader', () => { describe('predicates', () => { const readers = createReader({ integrations: { - googleGcs: [{}], + googleGcs: {}, }, }); const predicate = readers[0].predicate; diff --git a/packages/backend-common/src/reading/GoogleGcsUrlReader.ts b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts index a9100051a6..80b42dd5da 100644 --- a/packages/backend-common/src/reading/GoogleGcsUrlReader.ts +++ b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts @@ -24,7 +24,7 @@ import { import getRawBody from 'raw-body'; import { GOOGLE_GCS_HOST, - readGoogleGcsIntegrationConfigs, + readGoogleGcsIntegrationConfig, } from '@backstage/integration'; const parseURL = ( @@ -49,28 +49,26 @@ export class GoogleGcsUrlReader implements UrlReader { if (!config.has('integrations.googleGcs')) { return []; } - const configs = readGoogleGcsIntegrationConfigs( - config.getOptionalConfigArray('integrations.googleGcs') ?? [], + const gcsConfig = readGoogleGcsIntegrationConfig( + config.getConfig('integrations.googleGcs'), ); - return configs.map(integration => { - let storage: Storage; - if (!integration.clientEmail || !integration.privateKey) { - logger.warn( - 'googleGcs credentials not found in config. Using default credentials provider.', - ); - storage = new Storage(); - } else { - storage = new Storage({ - credentials: { - client_email: integration.clientEmail || undefined, - private_key: integration.privateKey || undefined, - }, - }); - } - const reader = new GoogleGcsUrlReader(storage); - const predicate = (url: URL) => url.host === GOOGLE_GCS_HOST; - return { reader, predicate }; - }); + let storage: Storage; + if (!gcsConfig.clientEmail || !gcsConfig.privateKey) { + logger.warn( + 'googleGcs credentials not found in config. Using default credentials provider.', + ); + storage = new Storage(); + } else { + storage = new Storage({ + credentials: { + client_email: gcsConfig.clientEmail || undefined, + private_key: gcsConfig.privateKey || undefined, + }, + }); + } + const reader = new GoogleGcsUrlReader(storage); + const predicate = (url: URL) => url.host === GOOGLE_GCS_HOST; + return [{ reader, predicate }]; }; constructor(private readonly storage: Storage) {} diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index 2947ba3889..9f9d588f0b 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -151,7 +151,7 @@ export interface Config { }>; /** Integration configuration for Google Cloud Storage */ - googleGcs?: Array<{ + googleGcs?: { /** * Service account email used to authenticate requests. * @visibility secret @@ -162,6 +162,6 @@ export interface Config { * @visibility secret */ privateKey?: string; - }>; + }; }; } diff --git a/packages/integration/src/googleGcs/config.test.ts b/packages/integration/src/googleGcs/config.test.ts index d612343b5e..f688df4071 100644 --- a/packages/integration/src/googleGcs/config.test.ts +++ b/packages/integration/src/googleGcs/config.test.ts @@ -18,7 +18,6 @@ import { Config, ConfigReader } from '@backstage/config'; import { GoogleGcsIntegrationConfig, readGoogleGcsIntegrationConfig, - readGoogleGcsIntegrationConfigs, } from './config'; describe('readGoogleGcsIntegrationConfig', () => { @@ -38,25 +37,9 @@ describe('readGoogleGcsIntegrationConfig', () => { token: 'someone@example.com', }); }); -}); -describe('readGoogleGcsIntegrationConfigs', () => { - function buildConfig(data: Partial[]): Config[] { - return data.map(item => new ConfigReader(item)); - } - - it('reads all values', () => { - const output = readGoogleGcsIntegrationConfigs( - buildConfig([ - { - privateKey: 'fake-key', - clientEmail: 'someone@example.com', - }, - ]), - ); - expect(output).toContainEqual({ - privateKey: 'fake-key', - clientEmail: 'someone@example.com', - }); + it('does not fail when config is not set', () => { + const output = readGoogleGcsIntegrationConfig(buildConfig({})); + expect(output).toEqual({}); }); }); diff --git a/packages/integration/src/googleGcs/config.ts b/packages/integration/src/googleGcs/config.ts index 5f70932638..90a679bb4c 100644 --- a/packages/integration/src/googleGcs/config.ts +++ b/packages/integration/src/googleGcs/config.ts @@ -38,6 +38,10 @@ export type GoogleGcsIntegrationConfig = { export function readGoogleGcsIntegrationConfig( config: Config, ): GoogleGcsIntegrationConfig { + if (!config) { + return {}; + } + if (!config.has('clientEmail') || !config.has('privateKey')) { return {}; } @@ -50,14 +54,3 @@ export function readGoogleGcsIntegrationConfig( const clientEmail = config.getOptionalString('clientEmail'); return { clientEmail: clientEmail, privateKey: privateKey }; } - -/** - * Reads a set of Google Cloud Storage integration configs. - * - * @param configs All of the integration config objects - */ -export function readGoogleGcsIntegrationConfigs( - configs: Config[], -): GoogleGcsIntegrationConfig[] { - return configs.map(readGoogleGcsIntegrationConfig); -} diff --git a/packages/integration/src/googleGcs/index.ts b/packages/integration/src/googleGcs/index.ts index f120256d35..ac85d706b5 100644 --- a/packages/integration/src/googleGcs/index.ts +++ b/packages/integration/src/googleGcs/index.ts @@ -14,9 +14,6 @@ * limitations under the License. */ -export { - readGoogleGcsIntegrationConfig, - readGoogleGcsIntegrationConfigs, -} from './config'; +export { readGoogleGcsIntegrationConfig } from './config'; export type { GoogleGcsIntegrationConfig } from './config'; export const GOOGLE_GCS_HOST = 'storage.cloud.google.com'; From 18f4301d3baea0e46dd952a9e77217ce8d035f22 Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Fri, 19 Mar 2021 18:13:52 +0100 Subject: [PATCH 48/61] Fix googleGcs integration test Signed-off-by: Martina Iglesias Fernandez --- packages/integration/src/googleGcs/config.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration/src/googleGcs/config.test.ts b/packages/integration/src/googleGcs/config.test.ts index f688df4071..a924e953b9 100644 --- a/packages/integration/src/googleGcs/config.test.ts +++ b/packages/integration/src/googleGcs/config.test.ts @@ -34,7 +34,7 @@ describe('readGoogleGcsIntegrationConfig', () => { ); expect(output).toEqual({ privateKey: 'fake-key', - token: 'someone@example.com', + clientEmail: 'someone@example.com', }); }); From 6a9b9d0f7c1eca836bb52687921308a6cf5bce61 Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Mon, 29 Mar 2021 10:14:52 +0200 Subject: [PATCH 49/61] Fix some comments Signed-off-by: Martina Iglesias Fernandez --- app-config.yaml | 10 ++++------ .../backend-common/src/reading/GoogleGcsUrlReader.ts | 9 ++++----- packages/integration/config.d.ts | 2 +- packages/integration/src/googleGcs/index.ts | 1 - 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 3dc071c85c..48d58dc837 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -122,12 +122,6 @@ kafka: - localhost:9092 integrations: - googleGcs: - clientEmail: - $env: GCS_CLIENT_EMAIL - privateKey: - $env: GCS_PRIVATE_KEY - github: - host: github.com token: @@ -156,6 +150,10 @@ integrations: - host: dev.azure.com token: $env: AZURE_TOKEN +# googleGcs: +# clientEmail: +# $env: GCS_CLIENT_EMAIL +# privateKey: 'example@example.com' catalog: rules: diff --git a/packages/backend-common/src/reading/GoogleGcsUrlReader.ts b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts index 80b42dd5da..a05a41e54a 100644 --- a/packages/backend-common/src/reading/GoogleGcsUrlReader.ts +++ b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts @@ -22,10 +22,9 @@ import { UrlReader, } from './types'; import getRawBody from 'raw-body'; -import { - GOOGLE_GCS_HOST, - readGoogleGcsIntegrationConfig, -} from '@backstage/integration'; +import { readGoogleGcsIntegrationConfig } from '@backstage/integration'; + +const GOOGLE_GCS_HOST = 'storage.cloud.google.com'; const parseURL = ( url: string, @@ -81,7 +80,7 @@ export class GoogleGcsUrlReader implements UrlReader { this.storage.bucket(bucket).file(key).createReadStream(), ); } catch (error) { - throw new Error(`unable to read gcs file from ${url}`); + throw new Error(`unable to read gcs file from ${url}, ${error}`); } } diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index 9f9d588f0b..ddc84ae011 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -154,7 +154,7 @@ export interface Config { googleGcs?: { /** * Service account email used to authenticate requests. - * @visibility secret + * @visibility backend */ clientEmail?: string; /** diff --git a/packages/integration/src/googleGcs/index.ts b/packages/integration/src/googleGcs/index.ts index ac85d706b5..d9355fd299 100644 --- a/packages/integration/src/googleGcs/index.ts +++ b/packages/integration/src/googleGcs/index.ts @@ -16,4 +16,3 @@ export { readGoogleGcsIntegrationConfig } from './config'; export type { GoogleGcsIntegrationConfig } from './config'; -export const GOOGLE_GCS_HOST = 'storage.cloud.google.com'; From c410cae0f2e141749653dbab5a2f5d45ba245343 Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Wed, 7 Apr 2021 12:06:07 +0200 Subject: [PATCH 50/61] Fix feedback Signed-off-by: Martina Iglesias Fernandez --- app-config.yaml | 6 +++--- .../src/reading/GoogleGcsUrlReader.ts | 17 ++++++++++++----- packages/integration/src/googleGcs/config.ts | 9 +++------ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 48d58dc837..309fa28234 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -151,9 +151,9 @@ integrations: token: $env: AZURE_TOKEN # googleGcs: -# clientEmail: -# $env: GCS_CLIENT_EMAIL -# privateKey: 'example@example.com' +# clientEmail: 'example@example.com' +# privateKey: +# $env: GCS_PRIVATE_KEY catalog: rules: diff --git a/packages/backend-common/src/reading/GoogleGcsUrlReader.ts b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts index a05a41e54a..4ea24fa22f 100644 --- a/packages/backend-common/src/reading/GoogleGcsUrlReader.ts +++ b/packages/backend-common/src/reading/GoogleGcsUrlReader.ts @@ -22,7 +22,10 @@ import { UrlReader, } from './types'; import getRawBody from 'raw-body'; -import { readGoogleGcsIntegrationConfig } from '@backstage/integration'; +import { + GoogleGcsIntegrationConfig, + readGoogleGcsIntegrationConfig, +} from '@backstage/integration'; const GOOGLE_GCS_HOST = 'storage.cloud.google.com'; @@ -53,7 +56,7 @@ export class GoogleGcsUrlReader implements UrlReader { ); let storage: Storage; if (!gcsConfig.clientEmail || !gcsConfig.privateKey) { - logger.warn( + logger.info( 'googleGcs credentials not found in config. Using default credentials provider.', ); storage = new Storage(); @@ -65,12 +68,15 @@ export class GoogleGcsUrlReader implements UrlReader { }, }); } - const reader = new GoogleGcsUrlReader(storage); + const reader = new GoogleGcsUrlReader(gcsConfig, storage); const predicate = (url: URL) => url.host === GOOGLE_GCS_HOST; return [{ reader, predicate }]; }; - constructor(private readonly storage: Storage) {} + constructor( + private readonly integration: GoogleGcsIntegrationConfig, + private readonly storage: Storage, + ) {} async read(url: string): Promise { try { @@ -93,6 +99,7 @@ export class GoogleGcsUrlReader implements UrlReader { } toString() { - return `gcs{host=${GOOGLE_GCS_HOST},authed=true}}`; + const key = this.integration.privateKey; + return `googleGcs{host=${GOOGLE_GCS_HOST},authed=${Boolean(key)}}`; } } diff --git a/packages/integration/src/googleGcs/config.ts b/packages/integration/src/googleGcs/config.ts index 90a679bb4c..7924138b5a 100644 --- a/packages/integration/src/googleGcs/config.ts +++ b/packages/integration/src/googleGcs/config.ts @@ -42,15 +42,12 @@ export function readGoogleGcsIntegrationConfig( return {}; } - if (!config.has('clientEmail') || !config.has('privateKey')) { + if (!config.has('clientEmail') && !config.has('privateKey')) { return {}; } - const privateKey = config - .getOptionalString('privateKey') - ?.split('\\n') - .join('\n'); + const privateKey = config.getString('privateKey').split('\\n').join('\n'); - const clientEmail = config.getOptionalString('clientEmail'); + const clientEmail = config.getString('clientEmail'); return { clientEmail: clientEmail, privateKey: privateKey }; } From 5136ae0db64e5d759cfcad0429746aaefbf64252 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 7 Apr 2021 16:52:35 +0200 Subject: [PATCH 51/61] Update packages/cli/config/tsconfig.json Signed-off-by: Patrik Oldsberg --- packages/cli/config/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/config/tsconfig.json b/packages/cli/config/tsconfig.json index d249d53f87..f6b4f65edd 100644 --- a/packages/cli/config/tsconfig.json +++ b/packages/cli/config/tsconfig.json @@ -11,7 +11,7 @@ "incremental": true, "isolatedModules": true, "jsx": "react", - "lib": ["DOM", "DOM.Iterable", "ScriptHost", "ES2020", "ES2020.Promise"], + "lib": ["DOM", "DOM.Iterable", "ScriptHost", "ES2020"], "module": "ESNext", "moduleResolution": "node", "noEmit": false, From fa188885177a2fb650182fbacd375d628567fd27 Mon Sep 17 00:00:00 2001 From: Martina Iglesias Fernandez Date: Wed, 7 Apr 2021 18:00:24 +0200 Subject: [PATCH 52/61] Fix sidebar docs for GCS integration Signed-off-by: Martina Iglesias Fernandez --- .../google-cloud-storage/{gcs.md => locations.md} | 6 +++--- microsite/sidebars.json | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) rename docs/integrations/google-cloud-storage/{gcs.md => locations.md} (95%) diff --git a/docs/integrations/google-cloud-storage/gcs.md b/docs/integrations/google-cloud-storage/locations.md similarity index 95% rename from docs/integrations/google-cloud-storage/gcs.md rename to docs/integrations/google-cloud-storage/locations.md index 1d46c57b10..e3f6b72847 100644 --- a/docs/integrations/google-cloud-storage/gcs.md +++ b/docs/integrations/google-cloud-storage/locations.md @@ -1,7 +1,7 @@ --- -id: googlegcs -title: Google Cloud Storage -sidebar_label: Google Cloud Storage Data +id: locations +sidebar_label: Locations +title: Google Cloud Storage Locations # prettier-ignore description: Setting up an integration with Google Cloud Storage --- diff --git a/microsite/sidebars.json b/microsite/sidebars.json index b719f9329c..5f3015582d 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -139,6 +139,11 @@ "type": "subcategory", "label": "LDAP", "ids": ["integrations/ldap/org"] + }, + { + "type": "subcategory", + "label": "Google GCS", + "ids": ["integrations/google-cloud-storage/locations"] } ], "Plugins": [ From 38726cd9a79a8d922b6e44ed666eff4985490838 Mon Sep 17 00:00:00 2001 From: Jeff Cook Date: Wed, 7 Apr 2021 23:22:30 +0000 Subject: [PATCH 53/61] Teach SonarQube `isPluginApplicableToEntity`. Signed-off-by: Jeff Cook --- plugins/sonarqube/src/components/index.ts | 1 + plugins/sonarqube/src/components/useProjectKey.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/plugins/sonarqube/src/components/index.ts b/plugins/sonarqube/src/components/index.ts index 8f0786bb8e..8ff9a77485 100644 --- a/plugins/sonarqube/src/components/index.ts +++ b/plugins/sonarqube/src/components/index.ts @@ -15,3 +15,4 @@ */ export * from './SonarQubeCard'; +export { isSonarQubeAvailable as isPluginApplicableToEntity } from './useProjectKey'; diff --git a/plugins/sonarqube/src/components/useProjectKey.ts b/plugins/sonarqube/src/components/useProjectKey.ts index dcff79d972..c0f488b00c 100644 --- a/plugins/sonarqube/src/components/useProjectKey.ts +++ b/plugins/sonarqube/src/components/useProjectKey.ts @@ -18,6 +18,9 @@ import { Entity } from '@backstage/catalog-model'; export const SONARQUBE_PROJECT_KEY_ANNOTATION = 'sonarqube.org/project-key'; +export const isSonarQubeAvailable = (entity: Entity) => + Boolean(entity.metadata.annotations?.[SONARQUBE_PROJECT_KEY_ANNOTATION]); + export const useProjectKey = (entity: Entity) => { return entity?.metadata.annotations?.[SONARQUBE_PROJECT_KEY_ANNOTATION] ?? ''; }; From db802fafbde016614982fbb38a82c9d54ca70bfb Mon Sep 17 00:00:00 2001 From: Jeff Cook Date: Wed, 7 Apr 2021 23:35:10 +0000 Subject: [PATCH 54/61] Include changeset for exporting `isPluginAvailableToEntity` in sonarqube Signed-off-by: Jeff Cook --- .changeset/chatty-ghosts-lay.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chatty-ghosts-lay.md diff --git a/.changeset/chatty-ghosts-lay.md b/.changeset/chatty-ghosts-lay.md new file mode 100644 index 0000000000..472975e826 --- /dev/null +++ b/.changeset/chatty-ghosts-lay.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-sonarqube': patch +--- + +Export isPluginAvailableToEntity From 413b98ff7d3d1b2642715ed06f97982fd9f28d96 Mon Sep 17 00:00:00 2001 From: Jeff Cook Date: Wed, 7 Apr 2021 23:46:02 +0000 Subject: [PATCH 55/61] Fix changelog description. Signed-off-by: Jeff Cook --- .changeset/chatty-ghosts-lay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/chatty-ghosts-lay.md b/.changeset/chatty-ghosts-lay.md index 472975e826..26a83f89f9 100644 --- a/.changeset/chatty-ghosts-lay.md +++ b/.changeset/chatty-ghosts-lay.md @@ -2,4 +2,4 @@ '@backstage/plugin-sonarqube': patch --- -Export isPluginAvailableToEntity +Export isPluginApplicableToEntity From d9e9cb65df5e8bf5ff821c27db09fe6ea318577f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Apr 2021 04:09:36 +0000 Subject: [PATCH 56/61] chore(deps-dev): bump @types/html-webpack-plugin from 3.2.4 to 3.2.5 Bumps [@types/html-webpack-plugin](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/html-webpack-plugin) from 3.2.4 to 3.2.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/html-webpack-plugin) Signed-off-by: dependabot[bot] --- yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index e335f31c18..6799b78105 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6286,13 +6286,13 @@ "@types/uglify-js" "*" "@types/html-webpack-plugin@*", "@types/html-webpack-plugin@^3.2.2": - version "3.2.4" - resolved "https://registry.npmjs.org/@types/html-webpack-plugin/-/html-webpack-plugin-3.2.4.tgz#ed770ddfec53ed2aa6b5f4523acca291192235c6" - integrity sha512-WM0s78bfCIXnTlICf+8nWP0IvP+fn4YfiI3uxAX1K1PSRpzs0iysp03j4zR0xTgxSqF67TbOsHs49YXonRAkeQ== + version "3.2.5" + resolved "https://registry.npmjs.org/@types/html-webpack-plugin/-/html-webpack-plugin-3.2.5.tgz#58e94c0d57801903b2b77674d2b9ef6c4a65a6db" + integrity sha512-DhC7NTte+Ikw/zxp2w9qjcWtHqpShbUx7ASPUZ00trn1EOftoRtMmy8nS7F/mW8ASTA2JGMFX2bbuqqxiqs6mQ== dependencies: "@types/html-minifier" "*" - "@types/tapable" "*" - "@types/webpack" "*" + "@types/tapable" "^1" + "@types/webpack" "^4" "@types/http-assert@*": version "1.5.1" @@ -7029,10 +7029,10 @@ dependencies: "@types/react" "*" -"@types/tapable@*", "@types/tapable@^1.0.5": - version "1.0.5" - resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" - integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== +"@types/tapable@^1", "@types/tapable@^1.0.5": + version "1.0.7" + resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.7.tgz#545158342f949e8fd3bfd813224971ecddc3fac4" + integrity sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ== "@types/tar@^4.0.3": version "4.0.4" @@ -7161,14 +7161,14 @@ "@types/source-list-map" "*" source-map "^0.6.1" -"@types/webpack@*", "@types/webpack@^4.41.7", "@types/webpack@^4.41.8": - version "4.41.26" - resolved "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.26.tgz#27a30d7d531e16489f9c7607c747be6bc1a459ef" - integrity sha512-7ZyTfxjCRwexh+EJFwRUM+CDB2XvgHl4vfuqf1ZKrgGvcS5BrNvPQqJh3tsZ0P6h6Aa1qClVHaJZszLPzpqHeA== +"@types/webpack@*", "@types/webpack@^4", "@types/webpack@^4.41.7", "@types/webpack@^4.41.8": + version "4.41.27" + resolved "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.27.tgz#f47da488c8037e7f1b2dbf2714fbbacb61ec0ffc" + integrity sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA== dependencies: "@types/anymatch" "*" "@types/node" "*" - "@types/tapable" "*" + "@types/tapable" "^1" "@types/uglify-js" "*" "@types/webpack-sources" "*" source-map "^0.6.0" From a360f947881bd5755f57514d5611506689348c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20A=CC=8Ahsberg?= Date: Wed, 7 Apr 2021 11:52:23 +0000 Subject: [PATCH 57/61] Expose the register-component route as an external route from the scaffolder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mathias AÌŠhsberg --- .changeset/loud-apricots-fix.md | 31 +++++++++++++++++++ packages/app/src/App.tsx | 9 +++++- .../default-app/packages/app/src/App.tsx | 5 ++- .../ScaffolderPage/ScaffolderPage.tsx | 22 ++++++++----- plugins/scaffolder/src/plugin.ts | 5 ++- plugins/scaffolder/src/routes.ts | 7 ++++- 6 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 .changeset/loud-apricots-fix.md diff --git a/.changeset/loud-apricots-fix.md b/.changeset/loud-apricots-fix.md new file mode 100644 index 0000000000..3763ebf0e3 --- /dev/null +++ b/.changeset/loud-apricots-fix.md @@ -0,0 +1,31 @@ +--- +'@backstage/create-app': patch +'@backstage/plugin-scaffolder': minor +--- + +Expose the catalog-import route as an external route from the scaffolder. + +This will make it possible to hide the "Register Existing Component" button +when you for example are running backstage with `catalog.readonly=true`. + +As a consequence of this change you need add a new binding to your createApp call to +keep the button visible. However, if you instead want to hide the button you can safely +ignore the following example. + +To bind the external route from the catalog-import plugin to the scaffolder template +index page, make sure you have the appropriate imports and add the following +to the createApp call: + +```typescript +import { catalogImportPlugin } from '@backstage/plugin-catalog-import'; + +const app = createApp({ + // ... + bindRoutes({ bind }) { + // ... + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + }); + }, +}); +``` diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index defa70dbb7..97136637d9 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -27,7 +27,11 @@ import { CatalogIndexPage, catalogPlugin, } from '@backstage/plugin-catalog'; -import { CatalogImportPage } from '@backstage/plugin-catalog-import'; + +import { + CatalogImportPage, + catalogImportPlugin, +} from '@backstage/plugin-catalog-import'; import { CostInsightsLabelDataflowInstructionsPage, CostInsightsPage, @@ -82,6 +86,9 @@ const app = createApp({ bind(explorePlugin.externalRoutes, { catalogEntity: catalogPlugin.routes.catalogEntity, }); + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + }); }, }); diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 1cd7b77965..ae7a43eb58 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 @@ -12,7 +12,7 @@ import { CatalogIndexPage, catalogPlugin, } from '@backstage/plugin-catalog'; -import { CatalogImportPage } from '@backstage/plugin-catalog-import'; +import {CatalogImportPage, catalogImportPlugin} from '@backstage/plugin-catalog-import'; import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { SearchPage } from '@backstage/plugin-search'; import { TechRadarPage } from '@backstage/plugin-tech-radar'; @@ -33,6 +33,9 @@ const app = createApp({ bind(apiDocsPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, }); + bind(scaffolderPlugin.externalRoutes, { + registerComponent: catalogImportPlugin.routes.importPage, + }); }, }); diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 0353dff798..68bf588d01 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -26,6 +26,7 @@ import { Progress, SupportButton, useApi, + useRouteRef, WarningPanel, } from '@backstage/core'; import { useStarredEntities } from '@backstage/plugin-catalog-react'; @@ -39,6 +40,7 @@ import { ScaffolderFilter } from '../ScaffolderFilter'; import { ButtonGroup } from '../ScaffolderFilter/ScaffolderFilter'; import SearchToolbar from '../SearchToolbar/SearchToolbar'; import { TemplateCard, TemplateCardProps } from '../TemplateCard'; +import { registerComponentRouteRef } from '../../routes'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -108,6 +110,8 @@ export const ScaffolderPageContents = () => { `${metadata.title}`.toLocaleUpperCase('en-US').includes(query) || metadata.tags?.join('').toLocaleUpperCase('en-US').indexOf(query) !== -1; + const registerComponentLink = useRouteRef(registerComponentRouteRef); + useEffect(() => { if (search.length === 0) { return setMatchingEntities(filteredEntities); @@ -132,14 +136,16 @@ export const ScaffolderPageContents = () => { /> - + {registerComponentLink && ( + + )} Create new software components using standard templates. Different templates create different kinds of components (services, websites, diff --git a/plugins/scaffolder/src/plugin.ts b/plugins/scaffolder/src/plugin.ts index 1aa25d8e18..be47daec46 100644 --- a/plugins/scaffolder/src/plugin.ts +++ b/plugins/scaffolder/src/plugin.ts @@ -23,7 +23,7 @@ import { } from '@backstage/core'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { scaffolderApiRef, ScaffolderClient } from './api'; -import { rootRouteRef } from './routes'; +import { rootRouteRef, registerComponentRouteRef } from './routes'; export const scaffolderPlugin = createPlugin({ id: 'scaffolder', @@ -42,6 +42,9 @@ export const scaffolderPlugin = createPlugin({ routes: { root: rootRouteRef, }, + externalRoutes: { + registerComponent: registerComponentRouteRef, + }, }); export const ScaffolderPage = scaffolderPlugin.provide( diff --git a/plugins/scaffolder/src/routes.ts b/plugins/scaffolder/src/routes.ts index 413e8f4194..3cca269b84 100644 --- a/plugins/scaffolder/src/routes.ts +++ b/plugins/scaffolder/src/routes.ts @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { createRouteRef } from '@backstage/core'; +import { createExternalRouteRef, createRouteRef } from '@backstage/core'; + +export const registerComponentRouteRef = createExternalRouteRef({ + id: 'register-component', + optional: true, +}); export const rootRouteRef = createRouteRef({ title: 'Create new entity', From 8664d122b9ae5a6a0d53d1cdc220034cd15e3e65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Apr 2021 11:08:31 +0000 Subject: [PATCH 58/61] chore(deps): bump pg from 8.4.2 to 8.5.1 Bumps [pg](https://github.com/brianc/node-postgres) from 8.4.2 to 8.5.1. - [Release notes](https://github.com/brianc/node-postgres/releases) - [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md) - [Commits](https://github.com/brianc/node-postgres/compare/pg@8.4.2...pg@8.5.1) Signed-off-by: dependabot[bot] --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 94a60e108f..04f194de4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20638,10 +20638,10 @@ pg-pool@^3.2.2: resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz#a560e433443ed4ad946b84d774b3f22452694dff" integrity sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA== -pg-protocol@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.3.0.tgz#3c8fb7ca34dbbfcc42776ce34ac5f537d6e34770" - integrity sha512-64/bYByMrhWULUaCd+6/72c9PMWhiVFs3EVxl9Ct6a3v/U8+rKgqP2w+kKg/BIGgMJyB+Bk/eNivT32Al+Jghw== +pg-protocol@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz#43a71a92f6fe3ac559952555aa3335c8cb4908be" + integrity sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA== pg-types@1.*: version "1.13.0" @@ -20680,15 +20680,15 @@ pg@^6.1.0: semver "4.3.2" pg@^8.3.0: - version "8.4.2" - resolved "https://registry.npmjs.org/pg/-/pg-8.4.2.tgz#2aa58166a23391e91d56a7ea57c6d99931c0642a" - integrity sha512-E9FlUrrc7w3+sbRmL1CSw99vifACzB2TjhMM9J5w9D1LIg+6un0jKkpHS1EQf2CWhKhec2bhrBLVMmUBDbjPRQ== + version "8.5.1" + resolved "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz#34dcb15f6db4a29c702bf5031ef2e1e25a06a120" + integrity sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" pg-connection-string "^2.4.0" pg-pool "^3.2.2" - pg-protocol "^1.3.0" + pg-protocol "^1.4.0" pg-types "^2.1.0" pgpass "1.x" From b02bc50d8a923c9a4b27bbac6857155f4c309cbe Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 8 Apr 2021 13:21:05 +0200 Subject: [PATCH 59/61] changesets: fix the fml Signed-off-by: Patrik Oldsberg --- .changeset/warm-rivers-dance.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.changeset/warm-rivers-dance.md b/.changeset/warm-rivers-dance.md index d128ddeb9e..283c78cd61 100644 --- a/.changeset/warm-rivers-dance.md +++ b/.changeset/warm-rivers-dance.md @@ -1,6 +1,5 @@ --- +--- '@backstage/create-app': patch - --- **Fully migrated the template to the new composability API** From 51b6cf465d1a573d205b4f59cbfc347d8d6e150a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 Apr 2021 11:32:56 +0000 Subject: [PATCH 60/61] Version Packages --- .changeset/bright-lions-camp.md | 5 - .changeset/eight-files-rhyme.md | 6 - .changeset/fair-geese-yell.md | 15 -- .changeset/forty-ladybugs-move.md | 5 - .changeset/forty-queens-hear.md | 7 - .changeset/funny-donkeys-jam.md | 6 - .changeset/little-starfishes-whisper.md | 5 - .changeset/mighty-bats-nail.md | 5 - .changeset/neat-lions-peel.md | 6 - .changeset/old-spies-love.md | 5 - .changeset/polite-cameras-sin.md | 5 - .changeset/sharp-rings-march.md | 8 - .changeset/sour-clocks-pay.md | 5 - .changeset/stale-geese-rule.md | 5 - .changeset/thirty-panthers-learn.md | 5 - .changeset/warm-rivers-dance.md | 187 --------------------- packages/app/CHANGELOG.md | 24 +++ packages/app/package.json | 22 +-- packages/backend-common/CHANGELOG.md | 8 + packages/backend-common/package.json | 8 +- packages/catalog-model/CHANGELOG.md | 7 + packages/catalog-model/package.json | 4 +- packages/cli/CHANGELOG.md | 7 + packages/cli/package.json | 10 +- packages/config-loader/CHANGELOG.md | 16 ++ packages/config-loader/package.json | 2 +- packages/core-api/CHANGELOG.md | 9 + packages/core-api/package.json | 6 +- packages/core/CHANGELOG.md | 14 ++ packages/core/package.json | 8 +- packages/create-app/CHANGELOG.md | 211 ++++++++++++++++++++++++ packages/create-app/package.json | 2 +- packages/integration/package.json | 6 +- packages/test-utils/CHANGELOG.md | 10 ++ packages/test-utils/package.json | 6 +- plugins/api-docs/package.json | 6 +- plugins/app-backend/CHANGELOG.md | 9 + plugins/app-backend/package.json | 8 +- plugins/auth-backend/CHANGELOG.md | 13 ++ plugins/auth-backend/package.json | 10 +- plugins/badges-backend/CHANGELOG.md | 12 ++ plugins/badges-backend/package.json | 8 +- plugins/badges/CHANGELOG.md | 16 ++ plugins/badges/package.json | 10 +- plugins/bitrise/package.json | 6 +- plugins/catalog-import/CHANGELOG.md | 13 ++ plugins/catalog-import/package.json | 10 +- plugins/catalog/CHANGELOG.md | 13 ++ plugins/catalog/package.json | 10 +- plugins/circleci/package.json | 6 +- plugins/cloudbuild/package.json | 6 +- plugins/cost-insights/package.json | 6 +- plugins/explore/package.json | 6 +- plugins/fossa/package.json | 6 +- plugins/gcp-projects/package.json | 6 +- plugins/github-actions/package.json | 6 +- plugins/github-deployments/CHANGELOG.md | 14 ++ plugins/github-deployments/package.json | 10 +- plugins/gitops-profiles/package.json | 6 +- plugins/graphiql/package.json | 6 +- plugins/jenkins/package.json | 6 +- plugins/kafka/package.json | 6 +- plugins/kubernetes/package.json | 6 +- plugins/lighthouse/package.json | 6 +- plugins/newrelic/package.json | 6 +- plugins/org/CHANGELOG.md | 14 ++ plugins/org/package.json | 12 +- plugins/pagerduty/package.json | 6 +- plugins/register-component/package.json | 6 +- plugins/rollbar/package.json | 6 +- plugins/scaffolder-backend/CHANGELOG.md | 11 ++ plugins/scaffolder-backend/package.json | 10 +- plugins/scaffolder/CHANGELOG.md | 14 ++ plugins/scaffolder/package.json | 10 +- plugins/search/package.json | 6 +- plugins/sentry/package.json | 6 +- plugins/sonarqube/package.json | 6 +- plugins/splunk-on-call/package.json | 6 +- plugins/tech-radar/package.json | 6 +- plugins/techdocs/package.json | 6 +- plugins/todo/package.json | 6 +- plugins/user-settings/package.json | 6 +- plugins/welcome/package.json | 6 +- 83 files changed, 605 insertions(+), 450 deletions(-) delete mode 100644 .changeset/bright-lions-camp.md delete mode 100644 .changeset/eight-files-rhyme.md delete mode 100644 .changeset/fair-geese-yell.md delete mode 100644 .changeset/forty-ladybugs-move.md delete mode 100644 .changeset/forty-queens-hear.md delete mode 100644 .changeset/funny-donkeys-jam.md delete mode 100644 .changeset/little-starfishes-whisper.md delete mode 100644 .changeset/mighty-bats-nail.md delete mode 100644 .changeset/neat-lions-peel.md delete mode 100644 .changeset/old-spies-love.md delete mode 100644 .changeset/polite-cameras-sin.md delete mode 100644 .changeset/sharp-rings-march.md delete mode 100644 .changeset/sour-clocks-pay.md delete mode 100644 .changeset/stale-geese-rule.md delete mode 100644 .changeset/thirty-panthers-learn.md delete mode 100644 .changeset/warm-rivers-dance.md create mode 100644 plugins/badges-backend/CHANGELOG.md create mode 100644 plugins/github-deployments/CHANGELOG.md diff --git a/.changeset/bright-lions-camp.md b/.changeset/bright-lions-camp.md deleted file mode 100644 index fee5d5b5fc..0000000000 --- a/.changeset/bright-lions-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Allow custom directory to be specified for GitHub publish action diff --git a/.changeset/eight-files-rhyme.md b/.changeset/eight-files-rhyme.md deleted file mode 100644 index c45556366d..0000000000 --- a/.changeset/eight-files-rhyme.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-badges': minor -'@backstage/plugin-badges-backend': patch ---- - -Support auth in badge plugin diff --git a/.changeset/fair-geese-yell.md b/.changeset/fair-geese-yell.md deleted file mode 100644 index 00c5b16854..0000000000 --- a/.changeset/fair-geese-yell.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -'@backstage/config-loader': minor ---- - -Fix bug where `$${...}` was not being escaped to `${...}` - -Add support for environment variable substitution in `$include`, `$file` and -`$env` transform values. - -- This change allows for including dynamic paths, such as environment specific - secrets by using the same environment variable substitution (`${..}`) already - supported outside of the various include transforms. -- If you are currently using the syntax `${...}` in your include transform values, - you will need to escape the substitution by using `$${...}` instead to maintain - the same behavior. diff --git a/.changeset/forty-ladybugs-move.md b/.changeset/forty-ladybugs-move.md deleted file mode 100644 index 254efea584..0000000000 --- a/.changeset/forty-ladybugs-move.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-auth-backend': patch ---- - -When using OAuth2 authentication the name is now taken from the name property of the JWT instead of the email property diff --git a/.changeset/forty-queens-hear.md b/.changeset/forty-queens-hear.md deleted file mode 100644 index 5e0b35162a..0000000000 --- a/.changeset/forty-queens-hear.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/core': patch -'@backstage/core-api': patch ---- - -Introduce a `load-chunk` step in the `BootErrorPage` to show make chunk loading -errors visible to the user. diff --git a/.changeset/funny-donkeys-jam.md b/.changeset/funny-donkeys-jam.md deleted file mode 100644 index 0c45d4b5ff..0000000000 --- a/.changeset/funny-donkeys-jam.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-api': patch -'@backstage/core': patch ---- - -Improved error messaging for routable extension errors, making it easier to identify the component and mount point that caused the error. diff --git a/.changeset/little-starfishes-whisper.md b/.changeset/little-starfishes-whisper.md deleted file mode 100644 index ea00753b4f..0000000000 --- a/.changeset/little-starfishes-whisper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': patch ---- - -Added `stringifyEntityRef`, which always creates a string representation of an entity reference. Also deprecated `serializeEntityRef`, as `stringifyEntityRef` should be used instead. diff --git a/.changeset/mighty-bats-nail.md b/.changeset/mighty-bats-nail.md deleted file mode 100644 index 1fe2c0ee1d..0000000000 --- a/.changeset/mighty-bats-nail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Support auth by sending cookies in event stream request diff --git a/.changeset/neat-lions-peel.md b/.changeset/neat-lions-peel.md deleted file mode 100644 index 1cd50641ec..0000000000 --- a/.changeset/neat-lions-peel.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-api': patch -'@backstage/core': patch ---- - -Fixed a bug with `useRouteRef` where navigating from routes beneath a mount point would often fail. diff --git a/.changeset/old-spies-love.md b/.changeset/old-spies-love.md deleted file mode 100644 index 722d9d937e..0000000000 --- a/.changeset/old-spies-love.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-import': patch ---- - -When importing components you will now have the ability to use non Unicode characters in the entity owner field diff --git a/.changeset/polite-cameras-sin.md b/.changeset/polite-cameras-sin.md deleted file mode 100644 index b7989be2c6..0000000000 --- a/.changeset/polite-cameras-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/test-utils': patch ---- - -Remove unnecessary wrapping of elements rendered by `wrapInTestApp` and `renderInTestApp`, which was breaking mount discovery. diff --git a/.changeset/sharp-rings-march.md b/.changeset/sharp-rings-march.md deleted file mode 100644 index 81ea9aa9df..0000000000 --- a/.changeset/sharp-rings-march.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/catalog-model': patch -'@backstage/core': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-scaffolder': patch ---- - -Add support for multiple links to post-scaffold task summary page diff --git a/.changeset/sour-clocks-pay.md b/.changeset/sour-clocks-pay.md deleted file mode 100644 index 11196aeebe..0000000000 --- a/.changeset/sour-clocks-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Add UrlReader for Google Cloud Storage diff --git a/.changeset/stale-geese-rule.md b/.changeset/stale-geese-rule.md deleted file mode 100644 index f22ca2983d..0000000000 --- a/.changeset/stale-geese-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-org': patch ---- - -Optimize data fetched for the `OwnershipCard`. diff --git a/.changeset/thirty-panthers-learn.md b/.changeset/thirty-panthers-learn.md deleted file mode 100644 index f0e2c05e81..0000000000 --- a/.changeset/thirty-panthers-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-github-deployments': patch ---- - -Add a button to reload the GitHub Deployments card diff --git a/.changeset/warm-rivers-dance.md b/.changeset/warm-rivers-dance.md deleted file mode 100644 index 283c78cd61..0000000000 --- a/.changeset/warm-rivers-dance.md +++ /dev/null @@ -1,187 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -**Fully migrated the template to the new composability API** - -The `create-app` template is now fully migrated to the new composability API, see [Composability System Migration Documentation](https://backstage.io/docs/plugins/composability) for explanations and more details. The final change which is now done was to migrate the `EntityPage` from being a component built on top of the `EntityPageLayout` and several more custom components, to an element tree built with `EntitySwitch` and `EntityLayout`. - -To apply this change to an existing plugin, it is important that all plugins that you are using have already been migrated. In this case the most crucial piece is that no entity page cards of contents may require the `entity` prop, and they must instead consume the entity from context using `useEntity`. - -Since this change is large with a lot of repeated changes, we'll describe a couple of common cases rather than the entire change. If your entity pages are unchanged from the `create-app` template, you can also just bring in the latest version directly from the [template itself](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx). - -The first step of the change is to change the `packages/app/src/components/catalog/EntityPage.tsx` export to `entityPage` rather than `EntityPage`. This will require an update to `App.tsx`, which is the only change we need to do outside of `EntityPage.tsx`: - -```diff --import { EntityPage } from './components/catalog/EntityPage'; -+import { entityPage } from './components/catalog/EntityPage'; - - } - > -- -+ {entityPage} - -``` - -The rest of the changes happen within `EntityPage.tsx`, and can be split into two broad categories, updating page components, and updating switch components. - -#### Migrating Page Components - -Let's start with an example of migrating a user page component. The following is the old code in the template: - -```tsx -const UserOverviewContent = ({ entity }: { entity: UserEntity }) => ( - - - - - - - - -); - -const UserEntityPage = ({ entity }: { entity: Entity }) => ( - - } - /> - -); -``` - -There's the main `UserEntityPage` component, and the `UserOverviewContent` component. Let's start with migrating the page contents, which we do by rendering an element rather than creating a component, as well as replace the cards with their new composability compatible variants. The new cards and content components can be identified by the `Entity` prefix. - -```tsx -const userOverviewContent = ( - - - - - - - - -); -``` - -Now let's migrate the page component, again by converting it into a rendered element instead of a component, as well as replacing the use of `EntityPageLayout` with `EntityLayout`. - -```tsx -const userPage = ( - - - {userOverviewContent} - - -); -``` - -At this point the `userPage` is quite small, so throughout this migration we have inlined the page contents for all pages. This is an optional step, but may help reduce noise. The final page now looks like this: - -```tsx -const userPage = ( - - - - - - - - - - - - -); -``` - -#### Migrating Switch Components - -Switch components were used to select what entity page components or cards to render, based on for example the kind of entity. For this example we'll focus on the root `EntityPage` switch component, but the process is the same for example for the CI/CD switcher. - -The old `EntityPage` looked like this: - -```tsx -export const EntityPage = () => { - const { entity } = useEntity(); - - switch (entity?.kind?.toLocaleLowerCase('en-US')) { - case 'component': - return ; - case 'api': - return ; - case 'group': - return ; - case 'user': - return ; - case 'system': - return ; - case 'domain': - return ; - case 'location': - case 'resource': - case 'template': - default: - return ; - } -}; -``` - -In order to migrate to the composability API, we need to make this an element instead of a component, which means we're unable to keep the switch statement as is. To help with this, the catalog plugin provides an `EntitySwitch` component, which functions similar to a regular `switch` statement, which the first match being the one that is rendered. The catalog plugin also provides a number of built-in filter functions to use, such as `isKind` and `isComponentType`. - -To migrate the `EntityPage`, we convert the `switch` statement into an `EntitySwitch` element, and each `case` statement into an `EntitySwitch.Case` element. We also move over to use our new element version of the page components, with the result looking like this: - -```tsx -export const entityPage = ( - - - - - - - - - {defaultEntityPage} - -); -``` - -Another example is the `ComponentEntityPage`, which is migrated from this: - -```tsx -export const ComponentEntityPage = ({ entity }: { entity: Entity }) => { - switch (entity?.spec?.type) { - case 'service': - return ; - case 'website': - return ; - default: - return ; - } -}; -``` - -To this: - -```tsx -const componentPage = ( - - - {serviceEntityPage} - - - - {websiteEntityPage} - - - {defaultEntityPage} - -); -``` - -Note that if you want to conditionally render some piece of content, you can omit the default `EntitySwitch.Case`. If no case is matched in an `EntitySwitch`, nothing will be rendered. diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 00cf81a903..5b96841e65 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,29 @@ # example-app +## 0.2.23 + +### Patch Changes + +- Updated dependencies [d0b4ebf22] +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [3f96a9d5a] +- Updated dependencies [b051e770c] +- Updated dependencies [f9c75f7a9] +- Updated dependencies [98dd5da71] +- Updated dependencies [97d53f686] +- Updated dependencies [64d2ce700] + - @backstage/plugin-badges@0.2.0 + - @backstage/core@0.7.4 + - @backstage/catalog-model@0.7.6 + - @backstage/plugin-scaffolder@0.8.2 + - @backstage/plugin-catalog-import@0.5.2 + - @backstage/plugin-catalog@0.5.3 + - @backstage/plugin-org@0.3.12 + - @backstage/plugin-github-deployments@0.1.2 + - @backstage/cli@0.6.7 + ## 0.2.21 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index d00ddefc76..2bac2e6e09 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,17 +1,17 @@ { "name": "example-app", - "version": "0.2.21", + "version": "0.2.23", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.7.4", - "@backstage/cli": "^0.6.6", - "@backstage/core": "^0.7.3", + "@backstage/catalog-model": "^0.7.6", + "@backstage/cli": "^0.6.7", + "@backstage/core": "^0.7.4", "@backstage/integration-react": "^0.1.1", "@backstage/plugin-api-docs": "^0.4.9", - "@backstage/plugin-badges": "^0.1.2", - "@backstage/plugin-catalog": "^0.5.1", - "@backstage/plugin-catalog-import": "^0.5.0", + "@backstage/plugin-badges": "^0.2.0", + "@backstage/plugin-catalog": "^0.5.3", + "@backstage/plugin-catalog-import": "^0.5.2", "@backstage/plugin-catalog-react": "^0.1.3", "@backstage/plugin-circleci": "^0.2.12", "@backstage/plugin-cloudbuild": "^0.2.13", @@ -19,7 +19,7 @@ "@backstage/plugin-explore": "^0.3.2", "@backstage/plugin-gcp-projects": "^0.2.5", "@backstage/plugin-github-actions": "^0.4.2", - "@backstage/plugin-github-deployments": "^0.1.1", + "@backstage/plugin-github-deployments": "^0.1.2", "@backstage/plugin-gitops-profiles": "^0.2.6", "@backstage/plugin-graphiql": "^0.2.9", "@backstage/plugin-jenkins": "^0.4.1", @@ -27,11 +27,11 @@ "@backstage/plugin-kubernetes": "^0.4.2", "@backstage/plugin-lighthouse": "^0.2.14", "@backstage/plugin-newrelic": "^0.2.6", - "@backstage/plugin-org": "^0.3.10", + "@backstage/plugin-org": "^0.3.12", "@backstage/plugin-pagerduty": "0.3.2", "@backstage/plugin-register-component": "^0.2.12", "@backstage/plugin-rollbar": "^0.3.3", - "@backstage/plugin-scaffolder": "^0.8.0", + "@backstage/plugin-scaffolder": "^0.8.2", "@backstage/plugin-search": "^0.3.4", "@backstage/plugin-sentry": "^0.3.8", "@backstage/plugin-tech-radar": "^0.3.8", @@ -57,7 +57,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/cypress": "^7.0.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index dfe057da53..c194f7e666 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/backend-common +## 0.6.2 + +### Patch Changes + +- b779b5fee: Add UrlReader for Google Cloud Storage +- Updated dependencies [82c66b8cd] + - @backstage/config-loader@0.6.0 + ## 0.6.1 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index ba846e0f5f..41fa657818 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.6.1", + "version": "0.6.2", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -31,7 +31,7 @@ "dependencies": { "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.4", - "@backstage/config-loader": "^0.5.1", + "@backstage/config-loader": "^0.6.0", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.1", "@google-cloud/storage": "^5.8.0", @@ -73,8 +73,8 @@ } }, "devDependencies": { - "@backstage/cli": "^0.6.5", - "@backstage/test-utils": "^0.1.9", + "@backstage/cli": "^0.6.7", + "@backstage/test-utils": "^0.1.10", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", "@types/concat-stream": "^1.6.0", diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 0ad5d6993f..71a0ba6676 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/catalog-model +## 0.7.6 + +### Patch Changes + +- 97b60de98: Added `stringifyEntityRef`, which always creates a string representation of an entity reference. Also deprecated `serializeEntityRef`, as `stringifyEntityRef` should be used instead. +- 98dd5da71: Add support for multiple links to post-scaffold task summary page + ## 0.7.5 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 1e5239cc23..680c73e905 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.7.5", + "version": "0.7.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -39,7 +39,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.4", + "@backstage/cli": "^0.6.7", "@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 c29669f31c..f9dc749009 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/cli +## 0.6.7 + +### Patch Changes + +- Updated dependencies [82c66b8cd] + - @backstage/config-loader@0.6.0 + ## 0.6.6 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 01306eba2a..22b116e87c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.6.6", + "version": "0.6.7", "private": false, "publishConfig": { "access": "public" @@ -32,7 +32,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.4.4", "@backstage/cli-common": "^0.1.1", "@backstage/config": "^0.1.4", - "@backstage/config-loader": "^0.5.1", + "@backstage/config-loader": "^0.6.0", "@hot-loader/react-dom": "^16.13.0", "@lerna/package-graph": "^4.0.0", "@lerna/project": "^4.0.0", @@ -116,11 +116,11 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/backend-common": "^0.6.0", + "@backstage/backend-common": "^0.6.2", "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@backstage/theme": "^0.2.5", "@types/diff": "^5.0.0", "@types/express": "^4.17.6", diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index cbc6473286..cb8ef123ce 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/config-loader +## 0.6.0 + +### Minor Changes + +- 82c66b8cd: Fix bug where `${...}` was not being escaped to `${...}` + + Add support for environment variable substitution in `$include`, `$file` and + `$env` transform values. + + - This change allows for including dynamic paths, such as environment specific + secrets by using the same environment variable substitution (`${..}`) already + supported outside of the various include transforms. + - If you are currently using the syntax `${...}` in your include transform values, + you will need to escape the substitution by using `${...}` instead to maintain + the same behavior. + ## 0.5.1 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index a54975c6ff..0a0a76c2d6 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "0.5.1", + "version": "0.6.0", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-api/CHANGELOG.md b/packages/core-api/CHANGELOG.md index f8f38cbf59..609940b8db 100644 --- a/packages/core-api/CHANGELOG.md +++ b/packages/core-api/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/core-api +## 0.2.16 + +### Patch Changes + +- 1279a3325: Introduce a `load-chunk` step in the `BootErrorPage` to show make chunk loading + errors visible to the user. +- 4a4681b1b: Improved error messaging for routable extension errors, making it easier to identify the component and mount point that caused the error. +- b051e770c: Fixed a bug with `useRouteRef` where navigating from routes beneath a mount point would often fail. + ## 0.2.15 ### Patch Changes diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 411fa44c0d..33e5ac146d 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-api", "description": "Internal Core API used by Backstage plugins and apps", - "version": "0.2.15", + "version": "0.2.16", "private": false, "publishConfig": { "access": "public", @@ -42,8 +42,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.6", - "@backstage/test-utils": "^0.1.9", + "@backstage/cli": "^0.6.7", + "@backstage/test-utils": "^0.1.10", "@backstage/test-utils-core": "^0.1.1", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 45f26804a5..519a43788d 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/core +## 0.7.4 + +### Patch Changes + +- 1279a3325: Introduce a `load-chunk` step in the `BootErrorPage` to show make chunk loading + errors visible to the user. +- 4a4681b1b: Improved error messaging for routable extension errors, making it easier to identify the component and mount point that caused the error. +- b051e770c: Fixed a bug with `useRouteRef` where navigating from routes beneath a mount point would often fail. +- 98dd5da71: Add support for multiple links to post-scaffold task summary page +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [b051e770c] + - @backstage/core-api@0.2.16 + ## 0.7.3 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 051bacc4c2..d5f9042d0e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core", "description": "Core API used by Backstage plugins and apps", - "version": "0.7.3", + "version": "0.7.4", "private": false, "publishConfig": { "access": "public", @@ -30,7 +30,7 @@ }, "dependencies": { "@backstage/config": "^0.1.4", - "@backstage/core-api": "^0.2.15", + "@backstage/core-api": "^0.2.16", "@backstage/errors": "^0.1.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -69,8 +69,8 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.6", - "@backstage/test-utils": "^0.1.9", + "@backstage/cli": "^0.6.7", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 762cfbc0d5..9855790e00 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,216 @@ # @backstage/create-app +## 0.3.17 + +### Patch Changes + +- 3e7de08af: **Fully migrated the template to the new composability API** + + The `create-app` template is now fully migrated to the new composability API, see [Composability System Migration Documentation](https://backstage.io/docs/plugins/composability) for explanations and more details. The final change which is now done was to migrate the `EntityPage` from being a component built on top of the `EntityPageLayout` and several more custom components, to an element tree built with `EntitySwitch` and `EntityLayout`. + + To apply this change to an existing plugin, it is important that all plugins that you are using have already been migrated. In this case the most crucial piece is that no entity page cards of contents may require the `entity` prop, and they must instead consume the entity from context using `useEntity`. + + Since this change is large with a lot of repeated changes, we'll describe a couple of common cases rather than the entire change. If your entity pages are unchanged from the `create-app` template, you can also just bring in the latest version directly from the [template itself](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx). + + The first step of the change is to change the `packages/app/src/components/catalog/EntityPage.tsx` export to `entityPage` rather than `EntityPage`. This will require an update to `App.tsx`, which is the only change we need to do outside of `EntityPage.tsx`: + + ```diff + -import { EntityPage } from './components/catalog/EntityPage'; + +import { entityPage } from './components/catalog/EntityPage'; + + } + > + - + + {entityPage} + + ``` + + The rest of the changes happen within `EntityPage.tsx`, and can be split into two broad categories, updating page components, and updating switch components. + + #### Migrating Page Components + + Let's start with an example of migrating a user page component. The following is the old code in the template: + + ```tsx + const UserOverviewContent = ({ entity }: { entity: UserEntity }) => ( + + + + + + + + + ); + + const UserEntityPage = ({ entity }: { entity: Entity }) => ( + + } + /> + + ); + ``` + + There's the main `UserEntityPage` component, and the `UserOverviewContent` component. Let's start with migrating the page contents, which we do by rendering an element rather than creating a component, as well as replace the cards with their new composability compatible variants. The new cards and content components can be identified by the `Entity` prefix. + + ```tsx + const userOverviewContent = ( + + + + + + + + + ); + ``` + + Now let's migrate the page component, again by converting it into a rendered element instead of a component, as well as replacing the use of `EntityPageLayout` with `EntityLayout`. + + ```tsx + const userPage = ( + + + {userOverviewContent} + + + ); + ``` + + At this point the `userPage` is quite small, so throughout this migration we have inlined the page contents for all pages. This is an optional step, but may help reduce noise. The final page now looks like this: + + ```tsx + const userPage = ( + + + + + + + + + + + + + ); + ``` + + #### Migrating Switch Components + + Switch components were used to select what entity page components or cards to render, based on for example the kind of entity. For this example we'll focus on the root `EntityPage` switch component, but the process is the same for example for the CI/CD switcher. + + The old `EntityPage` looked like this: + + ```tsx + export const EntityPage = () => { + const { entity } = useEntity(); + + switch (entity?.kind?.toLocaleLowerCase('en-US')) { + case 'component': + return ; + case 'api': + return ; + case 'group': + return ; + case 'user': + return ; + case 'system': + return ; + case 'domain': + return ; + case 'location': + case 'resource': + case 'template': + default: + return ; + } + }; + ``` + + In order to migrate to the composability API, we need to make this an element instead of a component, which means we're unable to keep the switch statement as is. To help with this, the catalog plugin provides an `EntitySwitch` component, which functions similar to a regular `switch` statement, which the first match being the one that is rendered. The catalog plugin also provides a number of built-in filter functions to use, such as `isKind` and `isComponentType`. + + To migrate the `EntityPage`, we convert the `switch` statement into an `EntitySwitch` element, and each `case` statement into an `EntitySwitch.Case` element. We also move over to use our new element version of the page components, with the result looking like this: + + ```tsx + export const entityPage = ( + + + + + + + + + {defaultEntityPage} + + ); + ``` + + Another example is the `ComponentEntityPage`, which is migrated from this: + + ```tsx + export const ComponentEntityPage = ({ entity }: { entity: Entity }) => { + switch (entity?.spec?.type) { + case 'service': + return ; + case 'website': + return ; + default: + return ; + } + }; + ``` + + To this: + + ```tsx + const componentPage = ( + + + {serviceEntityPage} + + + + {websiteEntityPage} + + + {defaultEntityPage} + + ); + ``` + + Note that if you want to conditionally render some piece of content, you can omit the default `EntitySwitch.Case`. If no case is matched in an `EntitySwitch`, nothing will be rendered. + +- Updated dependencies [802b41b65] +- Updated dependencies [2b2b31186] +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [3f96a9d5a] +- Updated dependencies [b051e770c] +- Updated dependencies [f9c75f7a9] +- Updated dependencies [ae6250ce3] +- Updated dependencies [98dd5da71] +- Updated dependencies [b779b5fee] + - @backstage/plugin-scaffolder-backend@0.9.5 + - @backstage/plugin-auth-backend@0.3.8 + - @backstage/core@0.7.4 + - @backstage/catalog-model@0.7.6 + - @backstage/plugin-scaffolder@0.8.2 + - @backstage/plugin-catalog-import@0.5.2 + - @backstage/test-utils@0.1.10 + - @backstage/plugin-catalog@0.5.3 + - @backstage/backend-common@0.6.2 + - @backstage/cli@0.6.7 + - @backstage/plugin-app-backend@0.3.11 + ## 0.3.16 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index e62a544ba1..35ce1b5180 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.16", + "version": "0.3.17", "private": false, "publishConfig": { "access": "public" diff --git a/packages/integration/package.json b/packages/integration/package.json index c57c0e78cd..dcfae4ea05 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -37,9 +37,9 @@ "luxon": "^1.25.0" }, "devDependencies": { - "@backstage/cli": "^0.6.4", - "@backstage/config-loader": "^0.5.1", - "@backstage/test-utils": "^0.1.7", + "@backstage/cli": "^0.6.7", + "@backstage/config-loader": "^0.6.0", + "@backstage/test-utils": "^0.1.10", "@types/jest": "^26.0.7", "@types/luxon": "^1.25.0", "msw": "^0.21.2" diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index bcdc94abbb..d2c2748449 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/test-utils +## 0.1.10 + +### Patch Changes + +- ae6250ce3: Remove unnecessary wrapping of elements rendered by `wrapInTestApp` and `renderInTestApp`, which was breaking mount discovery. +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [b051e770c] + - @backstage/core-api@0.2.16 + ## 0.1.9 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index f3a979fc72..a81455df01 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "0.1.9", + "version": "0.1.10", "private": false, "publishConfig": { "access": "public", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-api": "^0.2.14", + "@backstage/core-api": "^0.2.16", "@backstage/test-utils-core": "^0.1.1", "@backstage/theme": "^0.2.3", "@material-ui/core": "^4.11.0", @@ -45,7 +45,7 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.5", + "@backstage/cli": "^0.6.7", "@types/jest": "^26.0.7", "@types/node": "^14.14.32" }, diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 7f34627f6b..d7f5f3754b 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -31,7 +31,7 @@ "dependencies": { "@asyncapi/react-component": "^0.19.2", "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.4", "@backstage/theme": "^0.2.5", "@material-icons/font": "^1.0.2", @@ -49,9 +49,9 @@ "swagger-ui-react": "^3.37.2" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index e8c4510d54..85fab4455d 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-backend +## 0.3.11 + +### Patch Changes + +- Updated dependencies [82c66b8cd] +- Updated dependencies [b779b5fee] + - @backstage/config-loader@0.6.0 + - @backstage/backend-common@0.6.2 + ## 0.3.10 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 9346c6366d..c6a1dd5a6b 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.3.10", + "version": "0.3.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,8 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.0", - "@backstage/config-loader": "^0.5.1", + "@backstage/backend-common": "^0.6.2", + "@backstage/config-loader": "^0.6.0", "@backstage/config": "^0.1.4", "@types/express": "^4.17.6", "express": "^4.17.1", @@ -40,7 +40,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.5", + "@backstage/cli": "^0.6.7", "@types/supertest": "^2.0.8", "msw": "^0.20.5", "supertest": "^6.1.3" diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index bb2c44f7ff..47616b68c7 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-auth-backend +## 0.3.8 + +### Patch Changes + +- 2b2b31186: When using OAuth2 authentication the name is now taken from the name property of the JWT instead of the email property +- Updated dependencies [97b60de98] +- Updated dependencies [ae6250ce3] +- Updated dependencies [98dd5da71] +- Updated dependencies [b779b5fee] + - @backstage/catalog-model@0.7.6 + - @backstage/test-utils@0.1.10 + - @backstage/backend-common@0.6.2 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 183798c9e1..4af8a1736d 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.3.7", + "version": "0.3.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.1", + "@backstage/backend-common": "^0.6.2", "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.5", + "@backstage/catalog-model": "^0.7.6", "@backstage/config": "^0.1.4", "@backstage/errors": "^0.1.1", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@types/express": "^4.17.6", "@types/passport": "^1.0.3", "compression": "^1.7.4", @@ -68,7 +68,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md new file mode 100644 index 0000000000..69785a645f --- /dev/null +++ b/plugins/badges-backend/CHANGELOG.md @@ -0,0 +1,12 @@ +# @backstage/plugin-badges-backend + +## 0.1.2 + +### Patch Changes + +- d0b4ebf22: Support auth in badge plugin +- Updated dependencies [97b60de98] +- Updated dependencies [98dd5da71] +- Updated dependencies [b779b5fee] + - @backstage/catalog-model@0.7.6 + - @backstage/backend-common@0.6.2 diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 6ff83ecac9..eb2a93fe67 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-badges-backend", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.0", + "@backstage/backend-common": "^0.6.2", "@backstage/catalog-client": "^0.3.6", - "@backstage/catalog-model": "^0.7.3", + "@backstage/catalog-model": "^0.7.6", "@backstage/config": "^0.1.3", "@backstage/errors": "^0.1.1", "@types/express": "^4.17.6", @@ -45,7 +45,7 @@ "yn": "^4.0.0" }, "devDependencies": { - "@backstage/cli": "^0.6.3", + "@backstage/cli": "^0.6.7", "@types/supertest": "^2.0.8", "supertest": "^6.1.3" }, diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index bc3e64113a..a0efd43ccc 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-badges +## 0.2.0 + +### Minor Changes + +- d0b4ebf22: Support auth in badge plugin + +### Patch Changes + +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [b051e770c] +- Updated dependencies [98dd5da71] + - @backstage/core@0.7.4 + - @backstage/catalog-model@0.7.6 + ## 0.1.2 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index e438a6807b..1ab7315278 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-badges", - "version": "0.1.2", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/catalog-model": "^0.7.6", + "@backstage/core": "^0.7.4", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.3", "@backstage/theme": "^0.2.5", @@ -34,9 +34,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index f7f58d99cf..8a3b519fcb 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.2", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.2", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -37,9 +37,9 @@ "recharts": "^1.8.5" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 7b5cee247f..5fd93562d6 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-import +## 0.5.2 + +### Patch Changes + +- f9c75f7a9: When importing components you will now have the ability to use non Unicode characters in the entity owner field +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [b051e770c] +- Updated dependencies [98dd5da71] + - @backstage/core@0.7.4 + - @backstage/catalog-model@0.7.6 + ## 0.5.1 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index d3e8d45b4e..a168f089c8 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.5.1", + "version": "0.5.2", "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.7.5", + "@backstage/catalog-model": "^0.7.6", "@backstage/catalog-client": "^0.3.9", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/integration": "^0.5.0", "@backstage/integration-react": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.4", @@ -53,9 +53,9 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 31f511fdbf..7a79c39af1 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog +## 0.5.3 + +### Patch Changes + +- 98dd5da71: Add support for multiple links to post-scaffold task summary page +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [b051e770c] +- Updated dependencies [98dd5da71] + - @backstage/core@0.7.4 + - @backstage/catalog-model@0.7.6 + ## 0.5.2 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index f86c43430a..4e3d364b78 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "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": { "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.3", + "@backstage/catalog-model": "^0.7.6", + "@backstage/core": "^0.7.4", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.1", "@backstage/integration-react": "^0.1.1", @@ -53,9 +53,9 @@ "swr": "^0.3.0" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@microsoft/microsoft-graph-types": "^1.25.0", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 3a3b847e97..1d9fa11b3c 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.2", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -50,9 +50,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 8718a50a5f..a713d57883 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -32,7 +32,7 @@ "dependencies": { "@backstage/catalog-model": "^0.7.3", "@backstage/plugin-catalog-react": "^0.1.2", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,9 +47,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 7aece742fc..a983e4ed3e 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/config": "^0.1.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -55,9 +55,9 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index c640be6f23..ea6c41fcd4 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.4", "@backstage/plugin-explore-react": "^0.0.4", "@backstage/theme": "^0.2.5", @@ -45,9 +45,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index d503f76928..209f7512aa 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -44,9 +44,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 0f05e9d05b..a381d785a0 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.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,9 +41,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 8cc9471613..a744fcdc6e 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -34,7 +34,7 @@ "dependencies": { "@backstage/catalog-model": "^0.7.5", "@backstage/plugin-catalog-react": "^0.1.4", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/integration": "^0.5.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -50,9 +50,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md new file mode 100644 index 0000000000..9369ef87f9 --- /dev/null +++ b/plugins/github-deployments/CHANGELOG.md @@ -0,0 +1,14 @@ +# @backstage/plugin-github-deployments + +## 0.1.2 + +### Patch Changes + +- 64d2ce700: Add a button to reload the GitHub Deployments card +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [b051e770c] +- Updated dependencies [98dd5da71] + - @backstage/core@0.7.4 + - @backstage/catalog-model@0.7.6 diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index b8761c1385..b625316ed8 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-deployments", - "version": "0.1.1", + "version": "0.1.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,8 +20,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.7.4", - "@backstage/core": "^0.7.3", + "@backstage/catalog-model": "^0.7.6", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.3", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -34,9 +34,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index a82a2c26ed..2d12272e22 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.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -42,9 +42,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index b9e61b02da..e9d1353112 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -43,9 +43,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index f580dc2d15..3e00ae9373 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.3", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -47,9 +47,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index fb4ec0db41..52cd84b790 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.4", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -33,9 +33,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.4.2", diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 71f11e7a68..9d01997c13 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -33,7 +33,7 @@ "dependencies": { "@backstage/catalog-model": "^0.7.4", "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.3", "@backstage/plugin-kubernetes-backend": "^0.3.2", "@backstage/theme": "^0.2.5", @@ -50,9 +50,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.4.2", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 7f5b678afd..2fd25a9add 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -33,7 +33,7 @@ "dependencies": { "@backstage/catalog-model": "^0.7.3", "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.2", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -46,9 +46,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index b62fd82d20..83a46bb8b1 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -31,7 +31,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,9 +41,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 9239eabd9b..c62c618ff4 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-org +## 0.3.12 + +### Patch Changes + +- 97d53f686: Optimize data fetched for the `OwnershipCard`. +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [b051e770c] +- Updated dependencies [98dd5da71] + - @backstage/core@0.7.4 + - @backstage/core-api@0.2.16 + - @backstage/catalog-model@0.7.6 + ## 0.3.11 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index d8de1469ee..9fa79e27c6 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.3.11", + "version": "0.3.12", "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.7.5", - "@backstage/core": "^0.7.3", - "@backstage/core-api": "^0.2.14", + "@backstage/catalog-model": "^0.7.6", + "@backstage/core": "^0.7.4", + "@backstage/core-api": "^0.2.16", "@backstage/plugin-catalog-react": "^0.1.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -35,9 +35,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index df1078eedd..342d375863 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -46,9 +46,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/register-component/package.json b/plugins/register-component/package.json index 73d343a205..d63800db5c 100644 --- a/plugins/register-component/package.json +++ b/plugins/register-component/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -45,9 +45,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index 1421ee5051..084dcbad32 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -47,9 +47,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/react-hooks": "^3.3.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index a54ec4430d..e2c1717a71 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend +## 0.9.5 + +### Patch Changes + +- 802b41b65: Allow custom directory to be specified for GitHub publish action +- Updated dependencies [97b60de98] +- Updated dependencies [98dd5da71] +- Updated dependencies [b779b5fee] + - @backstage/catalog-model@0.7.6 + - @backstage/backend-common@0.6.2 + ## 0.9.4 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 69aced77b5..a41d8c3a7b 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.9.4", + "version": "0.9.5", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.6.1", + "@backstage/backend-common": "^0.6.2", "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.5", + "@backstage/catalog-model": "^0.7.6", "@backstage/config": "^0.1.4", "@backstage/errors": "^0.1.1", "@backstage/integration": "^0.5.1", @@ -64,8 +64,8 @@ "yaml": "^1.10.0" }, "devDependencies": { - "@backstage/cli": "^0.6.6", - "@backstage/test-utils": "^0.1.9", + "@backstage/cli": "^0.6.7", + "@backstage/test-utils": "^0.1.10", "@types/fs-extra": "^9.0.1", "@types/mock-fs": "^4.13.0", "@types/supertest": "^2.0.8", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 4823c19816..6a43bccab3 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder +## 0.8.2 + +### Patch Changes + +- 3f96a9d5a: Support auth by sending cookies in event stream request +- 98dd5da71: Add support for multiple links to post-scaffold task summary page +- Updated dependencies [1279a3325] +- Updated dependencies [4a4681b1b] +- Updated dependencies [97b60de98] +- Updated dependencies [b051e770c] +- Updated dependencies [98dd5da71] + - @backstage/core@0.7.4 + - @backstage/catalog-model@0.7.6 + ## 0.8.1 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 410e12a97d..0de3b5ee53 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.8.1", + "version": "0.8.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,9 +31,9 @@ }, "dependencies": { "@backstage/catalog-client": "^0.3.9", - "@backstage/catalog-model": "^0.7.5", + "@backstage/catalog-model": "^0.7.6", "@backstage/config": "^0.1.4", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/integration": "^0.5.1", "@backstage/integration-react": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.4", @@ -60,9 +60,9 @@ "zen-observable": "^0.8.15" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/search/package.json b/plugins/search/package.json index d0eab8f808..942035159c 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/catalog-model": "^0.7.3", "@backstage/plugin-catalog-react": "^0.1.2", "@backstage/search-common": "^0.1.1", @@ -44,9 +44,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 4b086c8e5e..cfed11acb8 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -46,9 +46,9 @@ "timeago.js": "^4.0.2" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 412d2fd905..14f3dcfe10 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -34,7 +34,7 @@ "dependencies": { "@backstage/catalog-model": "^0.7.3", "@backstage/plugin-catalog-react": "^0.1.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,9 +47,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 6ef9fe22af..4e06c68e3a 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -31,7 +31,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.4", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", @@ -45,9 +45,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 5d8da62ca9..e172fae350 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.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -43,9 +43,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index f8e7a184fd..4342b32a3a 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -33,7 +33,7 @@ "dependencies": { "@backstage/config": "^0.1.4", "@backstage/catalog-model": "^0.7.5", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/plugin-catalog-react": "^0.1.4", "@backstage/test-utils": "^0.1.9", "@backstage/theme": "^0.2.5", @@ -49,9 +49,9 @@ "sanitize-html": "^2.3.2" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 5a92e4a256..d253f18fa6 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "@backstage/catalog-model": "^0.7.3", - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/errors": "^0.1.1", "@backstage/plugin-catalog-react": "^0.1.1", "@backstage/theme": "^0.2.5", @@ -39,9 +39,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 674e8ee57c..fde05caee8 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.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,9 +41,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 85bda39a65..d100bd85b8 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core": "^0.7.3", + "@backstage/core": "^0.7.4", "@backstage/theme": "^0.2.5", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -41,9 +41,9 @@ "react-use": "^15.3.3" }, "devDependencies": { - "@backstage/cli": "^0.6.6", + "@backstage/cli": "^0.6.7", "@backstage/dev-utils": "^0.1.13", - "@backstage/test-utils": "^0.1.9", + "@backstage/test-utils": "^0.1.10", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.0.7", From ec75495ee3d62e211b55f19a80ef71464fd51288 Mon Sep 17 00:00:00 2001 From: Jeff Cook Date: Thu, 8 Apr 2021 13:27:49 +0000 Subject: [PATCH 61/61] No need to export under the alias isPluginApplicableToEntity. ... That's an old pattern, per https://github.com/backstage/backstage/pull/5245#discussion_r609367059. Signed-off-by: Jeff Cook --- .changeset/chatty-ghosts-lay.md | 2 +- plugins/sonarqube/src/components/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/chatty-ghosts-lay.md b/.changeset/chatty-ghosts-lay.md index 26a83f89f9..7d8ef3edbd 100644 --- a/.changeset/chatty-ghosts-lay.md +++ b/.changeset/chatty-ghosts-lay.md @@ -2,4 +2,4 @@ '@backstage/plugin-sonarqube': patch --- -Export isPluginApplicableToEntity +Export isSonarQubeAvailable. diff --git a/plugins/sonarqube/src/components/index.ts b/plugins/sonarqube/src/components/index.ts index 8ff9a77485..56b9d05885 100644 --- a/plugins/sonarqube/src/components/index.ts +++ b/plugins/sonarqube/src/components/index.ts @@ -15,4 +15,4 @@ */ export * from './SonarQubeCard'; -export { isSonarQubeAvailable as isPluginApplicableToEntity } from './useProjectKey'; +export { isSonarQubeAvailable } from './useProjectKey';