From 8ebc1dea21c221e5395945026d8de46b619aa358 Mon Sep 17 00:00:00 2001 From: Maximilian Ressel Date: Tue, 31 May 2022 16:08:50 +0200 Subject: [PATCH 001/101] Add allowedRepos Option and move Repo Field to own Component Signed-off-by: Maximilian Ressel --- .../fields/RepoUrlPicker/AzureRepoPicker.tsx | 15 +--- .../RepoUrlPicker/BitbucketRepoPicker.tsx | 15 +--- .../fields/RepoUrlPicker/GerritRepoPicker.tsx | 15 +--- .../fields/RepoUrlPicker/GithubRepoPicker.tsx | 15 +--- .../fields/RepoUrlPicker/GitlabRepoPicker.tsx | 18 +---- .../fields/RepoUrlPicker/RepoUrlPicker.tsx | 19 +++++ .../RepoUrlPicker/RepoUrlPickerRepoName.tsx | 77 +++++++++++++++++++ 7 files changed, 103 insertions(+), 71 deletions(-) create mode 100644 plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx index 0efadf4017..0246c67801 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.tsx @@ -27,7 +27,7 @@ export const AzureRepoPicker = (props: { rawErrors: string[]; }) => { const { rawErrors, state, onChange } = props; - const { organization, repoName, owner } = state; + const { organization, owner } = state; return ( <> The Owner that this repo will belong to - 0 && !repoName} - > - Repository - onChange({ repoName: e.target.value })} - value={repoName} - /> - The name of the repository - ); }; diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx index 64eb4b8dac..fc59398d94 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.tsx @@ -26,7 +26,7 @@ export const BitbucketRepoPicker = (props: { rawErrors: string[]; }) => { const { onChange, rawErrors, state } = props; - const { host, workspace, project, repoName } = state; + const { host, workspace, project } = state; return ( <> {host === 'bitbucket.org' && ( @@ -61,19 +61,6 @@ export const BitbucketRepoPicker = (props: { The Project that this repo will belong to - 0 && !repoName} - > - Repository - onChange({ repoName: e.target.value })} - value={repoName} - /> - The name of the repository - ); }; diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx index 4cbc0859e8..f021948532 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.tsx @@ -26,7 +26,7 @@ export const GerritRepoPicker = (props: { rawErrors: string[]; }) => { const { onChange, rawErrors, state } = props; - const { workspace, repoName, owner } = state; + const { workspace, owner } = state; return ( <> - 0 && !repoName} - > - Repository - onChange({ repoName: e.target.value })} - value={repoName} - /> - The name of the repository - ); }; diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx index 13b57cb449..ec59d89ef8 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.tsx @@ -32,7 +32,7 @@ export const GithubRepoPicker = (props: { ? allowedOwners.map(i => ({ label: i, value: i })) : [{ label: 'Loading...', value: 'loading' }]; - const { owner, repoName } = state; + const { owner } = state; return ( <> @@ -66,19 +66,6 @@ export const GithubRepoPicker = (props: { The organization, user or project that this repo will belong to - 0 && !repoName} - > - Repository - onChange({ repoName: e.target.value })} - value={repoName} - /> - The name of the repository - ); }; diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx index e2052b792f..c628b364b9 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.tsx @@ -23,16 +23,17 @@ import { RepoUrlPickerState } from './types'; export const GitlabRepoPicker = (props: { allowedOwners?: string[]; + allowedRepos?: string[]; state: RepoUrlPickerState; onChange: (state: RepoUrlPickerState) => void; rawErrors: string[]; }) => { - const { allowedOwners = [], rawErrors, state, onChange } = props; + const { allowedOwners = [], state, onChange, rawErrors } = props; const ownerItems: SelectItem[] = allowedOwners ? allowedOwners.map(i => ({ label: i, value: i })) : [{ label: 'Loading...', value: 'loading' }]; - const { owner, repoName } = state; + const { owner } = state; return ( <> @@ -69,19 +70,6 @@ export const GitlabRepoPicker = (props: { namespaces in gitlab), that this repo will belong to - 0 && !repoName} - > - Repository - onChange({ repoName: e.target.value })} - value={repoName} - /> - The name of the repository - ); }; diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx index 6f762933b6..b36b332d9e 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx @@ -26,6 +26,7 @@ import { BitbucketRepoPicker } from './BitbucketRepoPicker'; import { GerritRepoPicker } from './GerritRepoPicker'; import { FieldExtensionComponentProps } from '../../../extensions'; import { RepoUrlPickerHost } from './RepoUrlPickerHost'; +import { RepoUrlPickerRepoName } from './RepoUrlPickerRepoName'; import { parseRepoPickerUrl, serializeRepoPickerUrl } from './utils'; import { RepoUrlPickerState } from './types'; import useDebounce from 'react-use/lib/useDebounce'; @@ -40,6 +41,7 @@ import { useTemplateSecrets } from '../../secrets'; export interface RepoUrlPickerUiOptions { allowedHosts?: string[]; allowedOwners?: string[]; + allowedRepos?: string[]; requestUserCredentials?: { secretsKey: string; additionalScopes?: { @@ -76,6 +78,10 @@ export const RepoUrlPicker = ( () => uiSchema?.['ui:options']?.allowedOwners ?? [], [uiSchema], ); + const allowedRepos = useMemo( + () => uiSchema?.['ui:options']?.allowedRepos ?? [], + [uiSchema], + ); useEffect(() => { onChange(serializeRepoPickerUrl(state)); @@ -87,6 +93,11 @@ export const RepoUrlPicker = ( setState(prevState => ({ ...prevState, owner: allowedOwners[0] })); } }, [setState, allowedOwners]); + useEffect(() => { + if (allowedRepos.length === 1) { + setState(prevState => ({ ...prevState, repoName: allowedRepos[0] })); + } + }, [setState, allowedRepos]); const updateLocalState = useCallback( (newState: RepoUrlPickerState) => { @@ -179,6 +190,14 @@ export const RepoUrlPicker = ( onChange={updateLocalState} /> )} + + setState(prevState => ({ ...prevState, repoName })) + } + rawErrors={rawErrors} + /> ); }; diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx new file mode 100644 index 0000000000..f2d68b08f3 --- /dev/null +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx @@ -0,0 +1,77 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useEffect } from 'react'; +import { Select, SelectItem } from '@backstage/core-components'; +import FormControl from '@material-ui/core/FormControl'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import Input from '@material-ui/core/Input'; +import InputLabel from '@material-ui/core/InputLabel'; + +export const RepoUrlPickerRepoName = (props: { + repoName?: string; + allowedRepos?: string[]; + onChange: (host: string) => void; + rawErrors: string[]; +}) => { + const { repoName, allowedRepos, onChange, rawErrors } = props; + + useEffect(() => { + // If there is no repoName chosen currently + if (!repoName) { + // Set the first of the allowedRepos option if that available + if (allowedRepos?.length) { + onChange(allowedRepos[0]); + } + } + }, [allowedRepos, repoName, onChange]); + + const repoItems: SelectItem[] = allowedRepos + ? allowedRepos.map(i => ({ label: i, value: i })) + : [{ label: 'Loading...', value: 'loading' }]; + + return ( + <> + 0 && !repoName} + > + {allowedRepos?.length ? ( + onChange(String(e.target.value))} + value={repoName} + /> + + )} + The name of the repository + + + ); +}; From d8eb82f4474f04f413896deee1fccf2db20287d9 Mon Sep 17 00:00:00 2001 From: Maixmilian Ressel Date: Wed, 15 Jun 2022 16:46:57 +0200 Subject: [PATCH 002/101] Add changeset Signed-off-by: Maixmilian Ressel --- .changeset/hot-rice-sin.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hot-rice-sin.md diff --git a/.changeset/hot-rice-sin.md b/.changeset/hot-rice-sin.md new file mode 100644 index 0000000000..5a7363bdc8 --- /dev/null +++ b/.changeset/hot-rice-sin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +RepoUrlPicker: Add allowedRepos option and move repoName field to own component From 2de3fc32316cf7256633a5cbb1b7306ce5cc33cc Mon Sep 17 00:00:00 2001 From: Maixmilian Ressel Date: Wed, 15 Jun 2022 17:18:44 +0200 Subject: [PATCH 003/101] Add documentation for allowedOwners and allowedRepos Signed-off-by: Maixmilian Ressel --- .../software-templates/writing-templates.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 27e6e76817..4b11cc4990 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -320,6 +320,30 @@ The `allowedHosts` part should be set to where you wish to enable this template to publish to. And it can be any host that is listed in your `integrations` config in `app-config.yaml`. +Besides specifying `allowedHosts` you can also restrict the template to publish to +repositories owned by specific users/groups/namespaces by setting the `allowedOwners` +option. With the `allowedRepos` option you are able to narrow it down further to a +specific set of repository names. A full example could look like this: + +```yaml +- title: Choose a location + required: + - repoUrl + properties: + repoUrl: + title: Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - github.com + allowedOwners: + - backstage + - someGithubUser + allowedRepos: + - backstage +``` + The `RepoUrlPicker` is a custom field that we provide part of the `plugin-scaffolder`. You can provide your own custom fields by [writing your own Custom Field Extensions](./writing-custom-field-extensions.md) From 9d8ec897eb3a7f64153513983d5ed69c96ba32a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Thu, 16 Jun 2022 20:22:27 +0200 Subject: [PATCH 004/101] feat(MarkdownContent): Expose `transformLinkUri` and `transformImageUri` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cรดme Tresarrieu --- .../MarkdownContent/MarkdownContent.test.tsx | 38 +++++++++++++++++++ .../MarkdownContent/MarkdownContent.tsx | 12 +++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx index 2e1550078f..9c08b83c40 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.test.tsx @@ -62,4 +62,42 @@ describe('', () => { expect(fp2).toBeInTheDocument(); expect(rendered.getByText(');', { selector: 'span' })).toBeInTheDocument(); }); + + it('render MarkdownContent component with transformed link', async () => { + const rendered = await renderWithEffects( + wrapInTestApp( + { + return `${href}-modified`; + }} + />, + ), + ); + const fp1 = rendered.getByText('Title', { + selector: 'a', + }); + expect(fp1).toBeInTheDocument(); + expect(fp1.getAttribute('href')).toEqual( + 'https://backstage.io/link-modified', + ); + }); + + it('render MarkdownContent component with transformed image', async () => { + const rendered = await renderWithEffects( + wrapInTestApp( + { + return `https://example.com/blog/assets/6/header.png`; + }} + />, + ), + ); + const fp1 = rendered.getByAltText('Image'); + expect(fp1).toBeInTheDocument(); + expect(fp1.getAttribute('src')).toEqual( + 'https://example.com/blog/assets/6/header.png', + ); + }); }); diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index df4ad56776..4059482b79 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -68,6 +68,8 @@ type Props = { content: string; dialect?: 'gfm' | 'common-mark'; linkTarget?: Options['linkTarget']; + transformLinkUri?: Options['transformLinkUri']; + transformImageUri?: Options['transformImageUri']; }; const components: Options['components'] = { @@ -91,7 +93,13 @@ const components: Options['components'] = { * If you just want to render to plain [CommonMark](https://commonmark.org/), set the dialect to `'common-mark'` */ export function MarkdownContent(props: Props) { - const { content, dialect = 'gfm', linkTarget } = props; + const { + content, + dialect = 'gfm', + linkTarget, + transformLinkUri, + transformImageUri, + } = props; const classes = useStyles(); return ( ); } From 32204fa79454a1ef5907b6cd5616e022a14b335b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Thu, 16 Jun 2022 20:23:14 +0200 Subject: [PATCH 005/101] chore: add changeset file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cรดme Tresarrieu --- .changeset/cool-toys-flow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cool-toys-flow.md diff --git a/.changeset/cool-toys-flow.md b/.changeset/cool-toys-flow.md new file mode 100644 index 0000000000..799bf3ace4 --- /dev/null +++ b/.changeset/cool-toys-flow.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': minor +--- + +Add `transformLinkUri` and `transformImageUri` to `MarkdownContent` From 7e35d62a13baef3aeafbe0d28cb9b02395234c40 Mon Sep 17 00:00:00 2001 From: Vladimir Masarik Date: Fri, 17 Jun 2022 14:24:03 +0200 Subject: [PATCH 006/101] docs: custom tempalte actions clearly express that the builtin actions are replaced Signed-off-by: Vladimir Masarik --- docs/features/software-templates/writing-custom-actions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 8e698e1a05..635a373eb3 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -9,8 +9,9 @@ by writing custom actions which can be used along side our [built-in actions](./builtin-actions.md). > Note: When adding custom actions, the actions array will **replace the -> built-in actions too**. To ensure you can continue to include the builtin -> actions, see below to include them during registration of your action. +> built-in actions too**. Meaning, you will no longer be able to use them. +> If you want to continue using the builtin actions, include them in the actions +> array when registering your custom actions, as seen below. ## Writing your Custom Action From 1ee1a02d676199a7425c774824661acade56db48 Mon Sep 17 00:00:00 2001 From: Vladimir Masarik Date: Fri, 17 Jun 2022 14:26:56 +0200 Subject: [PATCH 007/101] docs: custom template actions update code exmaple Users don't need the container runner, and variables have to be passed in differently Signed-off-by: Vladimir Masarik --- .../writing-custom-actions.md | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 635a373eb3..abff0cf7c7 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -123,27 +123,32 @@ will set the available actions that the scaffolder has access to. ```ts import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; import { ScmIntegrations } from '@backstage/integration'; +import { createNewFileAction } from './actions/custom'; -const integrations = ScmIntegrations.fromConfig(env.config); +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const catalogClient = new CatalogClient({ discoveryApi: env.discovery }); + const integrations = ScmIntegrations.fromConfig(env.config); -const builtInActions = createBuiltinActions({ - containerRunner, - integrations, - catalogClient, - config: env.config, - reader: env.reader, -}); + const builtInActions = createBuiltinActions({ + integrations, + catalogClient, + config: env.config, + reader: env.reader, + }); + + const actions = [...builtInActions, createNewFileAction()]; -const actions = [...builtInActions, createNewFileAction()]; -return await createRouter({ - containerRunner, - catalogClient, - actions, - logger: env.logger, - config: env.config, - database: env.database, - reader: env.reader, -}); + return createRouter({ + actions, + catalogClient: catalogClient, + logger: env.logger, + config: env.config, + database: env.database, + reader: env.reader, + }); +} ``` ## List of custom action packages From 4ae8c4b0b76b20b04ca48dba6fbb33c1b20083fc Mon Sep 17 00:00:00 2001 From: Vladimir Masarik Date: Fri, 17 Jun 2022 14:49:33 +0200 Subject: [PATCH 008/101] docs: Adding templates, add template as a file example Signed-off-by: Vladimir Masarik --- docs/features/software-templates/adding-templates.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index db765fc7ee..821e44b0ab 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -97,6 +97,8 @@ catalog: target: https://github.com/backstage/software-templates/blob/main/scaffolder-templates/react-ssr-template/template.yaml rules: - allow: [Template] + - type: file + target: template.yaml # Backstage will expect the file to be in packages/backend/template.yaml ``` Or you can add the template using the `catalog-import` plugin, which unless From 7fc1f50f57ff01c24ccdddf43871bd02afe83760 Mon Sep 17 00:00:00 2001 From: Vladimir Masarik Date: Fri, 17 Jun 2022 15:00:58 +0200 Subject: [PATCH 009/101] docs: Adding templates, added note for users that they need to refresh the location Othwise, backstage won't display the updated or newly added template, and it won't report any errors either. Signed-off-by: Vladimir Masarik --- docs/features/software-templates/adding-templates.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index 821e44b0ab..62604df1b7 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -86,6 +86,13 @@ contains more information about the required fields. Once we have a `template.yaml` ready, we can then add it to the software catalog for use by the scaffolder. +> Note: When you add or modify a template, you will need to refresh the location entity. +> Otherwise, Backstage won't display the template in the available templates, +> or it will keep showing the old template. You can refresh the location instance by +> going into `Catalog` web page, choosing `Locations` instead of `Components`, and selecting the correct location entity. +> From there, you can click on the refresh icon representing "Sheduled entity refresh" action. +> Afterwards, you should see your template updated. + You can add the template files to the catalog through [static location configuration](../software-catalog/configuration.md#static-location-configuration), for example: From a6321c9b9f6a9b78ee525c6af66f1ec5b98c6d6a Mon Sep 17 00:00:00 2001 From: Vladimir Masarik Date: Fri, 17 Jun 2022 15:25:14 +0200 Subject: [PATCH 010/101] docs: writing templates, add examples of how to use values Add information on how to actually use the parameters from the UI in the actual code so that users can fully utilize the templating power of the default templating action. Signed-off-by: Vladimir Masarik --- .../software-templates/writing-templates.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index ff9962d8d4..5597916bee 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -485,6 +485,66 @@ the value of `firstName` from the parameters). This is great for passing the values from the form into different steps and reusing these input variables. These template strings preserve the type of the parameter. +The `${{ parameters.firstName }}` pattern will work only in the template file. +If you want to start using values provided from the UI in your code, you will have to use +the `${{ values.firstName }}` pattern. Additionally, you have to pass +the parameters from the UI to the input of the `fetch:template` step. + +```yaml +apiVersion: scaffolder.backstage.io/v1beta3 +kind: Template +metadata: + name: v1beta3-demo + title: Test Action + description: scaffolder v1beta3 template demo +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 your project + urlParameter: + title: URL endpoint + type: string + description: URL endpoint at which the component can be reached + default: "https://www.example.com" + enabledDB: + title: Enable Database + type: boolean + default: false + +... + + steps: + - id: fetch-base + name: Fetch Base + action: fetch:template + input: + url: ./template + values: + name: ${{ parameters.name }} + url: ${{ parameters.urlParameter }} + enabledDB: ${{ parameters.enabledDB }} +``` + +Afterwards, if you are using the builtin templating action, you can start using +the variables in your code. You can use also any other templating fuctions from +[Nunjucks](https://mozilla.github.io/nunjucks/templating.html#tags) as well. + +```bash +#!/bin/bash +echo "Hi my name is ${{ values.name }}, and you can fine me at ${{ values.url }}!" +{% if values.enabledDB %} +echo "You have enabled your database!" +{% endif %} +``` + As you can see above in the `Outputs` section, `actions` and `steps` can also output things. You can grab that output using `steps.$stepId.output.$property`. From e81be86682cee2a94c71aeaa593726b1a9dd190e Mon Sep 17 00:00:00 2001 From: Vladimir Masarik Date: Fri, 17 Jun 2022 16:01:37 +0200 Subject: [PATCH 011/101] docs: fix template typos Signed-off-by: Vladimir Masarik --- docs/features/software-templates/adding-templates.md | 2 +- docs/features/software-templates/writing-templates.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index 62604df1b7..e8176a688b 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -90,7 +90,7 @@ for use by the scaffolder. > Otherwise, Backstage won't display the template in the available templates, > or it will keep showing the old template. You can refresh the location instance by > going into `Catalog` web page, choosing `Locations` instead of `Components`, and selecting the correct location entity. -> From there, you can click on the refresh icon representing "Sheduled entity refresh" action. +> From there, you can click on the refresh icon representing "Scheduled entity refresh" action. > Afterwards, you should see your template updated. You can add the template files to the catalog through diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 5597916bee..3173a0500a 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -534,7 +534,7 @@ spec: ``` Afterwards, if you are using the builtin templating action, you can start using -the variables in your code. You can use also any other templating fuctions from +the variables in your code. You can use also any other templating functions from [Nunjucks](https://mozilla.github.io/nunjucks/templating.html#tags) as well. ```bash From ea07eb0bc8932ab68d7f1cc3d6bf2cae30655d91 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Sat, 18 Jun 2022 14:02:10 +0100 Subject: [PATCH 012/101] feat: introduce github:repo:create github:repo:push scaffolder actions Signed-off-by: Marco Crivellaro --- .../actions/builtin/createBuiltinActions.ts | 39 +- .../builtin/github/githubRepoCreate.test.ts | 416 ++++++++++++++++++ .../builtin/github/githubRepoCreate.ts | 337 ++++++++++++++ .../builtin/github/githubRepoPush.test.ts | 400 +++++++++++++++++ .../actions/builtin/github/githubRepoPush.ts | 224 ++++++++++ .../actions/builtin/github/index.ts | 4 +- 6 files changed, 1405 insertions(+), 15 deletions(-) create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.test.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 2ffbc09ce5..91f1e180d0 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -15,25 +15,34 @@ */ import { UrlReader } from '@backstage/backend-common'; -import { JsonObject } from '@backstage/types'; import { CatalogApi } from '@backstage/catalog-client'; -import { - GithubCredentialsProvider, - ScmIntegrations, - DefaultGithubCredentialsProvider, -} from '@backstage/integration'; import { Config } from '@backstage/config'; import { - createCatalogWriteAction, + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { JsonObject } from '@backstage/types'; +import { createCatalogRegisterAction, + createCatalogWriteAction, } from './catalog'; +import { TemplateFilter } from '../../../lib'; +import { TemplateAction } from '../types'; import { createDebugLogAction } from './debug'; import { createFetchPlainAction, createFetchTemplateAction } from './fetch'; import { createFilesystemDeleteAction, createFilesystemRenameAction, } from './filesystem'; +import { + createGithubActionsDispatchAction, + createGithubIssuesLabelAction, + createGithubRepoCreateAction, + createGithubRepoPushAction, + createGithubWebhookAction, +} from './github'; import { createPublishAzureAction, createPublishBitbucketAction, @@ -45,13 +54,6 @@ import { createPublishGitlabAction, createPublishGitlabMergeRequestAction, } from './publish'; -import { - createGithubActionsDispatchAction, - createGithubWebhookAction, - createGithubIssuesLabelAction, -} from './github'; -import { TemplateFilter } from '../../../lib'; -import { TemplateAction } from '../types'; /** * The options passed to {@link createBuiltinActions} @@ -165,6 +167,15 @@ export const createBuiltinActions = ( integrations, githubCredentialsProvider, }), + createGithubRepoCreateAction({ + integrations, + githubCredentialsProvider, + }), + createGithubRepoPushAction({ + integrations, + config, + githubCredentialsProvider, + }), ]; return actions as TemplateAction[]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts new file mode 100644 index 0000000000..0e7e907917 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.test.ts @@ -0,0 +1,416 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateAction } from '../../types'; + +jest.mock('../helpers'); + +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { when } from 'jest-when'; +import { PassThrough } from 'stream'; +import { createGithubRepoCreateAction } from './githubRepoCreate'; + +const mockOctokit = { + rest: { + users: { + getByUsername: jest.fn(), + }, + repos: { + addCollaborator: jest.fn(), + createInOrg: jest.fn(), + createForAuthenticatedUser: jest.fn(), + replaceAllTopics: jest.fn(), + }, + teams: { + addOrUpdateRepoPermissionsInOrg: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +describe('github:repo:create', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + let githubCredentialsProvider: GithubCredentialsProvider; + let action: TemplateAction; + + const mockContext = { + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + description: 'description', + repoVisibility: 'private' as const, + access: 'owner/blam', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + githubCredentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + action = createGithubRepoCreateAction({ + integrations, + githubCredentialsProvider, + }); + }); + + it('should call the githubApis with the correct values for createInOrg', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'Organization' }, + }); + + mockOctokit.rest.repos.createInOrg.mockResolvedValue({ data: {} }); + + await action.handler(mockContext); + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + org: 'owner', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + allow_merge_commit: true, + allow_rebase_merge: true, + visibility: 'private', + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoVisibility: 'public', + }, + }); + expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + org: 'owner', + private: false, + delete_branch_on_merge: false, + allow_squash_merge: true, + allow_merge_commit: true, + allow_rebase_merge: true, + visibility: 'public', + }); + }); + + it('should call the githubApis with the correct values for createForAuthenticatedUser', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: {}, + }); + + await action.handler(mockContext); + expect( + mockOctokit.rest.repos.createForAuthenticatedUser, + ).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + private: true, + delete_branch_on_merge: false, + allow_squash_merge: true, + allow_merge_commit: true, + allow_rebase_merge: true, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoVisibility: 'public', + }, + }); + expect( + mockOctokit.rest.repos.createForAuthenticatedUser, + ).toHaveBeenCalledWith({ + description: 'description', + name: 'repo', + private: false, + delete_branch_on_merge: false, + allow_squash_merge: true, + allow_merge_commit: true, + allow_rebase_merge: true, + }); + }); + + it('should add access for the team when it starts with the owner', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler(mockContext); + + expect( + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg, + ).toHaveBeenCalledWith({ + org: 'owner', + team_slug: 'blam', + owner: 'owner', + repo: 'repo', + permission: 'admin', + }); + }); + + it('should add outside collaborators when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + access: 'outsidecollaborator', + }, + }); + + expect(mockOctokit.rest.repos.addCollaborator).toHaveBeenCalledWith({ + username: 'outsidecollaborator', + owner: 'owner', + repo: 'repo', + permission: 'admin', + }); + }); + + it('should add multiple collaborators when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + collaborators: [ + { + access: 'pull', + user: 'robot-1', + }, + { + access: 'push', + team: 'robot-2', + }, + ], + }, + }); + + const commonProperties = { + owner: 'owner', + repo: 'repo', + }; + + expect(mockOctokit.rest.repos.addCollaborator).toHaveBeenCalledWith({ + ...commonProperties, + username: 'robot-1', + permission: 'pull', + }); + + expect( + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg, + ).toHaveBeenCalledWith({ + ...commonProperties, + org: 'owner', + team_slug: 'robot-2', + permission: 'push', + }); + }); + + it('should ignore failures when adding multiple collaborators', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + when(mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg) + .calledWith({ + org: 'owner', + owner: 'owner', + repo: 'repo', + team_slug: 'robot-1', + permission: 'pull', + }) + .mockRejectedValueOnce(new Error('Something bad happened') as never); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + collaborators: [ + { + access: 'pull', + team: 'robot-1', + }, + { + access: 'push', + team: 'robot-2', + }, + ], + }, + }); + + expect( + mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2], + ).toEqual([ + { + org: 'owner', + owner: 'owner', + repo: 'repo', + team_slug: 'robot-2', + permission: 'push', + }, + ]); + }); + + it('should add topics when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + mockOctokit.rest.repos.replaceAllTopics.mockResolvedValue({ + data: { + names: ['node.js'], + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + topics: ['node.js'], + }, + }); + + expect(mockOctokit.rest.repos.replaceAllTopics).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + names: ['node.js'], + }); + }); + + it('should lowercase topics when provided', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + mockOctokit.rest.repos.replaceAllTopics.mockResolvedValue({ + data: { + names: ['backstage'], + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + topics: ['BACKSTAGE'], + }, + }); + + expect(mockOctokit.rest.repos.replaceAllTopics).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + names: ['backstage'], + }); + }); + + it('should call output with the remoteUrl', async () => { + mockOctokit.rest.users.getByUsername.mockResolvedValue({ + data: { type: 'User' }, + }); + + mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler(mockContext); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/clone/url.git', + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts new file mode 100644 index 0000000000..494a47516c --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts @@ -0,0 +1,337 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { assertError, InputError } from '@backstage/errors'; +import { + GithubCredentialsProvider, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { Octokit } from 'octokit'; +import { createTemplateAction } from '../../createTemplateAction'; +import { parseRepoUrl } from '../publish/util'; +import { getOctokitOptions } from './helpers'; + +/** + * Creates a new action that initializes a git repository + * + * @public + */ +export function createGithubRepoCreateAction(options: { + integrations: ScmIntegrationRegistry; + githubCredentialsProvider?: GithubCredentialsProvider; +}) { + const { integrations, githubCredentialsProvider } = options; + + return createTemplateAction<{ + repoUrl: string; + description?: string; + access?: string; + deleteBranchOnMerge?: boolean; + gitAuthorName?: string; + gitAuthorEmail?: string; + allowRebaseMerge?: boolean; + allowSquashMerge?: boolean; + allowMergeCommit?: boolean; + requireCodeOwnerReviews?: boolean; + requiredStatusCheckContexts?: string[]; + repoVisibility?: 'private' | 'internal' | 'public'; + collaborators?: Array< + | { + user: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + team: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + >; + token?: string; + topics?: string[]; + }>({ + id: 'github:repo:create', + description: 'Creates a GitHub repository.', + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { + title: 'Repository Location', + description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, + type: 'string', + }, + description: { + title: 'Repository Description', + type: 'string', + }, + access: { + title: 'Repository Access', + description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`, + type: 'string', + }, + requireCodeOwnerReviews: { + title: 'Require CODEOWNER Reviews?', + description: + 'Require an approved review in PR including files with a designated Code Owner', + type: 'boolean', + }, + requiredStatusCheckContexts: { + title: 'Required Status Check Contexts', + description: + 'The list of status checks to require in order to merge into this branch', + type: 'array', + items: { + type: 'string', + }, + }, + repoVisibility: { + title: 'Repository Visibility', + type: 'string', + enum: ['private', 'public', 'internal'], + }, + deleteBranchOnMerge: { + title: 'Delete Branch On Merge', + type: 'boolean', + description: `Delete the branch after merging the PR. The default value is 'false'`, + }, + gitAuthorName: { + title: 'Default Author Name', + type: 'string', + description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, + }, + gitAuthorEmail: { + title: 'Default Author Email', + type: 'string', + description: `Sets the default author email for the commit.`, + }, + allowMergeCommit: { + title: 'Allow Merge Commits', + type: 'boolean', + description: `Allow merge commits. The default value is 'true'`, + }, + allowSquashMerge: { + title: 'Allow Squash Merges', + type: 'boolean', + description: `Allow squash merges. The default value is 'true'`, + }, + allowRebaseMerge: { + title: 'Allow Rebase Merges', + type: 'boolean', + description: `Allow rebase merges. The default value is 'true'`, + }, + collaborators: { + title: 'Collaborators', + description: 'Provide additional users or teams with permissions', + type: 'array', + items: { + type: 'object', + additionalProperties: false, + required: ['access'], + properties: { + access: { + type: 'string', + description: 'The type of access for the user', + enum: ['push', 'pull', 'admin', 'maintain', 'triage'], + }, + user: { + type: 'string', + description: + 'The name of the user that will be added as a collaborator', + }, + team: { + type: 'string', + description: + 'The name of the team that will be added as a collaborator', + }, + }, + oneOf: [{ required: ['user'] }, { required: ['team'] }], + }, + }, + token: { + title: 'Authentication Token', + type: 'string', + description: 'The token to use for authorization to GitHub', + }, + topics: { + title: 'Topics', + type: 'array', + items: { + type: 'string', + }, + }, + }, + }, + output: { + type: 'object', + properties: { + remoteUrl: { + title: 'A URL to the repository with the provider', + type: 'string', + }, + repoContentsUrl: { + title: 'A URL to the root of the repository', + type: 'string', + }, + }, + }, + }, + async handler(ctx) { + const { + repoUrl, + description, + access, + repoVisibility = 'private', + deleteBranchOnMerge = false, + allowMergeCommit = true, + allowSquashMerge = true, + allowRebaseMerge = true, + collaborators, + topics, + token: providedToken, + } = ctx.input; + + const { owner, repo } = parseRepoUrl(repoUrl, integrations); + + if (!owner) { + throw new InputError('Invalid repository owner provided in repoUrl'); + } + + const octokitOptions = await getOctokitOptions({ + integrations, + credentialsProvider: githubCredentialsProvider, + token: providedToken, + repoUrl, + }); + + const client = new Octokit(octokitOptions); + + const user = await client.rest.users.getByUsername({ + username: owner, + }); + + const repoCreationPromise = + user.data.type === 'Organization' + ? client.rest.repos.createInOrg({ + name: repo, + org: owner, + private: repoVisibility === 'private', + visibility: repoVisibility, + description: description, + delete_branch_on_merge: deleteBranchOnMerge, + allow_merge_commit: allowMergeCommit, + allow_squash_merge: allowSquashMerge, + allow_rebase_merge: allowRebaseMerge, + }) + : client.rest.repos.createForAuthenticatedUser({ + name: repo, + private: repoVisibility === 'private', + description: description, + delete_branch_on_merge: deleteBranchOnMerge, + allow_merge_commit: allowMergeCommit, + allow_squash_merge: allowSquashMerge, + allow_rebase_merge: allowRebaseMerge, + }); + + let newRepo; + + try { + newRepo = (await repoCreationPromise).data; + } catch (e) { + assertError(e); + if (e.message === 'Resource not accessible by integration') { + ctx.logger.warn( + `The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`, + ); + } + throw new Error( + `Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`, + ); + } + + if (access?.startsWith(`${owner}/`)) { + const [, team] = access.split('/'); + await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: team, + owner, + repo, + permission: 'admin', + }); + // No need to add access if it's the person who owns the personal account + } else if (access && access !== owner) { + await client.rest.repos.addCollaborator({ + owner, + repo, + username: access, + permission: 'admin', + }); + } + + if (collaborators) { + for (const collaborator of collaborators) { + try { + if ('user' in collaborator) { + await client.rest.repos.addCollaborator({ + owner, + repo, + username: collaborator.user, + permission: collaborator.access, + }); + } else if ('team' in collaborator) { + await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: collaborator.team, + owner, + repo, + permission: collaborator.access, + }); + } + } catch (e) { + assertError(e); + const name = extractCollaboratorName(collaborator); + ctx.logger.warn( + `Skipping ${collaborator.access} access for ${name}, ${e.message}`, + ); + } + } + } + + if (topics) { + try { + await client.rest.repos.replaceAllTopics({ + owner, + repo, + names: topics.map(t => t.toLowerCase()), + }); + } catch (e) { + assertError(e); + ctx.logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`); + } + } + + const remoteUrl = newRepo.clone_url; + + ctx.output('remoteUrl', remoteUrl); + }, + }); +} + +function extractCollaboratorName( + collaborator: { user: string } | { team: string } | { username: string }, +) { + if ('username' in collaborator) return collaborator.username; + if ('user' in collaborator) return collaborator.user; + return collaborator.team; +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.test.ts new file mode 100644 index 0000000000..f23e3dde9f --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.test.ts @@ -0,0 +1,400 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateAction } from '../../types'; + +jest.mock('../helpers'); + +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { PassThrough } from 'stream'; +import { + enableBranchProtectionOnDefaultRepoBranch, + initRepoAndPush, +} from '../helpers'; +import { createGithubRepoPushAction } from './githubRepoPush'; + +const mockOctokit = { + rest: { + repos: { + get: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +describe('github:repo:push', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + let githubCredentialsProvider: GithubCredentialsProvider; + let action: TemplateAction; + + const mockContext = { + input: { + repoUrl: 'github.com?repo=repository&owner=owner', + description: 'description', + repoVisibility: 'private' as const, + access: 'owner/blam', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + githubCredentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + action = createGithubRepoPushAction({ + integrations, + config, + githubCredentialsProvider, + }); + }); + + it('should call initRepoAndPush with the correct values', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler(mockContext); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://github.com/clone/url.git', + defaultBranch: 'master', + auth: { username: 'x-access-token', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, + }); + }); + + it('should call initRepoAndPush with the correct defaultBranch main', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + defaultBranch: 'main', + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://github.com/clone/url.git', + defaultBranch: 'main', + auth: { username: 'x-access-token', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, + }); + }); + + it('should call initRepoAndPush with the configured defaultAuthor', async () => { + const customAuthorConfig = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + scaffolder: { + defaultAuthor: { + name: 'Test', + email: 'example@example.com', + }, + }, + }); + + const customAuthorIntegrations = + ScmIntegrations.fromConfig(customAuthorConfig); + const customAuthorAction = createGithubRepoPushAction({ + integrations: customAuthorIntegrations, + config: customAuthorConfig, + githubCredentialsProvider, + }); + + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await customAuthorAction.handler(mockContext); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://github.com/clone/url.git', + defaultBranch: 'master', + auth: { username: 'x-access-token', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: { name: 'Test', email: 'example@example.com' }, + }); + }); + + it('should call initRepoAndPush with the configured defaultCommitMessage', async () => { + const customAuthorConfig = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + scaffolder: { + defaultCommitMessage: 'Test commit message', + }, + }); + + const customAuthorIntegrations = + ScmIntegrations.fromConfig(customAuthorConfig); + const customAuthorAction = createGithubRepoPushAction({ + integrations: customAuthorIntegrations, + config: customAuthorConfig, + githubCredentialsProvider, + }); + + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await customAuthorAction.handler(mockContext); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://github.com/clone/url.git', + defaultBranch: 'master', + auth: { username: 'x-access-token', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: { email: undefined, name: undefined }, + }); + }); + + it('should call output with the remoteUrl and the repoContentsUrl', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler(mockContext); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/clone/url.git', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://github.com/html/url/blob/master', + ); + }); + + it('should use main as default branch', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + defaultBranch: 'main', + }, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/clone/url.git', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://github.com/html/url/blob/main', + ); + }); + + it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requireCodeOwnerReviews', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler(mockContext); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockOctokit, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: false, + requiredStatusCheckContexts: [], + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + requireCodeOwnerReviews: true, + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockOctokit, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: true, + requiredStatusCheckContexts: [], + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + requireCodeOwnerReviews: false, + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockOctokit, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: false, + requiredStatusCheckContexts: [], + }); + }); + + it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requiredStatusCheckContexts', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler(mockContext); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockOctokit, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: false, + requiredStatusCheckContexts: [], + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + requiredStatusCheckContexts: ['statusCheck'], + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockOctokit, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: false, + requiredStatusCheckContexts: ['statusCheck'], + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + requiredStatusCheckContexts: [], + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ + owner: 'owner', + client: mockOctokit, + repoName: 'repository', + logger: mockContext.logger, + defaultBranch: 'master', + requireCodeOwnerReviews: false, + requiredStatusCheckContexts: [], + }); + }); + + it('should not call enableBranchProtectionOnDefaultRepoBranch with protectDefaultBranch disabled', async () => { + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + clone_url: 'https://github.com/clone/url.git', + html_url: 'https://github.com/html/url', + }, + }); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + protectDefaultBranch: false, + }, + }); + + expect(enableBranchProtectionOnDefaultRepoBranch).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts new file mode 100644 index 0000000000..cdf9a9b4e6 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts @@ -0,0 +1,224 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Config } from '@backstage/config'; +import { assertError, InputError } from '@backstage/errors'; +import { + GithubCredentialsProvider, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { Octokit } from 'octokit'; +import { createTemplateAction } from '../../createTemplateAction'; +import { + enableBranchProtectionOnDefaultRepoBranch, + initRepoAndPush, +} from '../helpers'; +import { getRepoSourceDirectory, parseRepoUrl } from '../publish/util'; +import { getOctokitOptions } from './helpers'; + +/** + * Creates a new action that initializes a git repository of the content in the workspace + * and publishes it to GitHub. + * + * @public + */ +export function createGithubRepoPushAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; + githubCredentialsProvider?: GithubCredentialsProvider; +}) { + const { integrations, config, githubCredentialsProvider } = options; + + return createTemplateAction<{ + repoUrl: string; + description?: string; + defaultBranch?: string; + protectDefaultBranch?: boolean; + gitCommitMessage?: string; + gitAuthorName?: string; + gitAuthorEmail?: string; + requireCodeOwnerReviews?: boolean; + requiredStatusCheckContexts?: string[]; + sourcePath?: string; + token?: string; + }>({ + id: 'github:repo:push', + description: + 'Initializes a git repository of contents in workspace and publishes it to GitHub.', + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { + title: 'Repository Location', + description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, + type: 'string', + }, + requireCodeOwnerReviews: { + title: 'Require CODEOWNER Reviews?', + description: + 'Require an approved review in PR including files with a designated Code Owner', + type: 'boolean', + }, + requiredStatusCheckContexts: { + title: 'Required Status Check Contexts', + description: + 'The list of status checks to require in order to merge into this branch', + type: 'array', + items: { + type: 'string', + }, + }, + defaultBranch: { + title: 'Default Branch', + type: 'string', + description: `Sets the default branch on the repository. The default value is 'master'`, + }, + protectDefaultBranch: { + title: 'Protect Default Branch', + type: 'boolean', + description: `Protect the default branch after creating the repository. The default value is 'true'`, + }, + gitCommitMessage: { + title: 'Git Commit Message', + type: 'string', + description: `Sets the commit message on the repository. The default value is 'initial commit'`, + }, + gitAuthorName: { + title: 'Default Author Name', + type: 'string', + description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, + }, + gitAuthorEmail: { + title: 'Default Author Email', + type: 'string', + description: `Sets the default author email for the commit.`, + }, + sourcePath: { + title: 'Source Path', + description: + 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.', + type: 'string', + }, + token: { + title: 'Authentication Token', + type: 'string', + description: 'The token to use for authorization to GitHub', + }, + topics: { + title: 'Topics', + type: 'array', + items: { + type: 'string', + }, + }, + }, + }, + output: { + type: 'object', + properties: { + remoteUrl: { + title: 'A URL to the repository with the provider', + type: 'string', + }, + repoContentsUrl: { + title: 'A URL to the root of the repository', + type: 'string', + }, + }, + }, + }, + async handler(ctx) { + const { + repoUrl, + defaultBranch = 'master', + protectDefaultBranch = true, + gitCommitMessage = 'initial commit', + gitAuthorName, + gitAuthorEmail, + requireCodeOwnerReviews = false, + requiredStatusCheckContexts = [], + token: providedToken, + } = ctx.input; + + const { owner, repo } = parseRepoUrl(repoUrl, integrations); + + if (!owner) { + throw new InputError('Invalid repository owner provided in repoUrl'); + } + + const octokitOptions = await getOctokitOptions({ + integrations, + credentialsProvider: githubCredentialsProvider, + token: providedToken, + repoUrl, + }); + + const client = new Octokit(octokitOptions); + + const targetRepo = await client.rest.repos.get({ owner, repo }); + + const remoteUrl = targetRepo.data.clone_url; + const repoContentsUrl = `${targetRepo.data.html_url}/blob/${defaultBranch}`; + + const gitAuthorInfo = { + name: gitAuthorName + ? gitAuthorName + : config.getOptionalString('scaffolder.defaultAuthor.name'), + email: gitAuthorEmail + ? gitAuthorEmail + : config.getOptionalString('scaffolder.defaultAuthor.email'), + }; + + await initRepoAndPush({ + dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), + remoteUrl, + defaultBranch, + auth: { + username: 'x-access-token', + password: octokitOptions.auth, + }, + logger: ctx.logger, + commitMessage: gitCommitMessage + ? gitCommitMessage + : config.getOptionalString('scaffolder.defaultCommitMessage'), + gitAuthorInfo, + }); + + if (protectDefaultBranch) { + try { + await enableBranchProtectionOnDefaultRepoBranch({ + owner, + client, + repoName: repo, + logger: ctx.logger, + defaultBranch, + requireCodeOwnerReviews, + requiredStatusCheckContexts, + }); + } catch (e) { + assertError(e); + ctx.logger.warn( + `Skipping: default branch protection on '${repo}', ${e.message}`, + ); + } + } + + ctx.output('remoteUrl', remoteUrl); + ctx.output('repoContentsUrl', repoContentsUrl); + }, + }); +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts index c5788afc3a..6746aef37e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/index.ts @@ -15,5 +15,7 @@ */ export { createGithubActionsDispatchAction } from './githubActionsDispatch'; -export { createGithubWebhookAction } from './githubWebhook'; export { createGithubIssuesLabelAction } from './githubIssuesLabel'; +export { createGithubRepoCreateAction } from './githubRepoCreate'; +export { createGithubRepoPushAction } from './githubRepoPush'; +export { createGithubWebhookAction } from './githubWebhook'; From 2db07887cb8f37f47afd0626626425c56e4fd40b Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Sat, 18 Jun 2022 14:10:41 +0100 Subject: [PATCH 013/101] chore: changeset Signed-off-by: Marco Crivellaro --- .changeset/thirty-rivers-watch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/thirty-rivers-watch.md diff --git a/.changeset/thirty-rivers-watch.md b/.changeset/thirty-rivers-watch.md new file mode 100644 index 0000000000..d92343f365 --- /dev/null +++ b/.changeset/thirty-rivers-watch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Added two new scaffolder actions: `github:repo:create` and `github:repo:push` From 4e8787ac5755d9bcd2a4c7323ad2ea56d0b2b102 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Sat, 18 Jun 2022 15:44:53 +0100 Subject: [PATCH 014/101] chore: api-report Signed-off-by: Marco Crivellaro --- plugins/scaffolder-backend/api-report.md | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 707b8e62ba..029000399b 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -168,6 +168,58 @@ export type CreateGithubPullRequestClientFactoryInput = { token?: string; }; +// @public +export function createGithubRepoCreateAction(options: { + integrations: ScmIntegrationRegistry; + githubCredentialsProvider?: GithubCredentialsProvider; +}): TemplateAction<{ + repoUrl: string; + description?: string | undefined; + access?: string | undefined; + deleteBranchOnMerge?: boolean | undefined; + gitAuthorName?: string | undefined; + gitAuthorEmail?: string | undefined; + allowRebaseMerge?: boolean | undefined; + allowSquashMerge?: boolean | undefined; + allowMergeCommit?: boolean | undefined; + requireCodeOwnerReviews?: boolean | undefined; + requiredStatusCheckContexts?: string[] | undefined; + repoVisibility?: 'internal' | 'private' | 'public' | undefined; + collaborators?: + | ( + | { + user: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + team: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + )[] + | undefined; + token?: string | undefined; + topics?: string[] | undefined; +}>; + +// @public +export function createGithubRepoPushAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; + githubCredentialsProvider?: GithubCredentialsProvider; +}): TemplateAction<{ + repoUrl: string; + description?: string | undefined; + defaultBranch?: string | undefined; + protectDefaultBranch?: boolean | undefined; + gitCommitMessage?: string | undefined; + gitAuthorName?: string | undefined; + gitAuthorEmail?: string | undefined; + requireCodeOwnerReviews?: boolean | undefined; + requiredStatusCheckContexts?: string[] | undefined; + sourcePath?: string | undefined; + token?: string | undefined; +}>; + // @public export function createGithubWebhookAction(options: { integrations: ScmIntegrationRegistry; From 411e8e60819013fbcf847ded2be8d577abf4fc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Mon, 20 Jun 2022 17:32:29 +0200 Subject: [PATCH 015/101] chore: add custom types for transform function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to avoid exposing complex types from our internal deps. Signed-off-by: Cรดme Tresarrieu --- .../MarkdownContent/MarkdownContent.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index 4059482b79..3052aff7d9 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -64,12 +64,23 @@ const useStyles = makeStyles( { name: 'BackstageMarkdownContent' }, ); +type TransformLink = ( + href: string, + // Complex type from internal react-markdown dep library (hast)[./node_modules/@types/hast/index.d.ts] + children: any[], + title: string | null, +) => string; + type Props = { content: string; dialect?: 'gfm' | 'common-mark'; - linkTarget?: Options['linkTarget']; - transformLinkUri?: Options['transformLinkUri']; - transformImageUri?: Options['transformImageUri']; + linkTarget?: React.HTMLAttributeAnchorTarget | TransformLink; + transformLinkUri?: TransformLink; + transformImageUri?: ( + src: string, + alt: string, + title: string | null, + ) => string; }; const components: Options['components'] = { From fccddd80fd865c959a459c9e20375dd4270c855e Mon Sep 17 00:00:00 2001 From: Vladimir Masarik Date: Mon, 20 Jun 2022 17:25:00 +0200 Subject: [PATCH 016/101] docs: fix prettier complaints Signed-off-by: Vladimir Masarik --- docs/features/software-templates/writing-custom-actions.md | 2 +- docs/features/software-templates/writing-templates.md | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index abff0cf7c7..a9e1d297f3 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -137,7 +137,7 @@ export default async function createPlugin( config: env.config, reader: env.reader, }); - + const actions = [...builtInActions, createNewFileAction()]; return createRouter({ diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 3173a0500a..40b735764c 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -513,14 +513,12 @@ spec: title: URL endpoint type: string description: URL endpoint at which the component can be reached - default: "https://www.example.com" + default: 'https://www.example.com' enabledDB: title: Enable Database type: boolean default: false - -... - + ... steps: - id: fetch-base name: Fetch Base From 87516675416340b2a7e3dc7c1749c1b62599b052 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:02:41 +0000 Subject: [PATCH 017/101] fix(deps): update dependency kafkajs to v2 Signed-off-by: Renovate Bot --- .changeset/renovate-149779d.md | 5 +++++ plugins/kafka-backend/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .changeset/renovate-149779d.md diff --git a/.changeset/renovate-149779d.md b/.changeset/renovate-149779d.md new file mode 100644 index 0000000000..58d764485e --- /dev/null +++ b/.changeset/renovate-149779d.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kafka-backend': patch +--- + +Updated dependency `kafkajs` to `^2.0.0`. diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index b78c297586..ab9c515b69 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -42,7 +42,7 @@ "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "kafkajs": "^1.16.0-beta.6", + "kafkajs": "^2.0.0", "lodash": "^4.17.21", "winston": "^3.2.1" }, diff --git a/yarn.lock b/yarn.lock index 2b5e08220d..7036ba99a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16844,10 +16844,10 @@ jwt-decode@*, jwt-decode@^3.1.0: resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== -kafkajs@^1.16.0-beta.6: - version "1.16.0" - resolved "https://registry.npmjs.org/kafkajs/-/kafkajs-1.16.0.tgz#bfcc3ae2b69265ca8435b53a01ee9e8787b9fee5" - integrity sha512-+Rcfu2hyQ/jv5skqRY8xA7Ra+mmRkDAzCaLDYbkGtgsNKpzxPWiLbk8ub0dgr4EbWrN1Zb4BCXHUkD6+zYfdWg== +kafkajs@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/kafkajs/-/kafkajs-2.0.2.tgz#cdfc8f57aa4fd69f6d9ca1cce4ee89bbc2a3a1f9" + integrity sha512-g6CM3fAenofOjR1bfOAqeZUEaSGhNtBscNokybSdW1rmIKYNwBPC9xQzwulFJm36u/xcxXUiCl/L/qfslapihA== keyv-memcache@^1.2.5: version "1.2.9" From 679b32172eeddb53886f078efbb3de23eb4d308a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:03:17 +0000 Subject: [PATCH 018/101] fix(deps): update dependency knex to v2 Signed-off-by: Renovate Bot --- .changeset/renovate-7438bff.md | 16 ++++++++++++++++ packages/backend-common/package.json | 2 +- packages/backend-tasks/package.json | 2 +- packages/backend-test-utils/package.json | 2 +- plugins/app-backend/package.json | 2 +- plugins/auth-backend/package.json | 2 +- plugins/bazaar-backend/package.json | 2 +- plugins/catalog-backend/package.json | 2 +- plugins/code-coverage-backend/package.json | 2 +- plugins/scaffolder-backend/package.json | 2 +- plugins/search-backend-module-pg/package.json | 2 +- plugins/tech-insights-backend/package.json | 2 +- plugins/techdocs-backend/package.json | 2 +- yarn.lock | 8 ++++---- 14 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 .changeset/renovate-7438bff.md diff --git a/.changeset/renovate-7438bff.md b/.changeset/renovate-7438bff.md new file mode 100644 index 0000000000..43a4ac5016 --- /dev/null +++ b/.changeset/renovate-7438bff.md @@ -0,0 +1,16 @@ +--- +'@backstage/backend-common': patch +'@backstage/backend-tasks': patch +'@backstage/backend-test-utils': patch +'@backstage/plugin-app-backend': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-bazaar-backend': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-code-coverage-backend': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-search-backend-module-pg': patch +'@backstage/plugin-tech-insights-backend': patch +'@backstage/plugin-techdocs-backend': patch +--- + +Updated dependency `knex` to `^2.0.0`. diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 9c459373ff..e8ebcb9ea2 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -65,7 +65,7 @@ "keyv": "^4.0.3", "keyv-memcache": "^1.2.5", "@keyv/redis": "^2.2.3", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "logform": "^2.3.2", "luxon": "^2.3.1", diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index f99baea1d5..2cbfd0b290 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -39,7 +39,7 @@ "@backstage/types": "^1.0.0", "@types/luxon": "^2.0.4", "cron": "^2.0.0", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "luxon": "^2.0.2", "node-abort-controller": "^3.0.1", diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 56aa23bfd8..1dc7ddc821 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -38,7 +38,7 @@ "@backstage/cli": "^0.17.3-next.0", "@backstage/config": "^1.0.1", "better-sqlite3": "^7.5.0", - "knex": "^1.0.2", + "knex": "^2.0.0", "msw": "^0.42.0", "mysql2": "^2.2.5", "pg": "^8.3.0", diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 173b15c80c..7a62720f8b 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -43,7 +43,7 @@ "express-promise-router": "^4.1.0", "fs-extra": "10.1.0", "helmet": "^5.0.2", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "luxon": "^2.0.2", "winston": "^3.2.1", diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index c1fe319e3c..8403979f6f 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -53,7 +53,7 @@ "google-auth-library": "^8.0.0", "jose": "^4.6.0", "jwt-decode": "^3.1.0", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "luxon": "^2.0.2", "minimatch": "^5.0.0", diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index c0769fe5b8..51b4a7786b 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -29,7 +29,7 @@ "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "knex": "^1.0.2", + "knex": "^2.0.0", "winston": "^3.2.1", "yn": "^4.0.0" }, diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 61a8e68412..99037b09f7 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -55,7 +55,7 @@ "fs-extra": "10.1.0", "git-url-parse": "^11.6.0", "glob": "^7.1.6", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "luxon": "^2.0.2", "node-fetch": "^2.6.7", diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index 78b57974b9..d63fd88ad8 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -33,7 +33,7 @@ "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-xml-bodyparser": "^0.3.0", - "knex": "^1.0.2", + "knex": "^2.0.0", "uuid": "^8.3.2", "winston": "^3.2.1", "yn": "^4.0.0" diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index dba891bfa2..8531523034 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -59,7 +59,7 @@ "isbinaryfile": "^5.0.0", "isomorphic-git": "^1.8.0", "jsonschema": "^1.2.6", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "luxon": "^2.0.2", "morgan": "^1.10.0", diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index cf072f5fe3..86c30f63d3 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -27,7 +27,7 @@ "@backstage/plugin-search-backend-node": "^0.6.3-next.0", "@backstage/plugin-search-common": "^0.3.5", "lodash": "^4.17.21", - "knex": "^1.0.2" + "knex": "^2.0.0" }, "devDependencies": { "@backstage/backend-test-utils": "^0.1.26-next.0", diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 5461f22789..f0f9d0491a 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -45,7 +45,7 @@ "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "luxon": "^2.0.2", "semver": "^7.3.5", diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index bf29eeb9af..d42f34e1a4 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -49,7 +49,7 @@ "express": "^4.17.1", "express-promise-router": "^4.1.0", "fs-extra": "10.1.0", - "knex": "^1.0.2", + "knex": "^2.0.0", "lodash": "^4.17.21", "node-fetch": "^2.6.7", "p-limit": "^3.1.0", diff --git a/yarn.lock b/yarn.lock index 2b5e08220d..8d8b0acbc7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16906,10 +16906,10 @@ kleur@^4.0.3, kleur@^4.1.4: resolved "https://registry.npmjs.org/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== -knex@^1.0.2: - version "1.0.7" - resolved "https://registry.npmjs.org/knex/-/knex-1.0.7.tgz#965f4490efc451b140aac4c5c6efa39fd877597b" - integrity sha512-89jxuRATt4qJMb9ZyyaKBy0pQ4d5h7eOFRqiNFnUvsgU+9WZ2eIaZKrAPG1+F3mgu5UloPUnkVE5Yo2sKZUs6Q== +knex@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/knex/-/knex-2.1.0.tgz#9348aace3a08ff5be26eb1c8e838416ddf1aa216" + integrity sha512-vVsnD6UJdSJy55TvCXfFF9syfwyXNxfE9mvr2hJL/4Obciy2EPGoqjDpgRSlMruHuPWDOeYAG25nyrGvU+jJog== dependencies: colorette "2.0.16" commander "^9.1.0" From 2b74a8505985b68b31d90c3bbff614ebe27af157 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Tue, 21 Jun 2022 17:42:31 -0500 Subject: [PATCH 019/101] Added details about installing addons package Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/features/techdocs/addons.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/features/techdocs/addons.md b/docs/features/techdocs/addons.md index 831729d093..33d97521d3 100644 --- a/docs/features/techdocs/addons.md +++ b/docs/features/techdocs/addons.md @@ -52,6 +52,8 @@ Addons are rendered in the order in which they are registered. ## Installing and using Addons +To start using Addons you need to add the `@backstage/plugin-techdocs-module-addons-contrib` package you your app, you can do that by running this command form the root of your project: `yarn add --cwd packages/app @backstage/plugin-techdocs-module-addons-contrib` + Addons can be installed and configured in much the same way as extensions for other Backstage plugins: by adding them underneath an extension registry component (``) under the route representing the TechDocs Reader From dabbb286f7e345f4c1d522a2cd40c1353a8a0952 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Wed, 22 Jun 2022 06:58:23 -0500 Subject: [PATCH 020/101] Corrected typos Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/features/techdocs/addons.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/addons.md b/docs/features/techdocs/addons.md index 33d97521d3..a4e1de7d4f 100644 --- a/docs/features/techdocs/addons.md +++ b/docs/features/techdocs/addons.md @@ -52,7 +52,7 @@ Addons are rendered in the order in which they are registered. ## Installing and using Addons -To start using Addons you need to add the `@backstage/plugin-techdocs-module-addons-contrib` package you your app, you can do that by running this command form the root of your project: `yarn add --cwd packages/app @backstage/plugin-techdocs-module-addons-contrib` +To start using Addons you need to add the `@backstage/plugin-techdocs-module-addons-contrib` package to your app. You can do that by running this command from the root of your project: `yarn add --cwd packages/app @backstage/plugin-techdocs-module-addons-contrib` Addons can be installed and configured in much the same way as extensions for other Backstage plugins: by adding them underneath an extension registry From e150231eb962bbe8616580ff3c6e1ab76c2cdfb6 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Tue, 21 Jun 2022 14:23:08 +0100 Subject: [PATCH 021/101] refactor: DRY on input and output properties Signed-off-by: Marco Crivellaro --- .../builtin/github/githubRepoCreate.ts | 131 ++----------- .../actions/builtin/github/githubRepoPush.ts | 85 ++------- .../actions/builtin/github/inputProperties.ts | 161 ++++++++++++++++ .../builtin/github/outputProperties.ts | 27 +++ .../actions/builtin/publish/github.ts | 175 +++--------------- 5 files changed, 248 insertions(+), 331 deletions(-) create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/outputProperties.ts diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts index 494a47516c..a725602e07 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts @@ -22,6 +22,8 @@ import { Octokit } from 'octokit'; import { createTemplateAction } from '../../createTemplateAction'; import { parseRepoUrl } from '../publish/util'; import { getOctokitOptions } from './helpers'; +import * as inputProps from './inputProperties'; +import * as outputProps from './outputProperties'; /** * Creates a new action that initializes a git repository @@ -67,123 +69,26 @@ export function createGithubRepoCreateAction(options: { type: 'object', required: ['repoUrl'], properties: { - repoUrl: { - title: 'Repository Location', - description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, - type: 'string', - }, - description: { - title: 'Repository Description', - type: 'string', - }, - access: { - title: 'Repository Access', - description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`, - type: 'string', - }, - requireCodeOwnerReviews: { - title: 'Require CODEOWNER Reviews?', - description: - 'Require an approved review in PR including files with a designated Code Owner', - type: 'boolean', - }, - requiredStatusCheckContexts: { - title: 'Required Status Check Contexts', - description: - 'The list of status checks to require in order to merge into this branch', - type: 'array', - items: { - type: 'string', - }, - }, - repoVisibility: { - title: 'Repository Visibility', - type: 'string', - enum: ['private', 'public', 'internal'], - }, - deleteBranchOnMerge: { - title: 'Delete Branch On Merge', - type: 'boolean', - description: `Delete the branch after merging the PR. The default value is 'false'`, - }, - gitAuthorName: { - title: 'Default Author Name', - type: 'string', - description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, - }, - gitAuthorEmail: { - title: 'Default Author Email', - type: 'string', - description: `Sets the default author email for the commit.`, - }, - allowMergeCommit: { - title: 'Allow Merge Commits', - type: 'boolean', - description: `Allow merge commits. The default value is 'true'`, - }, - allowSquashMerge: { - title: 'Allow Squash Merges', - type: 'boolean', - description: `Allow squash merges. The default value is 'true'`, - }, - allowRebaseMerge: { - title: 'Allow Rebase Merges', - type: 'boolean', - description: `Allow rebase merges. The default value is 'true'`, - }, - collaborators: { - title: 'Collaborators', - description: 'Provide additional users or teams with permissions', - type: 'array', - items: { - type: 'object', - additionalProperties: false, - required: ['access'], - properties: { - access: { - type: 'string', - description: 'The type of access for the user', - enum: ['push', 'pull', 'admin', 'maintain', 'triage'], - }, - user: { - type: 'string', - description: - 'The name of the user that will be added as a collaborator', - }, - team: { - type: 'string', - description: - 'The name of the team that will be added as a collaborator', - }, - }, - oneOf: [{ required: ['user'] }, { required: ['team'] }], - }, - }, - token: { - title: 'Authentication Token', - type: 'string', - description: 'The token to use for authorization to GitHub', - }, - topics: { - title: 'Topics', - type: 'array', - items: { - type: 'string', - }, - }, + repoUrl: inputProps.repoUrl, + description: inputProps.description, + access: inputProps.access, + requireCodeOwnerReviews: inputProps.requireCodeOwnerReviews, + requiredStatusCheckContexts: inputProps.requiredStatusCheckContexts, + repoVisibility: inputProps.repoVisibility, + deleteBranchOnMerge: inputProps.deleteBranchOnMerge, + allowMergeCommit: inputProps.allowMergeCommit, + allowSquashMerge: inputProps.allowSquashMerge, + allowRebaseMerge: inputProps.allowRebaseMerge, + collaborators: inputProps.collaborators, + token: inputProps.token, + topics: inputProps.topics, }, }, output: { type: 'object', properties: { - remoteUrl: { - title: 'A URL to the repository with the provider', - type: 'string', - }, - repoContentsUrl: { - title: 'A URL to the root of the repository', - type: 'string', - }, + remoteUrl: outputProps.remoteUrl, + repoContentsUrl: outputProps.repoContentsUrl, }, }, }, @@ -212,7 +117,7 @@ export function createGithubRepoCreateAction(options: { integrations, credentialsProvider: githubCredentialsProvider, token: providedToken, - repoUrl, + repoUrl: repoUrl, }); const client = new Octokit(octokitOptions); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts index cdf9a9b4e6..df9f5463fe 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts @@ -27,6 +27,8 @@ import { } from '../helpers'; import { getRepoSourceDirectory, parseRepoUrl } from '../publish/util'; import { getOctokitOptions } from './helpers'; +import * as inputProps from './inputProperties'; +import * as outputProps from './outputProperties'; /** * Creates a new action that initializes a git repository of the content in the workspace @@ -62,82 +64,23 @@ export function createGithubRepoPushAction(options: { type: 'object', required: ['repoUrl'], properties: { - repoUrl: { - title: 'Repository Location', - description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, - type: 'string', - }, - requireCodeOwnerReviews: { - title: 'Require CODEOWNER Reviews?', - description: - 'Require an approved review in PR including files with a designated Code Owner', - type: 'boolean', - }, - requiredStatusCheckContexts: { - title: 'Required Status Check Contexts', - description: - 'The list of status checks to require in order to merge into this branch', - type: 'array', - items: { - type: 'string', - }, - }, - defaultBranch: { - title: 'Default Branch', - type: 'string', - description: `Sets the default branch on the repository. The default value is 'master'`, - }, - protectDefaultBranch: { - title: 'Protect Default Branch', - type: 'boolean', - description: `Protect the default branch after creating the repository. The default value is 'true'`, - }, - gitCommitMessage: { - title: 'Git Commit Message', - type: 'string', - description: `Sets the commit message on the repository. The default value is 'initial commit'`, - }, - gitAuthorName: { - title: 'Default Author Name', - type: 'string', - description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, - }, - gitAuthorEmail: { - title: 'Default Author Email', - type: 'string', - description: `Sets the default author email for the commit.`, - }, - sourcePath: { - title: 'Source Path', - description: - 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.', - type: 'string', - }, - token: { - title: 'Authentication Token', - type: 'string', - description: 'The token to use for authorization to GitHub', - }, - topics: { - title: 'Topics', - type: 'array', - items: { - type: 'string', - }, - }, + repoUrl: inputProps.repoUrl, + requireCodeOwnerReviews: inputProps.requireCodeOwnerReviews, + requiredStatusCheckContexts: inputProps.requiredStatusCheckContexts, + defaultBranch: inputProps.defaultBranch, + protectDefaultBranch: inputProps.protectDefaultBranch, + gitCommitMessage: inputProps.gitCommitMessage, + gitAuthorName: inputProps.gitAuthorName, + gitAuthorEmail: inputProps.gitAuthorEmail, + sourcePath: inputProps.sourcePath, + token: inputProps.token, }, }, output: { type: 'object', properties: { - remoteUrl: { - title: 'A URL to the repository with the provider', - type: 'string', - }, - repoContentsUrl: { - title: 'A URL to the root of the repository', - type: 'string', - }, + remoteUrl: outputProps.remoteUrl, + repoContentsUrl: outputProps.repoContentsUrl, }, }, }, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts new file mode 100644 index 0000000000..0aad402f1f --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/inputProperties.ts @@ -0,0 +1,161 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const repoUrl = { + title: 'Repository Location', + description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, + type: 'string', +}; +const description = { + title: 'Repository Description', + type: 'string', +}; +const access = { + title: 'Repository Access', + description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`, + type: 'string', +}; +const requireCodeOwnerReviews = { + title: 'Require CODEOWNER Reviews?', + description: + 'Require an approved review in PR including files with a designated Code Owner', + type: 'boolean', +}; +const requiredStatusCheckContexts = { + title: 'Required Status Check Contexts', + description: + 'The list of status checks to require in order to merge into this branch', + type: 'array', + items: { + type: 'string', + }, +}; +const repoVisibility = { + title: 'Repository Visibility', + type: 'string', + enum: ['private', 'public', 'internal'], +}; +const deleteBranchOnMerge = { + title: 'Delete Branch On Merge', + type: 'boolean', + description: `Delete the branch after merging the PR. The default value is 'false'`, +}; +const gitAuthorName = { + title: 'Default Author Name', + type: 'string', + description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, +}; +const gitAuthorEmail = { + title: 'Default Author Email', + type: 'string', + description: `Sets the default author email for the commit.`, +}; +const allowMergeCommit = { + title: 'Allow Merge Commits', + type: 'boolean', + description: `Allow merge commits. The default value is 'true'`, +}; +const allowSquashMerge = { + title: 'Allow Squash Merges', + type: 'boolean', + description: `Allow squash merges. The default value is 'true'`, +}; +const allowRebaseMerge = { + title: 'Allow Rebase Merges', + type: 'boolean', + description: `Allow rebase merges. The default value is 'true'`, +}; +const collaborators = { + title: 'Collaborators', + description: 'Provide additional users or teams with permissions', + type: 'array', + items: { + type: 'object', + additionalProperties: false, + required: ['access'], + properties: { + access: { + type: 'string', + description: 'The type of access for the user', + enum: ['push', 'pull', 'admin', 'maintain', 'triage'], + }, + user: { + type: 'string', + description: + 'The name of the user that will be added as a collaborator', + }, + team: { + type: 'string', + description: + 'The name of the team that will be added as a collaborator', + }, + }, + oneOf: [{ required: ['user'] }, { required: ['team'] }], + }, +}; +const token = { + title: 'Authentication Token', + type: 'string', + description: 'The token to use for authorization to GitHub', +}; +const topics = { + title: 'Topics', + type: 'array', + items: { + type: 'string', + }, +}; +const defaultBranch = { + title: 'Default Branch', + type: 'string', + description: `Sets the default branch on the repository. The default value is 'master'`, +}; +const protectDefaultBranch = { + title: 'Protect Default Branch', + type: 'boolean', + description: `Protect the default branch after creating the repository. The default value is 'true'`, +}; +const gitCommitMessage = { + title: 'Git Commit Message', + type: 'string', + description: `Sets the commit message on the repository. The default value is 'initial commit'`, +}; +const sourcePath = { + title: 'Source Path', + description: + 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.', + type: 'string', +}; + +export { access }; +export { allowMergeCommit }; +export { allowRebaseMerge }; +export { allowSquashMerge }; +export { collaborators }; +export { defaultBranch }; +export { deleteBranchOnMerge }; +export { description }; +export { gitAuthorEmail }; +export { gitAuthorName }; +export { gitCommitMessage }; +export { protectDefaultBranch }; +export { repoUrl }; +export { repoVisibility }; +export { requireCodeOwnerReviews }; +export { requiredStatusCheckContexts }; +export { sourcePath }; +export { token }; +export { topics }; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/outputProperties.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/outputProperties.ts new file mode 100644 index 0000000000..d5d6eb108f --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/outputProperties.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const remoteUrl = { + title: 'A URL to the repository with the provider', + type: 'string', +}; +const repoContentsUrl = { + title: 'A URL to the root of the repository', + type: 'string', +}; + +export { remoteUrl }; +export { repoContentsUrl }; 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 d9a7112fc9..145827702d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -13,20 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Config } from '@backstage/config'; +import { assertError, InputError } from '@backstage/errors'; import { GithubCredentialsProvider, ScmIntegrationRegistry, } from '@backstage/integration'; +import { Octokit } from 'octokit'; +import { createTemplateAction } from '../../createTemplateAction'; +import { getOctokitOptions } from '../github/helpers'; +import * as inputProps from '../github/inputProperties'; +import * as outputProps from '../github/outputProperties'; import { enableBranchProtectionOnDefaultRepoBranch, initRepoAndPush, } from '../helpers'; import { getRepoSourceDirectory, parseRepoUrl } from './util'; -import { createTemplateAction } from '../../createTemplateAction'; -import { Config } from '@backstage/config'; -import { assertError, InputError } from '@backstage/errors'; -import { getOctokitOptions } from '../github/helpers'; -import { Octokit } from 'octokit'; /** * Creates a new action that initializes a git repository of the content in the workspace @@ -84,153 +86,32 @@ export function createPublishGithubAction(options: { type: 'object', required: ['repoUrl'], properties: { - repoUrl: { - title: 'Repository Location', - description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`, - type: 'string', - }, - description: { - title: 'Repository Description', - type: 'string', - }, - access: { - title: 'Repository Access', - description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`, - type: 'string', - }, - requireCodeOwnerReviews: { - title: 'Require CODEOWNER Reviews?', - description: - 'Require an approved review in PR including files with a designated Code Owner', - type: 'boolean', - }, - requiredStatusCheckContexts: { - title: 'Required Status Check Contexts', - description: - 'The list of status checks to require in order to merge into this branch', - type: 'array', - items: { - type: 'string', - }, - }, - repoVisibility: { - title: 'Repository Visibility', - type: 'string', - enum: ['private', 'public', 'internal'], - }, - defaultBranch: { - title: 'Default Branch', - type: 'string', - description: `Sets the default branch on the repository. The default value is 'master'`, - }, - protectDefaultBranch: { - title: 'Protect Default Branch', - type: 'boolean', - description: `Protect the default branch after creating the repository. The default value is 'true'`, - }, - deleteBranchOnMerge: { - title: 'Delete Branch On Merge', - type: 'boolean', - description: `Delete the branch after merging the PR. The default value is 'false'`, - }, - gitCommitMessage: { - title: 'Git Commit Message', - type: 'string', - description: `Sets the commit message on the repository. The default value is 'initial commit'`, - }, - gitAuthorName: { - title: 'Default Author Name', - type: 'string', - description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, - }, - gitAuthorEmail: { - title: 'Default Author Email', - type: 'string', - description: `Sets the default author email for the commit.`, - }, - allowMergeCommit: { - title: 'Allow Merge Commits', - type: 'boolean', - description: `Allow merge commits. The default value is 'true'`, - }, - allowSquashMerge: { - title: 'Allow Squash Merges', - type: 'boolean', - description: `Allow squash merges. The default value is 'true'`, - }, - allowRebaseMerge: { - title: 'Allow Rebase Merges', - type: 'boolean', - description: `Allow rebase merges. The default value is 'true'`, - }, - sourcePath: { - title: 'Source Path', - description: - 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.', - type: 'string', - }, - collaborators: { - title: 'Collaborators', - description: 'Provide additional users or teams with permissions', - type: 'array', - items: { - type: 'object', - additionalProperties: false, - required: ['access'], - properties: { - access: { - type: 'string', - description: 'The type of access for the user', - enum: ['push', 'pull', 'admin', 'maintain', 'triage'], - }, - user: { - type: 'string', - description: - 'The name of the user that will be added as a collaborator', - }, - username: { - type: 'string', - description: - 'Deprecated. Use the `team` or `user` field instead.', - }, - team: { - type: 'string', - description: - 'The name of the team that will be added as a collaborator', - }, - }, - oneOf: [ - { required: ['user'] }, - { required: ['username'] }, - { required: ['team'] }, - ], - }, - }, - token: { - title: 'Authentication Token', - type: 'string', - description: 'The token to use for authorization to GitHub', - }, - topics: { - title: 'Topics', - type: 'array', - items: { - type: 'string', - }, - }, + repoUrl: inputProps.repoUrl, + description: inputProps.description, + access: inputProps.access, + requireCodeOwnerReviews: inputProps.requireCodeOwnerReviews, + requiredStatusCheckContexts: inputProps.requiredStatusCheckContexts, + repoVisibility: inputProps.repoVisibility, + defaultBranch: inputProps.defaultBranch, + protectDefaultBranch: inputProps.protectDefaultBranch, + deleteBranchOnMerge: inputProps.deleteBranchOnMerge, + gitCommitMessage: inputProps.gitCommitMessage, + gitAuthorName: inputProps.gitAuthorName, + gitAuthorEmail: inputProps.gitAuthorEmail, + allowMergeCommit: inputProps.allowMergeCommit, + allowSquashMerge: inputProps.allowSquashMerge, + allowRebaseMerge: inputProps.allowRebaseMerge, + sourcePath: inputProps.sourcePath, + collaborators: inputProps.collaborators, + token: inputProps.token, + topics: inputProps.topics, }, }, output: { type: 'object', properties: { - remoteUrl: { - title: 'A URL to the repository with the provider', - type: 'string', - }, - repoContentsUrl: { - title: 'A URL to the root of the repository', - type: 'string', - }, + remoteUrl: outputProps.remoteUrl, + repoContentsUrl: outputProps.repoContentsUrl, }, }, }, From 4271cc7ecced991ce8e5498330e414325a239b64 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Wed, 22 Jun 2022 11:02:42 +0100 Subject: [PATCH 022/101] refactor repo create, repo push Signed-off-by: Marco Crivellaro --- .../builtin/github/githubRepoCreate.ts | 149 ++-------- .../actions/builtin/github/githubRepoPush.ts | 66 ++--- .../actions/builtin/github/helpers.ts | 221 ++++++++++++++- .../src/scaffolder/actions/builtin/helpers.ts | 18 +- .../actions/builtin/publish/github.ts | 266 ++++++------------ 5 files changed, 369 insertions(+), 351 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts index a725602e07..1e92d91a64 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoCreate.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { assertError, InputError } from '@backstage/errors'; +import { InputError } from '@backstage/errors'; import { GithubCredentialsProvider, ScmIntegrationRegistry, @@ -21,7 +21,10 @@ import { import { Octokit } from 'octokit'; import { createTemplateAction } from '../../createTemplateAction'; import { parseRepoUrl } from '../publish/util'; -import { getOctokitOptions } from './helpers'; +import { + createGithubRepoWithCollaboratorsAndTopics, + getOctokitOptions, +} from './helpers'; import * as inputProps from './inputProperties'; import * as outputProps from './outputProperties'; @@ -58,6 +61,11 @@ export function createGithubRepoCreateAction(options: { team: string; access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; } + | { + /** @deprecated This field is deprecated in favor of team */ + username: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } >; token?: string; topics?: string[]; @@ -107,136 +115,37 @@ export function createGithubRepoCreateAction(options: { token: providedToken, } = ctx.input; - const { owner, repo } = parseRepoUrl(repoUrl, integrations); - - if (!owner) { - throw new InputError('Invalid repository owner provided in repoUrl'); - } - const octokitOptions = await getOctokitOptions({ integrations, credentialsProvider: githubCredentialsProvider, token: providedToken, repoUrl: repoUrl, }); - const client = new Octokit(octokitOptions); - const user = await client.rest.users.getByUsername({ - username: owner, - }); + const { owner, repo } = parseRepoUrl(repoUrl, integrations); - const repoCreationPromise = - user.data.type === 'Organization' - ? client.rest.repos.createInOrg({ - name: repo, - org: owner, - private: repoVisibility === 'private', - visibility: repoVisibility, - description: description, - delete_branch_on_merge: deleteBranchOnMerge, - allow_merge_commit: allowMergeCommit, - allow_squash_merge: allowSquashMerge, - allow_rebase_merge: allowRebaseMerge, - }) - : client.rest.repos.createForAuthenticatedUser({ - name: repo, - private: repoVisibility === 'private', - description: description, - delete_branch_on_merge: deleteBranchOnMerge, - allow_merge_commit: allowMergeCommit, - allow_squash_merge: allowSquashMerge, - allow_rebase_merge: allowRebaseMerge, - }); - - let newRepo; - - try { - newRepo = (await repoCreationPromise).data; - } catch (e) { - assertError(e); - if (e.message === 'Resource not accessible by integration') { - ctx.logger.warn( - `The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`, - ); - } - throw new Error( - `Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`, - ); + if (!owner) { + throw new InputError('Invalid repository owner provided in repoUrl'); } - if (access?.startsWith(`${owner}/`)) { - const [, team] = access.split('/'); - await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ - org: owner, - team_slug: team, - owner, - repo, - permission: 'admin', - }); - // No need to add access if it's the person who owns the personal account - } else if (access && access !== owner) { - await client.rest.repos.addCollaborator({ - owner, - repo, - username: access, - permission: 'admin', - }); - } + const newRepo = await createGithubRepoWithCollaboratorsAndTopics( + client, + repo, + owner, + repoVisibility, + description, + deleteBranchOnMerge, + allowMergeCommit, + allowSquashMerge, + allowRebaseMerge, + access, + collaborators, + topics, + ctx.logger, + ); - if (collaborators) { - for (const collaborator of collaborators) { - try { - if ('user' in collaborator) { - await client.rest.repos.addCollaborator({ - owner, - repo, - username: collaborator.user, - permission: collaborator.access, - }); - } else if ('team' in collaborator) { - await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ - org: owner, - team_slug: collaborator.team, - owner, - repo, - permission: collaborator.access, - }); - } - } catch (e) { - assertError(e); - const name = extractCollaboratorName(collaborator); - ctx.logger.warn( - `Skipping ${collaborator.access} access for ${name}, ${e.message}`, - ); - } - } - } - - if (topics) { - try { - await client.rest.repos.replaceAllTopics({ - owner, - repo, - names: topics.map(t => t.toLowerCase()), - }); - } catch (e) { - assertError(e); - ctx.logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`); - } - } - - const remoteUrl = newRepo.clone_url; - - ctx.output('remoteUrl', remoteUrl); + ctx.output('remoteUrl', newRepo.clone_url); }, }); } - -function extractCollaboratorName( - collaborator: { user: string } | { team: string } | { username: string }, -) { - if ('username' in collaborator) return collaborator.username; - if ('user' in collaborator) return collaborator.user; - return collaborator.team; -} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts index df9f5463fe..942ebe578d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubRepoPush.ts @@ -14,19 +14,15 @@ * limitations under the License. */ import { Config } from '@backstage/config'; -import { assertError, InputError } from '@backstage/errors'; +import { InputError } from '@backstage/errors'; import { GithubCredentialsProvider, ScmIntegrationRegistry, } from '@backstage/integration'; import { Octokit } from 'octokit'; import { createTemplateAction } from '../../createTemplateAction'; -import { - enableBranchProtectionOnDefaultRepoBranch, - initRepoAndPush, -} from '../helpers'; -import { getRepoSourceDirectory, parseRepoUrl } from '../publish/util'; -import { getOctokitOptions } from './helpers'; +import { parseRepoUrl } from '../publish/util'; +import { getOctokitOptions, initRepoPushAndProtect } from './helpers'; import * as inputProps from './inputProperties'; import * as outputProps from './outputProperties'; @@ -117,48 +113,24 @@ export function createGithubRepoPushAction(options: { const remoteUrl = targetRepo.data.clone_url; const repoContentsUrl = `${targetRepo.data.html_url}/blob/${defaultBranch}`; - const gitAuthorInfo = { - name: gitAuthorName - ? gitAuthorName - : config.getOptionalString('scaffolder.defaultAuthor.name'), - email: gitAuthorEmail - ? gitAuthorEmail - : config.getOptionalString('scaffolder.defaultAuthor.email'), - }; - - await initRepoAndPush({ - dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), + await initRepoPushAndProtect( remoteUrl, + octokitOptions.auth, + ctx.workspacePath, + ctx.input.sourcePath, defaultBranch, - auth: { - username: 'x-access-token', - password: octokitOptions.auth, - }, - logger: ctx.logger, - commitMessage: gitCommitMessage - ? gitCommitMessage - : config.getOptionalString('scaffolder.defaultCommitMessage'), - gitAuthorInfo, - }); - - if (protectDefaultBranch) { - try { - await enableBranchProtectionOnDefaultRepoBranch({ - owner, - client, - repoName: repo, - logger: ctx.logger, - defaultBranch, - requireCodeOwnerReviews, - requiredStatusCheckContexts, - }); - } catch (e) { - assertError(e); - ctx.logger.warn( - `Skipping: default branch protection on '${repo}', ${e.message}`, - ); - } - } + protectDefaultBranch, + owner, + client, + repo, + requireCodeOwnerReviews, + requiredStatusCheckContexts, + config, + ctx.logger, + gitCommitMessage, + gitAuthorName, + gitAuthorEmail, + ); ctx.output('remoteUrl', remoteUrl); ctx.output('repoContentsUrl', repoContentsUrl); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts index 29997e5843..c8f508e4c0 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/helpers.ts @@ -13,14 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InputError } from '@backstage/errors'; +import { Config } from '@backstage/config'; +import { assertError, InputError } from '@backstage/errors'; import { DefaultGithubCredentialsProvider, GithubCredentialsProvider, ScmIntegrationRegistry, } from '@backstage/integration'; import { OctokitOptions } from '@octokit/core/dist-types/types'; -import { parseRepoUrl } from '../publish/util'; +import { Octokit } from 'octokit'; +import { Logger } from 'winston'; +import { + enableBranchProtectionOnDefaultRepoBranch, + initRepoAndPush, +} from '../helpers'; +import { getRepoSourceDirectory, parseRepoUrl } from '../publish/util'; const DEFAULT_TIMEOUT_MS = 60_000; @@ -83,3 +90,213 @@ export async function getOctokitOptions(options: { previews: ['nebula-preview'], }; } + +export async function createGithubRepoWithCollaboratorsAndTopics( + client: Octokit, + repo: string, + owner: string, + repoVisibility: 'private' | 'internal' | 'public', + description: string | undefined, + deleteBranchOnMerge: boolean, + allowMergeCommit: boolean, + allowSquashMerge: boolean, + allowRebaseMerge: boolean, + access: string | undefined, + collaborators: + | ( + | { + user: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + team: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + | { + /** @deprecated This field is deprecated in favor of team */ + username: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } + )[] + | undefined, + topics: string[] | undefined, + logger: Logger, +) { + const user = await client.rest.users.getByUsername({ + username: owner, + }); + + const repoCreationPromise = + user.data.type === 'Organization' + ? client.rest.repos.createInOrg({ + name: repo, + org: owner, + private: repoVisibility === 'private', + visibility: repoVisibility, + description: description, + delete_branch_on_merge: deleteBranchOnMerge, + allow_merge_commit: allowMergeCommit, + allow_squash_merge: allowSquashMerge, + allow_rebase_merge: allowRebaseMerge, + }) + : client.rest.repos.createForAuthenticatedUser({ + name: repo, + private: repoVisibility === 'private', + description: description, + delete_branch_on_merge: deleteBranchOnMerge, + allow_merge_commit: allowMergeCommit, + allow_squash_merge: allowSquashMerge, + allow_rebase_merge: allowRebaseMerge, + }); + + let newRepo; + + try { + newRepo = (await repoCreationPromise).data; + } catch (e) { + assertError(e); + if (e.message === 'Resource not accessible by integration') { + logger.warn( + `The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`, + ); + } + throw new Error( + `Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`, + ); + } + + if (access?.startsWith(`${owner}/`)) { + const [, team] = access.split('/'); + await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: team, + owner, + repo, + permission: 'admin', + }); + // No need to add access if it's the person who owns the personal account + } else if (access && access !== owner) { + await client.rest.repos.addCollaborator({ + owner, + repo, + username: access, + permission: 'admin', + }); + } + + if (collaborators) { + for (const collaborator of collaborators) { + try { + if ('user' in collaborator) { + await client.rest.repos.addCollaborator({ + owner, + repo, + username: collaborator.user, + permission: collaborator.access, + }); + } else if ('team' in collaborator) { + await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ + org: owner, + team_slug: collaborator.team, + owner, + repo, + permission: collaborator.access, + }); + } + } catch (e) { + assertError(e); + const name = extractCollaboratorName(collaborator); + logger.warn( + `Skipping ${collaborator.access} access for ${name}, ${e.message}`, + ); + } + } + } + + if (topics) { + try { + await client.rest.repos.replaceAllTopics({ + owner, + repo, + names: topics.map(t => t.toLowerCase()), + }); + } catch (e) { + assertError(e); + logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`); + } + } + + return newRepo; +} + +export async function initRepoPushAndProtect( + remoteUrl: string, + password: string, + workspacePath: string, + sourcePath: string | undefined, + defaultBranch: string, + protectDefaultBranch: boolean, + owner: string, + client: Octokit, + repo: string, + requireCodeOwnerReviews: boolean, + requiredStatusCheckContexts: string[], + config: Config, + logger: any, + gitCommitMessage?: string, + gitAuthorName?: string, + gitAuthorEmail?: string, +) { + const gitAuthorInfo = { + name: gitAuthorName + ? gitAuthorName + : config.getOptionalString('scaffolder.defaultAuthor.name'), + email: gitAuthorEmail + ? gitAuthorEmail + : config.getOptionalString('scaffolder.defaultAuthor.email'), + }; + + const commitMessage = gitCommitMessage + ? gitCommitMessage + : config.getOptionalString('scaffolder.defaultCommitMessage'); + + await initRepoAndPush({ + dir: getRepoSourceDirectory(workspacePath, sourcePath), + remoteUrl, + defaultBranch, + auth: { + username: 'x-access-token', + password, + }, + logger, + commitMessage, + gitAuthorInfo, + }); + + if (protectDefaultBranch) { + try { + await enableBranchProtectionOnDefaultRepoBranch({ + owner, + client, + repoName: repo, + logger, + defaultBranch, + requireCodeOwnerReviews, + requiredStatusCheckContexts, + }); + } catch (e) { + assertError(e); + logger.warn( + `Skipping: default branch protection on '${repo}', ${e.message}`, + ); + } + } +} + +function extractCollaboratorName( + collaborator: { user: string } | { team: string } | { username: string }, +) { + if ('username' in collaborator) return collaborator.username; + if ('user' in collaborator) return collaborator.user; + return collaborator.team; +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts index 529a504cba..8753d44041 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts @@ -14,12 +14,13 @@ * limitations under the License. */ -import { SpawnOptionsWithoutStdio, spawn } from 'child_process'; +import { Git } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; +import { assertError } from '@backstage/errors'; +import { spawn, SpawnOptionsWithoutStdio } from 'child_process'; +import { Octokit } from 'octokit'; import { PassThrough, Writable } from 'stream'; import { Logger } from 'winston'; -import { Git } from '@backstage/backend-common'; -import { Octokit } from 'octokit'; -import { assertError } from '@backstage/errors'; /** @public */ export type RunCommandOptions = { @@ -200,3 +201,12 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({ await tryOnce(); } }; + +export function getGitCommitMessage( + gitCommitMessage: string | undefined, + config: Config, +): string | undefined { + return gitCommitMessage + ? gitCommitMessage + : config.getOptionalString('scaffolder.defaultCommitMessage'); +} 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 145827702d..87219ac1e6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -14,22 +14,21 @@ * limitations under the License. */ import { Config } from '@backstage/config'; -import { assertError, InputError } from '@backstage/errors'; +import { InputError } from '@backstage/errors'; import { GithubCredentialsProvider, ScmIntegrationRegistry, } from '@backstage/integration'; import { Octokit } from 'octokit'; import { createTemplateAction } from '../../createTemplateAction'; -import { getOctokitOptions } from '../github/helpers'; +import { + createGithubRepoWithCollaboratorsAndTopics, + getOctokitOptions, + initRepoPushAndProtect, +} from '../github/helpers'; import * as inputProps from '../github/inputProperties'; import * as outputProps from '../github/outputProperties'; -import { - enableBranchProtectionOnDefaultRepoBranch, - initRepoAndPush, -} from '../helpers'; -import { getRepoSourceDirectory, parseRepoUrl } from './util'; - +import { parseRepoUrl } from './util'; /** * Creates a new action that initializes a git repository of the content in the workspace * and publishes it to GitHub. @@ -137,192 +136,103 @@ export function createPublishGithubAction(options: { token: providedToken, } = ctx.input; + const octokitOptions = await getOctokitOptions({ + integrations, + credentialsProvider: githubCredentialsProvider, + token: providedToken, + repoUrl: repoUrl, + }); + const client = new Octokit(octokitOptions); + const { owner, repo } = parseRepoUrl(repoUrl, integrations); if (!owner) { throw new InputError('Invalid repository owner provided in repoUrl'); } - const octokitOptions = await getOctokitOptions({ - integrations, - credentialsProvider: githubCredentialsProvider, - token: providedToken, - repoUrl, - }); - - const client = new Octokit(octokitOptions); - - const user = await client.rest.users.getByUsername({ - username: owner, - }); - - const repoCreationPromise = - user.data.type === 'Organization' - ? client.rest.repos.createInOrg({ - name: repo, - org: owner, - private: repoVisibility === 'private', - visibility: repoVisibility, - description: description, - delete_branch_on_merge: deleteBranchOnMerge, - allow_merge_commit: allowMergeCommit, - allow_squash_merge: allowSquashMerge, - allow_rebase_merge: allowRebaseMerge, - }) - : client.rest.repos.createForAuthenticatedUser({ - name: repo, - private: repoVisibility === 'private', - description: description, - delete_branch_on_merge: deleteBranchOnMerge, - allow_merge_commit: allowMergeCommit, - allow_squash_merge: allowSquashMerge, - allow_rebase_merge: allowRebaseMerge, - }); - - let newRepo; - - try { - newRepo = (await repoCreationPromise).data; - } catch (e) { - assertError(e); - if (e.message === 'Resource not accessible by integration') { - ctx.logger.warn( - `The GitHub app or token provided may not have the required permissions to create the ${user.data.type} repository ${owner}/${repo}.`, - ); - } - throw new Error( - `Failed to create the ${user.data.type} repository ${owner}/${repo}, ${e.message}`, - ); - } - - if (access?.startsWith(`${owner}/`)) { - const [, team] = access.split('/'); - await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ - org: owner, - team_slug: team, - owner, - repo, - permission: 'admin', - }); - // No need to add access if it's the person who owns the personal account - } else if (access && access !== owner) { - await client.rest.repos.addCollaborator({ - owner, - repo, - username: access, - permission: 'admin', - }); - } - - if (collaborators) { - for (const collaborator of collaborators) { - try { - if ('user' in collaborator) { - await client.rest.repos.addCollaborator({ - owner, - repo, - username: collaborator.user, - permission: collaborator.access, - }); - } else if ('username' in collaborator) { - ctx.logger.warn( - 'The field `username` is deprecated in favor of `team` and will be removed in the future.', - ); - await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ - org: owner, - team_slug: collaborator.username, - owner, - repo, - permission: collaborator.access, - }); - } else if ('team' in collaborator) { - await client.rest.teams.addOrUpdateRepoPermissionsInOrg({ - org: owner, - team_slug: collaborator.team, - owner, - repo, - permission: collaborator.access, - }); - } - } catch (e) { - assertError(e); - const name = extractCollaboratorName(collaborator); - ctx.logger.warn( - `Skipping ${collaborator.access} access for ${name}, ${e.message}`, - ); - } - } - } - - if (topics) { - try { - await client.rest.repos.replaceAllTopics({ - owner, - repo, - names: topics.map(t => t.toLowerCase()), - }); - } catch (e) { - assertError(e); - ctx.logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`); - } - } + const newRepo = await createGithubRepoWithCollaboratorsAndTopics( + client, + repo, + owner, + repoVisibility, + description, + deleteBranchOnMerge, + allowMergeCommit, + allowSquashMerge, + allowRebaseMerge, + access, + collaborators, + topics, + ctx.logger, + ); const remoteUrl = newRepo.clone_url; const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`; - const gitAuthorInfo = { - name: gitAuthorName - ? gitAuthorName - : config.getOptionalString('scaffolder.defaultAuthor.name'), - email: gitAuthorEmail - ? gitAuthorEmail - : config.getOptionalString('scaffolder.defaultAuthor.email'), - }; + // const gitAuthorInfo = { + // name: gitAuthorName + // ? gitAuthorName + // : config.getOptionalString('scaffolder.defaultAuthor.name'), + // email: gitAuthorEmail + // ? gitAuthorEmail + // : config.getOptionalString('scaffolder.defaultAuthor.email'), + // }; - await initRepoAndPush({ - dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), + // await initRepoAndPush({ + // dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), + // remoteUrl, + // defaultBranch, + // auth: { + // username: 'x-access-token', + // password: octokitOptions.auth, + // }, + // logger: ctx.logger, + // commitMessage: gitCommitMessage + // ? gitCommitMessage + // : config.getOptionalString('scaffolder.defaultCommitMessage'), + // gitAuthorInfo, + // }); + + // if (protectDefaultBranch) { + // try { + // await enableBranchProtectionOnDefaultRepoBranch({ + // owner, + // client, + // repoName: newRepo.name, + // logger: ctx.logger, + // defaultBranch, + // requireCodeOwnerReviews, + // requiredStatusCheckContexts, + // }); + // } catch (e) { + // assertError(e); + // ctx.logger.warn( + // `Skipping: default branch protection on '${newRepo.name}', ${e.message}`, + // ); + // } + // } + + await initRepoPushAndProtect( remoteUrl, + octokitOptions.auth, + ctx.workspacePath, + ctx.input.sourcePath, defaultBranch, - auth: { - username: 'x-access-token', - password: octokitOptions.auth, - }, - logger: ctx.logger, - commitMessage: gitCommitMessage - ? gitCommitMessage - : config.getOptionalString('scaffolder.defaultCommitMessage'), - gitAuthorInfo, - }); - - if (protectDefaultBranch) { - try { - await enableBranchProtectionOnDefaultRepoBranch({ - owner, - client, - repoName: newRepo.name, - logger: ctx.logger, - defaultBranch, - requireCodeOwnerReviews, - requiredStatusCheckContexts, - }); - } catch (e) { - assertError(e); - ctx.logger.warn( - `Skipping: default branch protection on '${newRepo.name}', ${e.message}`, - ); - } - } + protectDefaultBranch, + owner, + client, + repo, + requireCodeOwnerReviews, + requiredStatusCheckContexts, + config, + ctx.logger, + gitCommitMessage, + gitAuthorName, + gitAuthorEmail, + ); ctx.output('remoteUrl', remoteUrl); ctx.output('repoContentsUrl', repoContentsUrl); }, }); } - -function extractCollaboratorName( - collaborator: { user: string } | { team: string } | { username: string }, -) { - if ('username' in collaborator) return collaborator.username; - if ('user' in collaborator) return collaborator.user; - return collaborator.team; -} From 4345479092e7609c32787f30d6c6530ed30c0e1b Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Wed, 22 Jun 2022 13:43:44 +0100 Subject: [PATCH 023/101] chore: fix tests Signed-off-by: Marco Crivellaro --- .../actions/builtin/publish/github.test.ts | 28 ++++++------ .../actions/builtin/publish/github.ts | 43 ------------------- 2 files changed, 14 insertions(+), 57 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts index ff20d1014b..036c5d66ee 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.test.ts @@ -18,20 +18,20 @@ import { TemplateAction } from '../../types'; jest.mock('../helpers'); -import { createPublishGithubAction } from './github'; +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; import { - ScmIntegrations, DefaultGithubCredentialsProvider, GithubCredentialsProvider, + ScmIntegrations, } from '@backstage/integration'; -import { ConfigReader } from '@backstage/config'; -import { getVoidLogger } from '@backstage/backend-common'; +import { when } from 'jest-when'; import { PassThrough } from 'stream'; import { enableBranchProtectionOnDefaultRepoBranch, initRepoAndPush, } from '../helpers'; -import { when } from 'jest-when'; +import { createPublishGithubAction } from './github'; const mockOctokit = { rest: { @@ -609,7 +609,7 @@ describe('publish:github', () => { mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { - name: 'repository', + name: 'repo', }, }); @@ -618,7 +618,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', client: mockOctokit, - repoName: 'repository', + repoName: 'repo', logger: mockContext.logger, defaultBranch: 'master', requireCodeOwnerReviews: false, @@ -636,7 +636,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', client: mockOctokit, - repoName: 'repository', + repoName: 'repo', logger: mockContext.logger, defaultBranch: 'master', requireCodeOwnerReviews: true, @@ -654,7 +654,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', client: mockOctokit, - repoName: 'repository', + repoName: 'repo', logger: mockContext.logger, defaultBranch: 'master', requireCodeOwnerReviews: false, @@ -669,7 +669,7 @@ describe('publish:github', () => { mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { - name: 'repository', + name: 'repo', }, }); @@ -678,7 +678,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', client: mockOctokit, - repoName: 'repository', + repoName: 'repo', logger: mockContext.logger, defaultBranch: 'master', requireCodeOwnerReviews: false, @@ -696,7 +696,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', client: mockOctokit, - repoName: 'repository', + repoName: 'repo', logger: mockContext.logger, defaultBranch: 'master', requireCodeOwnerReviews: false, @@ -714,7 +714,7 @@ describe('publish:github', () => { expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({ owner: 'owner', client: mockOctokit, - repoName: 'repository', + repoName: 'repo', logger: mockContext.logger, defaultBranch: 'master', requireCodeOwnerReviews: false, @@ -729,7 +729,7 @@ describe('publish:github', () => { mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({ data: { - name: 'repository', + name: 'repo', }, }); 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 87219ac1e6..0815a2cd00 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/github.ts @@ -169,49 +169,6 @@ export function createPublishGithubAction(options: { const remoteUrl = newRepo.clone_url; const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`; - // const gitAuthorInfo = { - // name: gitAuthorName - // ? gitAuthorName - // : config.getOptionalString('scaffolder.defaultAuthor.name'), - // email: gitAuthorEmail - // ? gitAuthorEmail - // : config.getOptionalString('scaffolder.defaultAuthor.email'), - // }; - - // await initRepoAndPush({ - // dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), - // remoteUrl, - // defaultBranch, - // auth: { - // username: 'x-access-token', - // password: octokitOptions.auth, - // }, - // logger: ctx.logger, - // commitMessage: gitCommitMessage - // ? gitCommitMessage - // : config.getOptionalString('scaffolder.defaultCommitMessage'), - // gitAuthorInfo, - // }); - - // if (protectDefaultBranch) { - // try { - // await enableBranchProtectionOnDefaultRepoBranch({ - // owner, - // client, - // repoName: newRepo.name, - // logger: ctx.logger, - // defaultBranch, - // requireCodeOwnerReviews, - // requiredStatusCheckContexts, - // }); - // } catch (e) { - // assertError(e); - // ctx.logger.warn( - // `Skipping: default branch protection on '${newRepo.name}', ${e.message}`, - // ); - // } - // } - await initRepoPushAndProtect( remoteUrl, octokitOptions.auth, From b15904a3675ff1726a504a76e6f585eb0fe9becf Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Wed, 22 Jun 2022 14:44:03 +0100 Subject: [PATCH 024/101] chore: api-report update Signed-off-by: Marco Crivellaro --- plugins/scaffolder-backend/api-report.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 029000399b..a4693125d9 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -195,6 +195,10 @@ export function createGithubRepoCreateAction(options: { team: string; access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; } + | { + username: string; + access: 'pull' | 'push' | 'admin' | 'maintain' | 'triage'; + } )[] | undefined; token?: string | undefined; From 7ee4abdcc90b0d36d5728997cf3fd014c8acfadc Mon Sep 17 00:00:00 2001 From: "denis.fortin" Date: Fri, 17 Jun 2022 17:04:40 +0200 Subject: [PATCH 025/101] fix(vault-plugin): display full secret name relative to the secret path Only secret name was displayed in the table. It could be a problem when secret path has multiple subpath containing secrets with the same name. Displaying the relative path from secret path configured in the entity annotation allows to differentiate the secrets in this case. Signed-off-by: denis.fortin --- .changeset/moody-crabs-march.md | 6 ++++++ plugins/vault-backend/src/service/vaultApi.test.ts | 2 ++ plugins/vault-backend/src/service/vaultApi.ts | 2 ++ plugins/vault/dev/index.tsx | 2 ++ plugins/vault/src/api.test.ts | 2 ++ plugins/vault/src/api.ts | 1 + .../components/EntityVaultTable/EntityVaultTable.test.tsx | 2 ++ .../src/components/EntityVaultTable/EntityVaultTable.tsx | 2 +- 8 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 .changeset/moody-crabs-march.md diff --git a/.changeset/moody-crabs-march.md b/.changeset/moody-crabs-march.md new file mode 100644 index 0000000000..3d7bc2d290 --- /dev/null +++ b/.changeset/moody-crabs-march.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-vault': minor +'@backstage/plugin-vault-backend': minor +--- + +Added a path notion in addition to secret name to allow to differentiate secrets in subpaths diff --git a/plugins/vault-backend/src/service/vaultApi.test.ts b/plugins/vault-backend/src/service/vaultApi.test.ts index 5a29633933..a407138e1e 100644 --- a/plugins/vault-backend/src/service/vaultApi.test.ts +++ b/plugins/vault-backend/src/service/vaultApi.test.ts @@ -46,11 +46,13 @@ describe('VaultApi', () => { const mockSecretsResult: VaultSecret[] = [ { name: 'secret::one', + path: 'test/success', editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`, showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`, }, { name: 'secret::two', + path: 'test/success', editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`, showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`, }, diff --git a/plugins/vault-backend/src/service/vaultApi.ts b/plugins/vault-backend/src/service/vaultApi.ts index 0623780654..5fc8c78149 100644 --- a/plugins/vault-backend/src/service/vaultApi.ts +++ b/plugins/vault-backend/src/service/vaultApi.ts @@ -34,6 +34,7 @@ export type VaultSecretList = { */ export type VaultSecret = { name: string; + path: string; showUrl: string; editUrl: string; }; @@ -131,6 +132,7 @@ export class VaultClient implements VaultApi { } else { secrets.push({ name: secret, + path: secretPath, editUrl: `${this.vaultConfig.baseUrl}/ui/vault/secrets/${this.vaultConfig.secretEngine}/edit/${secretPath}/${secret}`, showUrl: `${this.vaultConfig.baseUrl}/ui/vault/secrets/${this.vaultConfig.secretEngine}/show/${secretPath}/${secret}`, }); diff --git a/plugins/vault/dev/index.tsx b/plugins/vault/dev/index.tsx index f5056cd057..8d902e14c5 100644 --- a/plugins/vault/dev/index.tsx +++ b/plugins/vault/dev/index.tsx @@ -45,11 +45,13 @@ const mockedApi: VaultApi = { return [ { name: 'a::b', + path: '', editUrl: 'https://example.com', showUrl: 'https://example.com', }, { name: 'c::d', + path: '', editUrl: 'https://example.com', showUrl: 'https://example.com', }, diff --git a/plugins/vault/src/api.test.ts b/plugins/vault/src/api.test.ts index 70c6a553df..fb52cfbfdc 100644 --- a/plugins/vault/src/api.test.ts +++ b/plugins/vault/src/api.test.ts @@ -30,11 +30,13 @@ describe('api', () => { const mockSecretsResult: VaultSecret[] = [ { name: 'secret::one', + path: 'test/success', editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`, showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`, }, { name: 'secret::two', + path: 'test/success', editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`, showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`, }, diff --git a/plugins/vault/src/api.ts b/plugins/vault/src/api.ts index 74b212429f..ce51b4fcf9 100644 --- a/plugins/vault/src/api.ts +++ b/plugins/vault/src/api.ts @@ -21,6 +21,7 @@ export const vaultApiRef = createApiRef({ export type VaultSecret = { name: string; + path: string; showUrl: string; editUrl: string; }; diff --git a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.test.tsx b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.test.tsx index f2b181562a..c063166dbf 100644 --- a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.test.tsx +++ b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.test.tsx @@ -71,11 +71,13 @@ describe('EntityVaultTable', () => { const mockSecretsResult: VaultSecret[] = [ { name: 'secret::one', + path: 'test/success', editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::one`, showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::one`, }, { name: 'secret::two', + path: 'test/success', editUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/edit/test/success/secret::two`, showUrl: `${mockBaseUrl}/ui/vault/secrets/secrets/show/test/success/secret::two`, }, diff --git a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx index 197c82c5b8..fddfc8ef25 100644 --- a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx +++ b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx @@ -49,7 +49,7 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => { const data = (value || []).map(secret => { return { - secret: secret.name, + secret: `${secret.path.replace(secretPath + "/", "")}/${secret.name}`, view: ( Date: Thu, 16 Jun 2022 16:04:05 -0500 Subject: [PATCH 026/101] Add LimitRanges as Default Objects Signed-off-by: Salomon Moreno --- .changeset/eight-suits-fail.md | 6 ++++++ plugins/kubernetes-backend/api-report.md | 1 + .../src/service/KubernetesFanOutHandler.ts | 6 ++++++ plugins/kubernetes-backend/src/types/types.ts | 1 + plugins/kubernetes-common/api-report.md | 12 ++++++++++++ plugins/kubernetes-common/src/types.ts | 7 +++++++ 6 files changed, 33 insertions(+) create mode 100644 .changeset/eight-suits-fail.md diff --git a/.changeset/eight-suits-fail.md b/.changeset/eight-suits-fail.md new file mode 100644 index 0000000000..742154da91 --- /dev/null +++ b/.changeset/eight-suits-fail.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-kubernetes-common': patch +--- + +Fixed the lack of `limitranges` as part of the Default Objects to fetch from the kubernetes api diff --git a/plugins/kubernetes-backend/api-report.md b/plugins/kubernetes-backend/api-report.md index 41748f240b..adc8fb503f 100644 --- a/plugins/kubernetes-backend/api-report.md +++ b/plugins/kubernetes-backend/api-report.md @@ -214,6 +214,7 @@ export type KubernetesObjectTypes = | 'services' | 'configmaps' | 'deployments' + | 'limitranges' | 'replicasets' | 'horizontalpodautoscalers' | 'jobs' diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index ac2ffaf2ed..67748e1506 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -70,6 +70,12 @@ export const DEFAULT_OBJECTS: ObjectToFetch[] = [ plural: 'configmaps', objectType: 'configmaps', }, + { + group: '', + apiVersion: 'v1', + plural: 'limitranges', + objectType: 'limitranges', + }, { group: 'apps', apiVersion: 'v1', diff --git a/plugins/kubernetes-backend/src/types/types.ts b/plugins/kubernetes-backend/src/types/types.ts index 6302a49fa5..0826c4158c 100644 --- a/plugins/kubernetes-backend/src/types/types.ts +++ b/plugins/kubernetes-backend/src/types/types.ts @@ -101,6 +101,7 @@ export type KubernetesObjectTypes = | 'services' | 'configmaps' | 'deployments' + | 'limitranges' | 'replicasets' | 'horizontalpodautoscalers' | 'jobs' diff --git a/plugins/kubernetes-common/api-report.md b/plugins/kubernetes-common/api-report.md index d59db7422f..2ca33d1a97 100644 --- a/plugins/kubernetes-common/api-report.md +++ b/plugins/kubernetes-common/api-report.md @@ -11,6 +11,7 @@ import { V1Deployment } from '@kubernetes/client-node'; import { V1HorizontalPodAutoscaler } from '@kubernetes/client-node'; import { V1Ingress } from '@kubernetes/client-node'; import { V1Job } from '@kubernetes/client-node'; +import { V1LimitRange } from '@kubernetes/client-node'; import { V1Pod } from '@kubernetes/client-node'; import { V1ReplicaSet } from '@kubernetes/client-node'; import { V1Service } from '@kubernetes/client-node'; @@ -131,6 +132,7 @@ export type FetchResponse = | ServiceFetchResponse | ConfigMapFetchResponse | DeploymentFetchResponse + | LimitRangeFetchReponse | ReplicaSetsFetchResponse | HorizontalPodAutoscalersFetchResponse | JobsFetchResponse @@ -212,6 +214,16 @@ export interface KubernetesRequestBody { entity: Entity; } +// Warning: (ae-missing-release-tag) "LimitRangeFetchReponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface LimitRangeFetchReponse { + // (undocumented) + resources: Array; + // (undocumented) + type: 'limitranges'; +} + // Warning: (ae-missing-release-tag) "ObjectsByEntityResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index 68fbe09301..9b8b790c79 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -22,6 +22,7 @@ import { V1HorizontalPodAutoscaler, V1Ingress, V1Job, + V1LimitRange, V1Pod, V1ReplicaSet, V1Service, @@ -97,6 +98,7 @@ export type FetchResponse = | ServiceFetchResponse | ConfigMapFetchResponse | DeploymentFetchResponse + | LimitRangeFetchReponse | ReplicaSetsFetchResponse | HorizontalPodAutoscalersFetchResponse | JobsFetchResponse @@ -130,6 +132,11 @@ export interface ReplicaSetsFetchResponse { resources: Array; } +export interface LimitRangeFetchReponse { + type: 'limitranges'; + resources: Array; +} + export interface HorizontalPodAutoscalersFetchResponse { type: 'horizontalpodautoscalers'; resources: Array; From e271183fe7176c86dc3266a4128051751366b8e6 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Thu, 23 Jun 2022 10:54:24 -0500 Subject: [PATCH 027/101] Added example for custom reader page Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- docs/features/techdocs/addons.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/features/techdocs/addons.md b/docs/features/techdocs/addons.md index a4e1de7d4f..fe1058b2ca 100644 --- a/docs/features/techdocs/addons.md +++ b/docs/features/techdocs/addons.md @@ -76,6 +76,18 @@ import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; ; ``` +If you are using a custom [TechDocs reader page](./how-to-guides.md#how-to-customize-the-techdocs-reader-page) your setup will be very similar, here's an example: + +```ts +}> + + + {/* Other addons can be added here. */} + + {techDocsPage} // This is your custom TechDocs reader page + +``` + The process for configuring Addons on the documentation tab on the entity page is very similar; instead of adding the `` registry under a ``, you'd add it as a child of ``: From 4baf8a4ece338045f3a2ad919242661919cc5b14 Mon Sep 17 00:00:00 2001 From: Aisha Saini Date: Tue, 3 May 2022 15:40:24 +0100 Subject: [PATCH 028/101] Update GitLab MR Action to allow source branch to be deleted on merge Signed-off-by: Aisha Saini Signed-off-by: asaini1 --- .changeset/old-onions-hear.md | 5 +++++ .../actions/builtin/publish/gitlabMergeRequest.ts | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .changeset/old-onions-hear.md diff --git a/.changeset/old-onions-hear.md b/.changeset/old-onions-hear.md new file mode 100644 index 0000000000..82dcc09190 --- /dev/null +++ b/.changeset/old-onions-hear.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Update GitLab Merge Request Action to allow source branch to be deleted diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts index f717a7a0c6..fe94f5ae53 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts @@ -42,6 +42,7 @@ export const createPublishGitlabMergeRequestAction = (options: { token?: string; /** @deprecated Use projectPath instead */ projectid?: string; + removeSourceBranch?: boolean; }>({ id: 'publish:gitlab:merge-request', schema: { @@ -85,6 +86,12 @@ export const createPublishGitlabMergeRequestAction = (options: { type: 'string', description: 'The token to use for authorization to GitLab', }, + removeSourceBranch: { + title: 'Delete source branch', + type: 'boolean', + description: + 'Option to delete source branch once the MR has been merged. Default: false', + }, }, }, output: { @@ -187,7 +194,10 @@ export const createPublishGitlabMergeRequestAction = (options: { destinationBranch, String(defaultBranch), ctx.input.title, - { description: ctx.input.description }, + { + description: ctx.input.description, + removeSourceBranch: ctx.input.removeSourceBranch, + }, ).then((mergeRequest: { web_url: string }) => { return mergeRequest.web_url; }); From 5b99da51d9d0b090938e6224384ac2096680f83c Mon Sep 17 00:00:00 2001 From: Aisha Saini Date: Wed, 18 May 2022 08:43:42 +0100 Subject: [PATCH 029/101] Add api-report.md file Signed-off-by: Aisha Saini Signed-off-by: asaini1 --- plugins/scaffolder-backend/api-report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 707b8e62ba..b0f831fc8c 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -345,6 +345,7 @@ export const createPublishGitlabMergeRequestAction: (options: { targetPath: string; token?: string | undefined; projectid?: string | undefined; + removeSourceBranch?: boolean | undefined; }>; // @public From f560f836ca57429505092834f55a8a38ce48f44d Mon Sep 17 00:00:00 2001 From: Aisha Saini Date: Wed, 18 May 2022 09:56:48 +0100 Subject: [PATCH 030/101] Update removeSourceBranch option to cater for when not set Signed-off-by: Aisha Saini Signed-off-by: asaini1 --- .../scaffolder/actions/builtin/publish/gitlabMergeRequest.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts index fe94f5ae53..b1d5b35fa3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts @@ -196,7 +196,9 @@ export const createPublishGitlabMergeRequestAction = (options: { ctx.input.title, { description: ctx.input.description, - removeSourceBranch: ctx.input.removeSourceBranch, + removeSourceBranch: ctx.input.removeSourceBranch + ? ctx.input.removeSourceBranch + : false, }, ).then((mergeRequest: { web_url: string }) => { return mergeRequest.web_url; From 08a27a58ad4110b3f18cf4f932072be3dae5e983 Mon Sep 17 00:00:00 2001 From: Aisha Saini Date: Tue, 24 May 2022 10:56:10 +0100 Subject: [PATCH 031/101] Add gitlab MR test file Signed-off-by: Aisha Saini Signed-off-by: asaini1 --- .../publish/gitlabMergeRequest.test.ts | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts new file mode 100644 index 0000000000..cf2bc57514 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts @@ -0,0 +1,219 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// * Copyright 2021 The Backstage Authors +// * +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +import { getRootLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import mockFs from 'mock-fs'; +import os from 'os'; +import { resolve as resolvePath } from 'path'; +import { Writable } from 'stream'; +import { TemplateAction } from '../../types'; +import { createPublishGitlabMergeRequestAction } from './gitlabMergeRequest'; + +const root = os.platform() === 'win32' ? 'C:\\root' : '/root'; +const workspacePath = resolvePath(root, 'my-workspace'); + +const mockGitlabClient = { + Namespaces: { + show: jest.fn(), + }, + Branches: { + create: jest.fn(), + }, + Commits: { + create: jest.fn(), + }, + MergeRequests: { + create: jest.fn(async (_: any) => { + return { + default_branch: 'main', + }; + }), + }, + Projects: { + create: jest.fn(), + show: jest.fn(async (_: any) => { + return { + default_branch: 'main', + }; + }), + }, + Users: { + current: jest.fn(), + }, +}; + +jest.mock('@gitbeaker/node', () => ({ + Gitlab: class { + constructor() { + return mockGitlabClient; + } + }, +})); + +describe('createGitLabMergeRequest', () => { + let instance: TemplateAction; + + beforeEach(() => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'token', + apiBaseUrl: 'https://api.gitlab.com', + }, + { + host: 'hosted.gitlab.com', + apiBaseUrl: 'https://api.hosted.gitlab.com', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + instance = createPublishGitlabMergeRequestAction({ integrations }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + describe('createGitLabMergeRequestWithoutRemoveBranch', () => { + it('removeSourceBranch is false by default when not passed in options', async () => { + const input = { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + title: 'Create my new MR', + branchName: 'new-mr', + description: 'This MR is really good', + draft: true, + }; + mockFs({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + const ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + await instance.handler(ctx); + + expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith( + undefined, + 'new-mr', + 'main', + 'Create my new MR', + { description: 'This MR is really good', removeSourceBranch: false }, + ); + }); + }); + + describe('createGitLabMergeRequestWithRemoveBranch', () => { + it('removeSourceBranch is true when true is passed in options', async () => { + const input = { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + title: 'Create my new MR', + branchName: 'new-mr', + description: 'MR description', + removeSourceBranch: true, + draft: true, + }; + mockFs({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + + const ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + await instance.handler(ctx); + + expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith( + undefined, + 'new-mr', + 'main', + 'Create my new MR', + { description: 'MR description', removeSourceBranch: true }, + ); + }); + + it('removeSourceBranch is false when false is passed in options', async () => { + const input = { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + title: 'Create my new MR', + branchName: 'new-mr', + description: 'other MR description', + removeSourceBranch: false, + draft: true, + }; + mockFs({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + + const ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + await instance.handler(ctx); + + expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith( + undefined, + 'new-mr', + 'main', + 'Create my new MR', + { + description: 'other MR description', + removeSourceBranch: false, + }, + ); + }); + }); +}); From b4149fc0a361ac80d655a2eae164a8dbbe0989d1 Mon Sep 17 00:00:00 2001 From: Aisha Saini Date: Fri, 27 May 2022 15:21:26 +0100 Subject: [PATCH 032/101] Add config and tests for new gitlab actions update and delete Signed-off-by: Aisha Saini Signed-off-by: asaini1 --- .changeset/old-onions-hear.md | 2 +- plugins/scaffolder-backend/api-report.md | 1 + .../publish/gitlabMergeRequest.test.ts | 175 ++++++++++++++++++ .../builtin/publish/gitlabMergeRequest.ts | 7 + 4 files changed, 184 insertions(+), 1 deletion(-) diff --git a/.changeset/old-onions-hear.md b/.changeset/old-onions-hear.md index 82dcc09190..febf2c2bf6 100644 --- a/.changeset/old-onions-hear.md +++ b/.changeset/old-onions-hear.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': minor --- -Update GitLab Merge Request Action to allow source branch to be deleted +Update GitLab Merge Request Action to allow source branch to be deleted & configure additional gitlab actions: update and delete diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index b0f831fc8c..742293da30 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -346,6 +346,7 @@ export const createPublishGitlabMergeRequestAction: (options: { token?: string | undefined; projectid?: string | undefined; removeSourceBranch?: boolean | undefined; + gitlabAction?: 'update' | 'create' | 'delete' | undefined; }>; // @public diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts index cf2bc57514..889b8b862c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts @@ -79,6 +79,20 @@ jest.mock('@gitbeaker/node', () => ({ }, })); +jest.mock('globby', () => + jest.fn(async (_: any) => { + return ['foo/bar5']; + }), +); + +jest.mock('fs-extra', () => { + return { + readFile: jest.fn(async (_: any) => { + return Buffer.from('some content'); + }), + }; +}); + describe('createGitLabMergeRequest', () => { let instance: TemplateAction; @@ -216,4 +230,165 @@ describe('createGitLabMergeRequest', () => { ); }); }); + + describe('createGitLabMergeRequestWithoutGitlabAction', () => { + it('gitlabAction is create by default when not passed in options', async () => { + const input = { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + title: 'Create my new MR', + branchName: 'new-mr', + description: 'This MR is really good', + draft: true, + }; + mockFs({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + const ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + await instance.handler(ctx); + + expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( + undefined, + 'new-mr', + 'Create my new MR', + [ + { + action: 'create', + filePath: 'foo/bar5', + content: 'some content', + }, + ], + ); + }); + }); + + describe('createGitLabMergeRequestWithGitlabAction', () => { + it('gitlabAction is create when create is passed in options', async () => { + const input = { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + title: 'Create my new MR', + branchName: 'new-mr', + description: 'MR description', + gitlabAction: 'create', + draft: true, + }; + mockFs({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + + const ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + await instance.handler(ctx); + + expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( + undefined, + 'new-mr', + 'Create my new MR', + [ + { + action: 'create', + filePath: 'foo/bar5', + content: 'some content', + }, + ], + ); + }); + + it('gitlabAction is update when update is passed in options', async () => { + const input = { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + title: 'Create my new MR', + branchName: 'new-mr', + description: 'MR description', + gitlabAction: 'update', + draft: true, + }; + mockFs({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + + const ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + await instance.handler(ctx); + + expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( + undefined, + 'new-mr', + 'Create my new MR', + [ + { + action: 'update', + filePath: 'foo/bar5', + content: 'some content', + }, + ], + ); + }); + + it('gitlabAction is delete when delete is passed in options', async () => { + const input = { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + title: 'Create my new MR', + branchName: 'new-mr', + description: 'other MR description', + gitlabAction: 'delete', + draft: true, + }; + mockFs({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + + const ctx = { + createTemporaryDirectory: jest.fn(), + output: jest.fn(), + logger: getRootLogger(), + logStream: new Writable(), + input, + workspacePath, + }; + await instance.handler(ctx); + + expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( + undefined, + 'new-mr', + 'Create my new MR', + [ + { + action: 'delete', + filePath: 'foo/bar5', + }, + ], + ); + }); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts index b1d5b35fa3..7e0949454b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts @@ -43,6 +43,7 @@ export const createPublishGitlabMergeRequestAction = (options: { /** @deprecated Use projectPath instead */ projectid?: string; removeSourceBranch?: boolean; + gitlabAction?: 'create' | 'update' | 'delete'; }>({ id: 'publish:gitlab:merge-request', schema: { @@ -92,6 +93,12 @@ export const createPublishGitlabMergeRequestAction = (options: { description: 'Option to delete source branch once the MR has been merged. Default: false', }, + gitlabAction: { + title: 'GitLab Action', + type: 'string', + description: + 'Option to configure gitlab action. Options are: create (by default), update and delete', + }, }, }, output: { From c8e8a164b7744d6d4a990f21ea89863d2a5f9ce1 Mon Sep 17 00:00:00 2001 From: Aisha Saini Date: Wed, 15 Jun 2022 15:57:41 +0100 Subject: [PATCH 033/101] Remove gitlab action changes from PR Signed-off-by: Aisha Saini Signed-off-by: asaini1 --- .changeset/old-onions-hear.md | 2 +- plugins/scaffolder-backend/api-report.md | 1 - .../actions/builtin/publish/gitlabMergeRequest.ts | 7 ------- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.changeset/old-onions-hear.md b/.changeset/old-onions-hear.md index febf2c2bf6..82dcc09190 100644 --- a/.changeset/old-onions-hear.md +++ b/.changeset/old-onions-hear.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': minor --- -Update GitLab Merge Request Action to allow source branch to be deleted & configure additional gitlab actions: update and delete +Update GitLab Merge Request Action to allow source branch to be deleted diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 742293da30..b0f831fc8c 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -346,7 +346,6 @@ export const createPublishGitlabMergeRequestAction: (options: { token?: string | undefined; projectid?: string | undefined; removeSourceBranch?: boolean | undefined; - gitlabAction?: 'update' | 'create' | 'delete' | undefined; }>; // @public diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts index 7e0949454b..b1d5b35fa3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.ts @@ -43,7 +43,6 @@ export const createPublishGitlabMergeRequestAction = (options: { /** @deprecated Use projectPath instead */ projectid?: string; removeSourceBranch?: boolean; - gitlabAction?: 'create' | 'update' | 'delete'; }>({ id: 'publish:gitlab:merge-request', schema: { @@ -93,12 +92,6 @@ export const createPublishGitlabMergeRequestAction = (options: { description: 'Option to delete source branch once the MR has been merged. Default: false', }, - gitlabAction: { - title: 'GitLab Action', - type: 'string', - description: - 'Option to configure gitlab action. Options are: create (by default), update and delete', - }, }, }, output: { From 3c09270d2deffa67106cbe2dab584d50907aa3a4 Mon Sep 17 00:00:00 2001 From: asaini1 Date: Fri, 24 Jun 2022 11:49:41 +0100 Subject: [PATCH 034/101] Update tests Signed-off-by: Aisha Saini Signed-off-by: asaini1 --- .../publish/gitlabMergeRequest.test.ts | 184 +----------------- 1 file changed, 6 insertions(+), 178 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts index 889b8b862c..7fe01921d1 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts @@ -79,20 +79,6 @@ jest.mock('@gitbeaker/node', () => ({ }, })); -jest.mock('globby', () => - jest.fn(async (_: any) => { - return ['foo/bar5']; - }), -); - -jest.mock('fs-extra', () => { - return { - readFile: jest.fn(async (_: any) => { - return Buffer.from('some content'); - }), - }; -}); - describe('createGitLabMergeRequest', () => { let instance: TemplateAction; @@ -129,6 +115,7 @@ describe('createGitLabMergeRequest', () => { branchName: 'new-mr', description: 'This MR is really good', draft: true, + targetPath: 'Subdirectory', }; mockFs({ [workspacePath]: { @@ -147,7 +134,7 @@ describe('createGitLabMergeRequest', () => { await instance.handler(ctx); expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith( - undefined, + 'owner/repo', 'new-mr', 'main', 'Create my new MR', @@ -165,6 +152,7 @@ describe('createGitLabMergeRequest', () => { description: 'MR description', removeSourceBranch: true, draft: true, + targetPath: 'Subdirectory', }; mockFs({ [workspacePath]: { @@ -184,7 +172,7 @@ describe('createGitLabMergeRequest', () => { await instance.handler(ctx); expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith( - undefined, + 'owner/repo', 'new-mr', 'main', 'Create my new MR', @@ -200,6 +188,7 @@ describe('createGitLabMergeRequest', () => { description: 'other MR description', removeSourceBranch: false, draft: true, + targetPath: 'Subdirectory', }; mockFs({ [workspacePath]: { @@ -219,7 +208,7 @@ describe('createGitLabMergeRequest', () => { await instance.handler(ctx); expect(mockGitlabClient.MergeRequests.create).toHaveBeenCalledWith( - undefined, + 'owner/repo', 'new-mr', 'main', 'Create my new MR', @@ -230,165 +219,4 @@ describe('createGitLabMergeRequest', () => { ); }); }); - - describe('createGitLabMergeRequestWithoutGitlabAction', () => { - it('gitlabAction is create by default when not passed in options', async () => { - const input = { - repoUrl: 'gitlab.com?repo=repo&owner=owner', - title: 'Create my new MR', - branchName: 'new-mr', - description: 'This MR is really good', - draft: true, - }; - mockFs({ - [workspacePath]: { - source: { 'foo.txt': 'Hello there!' }, - irrelevant: { 'bar.txt': 'Nothing to see here' }, - }, - }); - const ctx = { - createTemporaryDirectory: jest.fn(), - output: jest.fn(), - logger: getRootLogger(), - logStream: new Writable(), - input, - workspacePath, - }; - await instance.handler(ctx); - - expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( - undefined, - 'new-mr', - 'Create my new MR', - [ - { - action: 'create', - filePath: 'foo/bar5', - content: 'some content', - }, - ], - ); - }); - }); - - describe('createGitLabMergeRequestWithGitlabAction', () => { - it('gitlabAction is create when create is passed in options', async () => { - const input = { - repoUrl: 'gitlab.com?repo=repo&owner=owner', - title: 'Create my new MR', - branchName: 'new-mr', - description: 'MR description', - gitlabAction: 'create', - draft: true, - }; - mockFs({ - [workspacePath]: { - source: { 'foo.txt': 'Hello there!' }, - irrelevant: { 'bar.txt': 'Nothing to see here' }, - }, - }); - - const ctx = { - createTemporaryDirectory: jest.fn(), - output: jest.fn(), - logger: getRootLogger(), - logStream: new Writable(), - input, - workspacePath, - }; - await instance.handler(ctx); - - expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( - undefined, - 'new-mr', - 'Create my new MR', - [ - { - action: 'create', - filePath: 'foo/bar5', - content: 'some content', - }, - ], - ); - }); - - it('gitlabAction is update when update is passed in options', async () => { - const input = { - repoUrl: 'gitlab.com?repo=repo&owner=owner', - title: 'Create my new MR', - branchName: 'new-mr', - description: 'MR description', - gitlabAction: 'update', - draft: true, - }; - mockFs({ - [workspacePath]: { - source: { 'foo.txt': 'Hello there!' }, - irrelevant: { 'bar.txt': 'Nothing to see here' }, - }, - }); - - const ctx = { - createTemporaryDirectory: jest.fn(), - output: jest.fn(), - logger: getRootLogger(), - logStream: new Writable(), - input, - workspacePath, - }; - await instance.handler(ctx); - - expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( - undefined, - 'new-mr', - 'Create my new MR', - [ - { - action: 'update', - filePath: 'foo/bar5', - content: 'some content', - }, - ], - ); - }); - - it('gitlabAction is delete when delete is passed in options', async () => { - const input = { - repoUrl: 'gitlab.com?repo=repo&owner=owner', - title: 'Create my new MR', - branchName: 'new-mr', - description: 'other MR description', - gitlabAction: 'delete', - draft: true, - }; - mockFs({ - [workspacePath]: { - source: { 'foo.txt': 'Hello there!' }, - irrelevant: { 'bar.txt': 'Nothing to see here' }, - }, - }); - - const ctx = { - createTemporaryDirectory: jest.fn(), - output: jest.fn(), - logger: getRootLogger(), - logStream: new Writable(), - input, - workspacePath, - }; - await instance.handler(ctx); - - expect(mockGitlabClient.Commits.create).toHaveBeenCalledWith( - undefined, - 'new-mr', - 'Create my new MR', - [ - { - action: 'delete', - filePath: 'foo/bar5', - }, - ], - ); - }); - }); }); From be26d95141bd883874f9f4f787da50bcb385251e Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Mon, 20 Jun 2022 12:19:11 -0500 Subject: [PATCH 035/101] Added EntityAdvancedPicker Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .changeset/wicked-ladybugs-argue.md | 43 +++++ plugins/catalog-react/api-report.md | 27 ++++ .../EntityAdvancedPicker.test.tsx | 152 ++++++++++++++++++ .../EntityAdvancedPicker.tsx | 109 +++++++++++++ .../components/EntityAdvancedPicker/index.ts | 17 ++ plugins/catalog-react/src/components/index.ts | 1 + plugins/catalog-react/src/filters.test.ts | 55 ++++++- plugins/catalog-react/src/filters.ts | 31 +++- .../src/hooks/useEntityListProvider.tsx | 4 + .../src/overridableComponents.ts | 2 + .../CatalogPage/DefaultCatalogPage.tsx | 2 + 11 files changed, 440 insertions(+), 3 deletions(-) create mode 100644 .changeset/wicked-ladybugs-argue.md create mode 100644 plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx create mode 100644 plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx create mode 100644 plugins/catalog-react/src/components/EntityAdvancedPicker/index.ts diff --git a/.changeset/wicked-ladybugs-argue.md b/.changeset/wicked-ladybugs-argue.md new file mode 100644 index 0000000000..d75f250689 --- /dev/null +++ b/.changeset/wicked-ladybugs-argue.md @@ -0,0 +1,43 @@ +--- +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-react': patch +--- + +Added new `EntityAdvancedPicker` that will filter for entities with orphans and/or errors. + +If you are using the default Catalog page this picker will be added automatically. For those who have customized their Catalog page you'll need to add this manually by doing something like this: + +```diff +... +import { + CatalogFilterLayout, + EntityTypePicker, + UserListPicker, + EntityTagPicker ++ EntityAdvancedPicker, +} from '@backstage/plugin-catalog-react'; +... +export const CustomCatalogPage = ({ + columns, + actions, + initiallySelectedFilter = 'owned', +}: CatalogPageProps) => { + return ( + ... + + + + + + ... +}; +``` diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 285ecafd70..7f3d7a5f3c 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -78,8 +78,12 @@ export type CatalogReactComponentsNameToClassKey = { CatalogReactEntitySearchBar: CatalogReactEntitySearchBarClassKey; CatalogReactEntityTagPicker: CatalogReactEntityTagPickerClassKey; CatalogReactEntityOwnerPicker: CatalogReactEntityOwnerPickerClassKey; + CatalogReactEntityAdvancedPicker: CatalogReactEntityAdvancedPickerClassKey; }; +// @public (undocumented) +export type CatalogReactEntityAdvancedPickerClassKey = 'input'; + // @public (undocumented) export type CatalogReactEntityLifecyclePickerClassKey = 'input'; @@ -137,8 +141,22 @@ export type DefaultEntityFilters = { lifecycles?: EntityLifecycleFilter; tags?: EntityTagFilter; text?: EntityTextFilter; + orphan?: EntityOrphanFilter; + error?: EntityErrorFilter; }; +// @public (undocumented) +export const EntityAdvancedPicker: () => JSX.Element; + +// @public +export class EntityErrorFilter implements EntityFilter { + constructor(values: string[]); + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + readonly values: string[]; +} + // @public (undocumented) export type EntityFilter = { getCatalogFilters?: () => Record< @@ -222,6 +240,15 @@ export type EntityLoadingStatus = { refresh?: VoidFunction; }; +// @public +export class EntityOrphanFilter implements EntityFilter { + constructor(values: string[]); + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + readonly values: string[]; +} + // @public export class EntityOwnerFilter implements EntityFilter { constructor(values: string[]); diff --git a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx new file mode 100644 index 0000000000..8964038d42 --- /dev/null +++ b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx @@ -0,0 +1,152 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { EntityErrorFilter, EntityOrphanFilter } from '../../filters'; +import { MockEntityListContextProvider } from '../../testUtils/providers'; +import { EntityAdvancedPicker } from './EntityAdvancedPicker'; + +const orphanAnnotation: Record = {}; +orphanAnnotation['backstage.io/orphan'] = 'true'; + +const sampleEntities: Entity[] = [ + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'valid-component', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'orphan-component', + annotations: orphanAnnotation, + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'error-component', + tags: ['Invalid Tag'], + }, + }, +]; + +describe('', () => { + it('renders all advanced options', () => { + const rendered = render( + + + , + ); + expect(rendered.getByText('Advanced')).toBeInTheDocument(); + + fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + expect(rendered.getByText('Is Orphan')).toBeInTheDocument(); + expect(rendered.getByText('Has Error')).toBeInTheDocument(); + }); + + it('adds orphan to orphan filter', () => { + const updateFilters = jest.fn(); + const rendered = render( + + + , + ); + + fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByText('Is Orphan')); + expect(updateFilters).toHaveBeenCalledWith({ + orphan: new EntityOrphanFilter(['true']), + }); + }); + + it('adds error to error filter', () => { + const updateFilters = jest.fn(); + const rendered = render( + + + , + ); + + fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByText('Has Error')); + expect(updateFilters).toHaveBeenCalledWith({ + error: new EntityErrorFilter(['true']), + }); + }); + + it('remove orphan from orphan filter', () => { + const updateFilters = jest.fn(); + const rendered = render( + + + , + ); + + fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByText('Is Orphan')); + expect(updateFilters).toHaveBeenCalledWith({ + orphan: undefined, + }); + }); + + it('remove error from error filter', () => { + const updateFilters = jest.fn(); + const rendered = render( + + + , + ); + + fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByText('Has Error')); + expect(updateFilters).toHaveBeenCalledWith({ + error: undefined, + }); + }); +}); diff --git a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx new file mode 100644 index 0000000000..9a813540f1 --- /dev/null +++ b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx @@ -0,0 +1,109 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EntityErrorFilter, EntityOrphanFilter } from '../../filters'; +import { + Box, + Checkbox, + FormControlLabel, + makeStyles, + TextField, + Typography, +} from '@material-ui/core'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import React, { useState } from 'react'; +import { useEntityList } from '../../hooks'; +import { Autocomplete } from '@material-ui/lab'; + +/** @public */ +export type CatalogReactEntityAdvancedPickerClassKey = 'input'; + +const useStyles = makeStyles( + { + input: {}, + }, + { + name: 'CatalogReactEntityAdvancedPicker', + }, +); + +const icon = ; +const checkedIcon = ; + +/** @public */ +export const EntityAdvancedPicker = () => { + const classes = useStyles(); + const { updateFilters } = useEntityList(); + + const [selectedAdvancedItems, setSelectedAdvancedItems] = useState( + [], + ); + + function orphanChange(value: string) { + updateFilters({ + orphan: value === 'true' ? new EntityOrphanFilter([value]) : undefined, + }); + } + + function errorChange(value: string) { + updateFilters({ + error: value === 'true' ? new EntityErrorFilter([value]) : undefined, + }); + } + + const availableAdvancedItems = ['Is Orphan', 'Has Error']; + + return ( + + + Advanced + { + setSelectedAdvancedItems(value); + orphanChange(value.includes('Is Orphan') ? 'true' : 'false'); + errorChange(value.includes('Has Error') ? 'true' : 'false'); + }} + renderOption={(option, { selected }) => ( + + } + label={option} + /> + )} + size="small" + popupIcon={} + renderInput={params => ( + + )} + /> + + + ); +}; diff --git a/plugins/catalog-react/src/components/EntityAdvancedPicker/index.ts b/plugins/catalog-react/src/components/EntityAdvancedPicker/index.ts new file mode 100644 index 0000000000..160793f7f6 --- /dev/null +++ b/plugins/catalog-react/src/components/EntityAdvancedPicker/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { EntityAdvancedPicker } from './EntityAdvancedPicker'; +export type { CatalogReactEntityAdvancedPickerClassKey } from './EntityAdvancedPicker'; diff --git a/plugins/catalog-react/src/components/index.ts b/plugins/catalog-react/src/components/index.ts index 7a26a72bf9..c495722243 100644 --- a/plugins/catalog-react/src/components/index.ts +++ b/plugins/catalog-react/src/components/index.ts @@ -27,3 +27,4 @@ export * from './FavoriteEntity'; export * from './InspectEntityDialog'; export * from './UnregisterEntityDialog'; export * from './UserListPicker'; +export * from './EntityAdvancedPicker'; diff --git a/plugins/catalog-react/src/filters.test.ts b/plugins/catalog-react/src/filters.test.ts index ba31e21122..430bfb1360 100644 --- a/plugins/catalog-react/src/filters.test.ts +++ b/plugins/catalog-react/src/filters.test.ts @@ -14,9 +14,13 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import { AlphaEntity, Entity } from '@backstage/catalog-model'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { EntityTextFilter } from './filters'; +import { + EntityErrorFilter, + EntityOrphanFilter, + EntityTextFilter, +} from './filters'; const entities: Entity[] = [ { @@ -91,3 +95,50 @@ describe('EntityTextFilter', () => { expect(filter.filterEntity(entities[1])).toBeTruthy(); }); }); + +describe('EntityOrphanFilter', () => { + const orphanAnnotation: Record = {}; + orphanAnnotation['backstage.io/orphan'] = 'true'; + + const orphan: Entity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'orphaned-service', + annotations: orphanAnnotation, + }, + }; + + it('should find orphans', () => { + const filter = new EntityOrphanFilter(['true']); + expect(filter.filterEntity(orphan)).toBeTruthy(); + expect(filter.filterEntity(entities[1])).toBeFalsy(); + }); +}); + +describe('EntityErrorFilter', () => { + const error: AlphaEntity = { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'service-with-error', + tags: ['Invalid Tag'], + }, + status: { + items: [ + { + type: 'invalid-tag', + level: 'error', + message: 'Tag is not valid', + error: undefined, + }, + ], + }, + }; + + it('should find errors', () => { + const filter = new EntityErrorFilter(['true']); + expect(filter.filterEntity(error)).toBeTruthy(); + expect(filter.filterEntity(entities[1])).toBeFalsy(); + }); +}); diff --git a/plugins/catalog-react/src/filters.ts b/plugins/catalog-react/src/filters.ts index 9fb057a01c..efd23552e9 100644 --- a/plugins/catalog-react/src/filters.ts +++ b/plugins/catalog-react/src/filters.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { + AlphaEntity, + Entity, + RELATION_OWNED_BY, +} from '@backstage/catalog-model'; import { humanizeEntityRef } from './components/EntityRefLink'; import { EntityFilter, UserListFilterKind } from './types'; import { getEntityRelations } from './utils'; @@ -159,3 +163,28 @@ export class UserListFilter implements EntityFilter { return this.value; } } + +/** + * Filters entities based if it is an orphan or not. + * @public + */ +export class EntityOrphanFilter implements EntityFilter { + constructor(readonly values: string[]) {} + filterEntity(entity: Entity): boolean { + const orphan = entity.metadata.annotations?.['backstage.io/orphan']; + return orphan !== undefined && this.values.includes(orphan); + } +} + +/** + * Filters entities based on if it has errors or not. + * @public + */ +export class EntityErrorFilter implements EntityFilter { + constructor(readonly values: string[]) {} + filterEntity(entity: Entity): boolean { + const error = + ((entity as AlphaEntity)?.status?.items?.length as number) > 0; + return error !== undefined && this.values.includes(error.toString()); + } +} diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx index ccac475ae8..089701d8b4 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx @@ -31,8 +31,10 @@ import useDebounce from 'react-use/lib/useDebounce'; import useMountedState from 'react-use/lib/useMountedState'; import { catalogApiRef } from '../api'; import { + EntityErrorFilter, EntityKindFilter, EntityLifecycleFilter, + EntityOrphanFilter, EntityOwnerFilter, EntityTagFilter, EntityTextFilter, @@ -52,6 +54,8 @@ export type DefaultEntityFilters = { lifecycles?: EntityLifecycleFilter; tags?: EntityTagFilter; text?: EntityTextFilter; + orphan?: EntityOrphanFilter; + error?: EntityErrorFilter; }; /** @public */ diff --git a/plugins/catalog-react/src/overridableComponents.ts b/plugins/catalog-react/src/overridableComponents.ts index 1e654988e4..b06d832481 100644 --- a/plugins/catalog-react/src/overridableComponents.ts +++ b/plugins/catalog-react/src/overridableComponents.ts @@ -22,6 +22,7 @@ import { CatalogReactEntitySearchBarClassKey, CatalogReactEntityTagPickerClassKey, CatalogReactEntityOwnerPickerClassKey, + CatalogReactEntityAdvancedPickerClassKey, } from './components'; /** @public */ @@ -31,6 +32,7 @@ export type CatalogReactComponentsNameToClassKey = { CatalogReactEntitySearchBar: CatalogReactEntitySearchBarClassKey; CatalogReactEntityTagPicker: CatalogReactEntityTagPickerClassKey; CatalogReactEntityOwnerPicker: CatalogReactEntityOwnerPickerClassKey; + CatalogReactEntityAdvancedPicker: CatalogReactEntityAdvancedPickerClassKey; }; /** @public */ diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx index 5250dcec0d..0e84fc00de 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.tsx @@ -28,6 +28,7 @@ import { CatalogFilterLayout, EntityLifecyclePicker, EntityListProvider, + EntityAdvancedPicker, EntityOwnerPicker, EntityTagPicker, EntityTypePicker, @@ -84,6 +85,7 @@ export function DefaultCatalogPage(props: DefaultCatalogPageProps) { + Date: Tue, 21 Jun 2022 16:35:04 -0500 Subject: [PATCH 036/101] Refactored based on PR feedback Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- plugins/catalog-react/api-report.md | 8 ++++---- .../EntityAdvancedPicker.test.tsx | 4 ++-- .../EntityAdvancedPicker/EntityAdvancedPicker.tsx | 12 ++++++------ plugins/catalog-react/src/filters.test.ts | 4 ++-- plugins/catalog-react/src/filters.ts | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 7f3d7a5f3c..61c1eb4592 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -150,11 +150,11 @@ export const EntityAdvancedPicker: () => JSX.Element; // @public export class EntityErrorFilter implements EntityFilter { - constructor(values: string[]); + constructor(value: boolean); // (undocumented) filterEntity(entity: Entity): boolean; // (undocumented) - readonly values: string[]; + readonly value: boolean; } // @public (undocumented) @@ -242,11 +242,11 @@ export type EntityLoadingStatus = { // @public export class EntityOrphanFilter implements EntityFilter { - constructor(values: string[]); + constructor(value: boolean); // (undocumented) filterEntity(entity: Entity): boolean; // (undocumented) - readonly values: string[]; + readonly value: boolean; } // @public diff --git a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx index 8964038d42..c139c66505 100644 --- a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx @@ -83,7 +83,7 @@ describe('', () => { fireEvent.click(rendered.getByTestId('advanced-picker-expand')); fireEvent.click(rendered.getByText('Is Orphan')); expect(updateFilters).toHaveBeenCalledWith({ - orphan: new EntityOrphanFilter(['true']), + orphan: new EntityOrphanFilter(true), }); }); @@ -104,7 +104,7 @@ describe('', () => { fireEvent.click(rendered.getByTestId('advanced-picker-expand')); fireEvent.click(rendered.getByText('Has Error')); expect(updateFilters).toHaveBeenCalledWith({ - error: new EntityErrorFilter(['true']), + error: new EntityErrorFilter(true), }); }); diff --git a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx index 9a813540f1..e3be9be3c5 100644 --- a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx +++ b/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx @@ -54,15 +54,15 @@ export const EntityAdvancedPicker = () => { [], ); - function orphanChange(value: string) { + function orphanChange(value: boolean) { updateFilters({ - orphan: value === 'true' ? new EntityOrphanFilter([value]) : undefined, + orphan: value ? new EntityOrphanFilter(value) : undefined, }); } - function errorChange(value: string) { + function errorChange(value: boolean) { updateFilters({ - error: value === 'true' ? new EntityErrorFilter([value]) : undefined, + error: value ? new EntityErrorFilter(value) : undefined, }); } @@ -78,8 +78,8 @@ export const EntityAdvancedPicker = () => { value={selectedAdvancedItems} onChange={(_: object, value: string[]) => { setSelectedAdvancedItems(value); - orphanChange(value.includes('Is Orphan') ? 'true' : 'false'); - errorChange(value.includes('Has Error') ? 'true' : 'false'); + orphanChange(value.includes('Is Orphan')); + errorChange(value.includes('Has Error')); }} renderOption={(option, { selected }) => ( { }; it('should find orphans', () => { - const filter = new EntityOrphanFilter(['true']); + const filter = new EntityOrphanFilter(true); expect(filter.filterEntity(orphan)).toBeTruthy(); expect(filter.filterEntity(entities[1])).toBeFalsy(); }); @@ -137,7 +137,7 @@ describe('EntityErrorFilter', () => { }; it('should find errors', () => { - const filter = new EntityErrorFilter(['true']); + const filter = new EntityErrorFilter(true); expect(filter.filterEntity(error)).toBeTruthy(); expect(filter.filterEntity(entities[1])).toBeFalsy(); }); diff --git a/plugins/catalog-react/src/filters.ts b/plugins/catalog-react/src/filters.ts index efd23552e9..a828015f0e 100644 --- a/plugins/catalog-react/src/filters.ts +++ b/plugins/catalog-react/src/filters.ts @@ -169,10 +169,10 @@ export class UserListFilter implements EntityFilter { * @public */ export class EntityOrphanFilter implements EntityFilter { - constructor(readonly values: string[]) {} + constructor(readonly value: boolean) {} filterEntity(entity: Entity): boolean { const orphan = entity.metadata.annotations?.['backstage.io/orphan']; - return orphan !== undefined && this.values.includes(orphan); + return orphan !== undefined && this.value.toString() === orphan; } } @@ -181,10 +181,10 @@ export class EntityOrphanFilter implements EntityFilter { * @public */ export class EntityErrorFilter implements EntityFilter { - constructor(readonly values: string[]) {} + constructor(readonly value: boolean) {} filterEntity(entity: Entity): boolean { const error = ((entity as AlphaEntity)?.status?.items?.length as number) > 0; - return error !== undefined && this.values.includes(error.toString()); + return error !== undefined && this.value === error; } } From 5395ab9ebb3a3b32e10da138b2792a0163b20a23 Mon Sep 17 00:00:00 2001 From: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Date: Fri, 24 Jun 2022 08:05:15 -0500 Subject: [PATCH 037/101] Renamed picker Signed-off-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> --- .changeset/wicked-ladybugs-argue.md | 6 ++-- plugins/catalog-react/api-report.md | 14 +++++----- .../EntityProcessingStatusPicker.test.tsx} | 28 +++++++++---------- .../EntityProcessingStatusPicker.tsx} | 12 ++++---- .../index.ts | 4 +-- plugins/catalog-react/src/components/index.ts | 2 +- .../src/overridableComponents.ts | 4 +-- .../CatalogPage/DefaultCatalogPage.tsx | 4 +-- 8 files changed, 38 insertions(+), 36 deletions(-) rename plugins/catalog-react/src/components/{EntityAdvancedPicker/EntityAdvancedPicker.test.tsx => EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx} (81%) rename plugins/catalog-react/src/components/{EntityAdvancedPicker/EntityAdvancedPicker.tsx => EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx} (89%) rename plugins/catalog-react/src/components/{EntityAdvancedPicker => EntityProcessingStatusPicker}/index.ts (73%) diff --git a/.changeset/wicked-ladybugs-argue.md b/.changeset/wicked-ladybugs-argue.md index d75f250689..9a36cf17cb 100644 --- a/.changeset/wicked-ladybugs-argue.md +++ b/.changeset/wicked-ladybugs-argue.md @@ -3,7 +3,7 @@ '@backstage/plugin-catalog-react': patch --- -Added new `EntityAdvancedPicker` that will filter for entities with orphans and/or errors. +Added new `EntityProcessingStatusPicker` that will filter for entities with orphans and/or errors. If you are using the default Catalog page this picker will be added automatically. For those who have customized their Catalog page you'll need to add this manually by doing something like this: @@ -14,7 +14,7 @@ import { EntityTypePicker, UserListPicker, EntityTagPicker -+ EntityAdvancedPicker, ++ EntityProcessingStatusPicker, } from '@backstage/plugin-catalog-react'; ... export const CustomCatalogPage = ({ @@ -31,7 +31,7 @@ export const CustomCatalogPage = ({ -+ ++ diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 61c1eb4592..5232c49150 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -78,18 +78,18 @@ export type CatalogReactComponentsNameToClassKey = { CatalogReactEntitySearchBar: CatalogReactEntitySearchBarClassKey; CatalogReactEntityTagPicker: CatalogReactEntityTagPickerClassKey; CatalogReactEntityOwnerPicker: CatalogReactEntityOwnerPickerClassKey; - CatalogReactEntityAdvancedPicker: CatalogReactEntityAdvancedPickerClassKey; + CatalogReactEntityProcessingStatusPicker: CatalogReactEntityProcessingStatusPickerClassKey; }; -// @public (undocumented) -export type CatalogReactEntityAdvancedPickerClassKey = 'input'; - // @public (undocumented) export type CatalogReactEntityLifecyclePickerClassKey = 'input'; // @public (undocumented) export type CatalogReactEntityOwnerPickerClassKey = 'input'; +// @public (undocumented) +export type CatalogReactEntityProcessingStatusPickerClassKey = 'input'; + // @public (undocumented) export type CatalogReactEntitySearchBarClassKey = 'searchToolbar' | 'input'; @@ -145,9 +145,6 @@ export type DefaultEntityFilters = { error?: EntityErrorFilter; }; -// @public (undocumented) -export const EntityAdvancedPicker: () => JSX.Element; - // @public export class EntityErrorFilter implements EntityFilter { constructor(value: boolean); @@ -263,6 +260,9 @@ export class EntityOwnerFilter implements EntityFilter { // @public (undocumented) export const EntityOwnerPicker: () => JSX.Element | null; +// @public (undocumented) +export const EntityProcessingStatusPicker: () => JSX.Element; + // @public export const EntityProvider: (props: EntityProviderProps) => JSX.Element; diff --git a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx similarity index 81% rename from plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx rename to plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx index c139c66505..8e90e04148 100644 --- a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.test.tsx @@ -19,7 +19,7 @@ import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import { EntityErrorFilter, EntityOrphanFilter } from '../../filters'; import { MockEntityListContextProvider } from '../../testUtils/providers'; -import { EntityAdvancedPicker } from './EntityAdvancedPicker'; +import { EntityProcessingStatusPicker } from './EntityProcessingStatusPicker'; const orphanAnnotation: Record = {}; orphanAnnotation['backstage.io/orphan'] = 'true'; @@ -50,18 +50,18 @@ const sampleEntities: Entity[] = [ }, ]; -describe('', () => { - it('renders all advanced options', () => { +describe('', () => { + it('renders all processing status options', () => { const rendered = render( - + , ); - expect(rendered.getByText('Advanced')).toBeInTheDocument(); + expect(rendered.getByText('Processing Status')).toBeInTheDocument(); - fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); expect(rendered.getByText('Is Orphan')).toBeInTheDocument(); expect(rendered.getByText('Has Error')).toBeInTheDocument(); }); @@ -76,11 +76,11 @@ describe('', () => { updateFilters, }} > - + , ); - fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); fireEvent.click(rendered.getByText('Is Orphan')); expect(updateFilters).toHaveBeenCalledWith({ orphan: new EntityOrphanFilter(true), @@ -97,11 +97,11 @@ describe('', () => { updateFilters, }} > - + , ); - fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); fireEvent.click(rendered.getByText('Has Error')); expect(updateFilters).toHaveBeenCalledWith({ error: new EntityErrorFilter(true), @@ -118,11 +118,11 @@ describe('', () => { updateFilters, }} > - + , ); - fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); fireEvent.click(rendered.getByText('Is Orphan')); expect(updateFilters).toHaveBeenCalledWith({ orphan: undefined, @@ -139,11 +139,11 @@ describe('', () => { updateFilters, }} > - + , ); - fireEvent.click(rendered.getByTestId('advanced-picker-expand')); + fireEvent.click(rendered.getByTestId('processing-status-picker-expand')); fireEvent.click(rendered.getByText('Has Error')); expect(updateFilters).toHaveBeenCalledWith({ error: undefined, diff --git a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx similarity index 89% rename from plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx rename to plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx index e3be9be3c5..3fe3b60e25 100644 --- a/plugins/catalog-react/src/components/EntityAdvancedPicker/EntityAdvancedPicker.tsx +++ b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx @@ -31,14 +31,14 @@ import { useEntityList } from '../../hooks'; import { Autocomplete } from '@material-ui/lab'; /** @public */ -export type CatalogReactEntityAdvancedPickerClassKey = 'input'; +export type CatalogReactEntityProcessingStatusPickerClassKey = 'input'; const useStyles = makeStyles( { input: {}, }, { - name: 'CatalogReactEntityAdvancedPicker', + name: 'CatalogReactEntityProcessingStatusPickerPicker', }, ); @@ -46,7 +46,7 @@ const icon = ; const checkedIcon = ; /** @public */ -export const EntityAdvancedPicker = () => { +export const EntityProcessingStatusPicker = () => { const classes = useStyles(); const { updateFilters } = useEntityList(); @@ -71,7 +71,7 @@ export const EntityAdvancedPicker = () => { return ( - Advanced + Processing Status { /> )} size="small" - popupIcon={} + popupIcon={ + + } renderInput={params => ( - + Date: Thu, 23 Jun 2022 10:39:12 +1000 Subject: [PATCH 038/101] Add a DefaultParentEntityPolicy. It's useful to have an entity policy to set a parent for unparented groups. This can be used to build a groups hierarchy with a single root parent group without having to make changes to every group entity in the catalog. Signed-off-by: James Peach --- .changeset/proud-toys-return.md | 5 ++ packages/catalog-model/api-report.md | 7 ++ .../DefaultParentEntityPolicy.test.ts | 71 +++++++++++++++++++ .../policies/DefaultParentEntityPolicy.ts | 68 ++++++++++++++++++ .../src/entity/policies/index.ts | 1 + 5 files changed, 152 insertions(+) create mode 100644 .changeset/proud-toys-return.md create mode 100644 packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.test.ts create mode 100644 packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.ts diff --git a/.changeset/proud-toys-return.md b/.changeset/proud-toys-return.md new file mode 100644 index 0000000000..c2a713a5c5 --- /dev/null +++ b/.changeset/proud-toys-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': patch +--- + +Introduced DefaultParentEntityPolicy to set a default group entity parent. diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 3eeb054174..eb8bf9af93 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -108,6 +108,13 @@ export class DefaultNamespaceEntityPolicy implements EntityPolicy { enforce(entity: Entity): Promise; } +// @public +export class DefaultParentEntityPolicy implements EntityPolicy { + constructor(parent: string); + // (undocumented) + enforce(entity: Entity): Promise; +} + // @public interface DomainEntityV1alpha1 extends Entity { // (undocumented) diff --git a/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.test.ts b/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.test.ts new file mode 100644 index 0000000000..de7c332d17 --- /dev/null +++ b/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.test.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { UserEntity, GroupEntity } from '../../kinds'; +import { DefaultParentEntityPolicy } from './DefaultParentEntityPolicy'; + +describe('DefaultParentEntityPolicy', () => { + it('should ignore non-group entities', async () => { + const p = new DefaultParentEntityPolicy('name'); + const u: UserEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { name: 'n' }, + spec: { profile: {}, memberOf: ['c'] }, + }; + const result = await p.enforce(u); + expect(result).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { name: 'n' }, + spec: { profile: {}, memberOf: ['c'] }, + }); + }); + + it('should parent group entities', async () => { + const p = new DefaultParentEntityPolicy('name'); + const g: GroupEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { name: 'n' }, + spec: { type: 'foo', children: [] }, + }; + const result = await p.enforce(g); + expect(result).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { name: 'n' }, + spec: { type: 'foo', parent: 'group:default/name', children: [] }, + }); + }); + + it('should not replace existing parents', async () => { + const p = new DefaultParentEntityPolicy('namespace/name'); + const g: GroupEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { name: 'n' }, + spec: { type: 'foo', parent: 'group:something/else', children: [] }, + }; + const result = await p.enforce(g); + expect(result).toEqual({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Group', + metadata: { name: 'n' }, + spec: { type: 'foo', parent: 'group:something/else', children: [] }, + }); + }); +}); diff --git a/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.ts b/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.ts new file mode 100644 index 0000000000..b5a001b1f9 --- /dev/null +++ b/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '../Entity'; +import { GroupEntity } from '../../kinds'; +import { EntityPolicy } from './types'; +import { DEFAULT_NAMESPACE } from '../constants'; +import { parseEntityRef, stringifyEntityRef } from '../ref'; + +/** + * DefaultParentPolicy is an EntityPolicy that updates group entities + * with a parent of last resort. This ensures that, while we preserve + * any existing group hierarchies, we can guarantee that there is a + * single global root of the group hierarchy. + * + * @public + */ +export class DefaultParentEntityPolicy implements EntityPolicy { + private readonly parentRef: string; + + constructor(parentEntityRef: string) { + const { kind, namespace, name } = parseEntityRef(parentEntityRef, { + defaultKind: 'Group', + defaultNamespace: DEFAULT_NAMESPACE, + }); + + if (kind.toLocaleUpperCase('en-US') !== 'GROUP') { + throw new TypeError('group parent must be a group'); + } + + this.parentRef = stringifyEntityRef({ + kind: kind, + namespace: namespace, + name: name, + }); + } + + async enforce(entity: Entity): Promise { + if (entity.kind !== 'Group') { + return entity; + } + + const group = entity as GroupEntity; + if (group.spec.parent) { + return group; + } + + // Avoid making the parent entity it's own parent. + if (stringifyEntityRef(group) !== this.parentRef) { + group.spec.parent = this.parentRef; + } + + return group; + } +} diff --git a/packages/catalog-model/src/entity/policies/index.ts b/packages/catalog-model/src/entity/policies/index.ts index ae14007d80..c35a214727 100644 --- a/packages/catalog-model/src/entity/policies/index.ts +++ b/packages/catalog-model/src/entity/policies/index.ts @@ -15,6 +15,7 @@ */ export { DefaultNamespaceEntityPolicy } from './DefaultNamespaceEntityPolicy'; +export { DefaultParentEntityPolicy } from './DefaultParentEntityPolicy'; export { FieldFormatEntityPolicy } from './FieldFormatEntityPolicy'; export { NoForeignRootFieldsEntityPolicy } from './NoForeignRootFieldsEntityPolicy'; export { SchemaValidEntityPolicy } from './SchemaValidEntityPolicy'; From 03019695a3e8f4c7931b989d4eda20e3edde4f24 Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Wed, 8 Jun 2022 16:52:48 +0200 Subject: [PATCH 039/101] Add special classes to identify entity page headers and tabs. Use that to make sure the sidebars in docs do not scroll beyond them Signed-off-by: Raghunandan --- .../src/components/TabbedLayout/RoutedTabs.tsx | 1 + .../src/layout/Header/Header.tsx | 4 +++- .../src/layout/HeaderTabs/HeaderTabs.tsx | 4 +++- .../components/EntityLayout/EntityLayout.tsx | 1 + .../TechDocsReaderPageContent/dom.tsx | 18 +++++++++++++++++- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx index 5debafd3f8..027d63e1a0 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx @@ -80,6 +80,7 @@ export function RoutedTabs(props: { routes: SubRoute[] }) { tabs={headerTabs} selectedIndex={index} onChange={onTabChange} + className="entity-page-tabs" /> diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index bb4d19b949..cb47550b6e 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -113,6 +113,7 @@ type Props = { tooltip?: string; type?: string; typeLink?: string; + className: string | ''; }; type TypeFragmentProps = { @@ -208,6 +209,7 @@ export function Header(props: PropsWithChildren) { tooltip, type, typeLink, + className, } = props; const classes = useStyles(); const configApi = useApi(configApiRef); @@ -220,7 +222,7 @@ export function Header(props: PropsWithChildren) { return ( <> -
+
void; selectedIndex?: number; + className: string | ''; }; /** @@ -76,7 +77,7 @@ type HeaderTabsProps = { * */ export function HeaderTabs(props: HeaderTabsProps) { - const { tabs, onChange, selectedIndex } = props; + const { tabs, onChange, selectedIndex, className } = props; const [selectedTab, setSelectedTab] = useState(selectedIndex ?? 0); const styles = useStyles(); @@ -104,6 +105,7 @@ export function HeaderTabs(props: HeaderTabsProps) { aria-label="scrollable auto tabs example" onChange={handleChange} value={selectedTab} + className={className} > {tabs.map((tab, index) => ( { title={} pageTitleOverride={headerTitle} type={headerType} + className="entity-page-header" > {entity && ( <> diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index 9f561bfcd3..497a3e1e5f 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -81,9 +81,25 @@ export const useTechDocsReaderDom = ( if (isMobileMedia) { element.style.top = '0px'; } else { - const domTop = dom.getBoundingClientRect().top ?? 0; + // Docs shown in entity pages should consider the entity tabs and multiple paddings at the top + const entityPageHeader = document.querySelector( + '.entity-page-header', + ); + const entityPageTabs = + document.querySelector('.entity-page-tabs'); + const entityPageHeaderTop = + entityPageHeader?.getBoundingClientRect().height ?? 0; + const entityPageTabsTop = + entityPageTabs?.getBoundingClientRect().height ?? 0; + + let domTop = dom.getBoundingClientRect().top ?? 0; const tabs = dom.querySelector('.md-container > .md-tabs'); const tabsHeight = tabs?.getBoundingClientRect().height ?? 0; + + // In entity pages the sidebars should stop at the tabs + if (domTop < entityPageHeaderTop + entityPageTabsTop) { + domTop = entityPageHeaderTop + entityPageTabsTop; + } element.style.top = `${Math.max(domTop, 0) + tabsHeight}px`; } From b49cc12d957f8dc173303beb15a137e0f6305e97 Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Wed, 8 Jun 2022 16:58:20 +0200 Subject: [PATCH 040/101] reorder code + fix comments Signed-off-by: Raghunandan --- .../components/TechDocsReaderPageContent/dom.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index 497a3e1e5f..0503a91a3d 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -81,7 +81,12 @@ export const useTechDocsReaderDom = ( if (isMobileMedia) { element.style.top = '0px'; } else { - // Docs shown in entity pages should consider the entity tabs and multiple paddings at the top + let domTop = dom.getBoundingClientRect().top ?? 0; + const tabs = dom.querySelector('.md-container > .md-tabs'); + const tabsHeight = tabs?.getBoundingClientRect().height ?? 0; + + // When docs are shown in entity pages this method to reposition the sidebars on scroll + // do not consider the header and tabs. const entityPageHeader = document.querySelector( '.entity-page-header', ); @@ -92,11 +97,7 @@ export const useTechDocsReaderDom = ( const entityPageTabsTop = entityPageTabs?.getBoundingClientRect().height ?? 0; - let domTop = dom.getBoundingClientRect().top ?? 0; - const tabs = dom.querySelector('.md-container > .md-tabs'); - const tabsHeight = tabs?.getBoundingClientRect().height ?? 0; - - // In entity pages the sidebars should stop at the tabs + // the sidebars should not scroll beyond the total height of the header and tabs if (domTop < entityPageHeaderTop + entityPageTabsTop) { domTop = entityPageHeaderTop + entityPageTabsTop; } From 05e68b0b3f63f14601912dc3236d0771620173ab Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Wed, 8 Jun 2022 17:15:54 +0200 Subject: [PATCH 041/101] Header & HeaderTabs components: make className prop optional Signed-off-by: Raghunandan --- packages/core-components/src/layout/Header/Header.tsx | 2 +- packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index cb47550b6e..4b7d59d3c2 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -113,7 +113,7 @@ type Props = { tooltip?: string; type?: string; typeLink?: string; - className: string | ''; + className?: string | ''; }; type TypeFragmentProps = { diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx index 794f26d44f..2e6d6213fc 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx @@ -67,7 +67,7 @@ type HeaderTabsProps = { tabs: Tab[]; onChange?: (index: number) => void; selectedIndex?: number; - className: string | ''; + className?: string | ''; }; /** From 7739141ab2452ee8ca3a2198d981e12325f502dc Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Mon, 13 Jun 2022 23:26:05 +0200 Subject: [PATCH 042/101] Add changeset Signed-off-by: Raghunandan --- .changeset/techdocs-sixty-mugs-hug.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/techdocs-sixty-mugs-hug.md diff --git a/.changeset/techdocs-sixty-mugs-hug.md b/.changeset/techdocs-sixty-mugs-hug.md new file mode 100644 index 0000000000..05c03a0b98 --- /dev/null +++ b/.changeset/techdocs-sixty-mugs-hug.md @@ -0,0 +1,7 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-techdocs': patch +--- + +Fix: When docs are shown in an entity page under the docs tab the sidebars start overlapping with the header and tabs in the page when you scroll the documentation content. From 823ae416c5e29e6f7a9b259ef5fdf401f4911bcf Mon Sep 17 00:00:00 2001 From: Raghunandan Date: Sun, 26 Jun 2022 10:56:32 +0200 Subject: [PATCH 043/101] Fix scroll issue within techdocs reader instead of adding special classes to entity page components Signed-off-by: Raghunandan --- .../components/TabbedLayout/RoutedTabs.tsx | 1 - .../src/layout/Header/Header.tsx | 4 +--- .../src/layout/HeaderTabs/HeaderTabs.tsx | 4 +--- .../components/EntityLayout/EntityLayout.tsx | 1 - .../TechDocsReaderPage/TechDocsReaderPage.tsx | 22 ++++++++++--------- .../TechDocsReaderPageContent/dom.tsx | 19 +++++----------- 6 files changed, 19 insertions(+), 32 deletions(-) diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx index 027d63e1a0..5debafd3f8 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx @@ -80,7 +80,6 @@ export function RoutedTabs(props: { routes: SubRoute[] }) { tabs={headerTabs} selectedIndex={index} onChange={onTabChange} - className="entity-page-tabs" /> diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 4b7d59d3c2..bb4d19b949 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -113,7 +113,6 @@ type Props = { tooltip?: string; type?: string; typeLink?: string; - className?: string | ''; }; type TypeFragmentProps = { @@ -209,7 +208,6 @@ export function Header(props: PropsWithChildren) { tooltip, type, typeLink, - className, } = props; const classes = useStyles(); const configApi = useApi(configApiRef); @@ -222,7 +220,7 @@ export function Header(props: PropsWithChildren) { return ( <> -
+
void; selectedIndex?: number; - className?: string | ''; }; /** @@ -77,7 +76,7 @@ type HeaderTabsProps = { * */ export function HeaderTabs(props: HeaderTabsProps) { - const { tabs, onChange, selectedIndex, className } = props; + const { tabs, onChange, selectedIndex } = props; const [selectedTab, setSelectedTab] = useState(selectedIndex ?? 0); const styles = useStyles(); @@ -105,7 +104,6 @@ export function HeaderTabs(props: HeaderTabsProps) { aria-label="scrollable auto tabs example" onChange={handleChange} value={selectedTab} - className={className} > {tabs.map((tab, index) => ( { title={} pageTitleOverride={headerTitle} type={headerType} - className="entity-page-header" > {entity && ( <> diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx index 6885957002..9f69dcb8d8 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx @@ -106,16 +106,18 @@ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { return ( {({ metadata, entityMetadata, onReady }) => ( - - {children instanceof Function - ? children({ - entityRef, - techdocsMetadataValue: metadata.value, - entityMetadataValue: entityMetadata.value, - onReady, - }) - : children} - +
+ + {children instanceof Function + ? children({ + entityRef, + techdocsMetadataValue: metadata.value, + entityMetadataValue: entityMetadata.value, + onReady, + }) + : children} + +
)}
); diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index 0503a91a3d..76b07c9d32 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -81,25 +81,16 @@ export const useTechDocsReaderDom = ( if (isMobileMedia) { element.style.top = '0px'; } else { + const page = document?.querySelector('.techdocs-reader-page'); + const pageTop = page?.getBoundingClientRect().top ?? 0; let domTop = dom.getBoundingClientRect().top ?? 0; + const tabs = dom.querySelector('.md-container > .md-tabs'); const tabsHeight = tabs?.getBoundingClientRect().height ?? 0; - // When docs are shown in entity pages this method to reposition the sidebars on scroll - // do not consider the header and tabs. - const entityPageHeader = document.querySelector( - '.entity-page-header', - ); - const entityPageTabs = - document.querySelector('.entity-page-tabs'); - const entityPageHeaderTop = - entityPageHeader?.getBoundingClientRect().height ?? 0; - const entityPageTabsTop = - entityPageTabs?.getBoundingClientRect().height ?? 0; - // the sidebars should not scroll beyond the total height of the header and tabs - if (domTop < entityPageHeaderTop + entityPageTabsTop) { - domTop = entityPageHeaderTop + entityPageTabsTop; + if (domTop < pageTop) { + domTop = pageTop; } element.style.top = `${Math.max(domTop, 0) + tabsHeight}px`; } From 69c6e4751e9146f09c576c2cffc401e2d5eb0379 Mon Sep 17 00:00:00 2001 From: df11 Date: Mon, 27 Jun 2022 14:32:09 +0200 Subject: [PATCH 044/101] Update links title Signed-off-by: denis.fortin --- .../src/components/EntityVaultTable/EntityVaultTable.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx index 5e369ff6d0..85035c04d9 100644 --- a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx +++ b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx @@ -58,7 +58,7 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => { view: ( @@ -67,7 +67,7 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => { edit: ( From eded24fc8c2bb1a65dd849740427ea22925b2dac Mon Sep 17 00:00:00 2001 From: asaini1 Date: Mon, 27 Jun 2022 14:01:53 +0100 Subject: [PATCH 045/101] Remove duplicate license Signed-off-by: asaini1 --- .../builtin/publish/gitlabMergeRequest.test.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts index 7fe01921d1..987e360617 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/gitlabMergeRequest.test.ts @@ -13,21 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// * Copyright 2021 The Backstage Authors -// * -// * Licensed under the Apache License, Version 2.0 (the "License"); -// * you may not use this file except in compliance with the License. -// * You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ import { getRootLogger } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; From dbb507eb02e9cd9a2b26cf8e297771e67ecf032a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Jun 2022 18:25:04 +0000 Subject: [PATCH 046/101] chore(deps): update dependency lint-staged to v13.0.3 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b038eb72df..4782e2562d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17158,9 +17158,9 @@ linkify-it@^3.0.1: uc.micro "^1.0.1" lint-staged@^13.0.0: - version "13.0.2" - resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.2.tgz#35a1c57130e9ad5b1dea784972a40777ba433dd5" - integrity sha512-qQLfLTh9z34eMzfEHENC+QBskZfxjomrf+snF3xJ4BzilORbD989NLqQ00ughsF/A+PT41e87+WsMFabf9++pQ== + version "13.0.3" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz#d7cdf03a3830b327a2b63c6aec953d71d9dc48c6" + integrity sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug== dependencies: cli-truncate "^3.1.0" colorette "^2.0.17" From b5170119fcc0d40f2e1089e522b9062f120e2a56 Mon Sep 17 00:00:00 2001 From: "denis.fortin" Date: Tue, 28 Jun 2022 11:04:41 +0200 Subject: [PATCH 047/101] fix(vault-plugin): code factorization Signed-off-by: denis.fortin --- .changeset/moody-crabs-march.md | 4 ++-- .../EntityVaultTable/EntityVaultTable.tsx | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.changeset/moody-crabs-march.md b/.changeset/moody-crabs-march.md index 3d7bc2d290..f3ae24629a 100644 --- a/.changeset/moody-crabs-march.md +++ b/.changeset/moody-crabs-march.md @@ -1,6 +1,6 @@ --- -'@backstage/plugin-vault': minor -'@backstage/plugin-vault-backend': minor +'@backstage/plugin-vault': patch +'@backstage/plugin-vault-backend': patch --- Added a path notion in addition to secret name to allow to differentiate secrets in subpaths diff --git a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx index 85035c04d9..5923a4db6b 100644 --- a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx +++ b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx @@ -32,6 +32,13 @@ export const vaultSecretPath = (entity: Entity) => { return { secretPath }; }; +function getSecretRelativeName( + secretPath: string, + secret: VaultSecret, +): string { + return `${secret.path.replace(`${secretPath}/`, '')}/${secret.name}`; +} + export const EntityVaultTable = ({ entity }: { entity: Entity }) => { const vaultApi = useApi(vaultApiRef); const { secretPath } = vaultSecretPath(entity); @@ -54,11 +61,11 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => { const data = (value || []).map(secret => { return { - secret: `${secret.path.replace(secretPath + "/", "")}/${secret.name}`, + secret: getSecretRelativeName(secretPath, secret), view: ( @@ -67,7 +74,7 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => { edit: ( From d1994e7ed11de5cf2c5cc6e3b764aefeceaf997f Mon Sep 17 00:00:00 2001 From: "denis.fortin" Date: Tue, 28 Jun 2022 11:43:16 +0200 Subject: [PATCH 048/101] fix(vault-plugin): rework Signed-off-by: denis.fortin --- .changeset/moody-crabs-march.md | 2 +- .../EntityVaultTable/EntityVaultTable.tsx | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.changeset/moody-crabs-march.md b/.changeset/moody-crabs-march.md index f3ae24629a..b5ddf12ffa 100644 --- a/.changeset/moody-crabs-march.md +++ b/.changeset/moody-crabs-march.md @@ -3,4 +3,4 @@ '@backstage/plugin-vault-backend': patch --- -Added a path notion in addition to secret name to allow to differentiate secrets in subpaths +Added a path notion in addition to secret name to allow to differentiate secrets in sub-paths diff --git a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx index 5923a4db6b..efc345b07f 100644 --- a/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx +++ b/plugins/vault/src/components/EntityVaultTable/EntityVaultTable.tsx @@ -32,13 +32,6 @@ export const vaultSecretPath = (entity: Entity) => { return { secretPath }; }; -function getSecretRelativeName( - secretPath: string, - secret: VaultSecret, -): string { - return `${secret.path.replace(`${secretPath}/`, '')}/${secret.name}`; -} - export const EntityVaultTable = ({ entity }: { entity: Entity }) => { const vaultApi = useApi(vaultApiRef); const { secretPath } = vaultSecretPath(entity); @@ -60,12 +53,16 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => { ]; const data = (value || []).map(secret => { + const secretName = `${secret.path.replace(`${secretPath}/`, '')}/${ + secret.name + }`; + return { - secret: getSecretRelativeName(secretPath, secret), + secret: secretName, view: ( @@ -74,7 +71,7 @@ export const EntityVaultTable = ({ entity }: { entity: Entity }) => { edit: ( From 14146703e5bb5389316e006c30afadf8f7f5a4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Aym=C3=A0?= Date: Tue, 28 Jun 2022 12:59:32 +0200 Subject: [PATCH 049/101] Add allowArbitraryValues in OwnedEntityPicker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Joan Aymร  --- .changeset/weak-jeans-cry.md | 5 +++++ .../fields/OwnedEntityPicker/OwnedEntityPicker.tsx | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .changeset/weak-jeans-cry.md diff --git a/.changeset/weak-jeans-cry.md b/.changeset/weak-jeans-cry.md new file mode 100644 index 0000000000..7be5c50279 --- /dev/null +++ b/.changeset/weak-jeans-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Add allowArbitraryValues to ui:options in OwnedEntityPicker uiSchema, similar to allowArbitraryValues in EntityPicker diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx index 8e3590e8bf..d176c0ec59 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx @@ -60,6 +60,8 @@ export const OwnedEntityPicker = ( const allowedKinds = uiSchema['ui:options']?.allowedKinds; const defaultKind = uiSchema['ui:options']?.defaultKind; + const allowArbitraryValues = + uiSchema['ui:options']?.allowArbitraryValues ?? true; const { ownedEntities, loading } = useOwnedEntities(allowedKinds); const entityRefs = ownedEntities?.items @@ -83,7 +85,7 @@ export const OwnedEntityPicker = ( onChange={onSelect} options={entityRefs || []} autoSelect - freeSolo + freeSolo={allowArbitraryValues} renderInput={params => ( Date: Tue, 28 Jun 2022 13:30:37 +0200 Subject: [PATCH 050/101] add allowArbitraryValues to OwnedEntityPicker ui:options list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Joan Aymร  --- .../components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx index d176c0ec59..a280ebad6a 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx @@ -37,6 +37,7 @@ import { FieldExtensionComponentProps } from '../../../extensions'; export interface OwnedEntityPickerUiOptions { allowedKinds?: string[]; defaultKind?: string; + allowArbitraryValues?: boolean; } /** From 67e32e15500c665fb44de1d5197ceb72f5c1c202 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 11:37:34 +0000 Subject: [PATCH 051/101] fix(deps): update dependency @rollup/plugin-commonjs to v22.0.1 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a4d732961e..bcc192f64f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5382,9 +5382,9 @@ react-use "^17.2.4" "@rollup/plugin-commonjs@^22.0.0": - version "22.0.0" - resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.0.tgz#f4d87016e2fbf187a593ab9f46626fe05b59e8bd" - integrity sha512-Ktvf2j+bAO+30awhbYoCaXpBcyPmJbaEUYClQns/+6SNCYFURbvBiNbWgHITEsIgDDWCDUclWRKEuf8cwZCFoQ== + version "22.0.1" + resolved "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.1.tgz#f7cb777d20de3eeeaf994f39080115c336bef810" + integrity sha512-dGfEZvdjDHObBiP5IvwTKMVeq/tBZGMBHZFMdIV1ClMM/YoWS34xrHFGfag9SN2ZtMgNZRFruqvxZQEa70O6nQ== dependencies: "@rollup/pluginutils" "^3.1.0" commondir "^1.0.1" From b8ca2f4f2e82a75341d309e95af4346ef3268ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Aym=C3=A0?= Date: Tue, 28 Jun 2022 13:40:53 +0200 Subject: [PATCH 052/101] update changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Joan Aymร  --- .changeset/weak-jeans-cry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/weak-jeans-cry.md b/.changeset/weak-jeans-cry.md index 7be5c50279..79f419e3db 100644 --- a/.changeset/weak-jeans-cry.md +++ b/.changeset/weak-jeans-cry.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder': patch --- -Add allowArbitraryValues to ui:options in OwnedEntityPicker uiSchema, similar to allowArbitraryValues in EntityPicker +Add `allowArbitraryValues` to `ui:options` in `OwnedEntityPicker`, similar to `allowArbitraryValues` in `EntityPicker` From 9ddaeb014c1322738d5cbd857af607bc28c7be62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Aym=C3=A0?= Date: Tue, 28 Jun 2022 14:42:49 +0200 Subject: [PATCH 053/101] update api-reports for allowArbitraryValues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Joan Aymร  --- plugins/scaffolder/api-report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 9ffca426ac..c20cd0eb67 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -153,6 +153,8 @@ export const OwnedEntityPickerFieldExtension: FieldExtensionComponent< // @public export interface OwnedEntityPickerUiOptions { + // (undocumented) + allowArbitraryValues?: boolean; // (undocumented) allowedKinds?: string[]; // (undocumented) From 652c64684e3aa54127457c12b7a61e0350a0dc1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 28 Jun 2022 16:18:32 +0200 Subject: [PATCH 054/101] Use the custom cron action for DCO and renovate merges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: blam Co-authored-by: Johan Haals Signed-off-by: Fredrik Adelรถw --- .../automate_merge_renovate_prs.yaml | 85 ------------------- .github/workflows/cron.yml | 11 +++ .github/workflows/verify_dco.yaml | 65 -------------- 3 files changed, 11 insertions(+), 150 deletions(-) delete mode 100644 .github/workflows/automate_merge_renovate_prs.yaml create mode 100644 .github/workflows/cron.yml delete mode 100644 .github/workflows/verify_dco.yaml diff --git a/.github/workflows/automate_merge_renovate_prs.yaml b/.github/workflows/automate_merge_renovate_prs.yaml deleted file mode 100644 index 8de488cf05..0000000000 --- a/.github/workflows/automate_merge_renovate_prs.yaml +++ /dev/null @@ -1,85 +0,0 @@ -name: Automate Merge Renovate PRs - -on: - workflow_dispatch: - schedule: - - cron: '*/10 * * * *' - -jobs: - diff: - runs-on: ubuntu-latest - steps: - - uses: actions/github-script@v6 - with: - script: | - const owner = "backstage"; - const repo = "backstage"; - const query = `{ - repository(owner: "backstage", name: "backstage") { - pullRequests(labels: ["dependencies"], last: 10, states: [OPEN]) { - nodes { - title - author { - login - } - number - mergeable - files(first: 1) { - nodes { - path - } - } - changedFiles - commits(last: 1) { - nodes { - commit { - statusCheckRollup { - state - } - } - } - } - reviewDecision - reviews(first: 10) { - nodes { - author { - login - } - } - } - } - } - } - }`; - - const date = new Date(); - if (date.getDay() === 2) { - console.log("Skipping auto merge because Tuesday is release day"); - return; - } - - const r = await github.graphql(query); - const mergable = r.repository.pullRequests.nodes.filter( - (pr) => - pr.author.login === "renovate" && - pr.mergeable === "MERGEABLE" && - pr.changedFiles === 1 && - pr.files.nodes[0].path.split("/").slice(-1)[0] === "yarn.lock" && - pr.commits.nodes[0].commit.statusCheckRollup.state === "SUCCESS" && - pr.reviewDecision === "APPROVED" - ); - - if (mergable.length === 0) { - console.log("no mergable PRs"); - return; - } - - for (const pr of mergable) { - console.log(`Merging #${pr.number} - ${pr.title}`); - await github.rest.pulls.merge({ - owner, - repo, - pull_number: pr.number, - }); - await new Promise((r) => setTimeout(r, 2000)); - } diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml new file mode 100644 index 0000000000..394d726fef --- /dev/null +++ b/.github/workflows/cron.yml @@ -0,0 +1,11 @@ +name: Cron +on: + workflow_dispatch: + schedule: + - cron: '*/5 * * * *' + +jobs: + cron: + runs-on: ubuntu-latest + steps: + - uses: backstage/actions/cron@v0.1.3 diff --git a/.github/workflows/verify_dco.yaml b/.github/workflows/verify_dco.yaml deleted file mode 100644 index 2094327d23..0000000000 --- a/.github/workflows/verify_dco.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: Verify DCO -on: - schedule: - - cron: '*/15 * * * *' - -jobs: - dco-helper: - runs-on: ubuntu-latest - steps: - - name: Verify DCO status for open pull requests - uses: actions/github-script@v6 - with: - script: | - const owner = "backstage"; - const repo = "backstage"; - const pulls = await github.paginate(github.rest.pulls.list, { - state: "open", - owner, - repo, - }); - - for (const pull of pulls) { - // Pick out the PRs that have the DCO check - const checks = await github.rest.checks.listForRef({ - owner, - repo, - ref: pull.head.sha, - check_name: "DCO", - status: "completed", - }); - // Skip if there are no checks - if (!checks.data.check_runs.length) { - continue; - } - // Skip if the conclusion is not action_required - if (checks.data.check_runs[0].conclusion !== "action_required") { - console.log(`No checks found for PR #${pull.number}, skipping`); - continue; - } - - const comments = await github.paginate(github.rest.issues.listComments, { - owner, - repo, - issue_number: pull.number, - }); - if (comments.find((c) => - c.user.login === "github-actions[bot]" && - c.body.includes("") - ) - ) { - console.log(`already commented on PR #${pull.number}, skipping`); - continue; - } - console.log(`creating comment on PR #${pull.number}`); - const body = ` - Thanks for the contribution! - All commits need to be DCO signed before they are reviewed. Please refer to the the [DCO section in CONTRIBUTING.md](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#developer-certificate-of-origin) or the [DCO](${checks.data.check_runs[0].html_url}) status for more info. - `; - await github.rest.issues.createComment({ - repo, - owner, - issue_number: pull.number, - body, - }); - } From 31bcb855954142b7a8b150b79ceaec8c807151ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Tue, 28 Jun 2022 16:23:39 +0200 Subject: [PATCH 055/101] chore: simplify `TransformLink` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cรดme Tresarrieu --- .../src/components/MarkdownContent/MarkdownContent.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index 3052aff7d9..98d5af278f 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -64,12 +64,7 @@ const useStyles = makeStyles( { name: 'BackstageMarkdownContent' }, ); -type TransformLink = ( - href: string, - // Complex type from internal react-markdown dep library (hast)[./node_modules/@types/hast/index.d.ts] - children: any[], - title: string | null, -) => string; +type TransformLink = (href: string) => string; type Props = { content: string; From 7187adfadd7d06498e015f3ac26eaaf5ebc8c01d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 28 Jun 2022 16:28:43 +0200 Subject: [PATCH 056/101] update cron thing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelรถw --- .github/workflows/cron.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 394d726fef..52a68a7d90 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -8,4 +8,4 @@ jobs: cron: runs-on: ubuntu-latest steps: - - uses: backstage/actions/cron@v0.1.3 + - uses: backstage/actions/cron@v0.1.4 From 8451856b2c5da091628049d0c81fcb4cf25bd8f9 Mon Sep 17 00:00:00 2001 From: "denis.fortin" Date: Tue, 28 Jun 2022 17:45:50 +0200 Subject: [PATCH 057/101] add api-report.md files Signed-off-by: denis.fortin --- plugins/vault-backend/api-report.md | 1 + plugins/vault/api-report.md | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/vault-backend/api-report.md b/plugins/vault-backend/api-report.md index 9f738b15e9..d2e9e1b177 100644 --- a/plugins/vault-backend/api-report.md +++ b/plugins/vault-backend/api-report.md @@ -70,6 +70,7 @@ export interface VaultEnvironment { // @public export type VaultSecret = { name: string; + path: string; showUrl: string; editUrl: string; }; diff --git a/plugins/vault/api-report.md b/plugins/vault/api-report.md index f6fa8d1269..b347989800 100644 --- a/plugins/vault/api-report.md +++ b/plugins/vault/api-report.md @@ -32,6 +32,7 @@ export const vaultPlugin: BackstagePlugin<{}, {}>; // @public export type VaultSecret = { name: string; + path: string; showUrl: string; editUrl: string; }; From 61813aff256d261363b666238f8749232993553e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Tue, 28 Jun 2022 17:52:51 +0200 Subject: [PATCH 058/101] chore: simplify types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cรดme Tresarrieu --- .../components/MarkdownContent/MarkdownContent.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index 98d5af278f..0e5aedda90 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -64,18 +64,12 @@ const useStyles = makeStyles( { name: 'BackstageMarkdownContent' }, ); -type TransformLink = (href: string) => string; - type Props = { content: string; dialect?: 'gfm' | 'common-mark'; - linkTarget?: React.HTMLAttributeAnchorTarget | TransformLink; - transformLinkUri?: TransformLink; - transformImageUri?: ( - src: string, - alt: string, - title: string | null, - ) => string; + linkTarget?: Options['linkTarget']; + transformLinkUri?: (href: string) => string; + transformImageUri?: (href: string) => string; }; const components: Options['components'] = { From beda85068a52f4de6465fd94a0b4d52032c9cc66 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:04:49 +0000 Subject: [PATCH 059/101] fix(deps): update dependency @yarnpkg/parsers to v3.0.0-rc.10 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index bcc192f64f..ab98dea287 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7563,9 +7563,9 @@ integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== "@yarnpkg/parsers@^3.0.0-rc.4": - version "3.0.0-rc.9" - resolved "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.9.tgz#2d284e4e0c79b1c4e410465e217fa303d98b7be3" - integrity sha512-JMBE+6OJoNN9AXBzZ72u22/t9M25K8KgUWZIjvk8CU/NsE/m946l8D7SqMhbi3ZaUfYa5QitqCqVWTTFtysGJQ== + version "3.0.0-rc.10" + resolved "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.10.tgz#cbbb9970d396fd274911ef0f8204d52132394271" + integrity sha512-TZxyre59hLjWQ0FcakvEl2a+PTArDJ2VYC3e3xjec+OpTuQSK7333VscpiT2UJKJPDUGrYq1Z1LDpnIYv4nHLw== dependencies: js-yaml "^3.10.0" tslib "^1.13.0" From 7d99a0428cd2ac3fa8f5c33cfa69b4adfd38ffca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:05:44 +0000 Subject: [PATCH 060/101] fix(deps): update dependency octokit to v1.8.1 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index bcc192f64f..242b94954f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19636,9 +19636,9 @@ octokit-plugin-create-pull-request@^3.10.0: "@octokit/types" "^6.8.2" octokit@^1.7.1: - version "1.8.0" - resolved "https://registry.npmjs.org/octokit/-/octokit-1.8.0.tgz#c2ea6ace083b3be1d594aa7eb2e05fccd14e7ca5" - integrity sha512-HtArk9ttGy5effKNiaqKCirR6VSZoYjqgLgvVm/1mSRR4WYmww5DHjLnZlCbvj8+DwBLLLduJ3XnX/SPCCcp6A== + version "1.8.1" + resolved "https://registry.npmjs.org/octokit/-/octokit-1.8.1.tgz#399b0032e89e058084a1a1922e40a02e87d4cd61" + integrity sha512-xBLKFIivbl7wnLwxzLYuDO/JDNYxdyxoSjFrl/QMrY/fwGGQYYklvKUDTUyGMU0aXPrQtJ0IZnG3BXpCkDQzWg== dependencies: "@octokit/app" "^12.0.4" "@octokit/core" "^3.5.1" From e558a1ba5d6e1b4e96902bc2d7f854c639ece84e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:20:05 +0000 Subject: [PATCH 061/101] fix(deps): update dependency core-js to v3.23.3 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index be0c6fab68..51c44941a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10360,9 +10360,9 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.10: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.4.1, core-js@^3.6.5: - version "3.23.2" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.23.2.tgz#e07a60ca8b14dd129cabdc3d2551baf5a01c76f0" - integrity sha512-ELJOWxNrJfOH/WK4VJ3Qd+fOqZuOuDNDJz0xG6Bt4mGg2eO/UT9CljCrbqDGovjLKUrGajEEBcoTOc0w+yBYeQ== + version "3.23.3" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.23.3.tgz#3b977612b15da6da0c9cc4aec487e8d24f371112" + integrity sha512-oAKwkj9xcWNBAvGbT//WiCdOMpb9XQG92/Fe3ABFM/R16BsHgePG00mFOgKf7IsCtfj8tA1kHtf/VwErhriz5Q== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" From b548fb778222cd41cadcf043e9fabbeea080b6c6 Mon Sep 17 00:00:00 2001 From: Patrick Schilling Date: Tue, 28 Jun 2022 14:28:08 -0500 Subject: [PATCH 062/101] Update documentation of readonly mode to clarify what functionality is disallowed Signed-off-by: Patrick Schilling --- docs/features/software-catalog/configuration.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index 849823a59e..5abd266099 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -106,8 +106,7 @@ with [Static Location Configuration](#static-location-configuration) or a discovery processor like [GitHub Discovery](../../integrations/github/discovery.md). To enforce usage of processors to locate entities we can configure the catalog into `readonly` mode. -This configuration disables the mutating backend catalog APIs and disallows -users from registering new entities at run-time. +This configuration disables registering and deleting locations with the catalog APIs. ```yaml catalog: @@ -117,6 +116,8 @@ catalog: > **Note that any plugin relying on the catalog API for creating, updating and > deleting entities will not work in this mode.** +Deleting an entity by UUID, `DELETE /entities/by-uid/:uid`, is allowed when using this mode. It may be rediscovered as noted in [explicit deletion](life-of-an-entity.md#explicit-deletion). + A common use case for this configuration is when organizations have a remote source that should be mirrored into Backstage. To make Backstage a mirror of this remote source, users cannot also register new entities with e.g. the From 068d5a8a7d5bbb28a2a91d7ecc97563fdd56aba7 Mon Sep 17 00:00:00 2001 From: Henrique Date: Tue, 28 Jun 2022 21:14:23 -0300 Subject: [PATCH 063/101] add new adopter Signed-off-by: Henrique --- ADOPTERS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index b72da367b1..5263fa1287 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -186,3 +186,5 @@ _If you're using Backstage in your organization, please try to add your company | [Cho Tot](https://www.chotot.com) | [Chotot Team](mailto:sre@chotot.vn) | Internal developer portal, service catalog with CI/CD tools. | | [William Hill](https://www.williamhillgroup.com/) | [Pat Mills](mailto:pat.mills@williamhill.com), [Nathan Flynn](mailto:nflynn@williamhill.co.uk), and [Nishkarsh Raj](mailto:nishkarsh.raj@williamhill.co.uk) | William Hill are leveraging Backstage to build our Engineering Portal. Our mission is to centralize the software catalog inventory to enable service discoverability, reduce the onboarding time for new Engineers, provide a single pane of glass to accelerate Developer Productivity and Save Engineers time. Our aspiration is to create an InnerSource community focussed on organization-wide patterns that are re-usable and can be self-served with the Scaffolder. | | [Vodafone NewZealand Limited](https://vodafone.co.nz) | [Ankit Gupta](mailto:ankit.gupta@vodafone.nz), [DevOps COE](mailto:devopstooling@vodafone.nz) | Vodafone NZ are leveraging Backstage to build centralised and self service Engineering Portal. Our mission is to standardised Pipeline templates across the Engineering teams, One shop stop to create the pipelines and repository with a template approach which reduces creation part from days to minutes and no wait time for developers. A unified view for Azure DevOps pipeline, Azure Repo pull requests, Deployment status from Azure RedHat Openshift-ArgoCD and SonarQube Security and code quality scans report on a single pan to provide a streamlined view for all microservices across the app stack. | +| [Coamo](http://www.coamo.com.br) | [@holiiveira](https://github.com/holiiveira), [@gpxlnx](https://github.com/gpxlnx) | We're starting to use it as the main tool of a DevOps platform. Our goal is to provide software templates, centralize our software catalog enabling efficient service discovery, and make it easy to manage the entire software ecosystem in one place. + | \ No newline at end of file From 79639013b5c2290688efdda6be839ba54c9403ea Mon Sep 17 00:00:00 2001 From: Nikolas Skoufis Date: Wed, 29 Jun 2022 17:38:41 +1000 Subject: [PATCH 064/101] Explain how to enable techdocs cache For dummies like me, it took me a while to figure out that even if you have configured the backend cache, the TechDocs cache is not enabled until you set a value for the TTL. My interpretation was that it was just configured by default, and the fact that a default value for ttl seems to appear here points to that as well. This change hopes to make this explicit, to avoid others spinning their wheels like I have. Signed-off-by: Nikolas Skoufis --- docs/features/techdocs/configuration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/features/techdocs/configuration.md b/docs/features/techdocs/configuration.md index 9440d9a824..8a54d76ed0 100644 --- a/docs/features/techdocs/configuration.md +++ b/docs/features/techdocs/configuration.md @@ -160,7 +160,8 @@ techdocs: # techdocs.cache is optional, and is only recommended when you've configured # an external techdocs.publisher.type above. Also requires backend.cache to - # be configured with a valid cache store. + # be configured with a valid cache store. Configure techdocs.cache.ttl to + # enable caching of techdocs assets. cache: # Represents the number of milliseconds a statically built asset should # stay cached. Cache invalidation is handled automatically by the frontend, From 11ed544460a65d26b64999ef671033b68247bf02 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 08:14:18 +0000 Subject: [PATCH 065/101] fix(deps): update dependency eslint-plugin-react to v7.30.1 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 51c44941a6..f6fc38951f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12353,9 +12353,9 @@ eslint-plugin-react-hooks@^4.3.0: integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== eslint-plugin-react@^7.28.0: - version "7.30.0" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz#8e7b1b2934b8426ac067a0febade1b13bd7064e3" - integrity sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A== + version "7.30.1" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22" + integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg== dependencies: array-includes "^3.1.5" array.prototype.flatmap "^1.3.0" From 33fe809fbc1e51193ad26f98e2d815b0a783eaa7 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 29 Jun 2022 10:24:47 +0200 Subject: [PATCH 066/101] chore: make the required changes to get the build to pass, and implement review feedback Signed-off-by: blam --- .changeset/proud-toys-return.md | 4 ++-- packages/catalog-model/api-report.md | 14 +++++++------- ...t.ts => GroupDefaultParentEntityPolicy.test.ts} | 10 +++++----- ...Policy.ts => GroupDefaultParentEntityPolicy.ts} | 2 +- .../catalog-model/src/entity/policies/index.ts | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) rename packages/catalog-model/src/entity/policies/{DefaultParentEntityPolicy.test.ts => GroupDefaultParentEntityPolicy.test.ts} (86%) rename packages/catalog-model/src/entity/policies/{DefaultParentEntityPolicy.ts => GroupDefaultParentEntityPolicy.ts} (96%) diff --git a/.changeset/proud-toys-return.md b/.changeset/proud-toys-return.md index c2a713a5c5..f93e6ef4a8 100644 --- a/.changeset/proud-toys-return.md +++ b/.changeset/proud-toys-return.md @@ -1,5 +1,5 @@ --- -'@backstage/catalog-model': patch +'@backstage/catalog-model': minor --- -Introduced DefaultParentEntityPolicy to set a default group entity parent. +Introduced `GroupDefaultParentEntityPolicy` to set a default group entity parent. diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index eb8bf9af93..9b9136fc3c 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/api-report.md @@ -108,13 +108,6 @@ export class DefaultNamespaceEntityPolicy implements EntityPolicy { enforce(entity: Entity): Promise; } -// @public -export class DefaultParentEntityPolicy implements EntityPolicy { - constructor(parent: string); - // (undocumented) - enforce(entity: Entity): Promise; -} - // @public interface DomainEntityV1alpha1 extends Entity { // (undocumented) @@ -237,6 +230,13 @@ export function getEntitySourceLocation(entity: Entity): { target: string; }; +// @public +export class GroupDefaultParentEntityPolicy implements EntityPolicy { + constructor(parentEntityRef: string); + // (undocumented) + enforce(entity: Entity): Promise; +} + // @public interface GroupEntityV1alpha1 extends Entity { // (undocumented) diff --git a/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.test.ts b/packages/catalog-model/src/entity/policies/GroupDefaultParentEntityPolicy.test.ts similarity index 86% rename from packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.test.ts rename to packages/catalog-model/src/entity/policies/GroupDefaultParentEntityPolicy.test.ts index de7c332d17..d5eb6145c2 100644 --- a/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.test.ts +++ b/packages/catalog-model/src/entity/policies/GroupDefaultParentEntityPolicy.test.ts @@ -15,11 +15,11 @@ */ import { UserEntity, GroupEntity } from '../../kinds'; -import { DefaultParentEntityPolicy } from './DefaultParentEntityPolicy'; +import { GroupDefaultParentEntityPolicy } from './GroupDefaultParentEntityPolicy'; -describe('DefaultParentEntityPolicy', () => { +describe('GroupDefaultParentEntityPolicy', () => { it('should ignore non-group entities', async () => { - const p = new DefaultParentEntityPolicy('name'); + const p = new GroupDefaultParentEntityPolicy('name'); const u: UserEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'User', @@ -36,7 +36,7 @@ describe('DefaultParentEntityPolicy', () => { }); it('should parent group entities', async () => { - const p = new DefaultParentEntityPolicy('name'); + const p = new GroupDefaultParentEntityPolicy('name'); const g: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Group', @@ -53,7 +53,7 @@ describe('DefaultParentEntityPolicy', () => { }); it('should not replace existing parents', async () => { - const p = new DefaultParentEntityPolicy('namespace/name'); + const p = new GroupDefaultParentEntityPolicy('namespace/name'); const g: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Group', diff --git a/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.ts b/packages/catalog-model/src/entity/policies/GroupDefaultParentEntityPolicy.ts similarity index 96% rename from packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.ts rename to packages/catalog-model/src/entity/policies/GroupDefaultParentEntityPolicy.ts index b5a001b1f9..8eb0aef757 100644 --- a/packages/catalog-model/src/entity/policies/DefaultParentEntityPolicy.ts +++ b/packages/catalog-model/src/entity/policies/GroupDefaultParentEntityPolicy.ts @@ -28,7 +28,7 @@ import { parseEntityRef, stringifyEntityRef } from '../ref'; * * @public */ -export class DefaultParentEntityPolicy implements EntityPolicy { +export class GroupDefaultParentEntityPolicy implements EntityPolicy { private readonly parentRef: string; constructor(parentEntityRef: string) { diff --git a/packages/catalog-model/src/entity/policies/index.ts b/packages/catalog-model/src/entity/policies/index.ts index c35a214727..d5740c0955 100644 --- a/packages/catalog-model/src/entity/policies/index.ts +++ b/packages/catalog-model/src/entity/policies/index.ts @@ -15,7 +15,7 @@ */ export { DefaultNamespaceEntityPolicy } from './DefaultNamespaceEntityPolicy'; -export { DefaultParentEntityPolicy } from './DefaultParentEntityPolicy'; +export { GroupDefaultParentEntityPolicy } from './GroupDefaultParentEntityPolicy'; export { FieldFormatEntityPolicy } from './FieldFormatEntityPolicy'; export { NoForeignRootFieldsEntityPolicy } from './NoForeignRootFieldsEntityPolicy'; export { SchemaValidEntityPolicy } from './SchemaValidEntityPolicy'; From 3a1732170961387a490911fb1408a3d23293c03b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 08:29:05 +0000 Subject: [PATCH 067/101] fix(deps): update dependency jose to v4.8.3 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index bfe05be8f6..e4133ddb4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16411,9 +16411,9 @@ jose@^4.1.4: integrity sha512-GFcVFQwYQKbQTUOo2JlpFGXTkgBw26uzDsRMD2q1WgSKNSnpKS9Ug7bdQ8dS+p4sZHNH6iRPu6WK2jLIjspaMA== jose@^4.6.0: - version "4.8.1" - resolved "https://registry.npmjs.org/jose/-/jose-4.8.1.tgz#dc7c2660b115ba29b44880e588c5ac313c158247" - integrity sha512-+/hpTbRcCw9YC0TOfN1W47pej4a9lRmltdOVdRLz5FP5UvUq3CenhXjQK7u/8NdMIIShMXYAh9VLPhc7TjhvFw== + version "4.8.3" + resolved "https://registry.npmjs.org/jose/-/jose-4.8.3.tgz#5a754fb4aa5f2806608d083f438e6916b11087da" + integrity sha512-7rySkpW78d8LBp4YU70Wb7+OTgE3OwAALNVZxhoIhp4Kscp+p/fBkdpxGAMKxvCAMV4QfXBU9m6l9nX/vGwd2g== joycon@^3.0.1: version "3.1.0" From 14d5eca8d36e4377916d629e1091367f6bb04c5e Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 29 Jun 2022 10:46:32 +0200 Subject: [PATCH 068/101] chore: fixing typings Signed-off-by: blam --- packages/backend-common/src/database/DatabaseManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 503945e594..004e7d13cd 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -251,7 +251,7 @@ export class DatabaseManager { // include base connection if client type has not been overridden ...(overridden ? {} : baseConnection), ...connection, - }; + } as Partial; } /** From 3a23a90f552b1f5a61f8b85ece3652e97dc9b67b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 09:11:40 +0000 Subject: [PATCH 069/101] fix(deps): update dependency openid-client to v5.1.7 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d75769b88d..42b442ca1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19749,9 +19749,9 @@ openid-client@^4.1.1: oidc-token-hash "^5.0.1" openid-client@^5.1.3: - version "5.1.6" - resolved "https://registry.npmjs.org/openid-client/-/openid-client-5.1.6.tgz#e5eb2032ecfdcfc108660b5c525910a14e352f11" - integrity sha512-HTFaXWdUHvLFw4GaEMgC0jXYBgpjgzQQNHW1pZsSqJorSgrXzxJ+4u/LWCGaClDEse5HLjXRV+zU5Bn3OefiZw== + version "5.1.7" + resolved "https://registry.npmjs.org/openid-client/-/openid-client-5.1.7.tgz#deb16847c610075716be1ec679068b04db16f065" + integrity sha512-VNtf/q+fv2Jiqi0ViLVmN3gGMSHF+YUGW6baKA/naoPKkKw4JdvghaP/kXQ/bzRRDWk6VmpCYDcR934UdDs8ug== dependencies: jose "^4.1.4" lru-cache "^6.0.0" From da9a0ea31f4f519801418d9fa8e5e172f88752e4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 11:30:32 +0000 Subject: [PATCH 070/101] fix(deps): update dependency @maxim_mazurok/gapi.client.calendar to v3.0.20220624 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 336a93c5fd..990f5d8aed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4358,9 +4358,9 @@ react-is "^16.8.0 || ^17.0.0" "@maxim_mazurok/gapi.client.calendar@^3.0.20220408": - version "3.0.20220617" - resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220617.tgz#140bbfd2caa1770fedbdaec4182eb43684f2d876" - integrity sha512-Vek5Y655GUi4RmUs3cNLfo/KQeReEuvcViJ0KYHl28KmC2Pqmxb4V2YIUhsH7BGVyq84cTIT59qHSirB919Prw== + version "3.0.20220624" + resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220624.tgz#17817142e348ce811415dfb111299fbdac2450a0" + integrity sha512-5+x4A6l8GY+dojvXUBQGc0Y4JOm9lBY1YZfCAEEZPjBcXkx0vs/CenPnmAu/fwQgGSrQLipxFV+pS6RL3L28mg== dependencies: "@types/gapi.client" "*" From e117caf22864d26ff63d249d93050fc224968bf1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 11:31:38 +0000 Subject: [PATCH 071/101] chore(deps): update dependency cypress to v10.3.0 Signed-off-by: Renovate Bot --- cypress/yarn.lock | 6 +++--- yarn.lock | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cypress/yarn.lock b/cypress/yarn.lock index 1ccce4ea24..991d2d8562 100644 --- a/cypress/yarn.lock +++ b/cypress/yarn.lock @@ -304,9 +304,9 @@ cross-spawn@^7.0.0: which "^2.0.1" cypress@^10.0.0: - version "10.2.0" - resolved "https://registry.npmjs.org/cypress/-/cypress-10.2.0.tgz#ca078abfceb13be2a33cbba6e0e80ded770f542a" - integrity sha512-+i9lY5ENlfi2mJwsggzR+XASOIgMd7S/Gd3/13NCpv596n3YSplMAueBTIxNLcxDpTcIksp+9pM3UaDrJDpFqA== + version "10.3.0" + resolved "https://registry.npmjs.org/cypress/-/cypress-10.3.0.tgz#fae8d32f0822fcfb938e79c7c31ef344794336ae" + integrity sha512-txkQWKzvBVnWdCuKs5Xc08gjpO89W2Dom2wpZgT9zWZT5jXxqPIxqP/NC1YArtkpmp3fN5HW8aDjYBizHLUFvg== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" diff --git a/yarn.lock b/yarn.lock index 336a93c5fd..565947d185 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10822,9 +10822,9 @@ cypress-plugin-snapshots@^1.4.4: unidiff "1.0.2" cypress@^10.0.0: - version "10.2.0" - resolved "https://registry.npmjs.org/cypress/-/cypress-10.2.0.tgz#ca078abfceb13be2a33cbba6e0e80ded770f542a" - integrity sha512-+i9lY5ENlfi2mJwsggzR+XASOIgMd7S/Gd3/13NCpv596n3YSplMAueBTIxNLcxDpTcIksp+9pM3UaDrJDpFqA== + version "10.3.0" + resolved "https://registry.npmjs.org/cypress/-/cypress-10.3.0.tgz#fae8d32f0822fcfb938e79c7c31ef344794336ae" + integrity sha512-txkQWKzvBVnWdCuKs5Xc08gjpO89W2Dom2wpZgT9zWZT5jXxqPIxqP/NC1YArtkpmp3fN5HW8aDjYBizHLUFvg== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" From ec21ff386552d9d9ada70f37717bda1cb7c22ace Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 29 Jun 2022 13:53:05 +0200 Subject: [PATCH 072/101] chore: only need one changeset Signed-off-by: blam --- .changeset/techdocs-sixty-mugs-hug.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.changeset/techdocs-sixty-mugs-hug.md b/.changeset/techdocs-sixty-mugs-hug.md index 05c03a0b98..a1a9e815a1 100644 --- a/.changeset/techdocs-sixty-mugs-hug.md +++ b/.changeset/techdocs-sixty-mugs-hug.md @@ -1,6 +1,4 @@ --- -'@backstage/core-components': patch -'@backstage/plugin-catalog': patch '@backstage/plugin-techdocs': patch --- From ed45ee23fad91db57903ea167d600a495af2ff79 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 29 Jun 2022 14:21:57 +0200 Subject: [PATCH 073/101] chore: added some simple tests Signed-off-by: blam --- .../RepoUrlPickerRepoName.test.tsx | 76 +++++++++++++++++++ .../RepoUrlPicker/RepoUrlPickerRepoName.tsx | 2 +- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.test.tsx diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.test.tsx new file mode 100644 index 0000000000..4c392d646c --- /dev/null +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.test.tsx @@ -0,0 +1,76 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { RepoUrlPickerRepoName } from './RepoUrlPickerRepoName'; +import { render, fireEvent } from '@testing-library/react'; + +describe('RepoUrlPickerRepoName', () => { + it('should call onChange with the first allowed repo if there is none set already', async () => { + const onChange = jest.fn(); + + render( + , + ); + + expect(onChange).toHaveBeenCalledWith('foo'); + }); + + it('should render a dropdown of all the options', async () => { + const allowedRepos = ['foo', 'bar']; + + const onChange = jest.fn(); + + const { getByRole } = render( + , + ); + + const select = getByRole('combobox'); + await fireEvent.click(select); + + for (const option of allowedRepos) { + const element = getByRole('option', { name: option }); + expect(element).toBeVisible(); + } + }); + + it('should render a normal text area when no options are passed', async () => { + const onChange = jest.fn(); + + const { getByRole } = render( + , + ); + + const textArea = getByRole('textbox'); + + expect(textArea).toBeVisible(); + + fireEvent.change(textArea, { target: { value: 'foo' } }); + + expect(onChange).toHaveBeenCalledWith('foo'); + }); +}); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx index f2d68b08f3..1393b1902e 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPickerRepoName.tsx @@ -54,7 +54,7 @@ export const RepoUrlPickerRepoName = (props: { native label="Repositories Available" onChange={selected => - onChange(String(Array.isArray(selected) ? selected[0] : selected)) + String(Array.isArray(selected) ? selected[0] : selected) } disabled={allowedRepos.length === 1} selected={repoName} From ce7abf314bedcdb39b5cd859e2d9c75c78b7170e Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 29 Jun 2022 14:24:33 +0200 Subject: [PATCH 074/101] chore: updating the changeset Signed-off-by: blam --- .changeset/hot-rice-sin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/hot-rice-sin.md b/.changeset/hot-rice-sin.md index 5a7363bdc8..5a3d6fdb7a 100644 --- a/.changeset/hot-rice-sin.md +++ b/.changeset/hot-rice-sin.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-scaffolder': patch +'@backstage/plugin-scaffolder': minor --- -RepoUrlPicker: Add allowedRepos option and move repoName field to own component +Add `allowedRepos` `ui:option` to `RepoUrlPicker` component, and move `repoName` field to own component From 975d20c8c99c5198346504bcb3df251ce4f9273f Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 29 Jun 2022 14:55:00 +0200 Subject: [PATCH 075/101] chore: added another option to the repoUrlPicker for the api-rports Signed-off-by: blam --- plugins/scaffolder/api-report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 9ffca426ac..d2805b460e 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -193,6 +193,8 @@ export interface RepoUrlPickerUiOptions { // (undocumented) allowedOwners?: string[]; // (undocumented) + allowedRepos?: string[]; + // (undocumented) requestUserCredentials?: { secretsKey: string; additionalScopes?: { From 982f7814b13f31330c7337c1f693f5a075ea849c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 29 Jun 2022 15:01:16 +0200 Subject: [PATCH 076/101] Use the custom PR action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Johan Haals Signed-off-by: Fredrik Adelรถw --- .github/workflows/pr.yaml | 14 +++++++ .../workflows/sync_approve_renovate_pr.yaml | 37 ------------------- 2 files changed, 14 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/pr.yaml delete mode 100644 .github/workflows/sync_approve_renovate_pr.yaml diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml new file mode 100644 index 0000000000..0d4fd01ca5 --- /dev/null +++ b/.github/workflows/pr.yaml @@ -0,0 +1,14 @@ +name: PR +on: + pull_request_target: + +jobs: + generate-changeset: + runs-on: ubuntu-latest + + if: github.repository == 'backstage/backstage' + steps: + - name: PR sync + uses: backstage/actions/pr-sync@v0.1.6 + with: + github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} diff --git a/.github/workflows/sync_approve_renovate_pr.yaml b/.github/workflows/sync_approve_renovate_pr.yaml deleted file mode 100644 index 2d25bea223..0000000000 --- a/.github/workflows/sync_approve_renovate_pr.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: Approve renovate lock file changes -on: - pull_request_target: - paths: - - '.github/workflows/sync_renovate-changesets.yml' - - '**/yarn.lock' - -jobs: - generate-changeset: - runs-on: ubuntu-latest - if: github.actor == 'renovate[bot]' && github.repository == 'backstage/backstage' - steps: - - name: Approve - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - script: | - const owner = 'backstage'; - const repo = 'backstage'; - - const r = await github.rest.pulls.listFiles({ - owner, - repo, - pull_number: context.issue.number, - }); - - if (r.data.some((f) => f.filename.split('/').slice(-1)[0] !== 'yarn.lock')) { - console.log('skipping approval since some files are not yarn.lock'); - return; - } - - await github.rest.pulls.createReview({ - owner, - repo, - pull_number: context.issue.number, - event: 'APPROVE' - }) From 45f621e22fdf8a2160e0a6982a589dd4a21fdf76 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:08:10 +0000 Subject: [PATCH 077/101] fix(deps): update dependency @google-cloud/storage to v6.2.1 Signed-off-by: Renovate Bot --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3c55fd5053..62ce9848df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2305,10 +2305,10 @@ arrify "^2.0.0" extend "^3.0.2" -"@google-cloud/projectify@^2.0.0": - version "2.0.1" - resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-2.0.1.tgz#13350ee609346435c795bbfe133a08dfeab78d65" - integrity sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ== +"@google-cloud/projectify@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-3.0.0.tgz#302b25f55f674854dce65c2532d98919b118a408" + integrity sha512-HRkZsNmjScY6Li8/kb70wjGlDDyLkVk3KvoEo9uIoxSjYLJasGiCch9+PqRVDOCGUFvEIqyogl+BeqILL4OJHA== "@google-cloud/promisify@^3.0.0": version "3.0.0" @@ -2316,12 +2316,12 @@ integrity sha512-91ArYvRgXWb73YvEOBMmOcJc0bDRs5yiVHnqkwoG0f3nm7nZuipllz6e7BvFESBvjkDTBC0zMD8QxedUwNLc1A== "@google-cloud/storage@^6.0.0": - version "6.1.0" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.1.0.tgz#f882a969c5637ff445764d7b172f68fd0d9e63f8" - integrity sha512-zqZwzpRWCJuPne7x9Vc2H79zANl0uh9bNPGis0xAuC88ZEvBXfQqYCAVyiL1YIxi7rf51l8wy9vBr1pONMfxxA== + version "6.2.1" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.2.1.tgz#6f02e44d907e2fcb2b80aeea5cc6e5575dfeefcc" + integrity sha512-obLGOFCp25rpRn4CZvZqQkSHhY+dBrK7IjDdFpF5gOXVxE40ilr287uikiGPjflVFbgNwO2qhioJNqMzxoqHZg== dependencies: "@google-cloud/paginator" "^3.0.7" - "@google-cloud/projectify" "^2.0.0" + "@google-cloud/projectify" "^3.0.0" "@google-cloud/promisify" "^3.0.0" abort-controller "^3.0.0" arrify "^2.0.0" From 4ef64446717b824dc63a182fe8fd46fc4dbfeacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 29 Jun 2022 15:19:22 +0200 Subject: [PATCH 078/101] Use the custom issue action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Co-authored-by: Johan Haals Signed-off-by: Fredrik Adelรถw --- .github/workflows/cron.yml | 2 +- .github/workflows/issue.yaml | 13 +++++++++ .github/workflows/pr.yaml | 4 +-- .github/workflows/sync_issue-labels.yml | 38 ------------------------- 4 files changed, 16 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/issue.yaml delete mode 100644 .github/workflows/sync_issue-labels.yml diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 52a68a7d90..4d1ba7fc0e 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -8,4 +8,4 @@ jobs: cron: runs-on: ubuntu-latest steps: - - uses: backstage/actions/cron@v0.1.4 + - uses: backstage/actions/cron@v0.1.8 diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml new file mode 100644 index 0000000000..972f90795c --- /dev/null +++ b/.github/workflows/issue.yaml @@ -0,0 +1,13 @@ +name: Issue +on: + issues: + types: [opened] + +jobs: + sync: + runs-on: ubuntu-latest + + if: github.repository == 'backstage/backstage' + steps: + - name: Issue sync + uses: backstage/actions/issue-sync@v0.1.8 diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 0d4fd01ca5..01254a53a8 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -3,12 +3,12 @@ on: pull_request_target: jobs: - generate-changeset: + sync: runs-on: ubuntu-latest if: github.repository == 'backstage/backstage' steps: - name: PR sync - uses: backstage/actions/pr-sync@v0.1.6 + uses: backstage/actions/pr-sync@v0.1.8 with: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} diff --git a/.github/workflows/sync_issue-labels.yml b/.github/workflows/sync_issue-labels.yml deleted file mode 100644 index 91b5f79f5d..0000000000 --- a/.github/workflows/sync_issue-labels.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Sync Issue Labels -on: - issues: - types: [opened] -jobs: - label-issue: - runs-on: ubuntu-latest - if: github.repository == 'backstage/backstage' - steps: - - name: View context attributes - uses: actions/github-script@v6 - with: - script: | - const keywords = { - 'techdocs|tech-docs|tech docs': 'docs-like-code', - 'search': 'search', - 'catalog': 'catalog', - 'scaffolder': 'scaffolder', - }; - - const labels = Object.entries(keywords) - .map(([regexp, label]) => { - if (new RegExp(regexp, 'gi').test(context.payload.issue.title)) { - return label; - } - }) - .filter(Boolean); - - if(!labels.length) { - return; - } - - github.rest.issues.addLabels({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - labels - }); From 2dae0427cfa37a7bbb22fd2604fa789091bd4ac5 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 29 Jun 2022 15:37:40 +0200 Subject: [PATCH 079/101] chore: removing the failing tests that have been replaced with the other functionality Signed-off-by: blam --- .../RepoUrlPicker/AzureRepoPicker.test.tsx | 19 ++------------- .../BitbucketRepoPicker.test.tsx | 23 ++----------------- .../RepoUrlPicker/GerritRepoPicker.test.tsx | 19 --------------- .../RepoUrlPicker/GithubRepoPicker.test.tsx | 20 ---------------- .../RepoUrlPicker/GitlabRepoPicker.test.tsx | 20 ---------------- 5 files changed, 4 insertions(+), 97 deletions(-) diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx index ffcf2920c1..eb77240d33 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/AzureRepoPicker.test.tsx @@ -19,14 +19,14 @@ import { AzureRepoPicker } from './AzureRepoPicker'; import { render, fireEvent } from '@testing-library/react'; describe('AzureRepoPicker', () => { - it('renders the three input fields', async () => { + it('renders the two input fields', async () => { const { getAllByRole } = render( , ); const allInputs = getAllByRole('textbox'); - expect(allInputs).toHaveLength(3); + expect(allInputs).toHaveLength(2); }); describe('org field', () => { @@ -58,19 +58,4 @@ describe('AzureRepoPicker', () => { expect(onChange).toHaveBeenCalledWith({ owner: 'owner' }); }); }); - - describe('repoName field', () => { - it('calls onChange when the repoName changes', () => { - const onChange = jest.fn(); - const { getAllByRole } = render( - , - ); - - const repoNameInput = getAllByRole('textbox')[2]; - - fireEvent.change(repoNameInput, { target: { value: 'repoName' } }); - - expect(onChange).toHaveBeenCalledWith({ repoName: 'repoName' }); - }); - }); }); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx index 17fe656006..0ced19a596 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/BitbucketRepoPicker.test.tsx @@ -26,7 +26,7 @@ describe('BitbucketRepoPicker', () => { , ); - expect(getAllByRole('textbox')).toHaveLength(3); + expect(getAllByRole('textbox')).toHaveLength(2); expect(getAllByRole('textbox')[0]).toHaveValue('lolsWorkspace'); }); @@ -39,7 +39,7 @@ describe('BitbucketRepoPicker', () => { , ); - expect(getAllByRole('textbox')).toHaveLength(2); + expect(getAllByRole('textbox')).toHaveLength(1); }); describe('workspace field', () => { it('calls onChange when the workspace changes', () => { @@ -78,23 +78,4 @@ describe('BitbucketRepoPicker', () => { expect(onChange).toHaveBeenCalledWith({ project: 'test-project' }); }); }); - - describe('repoName field', () => { - it('calls onChange when the repoName changes', () => { - const onChange = jest.fn(); - const { getAllByRole } = render( - , - ); - - const repoNameInput = getAllByRole('textbox')[2]; - - fireEvent.change(repoNameInput, { target: { value: 'test-repo' } }); - - expect(onChange).toHaveBeenCalledWith({ repoName: 'test-repo' }); - }); - }); }); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx index edc6732588..66f0a234be 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GerritRepoPicker.test.tsx @@ -56,23 +56,4 @@ describe('BitbucketRepoPicker', () => { expect(onChange).toHaveBeenCalledWith({ workspace: 'test-parent' }); }); }); - - describe('repoName field', () => { - it('calls onChange when the repoName changes', () => { - const onChange = jest.fn(); - const { getAllByRole } = render( - , - ); - - const repoNameInput = getAllByRole('textbox')[2]; - - fireEvent.change(repoNameInput, { target: { value: 'test-repo' } }); - - expect(onChange).toHaveBeenCalledWith({ repoName: 'test-repo' }); - }); - }); }); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx index 2c80c40c22..63bc5b8013 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GithubRepoPicker.test.tsx @@ -84,24 +84,4 @@ describe('GithubRepoPicker', () => { expect(onChange).toHaveBeenCalledWith({ owner: 'my-mock-owner' }); }); }); - - describe('repo name', () => { - it('should render free text field for input of repo name', () => { - const onChange = jest.fn(); - const { getAllByRole } = render( - , - ); - - const repoNameField = getAllByRole('textbox')[1]; - fireEvent.change(repoNameField, { - target: { value: 'my-mock-repo-name' }, - }); - - expect(onChange).toHaveBeenCalledWith({ repoName: 'my-mock-repo-name' }); - }); - }); }); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx index 49a91c116e..ad5c7f765e 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/GitlabRepoPicker.test.tsx @@ -84,24 +84,4 @@ describe('GitlabRepoPicker', () => { expect(onChange).toHaveBeenCalledWith({ owner: 'my-mock-owner' }); }); }); - - describe('repo name', () => { - it('should render free text field for input of repo name', () => { - const onChange = jest.fn(); - const { getAllByRole } = render( - , - ); - - const repoNameField = getAllByRole('textbox')[1]; - fireEvent.change(repoNameField, { - target: { value: 'my-mock-repo-name' }, - }); - - expect(onChange).toHaveBeenCalledWith({ repoName: 'my-mock-repo-name' }); - }); - }); }); From 1e909bb45c21aff5f76653822df6e6210b5fb398 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 14:16:42 +0000 Subject: [PATCH 080/101] fix(deps): update dependency aws-sdk to v2.1164.0 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 62ce9848df..8274265d7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8335,9 +8335,9 @@ aws-sdk-mock@^5.2.1: traverse "^0.6.6" aws-sdk@^2.1122.0, aws-sdk@^2.840.0, aws-sdk@^2.948.0: - version "2.1159.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1159.0.tgz#64d585ac608d58e6ff62678be71a4c4ca61ca5dc" - integrity sha512-zm3k/ufwZnkWc6M+HDz00CWuILot4L9kJ5VJsuDS9fwsT9To6k91Y1njCtIV4tcgcXvUru0Sbm4D0w5bc2847A== + version "2.1164.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1164.0.tgz#050ce644ed9993582bd02151bf3ac9d9ebc143f5" + integrity sha512-q/M9E68WabF22G8d8lFgo3NH+9RooYswSY9VG6zqN16C19RRm2sGThp8Sxtz/WUK98BAsxSnkLW1ksmy3BsP7Q== dependencies: buffer "4.9.2" events "1.1.1" From 2454cf3188c30539aac4babd6553b97eac3d1e76 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 14:17:32 +0000 Subject: [PATCH 081/101] fix(deps): update dependency cronstrue to v2.11.0 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 62ce9848df..8ea15abce5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10478,9 +10478,9 @@ cron@^2.0.0: luxon "^1.23.x" cronstrue@^2.2.0: - version "2.10.0" - resolved "https://registry.npmjs.org/cronstrue/-/cronstrue-2.10.0.tgz#9b57e9acc18eb44ebe9be5dc993753fd7d6d8e56" - integrity sha512-WCCaKuuzjZJl/xTaJiK2KB2lhHqAz+cTAHgSiZQc/pNnF2XUSZX0FBfxAG0qa9CogToNoQw7pEBJExc77QnFBQ== + version "2.11.0" + resolved "https://registry.npmjs.org/cronstrue/-/cronstrue-2.11.0.tgz#18ff1b95a836b9b4e06854f796db2dc8fa98ce41" + integrity sha512-iIBCSis5yqtFYWtJAmNOiwDveFWWIn+8uV5UYuPHYu/Aeu5CSSJepSbaHMyfc+pPFgnsCcGzfPQEo7LSGmWbTg== cross-env@^7.0.0: version "7.0.3" From 7ecaf0067de5daa1fa2382331e5175a633d0e100 Mon Sep 17 00:00:00 2001 From: Ke Ma Date: Wed, 29 Jun 2022 16:42:58 +0200 Subject: [PATCH 082/101] fix header style so that EntityContextMenu is in correct shape Signed-off-by: codermango --- packages/core-components/src/layout/Header/Header.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index bb4d19b949..a32d116b56 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -63,6 +63,7 @@ const useStyles = makeStyles( }, rightItemsBox: { width: 'auto', + alignItems: 'center', }, title: { color: theme.palette.bursts.fontColor, From b4b711bcc2c1e93b5851799ab596bababbf5f856 Mon Sep 17 00:00:00 2001 From: codermango Date: Wed, 29 Jun 2022 16:51:26 +0200 Subject: [PATCH 083/101] add changeset Signed-off-by: codermango --- .changeset/five-fireants-run.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/five-fireants-run.md diff --git a/.changeset/five-fireants-run.md b/.changeset/five-fireants-run.md new file mode 100644 index 0000000000..a18ba2b8d2 --- /dev/null +++ b/.changeset/five-fireants-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': minor +--- + +Fix the EntityLayout header style so that EntityContextMenu button can display in correct shape when user hover on it From bdc77d8cb61e1f578decef2d01d3cc0dd9476b5a Mon Sep 17 00:00:00 2001 From: codermango Date: Wed, 29 Jun 2022 17:18:16 +0200 Subject: [PATCH 084/101] fix changeset Signed-off-by: codermango --- .changeset/five-fireants-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/five-fireants-run.md b/.changeset/five-fireants-run.md index a18ba2b8d2..589d46cc81 100644 --- a/.changeset/five-fireants-run.md +++ b/.changeset/five-fireants-run.md @@ -1,5 +1,5 @@ --- -'@backstage/core-components': minor +'@backstage/core-components': patch --- Fix the EntityLayout header style so that EntityContextMenu button can display in correct shape when user hover on it From bb5f171472e0c948526fe13910c65b1635b50a1e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 16:19:23 +0000 Subject: [PATCH 085/101] chore(deps): update dependency @graphql-codegen/cli to v2.6.3 Signed-off-by: Renovate Bot --- yarn.lock | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cb38cc83b..ccb89c56bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2364,9 +2364,9 @@ meros "^1.1.4" "@graphql-codegen/cli@^2.3.1": - version "2.6.2" - resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-2.6.2.tgz#a9aa4656141ee0998cae8c7ad7d0bf9ca8e0c9ae" - integrity sha512-UO75msoVgvLEvfjCezM09cQQqp32+mR8Ma1ACsBpr7nroFvHbgcu2ulx1cMovg4sxDBCsvd9Eq/xOOMpARUxtw== + version "2.6.3" + resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-2.6.3.tgz#80d6f0324bcf9a375b9a1d5e998fcd62b3bace66" + integrity sha512-gxtKbe6LlBhGqDISZldCMcGaV1rSTS4D836clSR52kBY7UBhZZcDOcNZM0zDJDFGpjHtwuL9p0Kb4w9HRXYfew== dependencies: "@graphql-codegen/core" "2.5.1" "@graphql-codegen/plugin-helpers" "^2.4.1" @@ -2387,24 +2387,18 @@ common-tags "^1.8.0" cosmiconfig "^7.0.0" debounce "^1.2.0" - dependency-graph "^0.11.0" detect-indent "^6.0.0" - glob "^7.1.6" - globby "^11.0.4" graphql-config "^4.1.0" inquirer "^8.0.0" is-glob "^4.0.1" json-to-pretty-yaml "^1.2.2" - latest-version "5.1.0" + latest-version "^6.0.0" listr "^0.14.3" listr-update-renderer "^0.5.0" log-symbols "^4.0.0" - minimatch "^4.0.0" mkdirp "^1.0.4" string-env-interpolation "^1.0.1" ts-log "^2.2.3" - tslib "~2.3.0" - valid-url "^1.0.9" wrap-ansi "^7.0.0" yaml "^1.10.0" yargs "^17.0.0" @@ -14075,7 +14069,7 @@ got@11.8.3: p-cancelable "^2.0.0" responselike "^2.0.0" -got@^11.8.0: +got@^11.8.0, got@^11.8.2: version "11.8.5" resolved "https://registry.npmjs.org/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== @@ -17051,13 +17045,20 @@ language-tags@^1.0.5: dependencies: language-subtag-registry "~0.3.2" -latest-version@5.1.0, latest-version@^5.1.0: +latest-version@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== dependencies: package-json "^6.3.0" +latest-version@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/latest-version/-/latest-version-6.0.0.tgz#469fb95f1d588b38436c1ec8fec947b99ed268c7" + integrity sha512-zfTuGx4PwpoSJ1mABs58AkM6qMzu49LZ7LT5JHprKvpGpQ+cYtfSibi3tLLrH4z7UylYU42rfBdwN8YgqbTljA== + dependencies: + package-json "^7.0.0" + lazy-ass@1.6.0, lazy-ass@^1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" @@ -18681,13 +18682,6 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^4.0.0: - version "4.2.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== - dependencies: - brace-expansion "^1.1.7" - minimist-options@4.1.0, minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -20012,6 +20006,16 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" +package-json@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/package-json/-/package-json-7.0.0.tgz#1355416e50a5c1b8f1a6f471197a3650d21186bf" + integrity sha512-CHJqc94AA8YfSLHGQT3DbvSIuE12NLFekpM4n7LRrAd3dOJtA911+4xe9q6nC3/jcKraq7nNS9VxgtT0KC+diA== + dependencies: + got "^11.8.2" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^7.3.5" + packet-reader@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" @@ -25678,11 +25682,6 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -valid-url@^1.0.9: - version "1.0.9" - resolved "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" - integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= - validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" From 396111dd05cd7de573c2b37a434f5d46d8b6779c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 29 Jun 2022 16:25:18 +0200 Subject: [PATCH 086/101] workflows: use latest version of cron action with app auth Signed-off-by: Patrik Oldsberg --- .github/workflows/cron.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 4d1ba7fc0e..e2144a56c1 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -8,4 +8,8 @@ jobs: cron: runs-on: ubuntu-latest steps: - - uses: backstage/actions/cron@v0.1.8 + - uses: backstage/actions/cron@v0.1.12 + with: + app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} + private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} + installation-id: ${{ secrets.BACKSTAGE_GOALIE_INSTALLATION_ID }} From 48df40d383592cff0b8982e624656e7edcafba66 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 18:55:35 +0000 Subject: [PATCH 087/101] fix(deps): update dependency aws-sdk to v2.1165.0 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cb38cc83b..bd30071ed1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8335,9 +8335,9 @@ aws-sdk-mock@^5.2.1: traverse "^0.6.6" aws-sdk@^2.1122.0, aws-sdk@^2.840.0, aws-sdk@^2.948.0: - version "2.1164.0" - resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1164.0.tgz#050ce644ed9993582bd02151bf3ac9d9ebc143f5" - integrity sha512-q/M9E68WabF22G8d8lFgo3NH+9RooYswSY9VG6zqN16C19RRm2sGThp8Sxtz/WUK98BAsxSnkLW1ksmy3BsP7Q== + version "2.1165.0" + resolved "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1165.0.tgz#4da669d1e9020344cef75d961882f52a7931a379" + integrity sha512-2oVkSuXsLeErt+H4M2OGIz4p1LPS+QRfY2WnW4QKMndASOcvHKZTfzuY8jmc9ZnDGyguiGdT3idYU8KpNg0sGw== dependencies: buffer "4.9.2" events "1.1.1" From 9b1120cf9073fc7ee19dedd89e6eb6f62a05b761 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 18:57:06 +0000 Subject: [PATCH 088/101] fix(deps): update dependency eslint-plugin-jsx-a11y to v6.6.0 Signed-off-by: Renovate Bot --- yarn.lock | 56 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cb38cc83b..d56540762a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1457,7 +1457,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.17.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz#a5f3328dc41ff39d803f311cfe17703418cf9825" integrity sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA== @@ -1471,6 +1471,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.18.3": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580" + integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" @@ -8118,7 +8125,7 @@ array-ify@^1.0.0: resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-includes@^3.1.2, array-includes@^3.1.3, array-includes@^3.1.4: +array-includes@^3.1.2, array-includes@^3.1.4: version "3.1.4" resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== @@ -8359,10 +8366,10 @@ aws4@^1.11.0, aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axe-core@^4.3.5: - version "4.3.5" - resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5" - integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA== +axe-core@^4.4.2: + version "4.4.2" + resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.4.2.tgz#dcf7fb6dea866166c3eab33d68208afe4d5f670c" + integrity sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA== axios-cached-dns-resolve@0.5.2: version "0.5.2" @@ -11089,10 +11096,10 @@ dagre@^0.8.5: graphlib "^2.1.8" lodash "^4.17.15" -damerau-levenshtein@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d" - integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw== +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== dargs@^7.0.0: version "7.0.0" @@ -12308,22 +12315,23 @@ eslint-plugin-jest@^26.1.2: "@typescript-eslint/utils" "^5.10.0" eslint-plugin-jsx-a11y@^6.5.1: - version "6.5.1" - resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" - integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g== + version "6.6.0" + resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.0.tgz#2c5ac12e013eb98337b9aa261c3b355275cc6415" + integrity sha512-kTeLuIzpNhXL2CwLlc8AHI0aFRwWHcg483yepO9VQiHzM9bZwJdzTkzBszbuPrbgGmq2rlX/FaT2fJQsjUSHsw== dependencies: - "@babel/runtime" "^7.16.3" + "@babel/runtime" "^7.18.3" aria-query "^4.2.2" - array-includes "^3.1.4" + array-includes "^3.1.5" ast-types-flow "^0.0.7" - axe-core "^4.3.5" + axe-core "^4.4.2" axobject-query "^2.2.0" - damerau-levenshtein "^1.0.7" + damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" has "^1.0.3" - jsx-ast-utils "^3.2.1" + jsx-ast-utils "^3.3.1" language-tags "^1.0.5" - minimatch "^3.0.4" + minimatch "^3.1.2" + semver "^6.3.0" eslint-plugin-monorepo@^0.3.2: version "0.3.2" @@ -16885,12 +16893,12 @@ jss@~10.8.2: array-includes "^3.1.2" object.assign "^4.1.2" -jsx-ast-utils@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" - integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA== +jsx-ast-utils@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.1.tgz#a3e0f1cb7e230954eab4dcbce9f6288a78f8ba44" + integrity sha512-pxrjmNpeRw5wwVeWyEAk7QJu2GnBO3uzPFmHCKJJFPKK2Cy0cWL23krGtLdnMmbIi6/FjlrQpPyfQI19ByPOhQ== dependencies: - array-includes "^3.1.3" + array-includes "^3.1.5" object.assign "^4.1.2" just-diff-apply@^4.0.1: From a0258a9587c7c3fcf724236a16da380ade5787cf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 21:55:58 +0000 Subject: [PATCH 089/101] chore(deps): update dependency @types/node to v16.11.42 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cb38cc83b..450ec3b541 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6653,9 +6653,9 @@ integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== "@types/node@^16.0.0", "@types/node@^16.11.26", "@types/node@^16.9.2": - version "16.11.41" - resolved "https://registry.npmjs.org/@types/node/-/node-16.11.41.tgz#88eb485b1bfdb4c224d878b7832239536aa2f813" - integrity sha512-mqoYK2TnVjdkGk8qXAVGc/x9nSaTpSrFaGFm43BUH3IdoBV0nta6hYaGmdOvIMlbHJbUEVen3gvwpwovAZKNdQ== + version "16.11.42" + resolved "https://registry.npmjs.org/@types/node/-/node-16.11.42.tgz#d2a75c58e9b0902b82dc54bd4c13f8ef12bd1020" + integrity sha512-iwLrPOopPy6V3E+1yHTpJea3bdsNso0b0utLOJJwaa/PLzqBt3GZl3stMcakc/gr89SfcNk2ki3z7Gvue9hYGQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" From 26a38b60b3b88a92d3064e26a6e1c897a1d49f33 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Jun 2022 21:56:42 +0000 Subject: [PATCH 090/101] fix(deps): update dependency @google-cloud/storage to v6.2.2 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cb38cc83b..aa881dc838 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2316,9 +2316,9 @@ integrity sha512-91ArYvRgXWb73YvEOBMmOcJc0bDRs5yiVHnqkwoG0f3nm7nZuipllz6e7BvFESBvjkDTBC0zMD8QxedUwNLc1A== "@google-cloud/storage@^6.0.0": - version "6.2.1" - resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.2.1.tgz#6f02e44d907e2fcb2b80aeea5cc6e5575dfeefcc" - integrity sha512-obLGOFCp25rpRn4CZvZqQkSHhY+dBrK7IjDdFpF5gOXVxE40ilr287uikiGPjflVFbgNwO2qhioJNqMzxoqHZg== + version "6.2.2" + resolved "https://registry.npmjs.org/@google-cloud/storage/-/storage-6.2.2.tgz#1fd3ee85e5fea55a9696c98769d097b28ff4d775" + integrity sha512-KhAOxmGfmELKKn6cdvgGfAi/YBLi19hI1jX3QI7xQmbeajSFMgUKrIPbbyfMIxQPOEQ9vG0MQX1uganlA/HTRA== dependencies: "@google-cloud/paginator" "^3.0.7" "@google-cloud/projectify" "^3.0.0" From 34f4b938e304584654cc98c348bfd30b737d69f6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 01:08:24 +0000 Subject: [PATCH 091/101] fix(deps): update dependency npm-packlist to v5.1.1 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3cb38cc83b..7b7bb1ba45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19355,9 +19355,9 @@ npm-packlist@^3.0.0: npm-normalize-package-bin "^1.0.1" npm-packlist@^5.0.0, npm-packlist@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.0.tgz#f3fd52903a021009913a133732022132eb355ce7" - integrity sha512-a04sqF6FbkyOAFA19AA0e94gS7Et5T2/IMj3VOT9nOF2RaRdVPQ1Q17Fb/HaDRFs+gbC7HOmhVZ29adpWgmDZg== + version "5.1.1" + resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.1.tgz#79bcaf22a26b6c30aa4dd66b976d69cc286800e0" + integrity sha512-UfpSvQ5YKwctmodvPPkK6Fwk603aoVsf8AEbmVKAEECrfvL8SSe1A2YIwrJ6xmTHAITKPwwZsWo7WwEbNk0kxw== dependencies: glob "^8.0.1" ignore-walk "^5.0.1" From 4ae70086df3700d24e9e8028e0b22a9946a75b72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 07:36:33 +0000 Subject: [PATCH 092/101] chore(deps): update dependency esbuild to v0.14.48 Signed-off-by: Renovate Bot --- yarn.lock | 206 +++++++++++++++++++++++++++--------------------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/yarn.lock b/yarn.lock index 450ec3b541..5584ec9b1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12048,75 +12048,75 @@ es6-error@^4.1.1: resolved "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild-android-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.47.tgz#ef95b42c67bcf4268c869153fa3ad1466c4cea6b" - integrity sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g== +esbuild-android-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz#7e6394a0e517f738641385aaf553c7e4fb6d1ae3" + integrity sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg== -esbuild-android-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.47.tgz#4ebd7ce9fb250b4695faa3ee46fd3b0754ecd9e6" - integrity sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ== +esbuild-android-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz#6877566be0f82dd5a43030c0007d06ece7f7c02f" + integrity sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g== -esbuild-darwin-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.47.tgz#e0da6c244f497192f951807f003f6a423ed23188" - integrity sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA== +esbuild-darwin-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz#ea3caddb707d88f844b1aa1dea5ff3b0a71ef1fd" + integrity sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA== -esbuild-darwin-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.47.tgz#cd40fd49a672fca581ed202834239dfe540a9028" - integrity sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw== +esbuild-darwin-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz#4e5eaab54df66cc319b76a2ac0e8af4e6f0d9c2f" + integrity sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA== -esbuild-freebsd-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.47.tgz#8da6a14c095b29c01fc8087a16cb7906debc2d67" - integrity sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ== +esbuild-freebsd-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz#47b5abc7426eae66861490ffbb380acc67af5b15" + integrity sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA== -esbuild-freebsd-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.47.tgz#ad31f9c92817ff8f33fd253af7ab5122dc1b83f6" - integrity sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ== +esbuild-freebsd-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz#e8c54c8637cd44feed967ea12338b0a4da3a7b11" + integrity sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw== -esbuild-linux-32@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.47.tgz#de085e4db2e692ea30c71208ccc23fdcf5196c58" - integrity sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw== +esbuild-linux-32@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz#229cf3246de2b7937c3ac13fac622d4d7a1344c5" + integrity sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ== -esbuild-linux-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.47.tgz#2a9321bbccb01f01b04cebfcfccbabeba3658ba1" - integrity sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw== +esbuild-linux-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz#7c0e7226c02c42aacc5656c36977493dc1e96c4f" + integrity sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug== -esbuild-linux-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.47.tgz#b9da7b6fc4b0ca7a13363a0c5b7bb927e4bc535a" - integrity sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw== +esbuild-linux-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz#0af1eda474b5c6cc0cace8235b74d0cb8fcf57a7" + integrity sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw== -esbuild-linux-arm@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.47.tgz#56fec2a09b9561c337059d4af53625142aded853" - integrity sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA== +esbuild-linux-arm@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz#de4d1fa6b77cdcd00e2bb43dd0801e4680f0ab52" + integrity sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw== -esbuild-linux-mips64le@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.47.tgz#9db21561f8f22ed79ef2aedb7bbef082b46cf823" - integrity sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg== +esbuild-linux-mips64le@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz#822c1778495f7868e990d4da47ad7281df28fd15" + integrity sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA== -esbuild-linux-ppc64le@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.47.tgz#dc3a3da321222b11e96e50efafec9d2de408198b" - integrity sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w== +esbuild-linux-ppc64le@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz#55de0a9ec4a48fedfe82a63e083164d001709447" + integrity sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ== -esbuild-linux-riscv64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.47.tgz#9bd6dcd3dca6c0357084ecd06e1d2d4bf105335f" - integrity sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g== +esbuild-linux-riscv64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz#cd2b7381880b2f4b21a5a598fb673492120f18a5" + integrity sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA== -esbuild-linux-s390x@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.47.tgz#a458af939b52f2cd32fc561410d441a51f69d41f" - integrity sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw== +esbuild-linux-s390x@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz#4b319eca2a5c64637fc7397ffbd9671719cdb6bf" + integrity sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g== esbuild-loader@^2.18.0: version "2.19.0" @@ -12130,61 +12130,61 @@ esbuild-loader@^2.18.0: tapable "^2.2.0" webpack-sources "^2.2.0" -esbuild-netbsd-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.47.tgz#6388e785d7e7e4420cb01348d7483ab511b16aa8" - integrity sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ== +esbuild-netbsd-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz#c27cde8b5cb55dcc227943a18ab078fb98d0adbf" + integrity sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw== -esbuild-openbsd-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.47.tgz#309af806db561aa886c445344d1aacab850dbdc5" - integrity sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw== +esbuild-openbsd-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz#af5ab2d1cb41f09064bba9465fc8bf1309150df1" + integrity sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA== -esbuild-sunos-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.47.tgz#3f19612dcdb89ba6c65283a7ff6e16f8afbf8aaa" - integrity sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ== +esbuild-sunos-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz#db3ae20526055cf6fd5c4582676233814603ac54" + integrity sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA== -esbuild-windows-32@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.47.tgz#a92d279c8458d5dc319abcfeb30aa49e8f2e6f7f" - integrity sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ== +esbuild-windows-32@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz#021ffceb0a3f83078262870da88a912293c57475" + integrity sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg== -esbuild-windows-64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.47.tgz#2564c3fcf0c23d701edb71af8c52d3be4cec5f8a" - integrity sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ== +esbuild-windows-64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz#a4d3407b580f9faac51f61eec095fa985fb3fee4" + integrity sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA== -esbuild-windows-arm64@0.14.47: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.47.tgz#86d9db1a22d83360f726ac5fba41c2f625db6878" - integrity sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ== +esbuild-windows-arm64@0.14.48: + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz#762c0562127d8b09bfb70a3c816460742dd82880" + integrity sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g== esbuild@^0.14.1, esbuild@^0.14.10, esbuild@^0.14.39: - version "0.14.47" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.47.tgz#0d6415f6bd8eb9e73a58f7f9ae04c5276cda0e4d" - integrity sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA== + version "0.14.48" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz#da5d8d25cd2d940c45ea0cfecdca727f7aee2b85" + integrity sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA== optionalDependencies: - esbuild-android-64 "0.14.47" - esbuild-android-arm64 "0.14.47" - esbuild-darwin-64 "0.14.47" - esbuild-darwin-arm64 "0.14.47" - esbuild-freebsd-64 "0.14.47" - esbuild-freebsd-arm64 "0.14.47" - esbuild-linux-32 "0.14.47" - esbuild-linux-64 "0.14.47" - esbuild-linux-arm "0.14.47" - esbuild-linux-arm64 "0.14.47" - esbuild-linux-mips64le "0.14.47" - esbuild-linux-ppc64le "0.14.47" - esbuild-linux-riscv64 "0.14.47" - esbuild-linux-s390x "0.14.47" - esbuild-netbsd-64 "0.14.47" - esbuild-openbsd-64 "0.14.47" - esbuild-sunos-64 "0.14.47" - esbuild-windows-32 "0.14.47" - esbuild-windows-64 "0.14.47" - esbuild-windows-arm64 "0.14.47" + esbuild-android-64 "0.14.48" + esbuild-android-arm64 "0.14.48" + esbuild-darwin-64 "0.14.48" + esbuild-darwin-arm64 "0.14.48" + esbuild-freebsd-64 "0.14.48" + esbuild-freebsd-arm64 "0.14.48" + esbuild-linux-32 "0.14.48" + esbuild-linux-64 "0.14.48" + esbuild-linux-arm "0.14.48" + esbuild-linux-arm64 "0.14.48" + esbuild-linux-mips64le "0.14.48" + esbuild-linux-ppc64le "0.14.48" + esbuild-linux-riscv64 "0.14.48" + esbuild-linux-s390x "0.14.48" + esbuild-netbsd-64 "0.14.48" + esbuild-openbsd-64 "0.14.48" + esbuild-sunos-64 "0.14.48" + esbuild-windows-32 "0.14.48" + esbuild-windows-64 "0.14.48" + esbuild-windows-arm64 "0.14.48" escalade@^3.1.1: version "3.1.1" From ddf30fbd8acc1dee9b5473fd41079ad46be89d3a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 07:50:46 +0000 Subject: [PATCH 093/101] fix(deps): update dependency @uiw/react-codemirror to v4.9.5 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1e60c65663..6e03b9eade 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7402,9 +7402,9 @@ eslint-visitor-keys "^3.0.0" "@uiw/react-codemirror@^4.9.3": - version "4.9.4" - resolved "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.9.4.tgz#4643201f279fed103f2cf15df9431931fe4e9f15" - integrity sha512-IsC5xDevpIeLMzHQQwT2W40gFFIdKeT1T0DHjzzai+s5SIrMlGe3QSHWeC1wSO7FtfNxFpFlTYMGJm5JwUviMA== + version "4.9.5" + resolved "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.9.5.tgz#0f66c09dfc355baef5a7020f206da8db90bae223" + integrity sha512-KHgP/PII9Gv4iEUzbdO95qpSSPy27iSHzxQ01mBPWC4UvzuA0eQY2h64gzi/ld68esbKMGYoevgOBPCRJwNN1A== dependencies: "@babel/runtime" ">=7.11.0" "@codemirror/theme-one-dark" "^6.0.0" From e6ed8153ccc4414a29abddf36f041ae3b004310f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 29 Jun 2022 21:11:19 +0200 Subject: [PATCH 094/101] workflows: switch to common composite action for yarn install Signed-off-by: Patrik Oldsberg --- .github/workflows/ci.yml | 35 ++------------ .github/workflows/deploy_nightly.yml | 24 ++-------- .github/workflows/deploy_packages.yml | 48 +++---------------- .github/workflows/sync_code-formatting.yml | 24 ++-------- .github/workflows/sync_snyk-github-issues.yml | 24 ++-------- .github/workflows/verify_e2e-linux.yml | 26 ++-------- .github/workflows/verify_storybook.yml | 34 ++++--------- .github/workflows/verify_windows.yml | 14 +----- 8 files changed, 33 insertions(+), 196 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0718c8a8b9..c9fc151fb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,45 +64,16 @@ jobs: - name: fetch branch master run: git fetch origin master - # Beginning of yarn setup, keep in sync between all workflows. - # TODO(Rugvip): move this to composite action once all features we use are supported - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - # Cache every node_modules folder inside the monorepo - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - # We use both yarn.lock and package.json as cache keys to ensure that - # changes to local monorepo packages bust the cache. - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - # If we get a cache hit for node_modules, there's no need to bring in the global - # yarn cache or run yarn install, as all dependencies will be installed already. - - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: yarn install - if: steps.cache-modules.outputs.cache-hit != 'true' - run: yarn install --frozen-lockfile - # End of yarn setup + uses: backstage/actions/yarn-install@v0.2.1 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: check for yarn.lock changes id: yarn-lock diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index c90894bce4..97b3f45954 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -20,33 +20,15 @@ jobs: steps: - uses: actions/checkout@v3 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - name: yarn install - run: yarn install --frozen-lockfile - # End of yarn setup + uses: backstage/actions/yarn-install@v0.2.1 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} # No verification done here, only build & publish. If the master branch # is broken we will see that from those builds, but we still want to push nightly diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index b63507df0c..529c7a4286 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -61,33 +61,15 @@ jobs: steps: - uses: actions/checkout@v3 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - name: yarn install - run: yarn install --frozen-lockfile - # End of yarn setup + uses: backstage/actions/yarn-install@v0.2.1 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: Fetch previous commit for release check run: git fetch origin '${{ github.event.before }}' @@ -157,33 +139,15 @@ jobs: steps: - uses: actions/checkout@v3 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - name: yarn install - run: yarn install --frozen-lockfile - # End of yarn setup + uses: backstage/actions/yarn-install@v0.2.1 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: build type declarations run: yarn tsc:full diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index b6fce0e68d..6ea6bc5de6 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -14,33 +14,15 @@ jobs: # Fetch changes to previous commit - required for 'only_changed' in Prettier action fetch-depth: 0 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - name: yarn install - run: yarn install --frozen-lockfile - # End of yarn setup + uses: backstage/actions/yarn-install@v0.2.1 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: Run Prettier on ADOPTERS.md uses: creyD/prettier_action@v4.2 diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index e45a36c8e8..9daf85ffa6 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -16,33 +16,15 @@ jobs: steps: - uses: actions/checkout@v3 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - name: yarn install - run: yarn install --frozen-lockfile - # End of yarn setup + uses: backstage/actions/yarn-install@v0.2.1 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: Create Snyk report uses: snyk/actions/node@master diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 884d8bf172..0fe18b345e 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -40,35 +40,15 @@ jobs: steps: - uses: actions/checkout@v3 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 - with: - path: '**/node_modules' - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: setup chrome - uses: browser-actions/setup-chrome@latest - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - name: yarn install - run: yarn install --frozen-lockfile - # End of yarn setup + uses: backstage/actions/yarn-install@v0.2.1 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - run: yarn tsc - run: yarn backstage-cli repo build diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index a13aa3ae71..7bbbac8a40 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -17,44 +17,30 @@ on: jobs: chromatic: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest] + node-version: [16.x] + steps: - uses: actions/checkout@v3 with: fetch-depth: 0 # Required to retrieve git history - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: cache all node_modules - id: cache-modules - uses: actions/cache@v3 + - name: yarn install + uses: backstage/actions/yarn-install@v0.2.1 with: - path: '**/node_modules' - key: ${{ runner.os }}-v${{ matrix.node-version }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} - - name: find location of global yarn cache - id: yarn-cache - if: steps.cache-modules.outputs.cache-hit != 'true' - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v3 - if: steps.cache-modules.outputs.cache-hit != 'true' - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - - name: top-level install - run: yarn install --frozen-lockfile - + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: storybook yarn install run: yarn install --frozen-lockfile working-directory: storybook - # End of yarn setup - run: yarn build-storybook diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index bb8aa9e5aa..e286d42c7e 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -26,25 +26,15 @@ jobs: steps: - uses: actions/checkout@v3 - # Beginning of yarn setup, keep in sync between all workflows, see ci.yml - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - - name: find location of global yarn cache - id: yarn-cache - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: cache global yarn cache - uses: actions/cache@v3 - with: - path: ${{ steps.yarn-cache.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- + + # Windows file operation slowness means there's no point caching this - name: yarn install run: yarn install --frozen-lockfile - # End of yarn setup - name: lint run: yarn backstage-cli repo lint From ec5a609dedf08568175a5ae494a596ccbede8877 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 08:05:55 +0000 Subject: [PATCH 095/101] fix(deps): update dependency webpack-dev-server to v4.9.3 Signed-off-by: Renovate Bot --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1e60c65663..c069f8a2d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10142,10 +10142,10 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" -connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== consola@^2.15.0: version "2.15.3" @@ -25930,9 +25930,9 @@ webpack-dev-middleware@^5.3.1: schema-utils "^4.0.0" webpack-dev-server@^4.7.3: - version "4.9.2" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.2.tgz#c188db28c7bff12f87deda2a5595679ebbc3c9bc" - integrity sha512-H95Ns95dP24ZsEzO6G9iT+PNw4Q7ltll1GfJHV4fKphuHWgKFzGHWi4alTlTnpk1SPPk41X+l2RB7rLfIhnB9Q== + version "4.9.3" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz#2360a5d6d532acb5410a668417ad549ee3b8a3c9" + integrity sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw== dependencies: "@types/bonjour" "^3.5.9" "@types/connect-history-api-fallback" "^1.3.5" @@ -25946,7 +25946,7 @@ webpack-dev-server@^4.7.3: chokidar "^3.5.3" colorette "^2.0.10" compression "^1.7.4" - connect-history-api-fallback "^1.6.0" + connect-history-api-fallback "^2.0.0" default-gateway "^6.0.3" express "^4.17.3" graceful-fs "^4.2.6" From a581804805a721ce21fe03246aafe3e8b6632ed8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 08:06:06 +0000 Subject: [PATCH 096/101] chore(deps): update backstage/actions action to v0.2.1 Signed-off-by: Renovate Bot --- .github/workflows/cron.yml | 2 +- .github/workflows/issue.yaml | 2 +- .github/workflows/pr.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index e2144a56c1..85d7786cf7 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -8,7 +8,7 @@ jobs: cron: runs-on: ubuntu-latest steps: - - uses: backstage/actions/cron@v0.1.12 + - uses: backstage/actions/cron@v0.2.1 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 972f90795c..5fa11b839f 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -10,4 +10,4 @@ jobs: if: github.repository == 'backstage/backstage' steps: - name: Issue sync - uses: backstage/actions/issue-sync@v0.1.8 + uses: backstage/actions/issue-sync@v0.2.1 diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 01254a53a8..e65240ac1b 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -9,6 +9,6 @@ jobs: if: github.repository == 'backstage/backstage' steps: - name: PR sync - uses: backstage/actions/pr-sync@v0.1.8 + uses: backstage/actions/pr-sync@v0.2.1 with: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} From 38d46bfe02b9aaec0459b1027fbb3e04ee5f8992 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 08:21:22 +0000 Subject: [PATCH 097/101] fix(deps): update dependency eslint-webpack-plugin to v3.2.0 Signed-off-by: Renovate Bot --- yarn.lock | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index bcd803a1e7..886d7cb6b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6213,7 +6213,7 @@ "@types/eslint" "*" "@types/estree" "*" -"@types/eslint@*", "@types/eslint@^7.28.2": +"@types/eslint@*": version "7.29.0" resolved "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78" integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== @@ -6221,6 +6221,14 @@ "@types/estree" "*" "@types/json-schema" "*" +"@types/eslint@^7.29.0 || ^8.4.1": + version "8.4.3" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.3.tgz#5c92815a3838b1985c90034cd85f26f59d9d0ece" + integrity sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + "@types/estree@*", "@types/estree@^0.0.51": version "0.0.51" resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" @@ -12408,15 +12416,15 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint-webpack-plugin@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz#83dad2395e5f572d6f4d919eedaa9cf902890fcb" - integrity sha512-xSucskTN9tOkfW7so4EaiFIkulWLXwCB/15H917lR6pTv0Zot6/fetFucmENRb7J5whVSFKIvwnrnsa78SG2yg== + version "3.2.0" + resolved "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz#1978cdb9edc461e4b0195a20da950cf57988347c" + integrity sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w== dependencies: - "@types/eslint" "^7.28.2" - jest-worker "^27.3.1" - micromatch "^4.0.4" + "@types/eslint" "^7.29.0 || ^8.4.1" + jest-worker "^28.0.2" + micromatch "^4.0.5" normalize-path "^3.0.0" - schema-utils "^3.1.1" + schema-utils "^4.0.0" eslint@^8.6.0: version "8.18.0" @@ -16344,7 +16352,7 @@ jest-when@^3.1.0: resolved "https://registry.npmjs.org/jest-when/-/jest-when-3.5.1.tgz#33ab6f923661cf878cd08fe9df64b507934603db" integrity sha512-o+HiaIVCg1IC95sMDKHU9G5v5N5l3UHqXvJpf0PgAMThZeQo4Hf5Sgoj+wpCBRGg4/KtzSAZZZEKNiLqE0i4eQ== -jest-worker@^27.3.1, jest-worker@^27.4.5, jest-worker@^27.5.1: +jest-worker@^27.4.5, jest-worker@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== @@ -16353,6 +16361,15 @@ jest-worker@^27.3.1, jest-worker@^27.4.5, jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" +jest-worker@^28.0.2: + version "28.1.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.1.tgz#3480c73247171dfd01eda77200f0063ab6a3bf28" + integrity sha512-Au7slXB08C6h+xbJPp7VIb6U0XX5Kc9uel/WFc6/rcTzGiaVCBRngBExSYuXSLFPULPSYU3cJ3ybS988lNFQhQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" From 90b3bc0a20da4fd874ed29a1a8340a428881adc3 Mon Sep 17 00:00:00 2001 From: David Rubio Vidal Date: Thu, 30 Jun 2022 12:52:34 +0200 Subject: [PATCH 098/101] chore: update early adopters for DAZN Signed-off-by: David Rubio Vidal --- ADOPTERS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index b72da367b1..15432ab24c 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -36,7 +36,7 @@ _If you're using Backstage in your organization, please try to add your company | [PagerDuty](https://www.pagerduty.com/) | [Mark Shaw](https://github.com/markshawtoronto) | Developer portal, initially focused on software templates and tech-docs. | | [MoonShiner](https://moonshiner.at) | [Fabian Hippmann](https://github.com/FabianHippmann) | Developer portal - helps us keep track of our customer projects, onboard new developers & improve our development process ๐ŸŒ•๐Ÿš€๐Ÿง‘โ€๐Ÿš€ | | [FundApps](https://www.fundapps.co/) | [Elliot Greenwood](https://github.com/egnwd) | Developer Portal - A place for us to keep track of our projects and documentation for all services and processes | -| [DAZN](https://dazn.com/) | [Lou Bichard](https://twitter.com/loujaybee), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com), [Kamil Wolny](https://github.com/mrwolny) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | +| [DAZN](https://dazn.com/) | [David Rubio Vidal](https://github.com/davidrv87), [Marco Crivellaro](https://github.com/crivetechie), [Alex Hollerith](mailto:alex.hollerith@dazn.com), [Kamil Wolny](https://github.com/mrwolny) | Ingesting all of DAZN's repos for the catalog, migrating our internal platform apps (pull request boards, release information, inner source marketplace etc) to Backstage plugins (where applicable). | | [HelloFresh](https://www.hellofresh.de/) | [@iammuho](https://github.com/iammuho), [@ElenaForester](https://github.com/ElenaForester), [@diegomarangoni](https://github.com/diegomarangoni) | Our developer portal at HelloFresh - Spread across an organisation of 500+ engineers globally. | | [FactSet](https://www.factset.com/) | [@kuangp](https://github.com/kuangp) | Developer portal to provide discoverability to all internal components, APIs, documentation, and scaffold templates with integrations to our internal infrastructure tools. | | [Workrise](https://www.workrise.com/) | [Michael Rode](https://github.com/michaelrode) | Developer portal, main gateway to our infrastructure, documentation and internal tooling. | From 27b526e377cc3ecbcbb6dd775813559b3da2c2a3 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 30 Jun 2022 13:48:11 +0200 Subject: [PATCH 099/101] chore: updating adopter notice Signed-off-by: blam --- ADOPTERS.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index bb9b7fe077..2f47f22f90 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -1,6 +1,8 @@ # Adopters -_If you're using Backstage in your organization, please try to add your company name to this list. This really helps the project to gain momentum and credibility. It's a small contribution back to the project with a big impact._ +_If you're using Backstage in your organization, please try to add your company name to this list. It really helps the project to gain momentum and credibility. It's a small contribution back to the project with a big impact._ +_You can do this by using the [Adopter form](https://form.typeform.com/to/zcOaKikB) or by editing this file after following the [CONTRIBUTING.md](./CONTRIBUTING.md)._ + | Organization | Contact | Description of Use | |-----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -187,4 +189,4 @@ _If you're using Backstage in your organization, please try to add your company | [William Hill](https://www.williamhillgroup.com/) | [Pat Mills](mailto:pat.mills@williamhill.com), [Nathan Flynn](mailto:nflynn@williamhill.co.uk), and [Nishkarsh Raj](mailto:nishkarsh.raj@williamhill.co.uk) | William Hill are leveraging Backstage to build our Engineering Portal. Our mission is to centralize the software catalog inventory to enable service discoverability, reduce the onboarding time for new Engineers, provide a single pane of glass to accelerate Developer Productivity and Save Engineers time. Our aspiration is to create an InnerSource community focussed on organization-wide patterns that are re-usable and can be self-served with the Scaffolder. | | [Vodafone NewZealand Limited](https://vodafone.co.nz) | [Ankit Gupta](mailto:ankit.gupta@vodafone.nz), [DevOps COE](mailto:devopstooling@vodafone.nz) | Vodafone NZ are leveraging Backstage to build centralised and self service Engineering Portal. Our mission is to standardised Pipeline templates across the Engineering teams, One shop stop to create the pipelines and repository with a template approach which reduces creation part from days to minutes and no wait time for developers. A unified view for Azure DevOps pipeline, Azure Repo pull requests, Deployment status from Azure RedHat Openshift-ArgoCD and SonarQube Security and code quality scans report on a single pan to provide a streamlined view for all microservices across the app stack. | | [Coamo](http://www.coamo.com.br) | [@holiiveira](https://github.com/holiiveira), [@gpxlnx](https://github.com/gpxlnx) | We're starting to use it as the main tool of a DevOps platform. Our goal is to provide software templates, centralize our software catalog enabling efficient service discovery, and make it easy to manage the entire software ecosystem in one place. - | \ No newline at end of file + | From c86c0019ea6a42597d03a42589fbb9c578f9a8ee Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 30 Jun 2022 13:49:22 +0200 Subject: [PATCH 100/101] chore: updating contributing Signed-off-by: blam --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb60bc48e0..1afbf072fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,9 +58,9 @@ If you are proposing a feature: - Remember that this is a volunteer-driven project, and that contributions are welcome :) -### Add your company to ADOPTERS +### Add your company to `ADOPTERS` -Have you started using Backstage? Adding your company to [ADOPTERS](ADOPTERS.md) really helps the project. +Have you started using Backstage? Adding your company to [ADOPTERS](ADOPTERS.md) really helps the project, you can do this by filling out this [Adopter form](https://form.typeform.com/to/zcOaKikB). ## Get Started! From d5f3f64a8896af63aaf9b00c84e1c74f2094b287 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 30 Jun 2022 13:54:31 +0200 Subject: [PATCH 101/101] workflows: bump cron to 0.2.2 Signed-off-by: Patrik Oldsberg --- .github/workflows/cron.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 85d7786cf7..ca21c1c283 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -8,7 +8,7 @@ jobs: cron: runs-on: ubuntu-latest steps: - - uses: backstage/actions/cron@v0.2.1 + - uses: backstage/actions/cron@v0.2.2 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }}