From 384c1323824976f5c18708824953aa57e0071aab Mon Sep 17 00:00:00 2001 From: shmaram Date: Wed, 22 Nov 2023 10:38:31 +0200 Subject: [PATCH 001/468] support filter in HomePageVisitedByType Signed-off-by: shmaram --- .changeset/wise-flies-laugh.md | 5 +++ .../VisitedByType/Content.test.tsx | 19 ++++++++++ .../VisitedByType/Content.tsx | 38 +++++++++++++++---- 3 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 .changeset/wise-flies-laugh.md diff --git a/.changeset/wise-flies-laugh.md b/.changeset/wise-flies-laugh.md new file mode 100644 index 0000000000..79a9c3bf5a --- /dev/null +++ b/.changeset/wise-flies-laugh.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': minor +--- + +Added filter support for HomePageVisitedByType in order to enable filtering entites from the list diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx index 4442c319e6..87b5b672a3 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx @@ -117,6 +117,25 @@ describe('', () => { expect(container.querySelectorAll('li')[0]).toBeVisible(); expect(container.querySelectorAll('li')[1]).not.toBeVisible(); }); + + it('allows items to be filtered', async () => { + const { getByText, queryByText } = await renderInTestApp( + + + + + , + ); + await waitFor(() => + expect(getByText('Explore Backstage')).toBeInTheDocument(), + ); + await waitFor(() => expect(queryByText('Tech Radar')).toBeNull()); + }); }); describe('', () => { diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx index 4e847b717f..400cb48e09 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx @@ -31,6 +31,11 @@ export type VisitedByTypeProps = { numVisitsTotal?: number; loading?: boolean; kind: VisitedByTypeKind; + filterBy?: Array<{ + field: keyof Visit; + operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + value: string | number; + }>; }; /** @@ -43,6 +48,7 @@ export const Content = ({ numVisitsTotal, loading, kind, + filterBy, }: VisitedByTypeProps) => { const { setContext, setVisits, setLoading } = useContext(); // Allows behavior override from properties @@ -65,18 +71,34 @@ export const Content = ({ const { loading: reqLoading } = useAsync(async () => { if (!visits && !loading && kind === 'recent') { return await visitsApi - .list({ - limit: numVisitsTotal ?? 8, - orderBy: [{ field: 'timestamp', direction: 'desc' }], - }) + .list( + filterBy + ? { + limit: numVisitsTotal ?? 8, + orderBy: [{ field: 'timestamp', direction: 'desc' }], + filterBy, + } + : { + limit: numVisitsTotal ?? 8, + orderBy: [{ field: 'timestamp', direction: 'desc' }], + }, + ) .then(setVisits); } if (!visits && !loading && kind === 'top') { return await visitsApi - .list({ - limit: numVisitsTotal ?? 8, - orderBy: [{ field: 'hits', direction: 'desc' }], - }) + .list( + filterBy + ? { + limit: numVisitsTotal ?? 8, + orderBy: [{ field: 'hits', direction: 'desc' }], + filterBy, + } + : { + limit: numVisitsTotal ?? 8, + orderBy: [{ field: 'hits', direction: 'desc' }], + }, + ) .then(setVisits); } return undefined; From 8e1a0aa867a30c9d89a09ff03defc8d5e1c2d988 Mon Sep 17 00:00:00 2001 From: shmaram Date: Fri, 24 Nov 2023 00:45:33 +0200 Subject: [PATCH 002/468] Pull request changes Signed-off-by: shmaram --- .changeset/wise-flies-laugh.md | 2 +- plugins/home/api-report.md | 1 + .../VisitedByType/Content.test.tsx | 8 ++-- .../VisitedByType/Content.tsx | 42 ++++++------------- 4 files changed, 18 insertions(+), 35 deletions(-) diff --git a/.changeset/wise-flies-laugh.md b/.changeset/wise-flies-laugh.md index 79a9c3bf5a..d3d2ba8af0 100644 --- a/.changeset/wise-flies-laugh.md +++ b/.changeset/wise-flies-laugh.md @@ -2,4 +2,4 @@ '@backstage/plugin-home': minor --- -Added filter support for HomePageVisitedByType in order to enable filtering entites from the list +Added filter support for HomePageVisitedByType in order to enable filtering entities from the list diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 18049cae84..53baf80a0a 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -235,6 +235,7 @@ export type VisitedByTypeProps = { numVisitsTotal?: number; loading?: boolean; kind: VisitedByTypeKind; + filterBy?: VisitsApiQueryParams['filterBy']; }; // @public diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx index 87b5b672a3..8dde5b0e1c 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx @@ -131,10 +131,10 @@ describe('', () => { , ); - await waitFor(() => - expect(getByText('Explore Backstage')).toBeInTheDocument(), - ); - await waitFor(() => expect(queryByText('Tech Radar')).toBeNull()); + await waitFor(() => { + expect(getByText('Explore Backstage')).toBeInTheDocument(); + expect(queryByText('Tech Radar')).toBeNull(); + }); }); }); diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx index 400cb48e09..334f8c0e70 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx @@ -16,7 +16,7 @@ import React, { useEffect } from 'react'; import { VisitedByType } from './VisitedByType'; -import { Visit, visitsApiRef } from '../../api/VisitsApi'; +import { Visit, VisitsApiQueryParams, visitsApiRef } from '../../api'; import { ContextValueOnly, useContext } from './Context'; import { useApi } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; @@ -31,11 +31,7 @@ export type VisitedByTypeProps = { numVisitsTotal?: number; loading?: boolean; kind: VisitedByTypeKind; - filterBy?: Array<{ - field: keyof Visit; - operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; - value: string | number; - }>; + filterBy?: VisitsApiQueryParams['filterBy']; }; /** @@ -71,34 +67,20 @@ export const Content = ({ const { loading: reqLoading } = useAsync(async () => { if (!visits && !loading && kind === 'recent') { return await visitsApi - .list( - filterBy - ? { - limit: numVisitsTotal ?? 8, - orderBy: [{ field: 'timestamp', direction: 'desc' }], - filterBy, - } - : { - limit: numVisitsTotal ?? 8, - orderBy: [{ field: 'timestamp', direction: 'desc' }], - }, - ) + .list({ + limit: numVisitsTotal ?? 8, + orderBy: [{ field: 'timestamp', direction: 'desc' }], + ...(filterBy && { filterBy }), + }) .then(setVisits); } if (!visits && !loading && kind === 'top') { return await visitsApi - .list( - filterBy - ? { - limit: numVisitsTotal ?? 8, - orderBy: [{ field: 'hits', direction: 'desc' }], - filterBy, - } - : { - limit: numVisitsTotal ?? 8, - orderBy: [{ field: 'hits', direction: 'desc' }], - }, - ) + .list({ + limit: numVisitsTotal ?? 8, + orderBy: [{ field: 'hits', direction: 'desc' }], + ...(filterBy && { filterBy }), + }) .then(setVisits); } return undefined; From 6aa6f66e72c6e42e90087cc746325d4f9074da98 Mon Sep 17 00:00:00 2001 From: shmaram Date: Tue, 5 Dec 2023 17:50:28 +0200 Subject: [PATCH 003/468] Added support for filter using config instead of props Signed-off-by: shmaram --- .changeset/wise-flies-laugh.md | 2 +- plugins/home/README.md | 28 ++++++++ plugins/home/api-report.md | 1 - plugins/home/config.d.ts | 68 +++++++++++++++++++ plugins/home/package.json | 4 +- plugins/home/src/api/config.ts | 60 ++++++++++++++++ .../VisitedByType/Content.test.tsx | 35 +++++++--- .../VisitedByType/Content.tsx | 14 ++-- 8 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 plugins/home/config.d.ts create mode 100644 plugins/home/src/api/config.ts diff --git a/.changeset/wise-flies-laugh.md b/.changeset/wise-flies-laugh.md index d3d2ba8af0..4b31b4e9ba 100644 --- a/.changeset/wise-flies-laugh.md +++ b/.changeset/wise-flies-laugh.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-home': minor +'@backstage/plugin-home': patch --- Added filter support for HomePageVisitedByType in order to enable filtering entities from the list diff --git a/plugins/home/README.md b/plugins/home/README.md index d41ae57162..6d75bcac10 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -325,6 +325,34 @@ export default app.createRoot( ); ``` +You can filter the items that are shown in the component. +this can be done by using the config file. +Filtering is done by using 3 parameters: + +- `field` - define which field to filter. can be one of the following + - id: string; + - name: string; + - pathname: string; + - hits: number; + - timestamp: number; + - entityRef?: string; +- `operator` - can be one of the following `'<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'` +- `value` - the value of the filter + +```yaml +home: + recentVisits: + filterBy: + - field: + operator: + value: + topVisits: + filterBy: + - field: + operator: + value: +``` + ## Contributing ### Homepage Components diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 53baf80a0a..18049cae84 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -235,7 +235,6 @@ export type VisitedByTypeProps = { numVisitsTotal?: number; loading?: boolean; kind: VisitedByTypeKind; - filterBy?: VisitsApiQueryParams['filterBy']; }; // @public diff --git a/plugins/home/config.d.ts b/plugins/home/config.d.ts new file mode 100644 index 0000000000..3834a14d89 --- /dev/null +++ b/plugins/home/config.d.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + home?: { + /** + * Top visited plugin + * @visibility frontend + */ + topVisits?: { + /** + * Filter By config + * @visibility frontend + */ + filterBy?: Array<{ + /** + * @visibility frontend + */ + field: string; + /** + * @visibility frontend + */ + operator: string; + /** + * @visibility frontend + */ + value: string | number; + }>; + }; + /** + * Recent visited plugin + * @visibility frontend + */ + recentVisits?: { + /** + * Filter By config + * @visibility frontend + */ + filterBy?: Array<{ + /** + * @visibility frontend + */ + field: string; + /** + * @visibility frontend + */ + operator: string; + /** + * @visibility frontend + */ + value: string | number; + }>; + }; + }; +} diff --git a/plugins/home/package.json b/plugins/home/package.json index 78eaf80fe3..e239332412 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -36,6 +36,7 @@ ] } }, + "configSchema": "config.d.ts", "sideEffects": false, "scripts": { "build": "backstage-cli package build", @@ -90,6 +91,7 @@ "msw": "^1.0.0" }, "files": [ - "dist" + "dist", + "config.d.ts" ] } diff --git a/plugins/home/src/api/config.ts b/plugins/home/src/api/config.ts new file mode 100644 index 0000000000..893935f0e5 --- /dev/null +++ b/plugins/home/src/api/config.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Visit, VisitsApiQueryParams } from './VisitsApi'; +import { Config } from '@backstage/config'; + +/** + * Reads a single FilterBy config. + * + * @param config - The single config object + * + * @public + */ +export function readFilterByConfig(config: Config): { + field: keyof Visit; + operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + value: string | number; +} { + try { + const field = config.getString('field') as keyof Visit; + const operator = config.getString('operator') as + | '<' + | '<=' + | '==' + | '!=' + | '>' + | '>=' + | 'contains'; + const value = config.getString('value'); + return { field, operator, value }; + } catch (error) { + throw new Error(`Invalid config, ${error}`); + } +} + +/** + * Reads a set of filter by configs. + * + * @param configs - All of the config objects + * + * @public + */ +export function readFilterByConfigs( + configs: Config[], +): VisitsApiQueryParams['filterBy'] { + return configs.map(readFilterByConfig); +} diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx index 8dde5b0e1c..f45efc8781 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx @@ -16,10 +16,15 @@ import React from 'react'; import { Content } from './Content'; -import { TestApiProvider, renderInTestApp } from '@backstage/test-utils'; +import { + TestApiProvider, + renderInTestApp, + MockConfigApi, +} from '@backstage/test-utils'; import { visitsApiRef } from '../../api'; import { ContextProvider } from './Context'; import { waitFor } from '@testing-library/react'; +import { configApiRef } from '@backstage/core-plugin-api'; const visits = [ { @@ -119,15 +124,29 @@ describe('', () => { }); it('allows items to be filtered', async () => { + const configApiMock = new MockConfigApi({ + home: { + topVisits: { + filterBy: [ + { + field: 'pathname', + operator: '==', + value: '/explore', + }, + ], + }, + }, + }); + const { getByText, queryByText } = await renderInTestApp( - + - + , ); diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx index 334f8c0e70..6fdc313c63 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx @@ -15,10 +15,11 @@ */ import React, { useEffect } from 'react'; +import { readFilterByConfigs } from '../../api/config'; import { VisitedByType } from './VisitedByType'; -import { Visit, VisitsApiQueryParams, visitsApiRef } from '../../api'; +import { Visit, visitsApiRef } from '../../api'; import { ContextValueOnly, useContext } from './Context'; -import { useApi } from '@backstage/core-plugin-api'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; /** @public */ @@ -31,7 +32,6 @@ export type VisitedByTypeProps = { numVisitsTotal?: number; loading?: boolean; kind: VisitedByTypeKind; - filterBy?: VisitsApiQueryParams['filterBy']; }; /** @@ -44,7 +44,6 @@ export const Content = ({ numVisitsTotal, loading, kind, - filterBy, }: VisitedByTypeProps) => { const { setContext, setVisits, setLoading } = useContext(); // Allows behavior override from properties @@ -62,10 +61,14 @@ export const Content = ({ setContext(state => ({ ...state, ...context })); }, [setContext, kind, visits, loading, numVisitsOpen, numVisitsTotal]); + const config = useApi(configApiRef); // Fetches data from visitsApi in case visits and loading are not provided const visitsApi = useApi(visitsApiRef); const { loading: reqLoading } = useAsync(async () => { if (!visits && !loading && kind === 'recent') { + const filterBy = readFilterByConfigs( + config.getOptionalConfigArray('home.recentVisits.filterBy') ?? [], + ); return await visitsApi .list({ limit: numVisitsTotal ?? 8, @@ -75,6 +78,9 @@ export const Content = ({ .then(setVisits); } if (!visits && !loading && kind === 'top') { + const filterBy = readFilterByConfigs( + config.getOptionalConfigArray('home.topVisits.filterBy') ?? [], + ); return await visitsApi .list({ limit: numVisitsTotal ?? 8, From 9f2a817e87fd9ef4fc41538325fde0be04fa5643 Mon Sep 17 00:00:00 2001 From: shmaram Date: Tue, 5 Dec 2023 18:05:41 +0200 Subject: [PATCH 004/468] Fix README.md Signed-off-by: shmaram --- plugins/home/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/home/README.md b/plugins/home/README.md index 6d75bcac10..37c308da60 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -335,7 +335,7 @@ Filtering is done by using 3 parameters: - pathname: string; - hits: number; - timestamp: number; - - entityRef?: string; + - entityRef: string; - `operator` - can be one of the following `'<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'` - `value` - the value of the filter From 41167e39f9754e280d6e7522490ccfadffb54b3d Mon Sep 17 00:00:00 2001 From: shmaram Date: Tue, 5 Dec 2023 18:11:51 +0200 Subject: [PATCH 005/468] Fix README.md Signed-off-by: shmaram --- plugins/home/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/home/README.md b/plugins/home/README.md index 37c308da60..9883190be4 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -330,12 +330,12 @@ this can be done by using the config file. Filtering is done by using 3 parameters: - `field` - define which field to filter. can be one of the following - - id: string; - - name: string; - - pathname: string; - - hits: number; - - timestamp: number; - - entityRef: string; + - id: string + - name: string + - pathname: string + - hits: number + - timestamp: number + - entityRef: string - `operator` - can be one of the following `'<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'` - `value` - the value of the filter From 6640340085bff5d27deffba874385958b1be93fb Mon Sep 17 00:00:00 2001 From: shmaram Date: Tue, 5 Dec 2023 18:46:44 +0200 Subject: [PATCH 006/468] Fix README.md Signed-off-by: shmaram --- plugins/home/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/home/README.md b/plugins/home/README.md index 9883190be4..c84bae684f 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -330,12 +330,12 @@ this can be done by using the config file. Filtering is done by using 3 parameters: - `field` - define which field to filter. can be one of the following - - id: string - - name: string - - pathname: string - - hits: number - - timestamp: number - - entityRef: string + - `id`: string + - `name`: string + - `pathname`: string + - `hits`: number + - `timestamp`: number + - `entityRef`: string - `operator` - can be one of the following `'<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'` - `value` - the value of the filter From 42dcdfbfd52e2cf8e118ae5f37d151365c0dd60b Mon Sep 17 00:00:00 2001 From: shmaram Date: Wed, 6 Dec 2023 21:23:01 +0200 Subject: [PATCH 007/468] Fix tests Signed-off-by: shmaram --- .../VisitedByType/Content.test.tsx | 123 +++++++++++++++++- 1 file changed, 117 insertions(+), 6 deletions(-) diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx index f45efc8781..c6c0e6d81a 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx @@ -34,6 +34,13 @@ const visits = [ hits: 35, timestamp: Date.now() - 86400_000, }, + { + id: 'tech-radar', + name: 'Tech Radar', + pathname: '/tech-radar', + hits: 40, + timestamp: Date.now() - 360_000, + }, ]; const mockVisitsApi = { @@ -123,22 +130,24 @@ describe('', () => { expect(container.querySelectorAll('li')[1]).not.toBeVisible(); }); - it('allows items to be filtered', async () => { + it('allows recent items to be filtered using config', async () => { const configApiMock = new MockConfigApi({ home: { - topVisits: { + recentVisits: { filterBy: [ { field: 'pathname', operator: '==', - value: '/explore', + value: '/tech-radar', }, ], }, }, }); - const { getByText, queryByText } = await renderInTestApp( + const listSpy = jest.spyOn(mockVisitsApi, 'list'); + + await renderInTestApp( ', () => { , ); await waitFor(() => { - expect(getByText('Explore Backstage')).toBeInTheDocument(); - expect(queryByText('Tech Radar')).toBeNull(); + expect(listSpy).toHaveBeenCalledWith({ + limit: 8, + orderBy: [ + { + direction: 'desc', + field: 'timestamp', + }, + ], + filterBy: [{ field: 'pathname', operator: '==', value: '/tech-radar' }], + }); + }); + }); + + it('shows all recent items when there is no filtering in the config', async () => { + const listSpy = jest.spyOn(mockVisitsApi, 'list'); + + await renderInTestApp( + + + + + , + ); + + await waitFor(() => { + expect(listSpy).toHaveBeenCalledWith({ + limit: 8, + orderBy: [ + { + direction: 'desc', + field: 'timestamp', + }, + ], + filterBy: [], + }); }); }); }); @@ -171,4 +213,73 @@ describe('', () => { expect(getByText('Explore Backstage')).toBeInTheDocument(), ); }); + + it('allows top items to be filtered using config', async () => { + const configApiMock = new MockConfigApi({ + home: { + topVisits: { + filterBy: [ + { + field: 'pathname', + operator: '==', + value: '/explore', + }, + ], + }, + }, + }); + + const listSpy = jest.spyOn(mockVisitsApi, 'list'); + + await renderInTestApp( + + + + + , + ); + + await waitFor(() => { + expect(listSpy).toHaveBeenCalledWith({ + limit: 8, + orderBy: [ + { + direction: 'desc', + field: 'hits', + }, + ], + filterBy: [{ field: 'pathname', operator: '==', value: '/explore' }], + }); + }); + }); + + it('shows all top items when there is no filtering in the config', async () => { + const listSpy = jest.spyOn(mockVisitsApi, 'list'); + + await renderInTestApp( + + + + + , + ); + + await waitFor(() => { + expect(listSpy).toHaveBeenCalledWith({ + limit: 8, + orderBy: [ + { + direction: 'desc', + field: 'hits', + }, + ], + filterBy: [], + }); + }); + }); }); From 5812f27ad66a5b17015960320b45ef04194e9829 Mon Sep 17 00:00:00 2001 From: shmaram Date: Thu, 21 Dec 2023 11:55:10 +0200 Subject: [PATCH 008/468] Fix tests Signed-off-by: shmaram --- plugins/home/src/api/config.test.ts | 84 +++++++++++++++++++++++++++++ plugins/home/src/api/config.ts | 9 +++- 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 plugins/home/src/api/config.test.ts diff --git a/plugins/home/src/api/config.test.ts b/plugins/home/src/api/config.test.ts new file mode 100644 index 0000000000..62ca09dd82 --- /dev/null +++ b/plugins/home/src/api/config.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { readFilterByConfig, readFilterByConfigs } from './config'; +import { MockConfigApi } from '@backstage/test-utils'; + +describe('config', () => { + describe('readFilterByConfig', () => { + it('returns filter data', async () => { + const mockConfig = new MockConfigApi( + { + field: 'pathname', + operator: '==', + value: '3' + }); + const res = readFilterByConfig(mockConfig) + expect(res).toEqual({ + field: 'pathname', + operator: '==', + value: '3' + }) + }); + + it('throws an error for invalid filter', async () => { + const mockConfig = new MockConfigApi( + { + myField: 'pathname', + operator: '==', + value: '3' + }); + expect(() => readFilterByConfig(mockConfig)).toThrow('Invalid config, Error: Missing required config value at \'field\'') + }); + }); + + describe('readFilterByConfigs', () => { + it('returns filter data', async () => { + const mockConfig1 = new MockConfigApi( + { + field: 'id', + operator: '==', + value: '3' + }); + const mockConfig2 = new MockConfigApi( + { + field: 'pathname', + operator: '==', + value: 'path' + }); + const res = readFilterByConfigs([mockConfig1, mockConfig2]) + expect(res).toEqual([{"field": "id", "operator": "==", "value": "3"}, {"field": "pathname", "operator": "==", "value": "path"}]) + }); + + it('return undefined for invalid filter', async () => { + const mockConfig1 = new MockConfigApi( + { + field: 'id', + operator: '==', + value: '3' + }); + const mockConfig2 = new MockConfigApi( + { + myField: 'pathname', + operator: '==', + value: 'path' + }); + const res = readFilterByConfigs([mockConfig1, mockConfig2]) + expect(res).toEqual(undefined) + }); + }); +}); + diff --git a/plugins/home/src/api/config.ts b/plugins/home/src/api/config.ts index 893935f0e5..6604084244 100644 --- a/plugins/home/src/api/config.ts +++ b/plugins/home/src/api/config.ts @@ -55,6 +55,11 @@ export function readFilterByConfig(config: Config): { */ export function readFilterByConfigs( configs: Config[], -): VisitsApiQueryParams['filterBy'] { - return configs.map(readFilterByConfig); +): VisitsApiQueryParams['filterBy'] | undefined { + try { + return configs.map(readFilterByConfig); + } + catch { + return undefined; + } } From 891fcd5482f57e93da64b58654a74989c219b7cf Mon Sep 17 00:00:00 2001 From: shmaram Date: Fri, 22 Dec 2023 14:02:26 +0200 Subject: [PATCH 009/468] Fix tests Signed-off-by: shmaram --- plugins/home/src/api/VisitsStorageApi.test.ts | 2 +- .../VisitedByType/Content.test.tsx | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/plugins/home/src/api/VisitsStorageApi.test.ts b/plugins/home/src/api/VisitsStorageApi.test.ts index 6faeb62350..e361c624b9 100644 --- a/plugins/home/src/api/VisitsStorageApi.test.ts +++ b/plugins/home/src/api/VisitsStorageApi.test.ts @@ -250,7 +250,7 @@ describe('VisitsStorageApi.create', () => { ]); }); - it('filters by timestamp with >', async () => { + it('filters by timestamp with value >', async () => { const visits = await api.list({ filterBy: [{ field: 'timestamp', operator: '>', value: baseDate }], }); diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx index c6c0e6d81a..1234291f1a 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx @@ -197,6 +197,47 @@ describe('', () => { }); }); }); + + it('allows recent items to have no filter if the filter config is not valid', async () => { + const configApiMock = new MockConfigApi({ + home: { + recentVisits: { + filterBy: [ + { + operator: '==', + value: '/tech-radar', + }, + ], + }, + }, + }); + + const listSpy = jest.spyOn(mockVisitsApi, 'list'); + + await renderInTestApp( + + + + + , + ); + await waitFor(() => { + expect(listSpy).toHaveBeenCalledWith({ + limit: 8, + orderBy: [ + { + direction: 'desc', + field: 'timestamp', + }, + ], + }); + }); + }); }); describe('', () => { From 43dad25429bffbaebbeec08c22286e3485144fdc Mon Sep 17 00:00:00 2001 From: Rickard Dybeck Date: Thu, 21 Dec 2023 12:01:16 -0500 Subject: [PATCH 010/468] [CatalogBackend] Add API to get location by entity Introduce API route to get location by entity. Solves #21826. Signed-off-by: Rickard Dybeck --- .changeset/large-moons-speak.md | 6 ++ packages/catalog-client/api-report.md | 8 +++ .../catalog-client/src/CatalogClient.test.ts | 65 +++++++++++++++++++ packages/catalog-client/src/CatalogClient.ts | 12 ++++ .../src/generated/apis/DefaultApi.client.ts | 36 ++++++++++ packages/catalog-client/src/types/api.ts | 11 ++++ .../src/modules/core/DefaultLocationStore.ts | 52 ++++++++++++++- .../src/schema/openapi.generated.ts | 56 ++++++++++++++++ .../catalog-backend/src/schema/openapi.yaml | 35 ++++++++++ .../service/AuthorizedLocationService.test.ts | 33 ++++++++++ .../src/service/AuthorizedLocationService.ts | 19 +++++- .../service/DefaultLocationService.test.ts | 16 +++++ .../src/service/DefaultLocationService.ts | 5 ++ .../src/service/createRouter.test.ts | 63 ++++++++++++++++++ .../src/service/createRouter.ts | 12 ++++ plugins/catalog-backend/src/service/types.ts | 7 +- 16 files changed, 433 insertions(+), 3 deletions(-) create mode 100644 .changeset/large-moons-speak.md diff --git a/.changeset/large-moons-speak.md b/.changeset/large-moons-speak.md new file mode 100644 index 0000000000..efd94c5df3 --- /dev/null +++ b/.changeset/large-moons-speak.md @@ -0,0 +1,6 @@ +--- +'@backstage/catalog-client': minor +'@backstage/plugin-catalog-backend': minor +--- + +Add API to get location by entity diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index 22feceabec..9fd9426a94 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -50,6 +50,10 @@ export interface CatalogApi { request: GetEntityFacetsRequest, options?: CatalogRequestOptions, ): Promise; + getLocationByEntity( + entityRef: CompoundEntityRef, + options?: CatalogRequestOptions, + ): Promise; getLocationById( id: string, options?: CatalogRequestOptions, @@ -120,6 +124,10 @@ export class CatalogClient implements CatalogApi { request: GetEntityFacetsRequest, options?: CatalogRequestOptions, ): Promise; + getLocationByEntity( + entityRef: CompoundEntityRef, + options?: CatalogRequestOptions, + ): Promise; getLocationById( id: string, options?: CatalogRequestOptions, diff --git a/packages/catalog-client/src/CatalogClient.test.ts b/packages/catalog-client/src/CatalogClient.test.ts index b8212fde96..c36b3d96b5 100644 --- a/packages/catalog-client/src/CatalogClient.test.ts +++ b/packages/catalog-client/src/CatalogClient.test.ts @@ -617,6 +617,71 @@ describe('CatalogClient', () => { }); }); + describe('getLocationByEntity', () => { + const defaultResponse = { + data: { + kind: 'c', + namespace: 'ns', + name: 'n', + }, + }; + + beforeEach(() => { + server.use( + rest.get(`${mockBaseUrl}/locations/by-entity/c/ns/n`, (_, res, ctx) => { + return res(ctx.json(defaultResponse)); + }), + ); + }); + + it('should locations from correct endpoint', async () => { + const response = await client.getLocationByEntity( + { kind: 'c', namespace: 'ns', name: 'n' }, + { token }, + ); + expect(response).toEqual(defaultResponse); + }); + + it('forwards authorization token', async () => { + expect.assertions(1); + + server.use( + rest.get( + `${mockBaseUrl}/locations/by-entity/c/ns/n`, + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBe(`Bearer ${token}`); + return res(ctx.json(defaultResponse)); + }, + ), + ); + + await client.getLocationByEntity( + { kind: 'c', namespace: 'ns', name: 'n' }, + { token }, + ); + }); + + it('skips authorization header if token is omitted', async () => { + expect.assertions(1); + + server.use( + rest.get( + `${mockBaseUrl}/locations/by-entity/c/ns/n`, + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBeNull(); + return res(ctx.json(defaultResponse)); + }, + ), + ); + + await client.getLocationByEntity({ + kind: 'c', + namespace: 'ns', + name: 'n', + }); + }); + }); + describe('validateEntity', () => { it('returns valid false when validation fails', async () => { server.use( diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index 0d6bd6b926..eb99fa7830 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -88,6 +88,18 @@ export class CatalogClient implements CatalogApi { ); } + /** + * {@inheritdoc CatalogApi.getLocationByEntity} + */ + async getLocationByEntity( + entityRef: CompoundEntityRef, + options?: CatalogRequestOptions, + ): Promise { + return await this.requestOptional( + await this.apiClient.getLocationByEntity({ path: entityRef }, options), + ); + } + /** * {@inheritdoc CatalogApi.getEntities} */ diff --git a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts index 65b8699370..8c78d4c721 100644 --- a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts +++ b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts @@ -461,6 +461,42 @@ export class DefaultApiClient { }); } + /** + * Get a location for entity. + * @param kind + * @param namespace + * @param name + */ + public async getLocationByEntity( + // @ts-ignore + request: { + path: { + kind: string; + namespace: string; + name: string; + }; + }, + options?: RequestOptions, + ): Promise> { + const baseUrl = await this.discoveryApi.getBaseUrl(pluginId); + + const uriTemplate = `/locations/by-entity/{kind}/{namespace}/{name}`; + + const uri = parser.parse(uriTemplate).expand({ + kind: request.path.kind, + namespace: request.path.namespace, + name: request.path.name, + }); + + return await this.fetchApi.fetch(`${baseUrl}${uri}`, { + headers: { + 'Content-Type': 'application/json', + ...(options?.token && { Authorization: `Bearer ${options?.token}` }), + }, + method: 'GET', + }); + } + /** * Get all locations */ diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index 5285bda2f7..6d9f95b940 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -629,6 +629,17 @@ export interface CatalogApi { options?: CatalogRequestOptions, ): Promise; + /** + * Gets a location associated with an entity. + * + * @param entityRef - A reference to an entity + * @param options - Additional options + */ + getLocationByEntity( + entityRef: CompoundEntityRef, + options?: CatalogRequestOptions, + ): Promise; + /** * Validate entity and its location. * diff --git a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts index cf5db78203..267abce046 100644 --- a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts @@ -18,7 +18,11 @@ import { Location } from '@backstage/catalog-client'; import { ConflictError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import { v4 as uuid } from 'uuid'; -import { DbLocationsRow } from '../../database/tables'; +import { + DbLocationsRow, + DbRefreshStateRow, + DbSearchRow, +} from '../../database/tables'; import { getEntityLocationRef } from '../../processing/util'; import { EntityProvider, @@ -26,6 +30,11 @@ import { } from '@backstage/plugin-catalog-node'; import { locationSpecToLocationEntity } from '../../util/conversion'; import { LocationInput, LocationStore } from '../../service/types'; +import { + ANNOTATION_ORIGIN_LOCATION, + CompoundEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; export class DefaultLocationStore implements LocationStore, EntityProvider { private _connection: EntityProviderConnection | undefined; @@ -111,6 +120,47 @@ export class DefaultLocationStore implements LocationStore, EntityProvider { }); } + async getLocationByEntity(entityRef: CompoundEntityRef): Promise { + const entityRefString = stringifyEntityRef(entityRef); + + const [entity] = await this.db('refresh_state') + .where({ entity_ref: entityRefString }) + .select('entity_id') + .limit(1); + if (!entity) { + throw new NotFoundError(`found no entity for ref ${entityRefString}`); + } + + const [locationKeyValue] = await this.db('search') + .where({ + entity_id: entity.entity_id, + key: `metadata.annotations.${ANNOTATION_ORIGIN_LOCATION}`, + }) + .select('value') + .limit(1); + if (!locationKeyValue) { + throw new NotFoundError( + `found no origin annotation for ref ${entityRefString}`, + ); + } + const [type, ...rest] = locationKeyValue.value?.split(':') ?? []; + const target = rest.join(':'); + + // const kind, target = split[0], split[1]; + const [location] = await this.db('locations') + .where({ type, target }) + .select() + .limit(1); + + // select * from locations where type = 'split(prev)[0]' + if (!location) { + throw new NotFoundError( + `Found no location with type ${type} and target ${target}`, + ); + } + return location; + } + private get connection(): EntityProviderConnection { if (!this._connection) { throw new Error('location store is not initialized'); diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index fe64c75373..102558c9f6 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1420,6 +1420,62 @@ export const spec = { ], }, }, + '/locations/by-entity/{kind}/{namespace}/{name}': { + get: { + operationId: 'getLocationByEntity', + description: 'Get a location for entity.', + responses: { + '200': { + description: 'Ok', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/Location', + }, + }, + }, + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, + }, + security: [ + {}, + { + JWT: [], + }, + ], + parameters: [ + { + in: 'path', + name: 'kind', + required: true, + allowReserved: true, + schema: { + type: 'string', + }, + }, + { + in: 'path', + name: 'namespace', + required: true, + allowReserved: true, + schema: { + type: 'string', + }, + }, + { + in: 'path', + name: 'name', + required: true, + allowReserved: true, + schema: { + type: 'string', + }, + }, + ], + }, + }, '/analyze-location': { post: { operationId: 'AnalyzeLocation', diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index d3ef718286..fa0f7ec371 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1057,6 +1057,41 @@ paths: allowReserved: true schema: type: string + /locations/by-entity/{kind}/{namespace}/{name}: + get: + operationId: getLocationByEntity + description: Get a location for entity. + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Location' + default: + $ref: '#/components/responses/ErrorResponse' + security: + - {} + - JWT: [] + parameters: + - in: path + name: kind + required: true + allowReserved: true + schema: + type: string + - in: path + name: namespace + required: true + allowReserved: true + schema: + type: string + - in: path + name: name + required: true + allowReserved: true + schema: + type: string /analyze-location: post: operationId: AnalyzeLocation diff --git a/plugins/catalog-backend/src/service/AuthorizedLocationService.test.ts b/plugins/catalog-backend/src/service/AuthorizedLocationService.test.ts index e613f25cb2..c2ffee8003 100644 --- a/plugins/catalog-backend/src/service/AuthorizedLocationService.test.ts +++ b/plugins/catalog-backend/src/service/AuthorizedLocationService.test.ts @@ -24,6 +24,7 @@ describe('AuthorizedLocationService', () => { listLocations: jest.fn(), getLocation: jest.fn(), deleteLocation: jest.fn(), + getLocationByEntity: jest.fn(), }; const fakePermissionApi = { authorize: jest.fn(), @@ -144,4 +145,36 @@ describe('AuthorizedLocationService', () => { ).rejects.toThrow(NotAllowedError); }); }); + + describe('getLocationByEntity', () => { + it('calls underlying service to get location on ALLOW', async () => { + mockAllow(); + const service = createService(); + + await service.getLocationByEntity( + { kind: 'c', namespace: 'ns', name: 'n' }, + { + authorizationToken: 'Bearer authtoken', + }, + ); + + expect(fakeLocationService.getLocationByEntity).toHaveBeenCalledWith({ + kind: 'c', + namespace: 'ns', + name: 'n', + }); + }); + + it('throws error on DENY', async () => { + mockDeny(); + const service = createService(); + + await expect(() => + service.getLocationByEntity( + { kind: 'c', namespace: 'ns', name: 'n' }, + { authorizationToken: 'Bearer authtoken' }, + ), + ).rejects.toThrow(NotFoundError); + }); + }); }); diff --git a/plugins/catalog-backend/src/service/AuthorizedLocationService.ts b/plugins/catalog-backend/src/service/AuthorizedLocationService.ts index 68bb1553a8..fe4547a8fc 100644 --- a/plugins/catalog-backend/src/service/AuthorizedLocationService.ts +++ b/plugins/catalog-backend/src/service/AuthorizedLocationService.ts @@ -15,7 +15,7 @@ */ import { Location } from '@backstage/catalog-client'; -import { Entity } from '@backstage/catalog-model'; +import { CompoundEntityRef, Entity } from '@backstage/catalog-model'; import { NotAllowedError, NotFoundError } from '@backstage/errors'; import { catalogLocationCreatePermission, @@ -111,4 +111,21 @@ export class AuthorizedLocationService implements LocationService { return this.locationService.deleteLocation(id); } + + async getLocationByEntity( + entityRef: CompoundEntityRef, + options?: { authorizationToken?: string | undefined } | undefined, + ): Promise { + const authorizationResponse = ( + await this.permissionApi.authorize( + [{ permission: catalogLocationReadPermission }], + { token: options?.authorizationToken }, + ) + )[0]; + + if (authorizationResponse.result === AuthorizeResult.DENY) { + throw new NotFoundError(); + } + return this.locationService.getLocationByEntity(entityRef); + } } diff --git a/plugins/catalog-backend/src/service/DefaultLocationService.test.ts b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts index 6d70923556..1c17f271fb 100644 --- a/plugins/catalog-backend/src/service/DefaultLocationService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts @@ -28,6 +28,7 @@ describe('DefaultLocationServiceTest', () => { createLocation: jest.fn(), listLocations: jest.fn(), getLocation: jest.fn(), + getLocationByEntity: jest.fn(), }; const locationService = new DefaultLocationService(store, orchestrator); @@ -347,4 +348,19 @@ describe('DefaultLocationServiceTest', () => { expect(store.getLocation).toHaveBeenCalledWith('123'); }); }); + + describe('getLocationByEntity', () => { + it('should call locationStore.getLocationByEntity', async () => { + await locationService.getLocationByEntity({ + kind: 'c', + namespace: 'ns', + name: 'n', + }); + expect(store.getLocationByEntity).toHaveBeenCalledWith({ + kind: 'c', + namespace: 'ns', + name: 'n', + }); + }); + }); }); diff --git a/plugins/catalog-backend/src/service/DefaultLocationService.ts b/plugins/catalog-backend/src/service/DefaultLocationService.ts index 6603ebc08b..5ce9456212 100644 --- a/plugins/catalog-backend/src/service/DefaultLocationService.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.ts @@ -19,6 +19,7 @@ import { ANNOTATION_LOCATION, ANNOTATION_ORIGIN_LOCATION, stringifyEntityRef, + CompoundEntityRef, } from '@backstage/catalog-model'; import { Location } from '@backstage/catalog-client'; import { CatalogProcessingOrchestrator } from '../processing/types'; @@ -68,6 +69,10 @@ export class DefaultLocationService implements LocationService { return this.store.deleteLocation(id); } + getLocationByEntity(entityRef: CompoundEntityRef): Promise { + return this.store.getLocationByEntity(entityRef); + } + private async processEntities( unprocessedEntities: DeferredEntity[], ): Promise { diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 05ebf6e1a9..e08d3b1676 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -63,6 +63,7 @@ describe('createRouter readonly disabled', () => { createLocation: jest.fn(), listLocations: jest.fn(), deleteLocation: jest.fn(), + getLocationByEntity: jest.fn(), }; refreshService = { refresh: jest.fn() }; orchestrator = { process: jest.fn() }; @@ -621,6 +622,36 @@ describe('createRouter readonly disabled', () => { }); }); + describe('GET /locations/by-entity/:kind/:namespace/:name', () => { + it('happy path: gets location by entity ref', async () => { + const location: Location = { + id: 'foo', + type: 'url', + target: 'example.com', + }; + locationService.getLocationByEntity.mockResolvedValueOnce(location); + + const response = await request(app) + .get('/locations/by-entity/c/ns/n') + .set('authorization', 'Bearer someauthtoken'); + + expect(locationService.getLocationByEntity).toHaveBeenCalledTimes(1); + expect(locationService.getLocationByEntity).toHaveBeenCalledWith( + { kind: 'c', namespace: 'ns', name: 'n' }, + { + authorizationToken: 'someauthtoken', + }, + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + id: 'foo', + target: 'example.com', + type: 'url', + }); + }); + }); + describe('POST /validate-entity', () => { describe('valid entity', () => { it('returns 200', async () => { @@ -753,6 +784,7 @@ describe('createRouter readonly enabled', () => { createLocation: jest.fn(), listLocations: jest.fn(), deleteLocation: jest.fn(), + getLocationByEntity: jest.fn(), }; const router = await createRouter({ entitiesCatalog, @@ -911,6 +943,36 @@ describe('createRouter readonly enabled', () => { expect(response.status).toEqual(403); }); }); + + describe('GET /locations/by-entity/:kind/:namespace/:name', () => { + it('happy path: gets location by entity ref', async () => { + const location: Location = { + id: 'foo', + type: 'url', + target: 'example.com', + }; + locationService.getLocationByEntity.mockResolvedValueOnce(location); + + const response = await request(app) + .get('/locations/by-entity/c/ns/n') + .set('authorization', 'Bearer someauthtoken'); + + expect(locationService.getLocationByEntity).toHaveBeenCalledTimes(1); + expect(locationService.getLocationByEntity).toHaveBeenCalledWith( + { kind: 'c', namespace: 'ns', name: 'n' }, + { + authorizationToken: 'someauthtoken', + }, + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + id: 'foo', + target: 'example.com', + type: 'url', + }); + }); + }); }); describe('NextRouter permissioning', () => { @@ -944,6 +1006,7 @@ describe('NextRouter permissioning', () => { createLocation: jest.fn(), listLocations: jest.fn(), deleteLocation: jest.fn(), + getLocationByEntity: jest.fn(), }; refreshService = { refresh: jest.fn() }; const router = await createRouter({ diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 30c1fd8e3f..70b15c640d 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -290,6 +290,18 @@ export async function createRouter( ), }); res.status(204).end(); + }) + .get('/locations/by-entity/:kind/:namespace/:name', async (req, res) => { + const { kind, namespace, name } = req.params; + const output = await locationService.getLocationByEntity( + { kind, namespace, name }, + { + authorizationToken: getBearerTokenFromAuthorizationHeader( + req.header('authorization'), + ), + }, + ); + res.status(200).json(output); }); } diff --git a/plugins/catalog-backend/src/service/types.ts b/plugins/catalog-backend/src/service/types.ts index f6cfa2e783..db4dfc7f34 100644 --- a/plugins/catalog-backend/src/service/types.ts +++ b/plugins/catalog-backend/src/service/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import { CompoundEntityRef, Entity } from '@backstage/catalog-model'; import { Location } from '@backstage/catalog-client'; /** @@ -48,6 +48,10 @@ export interface LocationService { id: string, options?: { authorizationToken?: string }, ): Promise; + getLocationByEntity( + entityRef: CompoundEntityRef, + options?: { authorizationToken?: string }, + ): Promise; } /** @@ -82,4 +86,5 @@ export interface LocationStore { listLocations(): Promise; getLocation(id: string): Promise; deleteLocation(id: string): Promise; + getLocationByEntity(entityRef: CompoundEntityRef): Promise; } From c33f9d485d305d38decaf7f5de45c93d9085878f Mon Sep 17 00:00:00 2001 From: Rickard Dybeck Date: Thu, 21 Dec 2023 12:35:39 -0500 Subject: [PATCH 011/468] update api docs Signed-off-by: Rickard Dybeck --- docs/features/software-catalog/api.md | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/features/software-catalog/api.md b/docs/features/software-catalog/api.md index 3ab3c0df96..46f32e9064 100644 --- a/docs/features/software-catalog/api.md +++ b/docs/features/software-catalog/api.md @@ -474,6 +474,34 @@ Response type is JSON, on the form ] ``` +### `GET /locations/{id}` + +Gets a location by it's location ID. + +Response type is JSON, on the form + +```json +{ + "id": "b9784c38-7118-472f-9e22-5638fc73bab0", + "target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml", + "type": "url" +} +``` + +### `GET /locations/by-entity/{kind}/{namespace}/{name}` + +Gets a location referring to a given entity. + +Response type is JSON, on the form + +```json +{ + "id": "b9784c38-7118-472f-9e22-5638fc73bab0", + "target": "https://git.example.com/example-project/example-repository/blob/main/catalog-info.yaml", + "type": "url" +} +``` + ### `POST /locations` Adds a location to be ingested by the catalog. From 2214202d90cf88fc8b50968a662f7906a6568d42 Mon Sep 17 00:00:00 2001 From: Rickard Dybeck Date: Fri, 29 Dec 2023 10:25:53 -0500 Subject: [PATCH 012/468] review fixups Signed-off-by: Rickard Dybeck --- packages/catalog-client/src/CatalogClient.ts | 7 +++++-- packages/catalog-client/src/types/api.ts | 4 ++-- .../src/modules/core/DefaultLocationStore.ts | 4 ++-- .../src/service/AuthorizedLocationService.ts | 2 +- .../catalog-backend/src/service/DefaultLocationService.ts | 7 +++++-- plugins/catalog-backend/src/service/types.ts | 4 ++-- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/catalog-client/src/CatalogClient.ts b/packages/catalog-client/src/CatalogClient.ts index eb99fa7830..07fedd0e9c 100644 --- a/packages/catalog-client/src/CatalogClient.ts +++ b/packages/catalog-client/src/CatalogClient.ts @@ -92,11 +92,14 @@ export class CatalogClient implements CatalogApi { * {@inheritdoc CatalogApi.getLocationByEntity} */ async getLocationByEntity( - entityRef: CompoundEntityRef, + entityRef: CompoundEntityRef | string, options?: CatalogRequestOptions, ): Promise { return await this.requestOptional( - await this.apiClient.getLocationByEntity({ path: entityRef }, options), + await this.apiClient.getLocationByEntity( + { path: parseEntityRef(entityRef) }, + options, + ), ); } diff --git a/packages/catalog-client/src/types/api.ts b/packages/catalog-client/src/types/api.ts index 6d9f95b940..ecf2be7f44 100644 --- a/packages/catalog-client/src/types/api.ts +++ b/packages/catalog-client/src/types/api.ts @@ -632,11 +632,11 @@ export interface CatalogApi { /** * Gets a location associated with an entity. * - * @param entityRef - A reference to an entity + * @param entityRef - A complete entity ref, either on string or compound form * @param options - Additional options */ getLocationByEntity( - entityRef: CompoundEntityRef, + entityRef: string | CompoundEntityRef, options?: CatalogRequestOptions, ): Promise; diff --git a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts index 267abce046..ac506051d2 100644 --- a/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/modules/core/DefaultLocationStore.ts @@ -33,6 +33,7 @@ import { LocationInput, LocationStore } from '../../service/types'; import { ANNOTATION_ORIGIN_LOCATION, CompoundEntityRef, + parseLocationRef, stringifyEntityRef, } from '@backstage/catalog-model'; @@ -143,9 +144,8 @@ export class DefaultLocationStore implements LocationStore, EntityProvider { `found no origin annotation for ref ${entityRefString}`, ); } - const [type, ...rest] = locationKeyValue.value?.split(':') ?? []; - const target = rest.join(':'); + const { type, target } = parseLocationRef(entityRefString); // const kind, target = split[0], split[1]; const [location] = await this.db('locations') .where({ type, target }) diff --git a/plugins/catalog-backend/src/service/AuthorizedLocationService.ts b/plugins/catalog-backend/src/service/AuthorizedLocationService.ts index fe4547a8fc..0b9d50b20e 100644 --- a/plugins/catalog-backend/src/service/AuthorizedLocationService.ts +++ b/plugins/catalog-backend/src/service/AuthorizedLocationService.ts @@ -113,7 +113,7 @@ export class AuthorizedLocationService implements LocationService { } async getLocationByEntity( - entityRef: CompoundEntityRef, + entityRef: CompoundEntityRef | string, options?: { authorizationToken?: string | undefined } | undefined, ): Promise { const authorizationResponse = ( diff --git a/plugins/catalog-backend/src/service/DefaultLocationService.ts b/plugins/catalog-backend/src/service/DefaultLocationService.ts index 5ce9456212..d0b603b90b 100644 --- a/plugins/catalog-backend/src/service/DefaultLocationService.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.ts @@ -20,6 +20,7 @@ import { ANNOTATION_ORIGIN_LOCATION, stringifyEntityRef, CompoundEntityRef, + parseEntityRef, } from '@backstage/catalog-model'; import { Location } from '@backstage/catalog-client'; import { CatalogProcessingOrchestrator } from '../processing/types'; @@ -69,8 +70,10 @@ export class DefaultLocationService implements LocationService { return this.store.deleteLocation(id); } - getLocationByEntity(entityRef: CompoundEntityRef): Promise { - return this.store.getLocationByEntity(entityRef); + getLocationByEntity( + entityRef: CompoundEntityRef | string, + ): Promise { + return this.store.getLocationByEntity(parseEntityRef(entityRef)); } private async processEntities( diff --git a/plugins/catalog-backend/src/service/types.ts b/plugins/catalog-backend/src/service/types.ts index db4dfc7f34..878afe5662 100644 --- a/plugins/catalog-backend/src/service/types.ts +++ b/plugins/catalog-backend/src/service/types.ts @@ -49,7 +49,7 @@ export interface LocationService { options?: { authorizationToken?: string }, ): Promise; getLocationByEntity( - entityRef: CompoundEntityRef, + entityRef: CompoundEntityRef | string, options?: { authorizationToken?: string }, ): Promise; } @@ -86,5 +86,5 @@ export interface LocationStore { listLocations(): Promise; getLocation(id: string): Promise; deleteLocation(id: string): Promise; - getLocationByEntity(entityRef: CompoundEntityRef): Promise; + getLocationByEntity(entityRef: CompoundEntityRef | string): Promise; } From e0b14e39dcdbb949f3d89074dc3f5024a927864b Mon Sep 17 00:00:00 2001 From: Rickard Dybeck Date: Tue, 2 Jan 2024 11:02:39 -0500 Subject: [PATCH 013/468] fix api-report Signed-off-by: Rickard Dybeck --- packages/catalog-client/api-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index 9fd9426a94..b1dd2b9188 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -51,7 +51,7 @@ export interface CatalogApi { options?: CatalogRequestOptions, ): Promise; getLocationByEntity( - entityRef: CompoundEntityRef, + entityRef: string | CompoundEntityRef, options?: CatalogRequestOptions, ): Promise; getLocationById( @@ -125,7 +125,7 @@ export class CatalogClient implements CatalogApi { options?: CatalogRequestOptions, ): Promise; getLocationByEntity( - entityRef: CompoundEntityRef, + entityRef: CompoundEntityRef | string, options?: CatalogRequestOptions, ): Promise; getLocationById( From 8e8a25dba58de9582a3dd3379e46791f628affab Mon Sep 17 00:00:00 2001 From: Andy Muldoon Date: Fri, 22 Dec 2023 13:58:07 +0000 Subject: [PATCH 014/468] Ability for Users to configure auth token expiration [19341] Signed-off-by: Andy Muldoon --- .changeset/spotty-kids-pay.md | 5 +++ plugins/auth-backend/README.md | 9 ++++++ plugins/auth-backend/config.d.ts | 6 ++++ plugins/auth-backend/package.json | 1 + .../auth-backend/src/service/router.test.ts | 31 ++++++++++++++++++- plugins/auth-backend/src/service/router.ts | 29 ++++++++++++++--- yarn.lock | 1 + 7 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 .changeset/spotty-kids-pay.md diff --git a/.changeset/spotty-kids-pay.md b/.changeset/spotty-kids-pay.md new file mode 100644 index 0000000000..199d0fdff0 --- /dev/null +++ b/.changeset/spotty-kids-pay.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +Ability for user to configure backstage token expiration diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index ce7525fb6b..e9dca26a5b 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -165,3 +165,12 @@ To try out SAML, you can use the mock identity provider: ## Links - [The Backstage homepage](https://backstage.io) + +## Configuring Token Expiration in App Config + +The expiration feature is not enabled unless you set this in your config file: + +``` +auth: + backstageTokenExpiration: { minutes: } +``` diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index 34139593d3..d24b2c64b7 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { HumanDuration } from '@backstage/types'; + export interface Config { /** Configuration options for the auth plugin */ auth?: { @@ -212,6 +214,10 @@ export interface Config { cfaccess?: { teamName: string; }; + /** + * The backstage token expiration. + */ + backstageTokenExpiration?: HumanDuration; }; }; } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index f05551736a..07d80ddabe 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -48,6 +48,7 @@ "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/types": "workspace:^", "@google-cloud/firestore": "^7.0.0", "@types/express": "^4.17.6", "@types/passport": "^1.0.3", diff --git a/plugins/auth-backend/src/service/router.test.ts b/plugins/auth-backend/src/service/router.test.ts index 3a1fde881e..fd339e349e 100644 --- a/plugins/auth-backend/src/service/router.test.ts +++ b/plugins/auth-backend/src/service/router.test.ts @@ -15,7 +15,11 @@ */ import { ConfigReader } from '@backstage/config'; -import { createOriginFilter } from './router'; +import { + createOriginFilter, + getDefaultBackstageTokenExpiryTime, +} from './router'; +import { BACKSTAGE_SESSION_EXPIRATION } from '../lib/session'; describe('Auth origin filtering', () => { const config = new ConfigReader({ @@ -52,3 +56,28 @@ describe('Auth origin filtering', () => { expect(createOriginFilter(config)(origin)).toBeTruthy(); }); }); + +describe('Test for default backstage token expiry time', () => { + it('Will return default backstage session expiration', () => { + const config = new ConfigReader({ + app: { + baseUrl: 'http://example.com/extra-path', + }, + }); + expect(getDefaultBackstageTokenExpiryTime(config)).toBe( + BACKSTAGE_SESSION_EXPIRATION, + ); + }); + + it('Will return user defined backstage session expiration', () => { + const config = new ConfigReader({ + app: { + baseUrl: 'http://example.com/extra-path', + }, + auth: { + backstageTokenExpiration: { minutes: 120 }, + }, + }); + expect(getDefaultBackstageTokenExpiryTime(config)).toBe(7200); + }); +}); diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 320870b3ee..eea858dd9c 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -29,7 +29,6 @@ import { } from '@backstage/backend-common'; import { assertError, NotFoundError } from '@backstage/errors'; import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; -import { Config } from '@backstage/config'; import { createOidcRouter, TokenFactory, KeyStores } from '../identity'; import session from 'express-session'; import connectSessionKnex from 'connect-session-knex'; @@ -41,6 +40,8 @@ import { BACKSTAGE_SESSION_EXPIRATION } from '../lib/session'; import { TokenIssuer } from '../identity/types'; import { StaticTokenIssuer } from '../identity/StaticTokenIssuer'; import { StaticKeyStore } from '../identity/StaticKeyStore'; +import { Config, readDurationFromConfig } from '@backstage/config'; +import { durationToMilliseconds } from '@backstage/types'; /** @public */ export type ProviderFactories = { [s: string]: AuthProviderFactory }; @@ -76,9 +77,8 @@ export async function createRouter( const appUrl = config.getString('app.baseUrl'); const authUrl = await discovery.getExternalBaseUrl('auth'); - + const backstageTokenExpiration = getDefaultBackstageTokenExpiryTime(config); const authDb = AuthDatabase.create(database); - const sessionExpirationSeconds = BACKSTAGE_SESSION_EXPIRATION; const keyStore = await KeyStores.fromConfig(config, { logger, @@ -91,7 +91,7 @@ export async function createRouter( { logger: logger.child({ component: 'token-factory' }), issuer: authUrl, - sessionExpirationSeconds: sessionExpirationSeconds, + sessionExpirationSeconds: backstageTokenExpiration, }, keyStore as StaticKeyStore, ); @@ -99,7 +99,7 @@ export async function createRouter( tokenIssuer = new TokenFactory({ issuer: authUrl, keyStore, - keyDurationSeconds: sessionExpirationSeconds, + keyDurationSeconds: backstageTokenExpiration, logger: logger.child({ component: 'token-factory' }), algorithm: tokenFactoryAlgorithm ?? @@ -249,3 +249,22 @@ export function createOriginFilter( return allowedOriginPatterns.some(pattern => pattern.match(origin)); }; } + +/** @internal */ +export function getDefaultBackstageTokenExpiryTime(config: Config) { + const processingIntervalKey = 'auth.backstageTokenExpiration'; + + if (!config.has(processingIntervalKey)) { + return BACKSTAGE_SESSION_EXPIRATION; + } + + const duration = readDurationFromConfig(config, { + key: processingIntervalKey, + }); + const seconds = Math.max( + 600, + Math.round(durationToMilliseconds(duration) / 1000), + ); + + return seconds; +} diff --git a/yarn.lock b/yarn.lock index edb50887c1..c0bb552222 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4859,6 +4859,7 @@ __metadata: "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" + "@backstage/types": "workspace:^" "@google-cloud/firestore": ^7.0.0 "@types/body-parser": ^1.19.0 "@types/cookie-parser": ^1.4.2 From 3bd2cc33c3be9da7d390f1db939f32ce8658c60a Mon Sep 17 00:00:00 2001 From: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:15:49 +0000 Subject: [PATCH 015/468] Update plugins/auth-backend/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> --- plugins/auth-backend/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index e9dca26a5b..3133266614 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -168,7 +168,7 @@ To try out SAML, you can use the mock identity provider: ## Configuring Token Expiration in App Config -The expiration feature is not enabled unless you set this in your config file: +If you need to change Backstage token expiration from the default value of one hour, set the following in your config file: ``` auth: From ab9c9eb77e6638b03176cb90f53da6ccaa59e8da Mon Sep 17 00:00:00 2001 From: Danyelle Amarante <90638175+Danyelleac@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:04:03 -0300 Subject: [PATCH 016/468] fix/textSize-fix value label text color Signed-off-by: Danyelle Amarante <90638175+Danyelleac@users.noreply.github.com> --- .changeset/fresh-gifts-smile.md | 5 +++++ .../techdocs-module-addons-contrib/src/TextSize/TextSize.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/fresh-gifts-smile.md diff --git a/.changeset/fresh-gifts-smile.md b/.changeset/fresh-gifts-smile.md new file mode 100644 index 0000000000..bde5edc4b1 --- /dev/null +++ b/.changeset/fresh-gifts-smile.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-module-addons-contrib': major +--- + +textsize-fix value label text color diff --git a/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx b/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx index 50cc59b814..167f9c7c7c 100644 --- a/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx +++ b/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx @@ -70,7 +70,7 @@ const StyledSlider = withStyles(theme => ({ left: '50%', transform: 'scale(1) translate(-50%, -5px) !important', '& *': { - color: theme.palette.common.black, + color: theme.palette.textSubtle, fontSize: theme.typography.caption.fontSize, background: 'transparent', }, From 987f565d855806a44c35f01985acfac8c4d685d1 Mon Sep 17 00:00:00 2001 From: Rutuja Marathe Date: Thu, 18 Jan 2024 22:45:57 -0500 Subject: [PATCH 017/468] feat(SearchResultListItems): Apply consistent styling Signed-off-by: Rutuja Marathe --- .changeset/eight-hounds-dream.md | 5 + .changeset/orange-walls-complain.md | 5 + .../src/search/AdrSearchResultListItem.tsx | 119 +++++++++--------- plugins/catalog/api-report.md | 2 + .../CatalogSearchResultListItem.tsx | 32 +++-- 5 files changed, 92 insertions(+), 71 deletions(-) create mode 100644 .changeset/eight-hounds-dream.md create mode 100644 .changeset/orange-walls-complain.md diff --git a/.changeset/eight-hounds-dream.md b/.changeset/eight-hounds-dream.md new file mode 100644 index 0000000000..8bf945d684 --- /dev/null +++ b/.changeset/eight-hounds-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-adr': patch +--- + +Fix alignment of text in `AdrSearchResultListItem`. Update size and font to match other `SearchResultListItem`. diff --git a/.changeset/orange-walls-complain.md b/.changeset/orange-walls-complain.md new file mode 100644 index 0000000000..477d42d913 --- /dev/null +++ b/.changeset/orange-walls-complain.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Add line clamping to `CatalogSearchResultListItem` diff --git a/plugins/adr/src/search/AdrSearchResultListItem.tsx b/plugins/adr/src/search/AdrSearchResultListItem.tsx index eebf74861d..f0c408df48 100644 --- a/plugins/adr/src/search/AdrSearchResultListItem.tsx +++ b/plugins/adr/src/search/AdrSearchResultListItem.tsx @@ -18,8 +18,6 @@ import React, { ReactNode } from 'react'; import { Box, Chip, - Divider, - ListItem, ListItemIcon, ListItemText, makeStyles, @@ -68,66 +66,63 @@ export function AdrSearchResultListItem(props: AdrSearchResultListItemProps) { if (!result) return null; return ( - <> - - {icon && {icon}} -
- - {highlight?.fields.title ? ( - - ) : ( - result.title - )} - - } - secondary={ - - {highlight?.fields.text ? ( - - ) : ( - result.text - )} - - } +
+ {icon && {icon}} +
+ + {highlight?.fields.title ? ( + + ) : ( + result.title + )} + + } + secondary={ + + {highlight?.fields.text ? ( + + ) : ( + result.text + )} + + } + /> + + - - - {result.status && ( - - )} - {result.date && ( - - )} - -
- - - + {result.status && ( + + )} + {result.date && } + +
+
); } diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index bef24b50d8..94d316d2b1 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -139,6 +139,8 @@ export interface CatalogSearchResultListItemProps { // (undocumented) icon?: ReactNode | ((result: IndexableDocument) => ReactNode); // (undocumented) + lineClamp?: number; + // (undocumented) rank?: number; // (undocumented) result?: IndexableDocument; diff --git a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx index 4c6b567d8a..f2ef05b128 100644 --- a/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx +++ b/plugins/catalog/src/components/CatalogSearchResultListItem/CatalogSearchResultListItem.tsx @@ -20,6 +20,7 @@ import { Chip, ListItemIcon, ListItemText, + Typography, makeStyles, } from '@material-ui/core'; import { Link } from '@backstage/core-components'; @@ -56,6 +57,7 @@ export interface CatalogSearchResultListItemProps { result?: IndexableDocument; highlight?: ResultHighlight; rank?: number; + lineClamp?: number; } /** @public */ @@ -94,15 +96,27 @@ export function CatalogSearchResultListItem( } secondary={ - highlight?.fields.text ? ( - - ) : ( - result.text - ) + + {highlight?.fields.text ? ( + + ) : ( + result.text + )} + } /> From 7f29dc4e541a5ad5fb4fba43dd7965fb6e9b2c70 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 19 Jan 2024 11:59:42 -0500 Subject: [PATCH 018/468] document VMware Cloud auth provider Signed-off-by: Jamie Klassen --- docs/auth/index.md | 1 + docs/auth/vmware-cloud/provider.md | 250 +++++++++++++++++++++++++++++ microsite/sidebars.json | 3 +- 3 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 docs/auth/vmware-cloud/provider.md diff --git a/docs/auth/index.md b/docs/auth/index.md index 806cda0ff7..f6282264e4 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -35,6 +35,7 @@ Backstage comes with many common authentication providers in the core library: - [Okta](okta/provider.md) - [OAuth 2 Custom Proxy](oauth2-proxy/provider.md) - [OneLogin](onelogin/provider.md) +- [VMware Cloud](vmware-cloud/provider.md) These built-in providers handle the authentication flow for a particular service including required scopes, callbacks, etc. These providers are each added to a diff --git a/docs/auth/vmware-cloud/provider.md b/docs/auth/vmware-cloud/provider.md new file mode 100644 index 0000000000..00a1315b6a --- /dev/null +++ b/docs/auth/vmware-cloud/provider.md @@ -0,0 +1,250 @@ +--- +id: provider +title: VMware Cloud Authentication Provider +sidebar_label: VMware Cloud +description: Adding VMware Cloud as an authentication provider in Backstage +--- + +Backstage comes with an auth provider module to allow users to sign-in with +their VMware Cloud account. This page describes some actions within the VMware +Cloud Console and within a Backstage app required to enable this capability. + +## Create an OAuth App in the VMware Cloud Console + +1. Log in to the [VMware Cloud Console](https://console.cloud.vmware.com). +1. Navigate to [Identity & Access Management > OAuth + Apps](https://console.cloud.vmware.com/csp/gateway/portal/#/consumer/usermgmt/oauth-apps) + and click the [Owned + Apps](https://console.cloud.vmware.com/csp/gateway/portal/#/consumer/usermgmt/oauth-apps/owned-apps/view) + tab -- if you are not an Organization Owner or Administrator but only a + Member, you will not see this nav entry unless the **Developer** check box is + selected for your role (see the [Organization roles and + permissions](https://docs.vmware.com/en/VMware-Cloud-services/services/Using-VMware-Cloud-Services/GUID-C11D3AAC-267C-4F16-A0E3-3EDF286EBE53.html#organization-roles-and-permissions-0) + docs for details). +1. Click **Create App**, choose 'Web/Mobile app' and click **Continue**. +1. Use default settings except: + - `App Name` and `App Description` of your choosing. + - `Redirect URIs`: `${baseUrl}/api/auth/vmwareCloudServices/handler/frame` + where `baseUrl` is the URL where your Backstage backend can be reached; + note that VMware Cloud does not support the combination of an `http://` + scheme and a `localhost` hostname, so when testing locally it may help to + set your backend base URL to `http://127.0.0.1:7007`. + - `Refresh Token`: check `Issue refresh token`; refresh tokens are required + to prevent forcing users to re-login when they refresh their browser. + - `Define Scopes`: check `OpenID` at the bottom. +1. Click **Create**. +1. Take note of the `App ID` in the resulting modal; this is the client ID to be + used by Backstage. + +## Install the provider in the backend + +### New backend system + +Apps using the [new backend system](../../backend-system/index.md), +can enable the VMware Cloud provider with a small modification like: + +```ts title="packages/backend-next/src/index.ts" +import { createBackend } from '@backstage/backend-defaults'; + +const backend = createBackend(); +backend.add(import('@backstage/plugin-auth-backend')); +/* highlight-add-start */ +backend.add( + import('@backstage/plugin-auth-backend-module-vmware-cloud-provider'), +); +/* highlight-add-end */ +backend.start(); +``` + +### Old backend system + +This provider was added after the migration of the auth-backend plugin to the +new backend system, so no default provider factory was added. Because of this, +the installation procedure for old-style backends is slightly more involved: + +```ts title="packages/backend/src/plugins/auth.ts" +import { + DEFAULT_NAMESPACE, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { + createRouter, + providers, + defaultAuthProviderFactories, +} from '@backstage/plugin-auth-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; +/* highlight-add-start */ +import { + commonSignInResolvers, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { + vmwareCloudAuthenticator, +} from '@backstage/plugin-auth-backend-module-vmware-cloud-provider'; +/* highlight-add-end */ + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + return await createRouter({ + logger: env.logger, + config: env.config, + database: env.database, + discovery: env.discovery, + tokenManager: env.tokenManager, + providerFactories: { + ...defaultAuthProviderFactories, + /* highlight-add-start */ + vmwareCloudServices: createOAuthProviderFactory({ + authenticator: vmwareCloudAuthenticator, + signInResolver: + commonSignInResolvers.emailLocalPartMatchingUserEntityName(), + }), + /* highlight-add-end */ +``` + +In the above, `commonSignInResolvers.emailLocalPartMatchingUserEntityName()` +can be replaced with a more suitable resolver for the app in question. + +## Configure Sign-in Resolution + +See [Sign-in Identities and Resolvers](../identity-resolver.md) for details. + +## Create a Utility API + +At the moment, this auth provider is not included in the default API factories, +but you can create one with a change to your frontend like: + +```ts title="packages/app/src/apis.ts" +import { + ScmIntegrationsApi, + scmIntegrationsApiRef, + ScmAuth, +} from '@backstage/integration-react'; +import { + costInsightsApiRef, + ExampleCostInsightsClient, +} from '@backstage/plugin-cost-insights'; +import { + graphQlBrowseApiRef, + GraphQLEndpoints, +} from '@backstage/plugin-graphiql'; +/* highlight-add-start */ +import { OAuth2 } from '@backstage/core-app-api'; +/* highlight-add-end */ +import { + AnyApiFactory, + /* highlight-add-start */ + ApiRef, + OpenIdConnectApi, + ProfileInfoApi, + BackstageIdentityApi, + SessionApi, + /* highlight-add-end */ + configApiRef, + /* highlight-add-start */ + createApiRef, + /* highlight-add-end */ + createApiFactory, + discoveryApiRef, + errorApiRef, + fetchApiRef, + githubAuthApiRef, + /* highlight-add-start */ + oauthRequestApiRef, + /* highlight-add-end */ +} from '@backstage/core-plugin-api'; +import { AuthProxyDiscoveryApi } from './AuthProxyDiscoveryApi'; + +/* highlight-add-start */ +export const vmwareCloudAuthApiRef: ApiRef< + OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +> = createApiRef({ + id: 'auth.vmware-cloud-auth-provider', +}); +/* highlight-add-end */ + +export const apis: AnyApiFactory[] = [ + /* highlight-add-start */ + createApiFactory({ + api: vmwareCloudAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => + OAuth2.create({ + discoveryApi, + oauthRequestApi, + provider: { + id: 'vmwareCloudServices', + title: 'VMware Cloud', + icon: () => null, + }, + environment: configApi.getOptionalString('auth.environment'), + defaultScopes: ['openid'], + }), + }), + /* highlight-add-end */ +``` + +## Add to Sign-in Page + +See the [Sign-In Configuration](../index.md#sign-in-configuration) docs for +general guidance, but as an example: + +```tsx title="packages/app/src/App.tsx" +/* highlight-add-start */ +import { vmwareCloudAuthApiRef } from './apis'; +import { SignInPage } from '@backstage/core-components'; +/* highlight-add-end */ + +const app = createApp({ + /* highlight-add-start */ + components: { + SignInPage: props => ( + + ), + }, + /* highlight-add-end */ + // .. +}); +``` + +## Configuration + +Add the following to your `app-config.yaml` under the root `auth` configuration: + +```yaml +auth: + session: + secret: your session secret + environment: development + providers: + vmwareCloudServices: + development: + clientId: ${APP_ID} + organizationId: ${ORG_ID} +``` + +where `APP_ID` refers to the ID retrieved when creating the OAuth App, and +`ORG_ID` is the [long ID of the +Organization](https://docs.vmware.com/en/VMware-Cloud-services/services/Using-VMware-Cloud-Services/GUID-CF9E9318-B811-48CF-8499-9419997DC1F8.html#view-the-organization-id-1) +in VMware Cloud for which you wish to enable sign-in. + +Note that VMware Cloud requires OAuth Apps to use +[PKCE](https://oauth.net/2/pkce/) when performing authorization code flows; the +library used by this provider requires the use of Express session middleware to +do this. Therefore the value `your session secret` under `auth.session.secret` +should be replaced with a long, complex and unique string which will act as a +key for signing session cookies set by Backstage. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 187c41ad75..4dda27ea8a 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -309,7 +309,8 @@ "auth/google/gcp-iap-auth", "auth/okta/provider", "auth/oauth2-proxy/provider", - "auth/onelogin/provider" + "auth/onelogin/provider", + "auth/vmware-cloud/provider" ] }, "auth/identity-resolver", From 214f2da8c17a28996a686341e7fb822dc720839a Mon Sep 17 00:00:00 2001 From: Karthikeyan Perumal <7823084+karthikeyanjp@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:58:16 -0600 Subject: [PATCH 019/468] fix(Error): resolve misleading HTTP status code 501 in Error page. Fixes #22159 Signed-off-by: Karthikeyan Perumal <7823084+karthikeyanjp@users.noreply.github.com> --- .changeset/soft-balloons-relax.md | 6 ++++++ packages/app-defaults/src/defaults/components.tsx | 2 +- packages/core-components/src/layout/ErrorPage/ErrorPage.tsx | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/soft-balloons-relax.md diff --git a/.changeset/soft-balloons-relax.md b/.changeset/soft-balloons-relax.md new file mode 100644 index 0000000000..3fae8984a3 --- /dev/null +++ b/.changeset/soft-balloons-relax.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/app-defaults': patch +--- + +Fix invalid HTTP status code 501 in Error Page diff --git a/packages/app-defaults/src/defaults/components.tsx b/packages/app-defaults/src/defaults/components.tsx index 93a5d9cd50..1e2fddca07 100644 --- a/packages/app-defaults/src/defaults/components.tsx +++ b/packages/app-defaults/src/defaults/components.tsx @@ -49,7 +49,7 @@ const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => { // TODO: figure out a nicer way to handle routing on the error page, when it can be done. return ( - + ); }; diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 73acf1f650..4639e8de59 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -24,7 +24,7 @@ import { useSupportConfig } from '../../hooks'; import { MicDrop } from './MicDrop'; interface IErrorPageProps { - status: string; + status?: string; statusMessage: string; additionalInfo?: React.ReactNode; supportUrl?: string; From a29a9f1f6c67946d3eb2e266d8c0e7baca9edcda Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Sat, 20 Jan 2024 20:47:55 -0500 Subject: [PATCH 020/468] [feat] add microsite accessibility workflow, add microsite lighthouse config Signed-off-by: Jithen Shriyan --- .../workflows/verify_accessibility-noop.yml | 2 +- .github/workflows/verify_accessibility.yml | 4 +- .../verify_microsite_accessibility-noop.yml | 31 +++++++++ .../verify_microsite_accessibility.yml | 40 +++++++++++ microsite/lighthouserc.js | 66 +++++++++++++++++++ package.json | 1 + 6 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/verify_microsite_accessibility-noop.yml create mode 100644 .github/workflows/verify_microsite_accessibility.yml create mode 100644 microsite/lighthouserc.js diff --git a/.github/workflows/verify_accessibility-noop.yml b/.github/workflows/verify_accessibility-noop.yml index 79c9962fce..dc356c8809 100644 --- a/.github/workflows/verify_accessibility-noop.yml +++ b/.github/workflows/verify_accessibility-noop.yml @@ -7,7 +7,7 @@ on: branches: [master] paths-ignore: - 'lighthouserc.js' - - '.github/workflows/verify_accessibility_core.yml' + - '.github/workflows/verify_accessibility.yml' - 'plugins/catalog/src/**' - 'plugins/catalog-react/src/**' - 'plugins/techdocs/src/**' diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 1310fc15f4..cbe66838fd 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -1,11 +1,11 @@ name: Accessibility on: - # NOTE: If you change these you must update verify_accessibility_core-noop.yml as well + # NOTE: If you change these you must update verify_accessibility-noop.yml as well pull_request: branches: [master] paths: - 'lighthouserc.js' - - '.github/workflows/verify_accessibility_core.yml' + - '.github/workflows/verify_accessibility.yml' - 'plugins/catalog/src/**' - 'plugins/catalog-react/src/**' - 'plugins/techdocs/src/**' diff --git a/.github/workflows/verify_microsite_accessibility-noop.yml b/.github/workflows/verify_microsite_accessibility-noop.yml new file mode 100644 index 0000000000..35a7b3e19d --- /dev/null +++ b/.github/workflows/verify_microsite_accessibility-noop.yml @@ -0,0 +1,31 @@ +# NO-OP placeholder that always passes for other paths +# This is here so that we're able to set the status check as required + +name: Microsite Accessibility +on: + pull_request: + branches: [master] + paths-ignore: + - '.github/workflows/verify_microsite_accessibility.yml' + - 'microsite/scripts/**' + - 'microsite/src/**' + - 'microsite/data/**' + - 'microsite/blog/**' + - 'microsite/static/**' + - 'beps/**' + - 'mkdocs.yml' + - 'docs/**' +permissions: + contents: read + +jobs: + noop: + name: Microsite Accessibility + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + with: + egress-policy: audit + + - run: echo NOOP diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml new file mode 100644 index 0000000000..482d8270bd --- /dev/null +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -0,0 +1,40 @@ +name: Microsite Accessibility +on: + # NOTE: If you change these you must update verify_microsite_accessibility-noop.yml as well + pull_request: + branches: [master] + paths: + - '.github/workflows/verify_microsite_accessibility.yml' + - 'microsite/**' + - 'beps/**' + - 'mkdocs.yml' + - 'docs/**' +jobs: + lhci: + name: Microsite Accessibility + runs-on: ubuntu-latest + steps: + - name: Harden Runner + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + with: + egress-policy: audit + + - uses: actions/checkout@v4.1.1 + + - name: Use Node.js 18.x + uses: actions/setup-node@v4.0.1 + with: + node-version: 18.x + + - name: top-level install + run: yarn install --immutable + + - name: yarn install + run: yarn install --immutable + working-directory: microsite + + - name: run Lighthouse CI + run: | + yarn dlx @lhci/cli@0.11.x --config=microsite/lighthouserc.js autorun + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/microsite/lighthouserc.js b/microsite/lighthouserc.js new file mode 100644 index 0000000000..a980a53287 --- /dev/null +++ b/microsite/lighthouserc.js @@ -0,0 +1,66 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var sidebars = require('./sidebars.json') + +module.exports = { + ci: { + collect: { + url: [ + /** Home */ + 'http://localhost:3000', + /** Docs - Getting Started */ + 'http://localhost:3000/docs/getting-started', + /** Docs - Software Catalog */ + 'http://localhost:3000/docs/features/software-catalog', + /** Docs - Create a Plugin */ + 'http://localhost:3000/docs/plugins/create-a-plugin', + /** Docs - Designing for Backstage */ + 'http://localhost:3000/docs/dls/design', + /** Blog */ + 'http://localhost:3000/blog', + /** Plugins */ + 'http://localhost:3000/plugins', + /** Demos */ + 'http://localhost:3000/demos', + /** Community */ + 'http://localhost:3000/community', + /** Releases */ + ...(sidebars.releases['Release Notes'].map((path) => `http://localhost:3000/docs/${path}`)), + ], + settings: { + onlyCategories: ['accessibility'], + output: ['html', 'json'], + outputPath: './.lighthouseci/reports', + preset: 'desktop', + }, + // refers to root package scripts + startServerCommand: 'yarn run start:microsite', + startServerReadyPattern: 'compiled successfully', + startServerReadyTimeout: 600000, + numberOfRuns: 1, + }, + assert: { + assertions: { + 'categories:performance': 'off', + 'categories:pwa': 'off', + 'categories:best-practices': 'off', + 'categories:seo': 'off', + 'categories:accessibility': ['error', { minScore: 0.95 }], + }, + }, + }, +}; diff --git a/package.json b/package.json index 483e02fdb5..d8d49a0b1d 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "start-backend": "yarn workspace example-backend start", "start:next": "yarn workspace example-app-next start", "start-backend:next": "yarn workspace example-backend-next start", + "start:microsite": "cd microsite/ && yarn start", "build:backend": "yarn workspace example-backend build", "build:all": "backstage-cli repo build --all", "build:api-reports": "yarn build:api-reports:only --tsc", From d53ff7c1b6f67ca389eb37ff68dfb6cf87ca379d Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Sun, 21 Jan 2024 10:33:43 -0500 Subject: [PATCH 021/468] [style] format Signed-off-by: Jithen Shriyan --- .../verify_microsite_accessibility.yml | 2 +- microsite/lighthouserc.js | 94 ++++++++++--------- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml index 482d8270bd..683fe7840e 100644 --- a/.github/workflows/verify_microsite_accessibility.yml +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -37,4 +37,4 @@ jobs: run: | yarn dlx @lhci/cli@0.11.x --config=microsite/lighthouserc.js autorun env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/microsite/lighthouserc.js b/microsite/lighthouserc.js index a980a53287..f0024ee0b3 100644 --- a/microsite/lighthouserc.js +++ b/microsite/lighthouserc.js @@ -14,53 +14,55 @@ * limitations under the License. */ -var sidebars = require('./sidebars.json') +var sidebars = require('./sidebars.json'); module.exports = { - ci: { - collect: { - url: [ - /** Home */ - 'http://localhost:3000', - /** Docs - Getting Started */ - 'http://localhost:3000/docs/getting-started', - /** Docs - Software Catalog */ - 'http://localhost:3000/docs/features/software-catalog', - /** Docs - Create a Plugin */ - 'http://localhost:3000/docs/plugins/create-a-plugin', - /** Docs - Designing for Backstage */ - 'http://localhost:3000/docs/dls/design', - /** Blog */ - 'http://localhost:3000/blog', - /** Plugins */ - 'http://localhost:3000/plugins', - /** Demos */ - 'http://localhost:3000/demos', - /** Community */ - 'http://localhost:3000/community', - /** Releases */ - ...(sidebars.releases['Release Notes'].map((path) => `http://localhost:3000/docs/${path}`)), - ], - settings: { - onlyCategories: ['accessibility'], - output: ['html', 'json'], - outputPath: './.lighthouseci/reports', - preset: 'desktop', - }, - // refers to root package scripts - startServerCommand: 'yarn run start:microsite', - startServerReadyPattern: 'compiled successfully', - startServerReadyTimeout: 600000, - numberOfRuns: 1, - }, - assert: { - assertions: { - 'categories:performance': 'off', - 'categories:pwa': 'off', - 'categories:best-practices': 'off', - 'categories:seo': 'off', - 'categories:accessibility': ['error', { minScore: 0.95 }], - }, - }, + ci: { + collect: { + url: [ + /** Home */ + 'http://localhost:3000', + /** Docs - Getting Started */ + 'http://localhost:3000/docs/getting-started', + /** Docs - Software Catalog */ + 'http://localhost:3000/docs/features/software-catalog', + /** Docs - Create a Plugin */ + 'http://localhost:3000/docs/plugins/create-a-plugin', + /** Docs - Designing for Backstage */ + 'http://localhost:3000/docs/dls/design', + /** Blog */ + 'http://localhost:3000/blog', + /** Plugins */ + 'http://localhost:3000/plugins', + /** Demos */ + 'http://localhost:3000/demos', + /** Community */ + 'http://localhost:3000/community', + /** Releases */ + ...sidebars.releases['Release Notes'].map( + path => `http://localhost:3000/docs/${path}`, + ), + ], + settings: { + onlyCategories: ['accessibility'], + output: ['html', 'json'], + outputPath: './.lighthouseci/reports', + preset: 'desktop', + }, + // refers to root package scripts + startServerCommand: 'yarn run start:microsite', + startServerReadyPattern: 'compiled successfully', + startServerReadyTimeout: 600000, + numberOfRuns: 1, }, + assert: { + assertions: { + 'categories:performance': 'off', + 'categories:pwa': 'off', + 'categories:best-practices': 'off', + 'categories:seo': 'off', + 'categories:accessibility': ['error', { minScore: 0.95 }], + }, + }, + }, }; From d00c36df6d0b712a1dd0de5bb173908745eb105b Mon Sep 17 00:00:00 2001 From: Danyelle AC <90638175+Danyelleac@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:40:50 -0300 Subject: [PATCH 022/468] Update .changeset/fresh-gifts-smile.md Co-authored-by: Patrik Oldsberg Signed-off-by: Danyelle AC <90638175+Danyelleac@users.noreply.github.com> --- .changeset/fresh-gifts-smile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fresh-gifts-smile.md b/.changeset/fresh-gifts-smile.md index bde5edc4b1..ffa680bb8e 100644 --- a/.changeset/fresh-gifts-smile.md +++ b/.changeset/fresh-gifts-smile.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-techdocs-module-addons-contrib': major +'@backstage/plugin-techdocs-module-addons-contrib': patch --- textsize-fix value label text color From ff3a604dae3157b26f2b618411d8d860554d583e Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 18:00:01 -0500 Subject: [PATCH 023/468] feat(repo-tools): Update OpenAPI command naming. fixes: https://github.com/backstage/backstage/issues/21790 Signed-off-by: Aramis --- docs/openapi/01-getting-started.md | 6 +- packages/repo-tools/src/commands/index.ts | 99 +++++++++++++------ .../schema/openapi/generate/client.ts} | 35 ++++--- .../schema/openapi/generate/server.ts} | 57 +++-------- .../test => package/schema/openapi}/init.ts | 10 +- .../{ => repo/schema}/openapi/lint.ts | 25 ++--- .../index.ts => repo/schema/openapi/test.ts} | 16 +-- .../schema => repo/schema/openapi}/verify.ts | 20 ++-- .../{commands => lib}/openapi/constants.ts | 0 .../repo-tools/src/lib/openapi/helpers.ts | 32 ++++++ .../src/{commands/openapi => lib}/runner.ts | 4 +- plugins/catalog-backend/package.json | 5 +- plugins/search-backend/package.json | 3 +- plugins/todo-backend/package.json | 4 +- yarn.lock | 4 +- 15 files changed, 195 insertions(+), 125 deletions(-) rename packages/repo-tools/src/commands/{openapi/client/generate.ts => package/schema/openapi/generate/client.ts} (73%) rename packages/repo-tools/src/commands/{openapi/schema/generate.ts => package/schema/openapi/generate/server.ts} (60%) rename packages/repo-tools/src/commands/{openapi/test => package/schema/openapi}/init.ts (90%) rename packages/repo-tools/src/commands/{ => repo/schema}/openapi/lint.ts (83%) rename packages/repo-tools/src/commands/{openapi/test/index.ts => repo/schema/openapi/test.ts} (86%) rename packages/repo-tools/src/commands/{openapi/schema => repo/schema/openapi}/verify.ts (79%) rename packages/repo-tools/src/{commands => lib}/openapi/constants.ts (100%) create mode 100644 packages/repo-tools/src/lib/openapi/helpers.ts rename packages/repo-tools/src/{commands/openapi => lib}/runner.ts (94%) diff --git a/docs/openapi/01-getting-started.md b/docs/openapi/01-getting-started.md index eb3b1c1209..ce04e75107 100644 --- a/docs/openapi/01-getting-started.md +++ b/docs/openapi/01-getting-started.md @@ -45,7 +45,7 @@ You should create a new folder, `src/schema` in your backend plugin to store you ## Generating a typed express router from a spec -Run `yarn backstage-repo-tools schema openapi generate `. This will create an `openapi.generated.ts` file in the `src/schema` directory that contains the OpenAPI schema as well as a generated express router with types. +Run `yarn backstage-repo-tools package schema openapi generate server `. This will create an `openapi.generated.ts` file in the `src/schema` directory that contains the OpenAPI schema as well as a generated express router with types. You should add this command to your `package.json` for future use. Use it like so, update your `router.ts` or `createRouter.ts` file with the following content, @@ -63,7 +63,7 @@ export async function createRouter( ## Generating a typed client from a spec -Run `yarn backstage-repo-tools schema openapi generate-client --input-spec /src/schema/openapi.yaml --output-directory `. `` should match the same backend plugin we've been using so far. `` is a new directory and npm package that you should create. The general pattern is `plugins/-client`. +From your current backend plugin directory, run `yarn backstage-repo-tools package schema openapi generate client --output-package `. `` is a new directory and npm package that you should create. The general pattern is `plugins/-client` or if you want to co-locate this with your other shared types, use `plugins/-common`. You should add this command to your `package.json` for future use. The generated client will have a directory `src/generated` that exports a `DefaultApiClient` class and all generated types. You can use the client like so, @@ -108,7 +108,7 @@ describe('createRouter', () => { + app = wrapInOpenApiTestServer(express().use(router)); ``` -This adds a wrapper around the express server that allows it to reroute traffic for `supertest`. Run `yarn backstage-repo-tools schema openapi init` to create some required config files. Now, when you run `yarn backstage-repo-tools schema openapi test` your schema will now be tested against your test data. Any errors will be reported. +This adds a wrapper around the express server that allows it to reroute traffic for `supertest`. Run `yarn backstage-repo-tools package schema openapi init` to create some required files. Now, when you run `yarn backstage-repo-tools repo schema openapi test` your schema will now be tested against your test data. Any errors will be reported. Our command is a small wrapper over [`Optic`](https://github.com/opticdev/optic) which does all of the heavy lifting. diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index d9c6d892f5..06ea2fa95c 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -18,12 +18,71 @@ import { assertError } from '@backstage/errors'; import { Command } from 'commander'; import { exitWithError } from '../lib/errors'; -function registerSchemaCommand(program: Command) { +function registerPackageCommand(program: Command) { const command = program + .command('package [command]') + .description('Various tools for working with specific packages.'); + + const schemaCommand = command + .command('schema [command]') + .description( + "Various tools for working with specific packages' API schema", + ); + + const openApiCommand = schemaCommand + .command('openapi [command]') + .description('Tooling for OpenAPI schema'); + + openApiCommand + .command('init') + .description('Initialize any required files to use the OpenAPI tooling.') + .action( + lazy(() => import('./package/schema/openapi/init').then(m => m.default)), + ); + + const generateCommand = openApiCommand + .command('generate [command]') + .description( + 'Commands for generating various things from an OpenAPI spec.', + ); + + generateCommand + .command('server') + .description( + 'Generates an express server stub using the OpenAPI schema for typings.', + ) + .action( + lazy(() => + import('./package/schema/openapi/generate/server').then(m => m.command), + ), + ); + + generateCommand + .command('client') + .description( + 'Generates a client that can interact with your backend plugin using types from your OpenAPI schema.', + ) + .requiredOption( + '--output-package ', + 'Top-level path to where the client should be generated, ie packages/catalog-client.', + ) + .action( + lazy(() => + import('./package/schema/openapi/generate/client').then(m => m.command), + ), + ); +} + +function registerRepoCommand(program: Command) { + const command = program + .command('repo [command]') + .description('Tools for working across your entire repository.'); + + const schemaCommand = command .command('schema [command]') .description('Various tools for working with API schema'); - const openApiCommand = command + const openApiCommand = schemaCommand .command('openapi [command]') .description('Tooling for OpenApi schema'); @@ -33,16 +92,9 @@ function registerSchemaCommand(program: Command) { 'Verify that all OpenAPI schemas are valid and have a matching `schemas/openapi.generated.ts` file.', ) .action( - lazy(() => import('./openapi/schema/verify').then(m => m.bulkCommand)), - ); - - openApiCommand - .command('generate [paths...]') - .description( - 'Generates a Typescript file from an OpenAPI yaml spec. For use with the `@backstage/backend-openapi-utils` ApiRouter type.', - ) - .action( - lazy(() => import('./openapi/schema/generate').then(m => m.bulkCommand)), + lazy(() => + import('./repo/schema/openapi/verify').then(m => m.bulkCommand), + ), ); openApiCommand @@ -52,27 +104,16 @@ function registerSchemaCommand(program: Command) { '--strict', 'Fail on any linting severity messages, not just errors.', ) - .action(lazy(() => import('./openapi/lint').then(m => m.bulkCommand))); + .action( + lazy(() => import('./repo/schema/openapi/lint').then(m => m.bulkCommand)), + ); openApiCommand .command('test [paths...]') .description('Test OpenAPI schemas against written tests') .option('--update', 'Update the spec on failure.') - .action(lazy(() => import('./openapi/test').then(m => m.bulkCommand))); - - openApiCommand - .command('init ') - .description('Creates any config needed for the test command.') - .action(lazy(() => import('./openapi/test/init').then(m => m.default))); - - openApiCommand - .command('generate-client') - .requiredOption('--input-spec ') - .requiredOption('--output-directory ') .action( - lazy(() => - import('./openapi/client/generate').then(m => m.singleCommand), - ), + lazy(() => import('./repo/schema/openapi/test').then(m => m.bulkCommand)), ); } @@ -139,8 +180,8 @@ export function registerCommands(program: Command) { ), ), ); - - registerSchemaCommand(program); + registerPackageCommand(program); + registerRepoCommand(program); } // Wraps an action function so that it always exits and handles errors diff --git a/packages/repo-tools/src/commands/openapi/client/generate.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts similarity index 73% rename from packages/repo-tools/src/commands/openapi/client/generate.ts rename to packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts index f827f1005f..101c2c918b 100644 --- a/packages/repo-tools/src/commands/openapi/client/generate.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts @@ -16,16 +16,23 @@ import chalk from 'chalk'; import { resolve } from 'path'; -import { OPENAPI_IGNORE_FILES, OUTPUT_PATH } from '../constants'; -import { paths as cliPaths } from '../../../lib/paths'; +import { + OPENAPI_IGNORE_FILES, + OUTPUT_PATH, +} from '../../../../../lib/openapi/constants'; +import { paths as cliPaths } from '../../../../../lib/paths'; import { mkdirpSync } from 'fs-extra'; import fs from 'fs-extra'; -import { exec } from '../../../lib/exec'; +import { exec } from '../../../../../lib/exec'; import { resolvePackagePath } from '@backstage/backend-common'; +import { getPathToCurrentOpenApiSpec } from '../../../../../lib/openapi/helpers'; -async function generate(spec: string, outputDirectory: string) { - const resolvedOpenapiPath = resolve(spec); - const resolvedOutputDirectory = resolve(outputDirectory, OUTPUT_PATH); +async function generate(outputDirectory: string) { + const resolvedOpenapiPath = await getPathToCurrentOpenApiSpec(); + const resolvedOutputDirectory = cliPaths.resolveTargetRoot( + outputDirectory, + OUTPUT_PATH, + ); mkdirpSync(resolvedOutputDirectory); await fs.mkdirp(resolvedOutputDirectory); @@ -80,19 +87,19 @@ async function generate(spec: string, outputDirectory: string) { }); } -export async function singleCommand({ - inputSpec, - outputDirectory, +export async function command({ + outputPackage, }: { - inputSpec: string; - outputDirectory: string; + outputPackage: string; }): Promise { try { - await generate(inputSpec, outputDirectory); - console.log(chalk.green(`Generated client for ${inputSpec}`)); + await generate(outputPackage); + console.log( + chalk.green(`Generated client in ${outputPackage}/${OUTPUT_PATH}`), + ); } catch (err) { console.log(); - console.log(chalk.red(`Client generation failed in ${outputDirectory}:`)); + console.log(chalk.red(`Client generation failed:`)); console.log(err); process.exit(1); diff --git a/packages/repo-tools/src/commands/openapi/schema/generate.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts similarity index 60% rename from packages/repo-tools/src/commands/openapi/schema/generate.ts rename to packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts index cd9f572f2d..e6c56c3122 100644 --- a/packages/repo-tools/src/commands/openapi/schema/generate.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts @@ -17,30 +17,19 @@ import fs from 'fs-extra'; import YAML from 'js-yaml'; import chalk from 'chalk'; -import { resolve } from 'path'; -import { paths as cliPaths } from '../../../lib/paths'; -import { runner } from '../runner'; -import { TS_SCHEMA_PATH, YAML_SCHEMA_PATH } from '../constants'; +import { paths as cliPaths } from '../../../../../lib/paths'; +import { TS_SCHEMA_PATH } from '../../../../../lib/openapi/constants'; import { promisify } from 'util'; import { exec as execCb } from 'child_process'; +import { getPathToCurrentOpenApiSpec } from '../../../../../lib/openapi/helpers'; const exec = promisify(execCb); -async function generate( - directoryPath: string, - config?: { skipMissingYamlFile: boolean }, -) { - const { skipMissingYamlFile } = config ?? {}; - const openapiPath = resolve(directoryPath, YAML_SCHEMA_PATH); - if (!(await fs.pathExists(openapiPath))) { - if (skipMissingYamlFile) { - return; - } - throw new Error(`Could not find ${YAML_SCHEMA_PATH} in root of directory.`); - } +async function generate() { + const openapiPath = await getPathToCurrentOpenApiSpec(); const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8')); - const tsPath = resolve(directoryPath, TS_SCHEMA_PATH); + const tsPath = cliPaths.resolveTarget(TS_SCHEMA_PATH); // The first set of comment slashes allow for the eslint notice plugin to run // with onNonMatchingHeader: 'replace', as is the case in the open source @@ -63,33 +52,19 @@ export const createOpenApiRouter = async ( await exec(`yarn backstage-cli package lint --fix ${tsPath}`); if (await cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) { - await exec(`yarn prettier --write ${tsPath}`); + await exec(`yarn prettier --write ${tsPath}`, { + cwd: cliPaths.targetRoot, + }); } } -export async function bulkCommand(paths: string[] = []): Promise { - const resultsList = await runner(paths, (dir: string) => - generate(dir, { skipMissingYamlFile: true }), - ); - - let failed = false; - for (const { relativeDir, resultText } of resultsList) { - if (resultText) { - console.log(); - console.log( - chalk.red( - `OpenAPI yaml to Typescript generation failed in ${relativeDir}:`, - ), - ); - console.log(resultText.trimStart()); - - failed = true; - } - } - - if (failed) { - process.exit(1); - } else { +export async function command(): Promise { + try { + await generate(); console.log(chalk.green('Generated all files.')); + } catch (err) { + console.log(chalk.red(`OpenAPI server stub generation failed.`)); + console.log(err.message); + process.exit(1); } } diff --git a/packages/repo-tools/src/commands/openapi/test/init.ts b/packages/repo-tools/src/commands/package/schema/openapi/init.ts similarity index 90% rename from packages/repo-tools/src/commands/openapi/test/init.ts rename to packages/repo-tools/src/commands/package/schema/openapi/init.ts index a38416aabb..56136f8882 100644 --- a/packages/repo-tools/src/commands/openapi/test/init.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/init.ts @@ -15,12 +15,12 @@ */ import fs from 'fs-extra'; import { join } from 'path'; -import { YAML_SCHEMA_PATH } from './../constants'; +import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; -import { paths as cliPaths } from '../../../lib/paths'; -import { runner } from '../runner'; +import { paths as cliPaths } from '../../../../lib/paths'; +import { runner } from '../../../../lib/runner'; import chalk from 'chalk'; -import { exec } from '../../../lib/exec'; +import { exec } from '../../../../lib/exec'; const ROUTER_TEST_PATHS = [ 'src/service/router.test.ts', @@ -31,7 +31,7 @@ async function init(directoryPath: string) { const openapiPath = join(directoryPath, YAML_SCHEMA_PATH); if (!(await fs.pathExists(openapiPath))) { throw new Error( - `You do not have an OpenAPI YAML file at ${openapiPath}. Please create one and retry this command. If you already have existing test cases for your router, see 'backstage-repo-tools schema openapi test --update'`, + `You do not have an OpenAPI YAML file at ${openapiPath}. Please create one and retry this command. If you already have existing test cases for your router, see 'backstage-repo-tools package schema openapi test --update'`, ); } const opticConfigFilePath = join(directoryPath, 'optic.yml'); diff --git a/packages/repo-tools/src/commands/openapi/lint.ts b/packages/repo-tools/src/commands/repo/schema/openapi/lint.ts similarity index 83% rename from packages/repo-tools/src/commands/openapi/lint.ts rename to packages/repo-tools/src/commands/repo/schema/openapi/lint.ts index 8aac0189b1..eacb3ac50d 100644 --- a/packages/repo-tools/src/commands/openapi/lint.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/lint.ts @@ -24,24 +24,19 @@ import { Yaml } from '@stoplight/spectral-parsers'; import ruleset from '@apisyouwonthate/style-guide'; import fs from 'fs-extra'; import chalk from 'chalk'; -import { resolve } from 'path'; -import { runner } from './runner'; -import { YAML_SCHEMA_PATH } from './constants'; +import { runner } from '../../../../lib/runner'; import { oas } from '@stoplight/spectral-rulesets'; import { DiagnosticSeverity } from '@stoplight/types'; import { pretty } from '@stoplight/spectral-formatters'; +import { getPathToOpenApiSpec } from '../../../../lib/openapi/helpers'; -async function lint( - directoryPath: string, - config?: { skipMissingYamlFile: boolean; strict: boolean }, -) { - const { skipMissingYamlFile, strict } = config ?? {}; - const openapiPath = resolve(directoryPath, YAML_SCHEMA_PATH); - if (!(await fs.pathExists(openapiPath))) { - if (skipMissingYamlFile) { - return; - } - throw new Error(`Could not find a file at ${openapiPath}.`); +async function lint(directoryPath: string, config?: { strict: boolean }) { + const { strict } = config ?? {}; + let openapiPath = ''; + try { + openapiPath = await getPathToOpenApiSpec(directoryPath); + } catch { + return; } const openapiFileContent = await fs.readFile(openapiPath, 'utf8'); @@ -90,7 +85,7 @@ export async function bulkCommand( options: { strict?: boolean }, ): Promise { const resultsList = await runner(paths, (dir: string) => - lint(dir, { skipMissingYamlFile: true, strict: !!options.strict }), + lint(dir, { strict: !!options.strict }), ); let failed = false; diff --git a/packages/repo-tools/src/commands/openapi/test/index.ts b/packages/repo-tools/src/commands/repo/schema/openapi/test.ts similarity index 86% rename from packages/repo-tools/src/commands/openapi/test/index.ts rename to packages/repo-tools/src/commands/repo/schema/openapi/test.ts index 47b553aaef..833de181e3 100644 --- a/packages/repo-tools/src/commands/openapi/test/index.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/test.ts @@ -17,18 +17,22 @@ import fs from 'fs-extra'; import { join } from 'path'; import chalk from 'chalk'; -import { runner } from '../runner'; -import { YAML_SCHEMA_PATH } from '../constants'; -import { paths as cliPaths } from '../../../lib/paths'; -import { exec } from '../../../lib/exec'; +import { runner } from '../../../../lib/runner'; +import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; +import { paths as cliPaths } from '../../../../lib/paths'; +import { exec } from '../../../../lib/exec'; +import { getPathToOpenApiSpec } from '../../../../lib/openapi/helpers'; async function test( directoryPath: string, { port }: { port: number }, options?: { update?: boolean }, ) { - const openapiPath = join(directoryPath, YAML_SCHEMA_PATH); - if (!(await fs.pathExists(openapiPath))) { + let openapiPath = join(directoryPath, YAML_SCHEMA_PATH); + try { + openapiPath = await getPathToOpenApiSpec(directoryPath); + } catch { + // OpenAPI schema doesn't exist. return; } const opticConfigFilePath = join(directoryPath, 'optic.yml'); diff --git a/packages/repo-tools/src/commands/openapi/schema/verify.ts b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts similarity index 79% rename from packages/repo-tools/src/commands/openapi/schema/verify.ts rename to packages/repo-tools/src/commands/repo/schema/openapi/verify.ts index 7120dd67ad..442494610c 100644 --- a/packages/repo-tools/src/commands/openapi/schema/verify.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts @@ -21,13 +21,21 @@ import { join } from 'path'; import chalk from 'chalk'; import { relative as relativePath, resolve as resolvePath } from 'path'; import Parser from '@apidevtools/swagger-parser'; -import { runner } from '../runner'; -import { paths as cliPaths } from '../../../lib/paths'; -import { TS_MODULE, TS_SCHEMA_PATH, YAML_SCHEMA_PATH } from '../constants'; +import { runner } from '../../../../lib/runner'; +import { paths as cliPaths } from '../../../../lib/paths'; +import { + TS_MODULE, + TS_SCHEMA_PATH, + YAML_SCHEMA_PATH, +} from '../../../../lib/openapi/constants'; +import { getPathToOpenApiSpec } from '../../../../lib/openapi/helpers'; async function verify(directoryPath: string) { - const openapiPath = join(directoryPath, YAML_SCHEMA_PATH); - if (!(await fs.pathExists(openapiPath))) { + let openapiPath = ''; + try { + openapiPath = await getPathToOpenApiSpec(directoryPath); + } catch { + // Unable to find spec at path. return; } @@ -47,7 +55,7 @@ async function verify(directoryPath: string) { if (!isEqual(schema.spec, yaml)) { const path = relativePath(cliPaths.targetRoot, directoryPath); throw new Error( - `\`${YAML_SCHEMA_PATH}\` and \`${TS_SCHEMA_PATH}\` do not match. Please run \`yarn backstage-repo-tools schema openapi generate ${path}\` to regenerate \`${TS_SCHEMA_PATH}\`.`, + `\`${YAML_SCHEMA_PATH}\` and \`${TS_SCHEMA_PATH}\` do not match. Please run \`yarn backstage-repo-tools package schema openapi generate\` from '${path}' to regenerate \`${TS_SCHEMA_PATH}\`.`, ); } } diff --git a/packages/repo-tools/src/commands/openapi/constants.ts b/packages/repo-tools/src/lib/openapi/constants.ts similarity index 100% rename from packages/repo-tools/src/commands/openapi/constants.ts rename to packages/repo-tools/src/lib/openapi/constants.ts diff --git a/packages/repo-tools/src/lib/openapi/helpers.ts b/packages/repo-tools/src/lib/openapi/helpers.ts new file mode 100644 index 0000000000..5a5932c802 --- /dev/null +++ b/packages/repo-tools/src/lib/openapi/helpers.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2024 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 { pathExists } from 'fs-extra'; +import { paths } from '../paths'; +import { YAML_SCHEMA_PATH } from './constants'; +import { resolve } from 'path'; + +export const getPathToOpenApiSpec = async (directory: string) => { + const openapiPath = resolve(directory, YAML_SCHEMA_PATH); + if (!(await pathExists(openapiPath))) { + throw new Error(`Could not find ${YAML_SCHEMA_PATH}.`); + } + return openapiPath; +}; + +export const getPathToCurrentOpenApiSpec = async () => { + return await getPathToOpenApiSpec(paths.targetDir); +}; diff --git a/packages/repo-tools/src/commands/openapi/runner.ts b/packages/repo-tools/src/lib/runner.ts similarity index 94% rename from packages/repo-tools/src/commands/openapi/runner.ts rename to packages/repo-tools/src/lib/runner.ts index cc354daaaa..b9700cd09b 100644 --- a/packages/repo-tools/src/commands/openapi/runner.ts +++ b/packages/repo-tools/src/lib/runner.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { resolvePackagePaths } from '../../lib/paths'; +import { resolvePackagePaths } from './paths'; import pLimit from 'p-limit'; import { relative as relativePath } from 'path'; -import { paths as cliPaths } from '../../lib/paths'; +import { paths as cliPaths } from './paths'; import portFinder from 'portfinder'; export async function runner( diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 42f40cccd2..eee268736a 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -42,7 +42,9 @@ "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean" + "clean": "backstage-cli package clean", + "generate-server": "backstage-repo-tools package schema openapi generate server", + "generate-client": "backstage-repo-tools package schema openapi generate client --output-package packages/catalog-client" }, "dependencies": { "@backstage/backend-common": "workspace:^", @@ -61,6 +63,7 @@ "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-permission-node": "workspace:^", "@backstage/plugin-search-backend-module-catalog": "workspace:^", + "@backstage/repo-tools": "workspace:^", "@backstage/types": "workspace:^", "@opentelemetry/api": "^1.3.0", "@types/express": "^4.17.6", diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index bea288f6b2..b328083246 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -39,7 +39,8 @@ "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", - "clean": "backstage-cli package clean" + "clean": "backstage-cli package clean", + "generate-server": "backstage-repo-tools package schema openapi generate server" }, "dependencies": { "@backstage/backend-common": "workspace:^", diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index ddf3baac3d..75a070e284 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -26,7 +26,8 @@ "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", "clean": "backstage-cli package clean", - "start": "backstage-cli package start" + "start": "backstage-cli package start", + "generate-server": "backstage-repo-tools package schema openapi generate server" }, "dependencies": { "@backstage/backend-common": "workspace:^", @@ -38,6 +39,7 @@ "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/repo-tools": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "leasot": "^12.0.0", diff --git a/yarn.lock b/yarn.lock index 33ac9d7aab..ef01464078 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5560,6 +5560,7 @@ __metadata: "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-permission-node": "workspace:^" "@backstage/plugin-search-backend-module-catalog": "workspace:^" + "@backstage/repo-tools": "workspace:^" "@backstage/types": "workspace:^" "@opentelemetry/api": ^1.3.0 "@types/core-js": ^2.5.4 @@ -9350,6 +9351,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" + "@backstage/repo-tools": "workspace:^" "@types/express": ^4.17.6 "@types/supertest": ^2.0.8 express: ^4.17.1 @@ -9550,7 +9552,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/repo-tools@workspace:*, @backstage/repo-tools@workspace:packages/repo-tools": +"@backstage/repo-tools@workspace:*, @backstage/repo-tools@workspace:^, @backstage/repo-tools@workspace:packages/repo-tools": version: 0.0.0-use.local resolution: "@backstage/repo-tools@workspace:packages/repo-tools" dependencies: From 4c62935c8e6e17dd48d9d53a97e5c7da5473dd7a Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 18:09:49 -0500 Subject: [PATCH 024/468] add changeset Signed-off-by: Aramis --- .changeset/lovely-bugs-prove.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .changeset/lovely-bugs-prove.md diff --git a/.changeset/lovely-bugs-prove.md b/.changeset/lovely-bugs-prove.md new file mode 100644 index 0000000000..5b35402dfb --- /dev/null +++ b/.changeset/lovely-bugs-prove.md @@ -0,0 +1,19 @@ +--- +'@backstage/repo-tools': minor +--- + +Renames the `schema openapi *` commands into `package schema openapi *` and `repo schema openapi *`. The aim is to make it more clear what the command is operating on, the entire repo or just a single package. + +The following commands now live under the `package` namespace, + +- `schema openapi generate` is now `package schema openapi generate server` +- `schema openapi generate-client` is now `package schema openapi generate client` +- `schema openapi init` is now `package schema openapi init` + +And these commands live under the new `repo` namespace, + +- `schema openapi lint` is now `repo schema openapi lint` +- `schema openapi test` is now `repo schema openapi test` +- `schema openapi verify` is now `repo schema openapi verify` + +This also reworks the `package schema openapi generate client` to accept only an output directory as the input directory can now be inferred. From e16864039b7653813baeff5f279dbd28c1390cc6 Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 18:18:47 -0500 Subject: [PATCH 025/468] fix more documentation and workflows Signed-off-by: Aramis --- .changeset/lovely-bugs-prove.md | 2 +- .github/workflows/ci.yml | 6 +- docs/openapi/generate-client.md | 4 +- docs/openapi/test-case-validation.md | 6 +- packages/repo-tools/cli-report.md | 138 +++++++++++++++++++++------ 5 files changed, 116 insertions(+), 40 deletions(-) diff --git a/.changeset/lovely-bugs-prove.md b/.changeset/lovely-bugs-prove.md index 5b35402dfb..b1cb762e73 100644 --- a/.changeset/lovely-bugs-prove.md +++ b/.changeset/lovely-bugs-prove.md @@ -2,7 +2,7 @@ '@backstage/repo-tools': minor --- -Renames the `schema openapi *` commands into `package schema openapi *` and `repo schema openapi *`. The aim is to make it more clear what the command is operating on, the entire repo or just a single package. +**BREAKING**: The `schema openapi *` commands are now renamed into `package schema openapi *` and `repo schema openapi *`. The aim is to make it more clear what the command is operating on, the entire repo or just a single package. The following commands now live under the `package` namespace, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd6e2edbc3..ddc1712698 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,10 +113,10 @@ jobs: run: yarn backstage-repo-tools generate-catalog-info --ci - name: lint openapi yaml files - run: yarn backstage-repo-tools schema openapi lint + run: yarn backstage-repo-tools repo schema openapi lint - name: verify openapi yaml file matches generated ts file - run: yarn backstage-repo-tools schema openapi verify + run: yarn backstage-repo-tools repo schema openapi verify - name: verify doc links run: node scripts/verify-links.js @@ -225,7 +225,7 @@ jobs: # We run the test cases before verifying the specs to prevent any failing tests from causing errors. - name: verify openapi specs against test cases - run: yarn backstage-repo-tools schema openapi test + run: yarn backstage-repo-tools repo schema openapi test - name: ensure clean working directory run: | diff --git a/docs/openapi/generate-client.md b/docs/openapi/generate-client.md index f2c7c64574..640036960e 100644 --- a/docs/openapi/generate-client.md +++ b/docs/openapi/generate-client.md @@ -4,7 +4,7 @@ title: Generate a client from your OpenAPI spec description: Documentation on how to create a client for a given OpenAPI spec --- -## How to generate a client with `repo-tools schema openapi generate-client`? +## How to generate a client with `repo-tools package schema openapi generate client`? ### Prerequisites @@ -20,7 +20,7 @@ info: ### Generating your client -1. Run `yarn backstage-repo-tools schema openapi generate-client --input-spec --output-directory `. This will create a new folder in `/src/generated` to house the generated content. +1. Run `yarn backstage-repo-tools schema openapi generate client --output-package `. This will create a new folder in `/src/generated` to house the generated content. 2. You should use the generated files as follows, - `apis/DefaultApi.client.ts` - this is the client that you should use. It has types for all of the various operations on your API. diff --git a/docs/openapi/test-case-validation.md b/docs/openapi/test-case-validation.md index 0d93b6d5b5..28d0eb5470 100644 --- a/docs/openapi/test-case-validation.md +++ b/docs/openapi/test-case-validation.md @@ -1,15 +1,15 @@ --- id: test-case-validation title: Validate your OpenAPI spec against test data -description: Documentation on how to use the `schema openapi test` command. +description: Documentation on how to use the `repo schema openapi test` command. --- ## OpenAPI Validation using Test Cases -This is primarily performed by `backstage-repo-tools schema openapi test`. Any errors found in the generated specs can be either +This is primarily performed by `backstage-repo-tools repo schema openapi test`. Any errors found in the generated specs can be either 1. Fixed manually, this is usually relevant for request body or response body changes. -2. Fixed automatically with `backstage-repo-tools schema openapi test --update`. +2. Fixed automatically with `backstage-repo-tools repo schema openapi test --update`. 3. Fixing the test case. This can happen where a response is mocked as ```ts diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index be1e7ffceb..3a03e25352 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -15,7 +15,8 @@ Commands: api-reports [options] [paths...] type-deps generate-catalog-info [options] - schema [command] + package [command] + repo [command] help [command] ``` @@ -48,10 +49,23 @@ Options: -h, --help ``` -### `backstage-repo-tools schema` +### `backstage-repo-tools package` ``` -Usage: backstage-repo-tools schema [options] [command] [command] +Usage: backstage-repo-tools package [options] [command] [command] + +Options: + -h, --help + +Commands: + schema [command] + help [command] +``` + +### `backstage-repo-tools package schema` + +``` +Usage: backstage-repo-tools package schema [options] [command] [command] Options: -h, --help @@ -61,65 +75,127 @@ Commands: help [command] ``` -### `backstage-repo-tools schema openapi` +### `backstage-repo-tools package schema openapi` ``` -Usage: backstage-repo-tools schema openapi [options] [command] [command] +Usage: backstage-repo-tools package schema openapi [options] [command] [command] + +Options: + -h, --help + +Commands: + init + generate [command] + help [command] +``` + +### `backstage-repo-tools package schema openapi generate` + +``` +Usage: backstage-repo-tools package schema openapi generate [options] [command] [command] + +Options: + -h, --help + +Commands: + server + client [options] + help [command] +``` + +### `backstage-repo-tools package schema openapi generate client` + +``` +Usage: backstage-repo-tools package schema openapi generate client [options] + +Options: + --output-package + -h, --help +``` + +### `backstage-repo-tools package schema openapi generate server` + +``` +Usage: backstage-repo-tools package schema openapi generate server [options] + +Options: + -h, --help +``` + +### `backstage-repo-tools package schema openapi init` + +``` +Usage: backstage-repo-tools package schema openapi init [options] + +Options: + -h, --help +``` + +### `backstage-repo-tools repo` + +``` +Usage: backstage-repo-tools repo [options] [command] [command] + +Options: + -h, --help + +Commands: + schema [command] + help [command] +``` + +### `backstage-repo-tools repo schema` + +``` +Usage: backstage-repo-tools repo schema [options] [command] [command] + +Options: + -h, --help + +Commands: + openapi [command] + help [command] +``` + +### `backstage-repo-tools repo schema openapi` + +``` +Usage: backstage-repo-tools repo schema openapi [options] [command] [command] Options: -h, --help Commands: verify [paths...] - generate [paths...] lint [options] [paths...] test [options] [paths...] - init help [command] ``` -### `backstage-repo-tools schema openapi generate` +### `backstage-repo-tools repo schema openapi lint` ``` -Usage: backstage-repo-tools schema openapi generate [options] [paths...] - -Options: - -h, --help -``` - -### `backstage-repo-tools schema openapi init` - -``` -Usage: backstage-repo-tools schema openapi init [options] - -Options: - -h, --help -``` - -### `backstage-repo-tools schema openapi lint` - -``` -Usage: backstage-repo-tools schema openapi lint [options] [paths...] +Usage: backstage-repo-tools repo schema openapi lint [options] [paths...] Options: --strict -h, --help ``` -### `backstage-repo-tools schema openapi test` +### `backstage-repo-tools repo schema openapi test` ``` -Usage: backstage-repo-tools schema openapi test [options] [paths...] +Usage: backstage-repo-tools repo schema openapi test [options] [paths...] Options: --update -h, --help ``` -### `backstage-repo-tools schema openapi verify` +### `backstage-repo-tools repo schema openapi verify` ``` -Usage: backstage-repo-tools schema openapi verify [options] [paths...] +Usage: backstage-repo-tools repo schema openapi verify [options] [paths...] Options: -h, --help From 4b0bfa237e4aab9286fe5c07aae91d6908979e10 Mon Sep 17 00:00:00 2001 From: jjangga0214 Date: Tue, 23 Jan 2024 11:56:17 +0900 Subject: [PATCH 026/468] docs(overview/threat-model): add commas Signed-off-by: jjangga0214 --- docs/overview/threat-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/threat-model.md b/docs/overview/threat-model.md index cb1379aa27..115c12b307 100644 --- a/docs/overview/threat-model.md +++ b/docs/overview/threat-model.md @@ -52,7 +52,7 @@ As part of signing in with an identity resolver, a Backstage Token is issued con The token is used to prove the identity of the user within the Backstage system, and is used throughout Backstage plugins to control access. It is important that the ownership resolution logic is consistent across the entire Backstage ecosystem, with no possibility of misinterpreting the ownership information. -For cross backend communication the Backstage Token is typically forwarded or in strict backend to backend communication without a user party the backend itself issues a service token based on a pre-shared secret which is then validated on the receiving end. There are no unique service identities tied to these tokens at this point, meaning the tokens can be used across all services in a Backstage installation, this is something that we aim to improve in the future. +For cross-backend communication, the Backstage Token is typically forwarded or, in strict backend-to-backend communication without a user party, the backend itself issues a service token based on a pre-shared secret which is then validated on the receiving end. There are no unique service identities tied to these tokens at this point, meaning the tokens can be used across all services in a Backstage installation, this is something that we aim to improve in the future. Backstage also supports authentication through a proxy where the user identity is read from the incoming request from the proxy, which has been decorated by an authenticating reverse proxy such as [AWS ALB](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/). The following proxy auth providers verify the signature of incoming requests, and are therefore safe to deploy with direct access by users: `awsAlb`, `cfAccess`, and `gcpIap`. Providers like `oauth2Proxy` does not verify the incoming request and can therefore be spoofed by a malicious internal user to supply the `auth` backend with forged identity information. It’s therefore highly recommended to restrict access to the `oauth2Proxy` endpoints, or use a different provider. From 9a43c63662a2082c188c4677d0c533f21c44f729 Mon Sep 17 00:00:00 2001 From: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:49:27 +0000 Subject: [PATCH 027/468] Update .changeset/spotty-kids-pay.md Co-authored-by: Patrik Oldsberg Signed-off-by: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> --- .changeset/spotty-kids-pay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/spotty-kids-pay.md b/.changeset/spotty-kids-pay.md index 199d0fdff0..157515201d 100644 --- a/.changeset/spotty-kids-pay.md +++ b/.changeset/spotty-kids-pay.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-auth-backend': minor +'@backstage/plugin-auth-backend': patch --- Ability for user to configure backstage token expiration From 442206eaae5cb2ba0880121a0e38cbb82c56a4a6 Mon Sep 17 00:00:00 2001 From: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:50:22 +0000 Subject: [PATCH 028/468] Update plugins/auth-backend/README.md Co-authored-by: Patrik Oldsberg Signed-off-by: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> --- plugins/auth-backend/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 3133266614..290745454e 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -168,7 +168,9 @@ To try out SAML, you can use the mock identity provider: ## Configuring Token Expiration in App Config -If you need to change Backstage token expiration from the default value of one hour, set the following in your config file: +If you need to change Backstage token expiration from the default value of one hour you can do so through configuration. Note that this is **not** the session duration, but rather the duration that the short-term cryptographic tokens are valid for. The expiration can not be set lower than 10 minutes or above 24 hours. + +This is what the configuration looks like: ``` auth: From 0abf05967d1a0304e769c8adff71de0e38d0fcc6 Mon Sep 17 00:00:00 2001 From: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:51:39 +0000 Subject: [PATCH 029/468] Update router.ts with suggested change Signed-off-by: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> --- plugins/auth-backend/src/service/router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index eea858dd9c..e04053c478 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -262,7 +262,7 @@ export function getDefaultBackstageTokenExpiryTime(config: Config) { key: processingIntervalKey, }); const seconds = Math.max( - 600, + 86400, Math.round(durationToMilliseconds(duration) / 1000), ); From cf9b58c3405b942b3b8e692f58b57db4c6e740ef Mon Sep 17 00:00:00 2001 From: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> Date: Tue, 23 Jan 2024 09:52:42 +0000 Subject: [PATCH 030/468] Update router.test.ts as per the changes in router.ts Signed-off-by: Lavanya Sainik <137502642+lavanya-sainik-ericsson@users.noreply.github.com> --- plugins/auth-backend/src/service/router.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend/src/service/router.test.ts b/plugins/auth-backend/src/service/router.test.ts index fd339e349e..6c0b060fdb 100644 --- a/plugins/auth-backend/src/service/router.test.ts +++ b/plugins/auth-backend/src/service/router.test.ts @@ -78,6 +78,6 @@ describe('Test for default backstage token expiry time', () => { backstageTokenExpiration: { minutes: 120 }, }, }); - expect(getDefaultBackstageTokenExpiryTime(config)).toBe(7200); + expect(getDefaultBackstageTokenExpiryTime(config)).toBe(86400); }); }); From 9c1c15fe1b0d11b40eeef536d20c8d7c8ad2072f Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 23 Jan 2024 10:09:09 -0500 Subject: [PATCH 031/468] work to update init to be package focused Signed-off-by: Aramis --- .../commands/package/schema/openapi/init.ts | 25 +++++++++++-------- .../repo-tools/src/lib/openapi/helpers.ts | 22 ++++++++++------ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/packages/repo-tools/src/commands/package/schema/openapi/init.ts b/packages/repo-tools/src/commands/package/schema/openapi/init.ts index 56136f8882..ab91aabecc 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/init.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/init.ts @@ -21,20 +21,23 @@ import { paths as cliPaths } from '../../../../lib/paths'; import { runner } from '../../../../lib/runner'; import chalk from 'chalk'; import { exec } from '../../../../lib/exec'; +import { getPathToCurrentOpenApiSpec } from '../../../../lib/openapi/helpers'; const ROUTER_TEST_PATHS = [ 'src/service/router.test.ts', 'src/service/createRouter.test.ts', ]; -async function init(directoryPath: string) { - const openapiPath = join(directoryPath, YAML_SCHEMA_PATH); - if (!(await fs.pathExists(openapiPath))) { +async function init() { + try { + await getPathToCurrentOpenApiSpec(); + } catch (err) { throw new Error( - `You do not have an OpenAPI YAML file at ${openapiPath}. Please create one and retry this command. If you already have existing test cases for your router, see 'backstage-repo-tools package schema openapi test --update'`, + `OpenAPI.yaml not found in ${YAML_SCHEMA_PATH}. Please create one and retry this command.`, ); } - const opticConfigFilePath = join(directoryPath, 'optic.yml'); + + const opticConfigFilePath = await getPathTo; if (await fs.pathExists(opticConfigFilePath)) { throw new Error(`This directory already has an optic.yml file. Exiting.`); } @@ -65,6 +68,12 @@ capture: } export default async function initCommand(paths: string[] = []) { + try { + await init(); + } catch (err) { + console.log(chalk.red(`OpenAPI tooling initialization failed.`)); + console.log(err.message); + } const resultsList = await runner(paths, dir => init(dir), { concurrencyLimit: 5, }); @@ -72,12 +81,6 @@ export default async function initCommand(paths: string[] = []) { let failed = false; for (const { relativeDir, resultText } of resultsList) { if (resultText) { - console.log(); - console.log( - chalk.red(`Failed to initialize ${relativeDir} for OpenAPI commands.`), - ); - console.log(resultText.trimStart()); - failed = true; } } diff --git a/packages/repo-tools/src/lib/openapi/helpers.ts b/packages/repo-tools/src/lib/openapi/helpers.ts index 5a5932c802..dda7f110a4 100644 --- a/packages/repo-tools/src/lib/openapi/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/helpers.ts @@ -17,16 +17,24 @@ import { pathExists } from 'fs-extra'; import { paths } from '../paths'; import { YAML_SCHEMA_PATH } from './constants'; -import { resolve } from 'path'; +import { join, resolve } from 'path'; + +export const getPathToFile = async (directory: string, filename: string) => { + const path = resolve(directory, filename); + if (!(await pathExists(path))) { + throw new Error(`Could not find ${join(directory, filename)}.`); + } + return path; +}; + +export const getRelativePathToFile = async (filename: string) => { + return await getPathToFile(paths.targetDir, filename); +}; export const getPathToOpenApiSpec = async (directory: string) => { - const openapiPath = resolve(directory, YAML_SCHEMA_PATH); - if (!(await pathExists(openapiPath))) { - throw new Error(`Could not find ${YAML_SCHEMA_PATH}.`); - } - return openapiPath; + return await getPathToFile(directory, YAML_SCHEMA_PATH); }; export const getPathToCurrentOpenApiSpec = async () => { - return await getPathToOpenApiSpec(paths.targetDir); + return await getRelativePathToFile(YAML_SCHEMA_PATH); }; From 2a42f97175f2c04e7a4a60ea24150a8156a24ac3 Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 23 Jan 2024 13:25:42 -0500 Subject: [PATCH 032/468] make init for a single package Signed-off-by: Aramis --- packages/repo-tools/src/commands/index.ts | 8 +++-- .../commands/package/schema/openapi/init.ts | 32 ++++++------------- .../repo-tools/src/lib/openapi/helpers.ts | 19 ++++++----- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 06ea2fa95c..290999687c 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -35,9 +35,13 @@ function registerPackageCommand(program: Command) { openApiCommand .command('init') - .description('Initialize any required files to use the OpenAPI tooling.') + .description( + 'Initialize any required files to use the OpenAPI tooling for this package.', + ) .action( - lazy(() => import('./package/schema/openapi/init').then(m => m.default)), + lazy(() => + import('./package/schema/openapi/init').then(m => m.singleCommand), + ), ); const generateCommand = openApiCommand diff --git a/packages/repo-tools/src/commands/package/schema/openapi/init.ts b/packages/repo-tools/src/commands/package/schema/openapi/init.ts index ab91aabecc..00e82f65df 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/init.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/init.ts @@ -14,14 +14,14 @@ * limitations under the License. */ import fs from 'fs-extra'; -import { join } from 'path'; import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; - import { paths as cliPaths } from '../../../../lib/paths'; -import { runner } from '../../../../lib/runner'; import chalk from 'chalk'; import { exec } from '../../../../lib/exec'; -import { getPathToCurrentOpenApiSpec } from '../../../../lib/openapi/helpers'; +import { + getPathToCurrentOpenApiSpec, + getRelativePathToFile, +} from '../../../../lib/openapi/helpers'; const ROUTER_TEST_PATHS = [ 'src/service/router.test.ts', @@ -37,7 +37,7 @@ async function init() { ); } - const opticConfigFilePath = await getPathTo; + const opticConfigFilePath = await getRelativePathToFile('optic.yml'); if (await fs.pathExists(opticConfigFilePath)) { throw new Error(`This directory already has an optic.yml file. Exiting.`); } @@ -48,7 +48,9 @@ async function init() { capture: ${YAML_SCHEMA_PATH}: # 🔧 Runnable example with simple get requests. - # Run with "PORT=3000 optic capture ${YAML_SCHEMA_PATH} --update interactive" in '${directoryPath}' + # Run with "PORT=3000 optic capture ${YAML_SCHEMA_PATH} --update interactive" in '${ + cliPaths.targetDir + }' # You can change the server and the 'requests' section to experiment server: # This will not be used by 'backstage-repo-tools schema openapi test', but may be useful for interactive updates. @@ -67,27 +69,13 @@ capture: } } -export default async function initCommand(paths: string[] = []) { +export async function singleCommand() { try { await init(); + console.log(chalk.green(`Successfully configured.`)); } catch (err) { console.log(chalk.red(`OpenAPI tooling initialization failed.`)); console.log(err.message); - } - const resultsList = await runner(paths, dir => init(dir), { - concurrencyLimit: 5, - }); - - let failed = false; - for (const { relativeDir, resultText } of resultsList) { - if (resultText) { - failed = true; - } - } - - if (failed) { process.exit(1); - } else { - console.log(chalk.green(`All directories have already been configured.`)); } } diff --git a/packages/repo-tools/src/lib/openapi/helpers.ts b/packages/repo-tools/src/lib/openapi/helpers.ts index dda7f110a4..f7e684a407 100644 --- a/packages/repo-tools/src/lib/openapi/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/helpers.ts @@ -17,24 +17,27 @@ import { pathExists } from 'fs-extra'; import { paths } from '../paths'; import { YAML_SCHEMA_PATH } from './constants'; -import { join, resolve } from 'path'; +import { resolve } from 'path'; export const getPathToFile = async (directory: string, filename: string) => { - const path = resolve(directory, filename); - if (!(await pathExists(path))) { - throw new Error(`Could not find ${join(directory, filename)}.`); - } - return path; + return resolve(directory, filename); }; export const getRelativePathToFile = async (filename: string) => { return await getPathToFile(paths.targetDir, filename); }; +export const assertExists = async (path: string) => { + if (!(await pathExists(path))) { + throw new Error(`Could not find ${path}.`); + } + return path; +}; + export const getPathToOpenApiSpec = async (directory: string) => { - return await getPathToFile(directory, YAML_SCHEMA_PATH); + return await assertExists(await getPathToFile(directory, YAML_SCHEMA_PATH)); }; export const getPathToCurrentOpenApiSpec = async () => { - return await getRelativePathToFile(YAML_SCHEMA_PATH); + return await assertExists(await getRelativePathToFile(YAML_SCHEMA_PATH)); }; From 233045d964c0dfb4aa4088bf2b6a9a924fdc30f8 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 23 Jan 2024 22:12:55 +0100 Subject: [PATCH 033/468] chore(Renovate): add schema for intellisense in editors Signed-off-by: secustor --- .github/renovate.json5 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index b58c0beb05..69059b70c8 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,4 +1,6 @@ { + $schema: "https://docs.renovatebot.com/renovate-schema.json", + labels: ['dependencies'], extends: ['config:base', ':disableDependencyDashboard', ':gitSignOff'], postUpdateOptions: ['yarnDedupeHighest'], From c2dcfe4b261d944c06f8bd91a40ac9eb510016d4 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 23 Jan 2024 22:15:20 +0100 Subject: [PATCH 034/468] chore(Renovate): use config:recommended to get community groupings and workarounds, as well dependency dashboard Signed-off-by: secustor --- .github/renovate.json5 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 69059b70c8..8071169950 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -2,7 +2,8 @@ $schema: "https://docs.renovatebot.com/renovate-schema.json", labels: ['dependencies'], - extends: ['config:base', ':disableDependencyDashboard', ':gitSignOff'], + extends: ['config:recommended', ':gitSignOff'], + postUpdateOptions: ['yarnDedupeHighest'], rangeStrategy: 'update-lockfile', // @elastic/elasticsearch is ignored due to licensing issues. See #10992 From ac08102c8f17afce2b1f838773f7c0f6a87e7f82 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 23 Jan 2024 22:22:37 +0100 Subject: [PATCH 035/468] chore(Renovate): double concurrent PR limit to 20 Signed-off-by: secustor --- .github/renovate.json5 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 8071169950..a217e7a98d 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -4,6 +4,9 @@ labels: ['dependencies'], extends: ['config:recommended', ':gitSignOff'], + // the default limit are 10 PRs + prConcurrentLimit: 20, + postUpdateOptions: ['yarnDedupeHighest'], rangeStrategy: 'update-lockfile', // @elastic/elasticsearch is ignored due to licensing issues. See #10992 From 0c120f4cd20e3f1e4d7b07503fcd815fc1352a9e Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 23 Jan 2024 22:30:56 +0100 Subject: [PATCH 036/468] chore(Renovate): use-bestpractices like pinning docker images Signed-off-by: secustor --- .github/renovate.json5 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index a217e7a98d..32e46ead4a 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -2,7 +2,10 @@ $schema: "https://docs.renovatebot.com/renovate-schema.json", labels: ['dependencies'], - extends: ['config:recommended', ':gitSignOff'], + + extends: ['config:best-practices', ':gitSignOff'], + // do not pin dev dependencies, which are part of the best-practices preset + ignorePresets: [':pinDevDependencies'], // the default limit are 10 PRs prConcurrentLimit: 20, From d7665705019f4a00356f8fe46fca8ec08656aa9e Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 24 Jan 2024 09:54:24 +0100 Subject: [PATCH 037/468] prettier Signed-off-by: secustor --- .github/renovate.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 32e46ead4a..828e5f5481 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,5 +1,5 @@ { - $schema: "https://docs.renovatebot.com/renovate-schema.json", + $schema: 'https://docs.renovatebot.com/renovate-schema.json', labels: ['dependencies'], From 5c05f8ac0f3263fbd8537739f7faa7032f9fb4dd Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Wed, 24 Jan 2024 12:27:11 +0200 Subject: [PATCH 038/468] feat: harmonize the package naming with cli Custom scopes should be named as `backstage-plugin-` while backstage scoped ones shoul be named just `plugin-`. Also allow custom prefix after the scope for example `custom/myapp.` to be used as scope. Signed-off-by: Heikki Hellgren --- .changeset/spotty-jokes-unite.md | 5 ++ .../src/lib/new/factories/backendModule.ts | 11 ++- .../src/lib/new/factories/backendPlugin.ts | 11 ++- .../src/lib/new/factories/common/util.test.ts | 78 +++++++++++++++++++ .../cli/src/lib/new/factories/common/util.ts | 38 +++++++++ .../lib/new/factories/frontendPlugin.test.ts | 4 +- .../src/lib/new/factories/frontendPlugin.ts | 11 ++- .../lib/new/factories/nodeLibraryPackage.ts | 9 ++- .../cli/src/lib/new/factories/pluginCommon.ts | 11 ++- .../cli/src/lib/new/factories/pluginNode.ts | 11 ++- .../cli/src/lib/new/factories/pluginWeb.ts | 11 ++- .../src/lib/new/factories/scaffolderModule.ts | 16 ++-- .../lib/new/factories/webLibraryPackage.ts | 9 ++- 13 files changed, 186 insertions(+), 39 deletions(-) create mode 100644 .changeset/spotty-jokes-unite.md create mode 100644 packages/cli/src/lib/new/factories/common/util.test.ts create mode 100644 packages/cli/src/lib/new/factories/common/util.ts diff --git a/.changeset/spotty-jokes-unite.md b/.changeset/spotty-jokes-unite.md new file mode 100644 index 0000000000..15097e42d0 --- /dev/null +++ b/.changeset/spotty-jokes-unite.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Harmonize the package naming and allow custom prefix diff --git a/packages/cli/src/lib/new/factories/backendModule.ts b/packages/cli/src/lib/new/factories/backendModule.ts index 221adcb1f3..1450e7b6ce 100644 --- a/packages/cli/src/lib/new/factories/backendModule.ts +++ b/packages/cli/src/lib/new/factories/backendModule.ts @@ -19,7 +19,7 @@ import chalk from 'chalk'; import camelCase from 'lodash/camelCase'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { addPackageDependency, Task } from '../../tasks'; import { moduleIdIdPrompt, @@ -27,6 +27,7 @@ import { pluginIdPrompt, } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -45,9 +46,11 @@ export const backendModule = createFactory({ async create(options: Options, ctx: CreateContext) { const { id: pluginId, moduleId } = options; const dirName = `${pluginId}-backend-module-${moduleId}`; - const name = ctx.scope - ? `@${ctx.scope}/plugin-${dirName}` - : `backstage-plugin-${dirName}`; + const name = resolvePackageName({ + baseName: dirName, + scope: ctx.scope, + plugin: true, + }); Task.log(); Task.log(`Creating backend module ${chalk.cyan(name)}`); diff --git a/packages/cli/src/lib/new/factories/backendPlugin.ts b/packages/cli/src/lib/new/factories/backendPlugin.ts index c5c9b5bd0e..09ea98fbe7 100644 --- a/packages/cli/src/lib/new/factories/backendPlugin.ts +++ b/packages/cli/src/lib/new/factories/backendPlugin.ts @@ -19,10 +19,11 @@ import chalk from 'chalk'; import camelCase from 'lodash/camelCase'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { addPackageDependency, Task } from '../../tasks'; import { ownerPrompt, pluginIdPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -40,9 +41,11 @@ export const backendPlugin = createFactory({ async create(options: Options, ctx: CreateContext) { const { id } = options; const pluginId = `${id}-backend`; - const name = ctx.scope - ? `@${ctx.scope}/plugin-${pluginId}` - : `backstage-plugin-${pluginId}`; + const name = resolvePackageName({ + baseName: pluginId, + scope: ctx.scope, + plugin: true, + }); Task.log(); Task.log(`Creating backend plugin ${chalk.cyan(name)}`); diff --git a/packages/cli/src/lib/new/factories/common/util.test.ts b/packages/cli/src/lib/new/factories/common/util.test.ts new file mode 100644 index 0000000000..6f168d8035 --- /dev/null +++ b/packages/cli/src/lib/new/factories/common/util.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2024 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 { resolvePackageName } from './util'; + +describe('resolvePackageName', () => { + it('should generate correct name without scope', () => { + expect(resolvePackageName({ baseName: 'test', plugin: true })).toEqual( + 'backstage-plugin-test', + ); + expect(resolvePackageName({ baseName: 'test', plugin: false })).toEqual( + 'test', + ); + }); + + it('should generate correct name for backstage scope', () => { + expect( + resolvePackageName({ + baseName: 'test', + scope: 'backstage', + plugin: true, + }), + ).toEqual('@backstage/plugin-test'); + expect( + resolvePackageName({ + baseName: 'test', + scope: 'backstage', + plugin: false, + }), + ).toEqual('@backstage/test'); + }); + + it('should generate correct name for custom scope', () => { + expect( + resolvePackageName({ + baseName: 'test', + scope: 'custom', + plugin: true, + }), + ).toEqual('@custom/backstage-plugin-test'); + expect( + resolvePackageName({ + baseName: 'test', + scope: 'custom', + plugin: false, + }), + ).toEqual('@custom/test'); + }); + + it('should generate correct name for custom scope and custom prefix', () => { + expect( + resolvePackageName({ + baseName: 'test', + scope: 'custom/myapp.', + plugin: true, + }), + ).toEqual('@custom/myapp.backstage-plugin-test'); + expect( + resolvePackageName({ + baseName: 'test', + scope: 'custom/myapp.', + plugin: false, + }), + ).toEqual('@custom/myapp.test'); + }); +}); diff --git a/packages/cli/src/lib/new/factories/common/util.ts b/packages/cli/src/lib/new/factories/common/util.ts new file mode 100644 index 0000000000..d9c6970afd --- /dev/null +++ b/packages/cli/src/lib/new/factories/common/util.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2024 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 const resolvePackageName = (options: { + baseName: string; + scope?: string; + plugin: boolean; +}) => { + const { baseName, scope, plugin } = options; + if (scope) { + if (plugin) { + const pluginName = scope.startsWith('backstage') + ? 'plugin' + : 'backstage-plugin'; + return scope.includes('/') + ? `@${scope}${pluginName}-${baseName}` + : `@${scope}/${pluginName}-${baseName}`; + } + return scope.includes('/') + ? `@${scope}${baseName}` + : `@${scope}/${baseName}`; + } + + return plugin ? `backstage-plugin-${baseName}` : baseName; +}; diff --git a/packages/cli/src/lib/new/factories/frontendPlugin.test.ts b/packages/cli/src/lib/new/factories/frontendPlugin.test.ts index 94d4a7eb4c..cca79dea2a 100644 --- a/packages/cli/src/lib/new/factories/frontendPlugin.test.ts +++ b/packages/cli/src/lib/new/factories/frontendPlugin.test.ts @@ -180,7 +180,7 @@ const router = ( fs.readJson(mockDir.resolve('packages/app/package.json')), ).resolves.toEqual({ dependencies: { - '@internal/plugin-test': '^1.0.0', + '@internal/backstage-plugin-test': '^1.0.0', }, }); @@ -188,7 +188,7 @@ const router = ( fs.readFile(mockDir.resolve('packages/app/src/App.tsx'), 'utf8'), ).resolves.toBe(` import { createApp } from '@backstage/app-defaults'; -import { TestPage } from '@internal/plugin-test'; +import { TestPage } from '@internal/backstage-plugin-test'; const router = ( diff --git a/packages/cli/src/lib/new/factories/frontendPlugin.ts b/packages/cli/src/lib/new/factories/frontendPlugin.ts index d0e81207ca..1058c879c1 100644 --- a/packages/cli/src/lib/new/factories/frontendPlugin.ts +++ b/packages/cli/src/lib/new/factories/frontendPlugin.ts @@ -20,10 +20,11 @@ import camelCase from 'lodash/camelCase'; import upperFirst from 'lodash/upperFirst'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { addPackageDependency, Task } from '../../tasks'; import { ownerPrompt, pluginIdPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -41,9 +42,11 @@ export const frontendPlugin = createFactory({ async create(options: Options, ctx: CreateContext) { const { id } = options; - const name = ctx.scope - ? `@${ctx.scope}/plugin-${id}` - : `backstage-plugin-${id}`; + const name = resolvePackageName({ + baseName: id, + scope: ctx.scope, + plugin: true, + }); const extensionName = `${upperFirst(camelCase(id))}Page`; Task.log(); diff --git a/packages/cli/src/lib/new/factories/nodeLibraryPackage.ts b/packages/cli/src/lib/new/factories/nodeLibraryPackage.ts index 4b1fda4abc..3fae1242a2 100644 --- a/packages/cli/src/lib/new/factories/nodeLibraryPackage.ts +++ b/packages/cli/src/lib/new/factories/nodeLibraryPackage.ts @@ -17,10 +17,11 @@ import chalk from 'chalk'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { Task } from '../../tasks'; import { ownerPrompt, pluginIdPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -37,7 +38,11 @@ export const nodeLibraryPackage = createFactory({ optionsPrompts: [pluginIdPrompt(), ownerPrompt()], async create(options: Options, ctx: CreateContext) { const { id } = options; - const name = ctx.scope ? `@${ctx.scope}/${id}` : `${id}`; + const name = resolvePackageName({ + baseName: id, + scope: ctx.scope, + plugin: false, + }); Task.log(); Task.log(`Creating node-library package ${chalk.cyan(name)}`); diff --git a/packages/cli/src/lib/new/factories/pluginCommon.ts b/packages/cli/src/lib/new/factories/pluginCommon.ts index 1bcca2fb6a..560386c618 100644 --- a/packages/cli/src/lib/new/factories/pluginCommon.ts +++ b/packages/cli/src/lib/new/factories/pluginCommon.ts @@ -17,10 +17,11 @@ import chalk from 'chalk'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { Task } from '../../tasks'; import { ownerPrompt, pluginIdPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -38,9 +39,11 @@ export const pluginCommon = createFactory({ async create(options: Options, ctx: CreateContext) { const { id } = options; const suffix = `${id}-common`; - const name = ctx.scope - ? `@${ctx.scope}/plugin-${suffix}` - : `backstage-plugin-${suffix}`; + const name = resolvePackageName({ + baseName: suffix, + scope: ctx.scope, + plugin: true, + }); Task.log(); Task.log(`Creating backend plugin ${chalk.cyan(name)}`); diff --git a/packages/cli/src/lib/new/factories/pluginNode.ts b/packages/cli/src/lib/new/factories/pluginNode.ts index 44dbea9fce..c7f9ba6f6a 100644 --- a/packages/cli/src/lib/new/factories/pluginNode.ts +++ b/packages/cli/src/lib/new/factories/pluginNode.ts @@ -17,10 +17,11 @@ import chalk from 'chalk'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { Task } from '../../tasks'; import { ownerPrompt, pluginIdPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -38,9 +39,11 @@ export const pluginNode = createFactory({ async create(options: Options, ctx: CreateContext) { const { id } = options; const suffix = `${id}-node`; - const name = ctx.scope - ? `@${ctx.scope}/plugin-${suffix}` - : `backstage-plugin-${suffix}`; + const name = resolvePackageName({ + baseName: suffix, + scope: ctx.scope, + plugin: true, + }); Task.log(); Task.log(`Creating Node.js plugin library ${chalk.cyan(name)}`); diff --git a/packages/cli/src/lib/new/factories/pluginWeb.ts b/packages/cli/src/lib/new/factories/pluginWeb.ts index 8189654eb2..dc8cb73dce 100644 --- a/packages/cli/src/lib/new/factories/pluginWeb.ts +++ b/packages/cli/src/lib/new/factories/pluginWeb.ts @@ -17,10 +17,11 @@ import chalk from 'chalk'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { Task } from '../../tasks'; import { ownerPrompt, pluginIdPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -38,9 +39,11 @@ export const pluginWeb = createFactory({ async create(options: Options, ctx: CreateContext) { const { id } = options; const suffix = `${id}-react`; - const name = ctx.scope - ? `@${ctx.scope}/plugin-${suffix}` - : `backstage-plugin-${suffix}`; + const name = resolvePackageName({ + baseName: suffix, + scope: ctx.scope, + plugin: true, + }); Task.log(); Task.log(`Creating web plugin library ${chalk.cyan(name)}`); diff --git a/packages/cli/src/lib/new/factories/scaffolderModule.ts b/packages/cli/src/lib/new/factories/scaffolderModule.ts index b89f0fc691..07f67ce78c 100644 --- a/packages/cli/src/lib/new/factories/scaffolderModule.ts +++ b/packages/cli/src/lib/new/factories/scaffolderModule.ts @@ -17,10 +17,11 @@ import chalk from 'chalk'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { Task } from '../../tasks'; import { ownerPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -55,14 +56,11 @@ export const scaffolderModule = createFactory({ const { id } = options; const slug = `scaffolder-backend-module-${id}`; - let name = `backstage-plugin-${slug}`; - if (ctx.scope) { - if (ctx.scope === 'backstage') { - name = `@backstage/plugin-${slug}`; - } else { - name = `@${ctx.scope}/backstage-plugin-${slug}`; - } - } + const name = resolvePackageName({ + baseName: slug, + scope: ctx.scope, + plugin: true, + }); Task.log(); Task.log(`Creating module ${chalk.cyan(name)}`); diff --git a/packages/cli/src/lib/new/factories/webLibraryPackage.ts b/packages/cli/src/lib/new/factories/webLibraryPackage.ts index a160220cab..8f4488926d 100644 --- a/packages/cli/src/lib/new/factories/webLibraryPackage.ts +++ b/packages/cli/src/lib/new/factories/webLibraryPackage.ts @@ -17,10 +17,11 @@ import chalk from 'chalk'; import { paths } from '../../paths'; import { addCodeownersEntry, getCodeownersFilePath } from '../../codeowners'; -import { createFactory, CreateContext } from '../types'; +import { CreateContext, createFactory } from '../types'; import { Task } from '../../tasks'; import { ownerPrompt, pluginIdPrompt } from './common/prompts'; import { executePluginPackageTemplate } from './common/tasks'; +import { resolvePackageName } from './common/util'; type Options = { id: string; @@ -37,7 +38,11 @@ export const webLibraryPackage = createFactory({ optionsPrompts: [pluginIdPrompt(), ownerPrompt()], async create(options: Options, ctx: CreateContext) { const { id } = options; - const name = ctx.scope ? `@${ctx.scope}/${id}` : `${id}`; + const name = resolvePackageName({ + baseName: id, + scope: ctx.scope, + plugin: false, + }); Task.log(); Task.log(`Creating web-library package ${chalk.cyan(name)}`); From 89b674c1e56bccae2b55353fa93d42204ea11109 Mon Sep 17 00:00:00 2001 From: Brian Hudson Date: Wed, 24 Jan 2024 10:25:03 -0500 Subject: [PATCH 039/468] Minor perf improvement to queryEntities when limit=0 Updates queryEntities to not execute dbQuery at all when limit is 0 Signed-off-by: Brian Hudson --- .changeset/ninety-rules-sneeze.md | 5 +++++ .../catalog-backend/src/service/DefaultEntitiesCatalog.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/ninety-rules-sneeze.md diff --git a/.changeset/ninety-rules-sneeze.md b/.changeset/ninety-rules-sneeze.md new file mode 100644 index 0000000000..9efc790861 --- /dev/null +++ b/.changeset/ninety-rules-sneeze.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Minor performance improvement for queryEntities when the limit is 0. diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index 0245fe1d4e..81982d5617 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -451,7 +451,7 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { countQuery.count('search.entity_id', { as: 'count' }); const [rows, [{ count }]] = await Promise.all([ - dbQuery, + limit > 0 ? dbQuery : [], // for performance reasons we invoke the countQuery // only on the first request. // The result is then embedded into the cursor From ecc4f0eb8835b0564c32e9e4bb4390789175e24c Mon Sep 17 00:00:00 2001 From: Brian Hudson Date: Wed, 24 Jan 2024 10:34:00 -0500 Subject: [PATCH 040/468] Fix changeset Signed-off-by: Brian Hudson --- .changeset/ninety-rules-sneeze.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/ninety-rules-sneeze.md b/.changeset/ninety-rules-sneeze.md index 9efc790861..bea9d00f7e 100644 --- a/.changeset/ninety-rules-sneeze.md +++ b/.changeset/ninety-rules-sneeze.md @@ -2,4 +2,4 @@ '@backstage/plugin-catalog-backend': patch --- -Minor performance improvement for queryEntities when the limit is 0. +Minor performance improvement for `queryEntities` when the limit is 0. From b107fdf6090352c2c1715891b0f86e46fec67d0c Mon Sep 17 00:00:00 2001 From: Aramis Date: Wed, 24 Jan 2024 10:40:04 -0500 Subject: [PATCH 041/468] rework `generate` to take flags instead of subcommands Signed-off-by: Aramis --- .changeset/lovely-bugs-prove.md | 6 ++-- docs/openapi/01-getting-started.md | 4 +-- packages/repo-tools/cli-report.md | 29 ++-------------- packages/repo-tools/src/commands/index.ts | 34 +++++-------------- .../package/schema/openapi/generate/client.ts | 6 +--- .../package/schema/openapi/generate/index.ts | 34 +++++++++++++++++++ plugins/catalog-backend/package.json | 3 +- plugins/search-backend/package.json | 2 +- plugins/todo-backend/package.json | 2 +- 9 files changed, 55 insertions(+), 65 deletions(-) create mode 100644 packages/repo-tools/src/commands/package/schema/openapi/generate/index.ts diff --git a/.changeset/lovely-bugs-prove.md b/.changeset/lovely-bugs-prove.md index b1cb762e73..de55f329bb 100644 --- a/.changeset/lovely-bugs-prove.md +++ b/.changeset/lovely-bugs-prove.md @@ -6,8 +6,8 @@ The following commands now live under the `package` namespace, -- `schema openapi generate` is now `package schema openapi generate server` -- `schema openapi generate-client` is now `package schema openapi generate client` +- `schema openapi generate` is now `package schema openapi generate --server` +- `schema openapi generate-client` is now `package schema openapi generate --client-package` - `schema openapi init` is now `package schema openapi init` And these commands live under the new `repo` namespace, @@ -16,4 +16,4 @@ And these commands live under the new `repo` namespace, - `schema openapi test` is now `repo schema openapi test` - `schema openapi verify` is now `repo schema openapi verify` -This also reworks the `package schema openapi generate client` to accept only an output directory as the input directory can now be inferred. +The `package schema openapi generate` now supports defining both `--server` and `--client-package` to generate both at once.This update also reworks the `--client-package` flag to accept only an output directory as the input directory can now be inferred. diff --git a/docs/openapi/01-getting-started.md b/docs/openapi/01-getting-started.md index ce04e75107..5b9908756f 100644 --- a/docs/openapi/01-getting-started.md +++ b/docs/openapi/01-getting-started.md @@ -45,7 +45,7 @@ You should create a new folder, `src/schema` in your backend plugin to store you ## Generating a typed express router from a spec -Run `yarn backstage-repo-tools package schema openapi generate server `. This will create an `openapi.generated.ts` file in the `src/schema` directory that contains the OpenAPI schema as well as a generated express router with types. You should add this command to your `package.json` for future use. +Run `yarn backstage-repo-tools package schema openapi generate --server` from the directory with your plugin. This will create an `openapi.generated.ts` file in the `src/schema` directory that contains the OpenAPI schema as well as a generated express router with types. You should add this command to your `package.json` for future use and you can combine both the server generation and the client generation below like so, `yarn backstage-repo-tools package schema openapi generate --server --client-package ` Use it like so, update your `router.ts` or `createRouter.ts` file with the following content, @@ -63,7 +63,7 @@ export async function createRouter( ## Generating a typed client from a spec -From your current backend plugin directory, run `yarn backstage-repo-tools package schema openapi generate client --output-package `. `` is a new directory and npm package that you should create. The general pattern is `plugins/-client` or if you want to co-locate this with your other shared types, use `plugins/-common`. You should add this command to your `package.json` for future use. +From your current backend plugin directory, run `yarn backstage-repo-tools package schema openapi generate --client-package `. `` is a new directory and npm package that you should create. The general pattern is `plugins/-client` or if you want to co-locate this with your other shared types, use `plugins/-common`. You should add this command to your `package.json` for future use. The generated client will have a directory `src/generated` that exports a `DefaultApiClient` class and all generated types. You can use the client like so, diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 3a03e25352..7988c479b0 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -85,40 +85,17 @@ Options: Commands: init - generate [command] + generate [options] help [command] ``` ### `backstage-repo-tools package schema openapi generate` ``` -Usage: backstage-repo-tools package schema openapi generate [options] [command] [command] - -Options: - -h, --help - -Commands: - server - client [options] - help [command] -``` - -### `backstage-repo-tools package schema openapi generate client` - -``` -Usage: backstage-repo-tools package schema openapi generate client [options] - -Options: - --output-package - -h, --help -``` - -### `backstage-repo-tools package schema openapi generate server` - -``` -Usage: backstage-repo-tools package schema openapi generate server [options] +Usage: backstage-repo-tools package schema openapi generate [options] Options: + --client-package [package] -h, --help ``` diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 290999687c..1fa37402ce 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -44,35 +44,19 @@ function registerPackageCommand(program: Command) { ), ); - const generateCommand = openApiCommand - .command('generate [command]') - .description( - 'Commands for generating various things from an OpenAPI spec.', - ); - - generateCommand - .command('server') - .description( - 'Generates an express server stub using the OpenAPI schema for typings.', - ) - .action( - lazy(() => - import('./package/schema/openapi/generate/server').then(m => m.command), - ), - ); - - generateCommand - .command('client') - .description( - 'Generates a client that can interact with your backend plugin using types from your OpenAPI schema.', - ) - .requiredOption( - '--output-package ', + openApiCommand + .command('generate') + .option( + '--client-package [package]', 'Top-level path to where the client should be generated, ie packages/catalog-client.', ) + .option('--server') + .description( + 'Command to generate a client and/or a server stub from an OpenAPI spec.', + ) .action( lazy(() => - import('./package/schema/openapi/generate/client').then(m => m.command), + import('./package/schema/openapi/generate').then(m => m.command), ), ); } diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts index 101c2c918b..5dfed05f93 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts @@ -87,11 +87,7 @@ async function generate(outputDirectory: string) { }); } -export async function command({ - outputPackage, -}: { - outputPackage: string; -}): Promise { +export async function command(outputPackage: string): Promise { try { await generate(outputPackage); console.log( diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/index.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/index.ts new file mode 100644 index 0000000000..1e48fe3825 --- /dev/null +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/index.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import chalk from 'chalk'; +import { OptionValues } from 'commander'; +import { command as generateClient } from './client'; +import { command as generateServer } from './server'; + +export async function command(opts: OptionValues) { + if (!opts.clientPackage && !opts.server) { + console.log( + chalk.red('Either --client-package or --server must be defined.'), + ); + process.exit(1); + } + if (opts.clientPackage) { + await generateClient(opts.clientPackage); + } + if (opts.server) { + await generateServer(); + } +} diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index eee268736a..5e1d4f4e60 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -43,8 +43,7 @@ "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", "clean": "backstage-cli package clean", - "generate-server": "backstage-repo-tools package schema openapi generate server", - "generate-client": "backstage-repo-tools package schema openapi generate client --output-package packages/catalog-client" + "generate": "backstage-repo-tools package schema openapi generate --server --client-package packages/catalog-client" }, "dependencies": { "@backstage/backend-common": "workspace:^", diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index b328083246..c68f2b816e 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -40,7 +40,7 @@ "prepack": "backstage-cli package prepack", "postpack": "backstage-cli package postpack", "clean": "backstage-cli package clean", - "generate-server": "backstage-repo-tools package schema openapi generate server" + "generate": "backstage-repo-tools package schema openapi generate --server" }, "dependencies": { "@backstage/backend-common": "workspace:^", diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index 75a070e284..2d9eb469d1 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -27,7 +27,7 @@ "postpack": "backstage-cli package postpack", "clean": "backstage-cli package clean", "start": "backstage-cli package start", - "generate-server": "backstage-repo-tools package schema openapi generate server" + "generate": "backstage-repo-tools package schema openapi generate --server" }, "dependencies": { "@backstage/backend-common": "workspace:^", From 27cb093b3c71fe276e4352d0a5c5bfb78a5c1dc3 Mon Sep 17 00:00:00 2001 From: shmaram Date: Thu, 25 Jan 2024 08:22:21 +0200 Subject: [PATCH 042/468] Rename config functions + update README.md Signed-off-by: shmaram --- plugins/home/README.md | 4 + plugins/home/src/api/config.test.ts | 100 +++++++++--------- plugins/home/src/api/config.ts | 9 +- .../VisitedByType/Content.tsx | 6 +- 4 files changed, 63 insertions(+), 56 deletions(-) diff --git a/plugins/home/README.md b/plugins/home/README.md index c84bae684f..fdc5ce833b 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -353,6 +353,10 @@ home: value: ``` +Filters that are not defined according to the docs will be ignored. + +In order to validate the config you can use `backstage/cli config:check` + ## Contributing ### Homepage Components diff --git a/plugins/home/src/api/config.test.ts b/plugins/home/src/api/config.test.ts index 62ca09dd82..c2b715ec08 100644 --- a/plugins/home/src/api/config.test.ts +++ b/plugins/home/src/api/config.test.ts @@ -14,71 +14,75 @@ * limitations under the License. */ -import { readFilterByConfig, readFilterByConfigs } from './config'; +import { readFilterConfig, createFilterByQueryParamFromConfig } from './config'; import { MockConfigApi } from '@backstage/test-utils'; describe('config', () => { - describe('readFilterByConfig', () => { + describe('readFilterConfig', () => { it('returns filter data', async () => { - const mockConfig = new MockConfigApi( - { - field: 'pathname', - operator: '==', - value: '3' - }); - const res = readFilterByConfig(mockConfig) + const mockConfig = new MockConfigApi({ + field: 'pathname', + operator: '==', + value: '3', + }); + const res = readFilterConfig(mockConfig); expect(res).toEqual({ field: 'pathname', operator: '==', - value: '3' - }) + value: '3', + }); }); it('throws an error for invalid filter', async () => { - const mockConfig = new MockConfigApi( - { - myField: 'pathname', - operator: '==', - value: '3' - }); - expect(() => readFilterByConfig(mockConfig)).toThrow('Invalid config, Error: Missing required config value at \'field\'') + const mockConfig = new MockConfigApi({ + myField: 'pathname', + operator: '==', + value: '3', + }); + expect(() => readFilterConfig(mockConfig)).toThrow( + "Invalid config, Error: Missing required config value at 'field'", + ); }); }); - describe('readFilterByConfigs', () => { + describe('createFilterByQueryParamFromConfig', () => { it('returns filter data', async () => { - const mockConfig1 = new MockConfigApi( - { - field: 'id', - operator: '==', - value: '3' - }); - const mockConfig2 = new MockConfigApi( - { - field: 'pathname', - operator: '==', - value: 'path' - }); - const res = readFilterByConfigs([mockConfig1, mockConfig2]) - expect(res).toEqual([{"field": "id", "operator": "==", "value": "3"}, {"field": "pathname", "operator": "==", "value": "path"}]) + const mockConfig1 = new MockConfigApi({ + field: 'id', + operator: '==', + value: '3', + }); + const mockConfig2 = new MockConfigApi({ + field: 'pathname', + operator: '==', + value: 'path', + }); + const res = createFilterByQueryParamFromConfig([ + mockConfig1, + mockConfig2, + ]); + expect(res).toEqual([ + { field: 'id', operator: '==', value: '3' }, + { field: 'pathname', operator: '==', value: 'path' }, + ]); }); it('return undefined for invalid filter', async () => { - const mockConfig1 = new MockConfigApi( - { - field: 'id', - operator: '==', - value: '3' - }); - const mockConfig2 = new MockConfigApi( - { - myField: 'pathname', - operator: '==', - value: 'path' - }); - const res = readFilterByConfigs([mockConfig1, mockConfig2]) - expect(res).toEqual(undefined) + const mockConfig1 = new MockConfigApi({ + field: 'id', + operator: '==', + value: '3', + }); + const mockConfig2 = new MockConfigApi({ + myField: 'pathname', + operator: '==', + value: 'path', + }); + const res = createFilterByQueryParamFromConfig([ + mockConfig1, + mockConfig2, + ]); + expect(res).toEqual(undefined); }); }); }); - diff --git a/plugins/home/src/api/config.ts b/plugins/home/src/api/config.ts index 6604084244..a8af29fc9e 100644 --- a/plugins/home/src/api/config.ts +++ b/plugins/home/src/api/config.ts @@ -24,7 +24,7 @@ import { Config } from '@backstage/config'; * * @public */ -export function readFilterByConfig(config: Config): { +export function readFilterConfig(config: Config): { field: keyof Visit; operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; value: string | number; @@ -53,13 +53,12 @@ export function readFilterByConfig(config: Config): { * * @public */ -export function readFilterByConfigs( +export function createFilterByQueryParamFromConfig( configs: Config[], ): VisitsApiQueryParams['filterBy'] | undefined { try { - return configs.map(readFilterByConfig); - } - catch { + return configs.map(readFilterConfig); + } catch { return undefined; } } diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx index 6fdc313c63..6c9d889d72 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.tsx @@ -15,7 +15,7 @@ */ import React, { useEffect } from 'react'; -import { readFilterByConfigs } from '../../api/config'; +import { createFilterByQueryParamFromConfig } from '../../api/config'; import { VisitedByType } from './VisitedByType'; import { Visit, visitsApiRef } from '../../api'; import { ContextValueOnly, useContext } from './Context'; @@ -66,7 +66,7 @@ export const Content = ({ const visitsApi = useApi(visitsApiRef); const { loading: reqLoading } = useAsync(async () => { if (!visits && !loading && kind === 'recent') { - const filterBy = readFilterByConfigs( + const filterBy = createFilterByQueryParamFromConfig( config.getOptionalConfigArray('home.recentVisits.filterBy') ?? [], ); return await visitsApi @@ -78,7 +78,7 @@ export const Content = ({ .then(setVisits); } if (!visits && !loading && kind === 'top') { - const filterBy = readFilterByConfigs( + const filterBy = createFilterByQueryParamFromConfig( config.getOptionalConfigArray('home.topVisits.filterBy') ?? [], ); return await visitsApi From 7944df5ae449854603bffd83f35e65b993bcd68e Mon Sep 17 00:00:00 2001 From: shmaram Date: Thu, 25 Jan 2024 10:04:48 +0200 Subject: [PATCH 043/468] update package.json Signed-off-by: shmaram --- plugins/home/package.json | 1 + yarn.lock | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/home/package.json b/plugins/home/package.json index 603b286c7a..e8fa2b666c 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -50,6 +50,7 @@ "dependencies": { "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", "@backstage/core-app-api": "workspace:^", "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 0e3d81bc7f..458717f31a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7021,6 +7021,7 @@ __metadata: "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" "@backstage/core-app-api": "workspace:^" "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" From 90c5f9db54587d8da544912139ffce6930ac0572 Mon Sep 17 00:00:00 2001 From: Alex <52247724+Elya29@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:32:34 +0100 Subject: [PATCH 044/468] Update writing.md add warning about hard coded RSA Private Keys. Signed-off-by: Alex <52247724+Elya29@users.noreply.github.com> --- docs/conf/writing.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf/writing.md b/docs/conf/writing.md index aef9620373..b2c098e12b 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -220,6 +220,8 @@ clientSecret: someGithubAppClientSecret webhookSecret: someWebhookSecret privateKey: | -----BEGIN RSA PRIVATE KEY----- - SomeRsaPrivateKey + SomeRsaPrivateKeyForExampleOnly -----END RSA PRIVATE KEY----- ``` + +**Warning: RSA private keys should not be hard coded**. Keep them in a secure storage solution like Vault, to ensure they are neither exposed nor misused. From 5b70e5591d4c9fcdea9f414a80b549cb9b222c42 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 23 Jan 2024 14:05:10 +0100 Subject: [PATCH 045/468] Init doc structure Signed-off-by: Philipp Hugenroth --- .../building-plugins/04-built-in-data-refs.md | 24 +++++++++++++++++++ microsite/sidebars.json | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 docs/frontend-system/building-plugins/04-built-in-data-refs.md diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md new file mode 100644 index 0000000000..ee6875e1cb --- /dev/null +++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md @@ -0,0 +1,24 @@ +--- +id: built-in-data-refs +title: Built-in data refs +sidebar_label: Built-in data refs +# prettier-ignore +description: Configuring or overriding built-in data refs +--- + +## Disable built-in data refs + +## Override built-in data refs + +## Default built-in data refs + +### Example + +#### Example + +Extensions that provides a default `light` and `dark` app themes. + +| kind | namespace | name | id | +| :---: | :-------: | :---: | :---------------: | +| theme | app | light | `theme:app/light` | +| theme | app | dark | `theme:app/dark` | diff --git a/microsite/sidebars.json b/microsite/sidebars.json index e32e027807..754e26ec9f 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -414,7 +414,8 @@ "items": [ "frontend-system/building-plugins/index", "frontend-system/building-plugins/testing", - "frontend-system/building-plugins/extension-types" + "frontend-system/building-plugins/extension-types", + "frontend-system/building-plugins/built-in-data-refs" ] }, { From 2a04cef984dc65e7ea96c4b773aa806adef3e037 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 24 Jan 2024 09:39:28 +0100 Subject: [PATCH 046/468] Add all existing data refs Signed-off-by: Philipp Hugenroth --- .../architecture/03-extensions.md | 2 - .../building-plugins/04-built-in-data-refs.md | 95 ++++++++++++++++--- 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index 6d86415817..cfbabdf9bc 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -81,8 +81,6 @@ const extension = createExtension({ Note that while the `createExtension` is public API and used in many places, it is not typically what you use when building plugins and features. Instead there are many extension creator functions exported by both the core APIs and plugins that make it easier to create extensions for more specific usages. -... TODO ... - ## Extension Data Communication between extensions happens in one direction, from one child extension through the attachment point to its parent. The child extension outputs data which is then passed as inputs to the parent extension. This data is called Extension Data, where the shape of each individual piece of data is described by an Extension Data Reference. These references are created separately from the extensions themselves, and can be shared across multiple different kinds of extensions. Each reference consists of an ID and a TypeScript type that the data needs to conform to, and represents one type of data that can be shared between extensions. diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md index ee6875e1cb..16ded9a9fd 100644 --- a/docs/frontend-system/building-plugins/04-built-in-data-refs.md +++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md @@ -3,22 +3,95 @@ id: built-in-data-refs title: Built-in data refs sidebar_label: Built-in data refs # prettier-ignore -description: Configuring or overriding built-in data refs +description: Configuring or overriding built-in extension data references --- -## Disable built-in data refs +To have a better understanding of extension data references please read [extension data section the extensions architecture documentation](../architecture/03-extensions.md#extension-data) first. -## Override built-in data refs +## Built-in extension data references -## Default built-in data refs +### Frontend Plugin API -### Example +#### Core Extension Data -#### Example +| namespace | name | type | id | +| :-------: | :----------: | :---------: | :-----------------: | +| - | reactElement | JSX.Element | `core.reactElement` | +| - | routePath | string | `core.routing.path` | +| - | routeRef | RouteRef | `core.routing.ref` | -Extensions that provides a default `light` and `dark` app themes. +#### -| kind | namespace | name | id | -| :---: | :-------: | :---: | :---------------: | -| theme | app | light | `theme:app/light` | -| theme | app | dark | `theme:app/dark` | +| namespace | name | type | id | +| :----------------: | :------------: | :-----------: | :--------------: | +| createApiExtension | factoryDataRef | AnyApiFactory | core.api.factory | + +#### + +| namespace | name | type | id | +| :---------------------------: | :--------------: | :----------------------------------: | :--------------: | +| createAppRootWrapperExtension | componentDataRef | ComponentType> | app.root.wrapper | + +#### + +| namespace | name | type | id | +| :-------------------: | :--------------: | :----------------------------------: | :--------------: | +| createRouterExtension | componentDataRef | ComponentType> | app.root.wrapper | + +#### + +```ts +type DataType = { + title: string; + icon: IconComponent; + routeRef: RouteRef; +}; +``` + +| namespace | name | type | id | +| :--------------------: | :-----------: | :------: | :------------------: | +| createNavItemExtension | targetDataRef | DataType | core.nav-item.target | + +#### + +```ts +type DataType = { + logoIcon?: JSX.Element; + logoFull?: JSX.Element; +}; +``` + +| namespace | name | type | id | +| :--------------------: | :-----------------: | :----------------------------------: | :-------------------------: | +| createNavLogoExtension | logoElementsDataRef | ComponentType> | core.nav-logo.logo-elements | + +#### + +| namespace | name | type | id | +| :-----------------------: | :--------------: | :----------------------------: | :-------------------------: | +| createSignInPageExtension | componentDataRef | ComponentType | core.sign-in-page.component | + +#### + +| namespace | name | type | id | +| :------------------: | :----------: | :------: | :--------------: | +| createThemeExtension | themeDataRef | AppTheme | core.theme.theme | + +#### + +```ts +type DataType = { + ref: ComponentRef; + impl: ComponentType; +}; +``` + +| namespace | name | type | id | +| :----------------------: | :--------------: | :------: | :----------------------: | +| createComponentExtension | componentDataRef | DataType | core.component.component | + +#### + +| namespace | name | type | id | +| :------------------------: | :----------------: | :----------------------------------------: | :--------------------------: | +| createTranslationExtension | translationDataRef | TranslationResource or TranslationMessages | core.translation.translation | From dc7ae8b20a902017dea554e9d96a3c2ed32db52c Mon Sep 17 00:00:00 2001 From: secustor Date: Thu, 25 Jan 2024 16:12:42 +0100 Subject: [PATCH 047/468] fix(plugins/home/FeatureDocsCard): fall back to metadata.name if no title has been supplied Signed-off-by: secustor --- .changeset/nasty-shirts-look.md | 5 +++++ .../home/src/homePageComponents/FeaturedDocsCard/Content.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/nasty-shirts-look.md diff --git a/.changeset/nasty-shirts-look.md b/.changeset/nasty-shirts-look.md new file mode 100644 index 0000000000..2742ebd1f6 --- /dev/null +++ b/.changeset/nasty-shirts-look.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Fall back to metadata.name if no metadata.title has been defined diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx index eb8d78b0cc..21ed43003b 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx @@ -116,7 +116,7 @@ export const Content = (props: FeaturedDocsCardProps): JSX.Element => { }/` } > - {d.metadata.title} + {d.metadata.title ?? d.metadata.name} {d.metadata.description && ( From 8723c5a4da06ca7c11f84f3bbc9ae91527fd6499 Mon Sep 17 00:00:00 2001 From: David Festal Date: Thu, 25 Jan 2024 16:21:45 +0100 Subject: [PATCH 048/468] Fix wrong `alpha` support in dynamic plugins support The `alpha` sub-package should not be required for the dynamic plugins to be loaded under the new backend system. Signed-off-by: David Festal --- .changeset/calm-cups-rule.md | 5 +++ .../src/scanner/plugin-scanner.test.ts | 31 ++++++++++++++++++ .../src/scanner/plugin-scanner.ts | 32 ++++++++++--------- 3 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 .changeset/calm-cups-rule.md diff --git a/.changeset/calm-cups-rule.md b/.changeset/calm-cups-rule.md new file mode 100644 index 0000000000..48cb8a2012 --- /dev/null +++ b/.changeset/calm-cups-rule.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-dynamic-feature-service': patch +--- + +Fix wrong `alpha` support in dynamic plugins support: the `alpha` sub-package should not be required for the dynamic plugins to be loaded under the new backend system. diff --git a/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.test.ts b/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.test.ts index e52eee87c4..1abd7c234c 100644 --- a/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.test.ts +++ b/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.test.ts @@ -484,6 +484,37 @@ Please add '${mockDir.resolve( ], }, }, + { + name: "alpha manifest preferred but skipped because the `alpha` sub-directory doesn't exist", + preferAlpha: true, + fileSystem: { + backstageRoot: { + 'dist-dynamic': { + 'test-backend-plugin': { + 'package.json': JSON.stringify({ + name: 'test-backend-plugin-dynamic', + version: '0.0.0', + main: 'dist/index.cjs.js', + backstage: { role: 'backend-plugin' }, + }), + }, + }, + }, + }, + expectedPluginPackages: [ + { + location: url.pathToFileURL( + mockDir.resolve('backstageRoot/dist-dynamic/test-backend-plugin'), + ), + manifest: { + name: 'test-backend-plugin-dynamic', + version: '0.0.0', + main: 'dist/index.cjs.js', + backstage: { role: 'backend-plugin' }, + }, + }, + ], + }, { name: 'invalid alpha package.json', preferAlpha: true, diff --git a/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts b/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts index 25e0d614dd..af1144f019 100644 --- a/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts +++ b/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts @@ -16,7 +16,7 @@ import { Config } from '@backstage/config'; import { ScannedPluginPackage, ScannedPluginManifest } from './types'; import * as fs from 'fs/promises'; -import { Stats, lstatSync } from 'fs'; +import { Stats, lstatSync, existsSync } from 'fs'; import * as chokidar from 'chokidar'; import * as path from 'path'; import * as url from 'url'; @@ -178,22 +178,24 @@ export class PluginScanner { if (platform === 'node') { if (this.preferAlpha) { const pluginHomeAlpha = path.resolve(pluginHome, 'alpha'); - if ((await fs.lstat(pluginHomeAlpha)).isDirectory()) { - const backstage = scannedPlugin.manifest.backstage; - try { - scannedPlugin = await this.scanDir(pluginHomeAlpha); - } catch (e) { - this.logger.error( - `failed to load dynamic plugin manifest from '${pluginHomeAlpha}'`, - e, + if (existsSync(pluginHomeAlpha)) { + if ((await fs.lstat(pluginHomeAlpha)).isDirectory()) { + const backstage = scannedPlugin.manifest.backstage; + try { + scannedPlugin = await this.scanDir(pluginHomeAlpha); + } catch (e) { + this.logger.error( + `failed to load dynamic plugin manifest from '${pluginHomeAlpha}'`, + e, + ); + continue; + } + scannedPlugin.manifest.backstage = backstage; + } else { + this.logger.warn( + `skipping '${pluginHomeAlpha}' since it is not a directory`, ); - continue; } - scannedPlugin.manifest.backstage = backstage; - } else { - this.logger.warn( - `skipping '${pluginHomeAlpha}' since it is not a directory`, - ); } } } From 9fe9285b07f696ae961d17bdfd9389a06b70ebd1 Mon Sep 17 00:00:00 2001 From: secustor Date: Thu, 25 Jan 2024 16:46:18 +0100 Subject: [PATCH 049/468] feat(plugins/home/FeatureDocsCard): use EntityDisplayName rather then data direct Signed-off-by: secustor --- .changeset/nasty-shirts-look.md | 4 ++-- .../src/homePageComponents/FeaturedDocsCard/Content.tsx | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.changeset/nasty-shirts-look.md b/.changeset/nasty-shirts-look.md index 2742ebd1f6..2a6e2d9c19 100644 --- a/.changeset/nasty-shirts-look.md +++ b/.changeset/nasty-shirts-look.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-home': patch +'@backstage/plugin-home': minor --- -Fall back to metadata.name if no metadata.title has been defined +Use EntityDisplayName JSX element entity information directly for FeaturedDocsCard. diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx index 21ed43003b..a7ba4d5e9b 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx @@ -23,11 +23,16 @@ import { Progress, ErrorPanel, } from '@backstage/core-components'; -import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog-react'; +import { + catalogApiRef, + CatalogApi, + EntityDisplayName, +} from '@backstage/plugin-catalog-react'; import { useApi } from '@backstage/core-plugin-api'; import { EntityFilterQuery } from '@backstage/catalog-client'; import { makeStyles, Typography } from '@material-ui/core'; +import { stringifyEntityRef } from '@backstage/catalog-model'; /** * Props customizing the component. @@ -116,7 +121,7 @@ export const Content = (props: FeaturedDocsCardProps): JSX.Element => { }/` } > - {d.metadata.title ?? d.metadata.name} + {d.metadata.description && ( From f9b5866902880e644ea547a004146bb7a3a8f5a5 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Thu, 25 Jan 2024 22:38:21 +0530 Subject: [PATCH 050/468] some a11y fix Signed-off-by: npiyush97 --- docs/features/software-catalog/index.md | 14 ++++----- docs/overview/background.md | 2 +- docs/overview/what-is-backstage.md | 2 +- .../src/pages/plugins/plugins.module.scss | 3 +- microsite/src/theme/customTheme.scss | 31 +++++++++++++++++-- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index 0596d64ee4..ae3a4ef4c2 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -36,7 +36,7 @@ The Software Catalog is available to browse at `/catalog`. If you've followed [Getting Started with Backstage](../../getting-started), you should be able to browse the catalog at `http://localhost:3000`. -![](../../assets/software-catalog/software-catalog-home.png) +![screenshot of software catalog](../../assets/software-catalog/software-catalog-home.png) ## Adding components to the catalog @@ -55,7 +55,7 @@ There are 3 ways to add components to the catalog: Users can register new components by going to `/create` and clicking the **REGISTER EXISTING COMPONENT** button: -![](../../assets/software-catalog/bsc-register-1.png) +![screenshot of manually register existing component](../../assets/software-catalog/bsc-register-1.png) Backstage expects the full URL to the YAML in your source control. Example: @@ -66,7 +66,7 @@ https://github.com/backstage/backstage/blob/master/packages/catalog-model/exampl _More examples can be found [here](https://github.com/backstage/backstage/tree/master/packages/catalog-model/examples)._ -![](../../assets/software-catalog/bsc-register-2.png) +![screenshot of creating new components](../../assets/software-catalog/bsc-register-2.png) It is important to note that any kind of software can be registered in Backstage. Even if the software is not maintained by your company (SaaS @@ -100,7 +100,7 @@ More information about catalog configuration can be found Teams owning the components are responsible for maintaining the metadata about them, and do so using their normal Git workflow. -![](../../assets/software-catalog/bsc-edit.png) +![screenshot of updating component metadata](../../assets/software-catalog/bsc-edit.png) Once the change has been merged, Backstage will automatically show the updated metadata in the software catalog after a short while. @@ -112,14 +112,14 @@ in user. But you can also switch to _All_ to see all the components across your company's software ecosystem. Basic inline _search_ and _column filtering_ makes it easy to browse a big set of components. -![](../../assets/software-catalog/bsc-search.png) +![screenshot of finding software in the catalog](../../assets/software-catalog/bsc-search.png) ## Starring components For easy and quick access to components you visit frequently, Backstage supports _starring_ of components: -![](../../assets/software-catalog/bsc-starred.png) +![screenshot of starred components](../../assets/software-catalog/bsc-starred.png) ## Integrated tooling through plugins @@ -130,7 +130,7 @@ infrastructure UIs (and incurring additional cognitive overhead each time they make a context switch), most of these tools can be organized around the entities in the catalog. -![tools](https://backstage.io/assets/images/tabs-abfdf72185d3ceb1d92c4237f7f78809.png) +![screenshot of tools](https://backstage.io/assets/images/tabs-abfdf72185d3ceb1d92c4237f7f78809.png) The Backstage platform can be customized by incorporating [existing open source plugins](https://github.com/backstage/backstage/tree/master/plugins), diff --git a/docs/overview/background.md b/docs/overview/background.md index b74a2e0272..5e59967b21 100644 --- a/docs/overview/background.md +++ b/docs/overview/background.md @@ -35,4 +35,4 @@ Backstage was originally built by Spotify and then donated to the CNCF. Backstage is currently in the Incubation phase. Read the announcement [here](https://backstage.io/blog/2022/03/16/backstage-turns-two#out-of-the-sandbox-and-into-incubation). - +CNCF logo diff --git a/docs/overview/what-is-backstage.md b/docs/overview/what-is-backstage.md index f18993770a..e91b9774d7 100644 --- a/docs/overview/what-is-backstage.md +++ b/docs/overview/what-is-backstage.md @@ -40,7 +40,7 @@ Out of the box, Backstage includes: Backstage is a CNCF Incubation project after graduating from Sandbox. Read the announcement [here](https://backstage.io/blog/2022/03/16/backstage-turns-two#out-of-the-sandbox-and-into-incubation). - +CNCF logo ## Benefits diff --git a/microsite/src/pages/plugins/plugins.module.scss b/microsite/src/pages/plugins/plugins.module.scss index dbd0c62c75..30c4404fed 100644 --- a/microsite/src/pages/plugins/plugins.module.scss +++ b/microsite/src/pages/plugins/plugins.module.scss @@ -32,7 +32,8 @@ position: absolute; text-align: center; font-weight: bold; - + color: black; + transform: translateY(var(--translateY)) translateX(var(--translateX)) rotate(var(--rotation)); transform-origin: bottom left; diff --git a/microsite/src/theme/customTheme.scss b/microsite/src/theme/customTheme.scss index 57e1f8828e..4c71d43786 100644 --- a/microsite/src/theme/customTheme.scss +++ b/microsite/src/theme/customTheme.scss @@ -7,7 +7,7 @@ --ifm-color-primary-darker: #2e9e8a; --ifm-color-primary-darkest: #268271; } - +// for docs and releases #__docusaurus { .theme-doc-markdown { a { @@ -15,7 +15,34 @@ } } } - +// for blog +#__docusaurus { + .row { + .col { + &--7 { + a { + text-decoration: underline; + } + } + } + } +} +// for community page +#__docusaurus { + .communityPage_src-pages-demos-demos-module { + a:not([class]) { + text-decoration: underline; + } + } +} +// for plugin page +#__docusaurus { + .pluginsPage_src-pages-plugins-plugins-module { + a:not([class]) { + text-decoration: underline; + } + } +} .footerLogo { background-image: url(/img/logo.svg); background-repeat: no-repeat; From 4461c556c63e914c710305d60bec1963e9d0f367 Mon Sep 17 00:00:00 2001 From: secustor Date: Thu, 25 Jan 2024 18:09:50 +0100 Subject: [PATCH 051/468] adapt test Signed-off-by: secustor --- .../src/homePageComponents/FeaturedDocsCard/Content.test.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.test.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.test.tsx index 427a1484d5..c49b41eb18 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.test.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.test.tsx @@ -66,8 +66,7 @@ describe('', () => { }, ); const docsCardContent = getByTestId('docs-card-content'); - const docsEntity = getByText('Getting Started Docs'); - + const docsEntity = getByText('getting-started-with-idp'); expect(docsCardContent).toContainElement(docsEntity); }); }); From 49759eaab264613ffd8b679b3e98736dfabc95b1 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Thu, 25 Jan 2024 22:41:24 +0530 Subject: [PATCH 052/468] prettier Signed-off-by: npiyush97 --- microsite/src/pages/plugins/plugins.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/src/pages/plugins/plugins.module.scss b/microsite/src/pages/plugins/plugins.module.scss index 30c4404fed..fb17e90625 100644 --- a/microsite/src/pages/plugins/plugins.module.scss +++ b/microsite/src/pages/plugins/plugins.module.scss @@ -33,7 +33,7 @@ text-align: center; font-weight: bold; color: black; - + transform: translateY(var(--translateY)) translateX(var(--translateX)) rotate(var(--rotation)); transform-origin: bottom left; From 8823f7c1b5d64a2e6cc2ac998484e37679a4edd9 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Thu, 25 Jan 2024 23:00:13 +0530 Subject: [PATCH 053/468] forgot this bit Signed-off-by: npiyush97 --- microsite/src/theme/customTheme.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsite/src/theme/customTheme.scss b/microsite/src/theme/customTheme.scss index 4c71d43786..f9b844d3dc 100644 --- a/microsite/src/theme/customTheme.scss +++ b/microsite/src/theme/customTheme.scss @@ -29,7 +29,8 @@ } // for community page #__docusaurus { - .communityPage_src-pages-demos-demos-module { + div[class^='communityPage'], + div[class*='communityPage'] { a:not([class]) { text-decoration: underline; } From 7f9c43dec12a80ecdf9d8816fd69f46c7014f2e3 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Thu, 25 Jan 2024 23:09:18 +0530 Subject: [PATCH 054/468] clean up Signed-off-by: npiyush97 --- microsite/src/theme/customTheme.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/microsite/src/theme/customTheme.scss b/microsite/src/theme/customTheme.scss index f9b844d3dc..b29f76d3c0 100644 --- a/microsite/src/theme/customTheme.scss +++ b/microsite/src/theme/customTheme.scss @@ -38,7 +38,8 @@ } // for plugin page #__docusaurus { - .pluginsPage_src-pages-plugins-plugins-module { + div[class^='pluginsPage'], + div[class*='pluginsPage'] { a:not([class]) { text-decoration: underline; } From 81452d78e4cd87dcda77c4a98730ae8fc967f539 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Thu, 25 Jan 2024 23:15:15 +0530 Subject: [PATCH 055/468] hubspot color change Signed-off-by: npiyush97 --- microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss b/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss index 9533890660..20790c58f0 100644 --- a/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss +++ b/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss @@ -37,4 +37,10 @@ $page-header-height: 44px; padding-right: 2rem; } + select[class*='hs-input'] { + color: black; + } + input[class*='hs-button'] { + color: black; + } } From c57d61f0a9c2754bcb019fc8d555a14c5c88ab99 Mon Sep 17 00:00:00 2001 From: shmaram Date: Thu, 25 Jan 2024 23:10:30 +0200 Subject: [PATCH 056/468] Return undefined for invalid filters + fix README.md Signed-off-by: shmaram --- plugins/home/README.md | 2 +- plugins/home/src/api/config.test.ts | 21 ++++++------ plugins/home/src/api/config.ts | 19 +++++++---- .../VisitedByType/Content.test.tsx | 32 ++++++++++++++++++- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/plugins/home/README.md b/plugins/home/README.md index fdc5ce833b..066e637725 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -353,7 +353,7 @@ home: value: ``` -Filters that are not defined according to the docs will be ignored. +`filterBy` configs that are not defined in the above format will be ignored. In order to validate the config you can use `backstage/cli config:check` diff --git a/plugins/home/src/api/config.test.ts b/plugins/home/src/api/config.test.ts index c2b715ec08..178287bb3d 100644 --- a/plugins/home/src/api/config.test.ts +++ b/plugins/home/src/api/config.test.ts @@ -33,15 +33,14 @@ describe('config', () => { }); }); - it('throws an error for invalid filter', async () => { - const mockConfig = new MockConfigApi({ + it('returns undefined for invalid filter', async () => { + const mockInvalidConfig = new MockConfigApi({ myField: 'pathname', operator: '==', value: '3', }); - expect(() => readFilterConfig(mockConfig)).toThrow( - "Invalid config, Error: Missing required config value at 'field'", - ); + const res = readFilterConfig(mockInvalidConfig); + expect(res).toEqual(undefined); }); }); @@ -67,22 +66,22 @@ describe('config', () => { ]); }); - it('return undefined for invalid filter', async () => { - const mockConfig1 = new MockConfigApi({ + it('returns only invalid filters', async () => { + const mockValidConfig = new MockConfigApi({ field: 'id', operator: '==', value: '3', }); - const mockConfig2 = new MockConfigApi({ + const mockInvalidConfig = new MockConfigApi({ myField: 'pathname', operator: '==', value: 'path', }); const res = createFilterByQueryParamFromConfig([ - mockConfig1, - mockConfig2, + mockValidConfig, + mockInvalidConfig, ]); - expect(res).toEqual(undefined); + expect(res).toEqual([{ field: 'id', operator: '==', value: '3' }]); }); }); }); diff --git a/plugins/home/src/api/config.ts b/plugins/home/src/api/config.ts index a8af29fc9e..3cc6d93f24 100644 --- a/plugins/home/src/api/config.ts +++ b/plugins/home/src/api/config.ts @@ -24,11 +24,13 @@ import { Config } from '@backstage/config'; * * @public */ -export function readFilterConfig(config: Config): { - field: keyof Visit; - operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; - value: string | number; -} { +export function readFilterConfig(config: Config): + | { + field: keyof Visit; + operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + value: string | number; + } + | undefined { try { const field = config.getString('field') as keyof Visit; const operator = config.getString('operator') as @@ -42,7 +44,8 @@ export function readFilterConfig(config: Config): { const value = config.getString('value'); return { field, operator, value }; } catch (error) { - throw new Error(`Invalid config, ${error}`); + // invalid filter config - ignore filter + return undefined; } } @@ -57,7 +60,9 @@ export function createFilterByQueryParamFromConfig( configs: Config[], ): VisitsApiQueryParams['filterBy'] | undefined { try { - return configs.map(readFilterConfig); + return configs + .map(readFilterConfig) + .filter(Boolean) as VisitsApiQueryParams['filterBy']; } catch { return undefined; } diff --git a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx index 1234291f1a..2a7a6481ba 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Content.test.tsx @@ -43,12 +43,21 @@ const visits = [ }, ]; -const mockVisitsApi = { +let mockVisitsApi = { save: async () => visits[0], list: async () => visits, }; describe('', () => { + beforeEach(() => { + mockVisitsApi = { + save: async () => visits[0], + list: async () => visits, + }; + }); + + afterEach(() => jest.resetAllMocks()); + it('renders', async () => { const { getByText } = await renderInTestApp( @@ -207,6 +216,11 @@ describe('', () => { operator: '==', value: '/tech-radar', }, + { + field: 'pathname', + operator: '==', + value: '/explore', + }, ], }, }, @@ -235,12 +249,28 @@ describe('', () => { field: 'timestamp', }, ], + filterBy: [ + { + field: 'pathname', + operator: '==', + value: '/explore', + }, + ], }); }); }); }); describe('', () => { + beforeEach(() => { + mockVisitsApi = { + save: async () => visits[0], + list: async () => visits, + }; + }); + + afterEach(() => jest.resetAllMocks()); + it('renders', async () => { const { getByText } = await renderInTestApp( From 11e0767761a97b97663b7660d713624cfa18506e Mon Sep 17 00:00:00 2001 From: Emmanuel Auffray Date: Fri, 26 Jan 2024 10:42:22 +1300 Subject: [PATCH 057/468] Update template.examples.ts, typo fix on skeleton small typo fix Signed-off-by: Emmanuel Auffray --- .../src/scaffolder/actions/builtin/fetch/template.examples.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.ts index 4c4628e715..1e2212b4ee 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.examples.ts @@ -20,7 +20,7 @@ import yaml from 'yaml'; export const examples: TemplateExample[] = [ { description: - 'Downloads a skelaton directory that lives alongside the template file and fill it out with values.', + 'Downloads a skeleton directory that lives alongside the template file and fill it out with values.', example: yaml.stringify({ steps: [ { From c04c42b61a789221baed98f04933e19df49f549a Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 25 Jan 2024 18:00:28 -0500 Subject: [PATCH 058/468] feat(openapi-tooling): Fix oneOf client duplications. Signed-off-by: Aramis --- .changeset/six-jobs-sin.md | 5 +++++ .changeset/tasty-feet-cheat.md | 5 +++++ .../catalog-client/src/generated/apis/DefaultApi.client.ts | 5 ++++- packages/catalog-client/src/generated/apis/index.ts | 2 +- packages/catalog-client/src/generated/index.ts | 2 +- .../generated/models/AnalyzeLocationEntityField.model.ts | 6 +++++- .../models/AnalyzeLocationExistingEntity.model.ts | 5 ++++- .../models/AnalyzeLocationGenerateEntity.model.ts | 5 ++++- .../src/generated/models/AnalyzeLocationRequest.model.ts | 5 ++++- .../src/generated/models/AnalyzeLocationResponse.model.ts | 5 ++++- .../generated/models/CreateLocation201Response.model.ts | 5 ++++- .../src/generated/models/CreateLocationRequest.model.ts | 6 +++++- .../src/generated/models/EntitiesBatchResponse.model.ts | 5 ++++- .../src/generated/models/EntitiesQueryResponse.model.ts | 5 ++++- .../models/EntitiesQueryResponsePageInfo.model.ts | 6 +++++- .../catalog-client/src/generated/models/Entity.model.ts | 5 ++++- .../src/generated/models/EntityAncestryResponse.model.ts | 5 ++++- .../models/EntityAncestryResponseItemsInner.model.ts | 5 ++++- .../src/generated/models/EntityFacet.model.ts | 6 +++++- .../src/generated/models/EntityFacetsResponse.model.ts | 5 ++++- .../src/generated/models/EntityLink.model.ts | 6 +++++- .../src/generated/models/EntityMeta.model.ts | 6 +++++- .../src/generated/models/EntityRelation.model.ts | 6 +++++- .../src/generated/models/ErrorError.model.ts | 6 +++++- .../src/generated/models/ErrorRequest.model.ts | 6 +++++- .../src/generated/models/ErrorResponse.model.ts | 6 +++++- .../src/generated/models/GetEntitiesByRefsRequest.model.ts | 6 +++++- .../generated/models/GetLocations200ResponseInner.model.ts | 5 ++++- .../catalog-client/src/generated/models/Location.model.ts | 6 +++++- .../src/generated/models/LocationInput.model.ts | 6 +++++- .../src/generated/models/LocationSpec.model.ts | 6 +++++- .../src/generated/models/ModelError.model.ts | 6 +++++- .../src/generated/models/NullableEntity.model.ts | 5 ++++- .../src/generated/models/RecursivePartialEntity.model.ts | 5 ++++- .../generated/models/RecursivePartialEntityMeta.model.ts | 5 ++++- .../models/RecursivePartialEntityMetaAllOf.model.ts | 5 ++++- .../models/RecursivePartialEntityRelation.model.ts | 6 +++++- .../src/generated/models/RefreshEntityRequest.model.ts | 6 +++++- .../generated/models/ValidateEntity400Response.model.ts | 5 ++++- .../models/ValidateEntity400ResponseErrorsInner.model.ts | 7 ++++++- .../src/generated/models/ValidateEntityRequest.model.ts | 6 +++++- packages/catalog-client/src/generated/models/index.ts | 2 +- packages/catalog-client/src/generated/pluginId.ts | 2 +- .../templates/typescript-backstage/licenseInfo.mustache | 2 -- .../templates/typescript-backstage/model.mustache | 6 ------ .../modelGenericAdditionalProperties.mustache | 4 +--- 46 files changed, 183 insertions(+), 52 deletions(-) create mode 100644 .changeset/six-jobs-sin.md create mode 100644 .changeset/tasty-feet-cheat.md diff --git a/.changeset/six-jobs-sin.md b/.changeset/six-jobs-sin.md new file mode 100644 index 0000000000..307190ae81 --- /dev/null +++ b/.changeset/six-jobs-sin.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': patch +--- + +Internal updates to auto-generated files. diff --git a/.changeset/tasty-feet-cheat.md b/.changeset/tasty-feet-cheat.md new file mode 100644 index 0000000000..1e93439fa6 --- /dev/null +++ b/.changeset/tasty-feet-cheat.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Fixes an issue where comments would be duplicated in the template. Also, removes a header with the title and version of the OpenAPI spec from generated code. diff --git a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts index 65b8699370..99ab7bc4b1 100644 --- a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts +++ b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { DiscoveryApi } from '../types/discovery'; import { FetchApi } from '../types/fetch'; import crossFetch from 'cross-fetch'; diff --git a/packages/catalog-client/src/generated/apis/index.ts b/packages/catalog-client/src/generated/apis/index.ts index ad2f72c5b2..51dcca33fe 100644 --- a/packages/catalog-client/src/generated/apis/index.ts +++ b/packages/catalog-client/src/generated/apis/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. diff --git a/packages/catalog-client/src/generated/index.ts b/packages/catalog-client/src/generated/index.ts index 0cf3e9cc49..bb399e97a0 100644 --- a/packages/catalog-client/src/generated/index.ts +++ b/packages/catalog-client/src/generated/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts index a1c1e1f669..08fd73a0e6 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface AnalyzeLocationEntityField { /** * A text to show to the user to inform about the choices made. Like, it could say \"Found a CODEOWNERS file that covers this target, so we suggest leaving this field empty; which would currently make it owned by X\" where X is taken from the codeowners file. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts index 5ede0fb60b..058a4a9f29 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { Entity } from '../models/Entity.model'; import { LocationSpec } from '../models/LocationSpec.model'; diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts index 886aac05d9..b65b7ef29d 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { AnalyzeLocationEntityField } from '../models/AnalyzeLocationEntityField.model'; import { RecursivePartialEntity } from '../models/RecursivePartialEntity.model'; diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts index e8e9542f5a..bd70c9b2ba 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { LocationInput } from '../models/LocationInput.model'; export interface AnalyzeLocationRequest { diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts index bf60aa8018..ffde7ec576 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { AnalyzeLocationExistingEntity } from '../models/AnalyzeLocationExistingEntity.model'; import { AnalyzeLocationGenerateEntity } from '../models/AnalyzeLocationGenerateEntity.model'; diff --git a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts index d090d80bf2..763c956c76 100644 --- a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { Entity } from '../models/Entity.model'; import { Location } from '../models/Location.model'; diff --git a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts index 6ef950072e..596879e519 100644 --- a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface CreateLocationRequest { target: string; type: string; diff --git a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts index d09b2cc086..a7085db16c 100644 --- a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { NullableEntity } from '../models/NullableEntity.model'; export interface EntitiesBatchResponse { diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts index 10d4747caf..106176890b 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntitiesQueryResponsePageInfo } from '../models/EntitiesQueryResponsePageInfo.model'; import { Entity } from '../models/Entity.model'; diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts index 8a7d5b39bb..f257d89675 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface EntitiesQueryResponsePageInfo { /** * The cursor for the next batch of entities. diff --git a/packages/catalog-client/src/generated/models/Entity.model.ts b/packages/catalog-client/src/generated/models/Entity.model.ts index 59ef9bc741..0c8c1347bf 100644 --- a/packages/catalog-client/src/generated/models/Entity.model.ts +++ b/packages/catalog-client/src/generated/models/Entity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntityMeta } from '../models/EntityMeta.model'; import { EntityRelation } from '../models/EntityRelation.model'; diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts index 08076fcf36..9e0369659a 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntityAncestryResponseItemsInner } from '../models/EntityAncestryResponseItemsInner.model'; export interface EntityAncestryResponse { diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts index 7713c21295..b03ea1e5ef 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { Entity } from '../models/Entity.model'; export interface EntityAncestryResponseItemsInner { diff --git a/packages/catalog-client/src/generated/models/EntityFacet.model.ts b/packages/catalog-client/src/generated/models/EntityFacet.model.ts index 3872091d77..76e058568a 100644 --- a/packages/catalog-client/src/generated/models/EntityFacet.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacet.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface EntityFacet { value: string; count: number; diff --git a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts index 0ff37e4f2f..777499b6af 100644 --- a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntityFacet } from '../models/EntityFacet.model'; export interface EntityFacetsResponse { diff --git a/packages/catalog-client/src/generated/models/EntityLink.model.ts b/packages/catalog-client/src/generated/models/EntityLink.model.ts index f52f9f2df0..ae56e9dab4 100644 --- a/packages/catalog-client/src/generated/models/EntityLink.model.ts +++ b/packages/catalog-client/src/generated/models/EntityLink.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + /** * A link to external information that is related to the entity. */ diff --git a/packages/catalog-client/src/generated/models/EntityMeta.model.ts b/packages/catalog-client/src/generated/models/EntityMeta.model.ts index cb0fd28fdc..72f9f66e0d 100644 --- a/packages/catalog-client/src/generated/models/EntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/EntityMeta.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntityLink } from '../models/EntityLink.model'; /** @@ -21,6 +24,7 @@ import { EntityLink } from '../models/EntityLink.model'; */ export interface EntityMeta { [key: string]: any; + /** * A list of external hyperlinks related to the entity. */ diff --git a/packages/catalog-client/src/generated/models/EntityRelation.model.ts b/packages/catalog-client/src/generated/models/EntityRelation.model.ts index 1107f7563c..5a52707369 100644 --- a/packages/catalog-client/src/generated/models/EntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/EntityRelation.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + /** * A relation of a specific type to another entity in the catalog. */ diff --git a/packages/catalog-client/src/generated/models/ErrorError.model.ts b/packages/catalog-client/src/generated/models/ErrorError.model.ts index 9dbfa22813..2459bd3954 100644 --- a/packages/catalog-client/src/generated/models/ErrorError.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorError.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface ErrorError { name: string; message: string; diff --git a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts index e6306b87f5..8d52f0ff4e 100644 --- a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface ErrorRequest { method: string; url: string; diff --git a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts index 1f55c34b4a..0efd2893e6 100644 --- a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface ErrorResponse { statusCode: number; } diff --git a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts index bdda7c2fa6..5d07c5c172 100644 --- a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts +++ b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface GetEntitiesByRefsRequest { entityRefs: Array; fields?: Array; diff --git a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts index 134306cce7..db21eeda95 100644 --- a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts +++ b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { Location } from '../models/Location.model'; export interface GetLocations200ResponseInner { diff --git a/packages/catalog-client/src/generated/models/Location.model.ts b/packages/catalog-client/src/generated/models/Location.model.ts index 27e47a5a25..44f69d85b0 100644 --- a/packages/catalog-client/src/generated/models/Location.model.ts +++ b/packages/catalog-client/src/generated/models/Location.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + /** * Entity location for a specific entity. */ diff --git a/packages/catalog-client/src/generated/models/LocationInput.model.ts b/packages/catalog-client/src/generated/models/LocationInput.model.ts index 8de7e3e6c9..0468e1a40a 100644 --- a/packages/catalog-client/src/generated/models/LocationInput.model.ts +++ b/packages/catalog-client/src/generated/models/LocationInput.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface LocationInput { type: string; target: string; diff --git a/packages/catalog-client/src/generated/models/LocationSpec.model.ts b/packages/catalog-client/src/generated/models/LocationSpec.model.ts index 80db2cadf2..15826e5fec 100644 --- a/packages/catalog-client/src/generated/models/LocationSpec.model.ts +++ b/packages/catalog-client/src/generated/models/LocationSpec.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + /** * Holds the entity location information. */ diff --git a/packages/catalog-client/src/generated/models/ModelError.model.ts b/packages/catalog-client/src/generated/models/ModelError.model.ts index 989e755c9a..862398915d 100644 --- a/packages/catalog-client/src/generated/models/ModelError.model.ts +++ b/packages/catalog-client/src/generated/models/ModelError.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,12 +14,16 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { ErrorError } from '../models/ErrorError.model'; import { ErrorRequest } from '../models/ErrorRequest.model'; import { ErrorResponse } from '../models/ErrorResponse.model'; export interface ModelError { [key: string]: any; + error: ErrorError; request?: ErrorRequest; response: ErrorResponse; diff --git a/packages/catalog-client/src/generated/models/NullableEntity.model.ts b/packages/catalog-client/src/generated/models/NullableEntity.model.ts index 36aff86ba8..c9945dcb39 100644 --- a/packages/catalog-client/src/generated/models/NullableEntity.model.ts +++ b/packages/catalog-client/src/generated/models/NullableEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntityMeta } from '../models/EntityMeta.model'; import { EntityRelation } from '../models/EntityRelation.model'; diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts index 8de7824550..490485e9bb 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { RecursivePartialEntityMeta } from '../models/RecursivePartialEntityMeta.model'; import { RecursivePartialEntityRelation } from '../models/RecursivePartialEntityRelation.model'; diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts index b21cc1bb21..58f895c96a 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntityLink } from '../models/EntityLink.model'; export interface RecursivePartialEntityMeta { diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts index 5111cd7f9f..d74267080e 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { EntityLink } from '../models/EntityLink.model'; /** diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts index 50635b69f4..b715b84049 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + /** * A relation of a specific type to another entity in the catalog. */ diff --git a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts index 573523385b..78da2eb45a 100644 --- a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + /** * Options for requesting a refresh of entities in the catalog. */ diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts index 0031b2edc2..e6f824104d 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,9 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ import { ValidateEntity400ResponseErrorsInner } from '../models/ValidateEntity400ResponseErrorsInner.model'; export interface ValidateEntity400Response { diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts index 6223df27af..9088c7fcf4 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,8 +14,13 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface ValidateEntity400ResponseErrorsInner { [key: string]: any; + name: string; message: string; } diff --git a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts index d5ce1be5c9..414ffb5733 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. @@ -14,6 +14,10 @@ * limitations under the License. */ +/** + * NOTE: This class is auto generated, do not edit the class manually. + */ + export interface ValidateEntityRequest { location: string; entity: { [key: string]: any }; diff --git a/packages/catalog-client/src/generated/models/index.ts b/packages/catalog-client/src/generated/models/index.ts index 5a93dbb33a..d6c81becb5 100644 --- a/packages/catalog-client/src/generated/models/index.ts +++ b/packages/catalog-client/src/generated/models/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. diff --git a/packages/catalog-client/src/generated/pluginId.ts b/packages/catalog-client/src/generated/pluginId.ts index c8c8ce1ca3..b45379f0b0 100644 --- a/packages/catalog-client/src/generated/pluginId.ts +++ b/packages/catalog-client/src/generated/pluginId.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 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. diff --git a/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache b/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache index afc90f6227..fa54b15df0 100644 --- a/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache +++ b/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache @@ -1,7 +1,5 @@ // /** - * {{{appName}}}{{#version}}@{{{.}}}{{/version}} - * * NOTE: This class is auto generated, do not edit the class manually. */ \ No newline at end of file diff --git a/packages/repo-tools/templates/typescript-backstage/model.mustache b/packages/repo-tools/templates/typescript-backstage/model.mustache index 97d7858cb2..565eecb4e8 100644 --- a/packages/repo-tools/templates/typescript-backstage/model.mustache +++ b/packages/repo-tools/templates/typescript-backstage/model.mustache @@ -6,12 +6,6 @@ import { {{classname}} } from '{{filename}}.model'; {{/tsImports}} - -{{#description}} -/** - * {{{.}}} - */ -{{/description}} {{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{#isAlias}}{{>modelAlias}}{{/isAlias}}{{^isAlias}}{{#taggedUnions}}{{>modelTaggedUnion}}{{/taggedUnions}}{{^taggedUnions}}{{#oneOf}}{{#-first}}{{>modelOneOf}}{{/-first}}{{/oneOf}}{{^oneOf}}{{>modelGeneric}}{{/oneOf}}{{/taggedUnions}}{{/isAlias}}{{/isEnum}} {{/model}} {{/models}} diff --git a/packages/repo-tools/templates/typescript-backstage/modelGenericAdditionalProperties.mustache b/packages/repo-tools/templates/typescript-backstage/modelGenericAdditionalProperties.mustache index b8d7d1ceb7..e1e94cea80 100644 --- a/packages/repo-tools/templates/typescript-backstage/modelGenericAdditionalProperties.mustache +++ b/packages/repo-tools/templates/typescript-backstage/modelGenericAdditionalProperties.mustache @@ -1,7 +1,5 @@ {{! Sourced from https://github.com/OpenAPITools/openapi-generator/blob/7347daec61b2cb8d3d28e1ed06fe8b5e682090f8/modules/openapi-generator/src/main/resources/typescript-angular/modelGenericAdditionalProperties.mustache }} {{#additionalPropertiesType}} - - [key: string]: {{{additionalPropertiesType}}}{{#hasVars}} | any{{/hasVars}}; - + [key: string]: {{{additionalPropertiesType}}}; {{/additionalPropertiesType}} \ No newline at end of file From fed57c3b1eb241264c3ee76a99f1cf675aa46369 Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 25 Jan 2024 18:04:39 -0500 Subject: [PATCH 059/468] update the autogenerated file comment Signed-off-by: Aramis --- .../templates/typescript-backstage/licenseInfo.mustache | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache b/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache index fa54b15df0..7a1b10e780 100644 --- a/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache +++ b/packages/repo-tools/templates/typescript-backstage/licenseInfo.mustache @@ -1,5 +1,5 @@ // -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ \ No newline at end of file +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** \ No newline at end of file From ea76a53949c706ddb9c21b6552c1c699f45600b2 Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 25 Jan 2024 18:05:29 -0500 Subject: [PATCH 060/468] update catalog-client to use the new autogenerated comment. Signed-off-by: Aramis --- .../catalog-client/src/generated/apis/DefaultApi.client.ts | 6 +++--- .../generated/models/AnalyzeLocationEntityField.model.ts | 6 +++--- .../generated/models/AnalyzeLocationExistingEntity.model.ts | 6 +++--- .../generated/models/AnalyzeLocationGenerateEntity.model.ts | 6 +++--- .../src/generated/models/AnalyzeLocationRequest.model.ts | 6 +++--- .../src/generated/models/AnalyzeLocationResponse.model.ts | 6 +++--- .../src/generated/models/CreateLocation201Response.model.ts | 6 +++--- .../src/generated/models/CreateLocationRequest.model.ts | 6 +++--- .../src/generated/models/EntitiesBatchResponse.model.ts | 6 +++--- .../src/generated/models/EntitiesQueryResponse.model.ts | 6 +++--- .../generated/models/EntitiesQueryResponsePageInfo.model.ts | 6 +++--- .../catalog-client/src/generated/models/Entity.model.ts | 6 +++--- .../src/generated/models/EntityAncestryResponse.model.ts | 6 +++--- .../models/EntityAncestryResponseItemsInner.model.ts | 6 +++--- .../src/generated/models/EntityFacet.model.ts | 6 +++--- .../src/generated/models/EntityFacetsResponse.model.ts | 6 +++--- .../catalog-client/src/generated/models/EntityLink.model.ts | 6 +++--- .../catalog-client/src/generated/models/EntityMeta.model.ts | 6 +++--- .../src/generated/models/EntityRelation.model.ts | 6 +++--- .../catalog-client/src/generated/models/ErrorError.model.ts | 6 +++--- .../src/generated/models/ErrorRequest.model.ts | 6 +++--- .../src/generated/models/ErrorResponse.model.ts | 6 +++--- .../src/generated/models/GetEntitiesByRefsRequest.model.ts | 6 +++--- .../generated/models/GetLocations200ResponseInner.model.ts | 6 +++--- .../catalog-client/src/generated/models/Location.model.ts | 6 +++--- .../src/generated/models/LocationInput.model.ts | 6 +++--- .../src/generated/models/LocationSpec.model.ts | 6 +++--- .../catalog-client/src/generated/models/ModelError.model.ts | 6 +++--- .../src/generated/models/NullableEntity.model.ts | 6 +++--- .../src/generated/models/RecursivePartialEntity.model.ts | 6 +++--- .../generated/models/RecursivePartialEntityMeta.model.ts | 6 +++--- .../models/RecursivePartialEntityMetaAllOf.model.ts | 6 +++--- .../models/RecursivePartialEntityRelation.model.ts | 6 +++--- .../src/generated/models/RefreshEntityRequest.model.ts | 6 +++--- .../src/generated/models/ValidateEntity400Response.model.ts | 6 +++--- .../models/ValidateEntity400ResponseErrorsInner.model.ts | 6 +++--- .../src/generated/models/ValidateEntityRequest.model.ts | 6 +++--- 37 files changed, 111 insertions(+), 111 deletions(-) diff --git a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts index 99ab7bc4b1..e8e715ab32 100644 --- a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts +++ b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { DiscoveryApi } from '../types/discovery'; import { FetchApi } from '../types/fetch'; import crossFetch from 'cross-fetch'; diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts index 08fd73a0e6..48d2c897a7 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface AnalyzeLocationEntityField { /** diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts index 058a4a9f29..7a1417145e 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { Entity } from '../models/Entity.model'; import { LocationSpec } from '../models/LocationSpec.model'; diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts index b65b7ef29d..3e33c59330 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { AnalyzeLocationEntityField } from '../models/AnalyzeLocationEntityField.model'; import { RecursivePartialEntity } from '../models/RecursivePartialEntity.model'; diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts index bd70c9b2ba..15e9b96e2c 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { LocationInput } from '../models/LocationInput.model'; export interface AnalyzeLocationRequest { diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts index ffde7ec576..e22d45627a 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { AnalyzeLocationExistingEntity } from '../models/AnalyzeLocationExistingEntity.model'; import { AnalyzeLocationGenerateEntity } from '../models/AnalyzeLocationGenerateEntity.model'; diff --git a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts index 763c956c76..367e701af8 100644 --- a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { Entity } from '../models/Entity.model'; import { Location } from '../models/Location.model'; diff --git a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts index 596879e519..badddbf60d 100644 --- a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface CreateLocationRequest { target: string; diff --git a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts index a7085db16c..3275a7b6e7 100644 --- a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { NullableEntity } from '../models/NullableEntity.model'; export interface EntitiesBatchResponse { diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts index 106176890b..9950dd98d1 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntitiesQueryResponsePageInfo } from '../models/EntitiesQueryResponsePageInfo.model'; import { Entity } from '../models/Entity.model'; diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts index f257d89675..4d2a0af058 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface EntitiesQueryResponsePageInfo { /** diff --git a/packages/catalog-client/src/generated/models/Entity.model.ts b/packages/catalog-client/src/generated/models/Entity.model.ts index 0c8c1347bf..f4a30fbf66 100644 --- a/packages/catalog-client/src/generated/models/Entity.model.ts +++ b/packages/catalog-client/src/generated/models/Entity.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntityMeta } from '../models/EntityMeta.model'; import { EntityRelation } from '../models/EntityRelation.model'; diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts index 9e0369659a..4e0ead7d3b 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntityAncestryResponseItemsInner } from '../models/EntityAncestryResponseItemsInner.model'; export interface EntityAncestryResponse { diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts index b03ea1e5ef..22b59e201f 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { Entity } from '../models/Entity.model'; export interface EntityAncestryResponseItemsInner { diff --git a/packages/catalog-client/src/generated/models/EntityFacet.model.ts b/packages/catalog-client/src/generated/models/EntityFacet.model.ts index 76e058568a..aa637501f4 100644 --- a/packages/catalog-client/src/generated/models/EntityFacet.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacet.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface EntityFacet { value: string; diff --git a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts index 777499b6af..33aa505a64 100644 --- a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntityFacet } from '../models/EntityFacet.model'; export interface EntityFacetsResponse { diff --git a/packages/catalog-client/src/generated/models/EntityLink.model.ts b/packages/catalog-client/src/generated/models/EntityLink.model.ts index ae56e9dab4..9de21c1b10 100644 --- a/packages/catalog-client/src/generated/models/EntityLink.model.ts +++ b/packages/catalog-client/src/generated/models/EntityLink.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** /** * A link to external information that is related to the entity. diff --git a/packages/catalog-client/src/generated/models/EntityMeta.model.ts b/packages/catalog-client/src/generated/models/EntityMeta.model.ts index 72f9f66e0d..10cda7f226 100644 --- a/packages/catalog-client/src/generated/models/EntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/EntityMeta.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntityLink } from '../models/EntityLink.model'; /** diff --git a/packages/catalog-client/src/generated/models/EntityRelation.model.ts b/packages/catalog-client/src/generated/models/EntityRelation.model.ts index 5a52707369..14f7d0fab3 100644 --- a/packages/catalog-client/src/generated/models/EntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/EntityRelation.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** /** * A relation of a specific type to another entity in the catalog. diff --git a/packages/catalog-client/src/generated/models/ErrorError.model.ts b/packages/catalog-client/src/generated/models/ErrorError.model.ts index 2459bd3954..a9d3b11fdd 100644 --- a/packages/catalog-client/src/generated/models/ErrorError.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorError.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface ErrorError { name: string; diff --git a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts index 8d52f0ff4e..be62627814 100644 --- a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface ErrorRequest { method: string; diff --git a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts index 0efd2893e6..008f8b7348 100644 --- a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface ErrorResponse { statusCode: number; diff --git a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts index 5d07c5c172..07c6500c9f 100644 --- a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts +++ b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface GetEntitiesByRefsRequest { entityRefs: Array; diff --git a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts index db21eeda95..94c40d2233 100644 --- a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts +++ b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { Location } from '../models/Location.model'; export interface GetLocations200ResponseInner { diff --git a/packages/catalog-client/src/generated/models/Location.model.ts b/packages/catalog-client/src/generated/models/Location.model.ts index 44f69d85b0..456b76ec7b 100644 --- a/packages/catalog-client/src/generated/models/Location.model.ts +++ b/packages/catalog-client/src/generated/models/Location.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** /** * Entity location for a specific entity. diff --git a/packages/catalog-client/src/generated/models/LocationInput.model.ts b/packages/catalog-client/src/generated/models/LocationInput.model.ts index 0468e1a40a..41537aaded 100644 --- a/packages/catalog-client/src/generated/models/LocationInput.model.ts +++ b/packages/catalog-client/src/generated/models/LocationInput.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface LocationInput { type: string; diff --git a/packages/catalog-client/src/generated/models/LocationSpec.model.ts b/packages/catalog-client/src/generated/models/LocationSpec.model.ts index 15826e5fec..cd9eb284f7 100644 --- a/packages/catalog-client/src/generated/models/LocationSpec.model.ts +++ b/packages/catalog-client/src/generated/models/LocationSpec.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** /** * Holds the entity location information. diff --git a/packages/catalog-client/src/generated/models/ModelError.model.ts b/packages/catalog-client/src/generated/models/ModelError.model.ts index 862398915d..7bfa165493 100644 --- a/packages/catalog-client/src/generated/models/ModelError.model.ts +++ b/packages/catalog-client/src/generated/models/ModelError.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { ErrorError } from '../models/ErrorError.model'; import { ErrorRequest } from '../models/ErrorRequest.model'; import { ErrorResponse } from '../models/ErrorResponse.model'; diff --git a/packages/catalog-client/src/generated/models/NullableEntity.model.ts b/packages/catalog-client/src/generated/models/NullableEntity.model.ts index c9945dcb39..b4344a3bfb 100644 --- a/packages/catalog-client/src/generated/models/NullableEntity.model.ts +++ b/packages/catalog-client/src/generated/models/NullableEntity.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntityMeta } from '../models/EntityMeta.model'; import { EntityRelation } from '../models/EntityRelation.model'; diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts index 490485e9bb..db5ac31de3 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { RecursivePartialEntityMeta } from '../models/RecursivePartialEntityMeta.model'; import { RecursivePartialEntityRelation } from '../models/RecursivePartialEntityRelation.model'; diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts index 58f895c96a..93e849ac99 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntityLink } from '../models/EntityLink.model'; export interface RecursivePartialEntityMeta { diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts index d74267080e..7e06368102 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { EntityLink } from '../models/EntityLink.model'; /** diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts index b715b84049..9179add222 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** /** * A relation of a specific type to another entity in the catalog. diff --git a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts index 78da2eb45a..c5ab4c38eb 100644 --- a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** /** * Options for requesting a refresh of entities in the catalog. diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts index e6f824104d..7cd5044782 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** import { ValidateEntity400ResponseErrorsInner } from '../models/ValidateEntity400ResponseErrorsInner.model'; export interface ValidateEntity400Response { diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts index 9088c7fcf4..5617ee00bb 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface ValidateEntity400ResponseErrorsInner { [key: string]: any; diff --git a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts index 414ffb5733..152f28cf85 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -/** - * NOTE: This class is auto generated, do not edit the class manually. - */ +// ****************************************************************** +// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * +// ****************************************************************** export interface ValidateEntityRequest { location: string; From 2e440d4723dd2f6a5553c12eb53512fa2aa00656 Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 25 Jan 2024 23:42:14 -0500 Subject: [PATCH 061/468] docs: add a central glossary Signed-off-by: Aramis --- docs/auth/glossary.md | 30 -- docs/local-dev/cli-build-system.md | 13 +- docs/local-dev/cli-overview.md | 20 +- docs/overview/glossary.md | 26 -- docs/permissions/concepts.md | 14 +- docs/permissions/custom-rules.md | 2 +- .../02-adding-a-basic-permission-check.md | 4 +- .../03-adding-a-resource-permission-check.md | 2 +- docs/references/glossary.md | 310 ++++++++++++++++++ microsite/docusaurus.config.js | 8 + microsite/sidebars.json | 9 +- 11 files changed, 341 insertions(+), 97 deletions(-) delete mode 100644 docs/auth/glossary.md delete mode 100644 docs/overview/glossary.md create mode 100644 docs/references/glossary.md diff --git a/docs/auth/glossary.md b/docs/auth/glossary.md deleted file mode 100644 index ad7ae78d19..0000000000 --- a/docs/auth/glossary.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -id: glossary -title: Glossary -description: All Glossaries related to auth ---- - -- **Popup** - A separate browser window opened on top of the previous one. -- **OAuth** - More specifically OAuth 2.0, a standard protocol for - authorization. See [oauth.net/2/](https://oauth.net/2/). -- **OpenID Connect** - A layer on top of OAuth which standardises - authentication. See - [en.wikipedia.org/wiki/OpenID_Connect](https://en.wikipedia.org/wiki/OpenID_Connect). -- **JWT** - JSON Web Token, a popular JSON based token format that is commonly - encrypted and/or signed, see - [en.wikipedia.org/wiki/JSON_Web_Token](https://en.wikipedia.org/wiki/JSON_Web_Token) -- **Scope** - A string that describes a certain type of access that can be - granted to a user using OAuth. -- **Access token** - A token that gives access to perform actions on behalf of a - user. It will commonly have a short expiry time, and be limited to a set of - scopes. Part of the OAuth protocol. -- **ID token** - A JWT used to prove a user's identity, containing for example - the user's email. Part of OpenID Connect. -- **Offline access** - OAuth flow that results in both a refresh and access - token, where the refresh token has a long expiration or never expires, and can - be used to request more access tokens in the future. This lets the user go - "offline" with respect to the token issuer, but still be able to request more - tokens at a later time without further direct interaction for the user. -- **Code grant** - OAuth flow where the client receives an authorization code - that is passed to the backend to be exchanged for an access token and possibly - refresh token. diff --git a/docs/local-dev/cli-build-system.md b/docs/local-dev/cli-build-system.md index 51c328c871..a95db95f85 100644 --- a/docs/local-dev/cli-build-system.md +++ b/docs/local-dev/cli-build-system.md @@ -258,7 +258,7 @@ When building CommonJS or ESM output, the build commands will always use `src/index.ts` as the entrypoint. All non-relative modules imports are considered external, meaning the Rollup build will only compile the source code of the package itself. All import statements of external dependencies, even within the same -monorepo, will stay intact. +[monorepo](../references/glossary.md#monorepo), will stay intact. The build of the type definitions works quite differently. The entrypoint of the type definition build is the relative location of the package within the @@ -307,11 +307,12 @@ support for them instead. ### Frontend Production -The frontend production bundling creates your typical web content bundle, all -contained within a single folder, ready for static serving. It is used when building -packages with the `'frontend'` role, and unlike the development bundling there is no way to -build a production bundle of an individual plugin. The output of the bundling -process is written to the `dist` folder in the package. +The frontend production bundling creates your typical web content +[bundle](../references/glossary.md#bundle), all contained within a single +folder, ready for static serving. It is used when building packages with the +`'frontend'` role, and unlike the development bundling there is no way to +build a production [bundle](../references/glossary.md#bundle) of an individual plugin. +The output of the bundling process is written to the `dist` folder in the package. Just like the development bundling, the production bundling is based on [Webpack](https://webpack.js.org/). It uses the diff --git a/docs/local-dev/cli-overview.md b/docs/local-dev/cli-overview.md index 74c09d2865..4ab8bb21ac 100644 --- a/docs/local-dev/cli-overview.md +++ b/docs/local-dev/cli-overview.md @@ -7,11 +7,12 @@ description: Overview of the Backstage CLI ## Introduction A goal of Backstage is to provide a delightful developer experience in and -around the project. Creating new apps and plugins should be simple, iteration +around the project. Creating new [apps](../references/glossary.md#app) and +[plugins](../references/glossary.md#plugin) should be simple, iteration speed should be fast, and the overhead of maintaining custom tooling should be minimal. As a part of accomplishing this goal, Backstage provides its own build system and tooling, delivered primarily through the -[`@backstage/cli`](https://www.npmjs.com/package/@backstage/cli) package. When +[`@backstage/cli`](https://www.npmjs.com/package/@backstage/cli) [package](../references/glossary.md#package). When creating an app using [`@backstage/create-app`](https://www.npmjs.com/package/@backstage/create-app), you receive a project that's already prepared with a typical setup and package @@ -36,18 +37,3 @@ The Backstage CLI intentionally does not provide many hooks for overriding or customizing the build process. This is to allow for evolution of the CLI without having to take a wide API surface into account. This allows us to iterate and improve the tooling, as well as to more easily keep the system up to date. - -## Glossary - -- **Package** - A package in the Node.js ecosystem, often published to a package - registry such as [NPM](https://www.npmjs.com/). -- **Monorepo** - A project layout that consists of multiple packages within a - single project, where packages are able to have local dependencies on each - other. Often enabled through tooling such as [lerna](https://lerna.js.org/) - and [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) -- **Local Package** - One of the packages within a monorepo. These package may - or may not also be published to a package registry. -- **Bundle** - A collection of the deployment artifacts. The output of the - bundling process, which brings a collection of packages into a single - collection of deployment artifacts. -- **Package Role** - The declared role of a package, see [package roles](./cli-build-system.md#package-roles). diff --git a/docs/overview/glossary.md b/docs/overview/glossary.md deleted file mode 100644 index ab7d224f4d..0000000000 --- a/docs/overview/glossary.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -id: glossary -title: Backstage Glossary -# prettier-ignore -description: List of terms, abbreviations, and phrases used in Backstage, together with their explanations. ---- - -The Backstage Glossary lists terms, abbreviations, and phrases used in -Backstage, together with their explanations. We encourage you to use the -terminology below for clarity and consistency when discussing Backstage. - -See also [Authentication Glossary](../auth/glossary.md), a separate glossary of terms and phrases -specifically related to the authentication and identity section of Backstage. - -### Backstage User Profiles - -There are three main user profiles for Backstage: the integrator, the -contributor, and the end user (typically a software engineer). - -| Term | Explanation | -| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Integrator | The **integrator** hosts the Backstage app and configures which plugins are available to use in the app. | -| Contributor | The **contributor** adds functionality to the app by writing plugins. | -| End user | The **end user** uses the app's functionality and interacts with its plugins. This profile covers the various roles that help deliver software. The typical end user is a **software engineer**, but users might also consider themselves _designers_, _data scientists_, _product owners_, _engineering managers_, _technical writers_, and so on. | -| Software engineer | The **software engineer** is an **end user** who uses the app's functionality and interacts with its plugins in the course of writing and documenting code. This user is more likely to embed documentation in the code files they produce, and create rough drafts of conceptual pages in collaboration with a **technical writer** or _technical editor_. | -| Technical writer | The **technical writer** is an **end user** who uses the app's functionality and interacts with its plugins in the course of writing and editing documentation. This user is more likely to produce and customize templates and produce conceptual pages to supplement documentation embedded in code files. | diff --git a/docs/permissions/concepts.md b/docs/permissions/concepts.md index e7d65b3583..c3c32601df 100644 --- a/docs/permissions/concepts.md +++ b/docs/permissions/concepts.md @@ -4,22 +4,16 @@ title: Concepts description: A list of important permission framework concepts --- -### Permission - -Any action that a user performs within Backstage may be represented as a permission. More complex actions, like executing a software template, may require authorization for multiple permissions throughout the flow. Permissions are identified by a unique name and optionally include a set of attributes that describe the corresponding action. Plugins are responsible for defining and exposing the permissions they enforce. - -### Policy - -User permissions are authorized by a central, user-defined permission policy. At a high level, a policy is a function that receives a Backstage user and permission, and returns a decision to allow or deny. Policies are expressed as code, which decouples the framework from any particular authorization model, like role-based access control (RBAC) or attribute-based access control (ABAC). - ### Policy decision versus enforcement Two important responsibilities of any authorization system are to decide if a user can do something, and to enforce that decision. In the Backstage permission framework, policies are responsible for decisions and plugins (typically backends) are responsible for enforcing them. ### Resources and rules -In many cases, a permission represents a user's interaction with another object. This object likely has information that policy authors can use to define more granular access. The permission framework introduces two abstractions to account for this: resources and rules. Resources represent the objects that users interact with. Rules are predicate-based controls that tap into a resource's data. For example, the catalog plugin defines a resource for catalog entities and a rule to check if an entity has a given annotation. +In many cases, a permission represents a user's interaction with another object. This object likely has information that policy authors can use to define more granular access. The permission framework introduces two abstractions to account for this: [resources](../references/glossary.md#permission-resource) and [rules](../references/glossary.md#permission-rule). For example, the catalog plugin defines a resource for catalog entities and a rule to check if an entity has a given annotation. ### Conditional decisions -Rules need additional data before they can be used in a decision. For example, the catalog plugin's "has annotation" rule needs to know what annotation to look for on a given entity. Once a rule is bound to relevant information it forms a condition. Conditions are then used to return a conditional decision from a policy. Conditional decisions tell the permission framework to delegate evaluation to the plugin that owns the corresponding resource. Permission requests that result in a conditional decision are allowed if all of the provided conditions evaluate to be true. This conditional behavior avoids coupling between policies and resource schemas, and allows plugins to evaluate complex rules in an efficient way. For example, a plugin may convert a conditional decision to a database query instead of loading and filtering objects in memory. +See [Conditional decisions](../references/glossary.md#conditional-decisions). + +A good example would be the catalog plugin's "has annotation" rule needs to know what annotation to look for on a given entity. The permission framework would respond to a request by the catalog plugin in this case with a condition decision. The catalog plugin would then need to correctly filter for entities matching the "has annotations" condition. This conditional behavior avoids coupling between policies and resource schemas, and allows plugins to evaluate complex rules in an efficient way. For example, a plugin may convert a conditional decision to a database query instead of loading and filtering objects in memory. diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index c0f4e3e156..4b38c8986d 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -4,7 +4,7 @@ title: Defining custom permission rules description: How to define custom permission rules for existing resources --- -For some use cases, you may want to define custom [rules](./concepts.md#resources-and-rules) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of. +For some use cases, you may want to define custom [rules](../references/glossary.md#permission-rules) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of. ## Define a custom rule diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 23bc4e84f1..223d55d3d8 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -4,9 +4,9 @@ title: 2. Adding a basic permission check description: Explains how to add a basic permission check to a Backstage plugin --- -If the outcome of a permission check doesn't need to change for different [resources](../concepts.md#resources-and-rules), you can use a _basic permission check_. For this kind of check, we simply need to define a [permission](../concepts.md#resources-and-rules), and call `authorize` with it. +If the outcome of a permission check doesn't need to change for different [resources](../../references/glossary#permission-resource), you can use a _basic permission check_. For this kind of check, we simply need to define a [permission](../../references/glossary.md#permission), and call `authorize` with it. -For this tutorial, we'll use a basic permission check to authorize the `create` endpoint in our todo-backend. This will allow Backstage integrators to control whether each of their users is authorized to create todos by adjusting their [permission policy](../concepts.md#policy). +For this tutorial, we'll use a basic permission check to authorize the `create` endpoint in our todo-backend. This will allow Backstage integrators to control whether each of their users is authorized to create todos by adjusting their [permission policy](../../references/glossary.md#policy). We'll start by creating a new permission, and then we'll use the permission api to call `authorize` with it during todo creation. diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index a4d51b64d0..3feecc7342 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -4,7 +4,7 @@ title: 3. Adding a resource permission check description: Explains how to add a resource permission check to a Backstage plugin --- -When performing updates (or other operations) on specific [resources](../concepts.md#resources-and-rules), the permissions framework allows for the decision to be based on characteristics of the resource itself. This means that it's possible to write policies that (for example) allow the operation for users that own a resource, and deny the operation otherwise. +When performing updates (or other operations) on specific [resources](../../references/glossary.md#permission-resource), the permissions framework allows for the decision to be based on characteristics of the resource itself. This means that it's possible to write policies that (for example) allow the operation for users that own a resource, and deny the operation otherwise. ## Creating the update permission diff --git a/docs/references/glossary.md b/docs/references/glossary.md new file mode 100644 index 0000000000..b82b2da064 --- /dev/null +++ b/docs/references/glossary.md @@ -0,0 +1,310 @@ +--- +id: glossary +title: Glossary +# prettier-ignore +description: List of terms, abbreviations, and phrases used in Backstage, together with their explanations. +--- + +## Access token + +A [token](#token) that gives access to perform actions on behalf of a user. It will commonly have a short expiry time, and be limited to a set of [scopes](#scope). Part of the [OAuth](#oauth) protocol. + +https://oauth.net/2/access-tokens/ + +## Administrator + +Someone responsible for installing and maintaining a Backstage [app](#app) for an organization. A [user role](#user-role). + +## API + +In the Backstage [Catalog](#catalog), an API is an [entity](#entity) representing a boundary between two [components](#component). + +https://backstage.io/docs/features/software-catalog/system-model + +## App + +An installed instance of Backstage. An app can be local, intended for a single development group or individual developer, or organizational, for use by an entire enterprise. + +## Authorization Code + +A type of [OAuth flow](#oauth) used by confidential and public clients to get an [access token](#access-token). + +https://oauth.net/2/grant-types/authorization-code/ + +## Backstage + +A platform for creating and deploying [developer portals](#developer-portal), originally created at Spotify. + +Backstage is an incubation-stage open source project of the [Cloud Native Computing Foundation](#cloud-native-computing-foundation). + +## Bundle + +A collection of [deployment artifacts](#deployment-artifacts). + +Can also be: The output of the bundling process, which brings a collection of [packages](#package) into a single collection of [deployment artifacts](#deployment-artifacts). + +## Catalog + +An organization's portfolio of software products managed in Backstage. + +## Cloud Native Computing + +A set of technologies that "empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach." ([CNCF Cloud Native Definition v1.0](https://github.com/cncf/toc/blob/main/DEFINITION.md)). + +## Cloud Native Computing Foundation + +A foundation dedicated to the promotion and advancement of [Cloud Native Computing](#Cloud-Native-Computing). The mission of the Cloud Native Computing Foundation (CNCF) is "to make cloud native computing ubiquitous" ([CNCF Charter](https://github.com/cncf/foundation/blob/main/charter.md)). + +CNCF is part of the [Linux Foundation](https://www.linuxfoundation.org/). + +## CNCF + +Cloud Native Computing Foundation. + +## Code Grant + +[OAuth](#oauth) flow where the client receives an [authorization code](#code) that is passed to the backend to be exchanged for an [access token](#access-token) and possibly a [refresh token](#refresh-token). + +## Collators + +Collators transform streams of [documents](#documents) into searchable texts. They're usually responsible for the data transformation and definition and collection process for specific [documents](#documents). Part of [Backstage Search](#search). + +## Component + +A software product that is managed in the Backstage [Software Catalog](#software-catalog). A component can be a service, website, library, data pipeline, or any other piece of software managed as a single project. + +https://backstage.io/docs/features/software-catalog/system-model + +## Condition + +Conditions are used to return a conditional decision from a policy. They contain information about a given entity and restrictions on what types of users can view that entity. + +## Conditional decisions + +[Rules](#permission-rules) need additional data before they can be used in a decision. Once a [rule](#permission-rule) is bound to relevant information it forms a [condition](#condition). Conditional decisions tell the [permission framework](#permission) to delegate evaluation to the [plugin](#plugin) that owns the corresponding [resource](#permission-resource). Permission requests that result in a conditional decision are allowed if all of the provided conditions evaluate to be true. + +## Contributor + +A volunteer who helps to improve an OSS product such as Backstage. This volunteer effort includes coding, testing, technical writing, user support, and other work. A [user role](#user-role). + +## Decorators + +A transform stream. Decorators allow you to add additional information to documents outside of the [collator](#collators). They sit between the [collators](#collators) and the [indexers](#indexer) and can add extra fields to documents as they're being collated and indexed. + +Possible use cases for a decorator could be to bias search results or otherwise improve the search experience in your Backstage instance. Decorators can also be used to remove [metadata](#metadata), filter out, or even add extra documents at index-time. Part of [Backstage Search](#search). + +## Deployment Artifacts + +An executable or package file with all of the necessary information required to deploy the application at runtime. Deployment artifacts can be hosted on [package registries](#package-registry). + +## Developer + +Someone who writes code and develops software. + +A [user role](#user-role) defined as someone who uses a Backstage [app](#app). Might or might not actually be a software developer. + +## Developer Portal + +A centralized system comprising a user interface and database used to facilitate and document all the software projects within an organization. Backstage is both a developer portal and (by virtue of being based on plugins) a platform for creating developer portals. + +## Documents + +An abstract concept representing something that can be found by searching for it. A document can represent a software entity, a TechDocs page, etc. Documents are made up of metadata fields, at a minimum -- a title, text, and location (as in a URL). Part of [Backstage Search](#search). + +## Domain + +In the Backstage Catalog, a domain is an area that relates systems or entities to a business unit. + +https://backstage.io/docs/features/software-catalog/system-model + +## Entity + +What is cataloged in the Backstage Software Catalog. An entity is identified by a unique combination of [kind](#Kind), [namespace](#Namespace), and name. + +## Evaluator + +Someone who assesses whether Backstage is a suitable solution for their organization. The only [user role](#user-role) with a pre-deployment [use case](#use-case). + +## ID Token + +A [JWT](#jwt) used to prove a user's identity, containing for example the user's email. Part of [OpenID Connect](#openid-connect). + +## Index + +An index is a collection of [documents](#documents) of a given type. Part of [Backstage Search](#search). + +## Indexer + +A write stream of [documents](#documents). Part of [Backstage Search](#search). + +## Integrator + +Someone who develops one or more plugins that enable Backstage to interoperate with another software system. A [user role](#user-role). + +## JWT + +JSON Web Token. + +A popular JSON based token format that is commonly encrypted and/or signed, see [en.wikipedia.org/wiki/JSON_Web_Token](https://en.wikipedia.org/wiki/JSON_Web_Token) + +## Kind + +Classification of an [entity](#Entity) in the Backstage Software Catalog, for example _service_, _database_, and _team_. + +## Kubernetes Plugin + +A plugin enabling configuration of Backstage on a Kubernetes cluster. Kubernetes plugin has been promoted to a Backstage core feature. + +## Local Package + +One of the [packages](#package) within a [monorepo](#monorepo). These package may or may not also be published to a [package registry](#package-registry). + +## Monorepo + +A single repository for a collection of related software projects, such as all projects belonging to an organization. + +Can also mean: A project layout that consists of multiple [packages](#package) within a single project, where packages are able to have local dependencies on each other. Often enabled through tooling such as [lerna](https://lerna.js.org/) and [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) + +## Namespace + +In the Backstage Software Catalog, an optional attribute that can be used to organize [entities](#entity). + +## Objective + +A high level goal of a [user role](#User-Role) interacting with Backstage. Some goals of the _administrator_ user role, for example, are to maintain an instance ("app") of Backstage; to add and update functionality via plugins; and to troubleshoot issues. + +## OAuth + +Refers to: OAuth 2.0, a standard protocol for authorization. See [oauth.net/2/](https://oauth.net/2/). + +## Offline Access + +[OAuth](#oauth) flow that results in both a refresh token and [access token](#access-token), where the refresh token has a long expiration or never expires, and can be used to request more access tokens in the future. This lets the user go "offline" with respect to the token issuer, but still be able to request more tokens at a later time without further direct interaction for the user. + +## OpenID Connect + +A layer on top of [OAuth](#oauth) which standardises authentication. See [en.wikipedia.org/wiki/OpenID_Connect](https://en.wikipedia.org/wiki/OpenID_Connect). + +## OSS + +Open source software. + +## Package + +A package in the Node.js ecosystem, often published to a [package registry](#package-registry). + +## Package Registry + +A service that hosts packages. The most prominent example is [NPM](https://www.npmjs.com/). + +## Package Role + +The declared role of a package, see [package roles](../local-dev/cli-build-system.md#package-roles). + +## Permission + +A core Backstage plugin and framework that allows restriction of actions to specific users. + +Any action that a user performs within Backstage may be represented as a permission. More complex actions, like executing a [software template](#software-templates), may require [authorization](#authorization) for multiple permissions throughout the flow. Permissions are identified by a unique name and optionally include a set of attributes that describe the corresponding action. [Plugins](#plugin) are responsible for defining and exposing the permissions they enforce as well as enforcing restrictions from the permission framework. + +https://backstage.io/docs/permissions/overview + +## Permission Resource + +Not to be confused with [Software Catalog resources](#resource). Permission resources represent the objects that users interact with and that can be permissioned. + +## Permission Rule + +Rules are predicate-based controls that tap into a [resource](#permission-resource)'s data. + +## Persona + +Alternative term for a [User Role](#user-role). + +## Plugin + +A module in Backstage that adds a feature. All functionality in Backstage, even the core features, are implemented as plugins. + +## Policy + +User [permissions](#permission) are authorized by a central, user-defined [permission](#permission) policy. At a high level, a policy is a function that receives a Backstage user and [permission](#permission), and returns a decision to allow or deny. Policies are expressed as code, which decouples the framework from any particular [authorization](#authorization) model, like role-based access control (RBAC) or attribute-based access control (ABAC). + +## Policy decision + +Two important responsibilities of any authorization system are to decide if a user can do something, and to enforce that decision. In the Backstage permission framework, policies are responsible for decisions and plugins (typically backends) are responsible for enforcing them. + +## Popup + +A separate browser window opened on top of the previous one. + +## Procedure + +A set of actions that accomplish a goal, usually as part of a [use case](#Use-Case). A procedure can be high-level, containing other procedures, or can be as simple as a single [task](#Task). + +## Query Translators + +An abstraction layer between a search engine and the [Backstage Search](#search) backend. Allows for translation into queries against your search engine. Part of [Backstage Search](#search). + +## Refresh Token + +A string that an [OAuth](#oauth) client can use to get a new access token. + +https://oauth.net/2/refresh-tokens/ + +## Resource + +In the Backstage Catalog, an [entity](#entity) that represents a piece of physical or virtual infrastructure, for example a database, required by a component. + +https://backstage.io/docs/features/software-catalog/system-model + +## Role + +See [User Role](#User-Role). + +## Scope + +A string that describes a certain type of access that can be granted to a user using OAuth. + +## Search + +A Backstage plugin that provides a framework for searching a Backstage [app](#app), including the [Software Catalog](#Software-Catalog) and [TechDocs](#TechDocs). A core feature of Backstage. + +## Search Engines + +Existing search technology that [Backstage Search](#search) can take advantage of through its modular design. Lunr is the default search in [Backstage Search](#search). Part of [Backstage Search](#search). + +## Software Catalog + +A Backstage plugin that provides a framework to keep track of ownership and metadata for any number and type of software [components](#component). A core feature of Backstage. + +## Software Templates + +A Backstage plugin with which to create [components](#component) in Backstage. A core feature of Backstage. + +Can also refer to: A "skeleton" software project created and managed in the Backstage Software Templates tool. + +## System + +In the Backspace Catalog, a system is a collection of [entities](#entity) that cooperate to perform a function. A system generally provides one or a few public APIs and consists of a handful of components, resources and private APIs. + +https://backstage.io/docs/features/software-catalog/system-model + +## Task + +A low-level step-by-step [Procedure](#Procedure). + +## TechDocs + +A documentation solution that manages and generates a technical documentation from Markdown files stored with software component code. A core feature of Backstage. + +## Token + +A string containing information. + +## Use Case + +A purpose for which a [user role](#User-Role) interacts with Backstage. Related to [Objective](#objective): An objective is _what_ the user wants to do; a use case is _how_ the user does it. + +## User Role + +A class of Backspace user for purposes of analyzing [use cases](#use-case). One of: evaluator; administrator; developer; integrator; and contributor. diff --git a/microsite/docusaurus.config.js b/microsite/docusaurus.config.js index df2833b3e2..88fed74ac2 100644 --- a/microsite/docusaurus.config.js +++ b/microsite/docusaurus.config.js @@ -144,6 +144,14 @@ module.exports = { from: '/docs/features/software-templates/testing-scaffolder-alpha', to: '/docs/features/software-templates/migrating-to-rjsf-v5', }, + { + from: '/docs/auth/glossary', + to: '/docs/references/glossary' + }, + { + from: '/docs/overview/glossary', + to: '/docs/references/glossary' + } ], }, ], diff --git a/microsite/sidebars.json b/microsite/sidebars.json index e32e027807..1c151159db 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -37,7 +37,6 @@ "overview/versioning-policy", "overview/threat-model", "overview/support", - "overview/glossary", "overview/logos" ], "Getting Started": [ @@ -318,8 +317,7 @@ "auth/add-auth-provider", "auth/service-to-service-auth", "auth/autologout", - "auth/troubleshooting", - "auth/glossary" + "auth/troubleshooting" ], "Permissions": [ "permissions/overview", @@ -479,6 +477,9 @@ "architecture-decisions/adrs-adr013" ], "FAQ": ["faq/index", "faq/product", "faq/technical"], - "Accessibility": ["accessibility/index"] + "Accessibility": ["accessibility/index"], + "References": [ + "references/glossary" + ] } } From 62ba08930fc3abb7c003a7327015aacb18abcb9f Mon Sep 17 00:00:00 2001 From: Marek Libra Date: Thu, 25 Jan 2024 13:40:34 +0100 Subject: [PATCH 062/468] chore: specify content of a notification Signed-off-by: Marek Libra --- beps/0001-notifications-system/README.md | 33 +++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/beps/0001-notifications-system/README.md b/beps/0001-notifications-system/README.md index c7b0ffff64..2257e07261 100644 --- a/beps/0001-notifications-system/README.md +++ b/beps/0001-notifications-system/README.md @@ -85,12 +85,43 @@ The role of the notifications plugin is to manage the lifecycle of notifications The notification backend stores notification using the [database service](https://backstage.io/docs/backend-system/core-services/index#database). In particular it needs to store the following information for each notification: - ID -- Payload +- Title +- Priority +- Origin +- Topic (optional) +- Creation timestamp (generated) - Receivers - Read status by user +- Payload + - icon (optional) + - message (optional) + - links (list, optional) The receivers is **not** a list of users, but rather a filter that describes who should receive the notification. It must be possible to evaluate this filter in a database query, so that we can efficiently fetch all notifications for a given user. The same filter will also be used by the signal backend to determine which users should receive a signal. +The title is mandatory human readable text shortly describing the notifications. + +The priority is one of the predefined values to help with presentation of the notifications or their filtering. Values: Critical, High, Normal, Low. + +The origin is a string identifying the creator of the notification, i.e. the BE plugin or external system. + +The topic is an optional string helping to group notifications of particular context. Its use cases include: + +- several notifications emitted by an asynchronous external task can be grouped by a single topic +- a BE plugin can group several related messages to a particular processing, i.e. asynchronous progress monitoring + +The timestamp of message creation is auto-generated by the notifications backend at the time of receiving a request to create the notification. + +The icon is optional and is meant to improve UX. A string referencing an icon name from MUI icon library of a defined version. If missing, it will be determined from the priority + +The message is an optional human readable text providing more details to the user. + +The links are an array of title-URL pairs. As an example, they can be used + +- by an external system to provide more details related to the notification +- by an external system to request an action within an asynchronous task +- by a BE plugin to provide link to other part of the Backstage UI (i.e. to the Catalog) + The notification backend does not provide any new permissions, since creating notifications can only be done by other backend plugins, while reading notifications can only be done by the authenticated user. It is possible that we want to add a permissions for reading notifications, in particular for admin and impersonation use cases, but that is not part of this proposal or the initial implementation. The notification frontend plugin provides a UI for viewing notifications, which in the initial implementation can be as simple as needed. The only requirement is that a user is able to view recent notifications and distinguish between read and unread notifications. A notification as marked as read once it has been interacted with. The frontend plugin also subscribes to the notifications signal channel and alerts the user when a new notification is received. From 20843193e8908f76c3f68c9eadb996c1a0c03f01 Mon Sep 17 00:00:00 2001 From: shmaram Date: Fri, 26 Jan 2024 11:44:17 +0200 Subject: [PATCH 063/468] Created Operators type and added type guard + fixed filter value to be string or number Signed-off-by: shmaram --- plugins/home/src/api/VisitsApi.ts | 16 +++++++++++++++- plugins/home/src/api/config.ts | 27 +++++++++++++++------------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/plugins/home/src/api/VisitsApi.ts b/plugins/home/src/api/VisitsApi.ts index ad750d81f2..610f4be483 100644 --- a/plugins/home/src/api/VisitsApi.ts +++ b/plugins/home/src/api/VisitsApi.ts @@ -16,6 +16,20 @@ import { createApiRef } from '@backstage/core-plugin-api'; +/** + * @public + * The operators that can be used in filter. + */ +export type Operators = '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + +/** + * @public + * Type guard for operators. + */ +export const isOperator = (s: string): s is Operators => { + return ['<', '<=', '==', '!=', '>', '>=', 'contains'].includes(s); +}; + /** * @public * Model for a visit entity. @@ -84,7 +98,7 @@ export type VisitsApiQueryParams = { */ filterBy?: Array<{ field: keyof Visit; - operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + operator: Operators; value: string | number; }>; }; diff --git a/plugins/home/src/api/config.ts b/plugins/home/src/api/config.ts index 3cc6d93f24..359026958f 100644 --- a/plugins/home/src/api/config.ts +++ b/plugins/home/src/api/config.ts @@ -14,7 +14,12 @@ * limitations under the License. */ -import { Visit, VisitsApiQueryParams } from './VisitsApi'; +import { + Visit, + VisitsApiQueryParams, + Operators, + isOperator, +} from './VisitsApi'; import { Config } from '@backstage/config'; /** @@ -24,25 +29,23 @@ import { Config } from '@backstage/config'; * * @public */ + export function readFilterConfig(config: Config): | { field: keyof Visit; - operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + operator: Operators; value: string | number; } | undefined { try { const field = config.getString('field') as keyof Visit; - const operator = config.getString('operator') as - | '<' - | '<=' - | '==' - | '!=' - | '>' - | '>=' - | 'contains'; - const value = config.getString('value'); - return { field, operator, value }; + const operator = config.getString('operator'); + const value = + config.getOptionalNumber('value') ?? config.getString('value'); + if (isOperator(operator)) { + return { field, operator, value }; + } + return undefined; } catch (error) { // invalid filter config - ignore filter return undefined; From c47460ba98257317099bec75eaa837ac94608619 Mon Sep 17 00:00:00 2001 From: shmaram Date: Fri, 26 Jan 2024 12:46:03 +0200 Subject: [PATCH 064/468] fix tests + support value to be number or string Signed-off-by: shmaram --- plugins/home/src/api/config.test.ts | 10 +++++----- plugins/home/src/api/config.ts | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/plugins/home/src/api/config.test.ts b/plugins/home/src/api/config.test.ts index 178287bb3d..c21d856038 100644 --- a/plugins/home/src/api/config.test.ts +++ b/plugins/home/src/api/config.test.ts @@ -23,13 +23,13 @@ describe('config', () => { const mockConfig = new MockConfigApi({ field: 'pathname', operator: '==', - value: '3', + value: '/home', }); const res = readFilterConfig(mockConfig); expect(res).toEqual({ field: 'pathname', operator: '==', - value: '3', + value: '/home', }); }); @@ -66,11 +66,11 @@ describe('config', () => { ]); }); - it('returns only invalid filters', async () => { + it('returns only valid filters', async () => { const mockValidConfig = new MockConfigApi({ field: 'id', operator: '==', - value: '3', + value: 3, }); const mockInvalidConfig = new MockConfigApi({ myField: 'pathname', @@ -81,7 +81,7 @@ describe('config', () => { mockValidConfig, mockInvalidConfig, ]); - expect(res).toEqual([{ field: 'id', operator: '==', value: '3' }]); + expect(res).toEqual([{ field: 'id', operator: '==', value: 3 }]); }); }); }); diff --git a/plugins/home/src/api/config.ts b/plugins/home/src/api/config.ts index 359026958f..65833c13d5 100644 --- a/plugins/home/src/api/config.ts +++ b/plugins/home/src/api/config.ts @@ -40,9 +40,8 @@ export function readFilterConfig(config: Config): try { const field = config.getString('field') as keyof Visit; const operator = config.getString('operator'); - const value = - config.getOptionalNumber('value') ?? config.getString('value'); - if (isOperator(operator)) { + const value = getValue(config); + if (isOperator(operator) && value !== undefined) { return { field, operator, value }; } return undefined; @@ -52,6 +51,20 @@ export function readFilterConfig(config: Config): } } +function getValue(config: Config) { + let value = undefined; + try { + value = config.getString('value'); + } catch (error) { + try { + value = config.getNumber('value'); + } catch { + // value is not string or number - ignore filter + } + } + return value; +} + /** * Reads a set of filter by configs. * From 877efd01ca005b7a419c0b73fd0e414040a0fcb9 Mon Sep 17 00:00:00 2001 From: shmaram Date: Fri, 26 Jan 2024 12:59:55 +0200 Subject: [PATCH 065/468] update api-report.md Signed-off-by: shmaram --- plugins/home/api-report.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index dd6e22c57d..023c649ab4 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -169,6 +169,9 @@ export const homePlugin: BackstagePlugin< {} >; +// @public +export const isOperator: (s: string) => s is Operators; + // @public export type LayoutConfiguration = { component: ReactElement | string; @@ -181,6 +184,9 @@ export type LayoutConfiguration = { resizable?: boolean; }; +// @public +export type Operators = '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + // @public @deprecated (undocumented) export type RendererProps = RendererProps_2; @@ -271,7 +277,7 @@ export type VisitsApiQueryParams = { }>; filterBy?: Array<{ field: keyof Visit; - operator: '<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'; + operator: Operators; value: string | number; }>; }; From 447d21045b791e1e2dca38bc9fd3ee169e25e1a3 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Fri, 26 Jan 2024 09:31:06 +0200 Subject: [PATCH 066/468] fix: signal disconnect loop on server start this fixes the disconnect loop when the server has just started by waiting for the connection to open before subsribing to events. also includes other improvements: - rename types to interfaces - allow to input single receiver for signal - use discovery api to check for upgrade path instead hard coded one Signed-off-by: Heikki Hellgren --- .changeset/gorgeous-ways-applaud.md | 8 ++++++ packages/backend/src/plugins/signals.ts | 1 + plugins/signals-backend/README.md | 1 + plugins/signals-backend/api-report.md | 3 ++ plugins/signals-backend/src/plugin.ts | 4 ++- .../src/service/SignalManager.test.ts | 6 ++-- .../src/service/SignalManager.ts | 16 +++++++---- .../src/service/router.test.ts | 11 +++++++- plugins/signals-backend/src/service/router.ts | 12 +++++--- .../src/service/standaloneServer.ts | 3 +- plugins/signals-node/api-report.md | 6 ++-- .../signals-node/src/DefaultSignalService.ts | 4 +-- plugins/signals-node/src/SignalService.ts | 4 +-- plugins/signals-node/src/types.ts | 2 +- plugins/signals-react/api-report.md | 15 ++++++---- plugins/signals-react/src/api/SignalApi.ts | 11 ++++++-- plugins/signals/src/api/SignalClient.ts | 28 +++++++++---------- 17 files changed, 89 insertions(+), 46 deletions(-) create mode 100644 .changeset/gorgeous-ways-applaud.md diff --git a/.changeset/gorgeous-ways-applaud.md b/.changeset/gorgeous-ways-applaud.md new file mode 100644 index 0000000000..0149061f0f --- /dev/null +++ b/.changeset/gorgeous-ways-applaud.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-signals-backend': patch +'@backstage/plugin-signals-react': patch +'@backstage/plugin-signals-node': patch +'@backstage/plugin-signals': patch +--- + +Fix to disconnect loop on server start diff --git a/packages/backend/src/plugins/signals.ts b/packages/backend/src/plugins/signals.ts index 477cea1938..066fae74f8 100644 --- a/packages/backend/src/plugins/signals.ts +++ b/packages/backend/src/plugins/signals.ts @@ -24,5 +24,6 @@ export default async function createPlugin( logger: env.logger, eventBroker: env.eventBroker, identity: env.identity, + discovery: env.discovery, }); } diff --git a/plugins/signals-backend/README.md b/plugins/signals-backend/README.md index e30a0836e0..ff1fcb6562 100644 --- a/plugins/signals-backend/README.md +++ b/plugins/signals-backend/README.md @@ -22,6 +22,7 @@ export default async function createPlugin( logger: env.logger, eventBroker: env.eventBroker, identity: env.identity, + discovery: env.discovery, }); } ``` diff --git a/plugins/signals-backend/api-report.md b/plugins/signals-backend/api-report.md index dd46778e5d..8bdaa581f3 100644 --- a/plugins/signals-backend/api-report.md +++ b/plugins/signals-backend/api-report.md @@ -8,12 +8,15 @@ import { EventBroker } from '@backstage/plugin-events-node'; import express from 'express'; import { IdentityApi } from '@backstage/plugin-auth-node'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; // @public (undocumented) export function createRouter(options: RouterOptions): Promise; // @public (undocumented) export interface RouterOptions { + // (undocumented) + discovery: PluginEndpointDiscovery; // (undocumented) eventBroker?: EventBroker; // (undocumented) diff --git a/plugins/signals-backend/src/plugin.ts b/plugins/signals-backend/src/plugin.ts index 4a9cef028f..11b63163d5 100644 --- a/plugins/signals-backend/src/plugin.ts +++ b/plugins/signals-backend/src/plugin.ts @@ -32,14 +32,16 @@ export const signalsPlugin = createBackendPlugin({ httpRouter: coreServices.httpRouter, logger: coreServices.logger, identity: coreServices.identity, + discovery: coreServices.discovery, // TODO: EventBroker. It is optional for now but it's actually required so waiting for the new backend system // for the events-backend for this to work. }, - async init({ httpRouter, logger, identity }) { + async init({ httpRouter, logger, identity, discovery }) { httpRouter.use( await createRouter({ logger, identity, + discovery, }), ); }, diff --git a/plugins/signals-backend/src/service/SignalManager.test.ts b/plugins/signals-backend/src/service/SignalManager.test.ts index 5720d1ff52..76cff0d44e 100644 --- a/plugins/signals-backend/src/service/SignalManager.test.ts +++ b/plugins/signals-backend/src/service/SignalManager.test.ts @@ -89,7 +89,7 @@ describe('SignalManager', () => { await onEvent({ topic: 'signals', eventPayload: { - recipients: null, + receivers: null, channel: 'test', message: { msg: 'test' }, }, @@ -109,7 +109,7 @@ describe('SignalManager', () => { await onEvent({ topic: 'signals', eventPayload: { - recipients: null, + receivers: null, channel: 'test', message: { msg: 'test' }, }, @@ -162,7 +162,7 @@ describe('SignalManager', () => { await onEvent({ topic: 'signals', eventPayload: { - recipients: 'user:default/john.doe', + receivers: 'user:default/john.doe', channel: 'test', message: { msg: 'test' }, }, diff --git a/plugins/signals-backend/src/service/SignalManager.ts b/plugins/signals-backend/src/service/SignalManager.ts index 983496b96b..1f1681b86c 100644 --- a/plugins/signals-backend/src/service/SignalManager.ts +++ b/plugins/signals-backend/src/service/SignalManager.ts @@ -71,7 +71,9 @@ export class SignalManager { id, user: identity?.identity.userEntityRef ?? 'user:default/guest', ws, - ownershipEntityRefs: identity?.identity.ownershipEntityRefs ?? [], + ownershipEntityRefs: identity?.identity.ownershipEntityRefs ?? [ + 'user:default/guest', + ], subscriptions: new Set(), }; @@ -130,8 +132,12 @@ export class SignalManager { return; } - const { channel, recipients, message } = eventPayload; + const { channel, receivers, message } = eventPayload; const jsonMessage = JSON.stringify({ channel, message }); + let users: string[] = []; + if (receivers !== null) { + users = Array.isArray(receivers) ? receivers : [receivers]; + } // Actual websocket message sending this.connections.forEach(conn => { @@ -140,10 +146,8 @@ export class SignalManager { } // Sending to all users can be done with null if ( - recipients !== null && - !conn.ownershipEntityRefs.some((ref: string) => - recipients.includes(ref), - ) + receivers !== null && + !conn.ownershipEntityRefs.some((ref: string) => users.includes(ref)) ) { return; } diff --git a/plugins/signals-backend/src/service/router.test.ts b/plugins/signals-backend/src/service/router.test.ts index ecf2ad4aa6..807b45d53e 100644 --- a/plugins/signals-backend/src/service/router.test.ts +++ b/plugins/signals-backend/src/service/router.test.ts @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { getVoidLogger } from '@backstage/backend-common'; +import { + getVoidLogger, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; import express from 'express'; import request from 'supertest'; @@ -30,6 +33,11 @@ const identityApiMock: jest.Mocked = { getIdentity: jest.fn(), }; +const discovery: jest.Mocked = { + getBaseUrl: jest.fn().mockResolvedValue('/api/signals'), + getExternalBaseUrl: jest.fn(), +}; + describe('createRouter', () => { let app: express.Express; @@ -38,6 +46,7 @@ describe('createRouter', () => { logger: getVoidLogger(), identity: identityApiMock, eventBroker: eventBrokerMock, + discovery, }); app = express().use(router); }); diff --git a/plugins/signals-backend/src/service/router.ts b/plugins/signals-backend/src/service/router.ts index 4394a9f480..cc8fe9b851 100644 --- a/plugins/signals-backend/src/service/router.ts +++ b/plugins/signals-backend/src/service/router.ts @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { errorHandler } from '@backstage/backend-common'; +import { + errorHandler, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; import express, { NextFunction, Request, Response } from 'express'; import Router from 'express-promise-router'; import { LoggerService } from '@backstage/backend-plugin-api'; @@ -33,13 +36,14 @@ export interface RouterOptions { logger: LoggerService; eventBroker?: EventBroker; identity: IdentityApi; + discovery: PluginEndpointDiscovery; } /** @public */ export async function createRouter( options: RouterOptions, ): Promise { - const { logger, identity } = options; + const { logger, identity, discovery } = options; const manager = SignalManager.create(options); let subscribedToUpgradeRequests = false; @@ -66,9 +70,9 @@ export async function createRouter( } subscribedToUpgradeRequests = true; + const apiUrl = await discovery.getBaseUrl('signals'); server.on('upgrade', async (request, socket, head) => { - // TODO: Find a way to make this more generic - if (request.url !== '/api/signals') { + if (!request.url || !apiUrl.endsWith(request.url)) { return; } diff --git a/plugins/signals-backend/src/service/standaloneServer.ts b/plugins/signals-backend/src/service/standaloneServer.ts index 278de8621e..311d3f8587 100644 --- a/plugins/signals-backend/src/service/standaloneServer.ts +++ b/plugins/signals-backend/src/service/standaloneServer.ts @@ -68,6 +68,7 @@ export async function startStandaloneServer( logger, identity, eventBroker, + discovery, }); let service = createServiceBuilder(module) @@ -83,7 +84,7 @@ export async function startStandaloneServer( setInterval(() => { signals.publish({ - recipients: null, + receivers: null, channel: 'test', message: { hello: 'world' }, }); diff --git a/plugins/signals-node/api-report.md b/plugins/signals-node/api-report.md index 00d099f7db..bfd8543a17 100644 --- a/plugins/signals-node/api-report.md +++ b/plugins/signals-node/api-report.md @@ -16,15 +16,15 @@ export class DefaultSignalService implements SignalService { // @public (undocumented) export type SignalPayload = { - recipients: string[] | null; + receivers: string[] | string | null; channel: string; message: JsonObject; }; // @public (undocumented) -export type SignalService = { +export interface SignalService { publish(signal: SignalPayload): Promise; -}; +} // @public (undocumented) export const signalService: ServiceRef; diff --git a/plugins/signals-node/src/DefaultSignalService.ts b/plugins/signals-node/src/DefaultSignalService.ts index 1fba96b8bc..4824c1219f 100644 --- a/plugins/signals-node/src/DefaultSignalService.ts +++ b/plugins/signals-node/src/DefaultSignalService.ts @@ -37,11 +37,11 @@ export class DefaultSignalService implements SignalService { * @param message - message to publish */ async publish(signal: SignalPayload) { - const { recipients, channel, message } = signal; + const { receivers, channel, message } = signal; await this.eventBroker?.publish({ topic: 'signals', eventPayload: { - recipients, + receivers, message, channel, }, diff --git a/plugins/signals-node/src/SignalService.ts b/plugins/signals-node/src/SignalService.ts index f08a12661f..7d021ccf4e 100644 --- a/plugins/signals-node/src/SignalService.ts +++ b/plugins/signals-node/src/SignalService.ts @@ -16,9 +16,9 @@ import { SignalPayload } from './types'; /** @public */ -export type SignalService = { +export interface SignalService { /** * Publishes a message to user refs to specific topic */ publish(signal: SignalPayload): Promise; -}; +} diff --git a/plugins/signals-node/src/types.ts b/plugins/signals-node/src/types.ts index 0220a196aa..21af773268 100644 --- a/plugins/signals-node/src/types.ts +++ b/plugins/signals-node/src/types.ts @@ -25,7 +25,7 @@ export type SignalServiceOptions = { /** @public */ export type SignalPayload = { - recipients: string[] | null; + receivers: string[] | string | null; channel: string; message: JsonObject; }; diff --git a/plugins/signals-react/api-report.md b/plugins/signals-react/api-report.md index 2df2e4f1ce..e3c4f740a5 100644 --- a/plugins/signals-react/api-report.md +++ b/plugins/signals-react/api-report.md @@ -7,18 +7,23 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; // @public (undocumented) -export type SignalApi = { +export interface SignalApi { + // (undocumented) subscribe( channel: string, onMessage: (message: JsonObject) => void, - ): { - unsubscribe: () => void; - }; -}; + ): SignalSubscriber; +} // @public (undocumented) export const signalApiRef: ApiRef; +// @public (undocumented) +export interface SignalSubscriber { + // (undocumented) + unsubscribe(): void; +} + // @public (undocumented) export const useSignal: (channel: string) => { lastSignal: JsonObject | null; diff --git a/plugins/signals-react/src/api/SignalApi.ts b/plugins/signals-react/src/api/SignalApi.ts index b37b3ae2f5..b67b2ea0dc 100644 --- a/plugins/signals-react/src/api/SignalApi.ts +++ b/plugins/signals-react/src/api/SignalApi.ts @@ -22,9 +22,14 @@ export const signalApiRef = createApiRef({ }); /** @public */ -export type SignalApi = { +export interface SignalSubscriber { + unsubscribe(): void; +} + +/** @public */ +export interface SignalApi { subscribe( channel: string, onMessage: (message: JsonObject) => void, - ): { unsubscribe: () => void }; -}; + ): SignalSubscriber; +} diff --git a/plugins/signals/src/api/SignalClient.ts b/plugins/signals/src/api/SignalClient.ts index ee4ed8756b..d6c4634264 100644 --- a/plugins/signals/src/api/SignalClient.ts +++ b/plugins/signals/src/api/SignalClient.ts @@ -146,20 +146,6 @@ export class SignalClient implements SignalApi { url.protocol = url.protocol === 'http:' ? 'ws:' : 'wss:'; this.ws = new WebSocket(url.toString(), token); - this.ws.onmessage = (data: MessageEvent) => { - this.handleMessage(data); - }; - - this.ws.onerror = () => { - this.reconnect(); - }; - - this.ws.onclose = (ev: CloseEvent) => { - if (ev.code !== WS_CLOSE_NORMAL && ev.code !== WS_CLOSE_GOING_AWAY) { - this.reconnect(); - } - }; - // Wait until connection is open let connectSleep = 0; while ( @@ -174,6 +160,20 @@ export class SignalClient implements SignalApi { if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { throw new Error('Connect timeout'); } + + this.ws.onmessage = (data: MessageEvent) => { + this.handleMessage(data); + }; + + this.ws.onerror = () => { + this.reconnect(); + }; + + this.ws.onclose = (ev: CloseEvent) => { + if (ev.code !== WS_CLOSE_NORMAL && ev.code !== WS_CLOSE_GOING_AWAY) { + this.reconnect(); + } + }; } private handleMessage(data: MessageEvent) { From 09f8b31f7ab7a881a9b8722fe95d4b8735ee2f54 Mon Sep 17 00:00:00 2001 From: Emmanuel Auffray Date: Fri, 26 Jan 2024 11:14:27 +0000 Subject: [PATCH 067/468] addi changeset Signed-off-by: Emmanuel Auffray --- .changeset/dry-vans-kiss.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/dry-vans-kiss.md diff --git a/.changeset/dry-vans-kiss.md b/.changeset/dry-vans-kiss.md new file mode 100644 index 0000000000..96799240f9 --- /dev/null +++ b/.changeset/dry-vans-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +simple typo fix From 347a2b764288c5f12869c48060cd81a0f3dce27d Mon Sep 17 00:00:00 2001 From: Emmanuel Auffray Date: Fri, 26 Jan 2024 11:14:27 +0000 Subject: [PATCH 068/468] amend lazy changeset description Signed-off-by: Emmanuel Auffray --- .changeset/dry-vans-kiss.md | 2 +- .devcontainer/devcontainer.json | 97 +++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/devcontainer.json diff --git a/.changeset/dry-vans-kiss.md b/.changeset/dry-vans-kiss.md index 96799240f9..5838516b5e 100644 --- a/.changeset/dry-vans-kiss.md +++ b/.changeset/dry-vans-kiss.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': patch --- -simple typo fix +Simple typo fix in the the fetch:template action example on the word skeleton diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..322317e382 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,97 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node +{ + "name": "Node.js & TypeScript", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/typescript-node:1-18-bookworm", + // "build": { + // "dockerfile": "Dockerfile" + // }, + "mounts": [ + // {"source":"${localWorkspaceFolder}","target":"/workspace","type":"bind"}, + { + "source": "${localEnv:HOME}/.ssh", + "target": "/home/node/.ssh", + "type": "bind" + }, + { + "source": "${localEnv:HOME}/.kube", + "target": "/usr/local/share/kube-localhost", + "type": "bind" + }, + { + "source": "${localEnv:HOME}/.azure", + "target": "/home/node/.azure", + "type": "bind" + } + ], + + // "remoteEnv": { + // "SYNC_LOCALHOST_KUBECONFIG": "true" + // }, + + "customizations": { + "vscode": { + "extensions": [ + "GitHub.copilot", + "GitHub.copilot-chat", + "GitHub.vscode-pull-request-github", + "github.vscode-github-actions", + "Intility.vscode-backstage", + "ms-azuretools.vscode-docker", + "ms-kubernetes-tools.kind-vscode", + "ms-kubernetes-tools.vscode-kubernetes-tools", + "humao.rest-client" + ] + } + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + "features": { + // Azure CLI + // "ghcr.io/devcontainers/features/azure-cli:1": { + // "installBicep": true, + // "version": "latest" + // }, + // // Docker CLI accessing the host's Docker daemon + // "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + // "moby": true, + // "installDockerBuildx": true, + // "version": "latest", + // "dockerDashComposeVersion": "v2" + // }, + // // Kubernetes CLI tools + // "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": { + // "version": "latest", + // "helm": "latest", + // "minikube": "none" + // }, + // // KinD + // "ghcr.io/devcontainers-contrib/features/kind:1": { + // "version": "latest" + // }, + // "ghcr.io/rio/features/k9s:1": {}, + // "ghcr.io/devcontainers/features/azure-cli:1": { + // "version": "latest" + // }, + // "ghcr.io/rjfmachado/devcontainer-features/cloud-native:1": { + // "kubectl": "latest", + // "helm": "latest", + // "kubelogin": "latest", + // "azwi": "latest", + // "flux": "latest", + // "cilium": "latest" + // } + } + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "yarn install", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} From 25ab4aaf181dc22ec934141b5ab3c68529cfb989 Mon Sep 17 00:00:00 2001 From: Emmanuel Auffray Date: Fri, 26 Jan 2024 11:14:27 +0000 Subject: [PATCH 069/468] refine changeset and signoff Signed-off-by: Emmanuel Auffray --- .changeset/dry-vans-kiss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/dry-vans-kiss.md b/.changeset/dry-vans-kiss.md index 5838516b5e..031f31e912 100644 --- a/.changeset/dry-vans-kiss.md +++ b/.changeset/dry-vans-kiss.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': patch --- -Simple typo fix in the the fetch:template action example on the word skeleton +Simple typo fix in the the fetch:template action example on the word 'skeleton'. From 406c6e005b8c1fc951694ac91e7b12439f1110c7 Mon Sep 17 00:00:00 2001 From: Frank Showalter <842058+fshowalter@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:20:28 -0500 Subject: [PATCH 070/468] Update package.json Signed-off-by: Frank Showalter <842058+fshowalter@users.noreply.github.com> --- packages/theme/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/theme/package.json b/packages/theme/package.json index 56179fae19..c844ba2019 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -39,7 +39,7 @@ }, "peerDependencies": { "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0" }, From 68cc58ffefb134ce2c0239cc21560ee8c1e7b710 Mon Sep 17 00:00:00 2001 From: Mitchell Hentges Date: Fri, 26 Jan 2024 16:30:54 +0100 Subject: [PATCH 071/468] Address minor nits in new frontend system docs Fix some small typos, correct some casing, and add a link to an area that I struggled to understand without guidance. Signed-off-by: Mitchell Hentges --- docs/frontend-system/architecture/03-extensions.md | 8 ++++---- docs/frontend-system/architecture/06-utility-apis.md | 2 +- docs/frontend-system/architecture/07-routes.md | 2 +- docs/frontend-system/building-plugins/02-testing.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index 6d86415817..46bfa3d86f 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -114,7 +114,7 @@ const extension = createExtension({ ### Extension Data Uniqueness -Note that the key used in the output map, in this case `element`, is only used internally within the definition of the extension itself. That actual identifier for the data when consumed by other extensions is the ID of the reference, in this case `core.reactElement`. This means that you can not output multiple different values for the same extension data reference, as they would conflict with each other. That in turn makes overly generic extension data references a bad idea, for example a generic "string" type. Instead create separate references for each type of data that you want to share. +Note that the key used in the output map, in this case `element`, is only used internally within the definition of the extension itself. That actual identifier for the data when consumed by other extensions is the ID of the reference, in this case [`core.reactElement`](https://github.com/backstage/backstage/blob/916da47e8abdb880877daa18881eb8fdbb33e70a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts#L23). This means that you can not output multiple different values for the same extension data reference, as they would conflict with each other. That in turn makes overly generic extension data references a bad idea, for example a generic "string" type. Instead create separate references for each type of data that you want to share. ```tsx const extension = createExtension({ @@ -160,7 +160,7 @@ const extension = createExtension({ ## Extension Inputs -The Extension Data can be passed up to other extensions through their extension inputs. Similar to the outputs seen before, let's create an example an extension with a extension input: +The Extension Data can be passed up to other extensions through their extension inputs. Similar to the outputs seen before, let's create an example of an extension with a extension input: ```tsx const navigationExtension = createExtension({ @@ -254,7 +254,7 @@ With the `inputs` not only the `output` of an extensions item is passed to the e ## Extension Configuration -With the `app-config.yaml` there is already the option to pass configuration to plugins or the app to e.g. define the `baseURL` of your app. For extensions this concept would be limiting as an extension can be independent of the plugin & initiated several times. Therefor we created a possibility to configure each extension individually through config. The extension config schema is created using the [`zod`](https://zod.dev/) library, which in addition to TypeScript type checking also provides runtime validation and coercion. If we continue with the example of the `navigationExtension` and now want it to contain a configurable title, we could make it available like the following: +With the `app-config.yaml` there is already the option to pass configuration to plugins or the app to e.g. define the `baseURL` of your app. For extensions this concept would be limiting as an extension can be independent of the plugin & initiated several times. Therefore we created a possibility to configure each extension individually through config. The extension config schema is created using the [`zod`](https://zod.dev/) library, which in addition to TypeScript type checking also provides runtime validation and coercion. If we continue with the example of the `navigationExtension` and now want it to contain a configurable title, we could make it available like the following: ```tsx const navigationExtension = createExtension({ @@ -297,7 +297,7 @@ app: With creating an extension by using `createExtension(...)` you have the advantage that the extension can be anything in your Backstage application. We realised that this comes with the trade-off of having to repeat boilerplate code for similar building blocks. Here extension creators come into play for covering common building blocks in Backstage like pages using `createPageExtension`, themes using the `createThemeExtension` or items for the navigation using `createNavItemExtension`. -If we follow the example from above all items of the navigation have similarities, like they all want to be attached to the same extension with the same input as well as rendering the same navigation item component. Therefor `createExtension` can be abstracted for this use case to `createNavItemExtension` and if we add the extension to the app it will end up in the right place & looks like we expect a navigation item to look. +If we follow the example from above all items of the navigation have similarities, like they all want to be attached to the same extension with the same input as well as rendering the same navigation item component. Therefore `createExtension` can be abstracted for this use case to `createNavItemExtension` and if we add the extension to the app it will end up in the right place & looks like we expect a navigation item to look. ```tsx export const HomeNavIcon = createNavItemExtension({ diff --git a/docs/frontend-system/architecture/06-utility-apis.md b/docs/frontend-system/architecture/06-utility-apis.md index fe714793d7..49e5854b65 100644 --- a/docs/frontend-system/architecture/06-utility-apis.md +++ b/docs/frontend-system/architecture/06-utility-apis.md @@ -14,7 +14,7 @@ Utility APIs are pieces of standalone functionality, interfaces that can be requ A common example of a utility API is a client interface to interact with the backend part of a plugin, such as the catalog client. Any frontend plugin can then request an implementation of that interface to make requests through. -The following diagram shows a hypothetical application, which depends on two plugins and also provides some extra overrides. Note that both the plugins and the core framework provide utility APIs, and that they depended on each other. The app also chooses to use its overrides mechanism to supply a replacement implementation of one API, which takes precedence over the default one. Thus, all consumers of that API will be sure to get that new implementation provided to them. +The following diagram shows a hypothetical application, which depends on two plugins and also provides some extra overrides. Note that both the plugins and the core framework provide utility APIs, and that they depend on each other. The app also chooses to use its overrides mechanism to supply a replacement implementation of one API, which takes precedence over the default one. Thus, all consumers of that API will be sure to get that new implementation provided to them. ![frontend system utility apis diagram](../../assets/frontend-system/architecture-utility-apis.drawio.svg) diff --git a/docs/frontend-system/architecture/07-routes.md b/docs/frontend-system/architecture/07-routes.md index 11586573be..7fb053aa56 100644 --- a/docs/frontend-system/architecture/07-routes.md +++ b/docs/frontend-system/architecture/07-routes.md @@ -66,7 +66,7 @@ export default createPlugin({ }); ``` -In the example above we associated the `indexRouteRef` with the `catalogIndexPage` extension and provided both the route ref and page via the Catalog plugin. So, When this plugin is installed in the app, the index page will become associated with the newly created `RouteRef`, making it possible to use the route ref to navigate the page extension. +In the example above we associated the `indexRouteRef` with the `catalogIndexPage` extension and provided both the route ref and page via the Catalog plugin. So, when this plugin is installed in the app, the index page will become associated with the newly created `RouteRef`, making it possible to use the route ref to navigate the page extension. It may seem unclear why we configure the `routes` option when creating a plugin as the route has already been passed to the extension. We do that to make it possible for other plugins to route to our page, which is explained in detail in the [binding routes](#binding-external-route-references) section. diff --git a/docs/frontend-system/building-plugins/02-testing.md b/docs/frontend-system/building-plugins/02-testing.md index c6621d6584..fa29918311 100644 --- a/docs/frontend-system/building-plugins/02-testing.md +++ b/docs/frontend-system/building-plugins/02-testing.md @@ -70,7 +70,7 @@ describe('Entity details component', () => { await renderInTestApp( - + , ); From a560bfd7a3f7900c9f6534aa33a7b68c0f76f5ca Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 26 Jan 2024 10:39:36 -0500 Subject: [PATCH 072/468] fix missing extension Signed-off-by: Aramis --- .../plugin-authors/02-adding-a-basic-permission-check.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 223d55d3d8..0ef50670ef 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -4,7 +4,7 @@ title: 2. Adding a basic permission check description: Explains how to add a basic permission check to a Backstage plugin --- -If the outcome of a permission check doesn't need to change for different [resources](../../references/glossary#permission-resource), you can use a _basic permission check_. For this kind of check, we simply need to define a [permission](../../references/glossary.md#permission), and call `authorize` with it. +If the outcome of a permission check doesn't need to change for different [resources](../../references/glossary.md#permission-resource), you can use a _basic permission check_. For this kind of check, we simply need to define a [permission](../../references/glossary.md#permission), and call `authorize` with it. For this tutorial, we'll use a basic permission check to authorize the `create` endpoint in our todo-backend. This will allow Backstage integrators to control whether each of their users is authorized to create todos by adjusting their [permission policy](../../references/glossary.md#policy). From ecab5c032c5f0d2b3e4223065cca19bf4796e71a Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 26 Jan 2024 10:40:45 -0500 Subject: [PATCH 073/468] update mkdocs file Signed-off-by: Aramis --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 8c9f84ba81..2a17613672 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,7 +18,6 @@ nav: - Release & Versioning Policy: 'overview/versioning-policy.md' - Backstage Threat Model: 'overview/threat-model.md' - Support and community: 'overview/support.md' - - Glossary: 'overview/glossary.md' - Logo assets: 'overview/logos.md' - Getting Started: - Getting Started: 'getting-started/index.md' @@ -171,7 +170,6 @@ nav: - Contributing New Providers: 'auth/add-auth-provider.md' - Service to Service Auth: 'auth/service-to-service-auth.md' - Troubleshooting Auth: 'auth/troubleshooting.md' - - Glossary: 'auth/glossary.md' - Deployment: - Deploying Backstage: 'deployment/index.md' - Scaling: 'deployment/scaling.md' @@ -218,3 +216,5 @@ nav: - Overview: 'faq/index.md' - Product FAQ: 'faq/product.md' - Technical FAQ: 'faq/technical.md' + - References: + - Glossary: 'references/glossary.md' From 60ba48c5916d425f54667a3d0566635e961a1606 Mon Sep 17 00:00:00 2001 From: Adam Harvey <33203301+adamdmharvey@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:06:50 -0500 Subject: [PATCH 074/468] chore(docs): Improve API reference wording Signed-off-by: Adam Harvey <33203301+adamdmharvey@users.noreply.github.com> --- packages/catalog-model/src/kinds/relations.ts | 2 +- packages/test-utils/src/testUtils/TestApiProvider.tsx | 4 ++-- plugins/explore/src/api/ExploreApi.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/catalog-model/src/kinds/relations.ts b/packages/catalog-model/src/kinds/relations.ts index 4d545c9d35..69f9aae1c4 100644 --- a/packages/catalog-model/src/kinds/relations.ts +++ b/packages/catalog-model/src/kinds/relations.ts @@ -114,7 +114,7 @@ export const RELATION_CHILD_OF = 'childOf'; export const RELATION_MEMBER_OF = 'memberOf'; /** - * A relation from a group to its member, typcally a user in a group. Reversed direction of + * A relation from a group to its member, typically a user in a group. Reversed direction of * {@link RELATION_MEMBER_OF}. * * @public diff --git a/packages/test-utils/src/testUtils/TestApiProvider.tsx b/packages/test-utils/src/testUtils/TestApiProvider.tsx index c212b94bea..59fafbeadb 100644 --- a/packages/test-utils/src/testUtils/TestApiProvider.tsx +++ b/packages/test-utils/src/testUtils/TestApiProvider.tsx @@ -93,8 +93,8 @@ export class TestApiRegistry implements ApiHolder { * * @remarks * todo: remove this remark tag and ship in the api-reference. There's some odd formatting going on when this is made into a markdown doc, that there's no line break between - * the emmited

for To the following

so what happens is that when parsing in docusaurus, it thinks that the code block is mdx rather than a code - * snippet. Just ommiting this from the report for now until we can work out how to fix laterr. + * the emitted

for To the following

so what happens is that when parsing in docusaurus, it thinks that the code block is mdx rather than a code + * snippet. Just omitting this from the report for now until we can work out how to fix later. * A migration from `ApiRegistry` and `ApiProvider` might look like this, from: * * ```tsx diff --git a/plugins/explore/src/api/ExploreApi.ts b/plugins/explore/src/api/ExploreApi.ts index ef9eb646b7..e5d8c7efa6 100644 --- a/plugins/explore/src/api/ExploreApi.ts +++ b/plugins/explore/src/api/ExploreApi.ts @@ -29,7 +29,7 @@ export interface ExploreApi { /** * Returns a list of explore tools. * - * @param request - The The request query options + * @param request - The request query options */ getTools(request?: GetExploreToolsRequest): Promise; } From 07e7d12db6e72d85617aecfe233326327cbe9144 Mon Sep 17 00:00:00 2001 From: Adam Harvey <33203301+adamdmharvey@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:08:10 -0500 Subject: [PATCH 075/468] chore: Add changeset Signed-off-by: Adam Harvey <33203301+adamdmharvey@users.noreply.github.com> --- .changeset/hot-paws-tap.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/hot-paws-tap.md diff --git a/.changeset/hot-paws-tap.md b/.changeset/hot-paws-tap.md new file mode 100644 index 0000000000..30d8b396ba --- /dev/null +++ b/.changeset/hot-paws-tap.md @@ -0,0 +1,7 @@ +--- +'@backstage/catalog-model': patch +'@backstage/test-utils': patch +'@backstage/plugin-explore': patch +--- + +Fix wording in API reference From 23f073ff1459b77c859ab5e745b70ac359a228d3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:21:03 +0000 Subject: [PATCH 076/468] chore(deps): update dependency @types/express-serve-static-core to v4.17.42 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5a15adff92..ada693c4a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17995,14 +17995,14 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33, @types/express-serve-static-core@npm:^4.17.5": - version: 4.17.41 - resolution: "@types/express-serve-static-core@npm:4.17.41" + version: 4.17.42 + resolution: "@types/express-serve-static-core@npm:4.17.42" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" "@types/send": "*" - checksum: 12750f6511dd870bbaccfb8208ad1e79361cf197b147f62a3bedc19ec642f3a0f9926ace96705f4bc88ec2ae56f61f7ca8c2438e6b22f5540842b5569c28a121 + checksum: 58273f80fcc94de42691f48e22542e69f0b17863378e3216ce8b782ace012f32241bfeb02a2be837f0e2b4ef96e916979adc30bbfea13f6545bd3ab81b7d2773 languageName: node linkType: hard From 8316e3407ff78b29ebb41480d9e851118633a646 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 16:44:52 +0000 Subject: [PATCH 077/468] chore(deps): update dependency @types/tar to v6.1.11 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5a15adff92..105be68e30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19151,12 +19151,12 @@ __metadata: linkType: hard "@types/tar@npm:^6.1.1": - version: 6.1.10 - resolution: "@types/tar@npm:6.1.10" + version: 6.1.11 + resolution: "@types/tar@npm:6.1.11" dependencies: "@types/node": "*" minipass: ^4.0.0 - checksum: 82d46f1246e830b98833ed94fbdfbcb3ffb8a5d7173609622d56c9326124523a3cc541607876c63a6ab9117eb59fb37ef8a6284b194164ab1d1d8c03a8ba59c6 + checksum: 9b79f61f9179db65ecd3f5e9c0d2152839ad13381d39c38c3a9408aa1f3a2b061ba195e7d758be1863294a0ce69df6659f0e3e09f440c9f5309bded58bc87c89 languageName: node linkType: hard From 281e8c676d3104a1195787ef82acc11d390b4196 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 26 Jan 2024 17:47:54 +0100 Subject: [PATCH 078/468] core-components: remove SidebarIntro and IntroCard Signed-off-by: Patrik Oldsberg --- .changeset/four-mugs-try.md | 5 + packages/core-components/api-report.md | 18 -- .../src/layout/Sidebar/Intro.tsx | 199 ------------------ .../src/layout/Sidebar/Sidebar.stories.tsx | 3 - .../src/layout/Sidebar/index.ts | 2 - .../src/overridableComponents.ts | 2 - 6 files changed, 5 insertions(+), 224 deletions(-) create mode 100644 .changeset/four-mugs-try.md delete mode 100644 packages/core-components/src/layout/Sidebar/Intro.tsx diff --git a/.changeset/four-mugs-try.md b/.changeset/four-mugs-try.md new file mode 100644 index 0000000000..11fdf783d4 --- /dev/null +++ b/.changeset/four-mugs-try.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': minor +--- + +**BREAKING**: Removed the `SidebarIntro` component as it was providing instructions for features that do not exist, along with `IntroCard`. If you were relying on this component and want to keep using it you can refer to the original implementations of [`SidebarIntro`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L149) and [`IntroCard`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L100). diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index cca7017089..b5d54dd606 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -593,11 +593,6 @@ export type InfoCardClassKey = // @public (undocumented) export type InfoCardVariants = 'flex' | 'fullHeight' | 'gridItem'; -// Warning: (ae-forgotten-export) The symbol "IntroCardProps" needs to be exported by the entry point index.d.ts -// -// @public -export function IntroCard(props: IntroCardProps): React_2.JSX.Element; - // Warning: (ae-forgotten-export) The symbol "ItemCardProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ItemCard" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -953,19 +948,6 @@ export interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; } -// Warning: (ae-missing-release-tag) "SidebarIntro" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export function SidebarIntro(_props: {}): React_2.JSX.Element | null; - -// @public (undocumented) -export type SidebarIntroClassKey = - | 'introCard' - | 'introDismiss' - | 'introDismissLink' - | 'introDismissText' - | 'introDismissIcon'; - // Warning: (ae-forgotten-export) The symbol "SidebarItemProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarItem" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/core-components/src/layout/Sidebar/Intro.tsx b/packages/core-components/src/layout/Sidebar/Intro.tsx deleted file mode 100644 index 6f75f096fb..0000000000 --- a/packages/core-components/src/layout/Sidebar/Intro.tsx +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Box from '@material-ui/core/Box'; -import Collapse from '@material-ui/core/Collapse'; -import IconButton from '@material-ui/core/IconButton'; -import { makeStyles, Theme } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import CloseIcon from '@material-ui/icons/Close'; -import { useLocalStorageValue } from '@react-hookz/web'; -import React, { useContext, useState } from 'react'; - -import { - SIDEBAR_INTRO_LOCAL_STORAGE, - SidebarConfig, - SidebarConfigContext, -} from './config'; -import { SidebarDivider } from './Items'; -import { useSidebarOpenState } from './SidebarOpenStateContext'; - -/** @public */ -export type SidebarIntroClassKey = - | 'introCard' - | 'introDismiss' - | 'introDismissLink' - | 'introDismissText' - | 'introDismissIcon'; - -const useStyles = makeStyles( - theme => ({ - introCard: props => ({ - color: '#b5b5b5', - // XXX (@koroeskohr): should I be using a Mui theme variable? - fontSize: 12, - width: props.sidebarConfig.drawerWidthOpen, - marginTop: theme.spacing(2.25), - marginBottom: theme.spacing(1.5), - paddingLeft: props.sidebarConfig.iconPadding, - paddingRight: props.sidebarConfig.iconPadding, - }), - introDismiss: { - display: 'flex', - justifyContent: 'flex-end', - alignItems: 'center', - marginTop: theme.spacing(1.5), - }, - introDismissLink: { - color: '#dddddd', - display: 'flex', - alignItems: 'center', - marginBottom: theme.spacing(0.5), - '&:hover': { - color: theme.palette.linkHover, - transition: theme.transitions.create('color', { - easing: theme.transitions.easing.sharp, - duration: theme.transitions.duration.shortest, - }), - }, - }, - introDismissText: { - fontSize: '0.7rem', - fontWeight: 'bold', - textTransform: 'uppercase', - letterSpacing: 1, - }, - introDismissIcon: { - width: 18, - height: 18, - marginRight: theme.spacing(1.5), - }, - }), - { name: 'BackstageSidebarIntro' }, -); - -type IntroCardProps = { - text: string; - onClose: () => void; -}; - -/** - * Closable card with information from Navigation Sidebar - * - * @public - * - */ - -export function IntroCard(props: IntroCardProps) { - const { sidebarConfig } = useContext(SidebarConfigContext); - const classes = useStyles({ sidebarConfig }); - const { text, onClose } = props; - const handleClose = () => onClose(); - - return ( - - {text} - - - - - Dismiss - - - - - ); -} - -type SidebarIntroLocalStorage = { - starredItemsDismissed: boolean; - recentlyViewedItemsDismissed: boolean; -}; - -type SidebarIntroCardProps = { - text: string; - onDismiss: () => void; -}; - -const SidebarIntroCard = (props: SidebarIntroCardProps) => { - const { text, onDismiss } = props; - const [collapsing, setCollapsing] = useState(false); - const startDismissing = () => { - setCollapsing(true); - }; - return ( - - - - ); -}; - -const starredIntroText = `Fun fact! As you explore all the awesome plugins in Backstage, you can actually pin them to this side nav. -Keep an eye out for the little star icon (⭐) next to the plugin name and give it a click!`; -const recentlyViewedIntroText = - 'And your recently viewed plugins will pop up here!'; - -export function SidebarIntro(_props: {}) { - const { isOpen } = useSidebarOpenState(); - const defaultValue = { - starredItemsDismissed: false, - recentlyViewedItemsDismissed: false, - }; - const { value: dismissedIntro, set: setDismissedIntro } = - useLocalStorageValue(SIDEBAR_INTRO_LOCAL_STORAGE); - - const { starredItemsDismissed, recentlyViewedItemsDismissed } = - dismissedIntro ?? {}; - - const dismissStarred = () => { - setDismissedIntro(state => ({ - ...defaultValue, - ...state, - starredItemsDismissed: true, - })); - }; - const dismissRecentlyViewed = () => { - setDismissedIntro(state => ({ - ...defaultValue, - ...state, - recentlyViewedItemsDismissed: true, - })); - }; - - if (!isOpen) { - return null; - } - - return ( - <> - {!starredItemsDismissed && ( - <> - - - - )} - {!recentlyViewedItemsDismissed && ( - - )} - - ); -} diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 342a9b2a29..aef837b646 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -34,7 +34,6 @@ import { SidebarSearchField, SidebarSpace, } from './Items'; -import { SidebarIntro } from './Intro'; import { SidebarSubmenu } from './SidebarSubmenu'; import { SidebarSubmenuItem } from './SidebarSubmenuItem'; @@ -65,7 +64,6 @@ export const SampleSidebar = () => ( - @@ -104,7 +102,6 @@ export const SampleScalableSidebar = () => ( - diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 4c91ea6a8f..b614d50feb 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -44,8 +44,6 @@ export type { SidebarSpacerClassKey, SidebarDividerClassKey, } from './Items'; -export { IntroCard, SidebarIntro } from './Intro'; -export type { SidebarIntroClassKey } from './Intro'; export { SIDEBAR_INTRO_LOCAL_STORAGE, sidebarConfig } from './config'; export type { SidebarOptions, SubmenuOptions } from './config'; export { diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index 750303e4df..85e9dc96a8 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -83,7 +83,6 @@ import { SidebarSpaceClassKey, SidebarSpacerClassKey, SidebarDividerClassKey, - SidebarIntroClassKey, SidebarItemClassKey, SidebarPageClassKey, CustomProviderClassKey, @@ -157,7 +156,6 @@ type BackstageComponentsNameToClassKey = { BackstageSidebarSpace: SidebarSpaceClassKey; BackstageSidebarSpacer: SidebarSpacerClassKey; BackstageSidebarDivider: SidebarDividerClassKey; - BackstageSidebarIntro: SidebarIntroClassKey; BackstageSidebarItem: SidebarItemClassKey; BackstageSidebarPage: SidebarPageClassKey; BackstageCustomProvider: CustomProviderClassKey; From fe069efbe693cdc092d5d5b1729f7fe3b5c83796 Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 26 Jan 2024 11:49:19 -0500 Subject: [PATCH 079/468] fix prettier Signed-off-by: Aramis --- microsite/docusaurus.config.js | 6 +++--- microsite/sidebars.json | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/microsite/docusaurus.config.js b/microsite/docusaurus.config.js index 88fed74ac2..a49fca85c7 100644 --- a/microsite/docusaurus.config.js +++ b/microsite/docusaurus.config.js @@ -146,12 +146,12 @@ module.exports = { }, { from: '/docs/auth/glossary', - to: '/docs/references/glossary' + to: '/docs/references/glossary', }, { from: '/docs/overview/glossary', - to: '/docs/references/glossary' - } + to: '/docs/references/glossary', + }, ], }, ], diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 1c151159db..9bbc42dbfc 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -478,8 +478,6 @@ ], "FAQ": ["faq/index", "faq/product", "faq/technical"], "Accessibility": ["accessibility/index"], - "References": [ - "references/glossary" - ] + "References": ["references/glossary"] } } From 9185966262bf2718b94a0f00465bc0660d47d845 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 26 Jan 2024 12:14:01 -0500 Subject: [PATCH 080/468] Remove utility API instructions Since #22549 adds this API to the core framework, integrators don't need to define their own. Signed-off-by: Jamie Klassen --- docs/auth/vmware-cloud/provider.md | 81 +----------------------------- 1 file changed, 1 insertion(+), 80 deletions(-) diff --git a/docs/auth/vmware-cloud/provider.md b/docs/auth/vmware-cloud/provider.md index 00a1315b6a..20115f9e75 100644 --- a/docs/auth/vmware-cloud/provider.md +++ b/docs/auth/vmware-cloud/provider.md @@ -111,85 +111,6 @@ can be replaced with a more suitable resolver for the app in question. See [Sign-in Identities and Resolvers](../identity-resolver.md) for details. -## Create a Utility API - -At the moment, this auth provider is not included in the default API factories, -but you can create one with a change to your frontend like: - -```ts title="packages/app/src/apis.ts" -import { - ScmIntegrationsApi, - scmIntegrationsApiRef, - ScmAuth, -} from '@backstage/integration-react'; -import { - costInsightsApiRef, - ExampleCostInsightsClient, -} from '@backstage/plugin-cost-insights'; -import { - graphQlBrowseApiRef, - GraphQLEndpoints, -} from '@backstage/plugin-graphiql'; -/* highlight-add-start */ -import { OAuth2 } from '@backstage/core-app-api'; -/* highlight-add-end */ -import { - AnyApiFactory, - /* highlight-add-start */ - ApiRef, - OpenIdConnectApi, - ProfileInfoApi, - BackstageIdentityApi, - SessionApi, - /* highlight-add-end */ - configApiRef, - /* highlight-add-start */ - createApiRef, - /* highlight-add-end */ - createApiFactory, - discoveryApiRef, - errorApiRef, - fetchApiRef, - githubAuthApiRef, - /* highlight-add-start */ - oauthRequestApiRef, - /* highlight-add-end */ -} from '@backstage/core-plugin-api'; -import { AuthProxyDiscoveryApi } from './AuthProxyDiscoveryApi'; - -/* highlight-add-start */ -export const vmwareCloudAuthApiRef: ApiRef< - OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi -> = createApiRef({ - id: 'auth.vmware-cloud-auth-provider', -}); -/* highlight-add-end */ - -export const apis: AnyApiFactory[] = [ - /* highlight-add-start */ - createApiFactory({ - api: vmwareCloudAuthApiRef, - deps: { - discoveryApi: discoveryApiRef, - oauthRequestApi: oauthRequestApiRef, - configApi: configApiRef, - }, - factory: ({ discoveryApi, oauthRequestApi, configApi }) => - OAuth2.create({ - discoveryApi, - oauthRequestApi, - provider: { - id: 'vmwareCloudServices', - title: 'VMware Cloud', - icon: () => null, - }, - environment: configApi.getOptionalString('auth.environment'), - defaultScopes: ['openid'], - }), - }), - /* highlight-add-end */ -``` - ## Add to Sign-in Page See the [Sign-In Configuration](../index.md#sign-in-configuration) docs for @@ -197,7 +118,7 @@ general guidance, but as an example: ```tsx title="packages/app/src/App.tsx" /* highlight-add-start */ -import { vmwareCloudAuthApiRef } from './apis'; +import { vmwareCloudAuthApiRef } from '@backstage/core-plugin-api'; import { SignInPage } from '@backstage/core-components'; /* highlight-add-end */ From f919be97d995c4eb5cd2b11a48f1daab9809291f Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 26 Jan 2024 12:06:19 -0500 Subject: [PATCH 081/468] Utility API for VMware Cloud auth Signed-off-by: Jamie Klassen --- .changeset/kind-wombats-draw.md | 11 ++++ packages/app-defaults/src/defaults/apis.ts | 18 +++++++ packages/core-app-api/api-report.md | 7 +++ .../src/apis/implementations/auth/index.ts | 1 + .../auth/vmwareCloud/VMwareCloudAuth.ts | 53 +++++++++++++++++++ .../implementations/auth/vmwareCloud/index.ts | 16 ++++++ packages/core-plugin-api/api-report.md | 9 ++++ .../src/apis/definitions/auth.ts | 19 +++++++ .../src/wiring/createApp.test.tsx | 1 + packages/frontend-plugin-api/api-report.md | 3 ++ .../src/apis/definitions/auth.ts | 1 + 11 files changed, 139 insertions(+) create mode 100644 .changeset/kind-wombats-draw.md create mode 100644 packages/core-app-api/src/apis/implementations/auth/vmwareCloud/VMwareCloudAuth.ts create mode 100644 packages/core-app-api/src/apis/implementations/auth/vmwareCloud/index.ts diff --git a/.changeset/kind-wombats-draw.md b/.changeset/kind-wombats-draw.md new file mode 100644 index 0000000000..29a0e9da3c --- /dev/null +++ b/.changeset/kind-wombats-draw.md @@ -0,0 +1,11 @@ +--- +'@backstage/core-plugin-api': minor +'@backstage/app-defaults': minor +'@backstage/core-app-api': minor +'@backstage/frontend-plugin-api': patch +--- + +Added a utility API for VMware Cloud auth; the API ref is available in the +`@backstage/core-plugin-api` and `@backstage/frontend-plugin-api` packages, the +implementation is in `@backstage/core-app-api` and a factory has been added to +`@backstage/app-defaults`. diff --git a/packages/app-defaults/src/defaults/apis.ts b/packages/app-defaults/src/defaults/apis.ts index 848d204670..4e9e1a492c 100644 --- a/packages/app-defaults/src/defaults/apis.ts +++ b/packages/app-defaults/src/defaults/apis.ts @@ -34,6 +34,7 @@ import { AtlassianAuth, createFetchApi, FetchMiddlewares, + VMwareCloudAuth, } from '@backstage/core-app-api'; import { @@ -56,6 +57,7 @@ import { bitbucketAuthApiRef, bitbucketServerAuthApiRef, atlassianAuthApiRef, + vmwareCloudAuthApiRef, } from '@backstage/core-plugin-api'; import { permissionApiRef, @@ -259,6 +261,22 @@ export const apis = [ }); }, }), + createApiFactory({ + api: vmwareCloudAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return VMwareCloudAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), createApiFactory({ api: permissionApiRef, deps: { diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 05071255ba..8b8cc991c8 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/api-report.md @@ -64,6 +64,7 @@ import { SessionState } from '@backstage/core-plugin-api'; import { StorageApi } from '@backstage/core-plugin-api'; import { StorageValueSnapshot } from '@backstage/core-plugin-api'; import { SubRouteRef } from '@backstage/core-plugin-api'; +import { vmwareCloudAuthApiRef } from '@backstage/core-plugin-api'; // @public export class AlertApiForwarder implements AlertApi { @@ -651,6 +652,12 @@ export class UrlPatternDiscovery implements DiscoveryApi { getBaseUrl(pluginId: string): Promise; } +// @public +export class VMwareCloudAuth { + // (undocumented) + static create(options: OAuthApiCreateOptions): typeof vmwareCloudAuthApiRef.T; +} + // @public export class WebStorage implements StorageApi { constructor(namespace: string, errorApi: ErrorApi); diff --git a/packages/core-app-api/src/apis/implementations/auth/index.ts b/packages/core-app-api/src/apis/implementations/auth/index.ts index b54e0424da..e02e07961a 100644 --- a/packages/core-app-api/src/apis/implementations/auth/index.ts +++ b/packages/core-app-api/src/apis/implementations/auth/index.ts @@ -25,4 +25,5 @@ export * from './onelogin'; export * from './bitbucket'; export * from './bitbucketServer'; export * from './atlassian'; +export * from './vmwareCloud'; export type { OAuthApiCreateOptions, AuthApiCreateOptions } from './types'; diff --git a/packages/core-app-api/src/apis/implementations/auth/vmwareCloud/VMwareCloudAuth.ts b/packages/core-app-api/src/apis/implementations/auth/vmwareCloud/VMwareCloudAuth.ts new file mode 100644 index 0000000000..83a2be0668 --- /dev/null +++ b/packages/core-app-api/src/apis/implementations/auth/vmwareCloud/VMwareCloudAuth.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2024 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 { vmwareCloudAuthApiRef } from '@backstage/core-plugin-api'; +import { OAuth2 } from '../oauth2'; +import { OAuthApiCreateOptions } from '../types'; + +const DEFAULT_PROVIDER = { + id: 'vmwareCloudServices', + title: 'VMware Cloud', + icon: () => null, +}; + +/** + * Implements the OAuth flow for VMware Cloud Services + * + * @public + */ +export default class VMwareCloudAuth { + static create( + options: OAuthApiCreateOptions, + ): typeof vmwareCloudAuthApiRef.T { + const { + configApi, + discoveryApi, + oauthRequestApi, + environment = 'development', + provider = DEFAULT_PROVIDER, + defaultScopes = ['openid'], + } = options; + + return OAuth2.create({ + configApi, + discoveryApi, + oauthRequestApi, + provider, + environment, + defaultScopes, + }); + } +} diff --git a/packages/core-app-api/src/apis/implementations/auth/vmwareCloud/index.ts b/packages/core-app-api/src/apis/implementations/auth/vmwareCloud/index.ts new file mode 100644 index 0000000000..023f700149 --- /dev/null +++ b/packages/core-app-api/src/apis/implementations/auth/vmwareCloud/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2024 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 { default as VMwareCloudAuth } from './VMwareCloudAuth'; diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 1fa40e2d0c..2a993687b3 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -776,6 +776,15 @@ export function useRouteRefParams( _routeRef: RouteRef | SubRouteRef, ): Params; +// @public +export const vmwareCloudAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +>; + // @public export function withApis( apis: TypesToApiRefs, diff --git a/packages/core-plugin-api/src/apis/definitions/auth.ts b/packages/core-plugin-api/src/apis/definitions/auth.ts index d9aec2ca5a..d89544cf68 100644 --- a/packages/core-plugin-api/src/apis/definitions/auth.ts +++ b/packages/core-plugin-api/src/apis/definitions/auth.ts @@ -450,3 +450,22 @@ export const atlassianAuthApiRef: ApiRef< > = createApiRef({ id: 'core.auth.atlassian', }); + +/** + * Provides authentication towards VMware Cloud APIs and identities. + * + * @public + * @remarks + * + * For more info about VMware Cloud identity and access management: + * - {@link https://docs.vmware.com/en/VMware-Cloud-services/services/Using-VMware-Cloud-Services/GUID-53D39337-D93A-4B84-BD18-DDF43C21479A.html} + */ +export const vmwareCloudAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +> = createApiRef({ + id: 'core.auth.vmware-cloud', +}); diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index 43bb4499b1..70f8cfa2ae 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -291,6 +291,7 @@ describe('createApp', () => { + ] " diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 4116469c2a..28204ac455 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -86,6 +86,7 @@ import { TypesToApiRefs } from '@backstage/core-plugin-api'; import { useApi } from '@backstage/core-plugin-api'; import { useApiHolder } from '@backstage/core-plugin-api'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import { vmwareCloudAuthApiRef } from '@backstage/core-plugin-api'; import { withApis } from '@backstage/core-plugin-api'; import { z } from 'zod'; import { ZodSchema } from 'zod'; @@ -1167,5 +1168,7 @@ export function useRouteRefParams( export { useTranslationRef }; +export { vmwareCloudAuthApiRef }; + export { withApis }; ``` diff --git a/packages/frontend-plugin-api/src/apis/definitions/auth.ts b/packages/frontend-plugin-api/src/apis/definitions/auth.ts index d27aebbf47..89509082f0 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/auth.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/auth.ts @@ -36,4 +36,5 @@ export { oktaAuthApiRef, microsoftAuthApiRef, oneloginAuthApiRef, + vmwareCloudAuthApiRef, } from '@backstage/core-plugin-api'; From 69df041760ae02c25a4526513fb3f698da1721ba Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:01:59 +0000 Subject: [PATCH 082/468] chore(deps): update github/codeql-action action to v3.23.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/verify_codeql.yml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 73dd5f8c57..d8dd0d9a6a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -66,6 +66,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1 + uses: github/codeql-action/upload-sarif@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 245ef401fa..ddf6d2cf28 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -58,6 +58,6 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} NODE_OPTIONS: --max-old-space-size=7168 - name: Upload Snyk report - uses: github/codeql-action/upload-sarif@v3.23.1 + uses: github/codeql-action/upload-sarif@v3.23.2 with: sarif_file: snyk.sarif diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index 6fbf516c08..df0dd80118 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3.23.1 + uses: github/codeql-action/init@v3.23.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3.23.1 + uses: github/codeql-action/autobuild@v3.23.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +80,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3.23.1 + uses: github/codeql-action/analyze@v3.23.2 From 04c936b686d04d16f76b37fa109bd6e06acf572a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:02:25 +0000 Subject: [PATCH 083/468] chore(deps): update microsoft/setup-msbuild action to v1.3.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_e2e-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 395dbe0b7f..802247341f 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -61,7 +61,7 @@ jobs: python-version: '3.10' - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.3.1 + uses: microsoft/setup-msbuild@v1.3.2 - name: Setup gyp env run: | From 31f0a0a2da7c6484588b9cb32356b2aa139d7f12 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 20 Jan 2024 10:19:51 -0600 Subject: [PATCH 084/468] Added context menu to scaffolder pages Signed-off-by: Andre Wanlin --- .changeset/quick-penguins-refuse.md | 6 +++++ plugins/scaffolder-react/api-report-alpha.md | 1 + .../ScaffolderPageContextMenu.tsx | 19 ++++++++++++++- .../components/ActionsPage/ActionsPage.tsx | 24 +++++++++++++++++-- .../ListTasksPage/ListTasksPage.tsx | 19 +++++++++++++-- .../TemplateEditorPage/TemplateEditorPage.tsx | 23 +++++++++++++++++- 6 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 .changeset/quick-penguins-refuse.md diff --git a/.changeset/quick-penguins-refuse.md b/.changeset/quick-penguins-refuse.md new file mode 100644 index 0000000000..c76686ae75 --- /dev/null +++ b/.changeset/quick-penguins-refuse.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +--- + +Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages# diff --git a/plugins/scaffolder-react/api-report-alpha.md b/plugins/scaffolder-react/api-report-alpha.md index 6976951670..a521fecc70 100644 --- a/plugins/scaffolder-react/api-report-alpha.md +++ b/plugins/scaffolder-react/api-report-alpha.md @@ -129,6 +129,7 @@ export type ScaffolderPageContextMenuProps = { onEditorClicked?: () => void; onActionsClicked?: () => void; onTasksClicked?: () => void; + onCreateClicked?: () => void; }; // @alpha diff --git a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx index 8326b361e1..2d132473a8 100644 --- a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx +++ b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx @@ -20,6 +20,7 @@ import ListItemText from '@material-ui/core/ListItemText'; import MenuItem from '@material-ui/core/MenuItem'; import MenuList from '@material-ui/core/MenuList'; import Popover from '@material-ui/core/Popover'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import { makeStyles } from '@material-ui/core/styles'; import Description from '@material-ui/icons/Description'; import Edit from '@material-ui/icons/Edit'; @@ -40,6 +41,7 @@ export type ScaffolderPageContextMenuProps = { onEditorClicked?: () => void; onActionsClicked?: () => void; onTasksClicked?: () => void; + onCreateClicked?: () => void; }; /** @@ -48,7 +50,8 @@ export type ScaffolderPageContextMenuProps = { export function ScaffolderPageContextMenu( props: ScaffolderPageContextMenuProps, ) { - const { onEditorClicked, onActionsClicked, onTasksClicked } = props; + const { onEditorClicked, onActionsClicked, onTasksClicked, onCreateClicked } = + props; const classes = useStyles(); const [anchorEl, setAnchorEl] = useState(); @@ -89,6 +92,20 @@ export function ScaffolderPageContextMenu( transformOrigin={{ vertical: 'top', horizontal: 'right' }} > + {onCreateClicked && ( + + + + + + + )} + {/* + + + + + */} {onEditorClicked && ( diff --git a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx index 5e7f9810c5..9957d22456 100644 --- a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx +++ b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx @@ -41,7 +41,7 @@ import classNames from 'classnames'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import ExpandLessIcon from '@material-ui/icons/ExpandLess'; -import { useApi } from '@backstage/core-plugin-api'; +import { useApi, useRouteRef } from '@backstage/core-plugin-api'; import { CodeSnippet, Content, @@ -52,6 +52,13 @@ import { Progress, } from '@backstage/core-components'; import Chip from '@material-ui/core/Chip'; +import { ScaffolderPageContextMenu } from '@backstage/plugin-scaffolder-react/alpha'; +import { useNavigate } from 'react-router-dom'; +import { + editRouteRef, + rootRouteRef, + scaffolderListTaskRouteRef, +} from '../../routes'; const useStyles = makeStyles(theme => ({ code: { @@ -109,6 +116,17 @@ const ExamplesTable = (props: { examples: ActionExample[] }) => { export const ActionsPage = () => { const api = useApi(scaffolderApiRef); + const navigate = useNavigate(); + const editorLink = useRouteRef(editRouteRef); + const tasksLink = useRouteRef(scaffolderListTaskRouteRef); + const createLink = useRouteRef(rootRouteRef); + + const scaffolderPageContextMenuProps = { + onEditorClicked: () => navigate(editorLink()), + onActionsClicked: undefined, + onTasksClicked: () => navigate(tasksLink()), + onCreateClicked: () => navigate(createLink()), + }; const classes = useStyles(); const { loading, value, error } = useAsync(async () => { return api.listActions(); @@ -326,7 +344,9 @@ export const ActionsPage = () => { pageTitleOverride="Create a New Component" title="Installed actions" subtitle="This is the collection of all installed actions" - /> + > + + {items} ); diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx index aac0ed5c79..6a764eaa1f 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -38,7 +38,9 @@ import { TaskStatusColumn, TemplateTitleColumn, } from './columns'; -import { rootRouteRef } from '../../routes'; +import { actionsRouteRef, editRouteRef, rootRouteRef } from '../../routes'; +import { ScaffolderPageContextMenu } from '@backstage/plugin-scaffolder-react/alpha'; +import { useNavigate } from 'react-router-dom'; export interface MyTaskPageProps { initiallySelectedFilter?: 'owned' | 'all'; @@ -134,13 +136,26 @@ const ListTaskPageContent = (props: MyTaskPageProps) => { }; export const ListTasksPage = (props: MyTaskPageProps) => { + const navigate = useNavigate(); + const editorLink = useRouteRef(editRouteRef); + const actionsLink = useRouteRef(actionsRouteRef); + const createLink = useRouteRef(rootRouteRef); + + const scaffolderPageContextMenuProps = { + onEditorClicked: () => navigate(editorLink()), + onActionsClicked: () => navigate(actionsLink()), + onTasksClicked: () => undefined, + onCreateClicked: () => navigate(createLink()), + }; return (
+ > + +
diff --git a/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx index aab693e590..d8145c4646 100644 --- a/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx +++ b/plugins/scaffolder/src/next/TemplateEditorPage/TemplateEditorPage.tsx @@ -27,6 +27,14 @@ import { type LayoutOptions, } from '@backstage/plugin-scaffolder-react'; import { TemplateEditorIntro } from './TemplateEditorIntro'; +import { ScaffolderPageContextMenu } from '@backstage/plugin-scaffolder-react/alpha'; +import { useNavigate } from 'react-router-dom'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { + actionsRouteRef, + rootRouteRef, + scaffolderListTaskRouteRef, +} from '../../routes'; type Selection = | { @@ -48,6 +56,17 @@ interface TemplateEditorPageProps { export function TemplateEditorPage(props: TemplateEditorPageProps) { const [selection, setSelection] = useState(); + const navigate = useNavigate(); + const actionsLink = useRouteRef(actionsRouteRef); + const tasksLink = useRouteRef(scaffolderListTaskRouteRef); + const createLink = useRouteRef(rootRouteRef); + + const scaffolderPageContextMenuProps = { + onEditorClicked: undefined, + onActionsClicked: () => navigate(actionsLink()), + onTasksClicked: () => navigate(tasksLink()), + onCreateClicked: () => navigate(createLink()), + }; let content: JSX.Element | null = null; if (selection?.type === 'local') { @@ -100,7 +119,9 @@ export function TemplateEditorPage(props: TemplateEditorPageProps) {
+ > + +
{content}
); From b734c9c83d9aaa564ad59963db1b207d20a63903 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 20 Jan 2024 10:21:09 -0600 Subject: [PATCH 085/468] Fixed typo Signed-off-by: Andre Wanlin --- .changeset/quick-penguins-refuse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/quick-penguins-refuse.md b/.changeset/quick-penguins-refuse.md index c76686ae75..5d3165114b 100644 --- a/.changeset/quick-penguins-refuse.md +++ b/.changeset/quick-penguins-refuse.md @@ -3,4 +3,4 @@ '@backstage/plugin-scaffolder': patch --- -Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages# +Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages From a9507252ad4480d7b56792ac592c2cfd974f9303 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 20 Jan 2024 10:26:13 -0600 Subject: [PATCH 086/468] Fix task list Signed-off-by: Andre Wanlin --- .../scaffolder/src/components/ListTasksPage/ListTasksPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx index 6a764eaa1f..8cd8ccefda 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -144,7 +144,7 @@ export const ListTasksPage = (props: MyTaskPageProps) => { const scaffolderPageContextMenuProps = { onEditorClicked: () => navigate(editorLink()), onActionsClicked: () => navigate(actionsLink()), - onTasksClicked: () => undefined, + onTasksClicked: undefined, onCreateClicked: () => navigate(createLink()), }; return ( From 30c212f11b5d8047735b057fa3d30909bf13e231 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Fri, 26 Jan 2024 13:09:50 -0600 Subject: [PATCH 087/468] Removed commented out code Signed-off-by: Andre Wanlin --- .../ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx index 2d132473a8..2239c86e06 100644 --- a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx +++ b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx @@ -100,12 +100,6 @@ export function ScaffolderPageContextMenu(
)} - {/* - - - - - */} {onEditorClicked && ( From 0504b78251e235405933aec1e1642fe931583127 Mon Sep 17 00:00:00 2001 From: Emmanuel Auffray Date: Fri, 26 Jan 2024 20:28:54 +0000 Subject: [PATCH 088/468] Cleanup my own typo and remove devcontainer Signed-off-by: Emmanuel Auffray --- .changeset/dry-vans-kiss.md | 2 +- .devcontainer/devcontainer.json | 97 --------------------------------- 2 files changed, 1 insertion(+), 98 deletions(-) delete mode 100644 .devcontainer/devcontainer.json diff --git a/.changeset/dry-vans-kiss.md b/.changeset/dry-vans-kiss.md index 031f31e912..5d11df989c 100644 --- a/.changeset/dry-vans-kiss.md +++ b/.changeset/dry-vans-kiss.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': patch --- -Simple typo fix in the the fetch:template action example on the word 'skeleton'. +Simple typo fix in the fetch:template action example on the word 'skeleton'. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 322317e382..0000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,97 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node -{ - "name": "Node.js & TypeScript", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/typescript-node:1-18-bookworm", - // "build": { - // "dockerfile": "Dockerfile" - // }, - "mounts": [ - // {"source":"${localWorkspaceFolder}","target":"/workspace","type":"bind"}, - { - "source": "${localEnv:HOME}/.ssh", - "target": "/home/node/.ssh", - "type": "bind" - }, - { - "source": "${localEnv:HOME}/.kube", - "target": "/usr/local/share/kube-localhost", - "type": "bind" - }, - { - "source": "${localEnv:HOME}/.azure", - "target": "/home/node/.azure", - "type": "bind" - } - ], - - // "remoteEnv": { - // "SYNC_LOCALHOST_KUBECONFIG": "true" - // }, - - "customizations": { - "vscode": { - "extensions": [ - "GitHub.copilot", - "GitHub.copilot-chat", - "GitHub.vscode-pull-request-github", - "github.vscode-github-actions", - "Intility.vscode-backstage", - "ms-azuretools.vscode-docker", - "ms-kubernetes-tools.kind-vscode", - "ms-kubernetes-tools.vscode-kubernetes-tools", - "humao.rest-client" - ] - } - }, - - // Features to add to the dev container. More info: https://containers.dev/features. - "features": { - // Azure CLI - // "ghcr.io/devcontainers/features/azure-cli:1": { - // "installBicep": true, - // "version": "latest" - // }, - // // Docker CLI accessing the host's Docker daemon - // "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { - // "moby": true, - // "installDockerBuildx": true, - // "version": "latest", - // "dockerDashComposeVersion": "v2" - // }, - // // Kubernetes CLI tools - // "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": { - // "version": "latest", - // "helm": "latest", - // "minikube": "none" - // }, - // // KinD - // "ghcr.io/devcontainers-contrib/features/kind:1": { - // "version": "latest" - // }, - // "ghcr.io/rio/features/k9s:1": {}, - // "ghcr.io/devcontainers/features/azure-cli:1": { - // "version": "latest" - // }, - // "ghcr.io/rjfmachado/devcontainer-features/cloud-native:1": { - // "kubectl": "latest", - // "helm": "latest", - // "kubelogin": "latest", - // "azwi": "latest", - // "flux": "latest", - // "cilium": "latest" - // } - } - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "yarn install", - - // Configure tool-specific properties. - // "customizations": {}, - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" -} From e2083dfb55e6b5b211ae16b40fa9d10332c6d181 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 27 Dec 2023 12:11:26 -0600 Subject: [PATCH 089/468] Added alpha support for the New Frontend System Signed-off-by: Andre Wanlin --- .changeset/beige-apples-press.md | 5 ++ packages/app-next/app-config.yaml | 7 +- plugins/azure-devops/api-report-alpha.md | 13 +++ plugins/azure-devops/package.json | 21 ++++- plugins/azure-devops/src/alpha.ts | 18 ++++ plugins/azure-devops/src/alpha/index.ts | 17 ++++ plugins/azure-devops/src/alpha/plugin.tsx | 104 ++++++++++++++++++++++ yarn.lock | 2 + 8 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 .changeset/beige-apples-press.md create mode 100644 plugins/azure-devops/api-report-alpha.md create mode 100644 plugins/azure-devops/src/alpha.ts create mode 100644 plugins/azure-devops/src/alpha/index.ts create mode 100644 plugins/azure-devops/src/alpha/plugin.tsx diff --git a/.changeset/beige-apples-press.md b/.changeset/beige-apples-press.md new file mode 100644 index 0000000000..56c46db830 --- /dev/null +++ b/.changeset/beige-apples-press.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': patch +--- + +Added alpha support for the New Frontend System (Declarative Integration) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 9cc3507fa4..2ffc8967e3 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -17,8 +17,13 @@ app: config: filter: kind:component has:links - entity-card:linguist/languages + - entity-card:azure-devops/readme # Entity page content - entity-content:techdocs + - entity-content:azure-devops/pipelines + - entity-content:azure-devops/pull-requests + - entity-content:azure-devops/git-tags + # scmAuthExtension: >- # createScmAuthExtension({ @@ -32,7 +37,7 @@ app: # }) # externalRouteRefs: - # bind(catalogPlugin.externalRoutes, { + # bind(catalogPlugin.externalRoutes, # createComponent: scaffolderPlugin.routes.root, # viewTechDoc: techdocsPlugin.routes.docRoot, # createFromTemplate: scaffolderPlugin.routes.selectedTemplate, diff --git a/plugins/azure-devops/api-report-alpha.md b/plugins/azure-devops/api-report-alpha.md new file mode 100644 index 0000000000..8040c0235f --- /dev/null +++ b/plugins/azure-devops/api-report-alpha.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-azure-devops" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackstagePlugin } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +const _default: BackstagePlugin<{}, {}>; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index d0b5932b31..e367cfb89f 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -5,9 +5,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "frontend-plugin" @@ -30,9 +43,11 @@ }, "dependencies": { "@backstage/catalog-model": "workspace:^", + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-azure-devops-common": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", diff --git a/plugins/azure-devops/src/alpha.ts b/plugins/azure-devops/src/alpha.ts new file mode 100644 index 0000000000..e80f131817 --- /dev/null +++ b/plugins/azure-devops/src/alpha.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './alpha/index'; +export { default } from './alpha/index'; diff --git a/plugins/azure-devops/src/alpha/index.ts b/plugins/azure-devops/src/alpha/index.ts new file mode 100644 index 0000000000..2f137f09ee --- /dev/null +++ b/plugins/azure-devops/src/alpha/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './plugin'; diff --git a/plugins/azure-devops/src/alpha/plugin.tsx b/plugins/azure-devops/src/alpha/plugin.tsx new file mode 100644 index 0000000000..f30b5cf219 --- /dev/null +++ b/plugins/azure-devops/src/alpha/plugin.tsx @@ -0,0 +1,104 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { + createApiExtension, + createApiFactory, + createPageExtension, + createPlugin, + discoveryApiRef, + identityApiRef, +} from '@backstage/frontend-plugin-api'; +import { azureDevOpsApiRef, AzureDevOpsClient } from '../api'; +import { + compatWrapper, + convertLegacyRouteRef, +} from '@backstage/core-compat-api'; +import { + createEntityCardExtension, + createEntityContentExtension, +} from '@backstage/plugin-catalog-react/alpha'; +import { azurePullRequestDashboardRouteRef } from '../routes'; + +export const azureDevOpsApi = createApiExtension({ + factory: createApiFactory({ + api: azureDevOpsApiRef, + deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, + factory: ({ discoveryApi, identityApi }) => + new AzureDevOpsClient({ discoveryApi, identityApi }), + }), +}); + +const azureDevOpsPullRequestPage = createPageExtension({ + defaultPath: '/azure-pull-requests', + routeRef: convertLegacyRouteRef(azurePullRequestDashboardRouteRef), + loader: () => + import('../components/PullRequestsPage').then(m => + compatWrapper(), + ), +}); + +const entityAzurePipelinesContent = createEntityContentExtension({ + name: 'pipelines', + defaultPath: '/pipelines', + defaultTitle: 'Pipelines', + loader: () => + import('../components/EntityPageAzurePipelines').then(m => + compatWrapper(), + ), +}); + +const entityAzureGitTagsContent = createEntityContentExtension({ + name: 'git-tags', + defaultPath: '/git-tags', + defaultTitle: 'Git Tags', + loader: () => + import('../components/EntityPageAzureGitTags').then(m => + compatWrapper(), + ), +}); + +const entityAzurePullRequestsContent = createEntityContentExtension({ + name: 'pull-requests', + defaultPath: '/pull-requests', + defaultTitle: 'Pull Requests', + loader: () => + import('../components/EntityPageAzurePullRequests').then(m => + compatWrapper(), + ), +}); + +export const entityAzureReadmeCard = createEntityCardExtension({ + name: 'readme', + loader: async () => + import('../components/ReadmeCard').then(m => + compatWrapper(), + ), +}); + +/** @alpha */ +export default createPlugin({ + id: 'azure-devops', + extensions: [ + azureDevOpsApi, + entityAzureReadmeCard, + entityAzurePipelinesContent, + entityAzureGitTagsContent, + entityAzurePullRequestsContent, + azureDevOpsPullRequestPage, + ], +}); diff --git a/yarn.lock b/yarn.lock index 42811acc21..9649405700 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4909,10 +4909,12 @@ __metadata: dependencies: "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-azure-devops-common": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/test-utils": "workspace:^" From e6bb058f8d1a205dde05ffdfa645a65d62d65dc4 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 27 Dec 2023 12:58:21 -0600 Subject: [PATCH 090/468] Clean up Signed-off-by: Andre Wanlin --- packages/app-next/app-config.yaml | 3 +-- plugins/azure-devops/src/alpha/plugin.tsx | 14 ++++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 2ffc8967e3..9af6b6c438 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -24,7 +24,6 @@ app: - entity-content:azure-devops/pull-requests - entity-content:azure-devops/git-tags - # scmAuthExtension: >- # createScmAuthExtension({ # id: 'apis.scmAuth.addons.ghe', @@ -37,7 +36,7 @@ app: # }) # externalRouteRefs: - # bind(catalogPlugin.externalRoutes, + # bind(catalogPlugin.externalRoutes, { # createComponent: scaffolderPlugin.routes.root, # viewTechDoc: techdocsPlugin.routes.docRoot, # createFromTemplate: scaffolderPlugin.routes.selectedTemplate, diff --git a/plugins/azure-devops/src/alpha/plugin.tsx b/plugins/azure-devops/src/alpha/plugin.tsx index f30b5cf219..f601baf7c0 100644 --- a/plugins/azure-devops/src/alpha/plugin.tsx +++ b/plugins/azure-devops/src/alpha/plugin.tsx @@ -34,6 +34,7 @@ import { } from '@backstage/plugin-catalog-react/alpha'; import { azurePullRequestDashboardRouteRef } from '../routes'; +/** @alpha */ export const azureDevOpsApi = createApiExtension({ factory: createApiFactory({ api: azureDevOpsApiRef, @@ -43,7 +44,8 @@ export const azureDevOpsApi = createApiExtension({ }), }); -const azureDevOpsPullRequestPage = createPageExtension({ +/** @alpha */ +export const azureDevOpsPullRequestPage = createPageExtension({ defaultPath: '/azure-pull-requests', routeRef: convertLegacyRouteRef(azurePullRequestDashboardRouteRef), loader: () => @@ -52,7 +54,8 @@ const azureDevOpsPullRequestPage = createPageExtension({ ), }); -const entityAzurePipelinesContent = createEntityContentExtension({ +/** @alpha */ +export const entityAzurePipelinesContent = createEntityContentExtension({ name: 'pipelines', defaultPath: '/pipelines', defaultTitle: 'Pipelines', @@ -62,7 +65,8 @@ const entityAzurePipelinesContent = createEntityContentExtension({ ), }); -const entityAzureGitTagsContent = createEntityContentExtension({ +/** @alpha */ +export const entityAzureGitTagsContent = createEntityContentExtension({ name: 'git-tags', defaultPath: '/git-tags', defaultTitle: 'Git Tags', @@ -72,7 +76,8 @@ const entityAzureGitTagsContent = createEntityContentExtension({ ), }); -const entityAzurePullRequestsContent = createEntityContentExtension({ +/** @alpha */ +export const entityAzurePullRequestsContent = createEntityContentExtension({ name: 'pull-requests', defaultPath: '/pull-requests', defaultTitle: 'Pull Requests', @@ -82,6 +87,7 @@ const entityAzurePullRequestsContent = createEntityContentExtension({ ), }); +/** @alpha */ export const entityAzureReadmeCard = createEntityCardExtension({ name: 'readme', loader: async () => From c7e52c3cf30cfce11d9f43d76e3a01805525db82 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 26 Jan 2024 17:32:01 -0500 Subject: [PATCH 091/468] update labeler action to v5 and update config file according to new schema Signed-off-by: Jamie Klassen --- .github/labeler.yml | 76 ++++++++++++++-------- .github/workflows/automate_area-labels.yml | 2 +- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 5066ebaf05..6653c2d868 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,38 +1,62 @@ area:catalog: - - plugins/catalog/**/* - - plugins/catalog-*/**/* - - packages/catalog-*/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/catalog/**/* + - plugins/catalog-*/**/* + - packages/catalog-*/**/* area:discoverability: # search + home - - plugins/search/**/* - - plugins/search-*/**/* - - packages/search-*/**/* - - plugins/home/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/search/**/* + - plugins/search-*/**/* + - packages/search-*/**/* + - plugins/home/**/* area:kubernetes: - - plugins/kubernetes/**/* - - plugins/kubernetes-*/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/kubernetes/**/* + - plugins/kubernetes-*/**/* area:permission: - - plugins/permission-*/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/permission-*/**/* area:scaffolder: - - plugins/scaffolder/**/* - - plugins/scaffolder-*/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/scaffolder/**/* + - plugins/scaffolder-*/**/* area:techdocs: - - plugins/techdocs/**/* - - plugins/techdocs-*/**/* - - packages/techdocs-*/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/techdocs/**/* + - plugins/techdocs-*/**/* + - packages/techdocs-*/**/* auth: - - plugins/auth-*/**/* - - packages/core-app-api/src/apis/implementations/auth/**/* - - packages/core-app-api/src/lib/Auth*/**/* - - packages/core-plugin-api/src/apis/definitions/auth.ts + - changed-files: + - any-glob-to-any-file: + - plugins/auth-*/**/* + - packages/core-app-api/src/apis/implementations/auth/**/* + - packages/core-app-api/src/lib/Auth*/**/* + - packages/core-plugin-api/src/apis/definitions/auth.ts documentation: - - docs/**/* + - changed-files: + - any-glob-to-any-file: + - docs/**/* homepage: - - plugins/home/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/home/**/* microsite: - - microsite/**/* + - changed-files: + - any-glob-to-any-file: + - microsite/**/* search: - - plugins/search/**/* - - plugins/search-*/**/* - - packages/search-*/**/* + - changed-files: + - any-glob-to-any-file: + - plugins/search/**/* + - plugins/search-*/**/* + - packages/search-*/**/* storybook: - - storybook/**/* + - changed-files: + - any-glob-to-any-file: + - storybook/**/* diff --git a/.github/workflows/automate_area-labels.yml b/.github/workflows/automate_area-labels.yml index 5d12e4ca89..ecaf464903 100644 --- a/.github/workflows/automate_area-labels.yml +++ b/.github/workflows/automate_area-labels.yml @@ -17,7 +17,7 @@ jobs: with: egress-policy: audit - - uses: actions/labeler@v4.3.0 + - uses: actions/labeler@v5.0.0 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' sync-labels: true From 3c184afa918bc95841a0e3779eb3897283111e43 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 26 Jan 2024 19:09:50 -0500 Subject: [PATCH 092/468] refactor: extract common dialog component Signed-off-by: Jamie Klassen --- .changeset/quiet-donkeys-punch.md | 5 + plugins/kubernetes-react/api-report.md | 2 +- .../KubernetesDialog/KubernetesDialog.tsx | 110 ++++++++++++++++++ .../src/components/KubernetesDialog/index.ts | 16 +++ .../PodExecTerminal/PodExecTerminalDialog.tsx | 87 +++----------- .../components/Pods/PodLogs/PodLogsDialog.tsx | 72 ++---------- 6 files changed, 157 insertions(+), 135 deletions(-) create mode 100644 .changeset/quiet-donkeys-punch.md create mode 100644 plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx create mode 100644 plugins/kubernetes-react/src/components/KubernetesDialog/index.ts diff --git a/.changeset/quiet-donkeys-punch.md b/.changeset/quiet-donkeys-punch.md new file mode 100644 index 0000000000..a375ed148c --- /dev/null +++ b/.changeset/quiet-donkeys-punch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-react': patch +--- + +Extracted common dialog component. diff --git a/plugins/kubernetes-react/api-report.md b/plugins/kubernetes-react/api-report.md index 7e9c2fbd61..8a0ec4de8f 100644 --- a/plugins/kubernetes-react/api-report.md +++ b/plugins/kubernetes-react/api-report.md @@ -685,7 +685,7 @@ export const PodExecTerminal: ( // @public export const PodExecTerminalDialog: ( props: PodExecTerminalProps, -) => React_2.JSX.Element; +) => false | React_2.JSX.Element | undefined; // @public export interface PodExecTerminalProps { diff --git a/plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx b/plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx new file mode 100644 index 0000000000..cd5802f464 --- /dev/null +++ b/plugins/kubernetes-react/src/components/KubernetesDialog/KubernetesDialog.tsx @@ -0,0 +1,110 @@ +/* + * Copyright 2024 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 { + Button, + Dialog, + DialogContent, + DialogTitle, + IconButton, + Theme, + createStyles, + makeStyles, +} from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; +import React, { useState } from 'react'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + dialogPaper: { minHeight: 'calc(100% - 64px)' }, + dialogContent: { flexBasis: 0 }, + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + color: theme.palette.grey[500], + }, + }), +); + +export interface KubernetesDialogProps { + buttonAriaLabel: string; + buttonIcon: React.ReactNode; + buttonText: string; + children?: React.ReactNode; + disabled?: boolean; + title: string; +} + +/** + * Dialog component for use in the Kubernetes plugin + * + * @public + */ +export const KubernetesDialog = ({ + buttonAriaLabel, + buttonIcon, + buttonText, + children, + disabled, + title, +}: KubernetesDialogProps) => { + const classes = useStyles(); + + const [open, setOpen] = useState(false); + const openDialog = () => { + setOpen(true); + }; + const closeDialog = () => { + setOpen(false); + }; + + return ( + <> + + + {title} + + + + + + {children} + + + + + ); +}; diff --git a/plugins/kubernetes-react/src/components/KubernetesDialog/index.ts b/plugins/kubernetes-react/src/components/KubernetesDialog/index.ts new file mode 100644 index 0000000000..1b10c1346e --- /dev/null +++ b/plugins/kubernetes-react/src/components/KubernetesDialog/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2024 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 * from './KubernetesDialog'; diff --git a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalDialog.tsx b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalDialog.tsx index 5ed8c8c0d1..0b321aa108 100644 --- a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalDialog.tsx +++ b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalDialog.tsx @@ -13,98 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - Button, - createStyles, - Dialog, - DialogContent, - DialogTitle, - IconButton, - makeStyles, - Theme, -} from '@material-ui/core'; -import CloseIcon from '@material-ui/icons/Close'; -import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; -import React, { useState } from 'react'; +import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; +import React from 'react'; + +import { KubernetesDialog } from '../KubernetesDialog'; import { useIsPodExecTerminalSupported } from '../../hooks'; import { PodExecTerminal, PodExecTerminalProps } from './PodExecTerminal'; -const useStyles = makeStyles((theme: Theme) => - createStyles({ - dialogPaper: { minHeight: 'calc(100% - 64px)' }, - dialogContent: { flexBasis: 0 }, - closeButton: { - position: 'absolute', - right: theme.spacing(1), - top: theme.spacing(1), - color: theme.palette.grey[500], - }, - }), -); - /** * Opens a terminal connected to the given pod's container in a dialog * * @public */ export const PodExecTerminalDialog = (props: PodExecTerminalProps) => { - const classes = useStyles(); const { clusterName, containerName, podName } = props; - const [open, setOpen] = useState(false); - const isPodExecTerminalSupported = useIsPodExecTerminalSupported(); - const openDialog = () => { - setOpen(true); - }; - - const closeDialog = () => { - setOpen(false); - }; - return ( - <> - {!isPodExecTerminalSupported.loading && - isPodExecTerminalSupported.value && ( - - - {podName} - {containerName} terminal shell on cluster{' '} - {clusterName} - - - - - - - - - )} - - - + + + ) ); }; diff --git a/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx b/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx index 48fda2de2a..3ffd4d8fa0 100644 --- a/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx +++ b/plugins/kubernetes-react/src/components/Pods/PodLogs/PodLogsDialog.tsx @@ -13,36 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState } from 'react'; +import React from 'react'; -import { - Button, - createStyles, - Dialog, - DialogContent, - DialogTitle, - IconButton, - makeStyles, - Theme, -} from '@material-ui/core'; import SubjectIcon from '@material-ui/icons/Subject'; -import CloseIcon from '@material-ui/icons/Close'; - +import { KubernetesDialog } from '../../KubernetesDialog'; import { PodLogs } from './PodLogs'; import { ContainerScope } from './types'; -const useStyles = makeStyles((theme: Theme) => - createStyles({ - closeButton: { - position: 'absolute', - right: theme.spacing(1), - top: theme.spacing(1), - color: theme.palette.grey[500], - }, - }), -); - /** * Props for PodLogsDialog * @@ -58,43 +36,15 @@ export interface PodLogsDialogProps { * @public */ export const PodLogsDialog = ({ containerScope }: PodLogsDialogProps) => { - const classes = useStyles(); - const [open, setOpen] = useState(false); - - const openDialog = () => { - setOpen(true); - }; - - const closeDialog = () => { - setOpen(false); - }; return ( - <> - - - {containerScope.podName} - {containerScope.containerName} logs on - cluster {containerScope.clusterName} - - - - - - - - - - + } + buttonText="Logs" + disabled={false} + title={`${containerScope.podName} - ${containerScope.containerName} logs on cluster ${containerScope.clusterName}`} + > + + ); }; From b07ec70c90331bde0473d0ffe1d3b37dfb07de0f Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Wed, 24 Jan 2024 16:04:37 +0100 Subject: [PATCH 093/468] feat(scaffolder): Use more distinguishable icons for link and text output Use more distinguishable icons for link (`Link`) and text output (`Description`). Currently, both use the same icon `Web` (until specified explicitly at the template). For a user, it is not really clear which of these will open more output below it vs open a URL/a new tab. Signed-off-by: Patrick Jungermann --- .changeset/twelve-hounds-know.md | 5 +++++ .../src/next/components/TemplateOutputs/LinkOutputs.tsx | 4 ++-- .../src/next/components/TemplateOutputs/TextOutputs.tsx | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/twelve-hounds-know.md diff --git a/.changeset/twelve-hounds-know.md b/.changeset/twelve-hounds-know.md new file mode 100644 index 0000000000..3c95c71ddb --- /dev/null +++ b/.changeset/twelve-hounds-know.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': minor +--- + +Use more distinguishable icons for link (`Link`) and text output (`Description`). diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/LinkOutputs.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/LinkOutputs.tsx index d172aa9cb3..7877653197 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateOutputs/LinkOutputs.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/LinkOutputs.tsx @@ -17,7 +17,7 @@ import { IconComponent, useApp, useRouteRef } from '@backstage/core-plugin-api'; import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { Button, makeStyles } from '@material-ui/core'; import React from 'react'; -import WebIcon from '@material-ui/icons/Web'; +import LinkIcon from '@material-ui/icons/Link'; import { parseEntityRef } from '@backstage/catalog-model'; import { Link } from '@backstage/core-components'; import { ScaffolderTaskOutput } from '../../../api'; @@ -37,7 +37,7 @@ export const LinkOutputs = (props: { output: ScaffolderTaskOutput }) => { const entityRoute = useRouteRef(entityRouteRef); const iconResolver = (key?: string): IconComponent => - app.getSystemIcon(key!) ?? WebIcon; + app.getSystemIcon(key!) ?? LinkIcon; return ( <> diff --git a/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx index 8f61aa6a30..74a5eabb8f 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateOutputs/TextOutputs.tsx @@ -15,7 +15,7 @@ */ import { IconComponent, useApp } from '@backstage/core-plugin-api'; import { Button } from '@material-ui/core'; -import WebIcon from '@material-ui/icons/Web'; +import DescriptionIcon from '@material-ui/icons/Description'; import React from 'react'; import { ScaffolderTaskOutput } from '../../../api'; @@ -33,7 +33,7 @@ export const TextOutputs = (props: { const app = useApp(); const iconResolver = (key?: string): IconComponent => - app.getSystemIcon(key!) ?? WebIcon; + app.getSystemIcon(key!) ?? DescriptionIcon; return ( <> From a3243f206001e6567937c7858777ba772775a683 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 27 Jan 2024 13:08:37 -0600 Subject: [PATCH 094/468] Standardized naming based on feedback Signed-off-by: Andre Wanlin --- plugins/azure-devops/src/alpha/plugin.tsx | 33 ++++++++++++----------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/plugins/azure-devops/src/alpha/plugin.tsx b/plugins/azure-devops/src/alpha/plugin.tsx index f601baf7c0..40305c1714 100644 --- a/plugins/azure-devops/src/alpha/plugin.tsx +++ b/plugins/azure-devops/src/alpha/plugin.tsx @@ -55,7 +55,7 @@ export const azureDevOpsPullRequestPage = createPageExtension({ }); /** @alpha */ -export const entityAzurePipelinesContent = createEntityContentExtension({ +export const azureDevOpsPipelinesEntityContent = createEntityContentExtension({ name: 'pipelines', defaultPath: '/pipelines', defaultTitle: 'Pipelines', @@ -66,7 +66,7 @@ export const entityAzurePipelinesContent = createEntityContentExtension({ }); /** @alpha */ -export const entityAzureGitTagsContent = createEntityContentExtension({ +export const azureDevOpsGitTagsEntityContent = createEntityContentExtension({ name: 'git-tags', defaultPath: '/git-tags', defaultTitle: 'Git Tags', @@ -77,18 +77,19 @@ export const entityAzureGitTagsContent = createEntityContentExtension({ }); /** @alpha */ -export const entityAzurePullRequestsContent = createEntityContentExtension({ - name: 'pull-requests', - defaultPath: '/pull-requests', - defaultTitle: 'Pull Requests', - loader: () => - import('../components/EntityPageAzurePullRequests').then(m => - compatWrapper(), - ), -}); +export const azureDevOpsPullRequestsEntityContent = + createEntityContentExtension({ + name: 'pull-requests', + defaultPath: '/pull-requests', + defaultTitle: 'Pull Requests', + loader: () => + import('../components/EntityPageAzurePullRequests').then(m => + compatWrapper(), + ), + }); /** @alpha */ -export const entityAzureReadmeCard = createEntityCardExtension({ +export const azureDevOpsReadmeEntityCard = createEntityCardExtension({ name: 'readme', loader: async () => import('../components/ReadmeCard').then(m => @@ -101,10 +102,10 @@ export default createPlugin({ id: 'azure-devops', extensions: [ azureDevOpsApi, - entityAzureReadmeCard, - entityAzurePipelinesContent, - entityAzureGitTagsContent, - entityAzurePullRequestsContent, + azureDevOpsReadmeEntityCard, + azureDevOpsPipelinesEntityContent, + azureDevOpsGitTagsEntityContent, + azureDevOpsPullRequestsEntityContent, azureDevOpsPullRequestPage, ], }); From 4131bd27248922c64e084ae05e9ba0ff13235f9d Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Sat, 27 Jan 2024 22:59:17 +0100 Subject: [PATCH 095/468] Fix for issue 20256: Bug Report: converting circular structure to JSON error Signed-off-by: bnechyporenko --- plugins/scaffolder-react/package.json | 1 + plugins/scaffolder-react/src/next/lib/schema.ts | 3 ++- yarn.lock | 9 +++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index bb038cd2ee..0f6da12d6c 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -66,6 +66,7 @@ "@types/json-schema": "^7.0.9", "@types/react": "^16.13.1 || ^17.0.0", "classnames": "^2.2.6", + "flatted": "3.2.9", "humanize-duration": "^3.25.1", "immer": "^9.0.1", "json-schema": "^0.4.0", diff --git a/plugins/scaffolder-react/src/next/lib/schema.ts b/plugins/scaffolder-react/src/next/lib/schema.ts index c606d715e7..b7c1364f41 100644 --- a/plugins/scaffolder-react/src/next/lib/schema.ts +++ b/plugins/scaffolder-react/src/next/lib/schema.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import { JsonObject } from '@backstage/types'; +import { stringify, parse } from 'flatted'; import { FieldValidation, UiSchema } from '@rjsf/utils'; function isObject(value: unknown): value is JsonObject { @@ -123,7 +124,7 @@ export const extractSchemaFromStep = ( inputStep: JsonObject, ): { uiSchema: UiSchema; schema: JsonObject } => { const uiSchema: UiSchema = {}; - const returnSchema: JsonObject = JSON.parse(JSON.stringify(inputStep)); + const returnSchema: JsonObject = parse(stringify(inputStep)); extractUiSchema(returnSchema, uiSchema); return { uiSchema, schema: returnSchema }; }; diff --git a/yarn.lock b/yarn.lock index 1c83813e10..d9ffd84fca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8420,6 +8420,7 @@ __metadata: "@types/luxon": ^3.0.0 "@types/react": ^16.13.1 || ^17.0.0 classnames: ^2.2.6 + flatted: 3.2.9 humanize-duration: ^3.25.1 immer: ^9.0.1 json-schema: ^0.4.0 @@ -27530,10 +27531,10 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.1.0": - version: 3.1.1 - resolution: "flatted@npm:3.1.1" - checksum: 508935e3366d95444131f0aaa801a4301f24ea5bcb900d12764e7335b46b910730cc1b5bcfcfb8eccb7c8db261ba0671c6a7ca30d10870ff7a7756dc7e731a7a +"flatted@npm:3.2.9, flatted@npm:^3.1.0": + version: 3.2.9 + resolution: "flatted@npm:3.2.9" + checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026 languageName: node linkType: hard From 3f60ad55995a60ca345b7bd066db3244d7d38411 Mon Sep 17 00:00:00 2001 From: bnechyporenko Date: Sat, 27 Jan 2024 23:05:12 +0100 Subject: [PATCH 096/468] wip Signed-off-by: bnechyporenko --- .changeset/chilled-ways-wave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chilled-ways-wave.md diff --git a/.changeset/chilled-ways-wave.md b/.changeset/chilled-ways-wave.md new file mode 100644 index 0000000000..68e4d1eb7d --- /dev/null +++ b/.changeset/chilled-ways-wave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +fix for: converting circular structure to JSON error From c1b29586737b82f53931360632c0168b62253558 Mon Sep 17 00:00:00 2001 From: Mahendra <42772952+mahendra1290@users.noreply.github.com> Date: Sun, 28 Jan 2024 15:12:56 +0530 Subject: [PATCH 097/468] Update migrate-to-mui5.md update key from `provider` to `Provider` Signed-off-by: Mahendra <42772952+mahendra1290@users.noreply.github.com> --- docs/tutorials/migrate-to-mui5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/migrate-to-mui5.md b/docs/tutorials/migrate-to-mui5.md index aaf8cff7f5..c65abbc680 100644 --- a/docs/tutorials/migrate-to-mui5.md +++ b/docs/tutorials/migrate-to-mui5.md @@ -19,7 +19,7 @@ By default, the `UnifiedThemeProvider` is already used. If you add a custom them themes: [ { // ... - provider: ({ children }) => ( + Provider: ({ children }) => ( - . - {children}. - Date: Sun, 28 Jan 2024 18:27:44 +0530 Subject: [PATCH 098/468] ran lighthouse scripts over docs and releases find these Signed-off-by: npiyush97 --- docs/overview/logos.md | 28 ++++++++++++++-------------- docs/permissions/overview.md | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/overview/logos.md b/docs/overview/logos.md index ac54640e33..fe993cc378 100644 --- a/docs/overview/logos.md +++ b/docs/overview/logos.md @@ -12,31 +12,31 @@ The assets below are all in `.svg` format. Other formats are available in the ## Backstage logo - - + + Backstage White logo - - + + Backstage Teal logo - - + + Backstage Black logo ## Backstage icon diff --git a/docs/permissions/overview.md b/docs/permissions/overview.md index 693f848c82..8630b85e23 100644 --- a/docs/permissions/overview.md +++ b/docs/permissions/overview.md @@ -22,7 +22,7 @@ The permission framework was designed with a few key properties in mind: - **Integrators** can author or configure policies that define which users can take certain actions upon which resources. -![](../assets/permissions/permission-framework-overview.drawio.svg) +![backstage framework overview](../assets/permissions/permission-framework-overview.drawio.svg) 1. The user triggers a request to perform some action. The request specifies the authorization details using the permission specified by the plugin (in this case, a resource read action). From 33d2cd8f7221fb164e2704ba950f372b74de718b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 16:58:02 +0000 Subject: [PATCH 099/468] fix(deps): update dependency @swc/core to v1.3.107 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 86 ++++++++++++++++++++++----------------------- storybook/yarn.lock | 86 ++++++++++++++++++++++----------------------- yarn.lock | 86 ++++++++++++++++++++++----------------------- 3 files changed, 129 insertions(+), 129 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 5c92ba5e69..50465ec651 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2701,90 +2701,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-arm64@npm:1.3.105" +"@swc/core-darwin-arm64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-arm64@npm:1.3.107" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-x64@npm:1.3.105" +"@swc/core-darwin-x64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-x64@npm:1.3.107" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.105" +"@swc/core-linux-arm-gnueabihf@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.107" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.105" +"@swc/core-linux-arm64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.105" +"@swc/core-linux-arm64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.105" +"@swc/core-linux-x64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-musl@npm:1.3.105" +"@swc/core-linux-x64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-musl@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.105" +"@swc/core-win32-arm64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.107" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.105" +"@swc/core-win32-ia32-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.107" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.105" +"@swc/core-win32-x64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.107" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.3.105 - resolution: "@swc/core@npm:1.3.105" + version: 1.3.107 + resolution: "@swc/core@npm:1.3.107" dependencies: - "@swc/core-darwin-arm64": 1.3.105 - "@swc/core-darwin-x64": 1.3.105 - "@swc/core-linux-arm-gnueabihf": 1.3.105 - "@swc/core-linux-arm64-gnu": 1.3.105 - "@swc/core-linux-arm64-musl": 1.3.105 - "@swc/core-linux-x64-gnu": 1.3.105 - "@swc/core-linux-x64-musl": 1.3.105 - "@swc/core-win32-arm64-msvc": 1.3.105 - "@swc/core-win32-ia32-msvc": 1.3.105 - "@swc/core-win32-x64-msvc": 1.3.105 + "@swc/core-darwin-arm64": 1.3.107 + "@swc/core-darwin-x64": 1.3.107 + "@swc/core-linux-arm-gnueabihf": 1.3.107 + "@swc/core-linux-arm64-gnu": 1.3.107 + "@swc/core-linux-arm64-musl": 1.3.107 + "@swc/core-linux-x64-gnu": 1.3.107 + "@swc/core-linux-x64-musl": 1.3.107 + "@swc/core-win32-arm64-msvc": 1.3.107 + "@swc/core-win32-ia32-msvc": 1.3.107 + "@swc/core-win32-x64-msvc": 1.3.107 "@swc/counter": ^0.1.1 "@swc/types": ^0.1.5 peerDependencies: @@ -2813,7 +2813,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 5baa880bc92748ef4845d9c65eba5d6dd01adaa673854e20a5116f5e267c12180db50e563cf3c34a415772b9742d021176a9d9a91065c190ef6f54fefe85728c + checksum: 0dccff50461fb8c0f4af053b70e555c91386cb07aa7657a7328d58e397d15640723587549416d8fa7dcc073ad11b39318146bd50ec4a82345ce2ce39c7ba4c00 languageName: node linkType: hard diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 94acb9696e..54f50cbbb5 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -2842,90 +2842,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-arm64@npm:1.3.105" +"@swc/core-darwin-arm64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-arm64@npm:1.3.107" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-x64@npm:1.3.105" +"@swc/core-darwin-x64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-x64@npm:1.3.107" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.105" +"@swc/core-linux-arm-gnueabihf@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.107" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.105" +"@swc/core-linux-arm64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.105" +"@swc/core-linux-arm64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.105" +"@swc/core-linux-x64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-musl@npm:1.3.105" +"@swc/core-linux-x64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-musl@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.105" +"@swc/core-win32-arm64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.107" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.105" +"@swc/core-win32-ia32-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.107" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.105" +"@swc/core-win32-x64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.107" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.3.105 - resolution: "@swc/core@npm:1.3.105" + version: 1.3.107 + resolution: "@swc/core@npm:1.3.107" dependencies: - "@swc/core-darwin-arm64": 1.3.105 - "@swc/core-darwin-x64": 1.3.105 - "@swc/core-linux-arm-gnueabihf": 1.3.105 - "@swc/core-linux-arm64-gnu": 1.3.105 - "@swc/core-linux-arm64-musl": 1.3.105 - "@swc/core-linux-x64-gnu": 1.3.105 - "@swc/core-linux-x64-musl": 1.3.105 - "@swc/core-win32-arm64-msvc": 1.3.105 - "@swc/core-win32-ia32-msvc": 1.3.105 - "@swc/core-win32-x64-msvc": 1.3.105 + "@swc/core-darwin-arm64": 1.3.107 + "@swc/core-darwin-x64": 1.3.107 + "@swc/core-linux-arm-gnueabihf": 1.3.107 + "@swc/core-linux-arm64-gnu": 1.3.107 + "@swc/core-linux-arm64-musl": 1.3.107 + "@swc/core-linux-x64-gnu": 1.3.107 + "@swc/core-linux-x64-musl": 1.3.107 + "@swc/core-win32-arm64-msvc": 1.3.107 + "@swc/core-win32-ia32-msvc": 1.3.107 + "@swc/core-win32-x64-msvc": 1.3.107 "@swc/counter": ^0.1.1 "@swc/types": ^0.1.5 peerDependencies: @@ -2954,7 +2954,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 5baa880bc92748ef4845d9c65eba5d6dd01adaa673854e20a5116f5e267c12180db50e563cf3c34a415772b9742d021176a9d9a91065c190ef6f54fefe85728c + checksum: 0dccff50461fb8c0f4af053b70e555c91386cb07aa7657a7328d58e397d15640723587549416d8fa7dcc073ad11b39318146bd50ec4a82345ce2ce39c7ba4c00 languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index 6eff422ebd..bfd2c3fe95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17018,90 +17018,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-arm64@npm:1.3.105" +"@swc/core-darwin-arm64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-arm64@npm:1.3.107" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-darwin-x64@npm:1.3.105" +"@swc/core-darwin-x64@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-darwin-x64@npm:1.3.107" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.105" +"@swc/core-linux-arm-gnueabihf@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.107" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.105" +"@swc/core-linux-arm64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.105" +"@swc/core-linux-arm64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.107" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.105" +"@swc/core-linux-x64-gnu@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-linux-x64-musl@npm:1.3.105" +"@swc/core-linux-x64-musl@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-linux-x64-musl@npm:1.3.107" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.105" +"@swc/core-win32-arm64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.107" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.105" +"@swc/core-win32-ia32-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.107" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.105": - version: 1.3.105 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.105" +"@swc/core-win32-x64-msvc@npm:1.3.107": + version: 1.3.107 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.107" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.3.105 - resolution: "@swc/core@npm:1.3.105" + version: 1.3.107 + resolution: "@swc/core@npm:1.3.107" dependencies: - "@swc/core-darwin-arm64": 1.3.105 - "@swc/core-darwin-x64": 1.3.105 - "@swc/core-linux-arm-gnueabihf": 1.3.105 - "@swc/core-linux-arm64-gnu": 1.3.105 - "@swc/core-linux-arm64-musl": 1.3.105 - "@swc/core-linux-x64-gnu": 1.3.105 - "@swc/core-linux-x64-musl": 1.3.105 - "@swc/core-win32-arm64-msvc": 1.3.105 - "@swc/core-win32-ia32-msvc": 1.3.105 - "@swc/core-win32-x64-msvc": 1.3.105 + "@swc/core-darwin-arm64": 1.3.107 + "@swc/core-darwin-x64": 1.3.107 + "@swc/core-linux-arm-gnueabihf": 1.3.107 + "@swc/core-linux-arm64-gnu": 1.3.107 + "@swc/core-linux-arm64-musl": 1.3.107 + "@swc/core-linux-x64-gnu": 1.3.107 + "@swc/core-linux-x64-musl": 1.3.107 + "@swc/core-win32-arm64-msvc": 1.3.107 + "@swc/core-win32-ia32-msvc": 1.3.107 + "@swc/core-win32-x64-msvc": 1.3.107 "@swc/counter": ^0.1.1 "@swc/types": ^0.1.5 peerDependencies: @@ -17130,7 +17130,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 5baa880bc92748ef4845d9c65eba5d6dd01adaa673854e20a5116f5e267c12180db50e563cf3c34a415772b9742d021176a9d9a91065c190ef6f54fefe85728c + checksum: 0dccff50461fb8c0f4af053b70e555c91386cb07aa7657a7328d58e397d15640723587549416d8fa7dcc073ad11b39318146bd50ec4a82345ce2ce39c7ba4c00 languageName: node linkType: hard From 2e3f378eb5722afbeea614f79f0f4ad3eb1ab6e4 Mon Sep 17 00:00:00 2001 From: David Weber Date: Sun, 28 Jan 2024 22:03:15 +0100 Subject: [PATCH 100/468] feat: change links Signed-off-by: David Weber --- microsite/data/plugins/api-spectral-linter.yaml | 4 ++-- microsite/data/plugins/api-wsdl.yaml | 4 ++-- microsite/data/plugins/end-of-life.yaml | 4 ++-- microsite/data/plugins/tips.yaml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/microsite/data/plugins/api-spectral-linter.yaml b/microsite/data/plugins/api-spectral-linter.yaml index 292279e324..5f1682c71b 100644 --- a/microsite/data/plugins/api-spectral-linter.yaml +++ b/microsite/data/plugins/api-spectral-linter.yaml @@ -4,7 +4,7 @@ author: dweber019 authorUrl: https://github.com/dweber019 category: Linting description: API Spectral Linter is a quality assurance tool that checks the compliance of API's specifications Spectral rule sets. -documentation: https://github.com/dweber019/backstage-plugin-api-docs-spectral-linter -iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-api-docs-spectral-linter/main/plugins/api-docs-spectral-linter/docs/pluginIcon.png +documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/api-docs-spectral-linter +iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/api-docs-spectral-linter/docs/pluginIcon.png npmPackageName: '@dweber019/backstage-plugin-api-docs-spectral-linter' addedDate: '2023-03-27' diff --git a/microsite/data/plugins/api-wsdl.yaml b/microsite/data/plugins/api-wsdl.yaml index 6523531f1d..219227c509 100644 --- a/microsite/data/plugins/api-wsdl.yaml +++ b/microsite/data/plugins/api-wsdl.yaml @@ -4,7 +4,7 @@ author: dweber019 authorUrl: https://github.com/dweber019 category: Discovery description: An API docs extension to render WSDL / SOAP web services human readable. -documentation: https://github.com/dweber019/backstage-plugin-api-docs-module-wsdl -iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-api-docs-module-wsdl/main/docs/logo.png +documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/api-docs-module-wsdl +iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/api-docs-module-wsdl/docs/logo.png npmPackageName: '@dweber019/backstage-plugin-api-docs-module-wsdl' addedDate: '2023-12-22' diff --git a/microsite/data/plugins/end-of-life.yaml b/microsite/data/plugins/end-of-life.yaml index d54a6bc5f9..ddf4b26341 100644 --- a/microsite/data/plugins/end-of-life.yaml +++ b/microsite/data/plugins/end-of-life.yaml @@ -4,7 +4,7 @@ author: dweber019 authorUrl: https://github.com/dweber019 category: Quality description: Display end of life data for entities from endoflife.data -documentation: https://github.com/dweber019/backstage-plugin-endoflife -iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-endoflife/main/plugins/endoflife/docs/pluginIcon.png +documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/endoflife +iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/endoflife/docs/pluginIcon.png npmPackageName: '@dweber019/backstage-plugin-endoflife' addedDate: '2024-01-18' diff --git a/microsite/data/plugins/tips.yaml b/microsite/data/plugins/tips.yaml index e58ad0fb46..03327f3e41 100644 --- a/microsite/data/plugins/tips.yaml +++ b/microsite/data/plugins/tips.yaml @@ -4,7 +4,7 @@ author: dweber019 authorUrl: https://github.com/dweber019 category: Discovery description: The tips plugin will show the end user some helpful information for entities. -documentation: https://github.com/dweber019/backstage-plugin-tips -iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-tips/main/plugins/tips/docs/pluginIcon.png +documentation: https://github.com/dweber019/backstage-plugins/tree/main/plugins/tips +iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugins/main/plugins/tips/docs/pluginIcon.png npmPackageName: '@dweber019/backstage-plugin-tips' addedDate: '2023-10-08' From 01c4c810ddf4329ec0a6705c52a9b12be198ccd4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:59:08 +0000 Subject: [PATCH 101/468] chore(deps): update chromaui/action digest to 05a82ad Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_storybook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 1c3a5fd619..49a0c76df3 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -51,7 +51,7 @@ jobs: - run: yarn build-storybook - - uses: chromaui/action@5f2cdb26c04c0364f5c8ca3fc41627a83d6ffc8a # v10 + - uses: chromaui/action@05a82adb1e6919df177f54777e81a2ef3e312323 # v10 with: token: ${{ secrets.GITHUB_TOKEN }} # projectToken intentionally shared to allow collaborators to run Chromatic on forks From 46b63dea672ca789e16a7577fc33c542194cc25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 23 Jan 2024 09:26:38 +0100 Subject: [PATCH 102/468] add defaultTarget to ExternalRouteRef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/twelve-pens-rescue.md | 7 ++ packages/app-next/app-config.yaml | 1 - .../app-next/src/examples/pagesPlugin.tsx | 4 +- .../src/convertLegacyRouteRef.ts | 3 + .../src/routing/resolveRouteBindings.test.ts | 44 ++++++++++++ .../src/routing/resolveRouteBindings.ts | 72 +++++++++++-------- packages/frontend-plugin-api/api-report.md | 1 + .../src/routing/ExternalRouteRef.ts | 15 ++++ 8 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 .changeset/twelve-pens-rescue.md diff --git a/.changeset/twelve-pens-rescue.md b/.changeset/twelve-pens-rescue.md new file mode 100644 index 0000000000..9d40fb2851 --- /dev/null +++ b/.changeset/twelve-pens-rescue.md @@ -0,0 +1,7 @@ +--- +'@backstage/frontend-plugin-api': patch +'@backstage/frontend-app-api': patch +'@backstage/core-compat-api': patch +--- + +Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter. diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 9cc3507fa4..a79bb866db 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -3,7 +3,6 @@ app: packages: 'all' # ✨ routes: bindings: - pages.pageX: pages.pageX catalog.viewTechDoc: techdocs.docRoot catalog.createComponent: catalog-import.importPage diff --git a/packages/app-next/src/examples/pagesPlugin.tsx b/packages/app-next/src/examples/pagesPlugin.tsx index c5069f99c3..826040a6cb 100644 --- a/packages/app-next/src/examples/pagesPlugin.tsx +++ b/packages/app-next/src/examples/pagesPlugin.tsx @@ -27,7 +27,9 @@ import { Route, Routes } from 'react-router-dom'; const indexRouteRef = createRouteRef(); const page1RouteRef = createRouteRef(); -export const externalPageXRouteRef = createExternalRouteRef(); +export const externalPageXRouteRef = createExternalRouteRef({ + defaultTarget: 'pages.pageX', +}); export const pageXRouteRef = createRouteRef(); // const page2RouteRef = createSubRouteRef({ // id: 'page2', diff --git a/packages/core-compat-api/src/convertLegacyRouteRef.ts b/packages/core-compat-api/src/convertLegacyRouteRef.ts index 36311dc081..e6a52932ce 100644 --- a/packages/core-compat-api/src/convertLegacyRouteRef.ts +++ b/packages/core-compat-api/src/convertLegacyRouteRef.ts @@ -199,6 +199,9 @@ export function convertLegacyRouteRef( getDescription() { return legacyRefStr; }, + getDefaultTarget() { + return newRef.getDefaultTarget(); + }, setId(id: string) { newRef.setId(id); }, diff --git a/packages/frontend-app-api/src/routing/resolveRouteBindings.test.ts b/packages/frontend-app-api/src/routing/resolveRouteBindings.test.ts index d80a02e34d..9b150eb127 100644 --- a/packages/frontend-app-api/src/routing/resolveRouteBindings.test.ts +++ b/packages/frontend-app-api/src/routing/resolveRouteBindings.test.ts @@ -117,4 +117,48 @@ describe('resolveRouteBindings', () => { "Invalid config at app.routes.bindings['mySource'], 'myTarget' is not a valid route", ); }); + + it('can have default targets, but at the lowest priority', () => { + const source = createExternalRouteRef({ defaultTarget: 'target1' }); + const target1 = createRouteRef(); + const target2 = createRouteRef(); + const routesById = { + routes: new Map([ + ['target1', target1], + ['target2', target2], + ]), + externalRoutes: new Map([['source', source]]), + }; + + // defaultTarget wins only if no bind or config matches + let result = resolveRouteBindings( + () => {}, + new ConfigReader({}), + routesById, + ); + + expect(result.get(source)).toBe(target1); + + // config wins over defaultTarget + result = resolveRouteBindings( + () => {}, + new ConfigReader({ + app: { routes: { bindings: { source: 'target2' } } }, + }), + routesById, + ); + + expect(result.get(source)).toBe(target2); + + // bind wins over defaultTarget + result = resolveRouteBindings( + ({ bind }) => { + bind({ a: source }, { a: target2 }); + }, + new ConfigReader({}), + routesById, + ); + + expect(result.get(source)).toBe(target2); + }); }); diff --git a/packages/frontend-app-api/src/routing/resolveRouteBindings.ts b/packages/frontend-app-api/src/routing/resolveRouteBindings.ts index 18ea9f9bd8..49e3b1a1be 100644 --- a/packages/frontend-app-api/src/routing/resolveRouteBindings.ts +++ b/packages/frontend-app-api/src/routing/resolveRouteBindings.ts @@ -22,6 +22,8 @@ import { import { RouteRefsById } from './collectRouteIds'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef'; /** * Extracts a union of the keys in a map whose value extends the given type @@ -82,6 +84,7 @@ export function resolveRouteBindings( ): Map { const result = new Map(); + // Perform callback bindings first with highest priority if (bindRoutes) { const bind: CreateAppRouteBinder = ( externalRoutes, @@ -105,37 +108,50 @@ export function resolveRouteBindings( bindRoutes({ bind }); } - const bindingsConfig = config.getOptionalConfig('app.routes.bindings'); - if (!bindingsConfig) { - return result; + // Then perform config based bindings with lower priority + const bindings = config + .getOptionalConfig('app.routes.bindings') + ?.get(); + if (bindings) { + for (const [externalRefId, targetRefId] of Object.entries(bindings)) { + if (typeof targetRefId !== 'string' || targetRefId === '') { + throw new Error( + `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string`, + ); + } + + const externalRef = routesById.externalRoutes.get(externalRefId); + if (!externalRef) { + throw new Error( + `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`, + ); + } + if (result.has(externalRef)) { + continue; + } + const targetRef = routesById.routes.get(targetRefId); + if (!targetRef) { + throw new Error( + `Invalid config at app.routes.bindings['${externalRefId}'], '${targetRefId}' is not a valid route`, + ); + } + + result.set(externalRef, targetRef); + } } - const bindings = bindingsConfig.get(); - for (const [externalRefId, targetRefId] of Object.entries(bindings)) { - if (typeof targetRefId !== 'string' || targetRefId === '') { - throw new Error( - `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string`, - ); + // Finally fall back to attempting to map defaults, at lowest priority + for (const externalRef of routesById.externalRoutes.values()) { + if (!result.has(externalRef)) { + const defaultRefId = + toInternalExternalRouteRef(externalRef).getDefaultTarget(); + if (defaultRefId) { + const defaultRef = routesById.routes.get(defaultRefId); + if (defaultRef) { + result.set(externalRef, defaultRef); + } + } } - - const externalRef = routesById.externalRoutes.get(externalRefId); - if (!externalRef) { - throw new Error( - `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`, - ); - } - // Route bindings defined in config have lower priority than those defined in code - if (result.has(externalRef)) { - continue; - } - const targetRef = routesById.routes.get(targetRefId); - if (!targetRef) { - throw new Error( - `Invalid config at app.routes.bindings['${externalRefId}'], '${targetRefId}' is not a valid route`, - ); - } - - result.set(externalRef, targetRef); } return result; diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 4116469c2a..28c8ea321c 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -573,6 +573,7 @@ export function createExternalRouteRef< ? (keyof TParams)[] : TParamKeys[]; optional?: TOptional; + defaultTarget?: string; }): ExternalRouteRef< keyof TParams extends never ? undefined diff --git a/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts b/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts index c9aabdef27..7a754ffa7b 100644 --- a/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts +++ b/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts @@ -44,6 +44,7 @@ export interface InternalExternalRouteRef< readonly version: 'v1'; getParams(): string[]; getDescription(): string; + getDefaultTarget(): string | undefined; setId(id: string): void; } @@ -80,10 +81,15 @@ class ExternalRouteRefImpl constructor( readonly optional: boolean, readonly params: string[] = [], + readonly defaultTarget: string | undefined, creationSite: string, ) { super(params, creationSite); } + + getDefaultTarget() { + return this.defaultTarget; + } } /** @@ -115,6 +121,14 @@ export function createExternalRouteRef< * if they aren't, `useExternalRouteRef` will return `undefined`. */ optional?: TOptional; + + /** + * The route (typically in another plugin) that this should map to by default. + * + * The string is expected to be on the standard `.` form, + * for example `techdocs.docRoot`. + */ + defaultTarget?: string; }): ExternalRouteRef< keyof TParams extends never ? undefined @@ -126,6 +140,7 @@ export function createExternalRouteRef< return new ExternalRouteRefImpl( Boolean(options?.optional), options?.params as string[] | undefined, + options?.defaultTarget, describeParentCallSite(), ) as ExternalRouteRef; } From 93bb6b425fce93621182dcc289b24a20bc583df5 Mon Sep 17 00:00:00 2001 From: Alex <52247724+Elya29@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:30:57 +0100 Subject: [PATCH 103/468] Update writing.md Edit message to be more explicit about the secret RSA private keys Signed-off-by: Alex <52247724+Elya29@users.noreply.github.com> --- docs/conf/writing.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/conf/writing.md b/docs/conf/writing.md index b2c098e12b..b5515f8618 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -218,10 +218,7 @@ webhookUrl: https://smee.io/foo clientId: someGithubAppClientId clientSecret: someGithubAppClientSecret webhookSecret: someWebhookSecret -privateKey: | - -----BEGIN RSA PRIVATE KEY----- - SomeRsaPrivateKeyForExampleOnly - -----END RSA PRIVATE KEY----- +privateKey: someSecretRsaPrivateKey ``` -**Warning: RSA private keys should not be hard coded**. Keep them in a secure storage solution like Vault, to ensure they are neither exposed nor misused. +**Warning: RSA private keys should not be hard coded**. We recommend that this entire file should be a secret and stored as such in a secure storage solution like Vault, to ensure they are neither exposed nor misused. From 7360c3dc0a65821138c5b2ec2265ab82fd7af42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 29 Jan 2024 10:35:28 +0100 Subject: [PATCH 104/468] review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/core-compat-api/src/convertLegacyRouteRef.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core-compat-api/src/convertLegacyRouteRef.ts b/packages/core-compat-api/src/convertLegacyRouteRef.ts index e6a52932ce..a5375e0692 100644 --- a/packages/core-compat-api/src/convertLegacyRouteRef.ts +++ b/packages/core-compat-api/src/convertLegacyRouteRef.ts @@ -200,7 +200,8 @@ export function convertLegacyRouteRef( return legacyRefStr; }, getDefaultTarget() { - return newRef.getDefaultTarget(); + // TODO(freben): These are not yet supported in the old system; just returning undefined for now + return undefined; }, setId(id: string) { newRef.setId(id); From e83e5baa769d8708979b0c801e6a2bfe6a3fd469 Mon Sep 17 00:00:00 2001 From: Tommy Le Date: Mon, 29 Jan 2024 11:02:22 +0100 Subject: [PATCH 105/468] docs: update readme Signed-off-by: Tommy Le --- docs/tutorials/setup-opentelemetry.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/setup-opentelemetry.md b/docs/tutorials/setup-opentelemetry.md index b85cbd4398..aa96e2ad40 100644 --- a/docs/tutorials/setup-opentelemetry.md +++ b/docs/tutorials/setup-opentelemetry.md @@ -18,7 +18,8 @@ The `auto-instrumentations-node` will automatically create spans for code called ```bash yarn --cwd packages/backend add @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ - @opentelemetry/sdk-metrics + @opentelemetry/sdk-metrics \ + @opentelemetry/sdk-trace-node ``` ## Configure From 75dd39d084edba8b23fc438b4899f7068fded8ff Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 25 Jan 2024 19:08:56 +0100 Subject: [PATCH 106/468] chore: support passing through token in `fetchContents` and `fetchFile` Signed-off-by: blam Signed-off-by: blam --- .../scaffolder-node/src/actions/fetch.test.ts | 28 +++++++++++++++++++ plugins/scaffolder-node/src/actions/fetch.ts | 24 +++++++++++++--- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/plugins/scaffolder-node/src/actions/fetch.test.ts b/plugins/scaffolder-node/src/actions/fetch.test.ts index 55b949fca7..42f75d02b4 100644 --- a/plugins/scaffolder-node/src/actions/fetch.test.ts +++ b/plugins/scaffolder-node/src/actions/fetch.test.ts @@ -124,6 +124,20 @@ describe('fetchContents helper', () => { expect(fs.ensureDir).toHaveBeenCalledWith('foo'); expect(dirFunction).toHaveBeenCalledWith({ targetDir: 'foo' }); }); + + it('should pass through the token provided through to the URL reader', async () => { + await fetchContents({ + ...options, + outputPath: 'mydir/foo', + fetchUrl: 'https://github.com/backstage/foo', + token: 'mockToken', + }); + + expect(readTree).toHaveBeenCalledWith( + 'https://github.com/backstage/foo', + expect.objectContaining({ token: 'mockToken' }), + ); + }); }); describe('fetch file', () => { @@ -211,5 +225,19 @@ describe('fetchContents helper', () => { expect(fs.ensureDir).toHaveBeenCalledWith('mydir'); expect(fs.outputFile).toHaveBeenCalledWith('mydir/foo', 'test'); }); + + it('should pass through the token provided through to the URL reader', async () => { + await fetchFile({ + ...options, + outputPath: 'mydir/foo', + fetchUrl: 'https://github.com/backstage/foo', + token: 'mockToken', + }); + + expect(readUrl).toHaveBeenCalledWith( + 'https://github.com/backstage/foo', + expect.objectContaining({ token: 'mockToken' }), + ); + }); }); }); diff --git a/plugins/scaffolder-node/src/actions/fetch.ts b/plugins/scaffolder-node/src/actions/fetch.ts index 66bbc0a842..2ddc2b1ae6 100644 --- a/plugins/scaffolder-node/src/actions/fetch.ts +++ b/plugins/scaffolder-node/src/actions/fetch.ts @@ -32,8 +32,16 @@ export async function fetchContents(options: { baseUrl?: string; fetchUrl?: string; outputPath: string; + token?: string; }) { - const { reader, integrations, baseUrl, fetchUrl = '.', outputPath } = options; + const { + reader, + integrations, + baseUrl, + fetchUrl = '.', + outputPath, + token, + } = options; const fetchUrlIsAbsolute = isFetchUrlAbsolute(fetchUrl); @@ -45,7 +53,7 @@ export async function fetchContents(options: { } else { const readUrl = getReadUrl(fetchUrl, baseUrl, integrations); - const res = await reader.readTree(readUrl); + const res = await reader.readTree(readUrl, { token }); await fs.ensureDir(outputPath); await res.dir({ targetDir: outputPath }); } @@ -63,8 +71,16 @@ export async function fetchFile(options: { baseUrl?: string; fetchUrl?: string; outputPath: string; + token?: string; }) { - const { reader, integrations, baseUrl, fetchUrl = '.', outputPath } = options; + const { + reader, + integrations, + baseUrl, + fetchUrl = '.', + outputPath, + token, + } = options; const fetchUrlIsAbsolute = isFetchUrlAbsolute(fetchUrl); @@ -76,7 +92,7 @@ export async function fetchFile(options: { } else { const readUrl = getReadUrl(fetchUrl, baseUrl, integrations); - const res = await reader.readUrl(readUrl); + const res = await reader.readUrl(readUrl, { token }); await fs.ensureDir(path.dirname(outputPath)); const buffer = await res.buffer(); await fs.outputFile(outputPath, buffer.toString()); From ed0f7336d86e52879d72355fff45b14ee501ca8a Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 25 Jan 2024 19:21:58 +0100 Subject: [PATCH 107/468] feat: pass through the the token in scaffolder Signed-off-by: blam --- .../actions/builtin/fetch/plain.test.ts | 19 +++++++++++++++++++ .../scaffolder/actions/builtin/fetch/plain.ts | 13 ++++++++++++- .../actions/builtin/fetch/plainFile.test.ts | 15 +++++++++++++++ .../actions/builtin/fetch/plainFile.ts | 13 ++++++++++++- .../actions/builtin/fetch/template.test.ts | 12 ++++++++++++ .../actions/builtin/fetch/template.ts | 8 ++++++++ 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts index 249941096d..ad179d8269 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts @@ -85,4 +85,23 @@ describe('fetch:plain', () => { }), ); }); + + it('should fetch plain with token', async () => { + await action.handler({ + ...mockContext, + input: { + url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets', + targetPath: 'lol', + token: 'mockToken', + }, + }); + + expect(fetchContents).toHaveBeenCalledWith( + expect.objectContaining({ + outputPath: resolvePath(mockContext.workspacePath, 'lol'), + fetchUrl: + 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets', + }), + ); + }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts index 57f3d1121b..8bca630af9 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts @@ -36,7 +36,11 @@ export function createFetchPlainAction(options: { }) { const { reader, integrations } = options; - return createTemplateAction<{ url: string; targetPath?: string }>({ + return createTemplateAction<{ + url: string; + targetPath?: string; + token?: string; + }>({ id: ACTION_ID, examples, description: @@ -58,6 +62,12 @@ export function createFetchPlainAction(options: { 'Target path within the working directory to download the contents to.', type: 'string', }, + token: { + title: 'Token', + description: + 'An optional token to use for authentication when reading the resources.', + type: 'string', + }, }, }, }, @@ -75,6 +85,7 @@ export function createFetchPlainAction(options: { baseUrl: ctx.templateInfo?.baseUrl, fetchUrl: ctx.input.url, outputPath, + token: ctx.input.token, }); }, }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts index 084b08d640..7ea74a809b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts @@ -69,6 +69,21 @@ describe('fetch:plain:file', () => { ); }); + it('passed through the token to fetchFile', async () => { + await action.handler({ + ...mockContext, + input: { + url: 'https://github.com/backstage/community/tree/main/backstage-community-sessions/assets/Backstage%20Community%20Sessions.png', + token: 'mockToken', + targetPath: 'lol', + }, + }); + + expect(fetchFile).toHaveBeenCalledWith( + expect.objectContaining({ token: 'mockToken' }), + ); + }); + it('should fetch plain', async () => { await action.handler({ ...mockContext, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts index 0a791a31a4..da0255ff5a 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts @@ -33,7 +33,11 @@ export function createFetchPlainFileAction(options: { }) { const { reader, integrations } = options; - return createTemplateAction<{ url: string; targetPath: string }>({ + return createTemplateAction<{ + url: string; + targetPath: string; + token?: string; + }>({ id: 'fetch:plain:file', description: 'Downloads single file and places it in the workspace.', examples, @@ -54,6 +58,12 @@ export function createFetchPlainFileAction(options: { 'Target path within the working directory to download the file as.', type: 'string', }, + token: { + title: 'Token', + description: + 'An optional token to use for authentication when reading the resources.', + type: 'string', + }, }, }, }, @@ -73,6 +83,7 @@ export function createFetchPlainFileAction(options: { baseUrl: ctx.templateInfo?.baseUrl, fetchUrl: ctx.input.url, outputPath, + token: ctx.input.token, }); }, }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts index 119cd78eb7..25b65462e6 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -371,6 +371,18 @@ describe('fetch:template', () => { fs.readlink(`${workspacePath}/target/brokenSymlink`), ).resolves.toEqual(`.${pathSep}not-a-real-file.txt`); }); + + it('passed through the token to the fetchContents call', async () => { + await action.handler( + mockContext({ + token: 'mockToken', + }), + ); + + expect(mockFetchContents).toHaveBeenCalledWith( + expect.objectContaining({ token: 'mockToken' }), + ); + }); }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts index 2a2c138708..63a595ef80 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -71,6 +71,7 @@ export function createFetchTemplateAction(options: { replace?: boolean; trimBlocks?: boolean; lstripBlocks?: boolean; + token?: string; }>({ id: 'fetch:template', description: @@ -134,6 +135,12 @@ export function createFetchTemplateAction(options: { 'If set, replace files in targetPath instead of skipping existing ones.', type: 'boolean', }, + token: { + title: 'Token', + description: + 'An optional token to use for authentication when reading the resources.', + type: 'string', + }, }, }, }, @@ -197,6 +204,7 @@ export function createFetchTemplateAction(options: { baseUrl: ctx.templateInfo?.baseUrl, fetchUrl: ctx.input.url, outputPath: templateDir, + token: ctx.input.token, }); ctx.logger.info('Listing files and directories in template'); From 78c100bc60c21f4646795e53e0c817e2bb6dcfa3 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 25 Jan 2024 19:53:13 +0100 Subject: [PATCH 108/468] chore: add changeset Signed-off-by: blam Signed-off-by: blam --- .changeset/ten-trainers-cough.md | 6 ++++++ .../src/scaffolder/actions/builtin/fetch/plain.test.ts | 1 + 2 files changed, 7 insertions(+) create mode 100644 .changeset/ten-trainers-cough.md diff --git a/.changeset/ten-trainers-cough.md b/.changeset/ten-trainers-cough.md new file mode 100644 index 0000000000..53a82def90 --- /dev/null +++ b/.changeset/ten-trainers-cough.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +'@backstage/plugin-scaffolder-node': minor +--- + +Support providing an overriding token for `fetch:template`, `fetch:plain` and `fetch:file` when interacting with upstream integrations diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts index ad179d8269..917624acf4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts @@ -77,6 +77,7 @@ describe('fetch:plain', () => { targetPath: 'lol', }, }); + expect(fetchContents).toHaveBeenCalledWith( expect.objectContaining({ outputPath: resolvePath(mockContext.workspacePath, 'lol'), From e9987a467dbec75c06e077541356a7d6422b5875 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 25 Jan 2024 20:14:40 +0100 Subject: [PATCH 109/468] chore: support overriding the token Signed-off-by: blam --- .../src/reading/GithubUrlReader.test.ts | 65 +++++++++++++++++++ .../src/reading/GithubUrlReader.ts | 28 ++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index 5e6d30f519..ec775183b3 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -271,6 +271,33 @@ describe('GithubUrlReader', () => { expect(response.etag).toBe('foo'); expect(response.lastModifiedAt).toEqual(new Date('2021-01-01T00:00:00Z')); }); + + it('should override the token if its provided', async () => { + expect.assertions(1); + + (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + headers: { + Authorization: 'bearer blah', + }, + }); + + worker.use( + rest.get( + 'https://ghe.github.com/api/v3/repos/backstage/mock/tree/contents/', + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBe( + 'Bearer overridentoken', + ); + return res(ctx.status(200)); + }, + ), + ); + + await gheProcessor.readUrl( + 'https://github.com/backstage/mock/tree/blob/main', + { token: 'overridentoken' }, + ); + }); }); /* @@ -463,6 +490,44 @@ describe('GithubUrlReader', () => { ); }); + it('should override the token when provided', async () => { + expect.assertions(1); + + const mockHeaders = { + Authorization: 'bearer blah', + }; + + (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + headers: mockHeaders, + }); + + worker.use( + rest.get( + 'https://ghe.github.com/api/v3/repos/backstage/mock/tarball/etag123abc', + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBe( + 'Bearer overridentoken', + ); + + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/x-gzip'), + ctx.set( + 'content-disposition', + 'attachment; filename=backstage-mock-etag123.tar.gz', + ), + ctx.body(repoBuffer), + ); + }, + ), + ); + + await gheProcessor.readTree( + 'https://ghe.github.com/backstage/mock/tree/main', + { token: 'overridentoken' }, + ); + }); + it('includes the subdomain in the github url', async () => { const response = await gheProcessor.readTree( 'https://ghe.github.com/backstage/mock/tree/main/docs', diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index 462821a20f..b476435771 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -92,13 +92,31 @@ export class GithubUrlReader implements UrlReader { return response.buffer(); } + private getCredentials = async ( + url: string, + options?: { token?: string }, + ): Promise => { + if (options?.token) { + return { + headers: { + Authorization: `Bearer ${options.token}`, + }, + type: 'token', + token: options.token, + }; + } + + return await this.deps.credentialsProvider.getCredentials({ + url, + }); + }; + async readUrl( url: string, options?: ReadUrlOptions, ): Promise { - const credentials = await this.deps.credentialsProvider.getCredentials({ - url, - }); + const credentials = await this.getCredentials(url, options); + const ghUrl = getGithubFileFetchUrl( url, this.integration.config, @@ -141,9 +159,7 @@ export class GithubUrlReader implements UrlReader { } const { filepath } = parseGitUrl(url); - const { headers } = await this.deps.credentialsProvider.getCredentials({ - url, - }); + const { headers } = await this.getCredentials(url, options); return this.doReadTree( repoDetails.repo.archive_url, From 1e1dd798fa2bd2605c07729db35922ddcd22e2f2 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 25 Jan 2024 20:15:06 +0100 Subject: [PATCH 110/468] chore: updating reading tyupes Signed-off-by: blam --- .../services/definitions/UrlReaderService.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts b/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts index c189b4a339..e89deab0eb 100644 --- a/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts +++ b/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts @@ -92,6 +92,18 @@ export type ReadUrlOptions = { * Not all reader implementations may take this field into account. */ signal?: AbortSignal; + + /** + * An optional token to use for authentication when reading the resources. + * + * @remarks + * + * By default all URL Readers will use the integrations config which is supplied + * when creating the Readers. Sometimes it might be desireable to use the already + * created URLReaders but with a different token, maybe that's supplied by the user + * at runtime. + */ + token?: string; }; /** @@ -179,6 +191,18 @@ export type ReadTreeOptions = { * Not all reader implementations may take this field into account. */ signal?: AbortSignal; + + /** + * An optional token to use for authentication when reading the resources. + * + * @remarks + * + * By default all URL Readers will use the integrations config which is supplied + * when creating the Readers. Sometimes it might be desireable to use the already + * created URLReaders but with a different token, maybe that's supplied by the user + * at runtime. + */ + token?: string; }; /** From 98753d9bf0f3636e3e17538396ad2edc74c27f40 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 25 Jan 2024 20:20:58 +0100 Subject: [PATCH 111/468] chore: update api-reports Signed-off-by: blam --- packages/backend-plugin-api/api-report.md | 2 ++ plugins/scaffolder-backend/api-report.md | 3 +++ plugins/scaffolder-node/api-report.md | 2 ++ 3 files changed, 7 insertions(+) diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index 7807dd96c6..e4d11c7b62 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -316,6 +316,7 @@ export type ReadTreeOptions = { ): boolean; etag?: string; signal?: AbortSignal; + token?: string; }; // @public @@ -343,6 +344,7 @@ export type ReadUrlOptions = { etag?: string; lastModifiedAfter?: Date; signal?: AbortSignal; + token?: string; }; // @public diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 92e0d86167..074bc92bf5 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -145,6 +145,7 @@ export function createFetchPlainAction(options: { { url: string; targetPath?: string | undefined; + token?: string | undefined; }, JsonObject >; @@ -157,6 +158,7 @@ export function createFetchPlainFileAction(options: { { url: string; targetPath: string; + token?: string | undefined; }, JsonObject >; @@ -179,6 +181,7 @@ export function createFetchTemplateAction(options: { replace?: boolean | undefined; trimBlocks?: boolean | undefined; lstripBlocks?: boolean | undefined; + token?: string | undefined; }, JsonObject >; diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index 6b1e6a54b6..fec2205462 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -195,6 +195,7 @@ export function fetchContents(options: { baseUrl?: string; fetchUrl?: string; outputPath: string; + token?: string; }): Promise; // @public @@ -204,6 +205,7 @@ export function fetchFile(options: { baseUrl?: string; fetchUrl?: string; outputPath: string; + token?: string; }): Promise; // @public (undocumented) From 1f020fe05c9e07d77f34bdd662fc50e2628df996 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 25 Jan 2024 20:24:47 +0100 Subject: [PATCH 112/468] chore: add chantgeset Signed-off-by: blam --- .changeset/quick-ties-stare.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/quick-ties-stare.md diff --git a/.changeset/quick-ties-stare.md b/.changeset/quick-ties-stare.md new file mode 100644 index 0000000000..137344512b --- /dev/null +++ b/.changeset/quick-ties-stare.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-plugin-api': patch +'@backstage/backend-common': patch +--- + +Support `token` in `readTree` and `readUrl` From f937aae0c241653d6ed7e824784c926b5b35084a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20V=C4=82LCIU?= Date: Mon, 29 Jan 2024 12:59:38 +0200 Subject: [PATCH 113/468] catalog-graph: reduce the number of API requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use `CatalogClient.getEntitiesByRefs()` to reduce the number of requests to the backend; Signed-off-by: Valentin VĂLCIU --- .changeset/fair-mirrors-hammer.md | 5 ++ .../CatalogGraphCard.test.tsx | 55 +++++++++++++---- .../CatalogGraphPage.test.tsx | 44 +++++++++---- .../useEntityStore.test.ts | 61 ++++++++++++++++--- .../EntityRelationsGraph/useEntityStore.ts | 53 ++++++---------- 5 files changed, 147 insertions(+), 71 deletions(-) create mode 100644 .changeset/fair-mirrors-hammer.md diff --git a/.changeset/fair-mirrors-hammer.md b/.changeset/fair-mirrors-hammer.md new file mode 100644 index 0000000000..3884835d11 --- /dev/null +++ b/.changeset/fair-mirrors-hammer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-graph': patch +--- + +use `CatalogClient.getEntitiesByRefs()` to reduce the number of backend requests from plugin `catalog-graph` diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx index cdd55e300f..1f6c77ccf3 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx @@ -40,6 +40,7 @@ describe('', () => { const catalog = { getEntities: jest.fn(), getEntityByRef: jest.fn(), + getEntitiesByRefs: jest.fn(), removeEntityByUid: jest.fn(), getLocationById: jest.fn(), getLocationByRef: jest.fn(), @@ -75,9 +76,13 @@ describe('', () => { }); test('renders without exploding', async () => { - catalog.getEntityByRef.mockImplementation(async _ => ({ - ...entity, - relations: [], + catalog.getEntitiesByRefs.mockImplementation(async _ => ({ + items: [ + { + ...entity, + relations: [], + }, + ], })); await renderInTestApp(wrapper, { @@ -89,13 +94,20 @@ describe('', () => { expect(await screen.findByText('b:d/c')).toBeInTheDocument(); expect(await screen.findAllByTestId('node')).toHaveLength(1); - expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); + expect(catalog.getEntitiesByRefs).toHaveBeenCalledTimes(1); + expect(catalog.getEntitiesByRefs).toHaveBeenCalledWith( + expect.objectContaining({ entityRefs: ['b:d/c'] }), + ); }); test('renders with custom title', async () => { - catalog.getEntityByRef.mockImplementation(async _ => ({ - ...entity, - relations: [], + catalog.getEntitiesByRefs.mockImplementation(async _ => ({ + items: [ + { + ...entity, + relations: [], + }, + ], })); await renderInTestApp( @@ -116,9 +128,13 @@ describe('', () => { }); test('renders link to standalone viewer', async () => { - catalog.getEntityByRef.mockImplementation(async _ => ({ - ...entity, - relations: [], + catalog.getEntitiesByRefs.mockImplementation(async _ => ({ + items: [ + { + ...entity, + relations: [], + }, + ], })); await renderInTestApp(wrapper, { @@ -138,6 +154,15 @@ describe('', () => { }); test('renders link to standalone viewer with custom config', async () => { + catalog.getEntitiesByRefs.mockImplementation(async _ => ({ + items: [ + { + ...entity, + relations: [], + }, + ], + })); + await renderInTestApp( @@ -162,9 +187,13 @@ describe('', () => { }); test('captures analytics event on click', async () => { - catalog.getEntityByRef.mockImplementation(async _ => ({ - ...entity, - relations: [], + catalog.getEntitiesByRefs.mockImplementation(async _ => ({ + items: [ + { + ...entity, + relations: [], + }, + ], })); const analyticsSpy = new MockAnalyticsApi(); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx index f2360b678d..e188dcc7ff 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.test.tsx @@ -26,6 +26,7 @@ import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { CatalogGraphPage } from './CatalogGraphPage'; +import { GetEntitiesByRefsRequest } from '@backstage/catalog-client'; const navigate = jest.fn(); @@ -111,9 +112,14 @@ describe.skip('', () => { }, ], }; + const allEntities: Record = { + 'b:d/c': entityC, + 'b:d/e': entityE, + }; const catalog = { getEntities: jest.fn(), getEntityByRef: jest.fn(), + getEntitiesByRefs: jest.fn(), removeEntityByUid: jest.fn(), getLocationById: jest.fn(), getLocationByRef: jest.fn(), @@ -142,8 +148,10 @@ describe.skip('', () => { afterEach(() => jest.resetAllMocks()); test('should render without exploding', async () => { - catalog.getEntityByRef.mockImplementation(async (n: any) => - n === 'b:d/e' ? entityE : entityC, + catalog.getEntitiesByRefs.mockImplementation( + async ({ entityRefs }: GetEntitiesByRefsRequest) => ({ + items: entityRefs.map(ref => allEntities[ref]), + }), ); await renderInTestApp(wrapper, { @@ -156,12 +164,14 @@ describe.skip('', () => { await expect(screen.findByText('b:d/c')).resolves.toBeInTheDocument(); await expect(screen.findByText('b:d/e')).resolves.toBeInTheDocument(); await expect(screen.findAllByTestId('node')).resolves.toHaveLength(2); - expect(catalog.getEntityByRef).toHaveBeenCalledTimes(2); + expect(catalog.getEntitiesByRefs).toHaveBeenCalledTimes(2); }); test('should toggle filters', async () => { - catalog.getEntityByRef.mockImplementation(async (n: any) => - n === 'b:d/e' ? entityE : entityC, + catalog.getEntitiesByRefs.mockImplementation( + async ({ entityRefs }: GetEntitiesByRefsRequest) => ({ + items: entityRefs.map(ref => allEntities[ref]), + }), ); await renderInTestApp(wrapper, { @@ -178,8 +188,10 @@ describe.skip('', () => { }); test('should select other entity', async () => { - catalog.getEntityByRef.mockImplementation(async (n: any) => - n === 'b:d/e' ? entityE : entityC, + catalog.getEntitiesByRefs.mockImplementation( + async ({ entityRefs }: GetEntitiesByRefsRequest) => ({ + items: entityRefs.map(ref => allEntities[ref]), + }), ); await renderInTestApp(wrapper, { @@ -196,8 +208,10 @@ describe.skip('', () => { }); test('should navigate to entity', async () => { - catalog.getEntityByRef.mockImplementation(async (n: any) => - n === 'b:d/e' ? entityE : entityC, + catalog.getEntitiesByRefs.mockImplementation( + async ({ entityRefs }: GetEntitiesByRefsRequest) => ({ + items: entityRefs.map(ref => allEntities[ref]), + }), ); await renderInTestApp(wrapper, { @@ -215,8 +229,10 @@ describe.skip('', () => { }); test('should capture analytics event when selecting other entity', async () => { - catalog.getEntityByRef.mockImplementation(async (n: any) => - n === 'b:d/e' ? entityE : entityC, + catalog.getEntitiesByRefs.mockImplementation( + async ({ entityRefs }: GetEntitiesByRefsRequest) => ({ + items: entityRefs.map(ref => allEntities[ref]), + }), ); const analyticsSpy = new MockAnalyticsApi(); @@ -242,8 +258,10 @@ describe.skip('', () => { }); test('should capture analytics event when navigating to entity', async () => { - catalog.getEntityByRef.mockImplementation(async (n: any) => - n === 'b:d/e' ? entityE : entityC, + catalog.getEntitiesByRefs.mockImplementation( + async ({ entityRefs }: GetEntitiesByRefsRequest) => ({ + items: entityRefs.map(ref => allEntities[ref]), + }), ); const analyticsSpy = new MockAnalyticsApi(); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.test.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.test.ts index 0d75fabc3b..786c37ca7e 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.test.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.test.ts @@ -27,6 +27,7 @@ describe('useEntityStore', () => { const catalogApi = { getEntities: jest.fn(), getEntityByRef: jest.fn(), + getEntitiesByRefs: jest.fn(), removeEntityByUid: jest.fn(), getLocationById: jest.fn(), getLocationByRef: jest.fn(), @@ -63,7 +64,7 @@ describe('useEntityStore', () => { }, }; - catalogApi.getEntityByRef.mockResolvedValue(entity); + catalogApi.getEntitiesByRefs.mockResolvedValue({ items: [entity] }); const { result } = renderHook(() => useEntityStore()); @@ -83,7 +84,7 @@ describe('useEntityStore', () => { test('handles request failures', async () => { const err = new Error('Hello World'); - catalogApi.getEntityByRef.mockRejectedValue(err); + catalogApi.getEntitiesByRefs.mockRejectedValue(err); const { result } = renderHook(() => useEntityStore()); @@ -100,7 +101,7 @@ describe('useEntityStore', () => { }); test('handles loading', async () => { - catalogApi.getEntityByRef.mockReturnValue(new Promise(() => {})); + catalogApi.getEntitiesByRefs.mockReturnValue(new Promise(() => {})); const { result } = renderHook(() => useEntityStore()); @@ -131,8 +132,16 @@ describe('useEntityStore', () => { name: 'name2', }, }; + const entity3: Entity = { + apiVersion: 'v1', + kind: 'kind', + metadata: { + namespace: 'namespace', + name: 'name3', + }, + }; - catalogApi.getEntityByRef.mockResolvedValue(entity1); + catalogApi.getEntitiesByRefs.mockResolvedValue({ items: [entity1] }); const { result } = renderHook(() => useEntityStore()); @@ -149,12 +158,15 @@ describe('useEntityStore', () => { }); }); - catalogApi.getEntityByRef.mockResolvedValue(entity2); + catalogApi.getEntitiesByRefs.mockResolvedValue({ + items: [entity2, entity3], + }); act(() => { result.current.requestEntities([ 'kind:namespace/name1', 'kind:namespace/name2', + 'kind:namespace/name3', ]); }); @@ -165,6 +177,7 @@ describe('useEntityStore', () => { expect(entities).toEqual({ 'kind:namespace/name1': entity1, 'kind:namespace/name2': entity2, + 'kind:namespace/name3': entity3, }); }); }); @@ -186,13 +199,26 @@ describe('useEntityStore', () => { name: 'name2', }, }; + const entity3: Entity = { + apiVersion: 'v1', + kind: 'kind', + metadata: { + namespace: 'namespace', + name: 'name3', + }, + }; - catalogApi.getEntityByRef.mockResolvedValue(entity1); + catalogApi.getEntitiesByRefs.mockResolvedValue({ + items: [entity1, entity2], + }); const { result } = renderHook(() => useEntityStore()); act(() => { - result.current.requestEntities(['kind:namespace/name1']); + result.current.requestEntities([ + 'kind:namespace/name1', + 'kind:namespace/name2', + ]); }); await waitFor(() => { @@ -201,13 +227,22 @@ describe('useEntityStore', () => { expect(error).toBeUndefined(); expect(entities).toEqual({ 'kind:namespace/name1': entity1, + 'kind:namespace/name2': entity2, }); }); - catalogApi.getEntityByRef.mockResolvedValue(entity2); + expect(catalogApi.getEntitiesByRefs).toHaveBeenCalledTimes(1); + expect(catalogApi.getEntitiesByRefs).toHaveBeenLastCalledWith({ + entityRefs: ['kind:namespace/name1', 'kind:namespace/name2'], + }); + + catalogApi.getEntitiesByRefs.mockResolvedValue({ items: [entity3] }); act(() => { - result.current.requestEntities(['kind:namespace/name2']); + result.current.requestEntities([ + 'kind:namespace/name2', + 'kind:namespace/name3', + ]); }); await waitFor(() => { @@ -216,9 +251,15 @@ describe('useEntityStore', () => { expect(error).toBeUndefined(); expect(entities).toEqual({ 'kind:namespace/name2': entity2, + 'kind:namespace/name3': entity3, }); }); + expect(catalogApi.getEntitiesByRefs).toHaveBeenCalledTimes(2); + expect(catalogApi.getEntitiesByRefs).toHaveBeenLastCalledWith({ + entityRefs: ['kind:namespace/name3'], + }); + act(() => { result.current.requestEntities(['kind:namespace/name1']); }); @@ -232,6 +273,6 @@ describe('useEntityStore', () => { }); }); - expect(catalogApi.getEntityByRef).toHaveBeenCalledTimes(2); + expect(catalogApi.getEntitiesByRefs).toHaveBeenCalledTimes(2); }); }); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts index 843b308281..5c33f8053d 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts @@ -13,18 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import limiterFactory from 'p-limit'; import { Dispatch, useCallback, useRef, useState } from 'react'; import useAsyncFn from 'react-use/lib/useAsyncFn'; // TODO: This is a good use case for a graphql API, once it is available in the // future. -const limiter = limiterFactory(10); - /** * Ensures that a set of requested entities is loaded. */ @@ -58,38 +55,24 @@ export function useEntityStore(): { }, [state, setEntities]); const [asyncState, fetch] = useAsyncFn(async () => { - const { requestedEntities, outstandingEntities, cachedEntities } = - state.current; - - await Promise.all( - Array.from(requestedEntities).map(entityRef => - limiter(async () => { - if (cachedEntities.has(entityRef)) { - return; - } - - if (outstandingEntities.has(entityRef)) { - await outstandingEntities.get(entityRef); - return; - } - - const promise = catalogClient.getEntityByRef(entityRef); - - outstandingEntities.set(entityRef, promise); - - try { - const entity = await promise; - - if (entity) { - cachedEntities.set(entityRef, entity); - updateEntities(); - } - } finally { - outstandingEntities.delete(entityRef); - } - }), - ), + const { requestedEntities, cachedEntities } = state.current; + const entityRefs: string[] = Array.from(requestedEntities).filter( + entityRef => !cachedEntities.has(entityRef), ); + if (entityRefs.length === 0) { + updateEntities(); + return; + } + + const { items } = await catalogClient.getEntitiesByRefs({ entityRefs }); + items.forEach(ent => { + if (ent) { + const entityRef = stringifyEntityRef(ent); + cachedEntities.set(entityRef, ent); + } + }); + + updateEntities(); }, [state, updateEntities]); const { loading, error } = asyncState; From 7dd8463935af94b62b3c0412def46cd974a44804 Mon Sep 17 00:00:00 2001 From: secustor Date: Mon, 29 Jan 2024 12:20:34 +0100 Subject: [PATCH 114/468] chore(plugins/auth-backend): migrate from passport-saml package to @node-saml/passport-saml Signed-off-by: secustor --- .changeset/tasty-oranges-rescue.md | 5 + plugins/auth-backend/package.json | 2 +- .../src/providers/saml/provider.ts | 31 ++-- yarn.lock | 132 +++++++++++------- 4 files changed, 109 insertions(+), 61 deletions(-) create mode 100644 .changeset/tasty-oranges-rescue.md diff --git a/.changeset/tasty-oranges-rescue.md b/.changeset/tasty-oranges-rescue.md new file mode 100644 index 0000000000..cc57aaffad --- /dev/null +++ b/.changeset/tasty-oranges-rescue.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +migrate from 'passport-saml' to '@node-saml/passport-saml' diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 29c34b84e1..140b4b470a 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -52,6 +52,7 @@ "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", "@google-cloud/firestore": "^7.0.0", + "@node-saml/passport-saml": "^4.0.4", "@types/express": "^4.17.6", "@types/passport": "^1.0.3", "compression": "^1.7.4", @@ -81,7 +82,6 @@ "passport-microsoft": "^1.0.0", "passport-oauth2": "^1.6.1", "passport-onelogin-oauth": "^0.0.1", - "passport-saml": "^3.1.2", "uuid": "^8.0.0", "winston": "^3.2.1", "yn": "^4.0.0" diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts index 3d35f46628..0c09f1e987 100644 --- a/plugins/auth-backend/src/providers/saml/provider.ts +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -15,12 +15,12 @@ */ import express from 'express'; -import { SamlConfig } from 'passport-saml/lib/passport-saml/types'; +import { SamlConfig } from '@node-saml/passport-saml'; import { Strategy as SamlStrategy, Profile as SamlProfile, VerifyWithoutRequest, -} from 'passport-saml'; +} from '@node-saml/passport-saml'; import { executeFrameHandlerStrategy, executeRedirectStrategy, @@ -62,17 +62,22 @@ export class SamlAuthProvider implements AuthProviderRouteHandlers { this.signInResolver = options.signInResolver; this.authHandler = options.authHandler; this.resolverContext = options.resolverContext; - this.strategy = new SamlStrategy({ ...options }, (( - fullProfile: SamlProfile, - done: PassportDoneCallback, - ) => { - // TODO: There's plenty more validation and profile handling to do here, - // this provider is currently only intended to validate the provider pattern - // for non-oauth auth flows. - // TODO: This flow doesn't issue an identity token that can be used to validate - // the identity of the user in other backends, which we need in some form. - done(undefined, { fullProfile }); - }) as VerifyWithoutRequest); + this.strategy = new SamlStrategy( + { ...options }, + (( + fullProfile: SamlProfile, + done: PassportDoneCallback, + ) => { + // TODO: There's plenty more validation and profile handling to do here, + // this provider is currently only intended to validate the provider pattern + // for non-oauth auth flows. + // TODO: This flow doesn't issue an identity token that can be used to validate + // the identity of the user in other backends, which we need in some form. + done(undefined, { fullProfile }); + }) as VerifyWithoutRequest, + // TODO: Validate logout + () => {}, + ); } async start(req: express.Request, res: express.Response): Promise { diff --git a/yarn.lock b/yarn.lock index 1d31e0fc44..1d9d11f9b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4789,6 +4789,7 @@ __metadata: "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" "@google-cloud/firestore": ^7.0.0 + "@node-saml/passport-saml": ^4.0.4 "@types/body-parser": ^1.19.0 "@types/cookie-parser": ^1.4.2 "@types/express": ^4.17.6 @@ -4830,7 +4831,6 @@ __metadata: passport-microsoft: ^1.0.0 passport-oauth2: ^1.6.1 passport-onelogin-oauth: ^0.0.1 - passport-saml: ^3.1.2 supertest: ^6.1.3 uuid: ^8.0.0 winston: ^3.2.1 @@ -13122,6 +13122,39 @@ __metadata: languageName: node linkType: hard +"@node-saml/node-saml@npm:^4.0.4": + version: 4.0.5 + resolution: "@node-saml/node-saml@npm:4.0.5" + dependencies: + "@types/debug": ^4.1.7 + "@types/passport": ^1.0.11 + "@types/xml-crypto": ^1.4.2 + "@types/xml-encryption": ^1.2.1 + "@types/xml2js": ^0.4.11 + "@xmldom/xmldom": ^0.8.6 + debug: ^4.3.4 + xml-crypto: ^3.0.1 + xml-encryption: ^3.0.2 + xml2js: ^0.5.0 + xmlbuilder: ^15.1.1 + checksum: 7d97575111a381ef2d0f16e1fc85ae3f84322ccba06dcb0594b00cf598e429658f45e479b78836943f69f249c08a8593e5168404acf7f1ed659ead53ceef465e + languageName: node + linkType: hard + +"@node-saml/passport-saml@npm:^4.0.4": + version: 4.0.4 + resolution: "@node-saml/passport-saml@npm:4.0.4" + dependencies: + "@node-saml/node-saml": ^4.0.4 + "@types/express": ^4.17.14 + "@types/passport": ^1.0.11 + "@types/passport-strategy": ^0.2.35 + passport: ^0.6.0 + passport-strategy: ^1.0.0 + checksum: 75178669d7d47038c33bb0602454cb5030fc9b3ecdcae9163a35cef436bc6c22e68e57d06213e0118ff1cb0dcd2f2fa25112672ebe4cbad90578df21bec67fce + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -18017,7 +18050,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.17, @types/express@npm:^4.17.21, @types/express@npm:^4.17.6": +"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.14, @types/express@npm:^4.17.17, @types/express@npm:^4.17.21, @types/express@npm:^4.17.6": version: 4.17.21 resolution: "@types/express@npm:4.17.21" dependencies: @@ -18664,7 +18697,7 @@ __metadata: languageName: node linkType: hard -"@types/passport@npm:*, @types/passport@npm:^1.0.3": +"@types/passport@npm:*, @types/passport@npm:^1.0.11, @types/passport@npm:^1.0.3": version: 1.0.16 resolution: "@types/passport@npm:1.0.16" dependencies: @@ -19300,7 +19333,26 @@ __metadata: languageName: node linkType: hard -"@types/xml2js@npm:*, @types/xml2js@npm:^0.4.7": +"@types/xml-crypto@npm:^1.4.2": + version: 1.4.6 + resolution: "@types/xml-crypto@npm:1.4.6" + dependencies: + "@types/node": "*" + xpath: 0.0.27 + checksum: e53516a2f5e4e018e164eb1cb9fc922294b9a339624e567c1c00a2b1496e9f86826210473e62ceb0b45949638c9d149da088b3598f6b3acd86e933f0a2b23f2c + languageName: node + linkType: hard + +"@types/xml-encryption@npm:^1.2.1": + version: 1.2.4 + resolution: "@types/xml-encryption@npm:1.2.4" + dependencies: + "@types/node": "*" + checksum: 1ef957dfb47cf55b12e114755e271a2343f73eb4c59ab6c68b0b7d1b8111d7e1bd8d2bfe0601d2aea09be83c66355bc77fc59f9b71aeff9bb9e15371bcfef5d3 + languageName: node + linkType: hard + +"@types/xml2js@npm:*, @types/xml2js@npm:^0.4.11, @types/xml2js@npm:^0.4.7": version: 0.4.14 resolution: "@types/xml2js@npm:0.4.14" dependencies: @@ -19960,14 +20012,7 @@ __metadata: languageName: node linkType: hard -"@xmldom/xmldom@npm:^0.7.0, @xmldom/xmldom@npm:^0.7.6, @xmldom/xmldom@npm:^0.7.9": - version: 0.7.13 - resolution: "@xmldom/xmldom@npm:0.7.13" - checksum: b4054078530e5fa8ede9677425deff0fce6d965f4c477ca73f8490d8a089e60b8498a15560425a1335f5ff99ecb851ed2c734b0a9a879299a5694302f212f37a - languageName: node - linkType: hard - -"@xmldom/xmldom@npm:^0.8.3": +"@xmldom/xmldom@npm:^0.8.3, @xmldom/xmldom@npm:^0.8.5, @xmldom/xmldom@npm:^0.8.6, @xmldom/xmldom@npm:^0.8.8": version: 0.8.10 resolution: "@xmldom/xmldom@npm:0.8.10" checksum: 4c136aec31fb3b49aaa53b6fcbfe524d02a1dc0d8e17ee35bd3bf35e9ce1344560481cd1efd086ad1a4821541482528672306d5e37cdbd187f33d7fadd3e2cf0 @@ -36408,21 +36453,6 @@ __metadata: languageName: node linkType: hard -"passport-saml@npm:^3.1.2": - version: 3.2.4 - resolution: "passport-saml@npm:3.2.4" - dependencies: - "@xmldom/xmldom": ^0.7.6 - debug: ^4.3.2 - passport-strategy: ^1.0.0 - xml-crypto: ^2.1.3 - xml-encryption: ^2.0.0 - xml2js: ^0.4.23 - xmlbuilder: ^15.1.1 - checksum: 8e885af4d44c2d862b2ea0d051ab2a36bc6f9a70e62f90daf7ce4eefd126ac2ab4d5fc070693eba05f5e1be248af23fa018611bbfa7fad31708371f387f5dd77 - languageName: node - linkType: hard - "passport-strategy@npm:1.x.x, passport-strategy@npm:^1.0.0": version: 1.0.0 resolution: "passport-strategy@npm:1.0.0" @@ -36430,6 +36460,17 @@ __metadata: languageName: node linkType: hard +"passport@npm:^0.6.0": + version: 0.6.0 + resolution: "passport@npm:0.6.0" + dependencies: + passport-strategy: 1.x.x + pause: 0.0.1 + utils-merge: ^1.0.1 + checksum: ef932ad671d50de34765c7a53cd1e058d8331a82a6df09265a9c6c1168911aee4a7b5215803d0101110ab7f317e096b4954ca7e18fb2c33b9929f0bd17dbe159 + languageName: node + linkType: hard + "passport@npm:^0.7.0": version: 0.7.0 resolution: "passport@npm:0.7.0" @@ -44755,24 +44796,24 @@ __metadata: languageName: node linkType: hard -"xml-crypto@npm:^2.1.3": - version: 2.1.5 - resolution: "xml-crypto@npm:2.1.5" +"xml-crypto@npm:^3.0.1": + version: 3.2.0 + resolution: "xml-crypto@npm:3.2.0" dependencies: - "@xmldom/xmldom": ^0.7.9 + "@xmldom/xmldom": ^0.8.8 xpath: 0.0.32 - checksum: 387ed6aa812f9ea7fb33385bd3e934042152ee9a97870f28ebfa5c7931eee23a7a2d36ca35916fbe5eadd65163ce9483db661cf3f569c9177773e8efa1acfa37 + checksum: 6c4974a7518307ea006dcfc1405f61c6738b45574b4d9d1e62f53b602bfcf894d34017f99d618f26f67c40a5e6d78e6228116ded2768b2ca5b2df5c8bf7774b7 languageName: node linkType: hard -"xml-encryption@npm:^2.0.0": - version: 2.0.0 - resolution: "xml-encryption@npm:2.0.0" +"xml-encryption@npm:^3.0.2": + version: 3.0.2 + resolution: "xml-encryption@npm:3.0.2" dependencies: - "@xmldom/xmldom": ^0.7.0 + "@xmldom/xmldom": ^0.8.5 escape-html: ^1.0.3 xpath: 0.0.32 - checksum: a454445704c5e3aa3f992128c413c02f3c00c346cb0d63b01beae4b6a341cfc0a52a0219ccec47dcce250e336ba7b09d95909913b1f199ca43604961a00a1995 + checksum: aac1b987d5de5becfc747c88c3a656c00799a153ab541078b875a69e1ac1f1c2f29bf85f22eab6a78382dc2919f79401a916cc392aba7994475919e0695893eb languageName: node linkType: hard @@ -44790,16 +44831,6 @@ __metadata: languageName: node linkType: hard -"xml2js@npm:^0.4.23": - version: 0.4.23 - resolution: "xml2js@npm:0.4.23" - dependencies: - sax: ">=0.6.0" - xmlbuilder: ~11.0.0 - checksum: ca0cf2dfbf6deeaae878a891c8fbc0db6fd04398087084edf143cdc83d0509ad0fe199b890f62f39c4415cf60268a27a6aed0d343f0658f8779bd7add690fa98 - languageName: node - linkType: hard - "xml2js@npm:^0.5.0": version: 0.5.0 resolution: "xml2js@npm:0.5.0" @@ -44848,6 +44879,13 @@ __metadata: languageName: node linkType: hard +"xpath@npm:0.0.27": + version: 0.0.27 + resolution: "xpath@npm:0.0.27" + checksum: 51f45d211a9a552a8f6a12a474061e89bafb07e0aecd4bad18a557411feb975919c158e1a66e4ea0542198c6ed442481d9f709c625cca57b97aaedeaeded902e + languageName: node + linkType: hard + "xpath@npm:0.0.32": version: 0.0.32 resolution: "xpath@npm:0.0.32" From f640129d84c6de88fa4a499ffa0258f2ae51e0e9 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 29 Jan 2024 13:29:44 +0100 Subject: [PATCH 115/468] chore: tidying up tests and implementing for `search` also Signed-off-by: blam --- .../src/reading/GithubUrlReader.test.ts | 44 +++++++++++++++---- .../src/reading/GithubUrlReader.ts | 4 +- .../services/definitions/UrlReaderService.ts | 12 +++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index ec775183b3..f07ada0cc3 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -46,7 +46,7 @@ const treeResponseFactory = DefaultReadTreeResponseFactory.create({ const mockCredentialsProvider = { getCredentials: jest.fn().mockResolvedValue({ headers: {} }), -} as unknown as GithubCredentialsProvider; +} satisfies GithubCredentialsProvider; const githubProcessor = new GithubUrlReader( new GithubIntegration( @@ -105,7 +105,7 @@ describe('GithubUrlReader', () => { otherheader: 'something', }; - (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + mockCredentialsProvider.getCredentials.mockResolvedValue({ headers: mockHeaders, }); @@ -146,7 +146,7 @@ describe('GithubUrlReader', () => { otherheader: 'something', }; - (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + mockCredentialsProvider.getCredentials.mockResolvedValue({ headers: mockHeaders, }); @@ -182,7 +182,7 @@ describe('GithubUrlReader', () => { otherheader: 'something', }; - (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + mockCredentialsProvider.getCredentials.mockResolvedValue({ headers: mockHeaders, }); @@ -241,7 +241,7 @@ describe('GithubUrlReader', () => { }); it('should return etag and last-modified from the response', async () => { - (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + mockCredentialsProvider.getCredentials.mockResolvedValue({ headers: { Authorization: 'bearer blah', }, @@ -275,7 +275,7 @@ describe('GithubUrlReader', () => { it('should override the token if its provided', async () => { expect.assertions(1); - (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + mockCredentialsProvider.getCredentials.mockResolvedValue({ headers: { Authorization: 'bearer blah', }, @@ -458,7 +458,7 @@ describe('GithubUrlReader', () => { otherheader: 'something', }; - (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + mockCredentialsProvider.getCredentials.mockResolvedValue({ headers: mockHeaders, }); @@ -497,7 +497,7 @@ describe('GithubUrlReader', () => { Authorization: 'bearer blah', }; - (mockCredentialsProvider.getCredentials as jest.Mock).mockResolvedValue({ + mockCredentialsProvider.getCredentials.mockResolvedValue({ headers: mockHeaders, }); @@ -970,6 +970,34 @@ describe('GithubUrlReader', () => { await runTests(gheProcessor, 'https://ghe.github.com'); }); + it('passes through a token for the search request', async () => { + expect.assertions(1); + + worker.use( + rest.get( + 'https://ghe.github.com/api/v3/repos/backstage/mock/git/trees/etag123abc', + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBe( + 'Bearer overridentoken', + ); + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json({ + truncated: true, + tree: [], + } as Partial), + ); + }, + ), + ); + + await gheProcessor.search( + `https://ghe.github.com/backstage/mock/tree/main/**/*`, + { token: 'overridentoken' }, + ); + }); + // eslint-disable-next-line jest/expect-expect it('succeeds on ghe when going via readTree', async () => { worker.use( diff --git a/packages/backend-common/src/reading/GithubUrlReader.ts b/packages/backend-common/src/reading/GithubUrlReader.ts index b476435771..b49945013d 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.ts @@ -185,9 +185,7 @@ export class GithubUrlReader implements UrlReader { } const { filepath } = parseGitUrl(url); - const { headers } = await this.deps.credentialsProvider.getCredentials({ - url, - }); + const { headers } = await this.getCredentials(url, options); const files = await this.doSearch( url, diff --git a/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts b/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts index e89deab0eb..734dcd9902 100644 --- a/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts +++ b/packages/backend-plugin-api/src/services/definitions/UrlReaderService.ts @@ -305,6 +305,18 @@ export type SearchOptions = { * Not all reader implementations may take this field into account. */ signal?: AbortSignal; + + /** + * An optional token to use for authentication when reading the resources. + * + * @remarks + * + * By default all URL Readers will use the integrations config which is supplied + * when creating the Readers. Sometimes it might be desireable to use the already + * created URLReaders but with a different token, maybe that's supplied by the user + * at runtime. + */ + token?: string; }; /** From 1c85cc016800e12fe41f6e23e9174cecc4ea4e85 Mon Sep 17 00:00:00 2001 From: AmbrishRamachandiran Date: Mon, 29 Jan 2024 18:14:39 +0530 Subject: [PATCH 116/468] Updated the readme for the licence year Signed-off-by: AmbrishRamachandiran --- README-ko_kr.md | 2 +- README-zh_Hans.md | 2 +- README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README-ko_kr.md b/README-ko_kr.md index 0544304cea..e5f50bd2b8 100644 --- a/README-ko_kr.md +++ b/README-ko_kr.md @@ -66,7 +66,7 @@ Backstage의 문서는 다음을 포함합니다: ## License -Copyright 2020-2023 © The Backstage Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage +Copyright 2020-2024 © The Backstage Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/README-zh_Hans.md b/README-zh_Hans.md index b1e5a16add..3e6cea4e95 100644 --- a/README-zh_Hans.md +++ b/README-zh_Hans.md @@ -66,7 +66,7 @@ Backstage 的文档包括: ## 许可 -版权所有 2020-2023 © Backstage 作者。版权所有。Linux 基金会已注册商标并使用商标。有关 Linux 基金会的商标列表,请参阅我们的商标使用页面:https://www.linuxfoundation.org/trademark-usage +版权所有 2020-2024 © Backstage 作者。版权所有。Linux 基金会已注册商标并使用商标。有关 Linux 基金会的商标列表,请参阅我们的商标使用页面:https://www.linuxfoundation.org/trademark-usage 采用 Apache v2.0 许可:http://www.apache.org/licenses/LICENSE-2.0 diff --git a/README.md b/README.md index a1c53e4c8d..f7b74adee4 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ To engage with our community, you can use the following resources: ## License -Copyright 2020-2023 © The Backstage Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage +Copyright 2020-2024 © The Backstage Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 From a8b0c802b0904f1c5b0dc6785829aab91f81ae03 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 29 Jan 2024 14:12:36 +0100 Subject: [PATCH 117/468] chore: make sure to keep the existing uiSchema when parsing dependencies Signed-off-by: blam --- .../src/next/lib/schema.test.ts | 107 ++++++++++++++++++ .../scaffolder-react/src/next/lib/schema.ts | 10 +- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-react/src/next/lib/schema.test.ts b/plugins/scaffolder-react/src/next/lib/schema.test.ts index 4dc6cda603..5bfd65133c 100644 --- a/plugins/scaffolder-react/src/next/lib/schema.test.ts +++ b/plugins/scaffolder-react/src/next/lib/schema.test.ts @@ -412,6 +412,113 @@ describe('extractSchemaFromStep', () => { }); }); + it('doesnt override existing uiSchema with things from dependencies', () => { + const inputSchema: JsonObject = { + type: 'object', + title: + "Field 2 depend on field 1, field 1 is a radio button but it's visible as a field", + required: ['exampleField1'], + properties: { + exampleField0: { + title: 'Radio button that is not a dependency', + type: 'string', + enum: ['foo', 'bar'], + 'ui:widget': 'radio', + }, + exampleField1: { + title: 'Radio button input that is a dependency', + type: 'string', + enum: ['visible', 'hidden'], + 'ui:widget': 'radio', + }, + }, + dependencies: { + exampleField1: { + oneOf: [ + { + properties: { + exampleField1: { + enum: ['visible'], + }, + exampleField2: { + title: 'FIELD 2', + type: 'string', + description: 'Explanation', + }, + }, + }, + { + properties: { + exampleField1: { + enum: ['hidden'], + }, + }, + }, + ], + }, + }, + }; + + const expectedSchema = { + type: 'object', + title: + "Field 2 depend on field 1, field 1 is a radio button but it's visible as a field", + required: ['exampleField1'], + properties: { + exampleField0: { + title: 'Radio button that is not a dependency', + type: 'string', + enum: ['foo', 'bar'], + }, + exampleField1: { + title: 'Radio button input that is a dependency', + type: 'string', + enum: ['visible', 'hidden'], + }, + }, + dependencies: { + exampleField1: { + oneOf: [ + { + properties: { + exampleField1: { + enum: ['visible'], + }, + exampleField2: { + title: 'FIELD 2', + type: 'string', + description: 'Explanation', + }, + }, + }, + { + properties: { + exampleField1: { + enum: ['hidden'], + }, + }, + }, + ], + }, + }, + }; + + const expectedUiSchema = { + exampleField0: { + 'ui:widget': 'radio', + }, + exampleField1: { + 'ui:widget': 'radio', + }, + exampleField2: {}, + }; + + expect(extractSchemaFromStep(inputSchema)).toEqual({ + schema: expectedSchema, + uiSchema: expectedUiSchema, + }); + }); + it('transforms conditional schema', () => { const inputSchema: JsonObject = { type: 'object', diff --git a/plugins/scaffolder-react/src/next/lib/schema.ts b/plugins/scaffolder-react/src/next/lib/schema.ts index b7c1364f41..2ca2a6fa7b 100644 --- a/plugins/scaffolder-react/src/next/lib/schema.ts +++ b/plugins/scaffolder-react/src/next/lib/schema.ts @@ -58,9 +58,13 @@ function extractUiSchema(schema: JsonObject, uiSchema: JsonObject) { if (!isObject(schemaNode)) { continue; } - const innerUiSchema = {}; - uiSchema[propName] = innerUiSchema; - extractUiSchema(schemaNode, innerUiSchema); + + if (!isObject(uiSchema[propName])) { + const innerUiSchema = {}; + uiSchema[propName] = innerUiSchema; + } + + extractUiSchema(schemaNode, uiSchema[propName] as JsonObject); } } From 82affc70afa0f98e8a2735facccda2a7c255b92b Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 29 Jan 2024 14:13:26 +0100 Subject: [PATCH 118/468] chore: updating ui:schema Signed-off-by: blam --- .changeset/twelve-weeks-march.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/twelve-weeks-march.md diff --git a/.changeset/twelve-weeks-march.md b/.changeset/twelve-weeks-march.md new file mode 100644 index 0000000000..4d9fff5b34 --- /dev/null +++ b/.changeset/twelve-weeks-march.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Fix issue where `ui:schema` was replaced with an empty object if `dependencies` is defined From dca3b7251a942e1f1e2cbc2364a71a66bc950b69 Mon Sep 17 00:00:00 2001 From: Jean-Louis FEREY Date: Mon, 29 Jan 2024 14:14:35 +0100 Subject: [PATCH 119/468] Update v1.22.0.md A little correction: Gitlab instead of Github for the new Gitlab scaffolder action Signed-off-by: Jean-Louis FEREY --- docs/releases/v1.22.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releases/v1.22.0.md b/docs/releases/v1.22.0.md index e5ce345ccc..0c2d3b4d5f 100644 --- a/docs/releases/v1.22.0.md +++ b/docs/releases/v1.22.0.md @@ -39,7 +39,7 @@ Contributed by [@davidfestal](https://github.com/davidfestal) in [#18862](https: ### New Scaffolder action `gitlab:issues:create` -You can now create GitHub issues in your scaffolder flows! Contributed by [@elaine-mattos](https://github.com/elaine-mattos) in [#21929](https://github.com/backstage/backstage/pull/21929) +You can now create Gitlab issues in your scaffolder flows! Contributed by [@elaine-mattos](https://github.com/elaine-mattos) in [#21929](https://github.com/backstage/backstage/pull/21929) ### New Scaffolder action `gitlab:repo:push` From fb0ae81b6a93a8abec72e200de619747e0334dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 29 Jan 2024 14:18:16 +0100 Subject: [PATCH 120/468] Update docs/releases/v1.22.0.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/releases/v1.22.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releases/v1.22.0.md b/docs/releases/v1.22.0.md index 0c2d3b4d5f..9f9295fb96 100644 --- a/docs/releases/v1.22.0.md +++ b/docs/releases/v1.22.0.md @@ -39,7 +39,7 @@ Contributed by [@davidfestal](https://github.com/davidfestal) in [#18862](https: ### New Scaffolder action `gitlab:issues:create` -You can now create Gitlab issues in your scaffolder flows! Contributed by [@elaine-mattos](https://github.com/elaine-mattos) in [#21929](https://github.com/backstage/backstage/pull/21929) +You can now create GitLab issues in your scaffolder flows! Contributed by [@elaine-mattos](https://github.com/elaine-mattos) in [#21929](https://github.com/backstage/backstage/pull/21929) ### New Scaffolder action `gitlab:repo:push` From e536746b1660237af8cdb510d758b43b79fd9cc1 Mon Sep 17 00:00:00 2001 From: Frank Showalter <842058+fshowalter@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:47:51 -0500 Subject: [PATCH 121/468] update yarn.lock Signed-off-by: Frank Showalter <842058+fshowalter@users.noreply.github.com> --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 23bb660897..71909cf568 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9737,7 +9737,7 @@ __metadata: "@types/react": ^16.13.1 || ^17.0.0 peerDependencies: "@material-ui/core": ^4.12.2 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react: ^16.13.1 || ^17.0.0 || ^18.0.0 react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 languageName: unknown From f7d45ca65c7f10b44a4ac05d412b3328572c5106 Mon Sep 17 00:00:00 2001 From: Frank Showalter <842058+fshowalter@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:50:41 -0500 Subject: [PATCH 122/468] add changeset Signed-off-by: Frank Showalter <842058+fshowalter@users.noreply.github.com> --- .changeset/quick-beans-own.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/quick-beans-own.md diff --git a/.changeset/quick-beans-own.md b/.changeset/quick-beans-own.md new file mode 100644 index 0000000000..d5939c14ac --- /dev/null +++ b/.changeset/quick-beans-own.md @@ -0,0 +1,5 @@ +--- +'@backstage/theme': patch +--- + +Widen @types/react range to include 18. From 2c5a49ec61ebcbe488c641a8727e1a0e64e9d6ce Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 29 Jan 2024 15:48:06 +0100 Subject: [PATCH 123/468] Adjust style Signed-off-by: Philipp Hugenroth --- .../architecture/03-extensions.md | 2 +- .../building-plugins/04-built-in-data-refs.md | 96 ++++++++++--------- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index cfbabdf9bc..cdee6acb2a 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -135,7 +135,7 @@ const extension = createExtension({ We provide default `coreExtensionData`, which provides commonly used `ExtensionDataRef`s - e.g. for `React.JSX.Element` and `RouteRef`. They can be used when creating your own extension. For example, the React Element extension data that we defined above is already provided as `coreExtensionData.reactElement`. - +For a full list and explanations of all types of core extension data, see the [core extension data reference](../building-plugins/04-built-in-data-refs.md). --> ### Optional Extension Data diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md index 16ded9a9fd..c338dddc4a 100644 --- a/docs/frontend-system/building-plugins/04-built-in-data-refs.md +++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md @@ -6,39 +6,33 @@ sidebar_label: Built-in data refs description: Configuring or overriding built-in extension data references --- +> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.** + To have a better understanding of extension data references please read [extension data section the extensions architecture documentation](../architecture/03-extensions.md#extension-data) first. ## Built-in extension data references -### Frontend Plugin API +Data references help to define the input & output of an extension. A data ref is uniquely identified through there `id`. Through the data ref strong typing is enforced for the input/output of the extension. -#### Core Extension Data +### Core Extension Data -| namespace | name | type | id | -| :-------: | :----------: | :---------: | :-----------------: | -| - | reactElement | JSX.Element | `core.reactElement` | -| - | routePath | string | `core.routing.path` | -| - | routeRef | RouteRef | `core.routing.ref` | +Commonly used `ExtensionDataRef`s for extensions. -#### +| namespace | name | type | id | +| :-------: | :----------: | :-----------: | :-----------------: | +| - | reactElement | `JSX.Element` | `core.reactElement` | +| - | routePath | `string` | `core.routing.path` | +| - | routeRef | `RouteRef` | `core.routing.ref` | -| namespace | name | type | id | -| :----------------: | :------------: | :-----------: | :--------------: | -| createApiExtension | factoryDataRef | AnyApiFactory | core.api.factory | +### Core API Data -#### +| namespace | name | type | id | +| :----------------: | :------------: | :-------------: | :----------------: | +| createApiExtension | factoryDataRef | `AnyApiFactory` | `core.api.factory` | -| namespace | name | type | id | -| :---------------------------: | :--------------: | :----------------------------------: | :--------------: | -| createAppRootWrapperExtension | componentDataRef | ComponentType> | app.root.wrapper | +### Core Navigation -#### - -| namespace | name | type | id | -| :-------------------: | :--------------: | :----------------------------------: | :--------------: | -| createRouterExtension | componentDataRef | ComponentType> | app.root.wrapper | - -#### +#### Item Data ```ts type DataType = { @@ -48,11 +42,11 @@ type DataType = { }; ``` -| namespace | name | type | id | -| :--------------------: | :-----------: | :------: | :------------------: | -| createNavItemExtension | targetDataRef | DataType | core.nav-item.target | +| namespace | name | type | id | +| :--------------------: | :-----------: | :--------: | :--------------------: | +| createNavItemExtension | targetDataRef | `DataType` | `core.nav-item.target` | -#### +#### Logo Data ```ts type DataType = { @@ -61,23 +55,23 @@ type DataType = { }; ``` -| namespace | name | type | id | -| :--------------------: | :-----------------: | :----------------------------------: | :-------------------------: | -| createNavLogoExtension | logoElementsDataRef | ComponentType> | core.nav-logo.logo-elements | +| namespace | name | type | id | +| :--------------------: | :-----------------: | :------------------------------------: | :---------------------------: | +| createNavLogoExtension | logoElementsDataRef | `ComponentType>` | `core.nav-logo.logo-elements` | -#### +### Core App Components Data -| namespace | name | type | id | -| :-----------------------: | :--------------: | :----------------------------: | :-------------------------: | -| createSignInPageExtension | componentDataRef | ComponentType | core.sign-in-page.component | +| namespace | name | type | id | +| :-----------------------: | :--------------: | :------------------------------: | :---------------------------: | +| createSignInPageExtension | componentDataRef | `ComponentType` | `core.sign-in-page.component` | -#### +### Core Theme Data -| namespace | name | type | id | -| :------------------: | :----------: | :------: | :--------------: | -| createThemeExtension | themeDataRef | AppTheme | core.theme.theme | +| namespace | name | type | id | +| :------------------: | :----------: | :--------: | :----------------: | +| createThemeExtension | themeDataRef | `AppTheme` | `core.theme.theme` | -#### +### Core Components ```ts type DataType = { @@ -86,12 +80,24 @@ type DataType = { }; ``` -| namespace | name | type | id | -| :----------------------: | :--------------: | :------: | :----------------------: | -| createComponentExtension | componentDataRef | DataType | core.component.component | +| namespace | name | type | id | +| :----------------------: | :--------------: | :--------: | :------------------------: | +| createComponentExtension | componentDataRef | `DataType` | `core.component.component` | -#### +### Core Translation -| namespace | name | type | id | -| :------------------------: | :----------------: | :----------------------------------------: | :--------------------------: | -| createTranslationExtension | translationDataRef | TranslationResource or TranslationMessages | core.translation.translation | +| namespace | name | type | id | +| :------------------------: | :----------------: | :--------------------------------------------: | :----------------------------: | +| createTranslationExtension | translationDataRef | `TranslationResource` or `TranslationMessages` | `core.translation.translation` | + +### App Root + +| namespace | name | type | id | +| :---------------------------: | :--------------: | :------------------------------------: | :----------------: | +| createAppRootWrapperExtension | componentDataRef | `ComponentType>` | `app.root.wrapper` | + +### App Router + +| namespace | name | type | id | +| :-------------------: | :--------------: | :------------------------------------: | :------------------: | +| createRouterExtension | componentDataRef | `ComponentType>` | `app.router.wrapper` | From 666eff5cc08eb4f0637240570c1c36a64e292624 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Mon, 29 Jan 2024 10:51:09 -0500 Subject: [PATCH 124/468] forbid duplicate cluster names in config Signed-off-by: Jamie Klassen --- .changeset/great-rats-collect.md | 7 +++++++ .../ConfigClusterLocator.test.ts | 21 +++++++++++++++++++ .../cluster-locator/ConfigClusterLocator.ts | 5 +++++ 3 files changed, 33 insertions(+) create mode 100644 .changeset/great-rats-collect.md diff --git a/.changeset/great-rats-collect.md b/.changeset/great-rats-collect.md new file mode 100644 index 0000000000..24b01c0fe3 --- /dev/null +++ b/.changeset/great-rats-collect.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-kubernetes-backend': minor +--- + +**BREAKING** The backend will fail to start if two clusters in the app-config +have the same name. The requirement for unique names has been declared in the +docs for some time, but is now enforced. diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts index ad3203a16d..d36f10bb13 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.test.ts @@ -405,4 +405,25 @@ describe('ConfigClusterLocator', () => { `Invalid cluster 'cluster1': mock error`, ); }); + + it('fails on duplicate cluster names', () => { + const config: Config = new ConfigReader({ + clusters: [ + { + name: 'cluster', + url: 'url', + authProvider: 'authProvider', + }, + { + name: 'cluster', + url: 'url', + authProvider: 'authProvider', + }, + ], + }); + + expect(() => ConfigClusterLocator.fromConfig(config, authStrategy)).toThrow( + `Duplicate cluster name 'cluster'`, + ); + }); }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts index 19ba8634ad..6fd5f3b1cb 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/ConfigClusterLocator.ts @@ -35,12 +35,17 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier { config: Config, authStrategy: AuthenticationStrategy, ): ConfigClusterLocator { + const clusterNames = new Set(); return new ConfigClusterLocator( config.getConfigArray('clusters').map(c => { const authMetadataBlock = c.getOptional<{ [ANNOTATION_KUBERNETES_AUTH_PROVIDER]?: string; }>('authMetadata'); const name = c.getString('name'); + if (clusterNames.has(name)) { + throw new Error(`Duplicate cluster name '${name}'`); + } + clusterNames.add(name); const authProvider = authMetadataBlock?.[ANNOTATION_KUBERNETES_AUTH_PROVIDER] ?? c.getOptionalString('authProvider'); From fd563915ebc5c5831f7884b27fe46e3d5b9e3046 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey <34432188+sennyeya@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:34:41 -0500 Subject: [PATCH 125/468] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Aramis Sennyey <34432188+sennyeya@users.noreply.github.com> --- docs/permissions/concepts.md | 2 +- docs/references/glossary.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/permissions/concepts.md b/docs/permissions/concepts.md index c3c32601df..556d752c52 100644 --- a/docs/permissions/concepts.md +++ b/docs/permissions/concepts.md @@ -16,4 +16,4 @@ In many cases, a permission represents a user's interaction with another object. See [Conditional decisions](../references/glossary.md#conditional-decisions). -A good example would be the catalog plugin's "has annotation" rule needs to know what annotation to look for on a given entity. The permission framework would respond to a request by the catalog plugin in this case with a condition decision. The catalog plugin would then need to correctly filter for entities matching the "has annotations" condition. This conditional behavior avoids coupling between policies and resource schemas, and allows plugins to evaluate complex rules in an efficient way. For example, a plugin may convert a conditional decision to a database query instead of loading and filtering objects in memory. +A good example would be the catalog plugin's "has annotation" rule which needs to know what annotation to look for on a given entity. The permission framework would respond to a request by the catalog plugin in this case with a condition decision. The catalog plugin would then need to correctly filter for entities matching the "has annotations" condition. This conditional behavior avoids coupling between policies and resource schemas, and allows plugins to evaluate complex rules in an efficient way. For example, a plugin may convert a conditional decision to a database query instead of loading and filtering objects in memory. diff --git a/docs/references/glossary.md b/docs/references/glossary.md index b82b2da064..abbb8ce5ef 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -183,7 +183,7 @@ Refers to: OAuth 2.0, a standard protocol for authorization. See [oauth.net/2/]( ## OpenID Connect -A layer on top of [OAuth](#oauth) which standardises authentication. See [en.wikipedia.org/wiki/OpenID_Connect](https://en.wikipedia.org/wiki/OpenID_Connect). +A layer on top of [OAuth](#oauth) which standardises authentication. See [the Wikipedia article](https://en.wikipedia.org/wiki/OpenID_Connect) for more details. ## OSS @@ -195,7 +195,7 @@ A package in the Node.js ecosystem, often published to a [package registry](#pac ## Package Registry -A service that hosts packages. The most prominent example is [NPM](https://www.npmjs.com/). +A service that hosts [packages](#package). The most prominent example is [NPM](https://www.npmjs.com/). ## Package Role @@ -247,7 +247,7 @@ An abstraction layer between a search engine and the [Backstage Search](#search) ## Refresh Token -A string that an [OAuth](#oauth) client can use to get a new access token. +A special token that an [OAuth](#oauth) client can use to get a new [access token](#access-token) when the latter expires. https://oauth.net/2/refresh-tokens/ @@ -263,7 +263,7 @@ See [User Role](#User-Role). ## Scope -A string that describes a certain type of access that can be granted to a user using OAuth. +A string that describes a certain type of access that can be granted to a user using OAuth, usually in conjunction with [access tokens](#access-token). ## Search @@ -307,4 +307,4 @@ A purpose for which a [user role](#User-Role) interacts with Backstage. Related ## User Role -A class of Backspace user for purposes of analyzing [use cases](#use-case). One of: evaluator; administrator; developer; integrator; and contributor. +A class of Backstage user for purposes of analyzing [use cases](#use-case). One of: evaluator; administrator; developer; integrator; and contributor. From 312257e1a1bfa85298878db72b98dd25cfb9cc4e Mon Sep 17 00:00:00 2001 From: Aramis Date: Mon, 29 Jan 2024 14:49:31 -0500 Subject: [PATCH 126/468] add better headers for differentiating similar terms and add more definitions Signed-off-by: Aramis --- docs/local-dev/cli-build-system.md | 2 +- docs/references/glossary.md | 62 +++++++++++++++++++----------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/docs/local-dev/cli-build-system.md b/docs/local-dev/cli-build-system.md index a95db95f85..ddcfa44cf5 100644 --- a/docs/local-dev/cli-build-system.md +++ b/docs/local-dev/cli-build-system.md @@ -311,7 +311,7 @@ The frontend production bundling creates your typical web content [bundle](../references/glossary.md#bundle), all contained within a single folder, ready for static serving. It is used when building packages with the `'frontend'` role, and unlike the development bundling there is no way to -build a production [bundle](../references/glossary.md#bundle) of an individual plugin. +build a production bundle of an individual plugin. The output of the bundling process is written to the `dist` folder in the package. Just like the development bundling, the production bundling is based on diff --git a/docs/references/glossary.md b/docs/references/glossary.md index abbb8ce5ef..751b56608d 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -15,7 +15,7 @@ https://oauth.net/2/access-tokens/ Someone responsible for installing and maintaining a Backstage [app](#app) for an organization. A [user role](#user-role). -## API +## API (catalog plugin) In the Backstage [Catalog](#catalog), an API is an [entity](#entity) representing a boundary between two [components](#component). @@ -37,6 +37,12 @@ A platform for creating and deploying [developer portals](#developer-portal), or Backstage is an incubation-stage open source project of the [Cloud Native Computing Foundation](#cloud-native-computing-foundation). +Can also refer to: [Backstage framework](#backstage-framework). + +## Backstage framework + +The actual framework that Backstage [plugins](#plugin) sit on. This spans both the frontend and the backend, and includes core functionality such as declarative integration, config reading, database management, and many more. + ## Bundle A collection of [deployment artifacts](#deployment-artifacts). @@ -47,6 +53,8 @@ Can also be: The output of the bundling process, which brings a collection of [p An organization's portfolio of software products managed in Backstage. +Can also be: The core Backstage plugin that handle ingestion and display of your organizations software products. + ## Cloud Native Computing A set of technologies that "empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach." ([CNCF Cloud Native Definition v1.0](https://github.com/cncf/toc/blob/main/DEFINITION.md)). @@ -65,17 +73,17 @@ Cloud Native Computing Foundation. [OAuth](#oauth) flow where the client receives an [authorization code](#code) that is passed to the backend to be exchanged for an [access token](#access-token) and possibly a [refresh token](#refresh-token). -## Collators +## Collators (search plugin) Collators transform streams of [documents](#documents) into searchable texts. They're usually responsible for the data transformation and definition and collection process for specific [documents](#documents). Part of [Backstage Search](#search). -## Component +## Component (catalog plugin) A software product that is managed in the Backstage [Software Catalog](#software-catalog). A component can be a service, website, library, data pipeline, or any other piece of software managed as a single project. https://backstage.io/docs/features/software-catalog/system-model -## Condition +## Condition (permission plugin) Conditions are used to return a conditional decision from a policy. They contain information about a given entity and restrictions on what types of users can view that entity. @@ -87,7 +95,13 @@ Conditions are used to return a conditional decision from a policy. They contain A volunteer who helps to improve an OSS product such as Backstage. This volunteer effort includes coding, testing, technical writing, user support, and other work. A [user role](#user-role). -## Decorators +## Declarative integration + +A new paradigm for Backstage frontend plugins, allowing definition in config files instead of hosting complete React pages. + +https://backstage.io/docs/frontend-system + +## Decorators (search plugin) A transform stream. Decorators allow you to add additional information to documents outside of the [collator](#collators). They sit between the [collators](#collators) and the [indexers](#indexer) and can add extra fields to documents as they're being collated and indexed. @@ -107,7 +121,7 @@ A [user role](#user-role) defined as someone who uses a Backstage [app](#app). M A centralized system comprising a user interface and database used to facilitate and document all the software projects within an organization. Backstage is both a developer portal and (by virtue of being based on plugins) a platform for creating developer portals. -## Documents +## Documents (search plugin) An abstract concept representing something that can be found by searching for it. A document can represent a software entity, a TechDocs page, etc. Documents are made up of metadata fields, at a minimum -- a title, text, and location (as in a URL). Part of [Backstage Search](#search). @@ -129,11 +143,11 @@ Someone who assesses whether Backstage is a suitable solution for their organiza A [JWT](#jwt) used to prove a user's identity, containing for example the user's email. Part of [OpenID Connect](#openid-connect). -## Index +## Index (search plugin) An index is a collection of [documents](#documents) of a given type. Part of [Backstage Search](#search). -## Indexer +## Indexer (search plugin) A write stream of [documents](#documents). Part of [Backstage Search](#search). @@ -209,21 +223,13 @@ Any action that a user performs within Backstage may be represented as a permiss https://backstage.io/docs/permissions/overview -## Permission Resource - -Not to be confused with [Software Catalog resources](#resource). Permission resources represent the objects that users interact with and that can be permissioned. - -## Permission Rule - -Rules are predicate-based controls that tap into a [resource](#permission-resource)'s data. - ## Persona Alternative term for a [User Role](#user-role). ## Plugin -A module in Backstage that adds a feature. All functionality in Backstage, even the core features, are implemented as plugins. +A module in Backstage that adds a feature. All functionality outside of [the Backstage framework](#backstage-framework), even the core features, are implemented as plugins. ## Policy @@ -231,7 +237,7 @@ User [permissions](#permission) are authorized by a central, user-defined [permi ## Policy decision -Two important responsibilities of any authorization system are to decide if a user can do something, and to enforce that decision. In the Backstage permission framework, policies are responsible for decisions and plugins (typically backends) are responsible for enforcing them. +Two important responsibilities of any authorization system are to decide if a user can do something, and to enforce that decision. In the Backstage permission framework, [policies](#policy) are responsible for decisions and [plugins](#plugin) (typically backends) are responsible for enforcing them. ## Popup @@ -251,16 +257,28 @@ A special token that an [OAuth](#oauth) client can use to get a new [access toke https://oauth.net/2/refresh-tokens/ -## Resource +## Resource (catalog plugin) In the Backstage Catalog, an [entity](#entity) that represents a piece of physical or virtual infrastructure, for example a database, required by a component. https://backstage.io/docs/features/software-catalog/system-model +## Resource (permission plugin) + +Not to be confused with [Software Catalog resources](#resource-catalog-plugin). Permission resources represent the objects that users interact with and that can be permissioned. + +## Rule + +Rules are predicate-based controls that tap into a [resource](#resource-permission-plugin)'s data. + ## Role See [User Role](#User-Role). +## Scaffolder + +Known as [Software Templates](#software-templates). + ## Scope A string that describes a certain type of access that can be granted to a user using OAuth, usually in conjunction with [access tokens](#access-token). @@ -269,7 +287,7 @@ A string that describes a certain type of access that can be granted to a user u A Backstage plugin that provides a framework for searching a Backstage [app](#app), including the [Software Catalog](#Software-Catalog) and [TechDocs](#TechDocs). A core feature of Backstage. -## Search Engines +## Search Engine Existing search technology that [Backstage Search](#search) can take advantage of through its modular design. Lunr is the default search in [Backstage Search](#search). Part of [Backstage Search](#search). @@ -279,7 +297,7 @@ A Backstage plugin that provides a framework to keep track of ownership and meta ## Software Templates -A Backstage plugin with which to create [components](#component) in Backstage. A core feature of Backstage. +A Backstage plugin with which to create [components](#component) in Backstage. A core feature of Backstage. Also known as the scaffolder. Can also refer to: A "skeleton" software project created and managed in the Backstage Software Templates tool. @@ -289,7 +307,7 @@ In the Backspace Catalog, a system is a collection of [entities](#entity) that c https://backstage.io/docs/features/software-catalog/system-model -## Task +## Task (use cases) A low-level step-by-step [Procedure](#Procedure). From e221a0b285500e5c1ce269aefdf7376f7a3ddb63 Mon Sep 17 00:00:00 2001 From: Aramis Date: Mon, 29 Jan 2024 14:51:56 -0500 Subject: [PATCH 127/468] add more header clarifications Signed-off-by: Aramis --- docs/references/glossary.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/references/glossary.md b/docs/references/glossary.md index 751b56608d..c5d991292a 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -223,7 +223,7 @@ Any action that a user performs within Backstage may be represented as a permiss https://backstage.io/docs/permissions/overview -## Persona +## Persona (use cases) Alternative term for a [User Role](#user-role). @@ -231,11 +231,11 @@ Alternative term for a [User Role](#user-role). A module in Backstage that adds a feature. All functionality outside of [the Backstage framework](#backstage-framework), even the core features, are implemented as plugins. -## Policy +## Policy (permission plugin) User [permissions](#permission) are authorized by a central, user-defined [permission](#permission) policy. At a high level, a policy is a function that receives a Backstage user and [permission](#permission), and returns a decision to allow or deny. Policies are expressed as code, which decouples the framework from any particular [authorization](#authorization) model, like role-based access control (RBAC) or attribute-based access control (ABAC). -## Policy decision +## Policy decision (permission plugin) Two important responsibilities of any authorization system are to decide if a user can do something, and to enforce that decision. In the Backstage permission framework, [policies](#policy) are responsible for decisions and [plugins](#plugin) (typically backends) are responsible for enforcing them. @@ -243,11 +243,11 @@ Two important responsibilities of any authorization system are to decide if a us A separate browser window opened on top of the previous one. -## Procedure +## Procedure (use cases) A set of actions that accomplish a goal, usually as part of a [use case](#Use-Case). A procedure can be high-level, containing other procedures, or can be as simple as a single [task](#Task). -## Query Translators +## Query Translators (search plugin) An abstraction layer between a search engine and the [Backstage Search](#search) backend. Allows for translation into queries against your search engine. Part of [Backstage Search](#search). @@ -267,7 +267,7 @@ https://backstage.io/docs/features/software-catalog/system-model Not to be confused with [Software Catalog resources](#resource-catalog-plugin). Permission resources represent the objects that users interact with and that can be permissioned. -## Rule +## Rule (permission plugin) Rules are predicate-based controls that tap into a [resource](#resource-permission-plugin)'s data. From 1c3cb3b2e53ca24a02782bf3057eae1536653198 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Mon, 29 Jan 2024 14:43:29 -0500 Subject: [PATCH 128/468] log warning on duplicate cluster names Signed-off-by: Jamie Klassen --- .changeset/tiny-donuts-drive.md | 6 ++ .../src/cluster-locator/index.test.ts | 57 +++++++++++++++++++ .../src/cluster-locator/index.ts | 30 ++++++++-- .../src/service/KubernetesBuilder.ts | 1 + 4 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 .changeset/tiny-donuts-drive.md diff --git a/.changeset/tiny-donuts-drive.md b/.changeset/tiny-donuts-drive.md new file mode 100644 index 0000000000..aadac7a276 --- /dev/null +++ b/.changeset/tiny-donuts-drive.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Backstage will log a warning whenever duplicate cluster names are detected -- +even if clusters sharing the same name come from separate locators. diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts index beb9fc9cfe..fcdac32ecf 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.test.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { getVoidLogger } from '@backstage/backend-common'; import { Config, ConfigReader } from '@backstage/config'; import { CatalogApi } from '@backstage/catalog-client'; import { ANNOTATION_KUBERNETES_AUTH_PROVIDER } from '@backstage/plugin-kubernetes-common'; @@ -60,6 +61,7 @@ describe('getCombinedClusterSupplier', () => { config, catalogApi, mockStrategy, + getVoidLogger(), ); const result = await clusterSupplier.getClusters(); @@ -99,9 +101,64 @@ describe('getCombinedClusterSupplier', () => { config, catalogApi, new DispatchStrategy({ authStrategyMap: {} }), + getVoidLogger(), ), ).toThrow( new Error('Unsupported kubernetes.clusterLocatorMethods: "magic"'), ); }); + + it('logs a warning when two clusters have the same name', async () => { + const logger = getVoidLogger(); + const warn = jest.spyOn(logger, 'warn'); + const config: Config = new ConfigReader( + { + kubernetes: { + clusterLocatorMethods: [ + { + type: 'config', + clusters: [ + { name: 'cluster', url: 'url', authProvider: 'authProvider' }, + ], + }, + { type: 'catalog' }, + ], + }, + }, + 'ctx', + ); + const mockStrategy: jest.Mocked = { + getCredential: jest.fn(), + validateCluster: jest.fn().mockReturnValue([]), + presentAuthMetadata: jest.fn(), + }; + catalogApi = { + getEntities: jest.fn().mockResolvedValue({ + items: [{ metadata: { annotations: {}, name: 'cluster' } }], + }), + getEntitiesByRefs: jest.fn(), + queryEntities: jest.fn(), + getEntityAncestors: jest.fn(), + getEntityByRef: jest.fn(), + removeEntityByUid: jest.fn(), + refreshEntity: jest.fn(), + getEntityFacets: jest.fn(), + getLocationById: jest.fn(), + getLocationByRef: jest.fn(), + addLocation: jest.fn(), + removeLocationById: jest.fn(), + getLocationByEntity: jest.fn(), + validateEntity: jest.fn(), + }; + + const clusterSupplier = getCombinedClusterSupplier( + config, + catalogApi, + mockStrategy, + logger, + ); + await clusterSupplier.getClusters(); + + expect(warn).toHaveBeenCalledWith(`Duplicate cluster name 'cluster'`); + }); }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/index.ts b/plugins/kubernetes-backend/src/cluster-locator/index.ts index 03a459b634..2dfd3178f7 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/index.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/index.ts @@ -14,21 +14,25 @@ * limitations under the License. */ +import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { Duration } from 'luxon'; +import { Logger } from 'winston'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; import { AuthenticationStrategy } from '../auth/types'; import { ConfigClusterLocator } from './ConfigClusterLocator'; import { GkeClusterLocator } from './GkeClusterLocator'; import { CatalogClusterLocator } from './CatalogClusterLocator'; -import { CatalogApi } from '@backstage/catalog-client'; import { LocalKubectlProxyClusterLocator } from './LocalKubectlProxyLocator'; class CombinedClustersSupplier implements KubernetesClustersSupplier { - constructor(readonly clusterSuppliers: KubernetesClustersSupplier[]) {} + constructor( + readonly clusterSuppliers: KubernetesClustersSupplier[], + readonly logger: Logger, + ) {} async getClusters(): Promise { - return await Promise.all( + const clusters = await Promise.all( this.clusterSuppliers.map(supplier => supplier.getClusters()), ) .then(res => { @@ -37,6 +41,23 @@ class CombinedClustersSupplier implements KubernetesClustersSupplier { .catch(e => { throw e; }); + return this.warnDuplicates(clusters); + } + + private warnDuplicates(clusters: ClusterDetails[]): ClusterDetails[] { + const clusterNames = new Set(); + const duplicatedNames = new Set(); + for (const clusterName of clusters.map(c => c.name)) { + if (clusterNames.has(clusterName)) { + duplicatedNames.add(clusterName); + } else { + clusterNames.add(clusterName); + } + } + for (const clusterName of duplicatedNames) { + this.logger.warn(`Duplicate cluster name '${clusterName}'`); + } + return clusters; } } @@ -44,6 +65,7 @@ export const getCombinedClusterSupplier = ( rootConfig: Config, catalogClient: CatalogApi, authStrategy: AuthenticationStrategy, + logger: Logger, refreshInterval: Duration | undefined = undefined, ): KubernetesClustersSupplier => { const clusterSuppliers = rootConfig @@ -72,5 +94,5 @@ export const getCombinedClusterSupplier = ( } }); - return new CombinedClustersSupplier(clusterSuppliers); + return new CombinedClustersSupplier(clusterSuppliers, logger); }; diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts index 81cc3d471d..8cc9b8a8b6 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.ts @@ -243,6 +243,7 @@ export class KubernetesBuilder { config, this.env.catalogApi, new DispatchStrategy({ authStrategyMap: this.getAuthStrategyMap() }), + this.env.logger, refreshInterval, ); From 54353b124f18186fd3ad197c10bd2f8ec15b2762 Mon Sep 17 00:00:00 2001 From: Luis Komatsu Date: Fri, 5 Jan 2024 16:07:05 -0300 Subject: [PATCH 129/468] feat: markdown code no word-break Signed-off-by: Luis Komatsu --- .../techdocs/src/reader/transformers/styles/rules/typeset.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts index 4c1eef1ea4..cc7ba6bace 100644 --- a/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts +++ b/plugins/techdocs/src/reader/transformers/styles/rules/typeset.ts @@ -115,4 +115,8 @@ ${headings.reduce((style, heading) => { .md-typeset pre > code::-webkit-scrollbar-thumb:hover { background-color: hsla(0, 0%, 0%, 0.87); } + +.md-typeset code { + word-break: keep-all; +} `; From af4d147ef7a7395a8d9b3e965e36e5e4b80fe738 Mon Sep 17 00:00:00 2001 From: Luis Komatsu Date: Tue, 9 Jan 2024 14:42:42 -0300 Subject: [PATCH 130/468] fix: changeset Signed-off-by: Luis Komatsu --- .changeset/tender-flowers-collect.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tender-flowers-collect.md diff --git a/.changeset/tender-flowers-collect.md b/.changeset/tender-flowers-collect.md new file mode 100644 index 0000000000..ea26f3b69a --- /dev/null +++ b/.changeset/tender-flowers-collect.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': minor +--- + +code tag css style changed to avoid word break From f643e910f98af60327b8714a57e078352d338316 Mon Sep 17 00:00:00 2001 From: Luis Komatsu Date: Mon, 29 Jan 2024 17:01:19 -0300 Subject: [PATCH 131/468] fix: changeset description Signed-off-by: Luis Komatsu --- .changeset/tender-flowers-collect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/tender-flowers-collect.md b/.changeset/tender-flowers-collect.md index ea26f3b69a..dfbf96a52c 100644 --- a/.changeset/tender-flowers-collect.md +++ b/.changeset/tender-flowers-collect.md @@ -2,4 +2,4 @@ '@backstage/plugin-techdocs': minor --- -code tag css style changed to avoid word break +Updated the styling for tags to avoid word break. From aa91cd69acffd06db4febf10768d9003f1fd8426 Mon Sep 17 00:00:00 2001 From: Brian Hudson Date: Mon, 29 Jan 2024 15:54:06 -0500 Subject: [PATCH 132/468] Fix safeEntityName createOrMergeEntity was replacing capital letters with -. For example "@internal/pluginApi" would result in "internal-plugin-pi". Updated to correctly handle capital letters, prefixing them with - unless they are the first character. For example "@internal/pluginApi" would result in "internal-plugin-api". Signed-off-by: Brian Hudson m> --- .changeset/calm-onions-exercise.md | 5 ++++ .../generate-catalog-info.ts | 26 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .changeset/calm-onions-exercise.md diff --git a/.changeset/calm-onions-exercise.md b/.changeset/calm-onions-exercise.md new file mode 100644 index 0000000000..f1c58a173f --- /dev/null +++ b/.changeset/calm-onions-exercise.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Resolved an issue with generate-catalog-info where it was replacing upper case characters with -. diff --git a/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts index b8a473c67b..c15f478bd0 100644 --- a/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts +++ b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts @@ -165,8 +165,17 @@ async function fixCatalogInfoYaml(options: FixOptions) { relativePath('.', yamlPath), ); const safeName = packageJson.name - .replace(/[^a-z0-9_\-\.]+/g, '-') - .replace(/^[^a-z0-9]|[^a-z0-9]$/g, ''); + .replace(/^[^\w\s]|[^a-z0-9]$/g, '') + .replace(/[^A-Za-z0-9_\-.]+/g, '-') + .replace(/([A-Z])/g, (_, letter, index, original) => { + if (index !== 0) { + const previousChar = original[index - 1]; + if (previousChar !== '-') { + return `-${letter.toLowerCase()}`; + } + } + return letter.toLowerCase(); + }); let yamlJson: BackstagePackageEntity; try { @@ -241,8 +250,17 @@ function createOrMergeEntity( existingEntity: BackstagePackageEntity | Record = {}, ): BackstagePackageEntity { const safeEntityName = packageJson.name - .replace(/[^a-z0-9_\-\.]+/g, '-') - .replace(/^[^a-z0-9]|[^a-z0-9]$/g, ''); + .replace(/^[^\w\s]|[^a-z0-9]$/g, '') + .replace(/[^A-Za-z0-9_\-.]+/g, '-') + .replace(/([A-Z])/g, (_, letter, index, original) => { + if (index !== 0) { + const previousChar = original[index - 1]; + if (previousChar !== '-') { + return `-${letter.toLowerCase()}`; + } + } + return letter.toLowerCase(); + }); return { ...existingEntity, From 0269b015766b65e98a6e44200135e24e88c37959 Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Mon, 8 Jan 2024 19:37:29 +0100 Subject: [PATCH 133/468] refactor(tech-insights): reorg exports Signed-off-by: Patrick Jungermann --- .../src/index.ts | 10 ++----- .../src/service/index.ts | 25 ++++++++++++++++++ plugins/tech-insights-backend/src/index.ts | 19 +------------- .../src/service/fact/factRetrievers/index.ts | 3 ++- .../src/service/fact/index.ts | 21 +++++++++++++++ .../src/service/index.ts | 26 +++++++++++++++++++ .../src/service/persistence/index.ts | 21 +++++++++++++++ 7 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 plugins/tech-insights-backend-module-jsonfc/src/service/index.ts create mode 100644 plugins/tech-insights-backend/src/service/fact/index.ts create mode 100644 plugins/tech-insights-backend/src/service/index.ts create mode 100644 plugins/tech-insights-backend/src/service/persistence/index.ts diff --git a/plugins/tech-insights-backend-module-jsonfc/src/index.ts b/plugins/tech-insights-backend-module-jsonfc/src/index.ts index 1239211f73..6462efc403 100644 --- a/plugins/tech-insights-backend-module-jsonfc/src/index.ts +++ b/plugins/tech-insights-backend-module-jsonfc/src/index.ts @@ -13,15 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { - JsonRulesEngineFactCheckerFactory, - JsonRulesEngineFactChecker, -} from './service/JsonRulesEngineFactChecker'; + export { JSON_RULE_ENGINE_CHECK_TYPE } from './constants'; -export type { - JsonRulesEngineFactCheckerFactoryOptions, - JsonRulesEngineFactCheckerOptions, -} from './service/JsonRulesEngineFactChecker'; +export * from './service'; export type { JsonRuleCheckResponse, JsonRuleBooleanCheckResult, diff --git a/plugins/tech-insights-backend-module-jsonfc/src/service/index.ts b/plugins/tech-insights-backend-module-jsonfc/src/service/index.ts new file mode 100644 index 0000000000..5670c138a1 --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/src/service/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2024 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 { + JsonRulesEngineFactCheckerFactory, + JsonRulesEngineFactChecker, +} from './JsonRulesEngineFactChecker'; + +export type { + JsonRulesEngineFactCheckerFactoryOptions, + JsonRulesEngineFactCheckerOptions, +} from './JsonRulesEngineFactChecker'; diff --git a/plugins/tech-insights-backend/src/index.ts b/plugins/tech-insights-backend/src/index.ts index e9e037cce1..d6b845a78d 100644 --- a/plugins/tech-insights-backend/src/index.ts +++ b/plugins/tech-insights-backend/src/index.ts @@ -14,21 +14,4 @@ * limitations under the License. */ -export * from './service/router'; -export type { RouterOptions } from './service/router'; - -export { buildTechInsightsContext } from './service/techInsightsContextBuilder'; -export { initializePersistenceContext } from './service/persistence/persistenceContext'; -export type { - TechInsightsOptions, - TechInsightsContext, -} from './service/techInsightsContextBuilder'; -export type { FactRetrieverEngine } from './service/fact/FactRetrieverEngine'; -export type { - PersistenceContext, - PersistenceContextOptions, -} from './service/persistence/persistenceContext'; -export { createFactRetrieverRegistration } from './service/fact/createFactRetriever'; -export type { FactRetrieverRegistry } from './service/fact/FactRetrieverRegistry'; -export type { FactRetrieverRegistrationOptions } from './service/fact/createFactRetriever'; -export * from './service/fact/factRetrievers'; +export * from './service'; diff --git a/plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts b/plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts index 587db8c4b9..5983a82e1d 100644 --- a/plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts +++ b/plugins/tech-insights-backend/src/service/fact/factRetrievers/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export * from './entityOwnershipFactRetriever'; + export * from './entityMetadataFactRetriever'; +export * from './entityOwnershipFactRetriever'; export * from './techdocsFactRetriever'; diff --git a/plugins/tech-insights-backend/src/service/fact/index.ts b/plugins/tech-insights-backend/src/service/fact/index.ts new file mode 100644 index 0000000000..0eeb498481 --- /dev/null +++ b/plugins/tech-insights-backend/src/service/fact/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2024 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 { createFactRetrieverRegistration } from './createFactRetriever'; +export type { FactRetrieverRegistrationOptions } from './createFactRetriever'; +export type { FactRetrieverEngine } from './FactRetrieverEngine'; +export type { FactRetrieverRegistry } from './FactRetrieverRegistry'; +export * from './factRetrievers'; diff --git a/plugins/tech-insights-backend/src/service/index.ts b/plugins/tech-insights-backend/src/service/index.ts new file mode 100644 index 0000000000..5306ad26d6 --- /dev/null +++ b/plugins/tech-insights-backend/src/service/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2024 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 * from './fact'; +export * from './persistence'; +export * from './router'; +export type { RouterOptions } from './router'; + +export { buildTechInsightsContext } from './techInsightsContextBuilder'; +export type { + TechInsightsOptions, + TechInsightsContext, +} from './techInsightsContextBuilder'; diff --git a/plugins/tech-insights-backend/src/service/persistence/index.ts b/plugins/tech-insights-backend/src/service/persistence/index.ts new file mode 100644 index 0000000000..64cc876026 --- /dev/null +++ b/plugins/tech-insights-backend/src/service/persistence/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2024 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 { initializePersistenceContext } from './persistenceContext'; +export type { + PersistenceContext, + PersistenceContextOptions, +} from './persistenceContext'; From bc72782dd66aef5bb52d434873f4182c0cb1b5c6 Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Mon, 8 Jan 2024 21:22:48 +0100 Subject: [PATCH 134/468] feat(tech-insights): define fact checks in app-config.yaml Allow to define JSON checks in `app-config.yaml` as `techInsights.factChecker.checks`. Signed-off-by: Patrick Jungermann --- .changeset/fuzzy-comics-collect.md | 27 +++ .../README.md | 24 ++- .../api-report.md | 6 + .../config.json | 183 ++++++++++++++++++ .../package.json | 6 +- .../src/service/JsonRulesEngineFactChecker.ts | 32 ++- .../src/service/config.test.ts | 164 ++++++++++++++++ .../src/service/config.ts | 162 ++++++++++++++++ 8 files changed, 593 insertions(+), 11 deletions(-) create mode 100644 .changeset/fuzzy-comics-collect.md create mode 100644 plugins/tech-insights-backend-module-jsonfc/config.json create mode 100644 plugins/tech-insights-backend-module-jsonfc/src/service/config.test.ts create mode 100644 plugins/tech-insights-backend-module-jsonfc/src/service/config.ts diff --git a/.changeset/fuzzy-comics-collect.md b/.changeset/fuzzy-comics-collect.md new file mode 100644 index 0000000000..5ce6b3c967 --- /dev/null +++ b/.changeset/fuzzy-comics-collect.md @@ -0,0 +1,27 @@ +--- +'@backstage/plugin-tech-insights-backend-module-jsonfc': patch +--- + +Support loading `TechInsightsJsonRuleCheck` instances from config. + +Uses the check `id` as key. + +Example: + +```yaml title="app-config.yaml" +techInsights: + factChecker: + checks: + groupOwnerCheck: + type: json-rules-engine + name: Group Owner Check + description: Verifies that a group has been set as the spec.owner for this entity + factIds: + - entityOwnershipFactRetriever + rule: + conditions: + all: + - fact: hasGroupOwner + operator: equal + value: true +``` diff --git a/plugins/tech-insights-backend-module-jsonfc/README.md b/plugins/tech-insights-backend-module-jsonfc/README.md index 5940d39b15..3f9e91408c 100644 --- a/plugins/tech-insights-backend-module-jsonfc/README.md +++ b/plugins/tech-insights-backend-module-jsonfc/README.md @@ -46,7 +46,7 @@ By default this implementation comes with an in-memory storage to store checks. ``` -## Adding checks +## Adding checks in code Checks for this FactChecker are constructed as [`json-rules-engine` compatible JSON rules](https://github.com/CacheControl/json-rules-engine/blob/master/docs/rules.md#conditions). A check could look like the following for example: @@ -86,6 +86,28 @@ export const exampleCheck: TechInsightJsonRuleCheck = { }; ``` +## Adding checks in config + +Example: + +```yaml title="app-config.yaml" +techInsights: + factChecker: + checks: + groupOwnerCheck: + type: json-rules-engine + name: Group Owner Check + description: Verifies that a group has been set as the spec.owner for this entity + factIds: + - entityOwnershipFactRetriever + rule: + conditions: + all: + - fact: hasGroupOwner + operator: equal + value: true +``` + ### More than one `factIds` for a check. When more than one is supplied, the requested fact **MUST** be present in at least one of the fact retrievers. diff --git a/plugins/tech-insights-backend-module-jsonfc/api-report.md b/plugins/tech-insights-backend-module-jsonfc/api-report.md index 5fb29d275d..d28ee28f8d 100644 --- a/plugins/tech-insights-backend-module-jsonfc/api-report.md +++ b/plugins/tech-insights-backend-module-jsonfc/api-report.md @@ -6,6 +6,7 @@ import { BooleanCheckResult } from '@backstage/plugin-tech-insights-common'; import { CheckResponse } from '@backstage/plugin-tech-insights-common'; import { CheckValidationResponse } from '@backstage/plugin-tech-insights-node'; +import { Config } from '@backstage/config'; import { FactChecker } from '@backstage/plugin-tech-insights-node'; import { Logger } from 'winston'; import { Operator } from 'json-rules-engine'; @@ -63,6 +64,11 @@ export class JsonRulesEngineFactCheckerFactory { constructor(options: JsonRulesEngineFactCheckerFactoryOptions); // (undocumented) construct(repository: TechInsightsStore): JsonRulesEngineFactChecker; + // (undocumented) + static fromConfig( + config: Config, + options: Omit, + ): JsonRulesEngineFactCheckerFactory; } // @public diff --git a/plugins/tech-insights-backend-module-jsonfc/config.json b/plugins/tech-insights-backend-module-jsonfc/config.json new file mode 100644 index 0000000000..b95b5e52bb --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/config.json @@ -0,0 +1,183 @@ +{ + "$schema": "https://backstage.io/schema/config-v1", + "type": "object", + "properties": { + "techInsights": { + "type": "object", + "properties": { + "factChecker": { + "type": "object", + "properties": { + "checks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/check" + } + } + } + } + } + } + }, + "$defs": { + "check": { + "type": "object", + "required": ["type", "name", "description", "factIds", "rule"], + "properties": { + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "factIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "rule": { + "$ref": "#/$defs/rule" + } + } + }, + "conditionProperties": { + "type": "object", + "required": ["fact", "operator", "value"], + "properties": { + "fact": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "value": { + "oneOf": [ + { + "type": "object", + "required": ["fact"], + "properties": { + "fact": { + "type": "string" + } + } + }, + {} + ] + }, + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "priority": { + "type": "number" + }, + "params": { + "type": "object", + "additionalProperties": true + } + } + }, + "conditionReference": { + "type": "object", + "required": ["condition"], + "properties": { + "condition": { + "type": "string" + }, + "name": { + "type": "string" + }, + "priority": { + "type": "number" + } + } + }, + "nestedCondition": { + "oneOf": [ + { + "$ref": "#/$defs/conditionProperties" + }, + { + "$ref": "#/$defs/topLevelCondition" + } + ] + }, + "rule": { + "type": "string", + "required": ["conditions"], + "properties": { + "conditions": { + "$ref": "#/$defs/topLevelCondition" + }, + "name": { + "type": "string" + }, + "priority": { + "type": "number" + }, + "successMetadata": { + "type": "object", + "additionalProperties": true + }, + "failureMetadata": { + "type": "object", + "additionalProperties": true + } + } + }, + "topLevelCondition": { + "oneOf": [ + { + "type": "object", + "required": ["all"], + "properties": { + "all": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/$defs/nestedCondition" + } + ] + } + } + } + }, + { + "type": "object", + "required": ["any"], + "properties": { + "any": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/$defs/nestedCondition" + } + ] + } + } + } + }, + { + "type": "object", + "required": ["not"], + "properties": { + "not": { + "$ref": "#/$defs/nestedCondition" + } + } + }, + { + "$ref": "#/$defs/conditionReference" + } + ] + } + } +} diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index 1c34a2157b..431a7152d4 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -34,9 +34,11 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-tech-insights-common": "workspace:^", "@backstage/plugin-tech-insights-node": "workspace:^", + "@backstage/types": "workspace:^", "ajv": "^8.10.0", "json-rules-engine": "^6.1.2", "lodash": "^4.17.21", @@ -47,6 +49,8 @@ "@backstage/cli": "workspace:^" }, "files": [ + "config.json", "dist" - ] + ], + "configSchema": "config.json" } diff --git a/plugins/tech-insights-backend-module-jsonfc/src/service/JsonRulesEngineFactChecker.ts b/plugins/tech-insights-backend-module-jsonfc/src/service/JsonRulesEngineFactChecker.ts index 6c1c559fc4..51609bc916 100644 --- a/plugins/tech-insights-backend-module-jsonfc/src/service/JsonRulesEngineFactChecker.ts +++ b/plugins/tech-insights-backend-module-jsonfc/src/service/JsonRulesEngineFactChecker.ts @@ -14,7 +14,9 @@ * limitations under the License. */ -import { JsonRuleBooleanCheckResult, TechInsightJsonRuleCheck } from '../types'; +import { Config } from '@backstage/config'; +import { isError } from '@backstage/errors'; +import { FactResponse } from '@backstage/plugin-tech-insights-common'; import { FactChecker, TechInsightCheckRegistry, @@ -22,20 +24,20 @@ import { TechInsightsStore, CheckValidationResponse, } from '@backstage/plugin-tech-insights-node'; -import { FactResponse } from '@backstage/plugin-tech-insights-common'; +import Ajv, { SchemaObject } from 'ajv'; import { Engine, EngineResult, Operator, TopLevelCondition, } from 'json-rules-engine'; -import { DefaultCheckRegistry } from './CheckRegistry'; -import { Logger } from 'winston'; import { pick } from 'lodash'; -import Ajv, { SchemaObject } from 'ajv'; -import * as validationSchema from './validation-schema.json'; +import { Logger } from 'winston'; import { JSON_RULE_ENGINE_CHECK_TYPE } from '../constants'; -import { isError } from '@backstage/errors'; +import { JsonRuleBooleanCheckResult, TechInsightJsonRuleCheck } from '../types'; +import { DefaultCheckRegistry } from './CheckRegistry'; +import { readChecksFromConfig } from './config'; +import * as validationSchema from './validation-schema.json'; const noopEvent = { type: 'noop', @@ -340,7 +342,7 @@ export class JsonRulesEngineFactChecker * * Implementation of checkRegistry is optional. * If there is a need to use persistent storage for checks, it is recommended to inject a storage implementation here. - * Otherwise an in-memory option is instantiated and used. + * Otherwise, an in-memory option is instantiated and used. */ export type JsonRulesEngineFactCheckerFactoryOptions = { checks: TechInsightJsonRuleCheck[]; @@ -354,7 +356,7 @@ export type JsonRulesEngineFactCheckerFactoryOptions = { * * Factory to construct JsonRulesEngineFactChecker * Can be constructed with optional implementation of CheckInsightCheckRegistry if needed. - * Otherwise defaults to using in-memory CheckRegistry + * Otherwise, defaults to using in-memory CheckRegistry. */ export class JsonRulesEngineFactCheckerFactory { private readonly checks: TechInsightJsonRuleCheck[]; @@ -362,6 +364,18 @@ export class JsonRulesEngineFactCheckerFactory { private readonly checkRegistry?: TechInsightCheckRegistry; private readonly operators?: Operator[]; + static fromConfig( + config: Config, + options: Omit, + ): JsonRulesEngineFactCheckerFactory { + const checks = readChecksFromConfig(config); + + return new JsonRulesEngineFactCheckerFactory({ + ...options, + checks, + }); + } + constructor(options: JsonRulesEngineFactCheckerFactoryOptions) { this.logger = options.logger; this.checks = options.checks; diff --git a/plugins/tech-insights-backend-module-jsonfc/src/service/config.test.ts b/plugins/tech-insights-backend-module-jsonfc/src/service/config.test.ts new file mode 100644 index 0000000000..732cc7f731 --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/src/service/config.test.ts @@ -0,0 +1,164 @@ +/* + * Copyright 2024 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 { ConfigReader } from '@backstage/config'; +import { JSON_RULE_ENGINE_CHECK_TYPE } from '../constants'; +import { readChecksFromConfig } from './config'; + +describe('config', () => { + describe('readChecksFromConfig', () => { + it('no config return empty checks array', () => { + const config = new ConfigReader({}); + const checks = readChecksFromConfig(config); + + expect(checks).toHaveLength(0); + }); + + it('empty checks config return empty checks array', () => { + const config = new ConfigReader({ + techInsights: { + factChecker: {}, + }, + }); + const checks = readChecksFromConfig(config); + + expect(checks).toHaveLength(0); + }); + + it('with checks return parsed checks', () => { + const config = new ConfigReader({ + techInsights: { + factChecker: { + checks: { + fooCheck: { + type: JSON_RULE_ENGINE_CHECK_TYPE, + name: 'Foo Check', + description: 'Verifies foo', + factIds: ['fooFactRetriever'], + rule: { + conditions: { + all: [ + { + fact: 'numFoo', + operator: 'greaterThanInclusive', + value: 1, + }, + { + fact: 'hasFoo', + operator: 'equal', + value: true, + }, + ], + }, + }, + }, + barCheck: { + type: JSON_RULE_ENGINE_CHECK_TYPE, + name: 'Bar Check', + description: 'Verifies bar', + factIds: ['barFactRetriever'], + rule: { + conditions: { + any: [ + { + fact: 'barEnabled', + operator: 'equal', + value: false, + }, + { + fact: 'hasBar', + operator: 'equal', + value: true, + }, + ], + }, + }, + }, + bazCheck: { + type: JSON_RULE_ENGINE_CHECK_TYPE, + name: 'Baz Check', + description: 'Verifies baz', + factIds: ['bazFactRetriever'], + rule: { + conditions: { + not: { + fact: 'bazConfig', + operator: 'equal', + value: { invalid: true }, + }, + }, + }, + }, + }, + }, + }, + }); + + const checks = readChecksFromConfig(config); + + expect(checks).toHaveLength(3); + expect(checks.map(check => check.id)).toEqual([ + 'fooCheck', + 'barCheck', + 'bazCheck', + ]); + + const fooCheck = checks.find(check => check.id === 'fooCheck')!; + expect(fooCheck.name).toEqual('Foo Check'); + expect(fooCheck.rule.conditions).toEqual({ + all: [ + { + fact: 'numFoo', + operator: 'greaterThanInclusive', + value: 1, + }, + { + fact: 'hasFoo', + operator: 'equal', + value: true, + }, + ], + }); + + const barCheck = checks.find(check => check.id === 'barCheck')!; + expect(barCheck.name).toEqual('Bar Check'); + expect(barCheck.rule.conditions).toEqual({ + any: [ + { + fact: 'barEnabled', + operator: 'equal', + value: false, + }, + { + fact: 'hasBar', + operator: 'equal', + value: true, + }, + ], + }); + + const bazCheck = checks.find(check => check.id === 'bazCheck')!; + expect(bazCheck.name).toEqual('Baz Check'); + expect(bazCheck.rule.conditions).toEqual({ + not: { + fact: 'bazConfig', + operator: 'equal', + value: { invalid: true }, + }, + }); + }); + }); +}); diff --git a/plugins/tech-insights-backend-module-jsonfc/src/service/config.ts b/plugins/tech-insights-backend-module-jsonfc/src/service/config.ts new file mode 100644 index 0000000000..5c663d0d41 --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/src/service/config.ts @@ -0,0 +1,162 @@ +/* + * Copyright 2024 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 { TopLevelCondition } from 'json-rules-engine'; +import { Rule, TechInsightJsonRuleCheck } from '../types'; + +// copy of non-exported `ConditionProperties` from 'json-rules-engine' +interface ConditionProperties { + fact: string; + operator: string; + value: { fact: string } | any; + path?: string; + priority?: number; + params?: Record; + name?: string; +} + +// copy of non-exported `NestedCondition` from 'json-rules-engine' +type NestedCondition = ConditionProperties | TopLevelCondition; + +function readRuleConditionProperties(config: Config): ConditionProperties { + const fact = config.getString('fact'); + const name = config.getOptionalString('name'); + const operator = config.getString('operator'); + const params = config.getOptionalConfig('params')?.get>(); + const path = config.getOptionalString('path'); + const priority = config.getOptionalNumber('priority'); + const value: { fact: string } | any = config.get('value'); + + return { + fact, + name, + operator, + params, + path, + priority, + value, + }; +} + +function readRuleNestedCondition(config: Config): NestedCondition { + if (config.has('fact')) { + return readRuleConditionProperties(config); + } + + return readRuleTopLevelCondition(config); +} + +function readRuleTopLevelCondition(config: Config): TopLevelCondition { + const base = { + name: config.getOptionalString('name'), + priority: config.getOptionalNumber('priority'), + }; + + if (config.has('all')) { + const all = config + .getConfigArray('all') + .map(conditionConfig => readRuleNestedCondition(conditionConfig)); + return { + ...base, + all, + }; + } + + if (config.has('any')) { + const any = config + .getConfigArray('any') + .map(conditionConfig => readRuleNestedCondition(conditionConfig)); + return { + ...base, + any, + }; + } + + if (config.has('not')) { + const not = readRuleNestedCondition(config.getConfig('not')); + return { + ...base, + not, + }; + } + + const condition = config.getString('condition'); + + return { + ...base, + condition, + }; +} + +function readRuleFromRuleConfig(config: Config): Rule { + const conditions = readRuleTopLevelCondition(config.getConfig('conditions')); + const name = config.getOptionalString('name'); + const priority = config.getOptionalNumber('priority'); + + return { + conditions, + name, + priority, + }; +} + +function readCheckFromCheckConfig( + id: string, + config: Config, +): TechInsightJsonRuleCheck { + const type = config.getString('type'); + const name = config.getString('name'); + const description = config.getString('description'); + const factIds = config.getStringArray('factIds'); + const successMetadata = config + .getOptionalConfig('successMetadata') + ?.get>(); + const failureMetadata = config + .getOptionalConfig('failureMetadata') + ?.get>(); + const rule = readRuleFromRuleConfig(config.getConfig('rule')); + + return { + description, + factIds, + failureMetadata, + id, + name, + rule, + successMetadata, + type, + }; +} + +export function readChecksFromConfig( + config: Config, +): TechInsightJsonRuleCheck[] { + const key = 'techInsights.factChecker.checks'; + if (!config.has(key)) { + return []; + } + + const checksConfig = config.getConfig(key); + const checks: TechInsightJsonRuleCheck[] = []; + checksConfig.keys().forEach(checkId => { + const checkConfig = checksConfig.getConfig(checkId); + + checks.push(readCheckFromCheckConfig(checkId, checkConfig)); + }); + + return checks; +} From 7201af3445d3b66eb74130ea339d0fbd96c32c02 Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Mon, 8 Jan 2024 23:34:43 +0100 Subject: [PATCH 135/468] feat(tech-insights): add plugin for new backend system A new backend plugin for the tech-insights backend was added and exported as `default` as well as extension points to be used by modules. The plugin can be used like ```ts title="packages/backend/src/index.ts" backend.add(import('@backstage/plugin-tech-insights-backend')); ``` Fact retrievers will be added (built-in or through the extension point), but only registered if the user adds a configuration for it (cadence, etc.). Signed-off-by: Patrick Jungermann --- .changeset/green-dogs-fold.md | 15 ++ plugins/tech-insights-backend/README.md | 25 ++++ plugins/tech-insights-backend/api-report.md | 5 + plugins/tech-insights-backend/config.d.ts | 37 +++++ plugins/tech-insights-backend/package.json | 5 +- plugins/tech-insights-backend/src/index.ts | 1 + .../src/plugin/config.test.ts | 76 +++++++++++ .../src/plugin/config.ts | 94 +++++++++++++ .../tech-insights-backend/src/plugin/index.ts | 17 +++ .../src/plugin/plugin.test.ts | 50 +++++++ .../src/plugin/plugin.ts | 128 ++++++++++++++++++ plugins/tech-insights-node/api-report.md | 24 ++++ plugins/tech-insights-node/package.json | 1 + .../tech-insights-node/src/extensionPoints.ts | 59 ++++++++ plugins/tech-insights-node/src/index.ts | 1 + yarn.lock | 4 + 16 files changed, 541 insertions(+), 1 deletion(-) create mode 100644 .changeset/green-dogs-fold.md create mode 100644 plugins/tech-insights-backend/config.d.ts create mode 100644 plugins/tech-insights-backend/src/plugin/config.test.ts create mode 100644 plugins/tech-insights-backend/src/plugin/config.ts create mode 100644 plugins/tech-insights-backend/src/plugin/index.ts create mode 100644 plugins/tech-insights-backend/src/plugin/plugin.test.ts create mode 100644 plugins/tech-insights-backend/src/plugin/plugin.ts create mode 100644 plugins/tech-insights-node/src/extensionPoints.ts diff --git a/.changeset/green-dogs-fold.md b/.changeset/green-dogs-fold.md new file mode 100644 index 0000000000..c23f7c2a29 --- /dev/null +++ b/.changeset/green-dogs-fold.md @@ -0,0 +1,15 @@ +--- +'@backstage/plugin-tech-insights-backend': patch +'@backstage/plugin-tech-insights-node': patch +--- + +Add support for the new backend system. + +A new backend plugin for the tech-insights backend +was added and exported as `default`. + +You can use it with the new backend system like + +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-tech-insights-backend')); +``` diff --git a/plugins/tech-insights-backend/README.md b/plugins/tech-insights-backend/README.md index 95fa3ccda9..29b374c18c 100644 --- a/plugins/tech-insights-backend/README.md +++ b/plugins/tech-insights-backend/README.md @@ -15,6 +15,31 @@ yarn add --cwd packages/backend @backstage/plugin-tech-insights-backend ### Adding the plugin to your `packages/backend` +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-tech-insights-backend')); +``` + +You can use the extension points [@backstage/plugin-tech-insights-node](../tech-insights-node) +to add your `FactRetriever` or set a `FactCheckerFactory`. + +The built-in `FactRetrievers`: + +- `entityMetadataFactRetriever` +- `entityOwnershipFactRetriever` +- `techdocsFactRetriever` + +`FactRetrievers` only get registered if they get configured: + +```yaml title="app-config.yaml" +techInsights: + factRetrievers: + entityOwnershipFactRetriever: + cadence: '*/15 * * * *' + lifecycle: { timeToLive: { weeks: 2 } } +``` + +### Adding the plugin to your `packages/backend` (old) + You'll need to add the plugin to the router in your `backend` package. You can do this by creating a file called `packages/backend/src/plugins/techInsights.ts`. An example content for `techInsights.ts` could be something like this. diff --git a/plugins/tech-insights-backend/api-report.md b/plugins/tech-insights-backend/api-report.md index 4b6e5b34dc..fd12dfed24 100644 --- a/plugins/tech-insights-backend/api-report.md +++ b/plugins/tech-insights-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; import { Config } from '@backstage/config'; import { Duration } from 'luxon'; @@ -140,5 +141,9 @@ export interface TechInsightsOptions< tokenManager: TokenManager; } +// @public +const techInsightsPlugin: () => BackendFeature; +export default techInsightsPlugin; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/tech-insights-backend/config.d.ts b/plugins/tech-insights-backend/config.d.ts new file mode 100644 index 0000000000..cb3602dc60 --- /dev/null +++ b/plugins/tech-insights-backend/config.d.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2024 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 { HumanDuration } from '@backstage/types'; + +export interface Config { + /** Configuration options for the tech-insights plugin */ + techInsights?: { + /** Configuration options for fact retrievers */ + factRetrievers?: { + /** Configuration for a fact retriever and its registration identified by its name. */ + [name: string]: { + /** A cron expression to indicate when the fact retriever should be triggered. */ + cadence: string; + /** Optional duration of the initial delay. */ + initialDelay?: HumanDuration; + /** Optional lifecycle definition indicating the cleanup logic of facts when this retriever is run. */ + lifecycle?: { timeToLive: HumanDuration } | { maxItems: number }; + /** Optional duration to determine how long the fact retriever should be allowed to run, defaults to 5 minutes. */ + timeout?: HumanDuration; + }; + }; + }; +} diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 50d86931ee..5cadc140bc 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -34,6 +34,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", @@ -63,7 +64,9 @@ "wait-for-expect": "^3.0.2" }, "files": [ + "config.d.ts", "dist", "migrations/**/*.{js,d.ts}" - ] + ], + "configSchema": "config.d.ts" } diff --git a/plugins/tech-insights-backend/src/index.ts b/plugins/tech-insights-backend/src/index.ts index d6b845a78d..2e87a53dfd 100644 --- a/plugins/tech-insights-backend/src/index.ts +++ b/plugins/tech-insights-backend/src/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ +export { techInsightsPlugin as default } from './plugin'; export * from './service'; diff --git a/plugins/tech-insights-backend/src/plugin/config.test.ts b/plugins/tech-insights-backend/src/plugin/config.test.ts new file mode 100644 index 0000000000..09b4954583 --- /dev/null +++ b/plugins/tech-insights-backend/src/plugin/config.test.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2024 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 { ConfigReader } from '@backstage/config'; +import { FactRetriever, TTL } from '@backstage/plugin-tech-insights-node'; +import { createFactRetrieverRegistrationFromConfig } from './config'; + +describe('config', () => { + const mockFactRetriever = jest.fn() as unknown as FactRetriever; + + describe('createFactRetrieverRegistrationFromConfig', () => { + it('no config return undefined', () => { + const config = new ConfigReader({}); + const registration = createFactRetrieverRegistrationFromConfig( + config, + 'any', + mockFactRetriever, + ); + + expect(registration).toBeUndefined(); + }); + + it('no entry for fact retriever return undefined', () => { + const config = new ConfigReader({ + techInsights: { + factRetrievers: {}, + }, + }); + const registration = createFactRetrieverRegistrationFromConfig( + config, + 'any', + mockFactRetriever, + ); + + expect(registration).toBeUndefined(); + }); + + it('with config for fact retriever return registration', () => { + const config = new ConfigReader({ + techInsights: { + factRetrievers: { + any: { + cadence: '*/15 * * * *', + lifecycle: { timeToLive: { weeks: 2 } }, + }, + }, + }, + }); + + const registration = createFactRetrieverRegistrationFromConfig( + config, + 'any', + mockFactRetriever, + ); + + expect(registration).toBeDefined(); + expect(registration!.cadence).toEqual('*/15 * * * *'); + expect((registration!.lifecycle! as TTL).timeToLive).toEqual({ + weeks: 2, + }); + }); + }); +}); diff --git a/plugins/tech-insights-backend/src/plugin/config.ts b/plugins/tech-insights-backend/src/plugin/config.ts new file mode 100644 index 0000000000..25827fbbc5 --- /dev/null +++ b/plugins/tech-insights-backend/src/plugin/config.ts @@ -0,0 +1,94 @@ +/* + * Copyright 2024 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, readDurationFromConfig } from '@backstage/config'; +import { + FactLifecycle, + FactRetriever, + FactRetrieverRegistration, +} from '@backstage/plugin-tech-insights-node'; +import { + createFactRetrieverRegistration, + FactRetrieverRegistrationOptions, +} from '../service'; + +type FactRetrieverConfig = Omit< + FactRetrieverRegistrationOptions, + 'factRetriever' +>; + +function readLifecycleConfig( + config: Config | undefined, +): FactLifecycle | undefined { + if (!config) { + return undefined; + } + + if (config.has('maxItems')) { + return { + maxItems: config.getNumber('maxItems'), + }; + } + + return { + timeToLive: readDurationFromConfig(config.getConfig('timeToLive')), + }; +} + +function readFactRetrieverConfig( + config: Config, + name: string, +): FactRetrieverConfig | undefined { + const factRetrieverConfig = config.getOptionalConfig( + `techInsights.factRetrievers.${name}`, + ); + if (!factRetrieverConfig) { + return undefined; + } + + const cadence = factRetrieverConfig.getString('cadence'); + const initialDelay = factRetrieverConfig.has('initialDelay') + ? readDurationFromConfig(factRetrieverConfig.getConfig('initialDelay')) + : undefined; + const lifecycle = readLifecycleConfig( + factRetrieverConfig.getOptionalConfig('lifecycle'), + ); + const timeout = factRetrieverConfig.has('timeout') + ? readDurationFromConfig(factRetrieverConfig.getConfig('timeout')) + : undefined; + + return { + cadence, + initialDelay, + lifecycle, + timeout, + }; +} + +export function createFactRetrieverRegistrationFromConfig( + config: Config, + name: string, + factRetriever: FactRetriever, +): FactRetrieverRegistration | undefined { + const factRetrieverConfig = readFactRetrieverConfig(config, name); + + return factRetrieverConfig + ? createFactRetrieverRegistration({ + ...factRetrieverConfig, + factRetriever, + }) + : undefined; +} diff --git a/plugins/tech-insights-backend/src/plugin/index.ts b/plugins/tech-insights-backend/src/plugin/index.ts new file mode 100644 index 0000000000..dd13bde435 --- /dev/null +++ b/plugins/tech-insights-backend/src/plugin/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 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 * from './plugin'; diff --git a/plugins/tech-insights-backend/src/plugin/plugin.test.ts b/plugins/tech-insights-backend/src/plugin/plugin.test.ts new file mode 100644 index 0000000000..66d06b827e --- /dev/null +++ b/plugins/tech-insights-backend/src/plugin/plugin.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2024 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 { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { techInsightsPlugin } from './plugin'; + +describe('techInsightsPlugin', () => { + it('should register tech-insights plugin and its router', async () => { + const httpRouterMock = mockServices.httpRouter.mock(); + + await startTestBackend({ + extensionPoints: [], + features: [ + techInsightsPlugin(), + httpRouterMock.factory, + mockServices.database.factory(), + mockServices.logger.factory(), + mockServices.rootConfig.factory({ + data: { + techInsights: { + factRetrievers: { + entityOwnershipFactRetriever: { + cadence: '*/15 * * * *', + lifecycle: { timeToLive: { weeks: 2 } }, + }, + }, + }, + }, + }), + mockServices.scheduler.factory(), + mockServices.tokenManager.factory(), + ], + }); + + expect(httpRouterMock.use).toHaveBeenCalledTimes(1); + }); +}); diff --git a/plugins/tech-insights-backend/src/plugin/plugin.ts b/plugins/tech-insights-backend/src/plugin/plugin.ts new file mode 100644 index 0000000000..f443075a74 --- /dev/null +++ b/plugins/tech-insights-backend/src/plugin/plugin.ts @@ -0,0 +1,128 @@ +/* + * Copyright 2024 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { CheckResult } from '@backstage/plugin-tech-insights-common'; +import { + FactCheckerFactory, + FactRetriever, + FactRetrieverRegistration, + TechInsightCheck, + techInsightsFactCheckerFactoryExtensionPoint, + techInsightsFactRetrieversExtensionPoint, +} from '@backstage/plugin-tech-insights-node'; +import { + buildTechInsightsContext, + createRouter, + entityMetadataFactRetriever, + entityOwnershipFactRetriever, + techdocsFactRetriever, +} from '../service'; +import { createFactRetrieverRegistrationFromConfig } from './config'; + +/** + * The tech-insights backend plugin. + * + * @public + */ +export const techInsightsPlugin = createBackendPlugin({ + pluginId: 'tech-insights', + register(env) { + let factCheckerFactory: + | FactCheckerFactory + | undefined = undefined; + env.registerExtensionPoint(techInsightsFactCheckerFactoryExtensionPoint, { + setFactCheckerFactory< + CheckType extends TechInsightCheck, + CheckResultType extends CheckResult, + >(factory: FactCheckerFactory): void { + factCheckerFactory = factory; + }, + }); + + // initialized with built-in fact retrievers + // only added as registration if there is config for them + const addedFactRetrievers: Record = { + entityMetadataFactRetriever, + entityOwnershipFactRetriever, + techdocsFactRetriever, + }; + env.registerExtensionPoint(techInsightsFactRetrieversExtensionPoint, { + addFactRetrievers(factRetrievers: Record): void { + Object.entries(factRetrievers).forEach(([key, value]) => { + addedFactRetrievers[key] = value; + }); + }, + }); + + env.registerInit({ + deps: { + config: coreServices.rootConfig, + database: coreServices.database, + discovery: coreServices.discovery, + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, + scheduler: coreServices.scheduler, + tokenManager: coreServices.tokenManager, + }, + async init({ + config, + database, + discovery, + httpRouter, + logger, + scheduler, + tokenManager, + }) { + const winstonLogger = loggerToWinstonLogger(logger); + const factRetrievers: FactRetrieverRegistration[] = Object.entries( + addedFactRetrievers, + ) + .map(([name, factRetriever]) => + createFactRetrieverRegistrationFromConfig( + config, + name, + factRetriever, + ), + ) + .filter(registration => registration) as FactRetrieverRegistration[]; + + const context = await buildTechInsightsContext({ + config, + database, + discovery, + factCheckerFactory, + factRetrievers, + logger: winstonLogger, + scheduler, + tokenManager, + }); + + httpRouter.use( + await createRouter({ + ...context, + config, + logger: winstonLogger, + }), + ); + }, + }); + }, +}); diff --git a/plugins/tech-insights-node/api-report.md b/plugins/tech-insights-node/api-report.md index dbb8a8b674..0d0d835b95 100644 --- a/plugins/tech-insights-node/api-report.md +++ b/plugins/tech-insights-node/api-report.md @@ -8,6 +8,7 @@ import { Config } from '@backstage/config'; import { DateTime } from 'luxon'; import { Duration } from 'luxon'; import { DurationLike } from 'luxon'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { FactSchema } from '@backstage/plugin-tech-insights-common'; import { HumanDuration } from '@backstage/types'; import { JsonValue } from '@backstage/types'; @@ -137,6 +138,29 @@ export type TechInsightFact = { timestamp?: DateTime; }; +// @public (undocumented) +export interface TechInsightsFactCheckerFactoryExtensionPoint { + // (undocumented) + setFactCheckerFactory< + CheckType extends TechInsightCheck, + CheckResultType extends CheckResult, + >( + factory: FactCheckerFactory, + ): void; +} + +// @public +export const techInsightsFactCheckerFactoryExtensionPoint: ExtensionPoint; + +// @public (undocumented) +export interface TechInsightsFactRetrieversExtensionPoint { + // (undocumented) + addFactRetrievers(factRetrievers: Record): void; +} + +// @public +export const techInsightsFactRetrieversExtensionPoint: ExtensionPoint; + // @public export interface TechInsightsStore { getFactsBetweenTimestampsByIds( diff --git a/plugins/tech-insights-node/package.json b/plugins/tech-insights-node/package.json index 4cd03c38a7..e8f71558d7 100644 --- a/plugins/tech-insights-node/package.json +++ b/plugins/tech-insights-node/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-tech-insights-common": "workspace:^", "@backstage/types": "workspace:^", diff --git a/plugins/tech-insights-node/src/extensionPoints.ts b/plugins/tech-insights-node/src/extensionPoints.ts new file mode 100644 index 0000000000..527aac7d5d --- /dev/null +++ b/plugins/tech-insights-node/src/extensionPoints.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2024 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 { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { CheckResult } from '@backstage/plugin-tech-insights-common'; +import { FactRetriever } from './facts'; +import { FactCheckerFactory, TechInsightCheck } from './checks'; + +/** + * @public + */ +export interface TechInsightsFactRetrieversExtensionPoint { + addFactRetrievers(factRetrievers: Record): void; +} + +/** + * An extension point that allows other plugins or modules to add fact retrievers. + * + * @public + */ +export const techInsightsFactRetrieversExtensionPoint = + createExtensionPoint({ + id: 'tech-insights.fact-retrievers', + }); + +/** + * @public + */ +export interface TechInsightsFactCheckerFactoryExtensionPoint { + setFactCheckerFactory< + CheckType extends TechInsightCheck, + CheckResultType extends CheckResult, + >( + factory: FactCheckerFactory, + ): void; +} + +/** + * An extension point that allows other plugins or modules to set a FactCheckerFactory. + * + * @public + */ +export const techInsightsFactCheckerFactoryExtensionPoint = + createExtensionPoint({ + id: 'tech-insights.fact-checker-factory', + }); diff --git a/plugins/tech-insights-node/src/index.ts b/plugins/tech-insights-node/src/index.ts index 70ef27e159..c1c321f925 100644 --- a/plugins/tech-insights-node/src/index.ts +++ b/plugins/tech-insights-node/src/index.ts @@ -15,5 +15,6 @@ */ export * from './checks'; +export * from './extensionPoints'; export * from './facts'; export * from './persistence'; diff --git a/yarn.lock b/yarn.lock index 1d31e0fc44..171c5107b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9089,9 +9089,11 @@ __metadata: dependencies: "@backstage/backend-common": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-tech-insights-common": "workspace:^" "@backstage/plugin-tech-insights-node": "workspace:^" + "@backstage/types": "workspace:^" ajv: ^8.10.0 json-rules-engine: ^6.1.2 lodash: ^4.17.21 @@ -9105,6 +9107,7 @@ __metadata: resolution: "@backstage/plugin-tech-insights-backend@workspace:plugins/tech-insights-backend" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-tasks": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-client": "workspace:^" @@ -9149,6 +9152,7 @@ __metadata: resolution: "@backstage/plugin-tech-insights-node@workspace:plugins/tech-insights-node" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-tech-insights-common": "workspace:^" From 25cfb763beee12e7818c90ac31a128f9e2a89eb4 Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Tue, 9 Jan 2024 00:27:58 +0100 Subject: [PATCH 136/468] feat(tech-insights): add module for new backend system A new backend module for the tech-insights backend was added and exported as `default` The module will register the `JsonRulesEngineFactCheckerFactory` as `FactCheckerFactory`, loading checks from the config. The plugin can be used like ```ts title="packages/backend/src/index.ts" backend.add(import('@backstage/plugin-tech-insights-backend-module-jsonfc')); ``` Signed-off-by: Patrick Jungermann --- .changeset/dirty-plums-fix.md | 17 +++++ .../README.md | 14 +++- .../api-report.md | 5 ++ .../package.json | 2 + .../src/index.ts | 1 + .../src/module/index.ts | 17 +++++ ...eJsonRulesEngineFactCheckerFactory.test.ts | 68 +++++++++++++++++++ ...ModuleJsonRulesEngineFactCheckerFactory.ts | 51 ++++++++++++++ yarn.lock | 2 + 9 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 .changeset/dirty-plums-fix.md create mode 100644 plugins/tech-insights-backend-module-jsonfc/src/module/index.ts create mode 100644 plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.test.ts create mode 100644 plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.ts diff --git a/.changeset/dirty-plums-fix.md b/.changeset/dirty-plums-fix.md new file mode 100644 index 0000000000..fb0655377c --- /dev/null +++ b/.changeset/dirty-plums-fix.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-tech-insights-backend-module-jsonfc': patch +--- + +Add support for the new backend system. + +A new backend module for the tech-insights backend +was added and exported as `default`. + +The module will register the `JsonRulesEngineFactCheckerFactory` +as `FactCheckerFactory`, loading checks from the config. + +You can use it with the new backend system like + +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-tech-insights-backend-module-jsonfc')); +``` diff --git a/plugins/tech-insights-backend-module-jsonfc/README.md b/plugins/tech-insights-backend-module-jsonfc/README.md index 3f9e91408c..c405ac34ef 100644 --- a/plugins/tech-insights-backend-module-jsonfc/README.md +++ b/plugins/tech-insights-backend-module-jsonfc/README.md @@ -13,7 +13,17 @@ To add this FactChecker into your Tech Insights you need to install the module i yarn add --cwd packages/backend @backstage/plugin-tech-insights-backend-module-jsonfc ``` -and modify the `techInsights.ts` file to contain a reference to the FactCheckers implementation. +### Add to the backend + +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-tech-insights-backend-module-jsonfc')); +``` + +This setup requires checks to be provided using the config. + +### Add to the backend (old) + +Modify the `techInsights.ts` file to contain a reference to the FactCheckers implementation. ```diff +import { JsonRulesEngineFactCheckerFactory } from '@backstage/plugin-tech-insights-backend-module-jsonfc'; @@ -34,7 +44,7 @@ and modify the `techInsights.ts` file to contain a reference to the FactCheckers }); ``` -By default this implementation comes with an in-memory storage to store checks. You can inject an additional data store by adding an implementation of `TechInsightCheckRegistry` into the constructor options when creating a `JsonRulesEngineFactCheckerFactory`. That can be done as follows +By default, this implementation comes with an in-memory storage to store checks. You can inject an additional data store by adding an implementation of `TechInsightCheckRegistry` into the constructor options when creating a `JsonRulesEngineFactCheckerFactory`. That can be done as follows ```diff const myTechInsightCheckRegistry: TechInsightCheckRegistry = // snip diff --git a/plugins/tech-insights-backend-module-jsonfc/api-report.md b/plugins/tech-insights-backend-module-jsonfc/api-report.md index d28ee28f8d..790aa0b666 100644 --- a/plugins/tech-insights-backend-module-jsonfc/api-report.md +++ b/plugins/tech-insights-backend-module-jsonfc/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { BooleanCheckResult } from '@backstage/plugin-tech-insights-common'; import { CheckResponse } from '@backstage/plugin-tech-insights-common'; import { CheckValidationResponse } from '@backstage/plugin-tech-insights-node'; @@ -110,5 +111,9 @@ export interface TechInsightJsonRuleCheck extends TechInsightCheck { rule: Rule; } +// @public +const techInsightsModuleJsonRulesEngineFactCheckerFactory: () => BackendFeature; +export default techInsightsModuleJsonRulesEngineFactCheckerFactory; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index 431a7152d4..c2231f4c7e 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -34,6 +34,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-tech-insights-common": "workspace:^", @@ -46,6 +47,7 @@ "winston": "^3.2.1" }, "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^" }, "files": [ diff --git a/plugins/tech-insights-backend-module-jsonfc/src/index.ts b/plugins/tech-insights-backend-module-jsonfc/src/index.ts index 6462efc403..1c1e8ab77d 100644 --- a/plugins/tech-insights-backend-module-jsonfc/src/index.ts +++ b/plugins/tech-insights-backend-module-jsonfc/src/index.ts @@ -15,6 +15,7 @@ */ export { JSON_RULE_ENGINE_CHECK_TYPE } from './constants'; +export { techInsightsModuleJsonRulesEngineFactCheckerFactory as default } from './module'; export * from './service'; export type { JsonRuleCheckResponse, diff --git a/plugins/tech-insights-backend-module-jsonfc/src/module/index.ts b/plugins/tech-insights-backend-module-jsonfc/src/module/index.ts new file mode 100644 index 0000000000..0fa167e883 --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/src/module/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 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 * from './techInsightsModuleJsonRulesEngineFactCheckerFactory'; diff --git a/plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.test.ts b/plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.test.ts new file mode 100644 index 0000000000..b6df48a8b2 --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.test.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2024 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 { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { techInsightsFactCheckerFactoryExtensionPoint } from '@backstage/plugin-tech-insights-node'; +import { JSON_RULE_ENGINE_CHECK_TYPE } from '../constants'; +import { techInsightsModuleJsonRulesEngineFactCheckerFactory } from './techInsightsModuleJsonRulesEngineFactCheckerFactory'; + +describe('techInsightsModuleJsonRulesEngineFactCheckerFactory', () => { + it('should register the factory', async () => { + const extensionPoint = { + setFactCheckerFactory: jest.fn(), + } satisfies Partial; + + await startTestBackend({ + extensionPoints: [ + [techInsightsFactCheckerFactoryExtensionPoint, extensionPoint], + ], + features: [ + techInsightsModuleJsonRulesEngineFactCheckerFactory(), + mockServices.logger.factory(), + mockServices.rootConfig.factory({ + data: { + techInsights: { + factChecker: { + checks: { + groupOwnerCheck: { + type: JSON_RULE_ENGINE_CHECK_TYPE, + name: 'Group Owner Check', + description: + 'Verifies that a group has been set as the spec.owner for this entity', + factIds: ['entityOwnershipFactRetriever'], + rule: { + conditions: { + all: [ + { + fact: 'hasGroupOwner', + operator: 'equal', + value: true, + }, + ], + }, + }, + }, + }, + }, + }, + }, + }), + ], + }); + + expect(extensionPoint.setFactCheckerFactory).toHaveBeenCalled(); + }); +}); diff --git a/plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.ts b/plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.ts new file mode 100644 index 0000000000..8527b91d9d --- /dev/null +++ b/plugins/tech-insights-backend-module-jsonfc/src/module/techInsightsModuleJsonRulesEngineFactCheckerFactory.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2024 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { techInsightsFactCheckerFactoryExtensionPoint } from '@backstage/plugin-tech-insights-node'; +import { JsonRulesEngineFactCheckerFactory } from '../service'; + +/** + * Sets a JsonRulesEngineFactCheckerFactory as FactCheckerFactory + * loading checks from the config. + * + * @public + */ +export const techInsightsModuleJsonRulesEngineFactCheckerFactory = + createBackendModule({ + pluginId: 'tech-insights', + moduleId: 'json-rules-engine-fact-checker-factory', + register(env) { + env.registerInit({ + deps: { + config: coreServices.rootConfig, + logger: coreServices.logger, + techInsights: techInsightsFactCheckerFactoryExtensionPoint, + }, + async init({ config, logger, techInsights }) { + const winstonLogger = loggerToWinstonLogger(logger); + const factory = JsonRulesEngineFactCheckerFactory.fromConfig(config, { + logger: winstonLogger, + }); + techInsights.setFactCheckerFactory(factory); + }, + }); + }, + }); diff --git a/yarn.lock b/yarn.lock index 171c5107b8..db8db76869 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9088,6 +9088,8 @@ __metadata: resolution: "@backstage/plugin-tech-insights-backend-module-jsonfc@workspace:plugins/tech-insights-backend-module-jsonfc" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" From 341c2a24a0732a2003bff5741dadc264e044225c Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Tue, 9 Jan 2024 12:28:31 +0100 Subject: [PATCH 137/468] feat(tech-insights): support registry and persistence at plugin Support `FactRetrieverRegistry` and `PersistenceContext` at the `techInsightsPlugin` using extension points. Additionally, move `FactRetrieverRegistry` and `PersistenceContext` to `@backstage/plugin-tech-insights-node`. Original exports are marked as deprecated and re-export the moved types. Signed-off-by: Patrick Jungermann --- .changeset/orange-gorillas-clean.md | 26 +++++++++++++ plugins/tech-insights-backend/api-report.md | 35 ++++++------------ .../tech-insights-backend/src/deprecated.ts | 32 ++++++++++++++++ plugins/tech-insights-backend/src/index.ts | 1 + .../src/plugin/plugin.ts | 23 ++++++++++++ .../service/fact/FactRetrieverEngine.test.ts | 2 +- .../src/service/fact/FactRetrieverEngine.ts | 2 +- .../src/service/fact/FactRetrieverRegistry.ts | 13 +------ .../src/service/fact/index.ts | 1 - .../src/service/persistence/index.ts | 5 +-- .../service/persistence/persistenceContext.ts | 11 +----- .../src/service/router.test.ts | 6 ++- .../src/service/router.ts | 2 +- .../src/service/techInsightsContextBuilder.ts | 12 ++---- plugins/tech-insights-node/api-report.md | 37 +++++++++++++++++++ .../tech-insights-node/src/extensionPoints.ts | 37 ++++++++++++++++++- plugins/tech-insights-node/src/facts.ts | 11 ++++++ plugins/tech-insights-node/src/persistence.ts | 9 +++++ 18 files changed, 200 insertions(+), 65 deletions(-) create mode 100644 .changeset/orange-gorillas-clean.md create mode 100644 plugins/tech-insights-backend/src/deprecated.ts diff --git a/.changeset/orange-gorillas-clean.md b/.changeset/orange-gorillas-clean.md new file mode 100644 index 0000000000..d346adce6c --- /dev/null +++ b/.changeset/orange-gorillas-clean.md @@ -0,0 +1,26 @@ +--- +'@backstage/plugin-tech-insights-backend': patch +'@backstage/plugin-tech-insights-node': patch +--- + +Move `FactRetrieverRegistry` and `PersistenceContext` to `@backstage/plugin-tech-insights-node`. + +Original exports are marked as deprecated and re-export the moved types. + +Please replace uses like + +```ts +import { + FactRetrieverRegistry, + PersistenceContext, +} from '@backstage/plugin-tech-insights-backend'; +``` + +with + +```ts +import { + FactRetrieverRegistry, + PersistenceContext, +} from '@backstage/plugin-tech-insights-node'; +``` diff --git a/plugins/tech-insights-backend/api-report.md b/plugins/tech-insights-backend/api-report.md index fd12dfed24..631e792141 100644 --- a/plugins/tech-insights-backend/api-report.md +++ b/plugins/tech-insights-backend/api-report.md @@ -13,14 +13,14 @@ import { FactCheckerFactory } from '@backstage/plugin-tech-insights-node'; import { FactLifecycle } from '@backstage/plugin-tech-insights-node'; import { FactRetriever } from '@backstage/plugin-tech-insights-node'; import { FactRetrieverRegistration } from '@backstage/plugin-tech-insights-node'; -import { FactSchema } from '@backstage/plugin-tech-insights-common'; +import { FactRetrieverRegistry as FactRetrieverRegistry_2 } from '@backstage/plugin-tech-insights-node'; import { HumanDuration } from '@backstage/types'; import { Logger } from 'winston'; +import { PersistenceContext as PersistenceContext_2 } from '@backstage/plugin-tech-insights-node'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { TechInsightCheck } from '@backstage/plugin-tech-insights-node'; -import { TechInsightsStore } from '@backstage/plugin-tech-insights-node'; import { TokenManager } from '@backstage/backend-common'; // @public @@ -64,30 +64,17 @@ export type FactRetrieverRegistrationOptions = { initialDelay?: Duration | HumanDuration; }; -// @public (undocumented) -export interface FactRetrieverRegistry { - // (undocumented) - get(retrieverReference: string): Promise; - // (undocumented) - getSchemas(): Promise; - // (undocumented) - listRegistrations(): Promise; - // (undocumented) - listRetrievers(): Promise; - // (undocumented) - register(registration: FactRetrieverRegistration): Promise; -} +// @public @deprecated (undocumented) +export type FactRetrieverRegistry = FactRetrieverRegistry_2; // @public export const initializePersistenceContext: ( database: PluginDatabaseManager, options?: PersistenceContextOptions, -) => Promise; +) => Promise; -// @public -export type PersistenceContext = { - techInsightsStore: TechInsightsStore; -}; +// @public @deprecated (undocumented) +export type PersistenceContext = PersistenceContext_2; // @public export type PersistenceContextOptions = { @@ -102,7 +89,7 @@ export interface RouterOptions< config: Config; factChecker?: FactChecker; logger: Logger; - persistenceContext: PersistenceContext; + persistenceContext: PersistenceContext_2; } // @public @@ -114,7 +101,7 @@ export type TechInsightsContext< CheckResultType extends CheckResult, > = { factChecker?: FactChecker; - persistenceContext: PersistenceContext; + persistenceContext: PersistenceContext_2; factRetrieverEngine: FactRetrieverEngine; }; @@ -130,11 +117,11 @@ export interface TechInsightsOptions< // (undocumented) discovery: PluginEndpointDiscovery; factCheckerFactory?: FactCheckerFactory; - factRetrieverRegistry?: FactRetrieverRegistry; + factRetrieverRegistry?: FactRetrieverRegistry_2; factRetrievers?: FactRetrieverRegistration[]; // (undocumented) logger: Logger; - persistenceContext?: PersistenceContext; + persistenceContext?: PersistenceContext_2; // (undocumented) scheduler: PluginTaskScheduler; // (undocumented) diff --git a/plugins/tech-insights-backend/src/deprecated.ts b/plugins/tech-insights-backend/src/deprecated.ts new file mode 100644 index 0000000000..1074e6f910 --- /dev/null +++ b/plugins/tech-insights-backend/src/deprecated.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2024 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 { + FactRetrieverRegistry as FactRetrieverRegistry_, + PersistenceContext as PersistenceContext_, +} from '@backstage/plugin-tech-insights-node'; + +/** + * @public + * @deprecated Use FactRetrieverRegistry from `@backstage/plugin-tech-insights-node` instead. + */ +export type FactRetrieverRegistry = FactRetrieverRegistry_; + +/** + * @public + * @deprecated Use PersistenceContext from `@backstage/plugin-tech-insights-node` instead. + */ +export type PersistenceContext = PersistenceContext_; diff --git a/plugins/tech-insights-backend/src/index.ts b/plugins/tech-insights-backend/src/index.ts index 2e87a53dfd..a582a04ebf 100644 --- a/plugins/tech-insights-backend/src/index.ts +++ b/plugins/tech-insights-backend/src/index.ts @@ -15,4 +15,5 @@ */ export { techInsightsPlugin as default } from './plugin'; +export * from './deprecated'; export * from './service'; diff --git a/plugins/tech-insights-backend/src/plugin/plugin.ts b/plugins/tech-insights-backend/src/plugin/plugin.ts index f443075a74..6da1ac1637 100644 --- a/plugins/tech-insights-backend/src/plugin/plugin.ts +++ b/plugins/tech-insights-backend/src/plugin/plugin.ts @@ -24,9 +24,13 @@ import { FactCheckerFactory, FactRetriever, FactRetrieverRegistration, + FactRetrieverRegistry, + PersistenceContext, TechInsightCheck, techInsightsFactCheckerFactoryExtensionPoint, + techInsightsFactRetrieverRegistryExtensionPoint, techInsightsFactRetrieversExtensionPoint, + techInsightsPersistenceContextExtensionPoint, } from '@backstage/plugin-tech-insights-node'; import { buildTechInsightsContext, @@ -57,6 +61,16 @@ export const techInsightsPlugin = createBackendPlugin({ }, }); + let factRetrieverRegistry: FactRetrieverRegistry | undefined = undefined; + env.registerExtensionPoint( + techInsightsFactRetrieverRegistryExtensionPoint, + { + setFactRetrieverRegistry(registry: FactRetrieverRegistry): void { + factRetrieverRegistry = registry; + }, + }, + ); + // initialized with built-in fact retrievers // only added as registration if there is config for them const addedFactRetrievers: Record = { @@ -72,6 +86,13 @@ export const techInsightsPlugin = createBackendPlugin({ }, }); + let persistenceContext: PersistenceContext | undefined = undefined; + env.registerExtensionPoint(techInsightsPersistenceContextExtensionPoint, { + setPersistenceContext(context: PersistenceContext): void { + persistenceContext = context; + }, + }); + env.registerInit({ deps: { config: coreServices.rootConfig, @@ -109,8 +130,10 @@ export const techInsightsPlugin = createBackendPlugin({ database, discovery, factCheckerFactory, + factRetrieverRegistry, factRetrievers, logger: winstonLogger, + persistenceContext, scheduler, tokenManager, }); diff --git a/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts b/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts index 677b778b9b..3bbaf9edf5 100644 --- a/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts +++ b/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.test.ts @@ -17,11 +17,11 @@ import { FactRetriever, FactRetrieverRegistration, + FactRetrieverRegistry, FactSchemaDefinition, TechInsightFact, TechInsightsStore, } from '@backstage/plugin-tech-insights-node'; -import { FactRetrieverRegistry } from './FactRetrieverRegistry'; import { DefaultFactRetrieverEngine, FactRetrieverEngine, diff --git a/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.ts b/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.ts index b723fcd7f5..04035ca8c7 100644 --- a/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.ts +++ b/plugins/tech-insights-backend/src/service/fact/FactRetrieverEngine.ts @@ -18,10 +18,10 @@ import { FactRetriever, FactRetrieverContext, FactRetrieverRegistration, + FactRetrieverRegistry, TechInsightFact, TechInsightsStore, } from '@backstage/plugin-tech-insights-node'; -import { FactRetrieverRegistry } from './FactRetrieverRegistry'; import { Logger } from 'winston'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { Duration } from 'luxon'; diff --git a/plugins/tech-insights-backend/src/service/fact/FactRetrieverRegistry.ts b/plugins/tech-insights-backend/src/service/fact/FactRetrieverRegistry.ts index 8796f1511a..42caca6b20 100644 --- a/plugins/tech-insights-backend/src/service/fact/FactRetrieverRegistry.ts +++ b/plugins/tech-insights-backend/src/service/fact/FactRetrieverRegistry.ts @@ -17,22 +17,11 @@ import { FactRetriever, FactRetrieverRegistration, + FactRetrieverRegistry, } from '@backstage/plugin-tech-insights-node'; import { FactSchema } from '@backstage/plugin-tech-insights-common'; import { ConflictError, NotFoundError } from '@backstage/errors'; -/** - * @public - * - */ -export interface FactRetrieverRegistry { - register(registration: FactRetrieverRegistration): Promise; - get(retrieverReference: string): Promise; - listRetrievers(): Promise; - listRegistrations(): Promise; - getSchemas(): Promise; -} - /** * A basic in memory fact retriever registry. * diff --git a/plugins/tech-insights-backend/src/service/fact/index.ts b/plugins/tech-insights-backend/src/service/fact/index.ts index 0eeb498481..b3df648a8e 100644 --- a/plugins/tech-insights-backend/src/service/fact/index.ts +++ b/plugins/tech-insights-backend/src/service/fact/index.ts @@ -17,5 +17,4 @@ export { createFactRetrieverRegistration } from './createFactRetriever'; export type { FactRetrieverRegistrationOptions } from './createFactRetriever'; export type { FactRetrieverEngine } from './FactRetrieverEngine'; -export type { FactRetrieverRegistry } from './FactRetrieverRegistry'; export * from './factRetrievers'; diff --git a/plugins/tech-insights-backend/src/service/persistence/index.ts b/plugins/tech-insights-backend/src/service/persistence/index.ts index 64cc876026..d87f91e1b0 100644 --- a/plugins/tech-insights-backend/src/service/persistence/index.ts +++ b/plugins/tech-insights-backend/src/service/persistence/index.ts @@ -15,7 +15,4 @@ */ export { initializePersistenceContext } from './persistenceContext'; -export type { - PersistenceContext, - PersistenceContextOptions, -} from './persistenceContext'; +export type { PersistenceContextOptions } from './persistenceContext'; diff --git a/plugins/tech-insights-backend/src/service/persistence/persistenceContext.ts b/plugins/tech-insights-backend/src/service/persistence/persistenceContext.ts index 07badb038c..3ddae3197f 100644 --- a/plugins/tech-insights-backend/src/service/persistence/persistenceContext.ts +++ b/plugins/tech-insights-backend/src/service/persistence/persistenceContext.ts @@ -20,22 +20,13 @@ import { } from '@backstage/backend-common'; import { Logger } from 'winston'; import { TechInsightsDatabase } from './TechInsightsDatabase'; -import { TechInsightsStore } from '@backstage/plugin-tech-insights-node'; +import { PersistenceContext } from '@backstage/plugin-tech-insights-node'; const migrationsDir = resolvePackagePath( '@backstage/plugin-tech-insights-backend', 'migrations', ); -/** - * A Container for persistence related components in TechInsights - * - * @public - */ -export type PersistenceContext = { - techInsightsStore: TechInsightsStore; -}; - /** * A Container for persistence context initialization options * diff --git a/plugins/tech-insights-backend/src/service/router.test.ts b/plugins/tech-insights-backend/src/service/router.test.ts index 6935338a56..bf4313748f 100644 --- a/plugins/tech-insights-backend/src/service/router.test.ts +++ b/plugins/tech-insights-backend/src/service/router.test.ts @@ -24,8 +24,10 @@ import { import { ConfigReader } from '@backstage/config'; import request from 'supertest'; import express from 'express'; -import { PersistenceContext } from './persistence/persistenceContext'; -import { TechInsightsStore } from '@backstage/plugin-tech-insights-node'; +import { + PersistenceContext, + TechInsightsStore, +} from '@backstage/plugin-tech-insights-node'; import { DateTime } from 'luxon'; import { Knex } from 'knex'; import { TaskScheduler } from '@backstage/backend-tasks'; diff --git a/plugins/tech-insights-backend/src/service/router.ts b/plugins/tech-insights-backend/src/service/router.ts index 5e58df1b20..53c1351ffa 100644 --- a/plugins/tech-insights-backend/src/service/router.ts +++ b/plugins/tech-insights-backend/src/service/router.ts @@ -19,13 +19,13 @@ import Router from 'express-promise-router'; import { Config } from '@backstage/config'; import { FactChecker, + PersistenceContext, TechInsightCheck, } from '@backstage/plugin-tech-insights-node'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; import { Logger } from 'winston'; import { DateTime } from 'luxon'; -import { PersistenceContext } from './persistence/persistenceContext'; import { CompoundEntityRef, parseEntityRef, diff --git a/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts b/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts index 20693d0a7d..4f49e0031b 100644 --- a/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts +++ b/plugins/tech-insights-backend/src/service/techInsightsContextBuilder.ts @@ -19,10 +19,7 @@ import { FactRetrieverEngine, } from './fact/FactRetrieverEngine'; import { Logger } from 'winston'; -import { - DefaultFactRetrieverRegistry, - FactRetrieverRegistry, -} from './fact/FactRetrieverRegistry'; +import { DefaultFactRetrieverRegistry } from './fact/FactRetrieverRegistry'; import { Config } from '@backstage/config'; import { PluginDatabaseManager, @@ -33,12 +30,11 @@ import { FactChecker, FactCheckerFactory, FactRetrieverRegistration, + FactRetrieverRegistry, + PersistenceContext, TechInsightCheck, } from '@backstage/plugin-tech-insights-node'; -import { - initializePersistenceContext, - PersistenceContext, -} from './persistence/persistenceContext'; +import { initializePersistenceContext } from './persistence'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; diff --git a/plugins/tech-insights-node/api-report.md b/plugins/tech-insights-node/api-report.md index 0d0d835b95..13056b5eaa 100644 --- a/plugins/tech-insights-node/api-report.md +++ b/plugins/tech-insights-node/api-report.md @@ -80,6 +80,20 @@ export type FactRetrieverRegistration = { initialDelay?: Duration | HumanDuration; }; +// @public (undocumented) +export interface FactRetrieverRegistry { + // (undocumented) + get(retrieverReference: string): Promise; + // (undocumented) + getSchemas(): Promise; + // (undocumented) + listRegistrations(): Promise; + // (undocumented) + listRetrievers(): Promise; + // (undocumented) + register(registration: FactRetrieverRegistration): Promise; +} + // @public export type FactSchemaDefinition = Omit; @@ -93,6 +107,11 @@ export type MaxItems = { maxItems: number; }; +// @public +export type PersistenceContext = { + techInsightsStore: TechInsightsStore; +}; + // @public export interface TechInsightCheck { description: string; @@ -152,6 +171,15 @@ export interface TechInsightsFactCheckerFactoryExtensionPoint { // @public export const techInsightsFactCheckerFactoryExtensionPoint: ExtensionPoint; +// @public (undocumented) +export interface TechInsightsFactRetrieverRegistryExtensionPoint { + // (undocumented) + setFactRetrieverRegistry(registry: FactRetrieverRegistry): void; +} + +// @public +export const techInsightsFactRetrieverRegistryExtensionPoint: ExtensionPoint; + // @public (undocumented) export interface TechInsightsFactRetrieversExtensionPoint { // (undocumented) @@ -161,6 +189,15 @@ export interface TechInsightsFactRetrieversExtensionPoint { // @public export const techInsightsFactRetrieversExtensionPoint: ExtensionPoint; +// @public (undocumented) +export interface TechInsightsPersistenceContextExtensionPoint { + // (undocumented) + setPersistenceContext(context: PersistenceContext): void; +} + +// @public +export const techInsightsPersistenceContextExtensionPoint: ExtensionPoint; + // @public export interface TechInsightsStore { getFactsBetweenTimestampsByIds( diff --git a/plugins/tech-insights-node/src/extensionPoints.ts b/plugins/tech-insights-node/src/extensionPoints.ts index 527aac7d5d..e1f581a840 100644 --- a/plugins/tech-insights-node/src/extensionPoints.ts +++ b/plugins/tech-insights-node/src/extensionPoints.ts @@ -16,8 +16,9 @@ import { createExtensionPoint } from '@backstage/backend-plugin-api'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; -import { FactRetriever } from './facts'; import { FactCheckerFactory, TechInsightCheck } from './checks'; +import { FactRetriever, FactRetrieverRegistry } from './facts'; +import { PersistenceContext } from './persistence'; /** * @public @@ -57,3 +58,37 @@ export const techInsightsFactCheckerFactoryExtensionPoint = createExtensionPoint({ id: 'tech-insights.fact-checker-factory', }); + +/** + * @public + */ +export interface TechInsightsFactRetrieverRegistryExtensionPoint { + setFactRetrieverRegistry(registry: FactRetrieverRegistry): void; +} + +/** + * An extension point that allows other plugins or modules to set a custom FactRetrieverRegistry. + * + * @public + */ +export const techInsightsFactRetrieverRegistryExtensionPoint = + createExtensionPoint({ + id: 'tech-insights.fact-retriever-registry', + }); + +/** + * @public + */ +export interface TechInsightsPersistenceContextExtensionPoint { + setPersistenceContext(context: PersistenceContext): void; +} + +/** + * An extension point that allows other plugins or modules to set a custom PersistenceContext. + * + * @public + */ +export const techInsightsPersistenceContextExtensionPoint = + createExtensionPoint({ + id: 'tech-insights.persistence-context', + }); diff --git a/plugins/tech-insights-node/src/facts.ts b/plugins/tech-insights-node/src/facts.ts index b35ad756ed..9257ea789e 100644 --- a/plugins/tech-insights-node/src/facts.ts +++ b/plugins/tech-insights-node/src/facts.ts @@ -230,3 +230,14 @@ export type FactRetrieverRegistration = { */ initialDelay?: Duration | HumanDuration; }; + +/** + * @public + */ +export interface FactRetrieverRegistry { + register(registration: FactRetrieverRegistration): Promise; + get(retrieverReference: string): Promise; + listRetrievers(): Promise; + listRegistrations(): Promise; + getSchemas(): Promise; +} diff --git a/plugins/tech-insights-node/src/persistence.ts b/plugins/tech-insights-node/src/persistence.ts index 73abb6c5af..391d23bae5 100644 --- a/plugins/tech-insights-node/src/persistence.ts +++ b/plugins/tech-insights-node/src/persistence.ts @@ -22,6 +22,15 @@ import { import { DateTime } from 'luxon'; import { FactSchema } from '@backstage/plugin-tech-insights-common'; +/** + * A Container for persistence related components in TechInsights + * + * @public + */ +export type PersistenceContext = { + techInsightsStore: TechInsightsStore; +}; + /** * TechInsights Database * From da98911e7d04d1219c5b8df9304dbf6c20dcc89c Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 30 Jan 2024 09:15:50 +0100 Subject: [PATCH 138/468] chore: updating api-reports Signed-off-by: blam --- packages/backend-plugin-api/api-report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend-plugin-api/api-report.md b/packages/backend-plugin-api/api-report.md index e4d11c7b62..3b3d28cc27 100644 --- a/packages/backend-plugin-api/api-report.md +++ b/packages/backend-plugin-api/api-report.md @@ -392,6 +392,7 @@ export interface SchedulerService extends PluginTaskScheduler {} export type SearchOptions = { etag?: string; signal?: AbortSignal; + token?: string; }; // @public From 9b9c05c05501a1a81c3eaf52d70c49b19c73bcd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Vit=C3=A1sek?= Date: Tue, 30 Jan 2024 09:23:51 +0100 Subject: [PATCH 139/468] adding fields limitation for EntityPicker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ladislav Vitásek --- .changeset/four-walls-perform.md | 5 +++++ .../components/fields/EntityPicker/EntityPicker.tsx | 12 +++++++++++- .../src/components/fields/EntityPicker/schema.ts | 7 +++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .changeset/four-walls-perform.md diff --git a/.changeset/four-walls-perform.md b/.changeset/four-walls-perform.md new file mode 100644 index 0000000000..bb30a0cc2b --- /dev/null +++ b/.changeset/four-walls-perform.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': minor +--- + +Scaffolding - EntityPicker component - now it does not load full entity data, only a specified set - defaults to `['metadata.name', 'metadata.namespace', 'metadata.title', 'kind']`. It can significantly reduce loaded time bigger data set. It's possible to set fields to ignore configure via `fieldsToIgnore` in UI options of the EntityPicker component. diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index 0d45fe771c..d2c88a0b03 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -61,14 +61,24 @@ export const EntityPicker = (props: EntityPickerProps) => { } = props; const catalogFilter = buildCatalogFilter(uiSchema); const defaultKind = uiSchema['ui:options']?.defaultKind; + const fieldsToIgnore = uiSchema['ui:options']?.fieldsToIgnore; const defaultNamespace = uiSchema['ui:options']?.defaultNamespace || undefined; const catalogApi = useApi(catalogApiRef); const { value: entities, loading } = useAsync(async () => { + const defaultFieldsToIgnore = [ + 'metadata.name', + 'metadata.namespace', + 'metadata.title', + 'kind', + ]; + const fields = fieldsToIgnore || defaultFieldsToIgnore; const { items } = await catalogApi.getEntities( - catalogFilter ? { filter: catalogFilter } : undefined, + catalogFilter + ? { filter: catalogFilter, fields: fields } + : { filter: undefined, fields: fields }, ); return items; }); diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts index eb1bba364b..5617ca2073 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts @@ -41,6 +41,13 @@ export const EntityPickerFieldSchema = makeFieldSchemaFromZod( .describe( 'DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from', ), + fieldsToIgnore: z + .array(z.string()) + .optional() + .describe( + 'Fields to ignore from loading - download only the parts of each entity that match the field declarations.' + + " Defaults to: fields: ['metadata.name', 'metadata.namespace', 'metadata.title', 'kind']", + ), defaultKind: z .string() .optional() From 747f9ba9cde7686121ca2c048af207e301a8e58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Vit=C3=A1sek?= Date: Tue, 30 Jan 2024 09:35:58 +0100 Subject: [PATCH 140/468] renamed `fields` configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ladislav Vitásek --- .changeset/four-walls-perform.md | 2 +- .../src/components/fields/EntityPicker/EntityPicker.tsx | 2 +- plugins/scaffolder/src/components/fields/EntityPicker/schema.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/four-walls-perform.md b/.changeset/four-walls-perform.md index bb30a0cc2b..e680a8b549 100644 --- a/.changeset/four-walls-perform.md +++ b/.changeset/four-walls-perform.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder': minor --- -Scaffolding - EntityPicker component - now it does not load full entity data, only a specified set - defaults to `['metadata.name', 'metadata.namespace', 'metadata.title', 'kind']`. It can significantly reduce loaded time bigger data set. It's possible to set fields to ignore configure via `fieldsToIgnore` in UI options of the EntityPicker component. +Scaffolding - EntityPicker component - now it does not load full entity data, only a specified set - defaults to `['metadata.name', 'metadata.namespace', 'metadata.title', 'kind']`. It can significantly reduce loaded time bigger data set. It's possible to set fields to ignore configure via `fields` in UI options of the EntityPicker component. diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index d2c88a0b03..760a14b8a8 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -61,7 +61,7 @@ export const EntityPicker = (props: EntityPickerProps) => { } = props; const catalogFilter = buildCatalogFilter(uiSchema); const defaultKind = uiSchema['ui:options']?.defaultKind; - const fieldsToIgnore = uiSchema['ui:options']?.fieldsToIgnore; + const fieldsToIgnore = uiSchema['ui:options']?.fields; const defaultNamespace = uiSchema['ui:options']?.defaultNamespace || undefined; diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts index 5617ca2073..fd02e80c73 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts @@ -41,7 +41,7 @@ export const EntityPickerFieldSchema = makeFieldSchemaFromZod( .describe( 'DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from', ), - fieldsToIgnore: z + fields: z .array(z.string()) .optional() .describe( From b8b68b34a9dc105043e0e958cdf8dea6ef70862f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Vit=C3=A1sek?= Date: Tue, 30 Jan 2024 09:38:04 +0100 Subject: [PATCH 141/468] renamed variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ladislav Vitásek --- .../src/components/fields/EntityPicker/EntityPicker.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index 760a14b8a8..6a74b9ae51 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -61,20 +61,20 @@ export const EntityPicker = (props: EntityPickerProps) => { } = props; const catalogFilter = buildCatalogFilter(uiSchema); const defaultKind = uiSchema['ui:options']?.defaultKind; - const fieldsToIgnore = uiSchema['ui:options']?.fields; + const customFields = uiSchema['ui:options']?.fields; const defaultNamespace = uiSchema['ui:options']?.defaultNamespace || undefined; const catalogApi = useApi(catalogApiRef); const { value: entities, loading } = useAsync(async () => { - const defaultFieldsToIgnore = [ + const defaultFields = [ 'metadata.name', 'metadata.namespace', 'metadata.title', 'kind', ]; - const fields = fieldsToIgnore || defaultFieldsToIgnore; + const fields = customFields || defaultFields; const { items } = await catalogApi.getEntities( catalogFilter ? { filter: catalogFilter, fields: fields } From 089f5800b48f3e73d5fe8a192c2ec6769ae7b915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Vit=C3=A1sek?= Date: Tue, 30 Jan 2024 09:41:47 +0100 Subject: [PATCH 142/468] change texting for `fields` describe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ladislav Vitásek --- plugins/scaffolder/src/components/fields/EntityPicker/schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts index fd02e80c73..0b9ddbe672 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts @@ -45,7 +45,7 @@ export const EntityPickerFieldSchema = makeFieldSchemaFromZod( .array(z.string()) .optional() .describe( - 'Fields to ignore from loading - download only the parts of each entity that match the field declarations.' + + 'Download only the parts of each entity that match the field declarations.' + " Defaults to: fields: ['metadata.name', 'metadata.namespace', 'metadata.title', 'kind']", ), defaultKind: z From 16e3f31218b5c3cfbbf0cbbe6708cc028aa8075d Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Tue, 30 Jan 2024 09:42:50 +0100 Subject: [PATCH 143/468] Update four-walls-perform.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ben Lambert Signed-off-by: blam Signed-off-by: Ladislav Vitásek --- .changeset/four-walls-perform.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.changeset/four-walls-perform.md b/.changeset/four-walls-perform.md index e680a8b549..b8e4c80416 100644 --- a/.changeset/four-walls-perform.md +++ b/.changeset/four-walls-perform.md @@ -2,4 +2,5 @@ '@backstage/plugin-scaffolder': minor --- -Scaffolding - EntityPicker component - now it does not load full entity data, only a specified set - defaults to `['metadata.name', 'metadata.namespace', 'metadata.title', 'kind']`. It can significantly reduce loaded time bigger data set. It's possible to set fields to ignore configure via `fields` in UI options of the EntityPicker component. +Updating the `EntityPicker` to only select by default `kind` `metadata.name` `metadata.namespace` and `metadata.title` by default to improve performance on larger datasets. + From 292c37b2fceac27131fd153bba3dde5f8fe09d3b Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Tue, 30 Jan 2024 10:40:28 +0100 Subject: [PATCH 144/468] Update quick-ties-stare.md Signed-off-by: Ben Lambert --- .changeset/quick-ties-stare.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/quick-ties-stare.md b/.changeset/quick-ties-stare.md index 137344512b..19eb50feed 100644 --- a/.changeset/quick-ties-stare.md +++ b/.changeset/quick-ties-stare.md @@ -3,4 +3,4 @@ '@backstage/backend-common': patch --- -Support `token` in `readTree` and `readUrl` +Support `token` in `readTree`, `readUrl` and `search` From 4687660941fe11329cdaf3f123c551ef1d50a653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 30 Jan 2024 10:48:46 +0100 Subject: [PATCH 145/468] Update docs/overview/threat-model.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Philipp Hugenroth Signed-off-by: Fredrik Adelöw --- docs/overview/threat-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/threat-model.md b/docs/overview/threat-model.md index 115c12b307..e1e1afd17e 100644 --- a/docs/overview/threat-model.md +++ b/docs/overview/threat-model.md @@ -52,7 +52,7 @@ As part of signing in with an identity resolver, a Backstage Token is issued con The token is used to prove the identity of the user within the Backstage system, and is used throughout Backstage plugins to control access. It is important that the ownership resolution logic is consistent across the entire Backstage ecosystem, with no possibility of misinterpreting the ownership information. -For cross-backend communication, the Backstage Token is typically forwarded or, in strict backend-to-backend communication without a user party, the backend itself issues a service token based on a pre-shared secret which is then validated on the receiving end. There are no unique service identities tied to these tokens at this point, meaning the tokens can be used across all services in a Backstage installation, this is something that we aim to improve in the future. +For cross-backend communication, the Backstage Token is typically forwarded or, in strict backend-to-backend communication without a user party, the backend itself issues a service token based on a pre-shared secret which is then validated on the receiving end. There are no unique service identities tied to these tokens at this point, meaning the tokens can be used across all services in a Backstage installation. This is something that we aim to improve in the future. Backstage also supports authentication through a proxy where the user identity is read from the incoming request from the proxy, which has been decorated by an authenticating reverse proxy such as [AWS ALB](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/). The following proxy auth providers verify the signature of incoming requests, and are therefore safe to deploy with direct access by users: `awsAlb`, `cfAccess`, and `gcpIap`. Providers like `oauth2Proxy` does not verify the incoming request and can therefore be spoofed by a malicious internal user to supply the `auth` backend with forged identity information. It’s therefore highly recommended to restrict access to the `oauth2Proxy` endpoints, or use a different provider. From d84d58028b886659930fecd36c23002b8f2bb9ad Mon Sep 17 00:00:00 2001 From: Richard Hallgren Date: Tue, 30 Jan 2024 11:08:00 +0100 Subject: [PATCH 146/468] Added Revision plugin to Backstage plugins via revision.yaml Signed-off-by: Richard Hallgren --- microsite/data/plugins/revision.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 microsite/data/plugins/revision.yaml diff --git a/microsite/data/plugins/revision.yaml b/microsite/data/plugins/revision.yaml new file mode 100644 index 0000000000..aeb0273602 --- /dev/null +++ b/microsite/data/plugins/revision.yaml @@ -0,0 +1,10 @@ +--- +title: Revision +author: Revision +authorUrl: https://github.com/revision-org +category: Visualization +description: Access Revision architecture diagrams from within Backstage +documentation: https://github.com/revision-org/backstage-plugins/tree/main/plugins/revision +iconUrl: https://raw.githubusercontent.com/revision-org/revision-assets/b4895ae036e99341576caf1c9bbf92ec89fb65dc/logo-black-full.svg +npmPackageName: '@revisionapp/backstage-revision-plugin' +addedDate: '2024-01-26' From b3a06270423604fdbbb36d85811bad7c34eb2624 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 30 Jan 2024 11:10:51 +0100 Subject: [PATCH 147/468] chore: move the messaging and fix windows verify builds Signed-off-by: blam --- .github/workflows/verify_windows.yml | 2 +- .../src/lib/templating/SecureTemplater.ts | 9 +++++ .../src/lib/templating/helpers.ts | 37 ++++++++++++++++++ .../scaffolder-backend/src/service/helpers.ts | 39 ------------------- .../scaffolder-backend/src/service/router.ts | 16 +------- 5 files changed, 48 insertions(+), 55 deletions(-) create mode 100644 plugins/scaffolder-backend/src/lib/templating/helpers.ts diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index 143b22c8b6..5b188770b4 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -21,7 +21,7 @@ jobs: env: CI: true - NODE_OPTIONS: --max-old-space-size=4096 + NODE_OPTIONS: ${{ matrix.node-version == '20.x' && '--max-old-space-size=4096 --no-node-snapshot' || '--max-old-space-size=4096' }} INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }} INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }} INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }} diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts index 607b1c6add..9c5fed3aa3 100644 --- a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts @@ -22,6 +22,7 @@ import { } from '@backstage/plugin-scaffolder-node'; import fs from 'fs-extra'; import { JsonValue } from '@backstage/types'; +import { getMajorNodeVersion, isNoNodeSnapshotOptionProvided } from './helpers'; // language=JavaScript const mkScript = (nunjucksSource: string) => ` @@ -128,6 +129,14 @@ export class SecureTemplater { nunjucksConfigs = {}, } = options; + const nodeVersion = getMajorNodeVersion(); + if (nodeVersion >= 20 && !isNoNodeSnapshotOptionProvided()) { + throw new Error( + `When using node v20+ Scaffolder requires that node be started with the --no-node-snapshot option. + Please make sure that you have NODE_OPTIONS=--no-node-snapshot in your environment.`, + ); + } + const isolate = new Isolate({ memoryLimit: 128 }); const context = await isolate.createContext(); const contextGlobal = context.global; diff --git a/plugins/scaffolder-backend/src/lib/templating/helpers.ts b/plugins/scaffolder-backend/src/lib/templating/helpers.ts new file mode 100644 index 0000000000..3dfa4589bf --- /dev/null +++ b/plugins/scaffolder-backend/src/lib/templating/helpers.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2024 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 function isNoNodeSnapshotOptionProvided(): boolean { + return ( + process.env.NODE_OPTIONS?.includes('--no-node-snapshot') || + process.argv.includes('--no-node-snapshot') + ); +} + +/** + * Gets the major version of the currently running Node.js process. + * + * @remarks + * This function extracts the major version from `process.versions.node` (a string representing the Node.js version), + * which includes the major, minor, and patch versions. It splits this string by the `.` character to get an array + * of these versions, and then parses the first element of this array (the major version) to a number. + * + * @returns {number} The major version of the currently running Node.js process. + */ +export function getMajorNodeVersion(): number { + const version = process.versions.node; + return parseInt(version.split('.')[0], 10); +} diff --git a/plugins/scaffolder-backend/src/service/helpers.ts b/plugins/scaffolder-backend/src/service/helpers.ts index 9e271a9d79..3412fec2d3 100644 --- a/plugins/scaffolder-backend/src/service/helpers.ts +++ b/plugins/scaffolder-backend/src/service/helpers.ts @@ -107,42 +107,3 @@ export async function findTemplate(options: { return template as TemplateEntityV1beta3; } - -/** - * Checks if the '--no-node-snapshot' option is included in the NODE_OPTIONS environment variable - * or not included in the command line arguments. - * - * @remarks - * This function checks whether the '--no-node-snapshot' option is part of the NODE_OPTIONS environment - * variable or is missing from the command line arguments. If either condition is met, the function returns `true`. - * This check is especially important when using the "isolated-vm" package with Node.js version 20.x or later. - * - * According to the "isolated-vm" documentation on GitHub (https://github.com/laverdet/isolated-vm), - * if you are using a version of Node.js 20.x or later and you don't pass the '--no-node-snapshot' option, - * it can cause the process to crash. This function helps prevent such crashes by ensuring that the option - * is correctly provided. - * - * @returns {boolean} Returns `true` if the '--no-node-snapshot' option is included in the NODE_OPTIONS - * environment variable or not included in the command line arguments. Otherwise, it returns `false`. - */ -export function isNoNodeSnapshotOptionProvided(): boolean { - return ( - process.env.NODE_OPTIONS?.includes('--no-node-snapshot') || - process.argv.includes('--no-node-snapshot') - ); -} - -/** - * Gets the major version of the currently running Node.js process. - * - * @remarks - * This function extracts the major version from `process.versions.node` (a string representing the Node.js version), - * which includes the major, minor, and patch versions. It splits this string by the `.` character to get an array - * of these versions, and then parses the first element of this array (the major version) to a number. - * - * @returns {number} The major version of the currently running Node.js process. - */ -export function getMajorNodeVersion(): number { - const version = process.versions.node; - return parseInt(version.split('.')[0], 10); -} diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index dec3dcf69a..c0322af283 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -61,13 +61,7 @@ import { } from '../scaffolder'; import { createDryRunner } from '../scaffolder/dryrun'; import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; -import { - findTemplate, - getEntityBaseUrl, - getMajorNodeVersion, - getWorkingDirectory, - isNoNodeSnapshotOptionProvided, -} from './helpers'; +import { findTemplate, getEntityBaseUrl, getWorkingDirectory } from './helpers'; import { IdentityApi, IdentityApiGetIdentityRequest, @@ -266,14 +260,6 @@ export async function createRouter( const logger = parentLogger.child({ plugin: 'scaffolder' }); - const nodeVersion = getMajorNodeVersion(); - if (nodeVersion >= 20 && !isNoNodeSnapshotOptionProvided()) { - throw new Error( - 'When using node v20+ Scaffolder requires that node be started with the --no-node-snapshot option. Please restart ' + - 'Backstage providing the node --no-node-snapshot option.', - ); - } - const identity: IdentityApi = options.identity || buildDefaultIdentityClient(options); const workingDirectory = await getWorkingDirectory(config, logger); From f6792c6758d5562879cfa833bef2dba636d64aa8 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 30 Jan 2024 11:14:07 +0100 Subject: [PATCH 148/468] chore: updating messaging Signed-off-by: blam --- .changeset/funny-buttons-sip.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/funny-buttons-sip.md diff --git a/.changeset/funny-buttons-sip.md b/.changeset/funny-buttons-sip.md new file mode 100644 index 0000000000..c455b8a31b --- /dev/null +++ b/.changeset/funny-buttons-sip.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Move the `NODE_OPTIONS` messaging for `--no-node-snapshot` to the `SecureTemplater` in order to get better messaging at runtime From c25c129ad1aa4677c3261b2904ae9f0d1598cd25 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 30 Jan 2024 11:18:10 +0100 Subject: [PATCH 149/468] Update docs/frontend-system/architecture/03-extensions.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Philipp Hugenroth --- docs/frontend-system/architecture/03-extensions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/03-extensions.md b/docs/frontend-system/architecture/03-extensions.md index cdee6acb2a..82375eae3b 100644 --- a/docs/frontend-system/architecture/03-extensions.md +++ b/docs/frontend-system/architecture/03-extensions.md @@ -135,7 +135,7 @@ const extension = createExtension({ We provide default `coreExtensionData`, which provides commonly used `ExtensionDataRef`s - e.g. for `React.JSX.Element` and `RouteRef`. They can be used when creating your own extension. For example, the React Element extension data that we defined above is already provided as `coreExtensionData.reactElement`. -For a full list and explanations of all types of core extension data, see the [core extension data reference](../building-plugins/04-built-in-data-refs.md). --> +For a full list and explanations of all types of core extension data, see the [core extension data reference](../building-plugins/04-built-in-data-refs.md). ### Optional Extension Data From c91b6d96c396956939f658ea57e73578ab4d11d2 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 30 Jan 2024 11:18:33 +0100 Subject: [PATCH 150/468] Update docs/frontend-system/building-plugins/04-built-in-data-refs.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Philipp Hugenroth --- docs/frontend-system/building-plugins/04-built-in-data-refs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md index c338dddc4a..d3b1468dd0 100644 --- a/docs/frontend-system/building-plugins/04-built-in-data-refs.md +++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md @@ -12,7 +12,7 @@ To have a better understanding of extension data references please read [extensi ## Built-in extension data references -Data references help to define the input & output of an extension. A data ref is uniquely identified through there `id`. Through the data ref strong typing is enforced for the input/output of the extension. +Data references help to define the inputs and outputs of an extension. A data ref is uniquely identified through its `id`. Through the data ref, strong typing is enforced for the input/output of the extension. ### Core Extension Data From ae9bb27dbcba3a38666ded6fb93170443ee828de Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 30 Jan 2024 11:18:42 +0100 Subject: [PATCH 151/468] Update docs/frontend-system/building-plugins/04-built-in-data-refs.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Philipp Hugenroth --- docs/frontend-system/building-plugins/04-built-in-data-refs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md index d3b1468dd0..ec48b74499 100644 --- a/docs/frontend-system/building-plugins/04-built-in-data-refs.md +++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md @@ -8,7 +8,7 @@ description: Configuring or overriding built-in extension data references > **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.** -To have a better understanding of extension data references please read [extension data section the extensions architecture documentation](../architecture/03-extensions.md#extension-data) first. +To have a better understanding of extension data references please read [the corresponding architecture section](../architecture/03-extensions.md#extension-data) first. ## Built-in extension data references From 674c9fbf36c5063c145fedece763e38380037bfe Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 10:23:46 +0000 Subject: [PATCH 152/468] fix(deps): update dependency @keyv/redis to v2.8.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 1d31e0fc44..79d27b5501 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12178,11 +12178,11 @@ __metadata: linkType: hard "@keyv/redis@npm:^2.5.3": - version: 2.8.3 - resolution: "@keyv/redis@npm:2.8.3" + version: 2.8.4 + resolution: "@keyv/redis@npm:2.8.4" dependencies: ioredis: ^5.3.2 - checksum: 4b8fa4baeab75aace0a8c5d02e30dfb87fa2e27236d8ce3577ff862c4efa2ae2f6d4c14b4933e08fd24fd2719a01d17ac596e20792a1d3c0c2af51a46ba48936 + checksum: 088fb439dc900d6c848c187a0a3218f8c80e3d5df0ec94995d2245b701d59c9d99d4ccc2b48b12682e20d1c0653ababc2f7a51a04737c4df539f4dac82eca87b languageName: node linkType: hard From 26ca6a43424ee621d4f521572f6052680b0859f6 Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Tue, 30 Jan 2024 11:34:28 +0100 Subject: [PATCH 153/468] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Ben Lambert Signed-off-by: blam --- .../scaffolder-backend/src/lib/templating/SecureTemplater.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts index 9c5fed3aa3..14a58e4975 100644 --- a/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts +++ b/plugins/scaffolder-backend/src/lib/templating/SecureTemplater.ts @@ -132,7 +132,7 @@ export class SecureTemplater { const nodeVersion = getMajorNodeVersion(); if (nodeVersion >= 20 && !isNoNodeSnapshotOptionProvided()) { throw new Error( - `When using node v20+ Scaffolder requires that node be started with the --no-node-snapshot option. + `When using Node.js version 20 or newer, the scaffolder backend plugin requires that it be started with the --no-node-snapshot option. Please make sure that you have NODE_OPTIONS=--no-node-snapshot in your environment.`, ); } From c88ec5c06d19182c48a96c0b1af5dc0d96af10f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ladislav=20Vit=C3=A1sek?= Date: Tue, 30 Jan 2024 11:42:05 +0100 Subject: [PATCH 154/468] bare minimum to resolve large datasets problem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ladislav Vitásek --- .changeset/four-walls-perform.md | 3 +-- .../src/components/fields/EntityPicker/EntityPicker.tsx | 9 +-------- .../src/components/fields/EntityPicker/schema.ts | 7 ------- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/.changeset/four-walls-perform.md b/.changeset/four-walls-perform.md index b8e4c80416..5275da7b79 100644 --- a/.changeset/four-walls-perform.md +++ b/.changeset/four-walls-perform.md @@ -2,5 +2,4 @@ '@backstage/plugin-scaffolder': minor --- -Updating the `EntityPicker` to only select by default `kind` `metadata.name` `metadata.namespace` and `metadata.title` by default to improve performance on larger datasets. - +Updating the `EntityPicker` to only select `kind` `metadata.name` and `metadata.namespace` by default to improve performance on larger datasets. diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index 6a74b9ae51..c9b74b49c7 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -61,20 +61,13 @@ export const EntityPicker = (props: EntityPickerProps) => { } = props; const catalogFilter = buildCatalogFilter(uiSchema); const defaultKind = uiSchema['ui:options']?.defaultKind; - const customFields = uiSchema['ui:options']?.fields; const defaultNamespace = uiSchema['ui:options']?.defaultNamespace || undefined; const catalogApi = useApi(catalogApiRef); const { value: entities, loading } = useAsync(async () => { - const defaultFields = [ - 'metadata.name', - 'metadata.namespace', - 'metadata.title', - 'kind', - ]; - const fields = customFields || defaultFields; + const fields = ['metadata.name', 'metadata.namespace', 'kind']; const { items } = await catalogApi.getEntities( catalogFilter ? { filter: catalogFilter, fields: fields } diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts index 0b9ddbe672..eb1bba364b 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityPicker/schema.ts @@ -41,13 +41,6 @@ export const EntityPickerFieldSchema = makeFieldSchemaFromZod( .describe( 'DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from', ), - fields: z - .array(z.string()) - .optional() - .describe( - 'Download only the parts of each entity that match the field declarations.' + - " Defaults to: fields: ['metadata.name', 'metadata.namespace', 'metadata.title', 'kind']", - ), defaultKind: z .string() .optional() From 03d9a7406f365131376b5176fe72bb28ec2fc57b Mon Sep 17 00:00:00 2001 From: Ben Lambert Date: Tue, 30 Jan 2024 11:44:49 +0100 Subject: [PATCH 155/468] Update EntityPicker.tsx Signed-off-by: Ben Lambert --- .../src/components/fields/EntityPicker/EntityPicker.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index c9b74b49c7..76dbe42332 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -70,8 +70,8 @@ export const EntityPicker = (props: EntityPickerProps) => { const fields = ['metadata.name', 'metadata.namespace', 'kind']; const { items } = await catalogApi.getEntities( catalogFilter - ? { filter: catalogFilter, fields: fields } - : { filter: undefined, fields: fields }, + ? { filter: catalogFilter, fields } + : { filter: undefined, fields }, ); return items; }); From 3da24c32bb6a3ce93ff567b6e2e215f71ec404ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 10:54:35 +0000 Subject: [PATCH 156/468] fix(deps): update dependency axios to v1.6.7 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 1d31e0fc44..4ec134797f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21036,7 +21036,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:1.6.5, axios@npm:^1.0.0, axios@npm:^1.4.0, axios@npm:^1.6.0": +"axios@npm:1.6.5": version: 1.6.5 resolution: "axios@npm:1.6.5" dependencies: @@ -21066,6 +21066,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.0.0, axios@npm:^1.4.0, axios@npm:^1.6.0": + version: 1.6.7 + resolution: "axios@npm:1.6.7" + dependencies: + follow-redirects: ^1.15.4 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: 87d4d429927d09942771f3b3a6c13580c183e31d7be0ee12f09be6d5655304996bb033d85e54be81606f4e89684df43be7bf52d14becb73a12727bf33298a082 + languageName: node + linkType: hard + "axobject-query@npm:^3.2.1": version: 3.2.1 resolution: "axobject-query@npm:3.2.1" From f72b0325cbadcc50dcf03489a00c508785e03f0d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 30 Jan 2024 12:16:31 +0100 Subject: [PATCH 157/468] beps: add Auth Architecture Evolution Signed-off-by: Patrik Oldsberg --- .../README.md | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 beps/0003-auth-architecture-evolution/README.md diff --git a/beps/0003-auth-architecture-evolution/README.md b/beps/0003-auth-architecture-evolution/README.md new file mode 100644 index 0000000000..f70f19e8f9 --- /dev/null +++ b/beps/0003-auth-architecture-evolution/README.md @@ -0,0 +1,141 @@ +--- +title: Auth Architecture Evolution +status: provisional +authors: + - '@Rugvip' +owners: + - '@backstage/maintainers' +project-areas: + - core +creation-date: 2024-01-28 +--- + +# BEP: Auth Architecture Evolution + + + +[**Discussion Issue**](https://github.com/backstage/backstage/issues/NNNNN) + +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) +- [Proposal](#proposal) +- [Design Details](#design-details) +- [Release Plan](#release-plan) +- [Dependencies](#dependencies) +- [Alternatives](#alternatives) + +## Summary + +This proposal outlines a new architecture for authenticating users and services in Backstage. It adds built-in access restriction to Backstage instances that protects them from outside access, and enables more granular access control for inter-plugin communication and access from external services. + +It proposes a new `AuthService` interface that handles all uses and service authentication and token management available to plugins, as well as a new `HttpAuthService` interface that is a higher level service used to protect endpoints of plugin routers. The `auth-backend` will also be extended to support issuing of user tokens with a reduced scope for cookie-based authentication of requests. + +The changes to the service-to-service auth are aimed to be the minimal needed to get the necessary interfaces in place, and will rely on the existing symmetrical keys for now. + +## Motivation + +This proposal aims to address several of the points in the [Auth Meta issue](https://github.com/backstage/backstage/issues/15999), with the overarching goal being to replace the existing [API request authentication](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial in `contrib/` with a more robust and secure built-in solution. The tutorial exists for two purposes: to add authentication of API requests as part of using the permission system in Backstage, and to protect a Backstage instance from external access. It does a fairly good job of the former, although we want to avoid placing user tokens in cookies, but it does a quite poor job of the latter, which we want to fix. + +A secondary goal is to do this work before stabilizing the APIs in the new Backend system, as it will have some impact on how plugin backends are built. This will inevitably also lead to the need to improve the way that service-to-service auth it handled in Backstage, although that is not the primary goal of this work. + +By providing protection of Backstage instances out of the box we drastically reduce both the barrier of entry as well as security risks for Backstage adopters. It will no longer be a requirement to either set up protection of your Backstage instance or not do so and risk exposing your instance to malicious actors. It also drastically reduces the complexity of enabling the permission system of Backstage, since access restrictions are already built-in. + +### Goals + +The following goals are the primary focus of this BEP: + +- Built-in protection of Backstage instances such that it is safe to deploy Backstage directly towards the internet. + - Protection of the frontend app bundle from being accessed by unauthenticated users. + - Basic rate limiting of non-authenticated requests. + - Cookie-based authentication of requests for static assets that protects against CSRF attacks and does not unnecessarily expose user tokens. +- Basic improvements to the service-to-service auth service interfaces such that we are confident that we do not need to break them in the near future. + - If possible we will keep using the existing symmetrical keys that are used today, but it is likely that we will need to break compatibility of existing tokens. + +### Non-Goals + +- No advanced rate limiting or other protection against DDoS attacks. If this is a concern then adopters should still use other external technologies to protect access to their Backstage instance. +- As part of the immediate work we will only add as much support for service-to-service auth as is needed for a stable API, and not necessarily make it very capable from the start. + +## Proposal + +Two new backend service interfaces are introduced to support these new features. The new `AuthService` is a low-level service that encapsulates authentication and creation of bearer tokens for all types of Backstage identities, including user, service, and services making requests on behalf of users. The new `HttpAuthService` is a higher level service that lets your create [express middleware](https://expressjs.com/en/guide/using-middleware.html) to protect endpoints, as well as access the credentials and identity of incoming requests. These new services replace the existing `IdentityService` and `TokenManagerService` + +The proposed design leaves the decision for how different endpoints are protected to the implementation of the plugin backends themselves. This includes whether particular routes should allow anonymous access, access from users authenticated via a cookie, or perhaps only allow access from other plugin backends and external services. This means that integrators to not need to and do not have the ability to configure access controls of individual endpoints, except for what the permission system already provides, and what is made available through static configuration or extension points. + +In order to allow for cookie-based authentication of incoming users requests, the `auth` plugin backend is extended to be able to issue users tokens with reduced scope, which in turn integrate with the new `AuthService` and `HttpAuthService`. The ability to use cookie auth for request is opt-in per route and is only be permitted for read methods (`GET`, `HEAD`, `OPTIONS`). The actual implementation of cookie-based flows will be up to each plugin, but with significant help from the new auth service interfaces. + +For service-to-service communication we will move away from reusing user tokens in upstream requests. We will instead implement an "On-Behalf-Of" flow where incoming user tokens are encapsulated in a service credential for the upstream request. In line with this the new auth service interfaces will aim to make it difficult to directly forward credentials from incoming requests, and instead encourage that plugin backends issue new service credentials for upstream requests. + +## Design Details + +### AuthService (WIP) + +The new `AuthService` interface is defined as follows: + +```ts +export type BackstageCredentials = { + token: string; + + user?: { + userEntityRef: string; + ownershipEntityRefs: string[]; + }; + + service?: { + id: string; + }; +}; + +export interface AuthService { + authenticate(token: string): Promise; + + issueToken(credentials: BackstageCredentials): Promise<{ token: string }>; +} +``` + +TODO: add usage patterns for this service + +### HttpAuthService (WIP) + +The new `HttpAuthService` interface is defined as follows: + +```ts +type AuthTypes = 'user' | 'user-cookie' | 'service' | 'unauthorized'; + +export interface HttpAuthServiceMiddlewareOptions { + allow: AuthTypes[]; +} + +export interface HttpAuthService { + createHttpPluginRouterMiddleware(): Handler; + + middleware(options?: HttpAuthServiceMiddlewareOptions): Handler; + + credentials( + req: Request, + options?: HttpAuthServiceMiddlewareOptions, + ): BackstageCredentials; + + requestHeaders(credentials: BackstageCredentials): Record; + + issueUserCookie(res: Response): Promise; +} +``` + +TODO: add usage patterns for this service + +## Release Plan + +The existing `IdentityService` and `TokenManagerService` will be deprecated and instead implemented in terms of the new `AuthService`. + +The release plan for the `HttpAuthService` is TBD, but is likely to be shipped as a no-op for plugins using the old backend system. + +## Dependencies + +No significant dependencies have been identified for this work, although any future security audits of Backstage are considered dependent on this work. + +## Alternatives + +An alternative to built-in protection from external access would be to keep relying on external mechanisms to protect access to Backstage. We feel that this is a suboptimal solution since it adds complexity to the adoption of Backstage, and increases the risk of misconfiguration and security breaches. Regardless of whether we add built-in protection or not the ability to protect API endpoints needs to be addressed in some way, since it is a requirement for the permission system to work. This means that the extra steps to ensure protection out of the box are fairly minimal when looking at just the delta for protecting API access. From 571f6bceffcd6c773476382633b8ac74b0e6a3e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 11:22:49 +0000 Subject: [PATCH 158/468] fix(deps): update dependency express-openapi-validator to v5.1.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 79d27b5501..c7c275d5d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26880,8 +26880,8 @@ __metadata: linkType: hard "express-openapi-validator@npm:^5.0.4": - version: 5.1.2 - resolution: "express-openapi-validator@npm:5.1.2" + version: 5.1.3 + resolution: "express-openapi-validator@npm:5.1.3" dependencies: "@apidevtools/json-schema-ref-parser": ^9.1.2 "@types/multer": ^1.4.7 @@ -26898,7 +26898,7 @@ __metadata: multer: ^1.4.5-lts.1 ono: ^7.1.3 path-to-regexp: ^6.2.0 - checksum: e02eaad8549893f874916cfc52a9d81f1ef15c553e726876e6b73cc93469a21e28e42d1d25449aa04c764f8d024b1ea664b7ce6083abdd8513168ece7929ee20 + checksum: c99785e6bf3072086a671e854369e338bce2a20fea65d2a4445dac6c40ac632e67dc91d9e0edeb4cce5e739124b5d5742bcf2447c09b3085dc76e1ba89a71ca0 languageName: node linkType: hard From 3fe92995fd5bac066ea0210fca5744fb4928dfce Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 25 Jan 2024 09:18:20 +0100 Subject: [PATCH 159/468] feat(analytics-module-ga4): add support to the new analytics api Signed-off-by: Camila Belo --- plugins/analytics-module-ga4/package.json | 1 + .../AnalyticsApi/GoogleAnalytics4.test.ts | 38 +++++++++++++++++++ .../AnalyticsApi/GoogleAnalytics4.ts | 21 +++++++--- yarn.lock | 1 + 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/plugins/analytics-module-ga4/package.json b/plugins/analytics-module-ga4/package.json index 97b610c2bf..89113efe6d 100644 --- a/plugins/analytics-module-ga4/package.json +++ b/plugins/analytics-module-ga4/package.json @@ -32,6 +32,7 @@ "@backstage/config": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "react-ga4": "^2.0.0" }, "peerDependencies": { diff --git a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts index 16db7075b3..5bae530b2b 100644 --- a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts +++ b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts @@ -492,4 +492,42 @@ describe('GoogleAnalytics4', () => { }); }); }); + + describe('Backward compatibility', () => { + it('fallback category for legacy context extension property', () => { + const api = GoogleAnalytics4.fromConfig(basicValidConfig); + + expect(api.captureEvent).toBeDefined(); + + api.captureEvent({ + action: 'navigate', + subject: '/', + context, + }); + + expect(fnEvent).toHaveBeenCalledWith('page_view', { + action: 'page_view', + label: '/', + category: 'App', + }); + }); + + it('prioritize new context extension id over old extension property', () => { + const api = GoogleAnalytics4.fromConfig(basicValidConfig); + + expect(api.captureEvent).toBeDefined(); + + api.captureEvent({ + action: 'navigate', + subject: '/', + context: { ...context, extensionId: 'App', extension: '' }, + }); + + expect(fnEvent).toHaveBeenCalledWith('page_view', { + action: 'page_view', + label: '/', + category: 'App', + }); + }); + }); }); diff --git a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts index 9abf4719c6..3d2e633962 100644 --- a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts +++ b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import ReactGA from 'react-ga4'; import { AnalyticsApi, @@ -21,6 +22,11 @@ import { AnalyticsEvent, IdentityApi, } from '@backstage/core-plugin-api'; +import { + AnalyticsApi as NewAnalyticsApi, + AnalyticsEvent as NewAnalyticsEvent, + AnalyticsContextValue as NewAnalyticsContextValue, +} from '@backstage/frontend-plugin-api'; import { Config } from '@backstage/config'; import { DeferredCapture } from '../../../util/DeferredCapture'; @@ -28,7 +34,7 @@ import { DeferredCapture } from '../../../util/DeferredCapture'; * Google Analytics API provider for the Backstage Analytics API. * @public */ -export class GoogleAnalytics4 implements AnalyticsApi { +export class GoogleAnalytics4 implements AnalyticsApi, NewAnalyticsApi { private readonly customUserIdTransform?: ( userEntityRef: string, ) => Promise; @@ -157,17 +163,20 @@ export class GoogleAnalytics4 implements AnalyticsApi { * applied as they should be (set on pageview, merged object on events). * @param event - AnalyticsEvent type captured */ - captureEvent(event: AnalyticsEvent) { + captureEvent(event: AnalyticsEvent | NewAnalyticsEvent) { const { context, action, subject, value, attributes } = event; const customEventData = this.setEventParameters(context, attributes); if (this.contentGroupBy) { customEventData.content_group = context[this.contentGroupBy]!; } - if (action === 'navigate' && context.extension === 'App') { + const extensionId = context.extensionId || context.extension; + const category = extensionId ? String(extensionId) : 'App'; + + if (action === 'navigate' && category === 'App') { this.capture.event( { - category: context.extension || 'App', + category, action: 'page_view', label: subject, value, @@ -183,7 +192,7 @@ export class GoogleAnalytics4 implements AnalyticsApi { this.capture.event( { - category: context.extension || 'App', + category, action, label: subject, value, @@ -199,7 +208,7 @@ export class GoogleAnalytics4 implements AnalyticsApi { * @param attributes additional analytics event attributes */ private setEventParameters( - context: AnalyticsContextValue, + context: AnalyticsContextValue | NewAnalyticsContextValue, attributes: AnalyticsEventAttributes = {}, ) { const customEventParameters: { diff --git a/yarn.lock b/yarn.lock index 79d27b5501..5f26cb9a82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4312,6 +4312,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 From 87f3d69dc50bdd5af49c02153a4c7a86daa9bab2 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 25 Jan 2024 09:19:27 +0100 Subject: [PATCH 160/468] fix(frontend-app-api): add global analytics context to the app extension Signed-off-by: Camila Belo --- packages/frontend-app-api/src/extensions/App.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/frontend-app-api/src/extensions/App.tsx b/packages/frontend-app-api/src/extensions/App.tsx index 751a255fa7..82e7600178 100644 --- a/packages/frontend-app-api/src/extensions/App.tsx +++ b/packages/frontend-app-api/src/extensions/App.tsx @@ -14,7 +14,9 @@ * limitations under the License. */ +import React from 'react'; import { + ExtensionBoundary, coreExtensionData, createApiExtension, createComponentExtension, @@ -50,9 +52,13 @@ export const App = createExtension({ output: { root: coreExtensionData.reactElement, }, - factory({ inputs }) { + factory({ node, inputs }) { return { - root: inputs.root.output.element, + root: ( + + {inputs.root.output.element} + + ), }; }, }); From 01abfa9c2b5884c2ccbd1deb3aa729fe6202a3e8 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 25 Jan 2024 09:22:26 +0100 Subject: [PATCH 161/468] feat(frontend-plugin-api): fix default app plugin id Signed-off-by: Camila Belo --- .../frontend-plugin-api/src/analytics/AnalyticsContext.tsx | 2 +- packages/frontend-plugin-api/src/analytics/types.ts | 3 --- .../frontend-plugin-api/src/components/ExtensionBoundary.tsx | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx index 10ef475803..2c022ad8e9 100644 --- a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx +++ b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx @@ -37,7 +37,7 @@ export const useAnalyticsContext = (): AnalyticsContextValue => { // Provide a default value if no value exists. if (theContext === undefined) { return { - pluginId: 'root', + pluginId: 'app', extensionId: 'App', }; } diff --git a/packages/frontend-plugin-api/src/analytics/types.ts b/packages/frontend-plugin-api/src/analytics/types.ts index 21347e2fc7..3e3a9ad5eb 100644 --- a/packages/frontend-plugin-api/src/analytics/types.ts +++ b/packages/frontend-plugin-api/src/analytics/types.ts @@ -25,9 +25,6 @@ export type CommonAnalyticsContext = { */ pluginId: string; - /** - * The nearest known parent extension where the event was captured. - */ /** * The nearest known parent extension where the event was captured. */ diff --git a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx index 9a8eb5b750..768352b76e 100644 --- a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx +++ b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx @@ -65,7 +65,7 @@ export function ExtensionBoundary(props: ExtensionBoundaryProps) { // Skipping "routeRef" attribute in the new system, the extension "id" should provide more insight const attributes = { extensionId: node.spec.id, - pluginId: node.spec.source?.id, + pluginId: node.spec.source?.id ?? 'app', }; return ( From b1afb710b8cf23c17c112d4161e983419509b547 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 25 Jan 2024 11:04:20 +0100 Subject: [PATCH 162/468] fix(frontend-plugin-api): default api config error Signed-off-by: Camila Belo --- packages/core-plugin-api/package.json | 1 + .../src/apis/system/useApi.tsx | 11 +++++--- .../src/routing/RouteTracker.test.tsx | 8 +++--- .../src/analytics/AnalyticsContext.test.tsx | 10 +++---- .../src/analytics/AnalyticsContext.tsx | 2 +- .../src/analytics/useAnalytics.test.tsx | 4 +-- .../src/analytics/useAnalytics.tsx | 7 +++-- .../AnalyticsApi/GoogleAnalytics4.test.ts | 26 ++++++++++++++++--- .../AnalyticsApi/GoogleAnalytics4.ts | 5 ++-- yarn.lock | 1 + 10 files changed, 51 insertions(+), 24 deletions(-) diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index d676260227..68d5ad68c3 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -47,6 +47,7 @@ }, "dependencies": { "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", "@types/react": "^16.13.1 || ^17.0.0", diff --git a/packages/core-plugin-api/src/apis/system/useApi.tsx b/packages/core-plugin-api/src/apis/system/useApi.tsx index 03c4fa33ac..2bb1532525 100644 --- a/packages/core-plugin-api/src/apis/system/useApi.tsx +++ b/packages/core-plugin-api/src/apis/system/useApi.tsx @@ -17,6 +17,7 @@ import React, { PropsWithChildren } from 'react'; import { ApiRef, ApiHolder, TypesToApiRefs } from './types'; import { useVersionedContext } from '@backstage/version-bridge'; +import { NotImplementedError } from '@backstage/errors'; /** * React hook for retrieving {@link ApiHolder}, an API catalog. @@ -26,12 +27,12 @@ import { useVersionedContext } from '@backstage/version-bridge'; export function useApiHolder(): ApiHolder { const versionedHolder = useVersionedContext<{ 1: ApiHolder }>('api-context'); if (!versionedHolder) { - throw new Error('API context is not available'); + throw new NotImplementedError('API context is not available'); } const apiHolder = versionedHolder.atVersion(1); if (!apiHolder) { - throw new Error('ApiContext v1 not available'); + throw new NotImplementedError('ApiContext v1 not available'); } return apiHolder; } @@ -47,7 +48,7 @@ export function useApi(apiRef: ApiRef): T { const api = apiHolder.get(apiRef); if (!api) { - throw new Error(`No implementation available for ${apiRef}`); + throw new NotImplementedError(`No implementation available for ${apiRef}`); } return api; } @@ -73,7 +74,9 @@ export function withApis(apis: TypesToApiRefs) { const api = apiHolder.get(ref); if (!api) { - throw new Error(`No implementation available for ${ref}`); + throw new NotImplementedError( + `No implementation available for ${ref}`, + ); } impls[key] = api; } diff --git a/packages/frontend-app-api/src/routing/RouteTracker.test.tsx b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx index 0e66d0e50d..935449b697 100644 --- a/packages/frontend-app-api/src/routing/RouteTracker.test.tsx +++ b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx @@ -211,8 +211,8 @@ describe('RouteTracker', () => { action: 'navigate', attributes: {}, context: { - extensionId: 'App', - pluginId: 'root', + extensionId: 'app', + pluginId: 'app', }, subject: '/not-routable-extension', value: undefined, @@ -221,8 +221,8 @@ describe('RouteTracker', () => { action: 'click', attributes: undefined, context: { - extensionId: 'App', - pluginId: 'root', + extensionId: 'app', + pluginId: 'app', }, subject: 'test', value: undefined, diff --git a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.test.tsx b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.test.tsx index 8e78bdc8c4..bdfdef3005 100644 --- a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.test.tsx +++ b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.test.tsx @@ -35,8 +35,8 @@ describe('AnalyticsContext', () => { it('returns default values', () => { const { result } = renderHook(() => useAnalyticsContext()); expect(result.current).toEqual({ - extensionId: 'App', - pluginId: 'root', + extensionId: 'app', + pluginId: 'app', }); }); }); @@ -49,8 +49,8 @@ describe('AnalyticsContext', () => { , ); - expect(result.getByTestId('extension-id')).toHaveTextContent('App'); - expect(result.getByTestId('plugin-id')).toHaveTextContent('root'); + expect(result.getByTestId('extension-id')).toHaveTextContent('app'); + expect(result.getByTestId('plugin-id')).toHaveTextContent('app'); }); it('uses provided analytics context', () => { @@ -60,7 +60,7 @@ describe('AnalyticsContext', () => { , ); - expect(result.getByTestId('extension-id')).toHaveTextContent('App'); + expect(result.getByTestId('extension-id')).toHaveTextContent('app'); expect(result.getByTestId('plugin-id')).toHaveTextContent('custom'); }); diff --git a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx index 2c022ad8e9..13047b41d7 100644 --- a/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx +++ b/packages/frontend-plugin-api/src/analytics/AnalyticsContext.tsx @@ -38,7 +38,7 @@ export const useAnalyticsContext = (): AnalyticsContextValue => { if (theContext === undefined) { return { pluginId: 'app', - extensionId: 'App', + extensionId: 'app', }; } diff --git a/packages/frontend-plugin-api/src/analytics/useAnalytics.test.tsx b/packages/frontend-plugin-api/src/analytics/useAnalytics.test.tsx index beb16b1fbb..6dbcc81f11 100644 --- a/packages/frontend-plugin-api/src/analytics/useAnalytics.test.tsx +++ b/packages/frontend-plugin-api/src/analytics/useAnalytics.test.tsx @@ -53,8 +53,8 @@ describe('useAnalytics', () => { some: 'value', }, context: { - extensionId: 'App', - pluginId: 'root', + extensionId: 'app', + pluginId: 'app', }, }); }); diff --git a/packages/frontend-plugin-api/src/analytics/useAnalytics.tsx b/packages/frontend-plugin-api/src/analytics/useAnalytics.tsx index f3b5998de4..397906d1d1 100644 --- a/packages/frontend-plugin-api/src/analytics/useAnalytics.tsx +++ b/packages/frontend-plugin-api/src/analytics/useAnalytics.tsx @@ -23,8 +23,11 @@ import { Tracker } from './Tracker'; function useAnalyticsApi(): AnalyticsApi { try { return useApi(analyticsApiRef); - } catch { - return { captureEvent: () => {} }; + } catch (error) { + if (error.name === 'NotImplementedError') { + return { captureEvent: () => {} }; + } + throw error; } } diff --git a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts index 5bae530b2b..93a6e6fe6a 100644 --- a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts +++ b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts @@ -493,8 +493,8 @@ describe('GoogleAnalytics4', () => { }); }); - describe('Backward compatibility', () => { - it('fallback category for legacy context extension property', () => { + describe('api backward compatibility', () => { + it('continue working with legacy App category', () => { const api = GoogleAnalytics4.fromConfig(basicValidConfig); expect(api.captureEvent).toBeDefined(); @@ -512,6 +512,24 @@ describe('GoogleAnalytics4', () => { }); }); + it('use lowercase app as the new default category', () => { + const api = GoogleAnalytics4.fromConfig(basicValidConfig); + + expect(api.captureEvent).toBeDefined(); + + api.captureEvent({ + action: 'navigate', + subject: '/', + context: { ...context, extensionId: '', extension: '' }, + }); + + expect(fnEvent).toHaveBeenCalledWith('page_view', { + action: 'page_view', + label: '/', + category: 'app', + }); + }); + it('prioritize new context extension id over old extension property', () => { const api = GoogleAnalytics4.fromConfig(basicValidConfig); @@ -520,13 +538,13 @@ describe('GoogleAnalytics4', () => { api.captureEvent({ action: 'navigate', subject: '/', - context: { ...context, extensionId: 'App', extension: '' }, + context: { ...context, extensionId: 'app', extension: '' }, }); expect(fnEvent).toHaveBeenCalledWith('page_view', { action: 'page_view', label: '/', - category: 'App', + category: 'app', }); }); }); diff --git a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts index 3d2e633962..1f4cbfeee5 100644 --- a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts +++ b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts @@ -171,9 +171,10 @@ export class GoogleAnalytics4 implements AnalyticsApi, NewAnalyticsApi { } const extensionId = context.extensionId || context.extension; - const category = extensionId ? String(extensionId) : 'App'; + const category = extensionId ? String(extensionId) : 'app'; - if (action === 'navigate' && category === 'App') { + // The legacy default extension was 'App' and the new one is 'app' + if (action === 'navigate' && category.toLocaleLowerCase() === 'app') { this.capture.event( { category, diff --git a/yarn.lock b/yarn.lock index 5f26cb9a82..efac8bb495 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3896,6 +3896,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/core-app-api": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" "@backstage/version-bridge": "workspace:^" From 405702ba8b04d6065b609897a5dbbe2480ebe739 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 26 Jan 2024 09:22:21 +0100 Subject: [PATCH 163/468] feat: add api backward compatibility for more analytics modules Signed-off-by: Camila Belo --- plugins/analytics-module-ga/package.json | 1 + .../AnalyticsApi/GoogleAnalytics.test.ts | 59 +++++++++++++++++++ .../AnalyticsApi/GoogleAnalytics.ts | 19 ++++-- .../package.json | 1 + .../AnalyticsApi/NewRelicBrowser.ts | 15 ++++- yarn.lock | 2 + 6 files changed, 89 insertions(+), 8 deletions(-) diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 3d367a5889..cabff881ad 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -32,6 +32,7 @@ "@backstage/config": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "react-ga": "^3.3.0" }, "peerDependencies": { diff --git a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts index 65e9256aa8..1380f9c715 100644 --- a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts +++ b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts @@ -508,4 +508,63 @@ describe('GoogleAnalytics', () => { expect(lastData.queueTime).toBeUndefined(); }); }); + + describe('api backward compatibility', () => { + it('continue working with legacy App category', () => { + const api = GoogleAnalytics.fromConfig(basicValidConfig); + + expect(api.captureEvent).toBeDefined(); + + api.captureEvent({ + action: 'navigate', + subject: '/', + context, + }); + + const [command, data] = ReactGA.testModeAPI.calls[1]; + expect(command).toBe('send'); + expect(data).toMatchObject({ + hitType: 'pageview', + page: '/', + }); + }); + + it('use lowercase app as the new default category', () => { + const api = GoogleAnalytics.fromConfig(basicValidConfig); + + expect(api.captureEvent).toBeDefined(); + + api.captureEvent({ + action: 'navigate', + subject: '/', + context: { ...context, extensionId: '', extension: '' }, + }); + + const [command, data] = ReactGA.testModeAPI.calls[1]; + expect(command).toBe('send'); + expect(data).toMatchObject({ + hitType: 'pageview', + page: '/', + }); + }); + + it('prioritize new context extension id over old extension property', () => { + const api = GoogleAnalytics.fromConfig(basicValidConfig); + + expect(api.captureEvent).toBeDefined(); + + api.captureEvent({ + action: 'navigate', + subject: '/', + context: { ...context, extensionId: 'app', extension: '' }, + }); + + const [command, data] = ReactGA.testModeAPI.calls[1]; + expect(command).toBe('send'); + expect(data).toMatchObject({ + hitType: 'pageview', + page: '/', + }); + }); + }); }); diff --git a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts index 17c5b26be1..045bc94f3a 100644 --- a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts +++ b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts @@ -22,6 +22,11 @@ import { AnalyticsEventAttributes, IdentityApi, } from '@backstage/core-plugin-api'; +import { + AnalyticsApi as NewAnalyticsApi, + AnalyticsEvent as NewAnalyticsEvent, + AnalyticsContextValue as NewAnalyticsContextValue, +} from '@backstage/frontend-plugin-api'; import { Config } from '@backstage/config'; import { DeferredCapture } from '../../../util'; import { @@ -40,7 +45,7 @@ type CustomDimensionOrMetricConfig = { * Google Analytics API provider for the Backstage Analytics API. * @public */ -export class GoogleAnalytics implements AnalyticsApi { +export class GoogleAnalytics implements AnalyticsApi, NewAnalyticsApi { private readonly cdmConfig: CustomDimensionOrMetricConfig[]; private customUserIdTransform?: (userEntityRef: string) => Promise; private readonly capture: DeferredCapture; @@ -157,11 +162,15 @@ export class GoogleAnalytics implements AnalyticsApi { * pageview and the rest as custom events. All custom dimensions/metrics are * applied as they should be (set on pageview, merged object on events). */ - captureEvent(event: AnalyticsEvent) { + captureEvent(event: AnalyticsEvent | NewAnalyticsEvent) { const { context, action, subject, value, attributes } = event; const customMetadata = this.getCustomDimensionMetrics(context, attributes); - if (action === 'navigate' && context.extension === 'App') { + const extensionId = context.extensionId || context.extension; + const category = extensionId ? String(extensionId) : 'app'; + + // The legacy default extension was 'App' and the new one is 'app' + if (action === 'navigate' && category.toLocaleLowerCase() === 'app') { this.capture.pageview(subject, customMetadata); return; } @@ -184,7 +193,7 @@ export class GoogleAnalytics implements AnalyticsApi { } this.capture.event({ - category: context.extension || 'App', + category, action, label: subject, value, @@ -197,7 +206,7 @@ export class GoogleAnalytics implements AnalyticsApi { * Event Attributes, e.g. { dimension1: "some value", metric8: 42 } */ private getCustomDimensionMetrics( - context: AnalyticsContextValue, + context: AnalyticsContextValue | NewAnalyticsContextValue, attributes: AnalyticsEventAttributes = {}, ) { const customDimensionsMetrics: { [x: string]: string | number | boolean } = diff --git a/plugins/analytics-module-newrelic-browser/package.json b/plugins/analytics-module-newrelic-browser/package.json index 0815b62b6c..44610dd026 100644 --- a/plugins/analytics-module-newrelic-browser/package.json +++ b/plugins/analytics-module-newrelic-browser/package.json @@ -26,6 +26,7 @@ "@backstage/config": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@newrelic/browser-agent": "^1.236.0" }, "peerDependencies": { diff --git a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts index f2f35b5765..ba04aea322 100644 --- a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts +++ b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts @@ -19,6 +19,10 @@ import { IdentityApi, AnalyticsEvent, } from '@backstage/core-plugin-api'; +import { + AnalyticsApi as NewAnalyicsApi, + AnalyticsEvent as NewAnalyticsEvent, +} from '@backstage/frontend-plugin-api'; import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent'; import type { setAPI } from '@newrelic/browser-agent/loaders/api/api'; @@ -37,7 +41,7 @@ type NewRelicBrowserOptions = { * New Relic Browser API provider for the Backstage Analytics API. * @public */ -export class NewRelicBrowser implements AnalyticsApi { +export class NewRelicBrowser implements AnalyticsApi, NewAnalyicsApi { private readonly agent: NewRelicAPI; private constructor( @@ -121,9 +125,14 @@ export class NewRelicBrowser implements AnalyticsApi { ); } - captureEvent(event: AnalyticsEvent) { + captureEvent(event: AnalyticsEvent | NewAnalyticsEvent) { const { context, action, subject, value, attributes } = event; - if (action === 'navigate' && context.extension === 'App') { + + const extensionId = context.extensionId || context.extension; + const category = extensionId ? String(extensionId) : 'app'; + + // The legacy default extension was 'App' and the new one is 'app' + if (action === 'navigate' && category.toLocaleLowerCase() === 'app') { const interaction = this.agent.interaction(); interaction.setName(subject); if (value) { diff --git a/yarn.lock b/yarn.lock index efac8bb495..41ceb79882 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4336,6 +4336,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -4357,6 +4358,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@newrelic/browser-agent": ^1.236.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 From e586f797026c86e44a16a7585cc7e71a4332c521 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 26 Jan 2024 09:39:32 +0100 Subject: [PATCH 164/468] docs: add changeset files Signed-off-by: Camila Belo --- .changeset/blue-keys-do.md | 5 +++++ .changeset/gorgeous-pumas-draw.md | 7 +++++++ .changeset/old-papayas-shave.md | 5 +++++ .changeset/shaggy-trainers-rule.md | 5 +++++ 4 files changed, 22 insertions(+) create mode 100644 .changeset/blue-keys-do.md create mode 100644 .changeset/gorgeous-pumas-draw.md create mode 100644 .changeset/old-papayas-shave.md create mode 100644 .changeset/shaggy-trainers-rule.md diff --git a/.changeset/blue-keys-do.md b/.changeset/blue-keys-do.md new file mode 100644 index 0000000000..c933bac0f2 --- /dev/null +++ b/.changeset/blue-keys-do.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +**BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root`. diff --git a/.changeset/gorgeous-pumas-draw.md b/.changeset/gorgeous-pumas-draw.md new file mode 100644 index 0000000000..d2ae0370e5 --- /dev/null +++ b/.changeset/gorgeous-pumas-draw.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-analytics-module-newrelic-browser': minor +'@backstage/plugin-analytics-module-ga4': minor +'@backstage/plugin-analytics-module-ga': minor +--- + +**BREAKING**: Add support to the new analytics api and use `app` as default extension and plugin ids. It is breaking only because you will start seeing app as the new default id for extensions and plugins events. diff --git a/.changeset/old-papayas-shave.md b/.changeset/old-papayas-shave.md new file mode 100644 index 0000000000..1372b9ce85 --- /dev/null +++ b/.changeset/old-papayas-shave.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Wrap the root element with the analytics context to ensure it always exists for all extensions. diff --git a/.changeset/shaggy-trainers-rule.md b/.changeset/shaggy-trainers-rule.md new file mode 100644 index 0000000000..4140a400e1 --- /dev/null +++ b/.changeset/shaggy-trainers-rule.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': patch +--- + +Throw a more specific exception `NotImplementedError` when an API implementation cannot be found. From e92f21f27a964b4677d53a66b10ddacd4f8cbebc Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 26 Jan 2024 10:12:04 +0100 Subject: [PATCH 165/468] docs: update api reports Signed-off-by: Camila Belo --- plugins/analytics-module-ga/api-report.md | 6 ++++-- plugins/analytics-module-ga4/api-report.md | 6 ++++-- plugins/analytics-module-newrelic-browser/api-report.md | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/analytics-module-ga/api-report.md b/plugins/analytics-module-ga/api-report.md index 1846211e72..5404248938 100644 --- a/plugins/analytics-module-ga/api-report.md +++ b/plugins/analytics-module-ga/api-report.md @@ -4,7 +4,9 @@ ```ts import { AnalyticsApi } from '@backstage/core-plugin-api'; +import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/frontend-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/frontend-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Config } from '@backstage/config'; import { IdentityApi } from '@backstage/core-plugin-api'; @@ -13,8 +15,8 @@ import { IdentityApi } from '@backstage/core-plugin-api'; export const analyticsModuleGA: BackstagePlugin<{}, {}>; // @public -export class GoogleAnalytics implements AnalyticsApi { - captureEvent(event: AnalyticsEvent): void; +export class GoogleAnalytics implements AnalyticsApi, AnalyticsApi_2 { + captureEvent(event: AnalyticsEvent | AnalyticsEvent_2): void; static fromConfig( config: Config, options?: { diff --git a/plugins/analytics-module-ga4/api-report.md b/plugins/analytics-module-ga4/api-report.md index 77f3094210..b8282e834a 100644 --- a/plugins/analytics-module-ga4/api-report.md +++ b/plugins/analytics-module-ga4/api-report.md @@ -4,13 +4,15 @@ ```ts import { AnalyticsApi } from '@backstage/core-plugin-api'; +import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/frontend-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/frontend-plugin-api'; import { Config } from '@backstage/config'; import { IdentityApi } from '@backstage/core-plugin-api'; // @public -export class GoogleAnalytics4 implements AnalyticsApi { - captureEvent(event: AnalyticsEvent): void; +export class GoogleAnalytics4 implements AnalyticsApi, AnalyticsApi_2 { + captureEvent(event: AnalyticsEvent | AnalyticsEvent_2): void; static fromConfig( config: Config, options?: { diff --git a/plugins/analytics-module-newrelic-browser/api-report.md b/plugins/analytics-module-newrelic-browser/api-report.md index 7556bae317..09fbf8f0f3 100644 --- a/plugins/analytics-module-newrelic-browser/api-report.md +++ b/plugins/analytics-module-newrelic-browser/api-report.md @@ -4,14 +4,16 @@ ```ts import { AnalyticsApi } from '@backstage/core-plugin-api'; +import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/frontend-plugin-api'; import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/frontend-plugin-api'; import { Config } from '@backstage/config'; import { IdentityApi } from '@backstage/core-plugin-api'; // @public -export class NewRelicBrowser implements AnalyticsApi { +export class NewRelicBrowser implements AnalyticsApi, AnalyticsApi_2 { // (undocumented) - captureEvent(event: AnalyticsEvent): void; + captureEvent(event: AnalyticsEvent | AnalyticsEvent_2): void; // (undocumented) static fromConfig( config: Config, From 915773bf0a28f4bc9889e4ba95f39a39755666b6 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 26 Jan 2024 13:37:35 +0100 Subject: [PATCH 166/468] test(analytics-module-ga): apply review suggestion Signed-off-by: Camila Belo --- .../AnalyticsApi/GoogleAnalytics.test.ts | 80 +++++++++++++++++-- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts index 1380f9c715..056efdb3e6 100644 --- a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts +++ b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts @@ -521,12 +521,30 @@ describe('GoogleAnalytics', () => { context, }); - const [command, data] = ReactGA.testModeAPI.calls[1]; + let [command, data] = ReactGA.testModeAPI.calls[1]; expect(command).toBe('send'); expect(data).toMatchObject({ hitType: 'pageview', page: '/', }); + + api.captureEvent({ + action: 'click', + subject: 'on something', + value: 42, + context, + }); + + [command, data] = ReactGA.testModeAPI.calls[2]; + expect(command).toBe('send'); + expect(data).toMatchObject({ + hitType: 'event', + // expect to use the legacy default category + eventCategory: 'App', + eventAction: 'click', + eventLabel: 'on something', + eventValue: 42, + }); }); it('use lowercase app as the new default category', () => { @@ -537,15 +555,41 @@ describe('GoogleAnalytics', () => { api.captureEvent({ action: 'navigate', subject: '/', - context: { ...context, extensionId: '', extension: '' }, + context: { + ...context, + extensionId: '', + extension: '', + }, }); - const [command, data] = ReactGA.testModeAPI.calls[1]; + let [command, data] = ReactGA.testModeAPI.calls[1]; expect(command).toBe('send'); expect(data).toMatchObject({ hitType: 'pageview', page: '/', }); + + api.captureEvent({ + action: 'click', + subject: 'on something', + value: 42, + context: { + ...context, + extensionId: '', + extension: '', + }, + }); + + [command, data] = ReactGA.testModeAPI.calls[2]; + expect(command).toBe('send'); + expect(data).toMatchObject({ + hitType: 'event', + // expect to use the new default category + eventCategory: 'app', + eventAction: 'click', + eventLabel: 'on something', + eventValue: 42, + }); }); it('prioritize new context extension id over old extension property', () => { @@ -556,15 +600,41 @@ describe('GoogleAnalytics', () => { api.captureEvent({ action: 'navigate', subject: '/', - context: { ...context, extensionId: 'app', extension: '' }, + context: { + ...context, + extensionId: 'app', + extension: '', + }, }); - const [command, data] = ReactGA.testModeAPI.calls[1]; + let [command, data] = ReactGA.testModeAPI.calls[1]; expect(command).toBe('send'); expect(data).toMatchObject({ hitType: 'pageview', page: '/', }); + + api.captureEvent({ + action: 'click', + subject: 'on something', + value: 42, + context: { + ...context, + extensionId: 'page:index', + extension: '', + }, + }); + + [command, data] = ReactGA.testModeAPI.calls[2]; + expect(command).toBe('send'); + expect(data).toMatchObject({ + hitType: 'event', + // expect use the new context extension id + eventCategory: 'page:index', + eventAction: 'click', + eventLabel: 'on something', + eventValue: 42, + }); }); }); }); From 8aaf88d8047d45ee15de9583d44d566ea7da2670 Mon Sep 17 00:00:00 2001 From: tduniec Date: Tue, 30 Jan 2024 12:56:02 +0100 Subject: [PATCH 167/468] registering backstage time saver plugin Signed-off-by: tduniec --- microsite/data/plugins/time-saver.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 microsite/data/plugins/time-saver.yaml diff --git a/microsite/data/plugins/time-saver.yaml b/microsite/data/plugins/time-saver.yaml new file mode 100644 index 0000000000..e9e6d0da57 --- /dev/null +++ b/microsite/data/plugins/time-saver.yaml @@ -0,0 +1,10 @@ +--- +title: TimeSaver +author: tduniec +authorUrl: https://github.com/tduniec +category: Discovery, Templates +description: Visualise your time saved using Backstage Scaffolder templates +documentation: https://github.com/tduniec/backstage-timesaver-plugin/blob/main/README.md +iconUrl: img/tsLogo.png +npmPackageName: 'backstage-plugin-time-saver' +addedDate: 2024-01-30 From b5b9b8d98ae74e5db0867f8dfe3902e74d675591 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 30 Jan 2024 11:30:33 +0100 Subject: [PATCH 168/468] fix: analytics catagory locale Signed-off-by: Camila Belo Co-authored-by: Patrik Oldsberg --- .../implementations/AnalyticsApi/GoogleAnalytics.test.ts | 2 +- .../apis/implementations/AnalyticsApi/GoogleAnalytics.ts | 7 +++++-- .../implementations/AnalyticsApi/GoogleAnalytics4.test.ts | 2 +- .../apis/implementations/AnalyticsApi/GoogleAnalytics4.ts | 7 +++++-- .../apis/implementations/AnalyticsApi/NewRelicBrowser.ts | 7 +++++-- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts index 056efdb3e6..48e43c2a45 100644 --- a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts +++ b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.test.ts @@ -585,7 +585,7 @@ describe('GoogleAnalytics', () => { expect(data).toMatchObject({ hitType: 'event', // expect to use the new default category - eventCategory: 'app', + eventCategory: 'App', eventAction: 'click', eventLabel: 'on something', eventValue: 42, diff --git a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts index 045bc94f3a..d241cd9860 100644 --- a/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts +++ b/plugins/analytics-module-ga/src/apis/implementations/AnalyticsApi/GoogleAnalytics.ts @@ -167,10 +167,13 @@ export class GoogleAnalytics implements AnalyticsApi, NewAnalyticsApi { const customMetadata = this.getCustomDimensionMetrics(context, attributes); const extensionId = context.extensionId || context.extension; - const category = extensionId ? String(extensionId) : 'app'; + const category = extensionId ? String(extensionId) : 'App'; // The legacy default extension was 'App' and the new one is 'app' - if (action === 'navigate' && category.toLocaleLowerCase() === 'app') { + if ( + action === 'navigate' && + category.toLocaleLowerCase('en-US').startsWith('app') + ) { this.capture.pageview(subject, customMetadata); return; } diff --git a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts index 93a6e6fe6a..025462903b 100644 --- a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts +++ b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.test.ts @@ -526,7 +526,7 @@ describe('GoogleAnalytics4', () => { expect(fnEvent).toHaveBeenCalledWith('page_view', { action: 'page_view', label: '/', - category: 'app', + category: 'App', }); }); diff --git a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts index 1f4cbfeee5..43ff47b84e 100644 --- a/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts +++ b/plugins/analytics-module-ga4/src/apis/implementations/AnalyticsApi/GoogleAnalytics4.ts @@ -171,10 +171,13 @@ export class GoogleAnalytics4 implements AnalyticsApi, NewAnalyticsApi { } const extensionId = context.extensionId || context.extension; - const category = extensionId ? String(extensionId) : 'app'; + const category = extensionId ? String(extensionId) : 'App'; // The legacy default extension was 'App' and the new one is 'app' - if (action === 'navigate' && category.toLocaleLowerCase() === 'app') { + if ( + action === 'navigate' && + category.toLocaleLowerCase('en-US').startsWith('app') + ) { this.capture.event( { category, diff --git a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts index ba04aea322..d65d02db64 100644 --- a/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts +++ b/plugins/analytics-module-newrelic-browser/src/apis/implementations/AnalyticsApi/NewRelicBrowser.ts @@ -129,10 +129,13 @@ export class NewRelicBrowser implements AnalyticsApi, NewAnalyicsApi { const { context, action, subject, value, attributes } = event; const extensionId = context.extensionId || context.extension; - const category = extensionId ? String(extensionId) : 'app'; + const category = extensionId ? String(extensionId) : 'App'; // The legacy default extension was 'App' and the new one is 'app' - if (action === 'navigate' && category.toLocaleLowerCase() === 'app') { + if ( + action === 'navigate' && + category.toLocaleLowerCase('en-US').startsWith('app') + ) { const interaction = this.agent.interaction(); interaction.setName(subject); if (value) { From 311dda9e18df096cbffdcecaad08f628d3d154cf Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 30 Jan 2024 11:31:46 +0100 Subject: [PATCH 169/468] feat: add backward compatibility for core anlytics api implementations Signed-off-by: Camila Belo --- .changeset/blue-keys-do.md | 2 +- .changeset/gorgeous-pumas-draw.md | 2 +- packages/frontend-plugin-api/api-report.md | 16 ++++ .../AnalyticsApi/MultipleAnalyticsApi.test.ts | 64 ++++++++++++++++ .../AnalyticsApi/MultipleAnalyticsApi.ts | 76 +++++++++++++++++++ .../AnalyticsApi/NoOpAnalyticsApi.ts | 30 ++++++++ .../implementations/AnalyticsApi/index.ts | 18 +++++ .../src/apis/implementations/index.ts | 20 +++++ .../frontend-plugin-api/src/apis/index.ts | 1 + 9 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts create mode 100644 packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts create mode 100644 packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts create mode 100644 packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/index.ts create mode 100644 packages/frontend-plugin-api/src/apis/implementations/index.ts diff --git a/.changeset/blue-keys-do.md b/.changeset/blue-keys-do.md index c933bac0f2..81e79a7975 100644 --- a/.changeset/blue-keys-do.md +++ b/.changeset/blue-keys-do.md @@ -2,4 +2,4 @@ '@backstage/frontend-plugin-api': minor --- -**BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root`. +**BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root` and update the `MultipleAnalyticsApi` to support new Analytics API context type . diff --git a/.changeset/gorgeous-pumas-draw.md b/.changeset/gorgeous-pumas-draw.md index d2ae0370e5..c5b2214829 100644 --- a/.changeset/gorgeous-pumas-draw.md +++ b/.changeset/gorgeous-pumas-draw.md @@ -4,4 +4,4 @@ '@backstage/plugin-analytics-module-ga': minor --- -**BREAKING**: Add support to the new analytics api and use `app` as default extension and plugin ids. It is breaking only because you will start seeing app as the new default id for extensions and plugins events. +Add support to the new analytics api. diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 4116469c2a..21d329fae8 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -8,6 +8,8 @@ import { AlertApi } from '@backstage/core-plugin-api'; import { alertApiRef } from '@backstage/core-plugin-api'; import { AlertMessage } from '@backstage/core-plugin-api'; +import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/core-plugin-api'; +import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/core-plugin-api'; import { AnyApiFactory } from '@backstage/core-plugin-api'; import { AnyApiRef } from '@backstage/core-plugin-api'; import { ApiFactory } from '@backstage/core-plugin-api'; @@ -993,6 +995,20 @@ export { identityApiRef }; export { microsoftAuthApiRef }; +// @public +export class MultipleAnalyticsApi implements AnalyticsApi_2, AnalyticsApi { + captureEvent(event: AnalyticsEvent_2 | AnalyticsEvent): void; + static fromApis( + actualApis: (AnalyticsApi_2 | AnalyticsApi)[], + ): MultipleAnalyticsApi; +} + +// @public +export class NoOpAnalyticsApi implements AnalyticsApi_2, AnalyticsApi { + // (undocumented) + captureEvent(_event: AnalyticsEvent_2 | AnalyticsEvent): void; +} + export { OAuthApi }; export { OAuthRequestApi }; diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts new file mode 100644 index 0000000000..8095ee0020 --- /dev/null +++ b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts @@ -0,0 +1,64 @@ +/* + * Copyright 2024 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 { MultipleAnalyticsApi } from './MultipleAnalyticsApi'; + +describe('MultipleAnalyticsApi', () => { + const analyticsApiOne = { captureEvent: jest.fn() }; + const analyticsApiTwo = { captureEvent: jest.fn() }; + const multipleApis = MultipleAnalyticsApi.fromApis([ + analyticsApiOne, + analyticsApiTwo, + ]); + + const event = { + action: 'navivate', + subject: '/path', + context: { + extension: 'App', + pluginId: 'plugin', + routeRef: 'unknown', + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('forwards events to all apis', () => { + // When an event is captured + multipleApis.captureEvent(event); + + // Then both underlying APIs should have received the event + expect(analyticsApiOne.captureEvent).toHaveBeenCalledTimes(1); + expect(analyticsApiOne.captureEvent).toHaveBeenCalledWith(event); + expect(analyticsApiTwo.captureEvent).toHaveBeenCalledTimes(1); + expect(analyticsApiTwo.captureEvent).toHaveBeenCalledWith(event); + }); + + it('forwards events to all apis even if one throws an error', () => { + // Given one underlying API that throws on capture + analyticsApiOne.captureEvent.mockImplementation(() => { + throw new Error('!!!'); + }); + + // When an event is captured + multipleApis.captureEvent(event); + + // Then the other underlying API should have still received the event + expect(analyticsApiTwo.captureEvent).toHaveBeenCalledTimes(1); + expect(analyticsApiTwo.captureEvent).toHaveBeenCalledWith(event); + }); +}); diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts new file mode 100644 index 0000000000..b99607a5fb --- /dev/null +++ b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2024 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 { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api'; +import { + AnalyticsApi as NewAnalyicsApi, + AnalyticsEvent as NewAnalyicsEvent, +} from '../../definitions'; + +/** + * An implementation of the AnalyticsApi that can be used to forward analytics + * events to multiple concrete implementations. + * + * @public + * + * @example + * + * ```jsx + * createApiFactory({ + * api: analyticsApiRef, + * deps: { configApi: configApiRef, identityApi: identityApiRef, storageApi: storageApiRef }, + * factory: ({ configApi, identityApi, storageApi }) => + * MultipleAnalyticsApi.fromApis([ + * VendorAnalyticsApi.fromConfig(configApi, { identityApi }), + * CustomAnalyticsApi.fromConfig(configApi, { identityApi, storageApi }), + * ]), + * }); + * ``` + */ +export class MultipleAnalyticsApi implements AnalyticsApi, NewAnalyicsApi { + private constructor( + private readonly actualApis: (AnalyticsApi | NewAnalyicsApi)[], + ) {} + + /** + * Create an AnalyticsApi implementation from an array of concrete + * implementations. + * + * @example + * + * ```jsx + * MultipleAnalyticsApi.fromApis([ + * SomeAnalyticsApi.fromConfig(configApi), + * new CustomAnalyticsApi(), + * ]); + * ``` + */ + static fromApis(actualApis: (AnalyticsApi | NewAnalyicsApi)[]) { + return new MultipleAnalyticsApi(actualApis); + } + + /** + * Forward the event to all configured analytics API implementations. + */ + captureEvent(event: AnalyticsEvent | NewAnalyicsEvent): void { + this.actualApis.forEach(analyticsApi => { + try { + analyticsApi.captureEvent(event as AnalyticsEvent & NewAnalyicsEvent); + } catch { + /* ignored */ + } + }); + } +} diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts new file mode 100644 index 0000000000..b64c31afaf --- /dev/null +++ b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2024 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 { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api'; +import { + AnalyticsApi as NewAnalyicsApi, + AnalyticsEvent as NewAnalyicsEvent, +} from '../../definitions'; + +/** + * Base implementation for the AnalyticsApi that does nothing. + * + * @public + */ +export class NoOpAnalyticsApi implements AnalyticsApi, NewAnalyicsApi { + captureEvent(_event: AnalyticsEvent | NewAnalyicsEvent): void {} +} diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/index.ts b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/index.ts new file mode 100644 index 0000000000..c9e2008516 --- /dev/null +++ b/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 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 { MultipleAnalyticsApi } from './MultipleAnalyticsApi'; +export { NoOpAnalyticsApi } from './NoOpAnalyticsApi'; diff --git a/packages/frontend-plugin-api/src/apis/implementations/index.ts b/packages/frontend-plugin-api/src/apis/implementations/index.ts new file mode 100644 index 0000000000..b3ec1c8513 --- /dev/null +++ b/packages/frontend-plugin-api/src/apis/implementations/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This folder contains implementations for all core APIs. +// Plugins should rely on these APIs for functionality as much as possible. + +export * from './AnalyticsApi'; diff --git a/packages/frontend-plugin-api/src/apis/index.ts b/packages/frontend-plugin-api/src/apis/index.ts index 5def6cb0f9..983497271f 100644 --- a/packages/frontend-plugin-api/src/apis/index.ts +++ b/packages/frontend-plugin-api/src/apis/index.ts @@ -15,4 +15,5 @@ */ export * from './definitions'; +export * from './implementations'; export * from './system'; From 4bc1ca5522726264a238d1fbd8fd7d5098b4251c Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 30 Jan 2024 13:11:39 +0100 Subject: [PATCH 170/468] refactor: move core analytics apis to the compat package Signed-off-by: Camila Belo --- .changeset/blue-keys-do.md | 2 +- .changeset/gorgeous-pumas-draw.md | 1 + packages/core-compat-api/api-report.md | 18 ++++++++++++++++++ .../AnalyticsApi/MultipleAnalyticsApi.test.ts | 0 .../AnalyticsApi/MultipleAnalyticsApi.ts | 2 +- .../AnalyticsApi/NoOpAnalyticsApi.ts | 2 +- .../apis/implementations/AnalyticsApi/index.ts | 0 .../src/apis/implementations/index.ts | 5 +---- packages/core-compat-api/src/apis/index.ts | 17 +++++++++++++++++ packages/core-compat-api/src/index.ts | 2 ++ packages/frontend-plugin-api/api-report.md | 16 ---------------- packages/frontend-plugin-api/src/apis/index.ts | 1 - 12 files changed, 42 insertions(+), 24 deletions(-) rename packages/{frontend-plugin-api => core-compat-api}/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts (100%) rename packages/{frontend-plugin-api => core-compat-api}/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts (98%) rename packages/{frontend-plugin-api => core-compat-api}/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts (96%) rename packages/{frontend-plugin-api => core-compat-api}/src/apis/implementations/AnalyticsApi/index.ts (100%) rename packages/{frontend-plugin-api => core-compat-api}/src/apis/implementations/index.ts (77%) create mode 100644 packages/core-compat-api/src/apis/index.ts diff --git a/.changeset/blue-keys-do.md b/.changeset/blue-keys-do.md index 81e79a7975..c933bac0f2 100644 --- a/.changeset/blue-keys-do.md +++ b/.changeset/blue-keys-do.md @@ -2,4 +2,4 @@ '@backstage/frontend-plugin-api': minor --- -**BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root` and update the `MultipleAnalyticsApi` to support new Analytics API context type . +**BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root`. diff --git a/.changeset/gorgeous-pumas-draw.md b/.changeset/gorgeous-pumas-draw.md index c5b2214829..2e94c61c63 100644 --- a/.changeset/gorgeous-pumas-draw.md +++ b/.changeset/gorgeous-pumas-draw.md @@ -2,6 +2,7 @@ '@backstage/plugin-analytics-module-newrelic-browser': minor '@backstage/plugin-analytics-module-ga4': minor '@backstage/plugin-analytics-module-ga': minor +'@backstage/core-compat-api': minor --- Add support to the new analytics api. diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index a7aae2b5b9..be80ca50e9 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -3,6 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { AnalyticsApi } from '@backstage/core-plugin-api'; +import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/frontend-plugin-api'; +import { AnalyticsEvent } from '@backstage/core-plugin-api'; +import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/core-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; @@ -51,6 +55,20 @@ export function convertLegacyRouteRefs< [KName in keyof TRefs]: ToNewRouteRef; }; +// @public +export class MultipleAnalyticsApi implements AnalyticsApi, AnalyticsApi_2 { + captureEvent(event: AnalyticsEvent | AnalyticsEvent_2): void; + static fromApis( + actualApis: (AnalyticsApi | AnalyticsApi_2)[], + ): MultipleAnalyticsApi; +} + +// @public +export class NoOpAnalyticsApi implements AnalyticsApi, AnalyticsApi_2 { + // (undocumented) + captureEvent(_event: AnalyticsEvent | AnalyticsEvent_2): void; +} + // @public export type ToNewRouteRef = T extends RouteRef diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts b/packages/core-compat-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts similarity index 100% rename from packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts rename to packages/core-compat-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.test.ts diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts b/packages/core-compat-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts similarity index 98% rename from packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts rename to packages/core-compat-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts index b99607a5fb..888474877d 100644 --- a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts +++ b/packages/core-compat-api/src/apis/implementations/AnalyticsApi/MultipleAnalyticsApi.ts @@ -17,7 +17,7 @@ import { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api'; import { AnalyticsApi as NewAnalyicsApi, AnalyticsEvent as NewAnalyicsEvent, -} from '../../definitions'; +} from '@backstage/frontend-plugin-api'; /** * An implementation of the AnalyticsApi that can be used to forward analytics diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts b/packages/core-compat-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts similarity index 96% rename from packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts rename to packages/core-compat-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts index b64c31afaf..ad57258fdd 100644 --- a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts +++ b/packages/core-compat-api/src/apis/implementations/AnalyticsApi/NoOpAnalyticsApi.ts @@ -18,7 +18,7 @@ import { AnalyticsApi, AnalyticsEvent } from '@backstage/core-plugin-api'; import { AnalyticsApi as NewAnalyicsApi, AnalyticsEvent as NewAnalyicsEvent, -} from '../../definitions'; +} from '@backstage/frontend-plugin-api'; /** * Base implementation for the AnalyticsApi that does nothing. diff --git a/packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/index.ts b/packages/core-compat-api/src/apis/implementations/AnalyticsApi/index.ts similarity index 100% rename from packages/frontend-plugin-api/src/apis/implementations/AnalyticsApi/index.ts rename to packages/core-compat-api/src/apis/implementations/AnalyticsApi/index.ts diff --git a/packages/frontend-plugin-api/src/apis/implementations/index.ts b/packages/core-compat-api/src/apis/implementations/index.ts similarity index 77% rename from packages/frontend-plugin-api/src/apis/implementations/index.ts rename to packages/core-compat-api/src/apis/implementations/index.ts index b3ec1c8513..82fc567355 100644 --- a/packages/frontend-plugin-api/src/apis/implementations/index.ts +++ b/packages/core-compat-api/src/apis/implementations/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2024 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. @@ -14,7 +14,4 @@ * limitations under the License. */ -// This folder contains implementations for all core APIs. -// Plugins should rely on these APIs for functionality as much as possible. - export * from './AnalyticsApi'; diff --git a/packages/core-compat-api/src/apis/index.ts b/packages/core-compat-api/src/apis/index.ts new file mode 100644 index 0000000000..dd4cc2a930 --- /dev/null +++ b/packages/core-compat-api/src/apis/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2024 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 * from './implementations'; diff --git a/packages/core-compat-api/src/index.ts b/packages/core-compat-api/src/index.ts index b632bf8a57..88e1892eac 100644 --- a/packages/core-compat-api/src/index.ts +++ b/packages/core-compat-api/src/index.ts @@ -15,6 +15,8 @@ */ export * from './compatWrapper'; +export * from './apis'; + export { convertLegacyApp } from './convertLegacyApp'; export { convertLegacyRouteRef, diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 21d329fae8..4116469c2a 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -8,8 +8,6 @@ import { AlertApi } from '@backstage/core-plugin-api'; import { alertApiRef } from '@backstage/core-plugin-api'; import { AlertMessage } from '@backstage/core-plugin-api'; -import { AnalyticsApi as AnalyticsApi_2 } from '@backstage/core-plugin-api'; -import { AnalyticsEvent as AnalyticsEvent_2 } from '@backstage/core-plugin-api'; import { AnyApiFactory } from '@backstage/core-plugin-api'; import { AnyApiRef } from '@backstage/core-plugin-api'; import { ApiFactory } from '@backstage/core-plugin-api'; @@ -995,20 +993,6 @@ export { identityApiRef }; export { microsoftAuthApiRef }; -// @public -export class MultipleAnalyticsApi implements AnalyticsApi_2, AnalyticsApi { - captureEvent(event: AnalyticsEvent_2 | AnalyticsEvent): void; - static fromApis( - actualApis: (AnalyticsApi_2 | AnalyticsApi)[], - ): MultipleAnalyticsApi; -} - -// @public -export class NoOpAnalyticsApi implements AnalyticsApi_2, AnalyticsApi { - // (undocumented) - captureEvent(_event: AnalyticsEvent_2 | AnalyticsEvent): void; -} - export { OAuthApi }; export { OAuthRequestApi }; diff --git a/packages/frontend-plugin-api/src/apis/index.ts b/packages/frontend-plugin-api/src/apis/index.ts index 983497271f..5def6cb0f9 100644 --- a/packages/frontend-plugin-api/src/apis/index.ts +++ b/packages/frontend-plugin-api/src/apis/index.ts @@ -15,5 +15,4 @@ */ export * from './definitions'; -export * from './implementations'; export * from './system'; From a245fad6c75c784ae644d07c59fbc36b685337c6 Mon Sep 17 00:00:00 2001 From: Brian Hudson Date: Tue, 30 Jan 2024 07:34:38 -0500 Subject: [PATCH 171/468] Add util function and related tests Signed-off-by: Brian Hudson --- .../generate-catalog-info.ts | 29 +++------------ .../generate-catalog-info/utils.test.ts | 36 +++++++++++++++++++ .../commands/generate-catalog-info/utils.ts | 17 +++++++++ 3 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 packages/repo-tools/src/commands/generate-catalog-info/utils.test.ts diff --git a/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts index c15f478bd0..1893bd25c8 100644 --- a/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts +++ b/packages/repo-tools/src/commands/generate-catalog-info/generate-catalog-info.ts @@ -33,6 +33,7 @@ import { isFulfilled, readFile, writeFile, + safeEntityName, } from './utils'; import { CodeOwnersEntry } from 'codeowners-utils'; @@ -164,18 +165,7 @@ async function fixCatalogInfoYaml(options: FixOptions) { codeowners, relativePath('.', yamlPath), ); - const safeName = packageJson.name - .replace(/^[^\w\s]|[^a-z0-9]$/g, '') - .replace(/[^A-Za-z0-9_\-.]+/g, '-') - .replace(/([A-Z])/g, (_, letter, index, original) => { - if (index !== 0) { - const previousChar = original[index - 1]; - if (previousChar !== '-') { - return `-${letter.toLowerCase()}`; - } - } - return letter.toLowerCase(); - }); + const safeName = safeEntityName(packageJson.name); let yamlJson: BackstagePackageEntity; try { @@ -249,18 +239,7 @@ function createOrMergeEntity( owner: string, existingEntity: BackstagePackageEntity | Record = {}, ): BackstagePackageEntity { - const safeEntityName = packageJson.name - .replace(/^[^\w\s]|[^a-z0-9]$/g, '') - .replace(/[^A-Za-z0-9_\-.]+/g, '-') - .replace(/([A-Z])/g, (_, letter, index, original) => { - if (index !== 0) { - const previousChar = original[index - 1]; - if (previousChar !== '-') { - return `-${letter.toLowerCase()}`; - } - } - return letter.toLowerCase(); - }); + const entityName = safeEntityName(packageJson.name); return { ...existingEntity, @@ -269,7 +248,7 @@ function createOrMergeEntity( metadata: { ...existingEntity.metadata, // Provide default name/title/description values. - name: safeEntityName, + name: entityName, title: packageJson.name, ...(packageJson.description && !existingEntity.metadata?.description ? { description: packageJson.description } diff --git a/packages/repo-tools/src/commands/generate-catalog-info/utils.test.ts b/packages/repo-tools/src/commands/generate-catalog-info/utils.test.ts new file mode 100644 index 0000000000..40c1bb1e19 --- /dev/null +++ b/packages/repo-tools/src/commands/generate-catalog-info/utils.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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 { safeEntityName } from './utils'; + +describe('utils', () => { + describe('safeEntityName', () => { + it('should remove non-alphanumeric characters at the start and end', () => { + const result = safeEntityName('%entityname$'); + expect(result).toBe('entityname'); + }); + + it('should replace non-alphanumeric characters, except - and _, with -', () => { + const result = safeEntityName('entity@#name$'); + expect(result).toBe('entity-name'); + }); + + it('should replace capital letters with - followed by the same letter in lowercase', () => { + const result = safeEntityName('EntityName'); + expect(result).toBe('entity-name'); + }); + }); +}); diff --git a/packages/repo-tools/src/commands/generate-catalog-info/utils.ts b/packages/repo-tools/src/commands/generate-catalog-info/utils.ts index b00fbd57a8..3f23b35112 100644 --- a/packages/repo-tools/src/commands/generate-catalog-info/utils.ts +++ b/packages/repo-tools/src/commands/generate-catalog-info/utils.ts @@ -48,3 +48,20 @@ export const isRejected = ( export const isFulfilled = ( input: PromiseSettledResult, ): input is PromiseFulfilledResult => input.status === 'fulfilled'; + +/** + * Generates a suitable entity name from a package name by slugifying the given package name. + * + * @param packageName - The package name to generate an entity name from. + * @returns The generated entity name, a slugified version of the package name. + */ +export const safeEntityName = (packageName: string): string => { + return packageName + .replace(/^[^\w\s]|[^a-z0-9]$/g, '') + .replace(/[^A-Za-z0-9_\-.]+/g, '-') + .replace( + /([a-z])([A-Z])/g, + (_, a, b) => `${a}-${b.toLocaleLowerCase('en-US')}`, + ) + .replace(/^(.)/, (_, a) => a.toLocaleLowerCase('en-US')); +}; From 1693ac03b1565257893a5fab93259f053bd658ac Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 30 Jan 2024 12:57:05 +0100 Subject: [PATCH 172/468] chore: fixing tests Signed-off-by: blam --- .../fields/EntityPicker/EntityPicker.test.tsx | 93 +++++++++++-------- .../fields/OwnerPicker/OwnerPicker.test.tsx | 66 +++++++------ 2 files changed, 91 insertions(+), 68 deletions(-) diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx index 4329b2208b..8016a6ae83 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx @@ -89,7 +89,10 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith(undefined); + expect(catalogApi.getEntities).toHaveBeenCalledWith({ + fields: ['metadata.name', 'metadata.namespace', 'kind'], + filter: undefined, + }); }); it('updates even if there is not an exact match', async () => { @@ -130,11 +133,13 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: { - kind: ['User'], - }, - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: { + kind: ['User'], + }, + }), + ); }); }); @@ -173,18 +178,20 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: [ - { - kind: ['Group'], - 'metadata.name': 'test-entity', - }, - { - kind: ['User'], - 'metadata.name': 'test-entity', - }, - ], - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: [ + { + kind: ['Group'], + 'metadata.name': 'test-entity', + }, + { + kind: ['User'], + 'metadata.name': 'test-entity', + }, + ], + }), + ); }); it('allow single top level filter', async () => { uiSchema = { @@ -204,12 +211,14 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: { - kind: ['Group'], - 'metadata.name': 'test-entity', - }, - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: { + kind: ['Group'], + 'metadata.name': 'test-entity', + }, + }), + ); }); it('search for entitities containing an specific key', async () => { @@ -230,14 +239,16 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: [ - { - kind: ['User'], - 'metadata.annotation.some/anotation': CATALOG_FILTER_EXISTS, - }, - ], - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: [ + { + kind: ['User'], + 'metadata.annotation.some/anotation': CATALOG_FILTER_EXISTS, + }, + ], + }), + ); }); }); @@ -273,14 +284,16 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: [ - { - kind: ['Group'], - 'metadata.name': 'test-group', - }, - ], - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: [ + { + kind: ['Group'], + 'metadata.name': 'test-group', + }, + ], + }), + ); }); }); diff --git a/plugins/scaffolder/src/components/fields/OwnerPicker/OwnerPicker.test.tsx b/plugins/scaffolder/src/components/fields/OwnerPicker/OwnerPicker.test.tsx index 3c948b8261..57ca8e53ab 100644 --- a/plugins/scaffolder/src/components/fields/OwnerPicker/OwnerPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/OwnerPicker/OwnerPicker.test.tsx @@ -94,11 +94,14 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: { - kind: ['Group', 'User'], - }, - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: { + kind: ['Group', 'User'], + }, + fields: ['metadata.name', 'metadata.namespace', 'kind'], + }), + ); }); }); @@ -124,11 +127,14 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: { - kind: ['User'], - }, - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: { + kind: ['User'], + }, + fields: ['metadata.name', 'metadata.namespace', 'kind'], + }), + ); }); }); @@ -163,14 +169,16 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: [ - { - kind: ['Group'], - 'spec.type': 'team', - }, - ], - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: [ + { + kind: ['Group'], + 'spec.type': 'team', + }, + ], + }), + ); }); }); @@ -208,16 +216,18 @@ describe('', () => { , ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ - filter: [ - { - kind: ['Group', 'User'], - }, - { - 'spec.type': ['team', 'business-unit'], - }, - ], - }); + expect(catalogApi.getEntities).toHaveBeenCalledWith( + expect.objectContaining({ + filter: [ + { + kind: ['Group', 'User'], + }, + { + 'spec.type': ['team', 'business-unit'], + }, + ], + }), + ); }); }); }); From d8f73229d63533699e88b90d8b0527a2b49fe1bc Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 30 Jan 2024 13:48:44 +0100 Subject: [PATCH 173/468] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Patrik Oldsberg --- beps/0003-auth-architecture-evolution/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/beps/0003-auth-architecture-evolution/README.md b/beps/0003-auth-architecture-evolution/README.md index f70f19e8f9..ba9ffd5ae4 100644 --- a/beps/0003-auth-architecture-evolution/README.md +++ b/beps/0003-auth-architecture-evolution/README.md @@ -30,15 +30,15 @@ creation-date: 2024-01-28 This proposal outlines a new architecture for authenticating users and services in Backstage. It adds built-in access restriction to Backstage instances that protects them from outside access, and enables more granular access control for inter-plugin communication and access from external services. -It proposes a new `AuthService` interface that handles all uses and service authentication and token management available to plugins, as well as a new `HttpAuthService` interface that is a higher level service used to protect endpoints of plugin routers. The `auth-backend` will also be extended to support issuing of user tokens with a reduced scope for cookie-based authentication of requests. +It proposes a new `AuthService` interface that handles all user and service authentication and token management available to plugins, as well as a new `HttpAuthService` interface that is a higher level service used to protect endpoints of plugin routers. The `auth-backend` will also be extended to support issuing of user tokens with a reduced scope for cookie-based authentication of requests. -The changes to the service-to-service auth are aimed to be the minimal needed to get the necessary interfaces in place, and will rely on the existing symmetrical keys for now. +The changes to the service-to-service auth are aimed to be the minimum needed to get the necessary interfaces in place, and will rely on the existing symmetrical keys for now. ## Motivation This proposal aims to address several of the points in the [Auth Meta issue](https://github.com/backstage/backstage/issues/15999), with the overarching goal being to replace the existing [API request authentication](https://github.com/backstage/backstage/blob/master/contrib/docs/tutorials/authenticate-api-requests.md) tutorial in `contrib/` with a more robust and secure built-in solution. The tutorial exists for two purposes: to add authentication of API requests as part of using the permission system in Backstage, and to protect a Backstage instance from external access. It does a fairly good job of the former, although we want to avoid placing user tokens in cookies, but it does a quite poor job of the latter, which we want to fix. -A secondary goal is to do this work before stabilizing the APIs in the new Backend system, as it will have some impact on how plugin backends are built. This will inevitably also lead to the need to improve the way that service-to-service auth it handled in Backstage, although that is not the primary goal of this work. +A secondary goal is to do this work before stabilizing the APIs in the new Backend system, as it will have some impact on how plugin backends are built. This will inevitably also lead to the need to improve the way that service-to-service auth is handled in Backstage, although that is not the primary goal of this work. By providing protection of Backstage instances out of the box we drastically reduce both the barrier of entry as well as security risks for Backstage adopters. It will no longer be a requirement to either set up protection of your Backstage instance or not do so and risk exposing your instance to malicious actors. It also drastically reduces the complexity of enabling the permission system of Backstage, since access restrictions are already built-in. @@ -62,11 +62,11 @@ The following goals are the primary focus of this BEP: Two new backend service interfaces are introduced to support these new features. The new `AuthService` is a low-level service that encapsulates authentication and creation of bearer tokens for all types of Backstage identities, including user, service, and services making requests on behalf of users. The new `HttpAuthService` is a higher level service that lets your create [express middleware](https://expressjs.com/en/guide/using-middleware.html) to protect endpoints, as well as access the credentials and identity of incoming requests. These new services replace the existing `IdentityService` and `TokenManagerService` -The proposed design leaves the decision for how different endpoints are protected to the implementation of the plugin backends themselves. This includes whether particular routes should allow anonymous access, access from users authenticated via a cookie, or perhaps only allow access from other plugin backends and external services. This means that integrators to not need to and do not have the ability to configure access controls of individual endpoints, except for what the permission system already provides, and what is made available through static configuration or extension points. +The proposed design leaves the decision for how different endpoints are protected to the implementation of the plugin backends themselves. This includes whether particular routes should allow anonymous access, access from users authenticated via a cookie, or perhaps only allow access from other plugin backends and external services. This means that integrators do not need to - and do not have the ability to - configure access controls of individual endpoints, except for what the permission system already provides, and what is made available through static configuration or extension points. -In order to allow for cookie-based authentication of incoming users requests, the `auth` plugin backend is extended to be able to issue users tokens with reduced scope, which in turn integrate with the new `AuthService` and `HttpAuthService`. The ability to use cookie auth for request is opt-in per route and is only be permitted for read methods (`GET`, `HEAD`, `OPTIONS`). The actual implementation of cookie-based flows will be up to each plugin, but with significant help from the new auth service interfaces. +In order to allow for cookie-based authentication of incoming user requests, the `auth` plugin backend is extended to be able to issue user tokens with reduced scope, which in turn integrate with the new `AuthService` and `HttpAuthService`. The ability to use cookie auth for requests is an opt-in per route and is only be permitted for read methods (`GET`, `HEAD`, `OPTIONS`). The actual implementation of cookie-based flows will be up to each plugin, but with significant help from the new auth service interfaces. -For service-to-service communication we will move away from reusing user tokens in upstream requests. We will instead implement an "On-Behalf-Of" flow where incoming user tokens are encapsulated in a service credential for the upstream request. In line with this the new auth service interfaces will aim to make it difficult to directly forward credentials from incoming requests, and instead encourage that plugin backends issue new service credentials for upstream requests. +For service-to-service communication we will move away from reusing user tokens in upstream requests. We will instead implement an "On-Behalf-Of" flow where incoming user credentials are encapsulated in a service token for the upstream request. In line with this the new auth service interfaces will aim to make it difficult to directly forward credentials from incoming requests, and instead encourage that plugin backends issue new service credentials for upstream requests. ## Design Details From daa64dcfabcf4f8e2efccf05fcaf6f6ee68601fe Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 30 Jan 2024 13:50:15 +0100 Subject: [PATCH 174/468] beps/0003: add goal to implement some obo flow Signed-off-by: Patrik Oldsberg --- beps/0003-auth-architecture-evolution/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/beps/0003-auth-architecture-evolution/README.md b/beps/0003-auth-architecture-evolution/README.md index ba9ffd5ae4..cc076acae1 100644 --- a/beps/0003-auth-architecture-evolution/README.md +++ b/beps/0003-auth-architecture-evolution/README.md @@ -52,6 +52,7 @@ The following goals are the primary focus of this BEP: - Cookie-based authentication of requests for static assets that protects against CSRF attacks and does not unnecessarily expose user tokens. - Basic improvements to the service-to-service auth service interfaces such that we are confident that we do not need to break them in the near future. - If possible we will keep using the existing symmetrical keys that are used today, but it is likely that we will need to break compatibility of existing tokens. + - Encapsulation of user credentials in upstream service requests, avoiding the pattern where backend plugins re-use the user token directly for their outgoing requests. ### Non-Goals From f86c37cf1fcc9d3a806889d2ac9fc8fb16f598fe Mon Sep 17 00:00:00 2001 From: tduniec Date: Tue, 30 Jan 2024 14:29:46 +0100 Subject: [PATCH 175/468] update package Signed-off-by: tduniec --- microsite/data/plugins/time-saver.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/time-saver.yaml b/microsite/data/plugins/time-saver.yaml index e9e6d0da57..054598c66e 100644 --- a/microsite/data/plugins/time-saver.yaml +++ b/microsite/data/plugins/time-saver.yaml @@ -6,5 +6,5 @@ category: Discovery, Templates description: Visualise your time saved using Backstage Scaffolder templates documentation: https://github.com/tduniec/backstage-timesaver-plugin/blob/main/README.md iconUrl: img/tsLogo.png -npmPackageName: 'backstage-plugin-time-saver' +npmPackageName: '@tduniec/backstage-plugin-time-saver' addedDate: 2024-01-30 From c70c3dc393e6aaf911803288326385f2f2d85046 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 30 Jan 2024 13:38:11 +0000 Subject: [PATCH 176/468] Version Packages (next) --- .changeset/create-app-1706621803.md | 5 + .changeset/pre.json | 54 +- docs/releases/v1.23.0-next.1-changelog.md | 3250 +++++++++++++++++ package.json | 2 +- packages/app-defaults/CHANGELOG.md | 12 + packages/app-defaults/package.json | 2 +- packages/app-next-example-plugin/CHANGELOG.md | 8 + packages/app-next-example-plugin/package.json | 2 +- packages/app-next/CHANGELOG.md | 76 + packages/app-next/package.json | 2 +- packages/app/CHANGELOG.md | 77 + packages/app/package.json | 2 +- packages/backend-app-api/CHANGELOG.md | 17 + packages/backend-app-api/package.json | 2 +- packages/backend-common/CHANGELOG.md | 18 + packages/backend-common/package.json | 2 +- packages/backend-defaults/CHANGELOG.md | 9 + packages/backend-defaults/package.json | 2 +- .../CHANGELOG.md | 24 + .../package.json | 2 +- packages/backend-next/CHANGELOG.md | 41 + packages/backend-next/package.json | 2 +- packages/backend-openapi-utils/CHANGELOG.md | 8 + packages/backend-openapi-utils/package.json | 2 +- packages/backend-plugin-api/CHANGELOG.md | 12 + packages/backend-plugin-api/package.json | 2 +- packages/backend-tasks/CHANGELOG.md | 10 + packages/backend-tasks/package.json | 2 +- packages/backend-test-utils/CHANGELOG.md | 13 + packages/backend-test-utils/package.json | 2 +- packages/backend/CHANGELOG.md | 60 + packages/backend/package.json | 2 +- packages/catalog-client/CHANGELOG.md | 13 + packages/catalog-client/package.json | 2 +- packages/catalog-model/CHANGELOG.md | 9 + packages/catalog-model/package.json | 2 +- packages/cli/CHANGELOG.md | 18 + packages/cli/package.json | 2 +- packages/core-app-api/CHANGELOG.md | 11 + packages/core-app-api/package.json | 2 +- packages/core-compat-api/CHANGELOG.md | 17 + packages/core-compat-api/package.json | 2 +- packages/core-components/CHANGELOG.md | 15 + packages/core-components/package.json | 2 +- packages/core-plugin-api/CHANGELOG.md | 11 + packages/core-plugin-api/package.json | 2 +- packages/create-app/CHANGELOG.md | 8 + packages/create-app/package.json | 2 +- packages/dev-utils/CHANGELOG.md | 14 + packages/dev-utils/package.json | 2 +- packages/e2e-test/CHANGELOG.md | 9 + packages/e2e-test/package.json | 2 +- packages/frontend-app-api/CHANGELOG.md | 23 + packages/frontend-app-api/package.json | 2 +- packages/frontend-plugin-api/CHANGELOG.md | 17 + packages/frontend-plugin-api/package.json | 2 +- packages/frontend-test-utils/CHANGELOG.md | 11 + packages/frontend-test-utils/package.json | 2 +- packages/integration-react/CHANGELOG.md | 9 + packages/integration-react/package.json | 2 +- packages/integration/CHANGELOG.md | 12 + packages/integration/package.json | 2 +- packages/repo-tools/CHANGELOG.md | 12 + packages/repo-tools/package.json | 2 +- .../techdocs-cli-embedded-app/CHANGELOG.md | 19 + .../techdocs-cli-embedded-app/package.json | 2 +- packages/techdocs-cli/CHANGELOG.md | 12 + packages/techdocs-cli/package.json | 2 +- packages/test-utils/CHANGELOG.md | 15 + packages/test-utils/package.json | 2 +- plugins/adr-backend/CHANGELOG.md | 15 + plugins/adr-backend/package.json | 2 +- plugins/adr-common/CHANGELOG.md | 9 + plugins/adr-common/package.json | 2 +- plugins/adr/CHANGELOG.md | 16 + plugins/adr/package.json | 2 +- plugins/airbrake-backend/CHANGELOG.md | 9 + plugins/airbrake-backend/package.json | 2 +- plugins/airbrake/CHANGELOG.md | 12 + plugins/airbrake/package.json | 2 +- plugins/allure/CHANGELOG.md | 10 + plugins/allure/package.json | 2 +- plugins/analytics-module-ga/CHANGELOG.md | 14 + plugins/analytics-module-ga/package.json | 2 +- plugins/analytics-module-ga4/CHANGELOG.md | 14 + plugins/analytics-module-ga4/package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- plugins/apache-airflow/CHANGELOG.md | 8 + plugins/apache-airflow/package.json | 2 +- plugins/api-docs/CHANGELOG.md | 13 + plugins/api-docs/package.json | 2 +- plugins/apollo-explorer/CHANGELOG.md | 8 + plugins/apollo-explorer/package.json | 2 +- plugins/app-backend/CHANGELOG.md | 12 + plugins/app-backend/package.json | 2 +- plugins/app-node/CHANGELOG.md | 7 + plugins/app-node/package.json | 2 +- plugins/app-visualizer/CHANGELOG.md | 9 + plugins/app-visualizer/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- plugins/auth-backend/CHANGELOG.md | 26 + plugins/auth-backend/package.json | 2 +- plugins/auth-node/CHANGELOG.md | 13 + plugins/auth-node/package.json | 2 +- plugins/azure-devops-backend/CHANGELOG.md | 15 + plugins/azure-devops-backend/package.json | 2 +- plugins/azure-devops/CHANGELOG.md | 12 + plugins/azure-devops/package.json | 2 +- plugins/azure-sites-backend/CHANGELOG.md | 72 + plugins/azure-sites-backend/package.json | 2 +- plugins/azure-sites-common/CHANGELOG.md | 36 + plugins/azure-sites-common/package.json | 2 +- plugins/azure-sites/CHANGELOG.md | 41 + plugins/azure-sites/package.json | 2 +- plugins/badges-backend/CHANGELOG.md | 13 + plugins/badges-backend/package.json | 2 +- plugins/badges/CHANGELOG.md | 11 + plugins/badges/package.json | 2 +- plugins/bazaar-backend/CHANGELOG.md | 10 + plugins/bazaar-backend/package.json | 2 +- plugins/bazaar/CHANGELOG.md | 12 + plugins/bazaar/package.json | 2 +- plugins/bitbucket-cloud-common/CHANGELOG.md | 7 + plugins/bitbucket-cloud-common/package.json | 2 +- plugins/bitrise/CHANGELOG.md | 10 + plugins/bitrise/package.json | 2 +- .../catalog-backend-module-aws/CHANGELOG.md | 17 + .../catalog-backend-module-aws/package.json | 2 +- .../catalog-backend-module-azure/CHANGELOG.md | 13 + .../catalog-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 17 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../catalog-backend-module-gcp/CHANGELOG.md | 13 + .../catalog-backend-module-gcp/package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 22 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../catalog-backend-module-ldap/CHANGELOG.md | 13 + .../catalog-backend-module-ldap/package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- plugins/catalog-backend/CHANGELOG.md | 29 + plugins/catalog-backend/package.json | 2 +- plugins/catalog-common/CHANGELOG.md | 9 + plugins/catalog-common/package.json | 2 +- plugins/catalog-graph/CHANGELOG.md | 13 + plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/CHANGELOG.md | 18 + plugins/catalog-import/package.json | 2 +- plugins/catalog-node/CHANGELOG.md | 14 + plugins/catalog-node/package.json | 2 +- plugins/catalog-react/CHANGELOG.md | 18 + plugins/catalog-react/package.json | 2 +- .../catalog-unprocessed-entities/CHANGELOG.md | 10 + .../catalog-unprocessed-entities/package.json | 2 +- plugins/catalog/CHANGELOG.md | 22 + plugins/catalog/package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- plugins/cicd-statistics/CHANGELOG.md | 9 + plugins/cicd-statistics/package.json | 2 +- plugins/circleci/CHANGELOG.md | 10 + plugins/circleci/package.json | 2 +- plugins/cloudbuild/CHANGELOG.md | 10 + plugins/cloudbuild/package.json | 2 +- plugins/code-climate/CHANGELOG.md | 10 + plugins/code-climate/package.json | 2 +- plugins/code-coverage-backend/CHANGELOG.md | 14 + plugins/code-coverage-backend/package.json | 2 +- plugins/code-coverage/CHANGELOG.md | 11 + plugins/code-coverage/package.json | 2 +- plugins/codescene/CHANGELOG.md | 10 + plugins/codescene/package.json | 2 +- plugins/config-schema/CHANGELOG.md | 10 + plugins/config-schema/package.json | 2 +- plugins/cost-insights/CHANGELOG.md | 13 + plugins/cost-insights/package.json | 2 +- plugins/devtools-backend/CHANGELOG.md | 17 + plugins/devtools-backend/package.json | 2 +- plugins/devtools/CHANGELOG.md | 13 + plugins/devtools/package.json | 2 +- plugins/dynatrace/CHANGELOG.md | 10 + plugins/dynatrace/package.json | 2 +- plugins/entity-feedback-backend/CHANGELOG.md | 13 + plugins/entity-feedback-backend/package.json | 2 +- plugins/entity-feedback/CHANGELOG.md | 12 + plugins/entity-feedback/package.json | 2 +- plugins/entity-validation/CHANGELOG.md | 13 + plugins/entity-validation/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../events-backend-module-azure/CHANGELOG.md | 8 + .../events-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../events-backend-module-gerrit/CHANGELOG.md | 8 + .../events-backend-module-gerrit/package.json | 2 +- .../events-backend-module-github/CHANGELOG.md | 9 + .../events-backend-module-github/package.json | 2 +- .../events-backend-module-gitlab/CHANGELOG.md | 9 + .../events-backend-module-gitlab/package.json | 2 +- .../events-backend-test-utils/CHANGELOG.md | 7 + .../events-backend-test-utils/package.json | 2 +- plugins/events-backend/CHANGELOG.md | 10 + plugins/events-backend/package.json | 2 +- plugins/events-node/CHANGELOG.md | 7 + plugins/events-node/package.json | 2 +- .../example-todo-list-backend/CHANGELOG.md | 10 + .../example-todo-list-backend/package.json | 2 +- plugins/example-todo-list/CHANGELOG.md | 8 + plugins/example-todo-list/package.json | 2 +- plugins/explore-backend/CHANGELOG.md | 12 + plugins/explore-backend/package.json | 2 +- plugins/explore-react/CHANGELOG.md | 8 + plugins/explore-react/package.json | 2 +- plugins/explore/CHANGELOG.md | 17 + plugins/explore/package.json | 2 +- plugins/firehydrant/CHANGELOG.md | 10 + plugins/firehydrant/package.json | 2 +- plugins/fossa/CHANGELOG.md | 11 + plugins/fossa/package.json | 2 +- plugins/gcalendar/CHANGELOG.md | 9 + plugins/gcalendar/package.json | 2 +- plugins/gcp-projects/CHANGELOG.md | 8 + plugins/gcp-projects/package.json | 2 +- plugins/git-release-manager/CHANGELOG.md | 9 + plugins/git-release-manager/package.json | 2 +- plugins/github-actions/CHANGELOG.md | 12 + plugins/github-actions/package.json | 2 +- plugins/github-deployments/CHANGELOG.md | 13 + plugins/github-deployments/package.json | 2 +- plugins/github-issues/CHANGELOG.md | 12 + plugins/github-issues/package.json | 2 +- .../github-pull-requests-board/CHANGELOG.md | 11 + .../github-pull-requests-board/package.json | 2 +- plugins/gitops-profiles/CHANGELOG.md | 8 + plugins/gitops-profiles/package.json | 2 +- plugins/gocd/CHANGELOG.md | 11 + plugins/gocd/package.json | 2 +- plugins/graphiql/CHANGELOG.md | 10 + plugins/graphiql/package.json | 2 +- plugins/graphql-voyager/CHANGELOG.md | 8 + plugins/graphql-voyager/package.json | 2 +- plugins/home-react/CHANGELOG.md | 8 + plugins/home-react/package.json | 2 +- plugins/home/CHANGELOG.md | 18 + plugins/home/package.json | 2 +- plugins/ilert/CHANGELOG.md | 11 + plugins/ilert/package.json | 2 +- plugins/jenkins-backend/CHANGELOG.md | 17 + plugins/jenkins-backend/package.json | 2 +- plugins/jenkins-common/CHANGELOG.md | 8 + plugins/jenkins-common/package.json | 2 +- plugins/jenkins/CHANGELOG.md | 12 + plugins/jenkins/package.json | 2 +- plugins/kafka-backend/CHANGELOG.md | 10 + plugins/kafka-backend/package.json | 2 +- plugins/kafka/CHANGELOG.md | 10 + plugins/kafka/package.json | 2 +- plugins/kubernetes-backend/CHANGELOG.md | 20 + plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes-cluster/CHANGELOG.md | 12 + plugins/kubernetes-cluster/package.json | 2 +- plugins/kubernetes-common/CHANGELOG.md | 9 + plugins/kubernetes-common/package.json | 2 +- plugins/kubernetes-node/CHANGELOG.md | 11 + plugins/kubernetes-node/package.json | 2 +- plugins/kubernetes-react/CHANGELOG.md | 13 + plugins/kubernetes-react/package.json | 2 +- plugins/kubernetes/CHANGELOG.md | 12 + plugins/kubernetes/package.json | 2 +- plugins/lighthouse-backend/CHANGELOG.md | 15 + plugins/lighthouse-backend/package.json | 2 +- plugins/lighthouse/CHANGELOG.md | 11 + plugins/lighthouse/package.json | 2 +- plugins/linguist-backend/CHANGELOG.md | 17 + plugins/linguist-backend/package.json | 2 +- plugins/linguist/CHANGELOG.md | 14 + plugins/linguist/package.json | 2 +- plugins/microsoft-calendar/CHANGELOG.md | 9 + plugins/microsoft-calendar/package.json | 2 +- plugins/newrelic-dashboard/CHANGELOG.md | 11 + plugins/newrelic-dashboard/package.json | 2 +- plugins/newrelic/CHANGELOG.md | 8 + plugins/newrelic/package.json | 2 +- plugins/nomad-backend/CHANGELOG.md | 10 + plugins/nomad-backend/package.json | 2 +- plugins/nomad/CHANGELOG.md | 10 + plugins/nomad/package.json | 2 +- plugins/octopus-deploy/CHANGELOG.md | 10 + plugins/octopus-deploy/package.json | 2 +- plugins/opencost/CHANGELOG.md | 8 + plugins/opencost/package.json | 2 +- plugins/org-react/CHANGELOG.md | 11 + plugins/org-react/package.json | 2 +- plugins/org/CHANGELOG.md | 11 + plugins/org/package.json | 2 +- plugins/pagerduty/CHANGELOG.md | 12 + plugins/pagerduty/package.json | 2 +- plugins/periskop-backend/CHANGELOG.md | 9 + plugins/periskop-backend/package.json | 2 +- plugins/periskop/CHANGELOG.md | 11 + plugins/periskop/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/permission-backend/CHANGELOG.md | 13 + plugins/permission-backend/package.json | 2 +- plugins/permission-node/CHANGELOG.md | 12 + plugins/permission-node/package.json | 2 +- plugins/permission-react/CHANGELOG.md | 9 + plugins/permission-react/package.json | 2 +- plugins/playlist-backend/CHANGELOG.md | 16 + plugins/playlist-backend/package.json | 2 +- plugins/playlist/CHANGELOG.md | 16 + plugins/playlist/package.json | 2 +- plugins/proxy-backend/CHANGELOG.md | 9 + plugins/proxy-backend/package.json | 2 +- plugins/puppetdb/CHANGELOG.md | 11 + plugins/puppetdb/package.json | 2 +- plugins/rollbar-backend/CHANGELOG.md | 8 + plugins/rollbar-backend/package.json | 2 +- plugins/rollbar/CHANGELOG.md | 10 + plugins/rollbar/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- plugins/scaffolder-backend/CHANGELOG.md | 36 + plugins/scaffolder-backend/package.json | 2 +- plugins/scaffolder-common/CHANGELOG.md | 9 + plugins/scaffolder-common/package.json | 2 +- plugins/scaffolder-node/CHANGELOG.md | 18 + plugins/scaffolder-node/package.json | 2 +- plugins/scaffolder-react/CHANGELOG.md | 22 + plugins/scaffolder-react/package.json | 2 +- plugins/scaffolder/CHANGELOG.md | 26 + plugins/scaffolder/package.json | 2 +- .../CHANGELOG.md | 18 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- plugins/search-backend-module-pg/CHANGELOG.md | 11 + plugins/search-backend-module-pg/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 18 + .../package.json | 2 +- plugins/search-backend-node/CHANGELOG.md | 13 + plugins/search-backend-node/package.json | 2 +- plugins/search-backend/CHANGELOG.md | 17 + plugins/search-backend/package.json | 2 +- plugins/search-react/CHANGELOG.md | 13 + plugins/search-react/package.json | 2 +- plugins/search/CHANGELOG.md | 16 + plugins/search/package.json | 2 +- plugins/sentry/CHANGELOG.md | 10 + plugins/sentry/package.json | 2 +- plugins/shortcuts/CHANGELOG.md | 10 + plugins/shortcuts/package.json | 2 +- plugins/signals-backend/CHANGELOG.md | 13 + plugins/signals-backend/package.json | 2 +- plugins/signals-node/CHANGELOG.md | 12 + plugins/signals-node/package.json | 2 +- plugins/signals-react/CHANGELOG.md | 8 + plugins/signals-react/package.json | 2 +- plugins/signals/CHANGELOG.md | 11 + plugins/signals/package.json | 2 +- plugins/sonarqube-backend/CHANGELOG.md | 10 + plugins/sonarqube-backend/package.json | 2 +- plugins/sonarqube-react/CHANGELOG.md | 8 + plugins/sonarqube-react/package.json | 2 +- plugins/sonarqube/CHANGELOG.md | 11 + plugins/sonarqube/package.json | 2 +- plugins/splunk-on-call/CHANGELOG.md | 10 + plugins/splunk-on-call/package.json | 2 +- plugins/stack-overflow-backend/CHANGELOG.md | 7 + plugins/stack-overflow-backend/package.json | 2 +- plugins/stack-overflow/CHANGELOG.md | 13 + plugins/stack-overflow/package.json | 2 +- plugins/stackstorm/CHANGELOG.md | 9 + plugins/stackstorm/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/tech-insights-backend/CHANGELOG.md | 15 + plugins/tech-insights-backend/package.json | 2 +- plugins/tech-insights-node/CHANGELOG.md | 10 + plugins/tech-insights-node/package.json | 2 +- plugins/tech-insights/CHANGELOG.md | 13 + plugins/tech-insights/package.json | 2 +- plugins/tech-radar/CHANGELOG.md | 10 + plugins/tech-radar/package.json | 2 +- .../techdocs-addons-test-utils/CHANGELOG.md | 15 + .../techdocs-addons-test-utils/package.json | 2 +- plugins/techdocs-backend/CHANGELOG.md | 17 + plugins/techdocs-backend/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- plugins/techdocs-node/CHANGELOG.md | 15 + plugins/techdocs-node/package.json | 2 +- plugins/techdocs-react/CHANGELOG.md | 11 + plugins/techdocs-react/package.json | 2 +- plugins/techdocs/CHANGELOG.md | 24 + plugins/techdocs/package.json | 2 +- plugins/todo-backend/CHANGELOG.md | 15 + plugins/todo-backend/package.json | 2 +- plugins/todo/CHANGELOG.md | 11 + plugins/todo/package.json | 2 +- plugins/user-settings-backend/CHANGELOG.md | 12 + plugins/user-settings-backend/package.json | 2 +- plugins/user-settings/CHANGELOG.md | 15 + plugins/user-settings/package.json | 2 +- plugins/vault-backend/CHANGELOG.md | 12 + plugins/vault-backend/package.json | 2 +- plugins/vault-node/CHANGELOG.md | 7 + plugins/vault-node/package.json | 2 +- plugins/vault/CHANGELOG.md | 11 + plugins/vault/package.json | 2 +- plugins/xcmetrics/CHANGELOG.md | 9 + plugins/xcmetrics/package.json | 2 +- yarn.lock | 156 +- 485 files changed, 6948 insertions(+), 249 deletions(-) create mode 100644 .changeset/create-app-1706621803.md create mode 100644 docs/releases/v1.23.0-next.1-changelog.md create mode 100644 plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md diff --git a/.changeset/create-app-1706621803.md b/.changeset/create-app-1706621803.md new file mode 100644 index 0000000000..b50d431d4b --- /dev/null +++ b/.changeset/create-app-1706621803.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped create-app version. diff --git a/.changeset/pre.json b/.changeset/pre.json index 7b419c4250..e430b3c2f1 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -267,37 +267,57 @@ "@backstage/plugin-signals": "0.0.0", "@backstage/plugin-signals-backend": "0.0.0", "@backstage/plugin-signals-node": "0.0.0", - "@backstage/plugin-signals-react": "0.0.0" + "@backstage/plugin-signals-react": "0.0.0", + "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.0.0" }, "changesets": [ "afraid-numbers-invite", "angry-phones-arrive", + "blue-keys-do", + "breezy-cobras-sin", "brown-items-hammer", + "calm-cups-rule", + "calm-items-double", "chilled-chefs-notice", + "chilled-ways-wave", "chilly-seahorses-bake", "cold-cooks-care", + "create-app-1706621803", "curvy-ladybugs-impress", "cyan-bats-lick", "cyan-icons-rest", + "dirty-mirrors-retire", "dry-lizards-taste", "dry-lobsters-flash", + "dry-vans-kiss", "dull-dolphins-explain", "dull-fireants-repeat", + "eight-hounds-dream", "empty-ligers-hang", + "fair-mirrors-hammer", + "famous-houses-thank", "fast-jeans-walk", "fifty-adults-watch", "fifty-files-argue", "flat-wasps-fold", "forty-cars-scream", + "four-mugs-try", + "four-walls-perform", "friendly-cheetahs-rescue", + "funny-buttons-sip", "funny-timers-visit", + "giant-suits-switch", "good-lemons-lick", "gorgeous-bobcats-press", + "gorgeous-pumas-draw", + "green-flies-draw", "grumpy-poets-study", + "hot-paws-tap", "hot-pillows-poke", "hot-tips-doubt", "kind-clouds-fly", "large-frogs-grab", + "large-moons-speak", "large-tables-wonder", "lemon-cameras-remember", "long-suns-bow", @@ -307,11 +327,20 @@ "metal-elephants-sit", "metal-students-drive", "neat-hotels-wink", + "nice-carrots-dream", "nine-bulldogs-camp", "nine-olives-swim", + "ninety-rules-sneeze", + "old-papayas-shave", "old-students-smoke", + "olive-experts-fold", "olive-singers-accept", + "orange-walls-complain", "polite-meals-hug", + "quick-penguins-refuse", + "quick-ties-stare", + "quiet-donkeys-punch", + "rare-seals-thank", "real-eggs-sip", "renovate-47c1714", "renovate-4b698fb", @@ -320,32 +349,55 @@ "renovate-f58dd5c", "renovate-fb2e0b9", "rotten-lemons-cry", + "selfish-mails-scream", "serious-carpets-learn", "seven-dots-serve", "seven-plums-return", + "shaggy-trainers-rule", + "shaggy-windows-cross", "sharp-pandas-hunt", + "shiny-poets-tease", "shy-carrots-decide", "silent-hotels-knock", "silent-poets-grab", + "silent-waves-pretend", + "six-jobs-sin", "six-melons-end", "sixty-shoes-prove", + "smart-numbers-call", "soft-beans-tease", "sour-rivers-fry", "strange-parents-hammer", + "strong-lobsters-hide", + "sweet-ravens-glow", + "tall-tools-compare", "tame-numbers-smile", "tame-rockets-sin", + "tasty-feet-cheat", "ten-numbers-happen", "ten-planets-guess", + "ten-trainers-cough", + "tender-flowers-collect", + "thirty-dolls-admire", + "tidy-cooks-mix", + "tidy-cooks-mixed", "tiny-kiwis-know", "tough-drinks-scream", + "twelve-hounds-know", + "twelve-pens-rescue", + "twelve-weeks-march", "twenty-taxis-mate", "two-coats-smile", + "two-geese-explain", "two-singers-learn", + "unlucky-pens-search", "unlucky-wasps-tan", "warm-maps-scream", "wet-emus-work", "wicked-elephants-scream", "wild-owls-doubt", + "wise-flies-laugh", + "young-ladybugs-decide", "young-rules-repeat" ] } diff --git a/docs/releases/v1.23.0-next.1-changelog.md b/docs/releases/v1.23.0-next.1-changelog.md new file mode 100644 index 0000000000..0f83f0ce5d --- /dev/null +++ b/docs/releases/v1.23.0-next.1-changelog.md @@ -0,0 +1,3250 @@ +# Release v1.23.0-next.1 + +## @backstage/catalog-client@1.6.0-next.1 + +### Minor Changes + +- 43dad25: Add API to get location by entity + +### Patch Changes + +- c04c42b: Internal updates to auto-generated files. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/errors@1.2.3 + +## @backstage/core-compat-api@0.2.0-next.1 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- edfd3a5: Updated dependency `@oriflame/backstage-plugin-score-card` to `^0.8.0`. +- bc621aa: Updates to use the new `RouteResolutionsApi`. +- 46b63de: Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/version-bridge@1.0.7 + +## @backstage/core-components@0.14.0-next.0 + +### Minor Changes + +- 281e8c6: **BREAKING**: Removed the `SidebarIntro` component as it was providing instructions for features that do not exist, along with `IntroCard`. If you were relying on this component and want to keep using it you can refer to the original implementations of [`SidebarIntro`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L149) and [`IntroCard`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L100). + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/version-bridge@1.0.7 + +## @backstage/frontend-app-api@0.6.0-next.1 + +### Minor Changes + +- bdf4a8e: **BREAKING**: Removed the experimental `createExtensionTree` API. + +### Patch Changes + +- bc621aa: Updates to use the new `RouteResolutionsApi`. +- e586f79: Wrap the root element with the analytics context to ensure it always exists for all extensions. +- fb9b5e7: The default `ComponentsApi` implementation now uses the `ComponentRef` ID as the component key, rather than the reference instance. This fixes a bug where duplicate installations of `@backstage/frontend-plugin-api` would break the app. +- 46b63de: Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/frontend-plugin-api@0.6.0-next.1 + +### Minor Changes + +- e586f79: **BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root`. + +### Patch Changes + +- bc621aa: Added `RouteResolutionsApi` as a replacement for the routing context. +- 1e61ad3: App component extensions are no longer wrapped in an `ExtensionBoundary`, allowing them to inherit the outer context instead. +- 46b63de: Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter. +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/integration@1.9.0-next.0 + +### Minor Changes + +- e27b7f3: Fix rate limit detection by looking for HTTP status code 429 and updating the header `x-ratelimit-remaining` to look for in case of a 403 code is returned + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-analytics-module-ga@0.2.0-next.0 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-analytics-module-ga4@0.2.0-next.0 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-analytics-module-newrelic-browser@0.1.0-next.0 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.0-next.0 + +### Minor Changes + +- 23a98f8: Migrated the AWS ALB auth provider to new `@backstage/plugin-auth-backend-module-aws-alb-provider` module package. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend@0.20.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-azure-sites-backend@0.2.0-next.1 + +### Minor Changes + +- 28610f4: **BREAKING**: `catalogApi` and `permissionsApi` are now a requirement to be passed through to the `createRouter` function. + + You can fix the typescript issues by passing through the required dependencies like the below `diff` shows: + + ```diff + import { + createRouter, + AzureSitesApi, + } from '@backstage/plugin-azure-sites-backend'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + + const catalogClient = new CatalogClient({ + + discoveryApi: env.discovery, + + }); + + return await createRouter({ + logger: env.logger, + azureSitesApi: AzureSitesApi.fromConfig(env.config), + + catalogApi: catalogClient, + + permissionsApi: env.permissions, + }); + } + ``` + +### Patch Changes + +- 5a409bb: Azure Sites `start` and `stop` action is now protected with the Permissions framework. + + The below example describes an action that forbids anyone but the owner of the catalog entity to trigger actions towards a site tied to an entity. + + ```typescript + // packages/backend/src/plugins/permission.ts + import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common'; + ... + class TestPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise { + if (isPermission(request.permission, azureSitesActionPermission)) { + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.identity.ownershipEntityRefs ?? [], + }), + ); + } + ... + return { + result: AuthorizeResult.ALLOW, + }; + } + ... + } + ``` + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-azure-sites-common@0.1.2-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/plugin-catalog-backend@1.17.0-next.1 + +### Minor Changes + +- 43dad25: Add API to get location by entity + +### Patch Changes + +- 89b674c: Minor performance improvement for `queryEntities` when the limit is 0. +- efa8160: Rollback the change for wildcard discovery, this fixes a bug with the `AzureUrlReader` not working with wildcard paths +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.14-next.1 + +## @backstage/plugin-catalog-backend-module-github@0.5.0-next.1 + +### Minor Changes + +- a950ed0: Prevent Entity Providers from eliminating Users and Groups from the DB when the synchronisation fails + +### Patch Changes + +- 9477133: Decreased number of teams fetched by GraphQL Query responsible for fetching Teams and Members in organization, due to timeouts when running against big organizations +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-scaffolder@1.18.0-next.1 + +### Minor Changes + +- 9b9c05c: Updating the `EntityPicker` to only select `kind` `metadata.name` and `metadata.namespace` by default to improve performance on larger datasets. + +### Patch Changes + +- 31f0a0a: Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/plugin-scaffolder-react@1.8.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + +## @backstage/plugin-scaffolder-backend@1.21.0-next.1 + +### Minor Changes + +- 78c100b: Support providing an overriding token for `fetch:template`, `fetch:plain` and `fetch:file` when interacting with upstream integrations + +### Patch Changes + +- 09f8b31: Simple typo fix in the fetch:template action example on the word 'skeleton'. +- f6792c6: Move the `NODE_OPTIONS` messaging for `--no-node-snapshot` to the `SecureTemplater` in order to get better messaging at runtime +- e1c479d: When using node 20+ the `scaffolder-backend` will now throw an error at startup if the `--no-node-snapshot` option was + not provided to node. +- e0e5afe: Add option to configure nunjucks with the `trimBlocks` and `lstripBlocks` options in the fetch:template action +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.2.0-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.1.2-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.1.2-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.1.2-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.2.13-next.1 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-github@0.2.0-next.1 + +### Minor Changes + +- fd5eb1c: Allow to force the creation of a pull request from a forked repository + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-node@0.3.0-next.1 + +### Minor Changes + +- 78c100b: Support providing an overriding token for `fetch:template`, `fetch:plain` and `fetch:file` when interacting with upstream integrations + +### Patch Changes + +- e0e5afe: Add option to configure nunjucks with the `trimBlocks` and `lstripBlocks` options in the fetch:template action +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + +## @backstage/plugin-scaffolder-react@1.8.0-next.1 + +### Minor Changes + +- b07ec70: Use more distinguishable icons for link (`Link`) and text output (`Description`). + +### Patch Changes + +- 3f60ad5: fix for: converting circular structure to JSON error +- 31f0a0a: Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages +- 82affc7: Fix issue where `ui:schema` was replaced with an empty object if `dependencies` is defined +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + +## @backstage/plugin-techdocs@1.10.0-next.1 + +### Minor Changes + +- af4d147: Updated the styling for tags to avoid word break. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/app-defaults@1.4.8-next.1 + +### Patch Changes + +- 7da67ce: Change `defaultScopes` for Bitbucket auth from invalid `team` to `account`. +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/theme@0.5.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + +## @backstage/backend-app-api@0.5.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/backend-common@0.21.0-next.1 + +### Patch Changes + +- 1f020fe: Support `token` in `readTree`, `readUrl` and `search` +- e27b7f3: Fix rate limit detection by looking for HTTP status code 429 and updating the header `x-ratelimit-remaining` to look for in case of a 403 code is returned +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-app-api@0.5.11-next.1 + - @backstage/backend-dev-utils@0.1.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + +## @backstage/backend-defaults@0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-app-api@0.5.11-next.1 + +## @backstage/backend-dynamic-feature-service@0.1.1-next.1 + +### Patch Changes + +- 8723c5a: Fix wrong `alpha` support in dynamic plugins support: the `alpha` sub-package should not be required for the dynamic plugins to be loaded under the new backend system. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-events-backend@0.2.19-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/backend-openapi-utils@0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + +## @backstage/backend-plugin-api@0.6.10-next.1 + +### Patch Changes + +- 1f020fe: Support `token` in `readTree`, `readUrl` and `search` +- Updated dependencies + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/backend-tasks@0.5.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/backend-test-utils@0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-app-api@0.5.11-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/catalog-model@1.4.4-next.0 + +### Patch Changes + +- 07e7d12: Fix wording in API reference +- Updated dependencies + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/cli@0.25.2-next.1 + +### Patch Changes + +- b58673e: Upgrade jest +- 08804c3: Fixed an issue that would cause an invalid `__backstage-autodetected-plugins__.js` to be written when using experimental module discovery. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/eslint-plugin@0.1.5-next.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + +## @backstage/core-app-api@1.11.4-next.0 + +### Patch Changes + +- 7da67ce: Change `defaultScopes` for Bitbucket auth from invalid `team` to `account`. +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/core-plugin-api@1.8.3-next.0 + +### Patch Changes + +- e586f79: Throw a more specific exception `NotImplementedError` when an API implementation cannot be found. +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/create-app@0.5.11-next.1 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.13 + +## @backstage/dev-utils@1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/theme@0.5.0 + +## @backstage/frontend-test-utils@0.1.2-next.1 + +### Patch Changes + +- bc621aa: Updates to use the new `RouteResolutionsApi`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/frontend-app-api@0.6.0-next.1 + - @backstage/test-utils@1.5.0-next.1 + - @backstage/types@1.1.1 + +## @backstage/integration-react@1.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + +## @backstage/repo-tools@0.6.0-next.1 + +### Patch Changes + +- c04c42b: Fixes an issue where comments would be duplicated in the template. Also, removes a header with the title and version of the OpenAPI spec from generated code. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/errors@1.2.3 + +## @techdocs/cli@1.8.2-next.1 + +### Patch Changes + +- d8d243c: fix: mkdocs parameter casing +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-techdocs-node@1.11.2-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## @backstage/test-utils@1.5.0-next.1 + +### Patch Changes + +- 07e7d12: Fix wording in API reference +- 7da67ce: Change `defaultScopes` for Bitbucket auth from invalid `team` to `account`. +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + +## @backstage/plugin-adr@0.6.13-next.1 + +### Patch Changes + +- 987f565: Fix alignment of text in `AdrSearchResultListItem`. Update size and font to match other `SearchResultListItem`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-adr-common@0.2.20-next.0 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-adr-backend@0.4.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-adr-common@0.2.20-next.0 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-adr-common@0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-airbrake@0.3.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/test-utils@1.5.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/dev-utils@1.0.27-next.1 + +## @backstage/plugin-airbrake-backend@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-allure@0.1.46-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-apache-airflow@0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-api-docs@0.10.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + +## @backstage/plugin-apollo-explorer@0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-app-backend@0.3.58-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.10-next.1 + +## @backstage/plugin-app-node@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + +## @backstage/plugin-app-visualizer@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-auth-backend@0.20.4-next.1 + +### Patch Changes + +- 23a98f8: Migrated the AWS ALB auth provider to new `@backstage/plugin-auth-backend-module-aws-alb-provider` module package. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.2-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.4-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.2-next.1 + - @backstage/plugin-auth-backend-module-oidc-provider@0.1.0-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.3-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-google-provider@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend@0.20.4-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-okta-provider@0.0.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-auth-node@0.4.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-azure-devops@0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-azure-devops-common@0.3.2 + +## @backstage/plugin-azure-devops-backend@0.5.2-next.1 + +### Patch Changes + +- 25bda45: Fixed bug with `extractPartsFromAsset` that resulted in a leading `.` being removed from the path in an otherwise valid path (ex. `.assets/image.png`). The leading `.` will now only be moved for paths beginning with `./`. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-devops-common@0.3.2 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-azure-sites@0.1.19-next.1 + +### Patch Changes + +- 5a409bb: Azure Sites `start` and `stop` action is now protected with the Permissions framework. + + The below example describes an action that forbids anyone but the owner of the catalog entity to trigger actions towards a site tied to an entity. + + ```typescript + // packages/backend/src/plugins/permission.ts + import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common'; + ... + class TestPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise { + if (isPermission(request.permission, azureSitesActionPermission)) { + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.identity.ownershipEntityRefs ?? [], + }), + ); + } + ... + return { + result: AuthorizeResult.ALLOW, + }; + } + ... + } + ``` + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-azure-sites-common@0.1.2-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + +## @backstage/plugin-azure-sites-common@0.1.2-next.0 + +### Patch Changes + +- 5a409bb: Azure Sites `start` and `stop` action is now protected with the Permissions framework. + + The below example describes an action that forbids anyone but the owner of the catalog entity to trigger actions towards a site tied to an entity. + + ```typescript + // packages/backend/src/plugins/permission.ts + import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common'; + ... + class TestPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise { + if (isPermission(request.permission, azureSitesActionPermission)) { + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.identity.ownershipEntityRefs ?? [], + }), + ); + } + ... + return { + result: AuthorizeResult.ALLOW, + }; + } + ... + } + ``` + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/plugin-badges@0.2.54-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-badges-backend@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-bazaar@0.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-bazaar-backend@0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-bitbucket-cloud-common@0.2.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.9.0-next.0 + +## @backstage/plugin-bitrise@0.1.57-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-catalog@1.17.0-next.1 + +### Patch Changes + +- 987f565: Add line clamping to `CatalogSearchResultListItem` +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-catalog-backend-module-aws@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + +## @backstage/plugin-catalog-backend-module-azure@0.1.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.16-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.16-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-github-org@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-catalog-common@1.0.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-catalog-graph@0.3.4-next.1 + +### Patch Changes + +- f937aae: use `CatalogClient.getEntitiesByRefs()` to reduce the number of backend requests from plugin `catalog-graph` +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-import@0.10.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + +## @backstage/plugin-catalog-node@1.6.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/plugin-catalog-react@1.9.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + +## @backstage/plugin-catalog-unprocessed-entities@0.1.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-cicd-statistics@0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-cicd-statistics@0.1.32-next.1 + +## @backstage/plugin-circleci@0.3.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-cloudbuild@0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-code-climate@0.1.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-code-coverage@0.2.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-code-coverage-backend@0.2.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-codescene@0.1.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-config-schema@0.1.50-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-cost-insights@0.12.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-cost-insights-common@0.1.2 + +## @backstage/plugin-devtools@0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + - @backstage/plugin-devtools-common@0.1.8 + - @backstage/plugin-permission-react@0.4.20-next.0 + +## @backstage/plugin-devtools-backend@0.2.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-devtools-common@0.1.8 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/plugin-dynatrace@8.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-entity-feedback@0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-entity-feedback-common@0.1.3 + +## @backstage/plugin-entity-feedback-backend@0.2.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-entity-feedback-common@0.1.3 + +## @backstage/plugin-entity-validation@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + +## @backstage/plugin-events-backend@0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-backend-module-aws-sqs@0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-backend-module-azure@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-backend-module-gerrit@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-backend-module-github@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-backend-module-gitlab@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-backend-test-utils@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-events-node@0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + +## @backstage/plugin-explore@0.4.16-next.1 + +### Patch Changes + +- 07e7d12: Fix wording in API reference +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-explore-react@0.0.36-next.0 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-explore-backend@0.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-module-explore@0.1.14-next.1 + +## @backstage/plugin-explore-react@0.0.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-explore-common@0.0.2 + +## @backstage/plugin-firehydrant@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-fossa@0.2.62-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-gcalendar@0.3.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-gcp-projects@0.3.46-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-git-release-manager@0.3.42-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + +## @backstage/plugin-github-actions@0.6.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + +## @backstage/plugin-github-deployments@0.1.61-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-github-issues@0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-github-pull-requests-board@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-gitops-profiles@0.3.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-gocd@0.1.36-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-graphiql@0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-graphql-voyager@0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-home@0.6.2-next.1 + +### Patch Changes + +- 384c132: Added filter support for HomePageVisitedByType in order to enable filtering entities from the list +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-home-react@0.1.8-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + +## @backstage/plugin-home-react@0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-ilert@0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-jenkins@0.9.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-jenkins-common@0.1.24-next.0 + +## @backstage/plugin-jenkins-backend@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-jenkins-common@0.1.24-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/plugin-jenkins-common@0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/plugin-kafka@0.3.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-kafka-backend@0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-kubernetes@0.11.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-kubernetes-react@0.3.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + +## @backstage/plugin-kubernetes-backend@0.14.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-kubernetes-node@0.1.4-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/plugin-kubernetes-cluster@0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-kubernetes-react@0.3.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + +## @backstage/plugin-kubernetes-common@0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/plugin-kubernetes-node@0.1.4-next.1 + +### Patch Changes + +- cceed8a: Introduced `PinnipedHelper` class to enable authentication to Kubernetes clusters through Pinniped +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + +## @backstage/plugin-kubernetes-react@0.3.0-next.1 + +### Patch Changes + +- 3c184af: Extracted common dialog component. +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + +## @backstage/plugin-lighthouse@0.4.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-lighthouse-common@0.1.4 + +## @backstage/plugin-lighthouse-backend@0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-lighthouse-common@0.1.4 + +## @backstage/plugin-linguist@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-linguist-common@0.1.2 + +## @backstage/plugin-linguist-backend@0.5.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-linguist-common@0.1.2 + +## @backstage/plugin-microsoft-calendar@0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-newrelic@0.3.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-newrelic-dashboard@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-nomad@0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-nomad-backend@0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-octopus-deploy@0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-opencost@0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-org@0.6.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + +## @backstage/plugin-org-react@0.1.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-pagerduty@0.7.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-home-react@0.1.8-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-periskop@0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-periskop-backend@0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-permission-backend@0.5.33-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + +## @backstage/plugin-permission-node@0.7.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/plugin-permission-react@0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/plugin-playlist@0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-playlist-common@0.1.14 + +## @backstage/plugin-playlist-backend@0.3.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-playlist-common@0.1.14 + +## @backstage/plugin-proxy-backend@0.4.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-puppetdb@0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-rollbar@0.4.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-rollbar-backend@0.1.55-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-azure@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-common@1.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + +## @backstage/plugin-search@1.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend@1.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend-module-catalog@0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend-module-elasticsearch@1.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend-module-explore@0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend-module-pg@0.5.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend-module-techdocs@0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-techdocs-node@1.11.2-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-backend-node@1.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-search-react@1.7.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-sentry@0.5.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-shortcuts@0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-signals@0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.1-next.1 + +## @backstage/plugin-signals-backend@0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-signals-node@0.0.1-next.1 + +## @backstage/plugin-signals-node@0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + +## @backstage/plugin-signals-react@0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-sonarqube@0.7.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-sonarqube-react@0.1.13-next.0 + +## @backstage/plugin-sonarqube-backend@0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-sonarqube-react@0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-splunk-on-call@0.4.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + +## @backstage/plugin-stack-overflow@0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/plugin-home-react@0.1.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-stack-overflow-backend@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.3-next.1 + +## @backstage/plugin-stackstorm@0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-tech-insights@0.3.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + +## @backstage/plugin-tech-insights-backend@0.5.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.16-next.1 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.42-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.16-next.1 + +## @backstage/plugin-tech-insights-node@0.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + +## @backstage/plugin-tech-radar@0.6.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/test-utils@1.5.0-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + +## @backstage/plugin-techdocs-backend@1.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-techdocs-node@1.11.2-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-backend-module-techdocs@0.1.14-next.1 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + +## @backstage/plugin-techdocs-node@1.11.2-next.1 + +### Patch Changes + +- 77e3050: Update to a newer version of `@trendyol-js/openstack-swift-sdk` +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-search-common@1.2.10 + +## @backstage/plugin-techdocs-react@1.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/version-bridge@1.0.7 + +## @backstage/plugin-todo@0.2.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-todo-backend@0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + +## @backstage/plugin-user-settings@0.8.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-user-settings-backend@0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + +## @backstage/plugin-vault@0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-vault-backend@0.4.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-vault-node@0.1.3-next.1 + +## @backstage/plugin-vault-node@0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + +## @backstage/plugin-xcmetrics@0.2.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + +## example-app@0.2.92-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.6.0-next.1 + - @backstage/plugin-scaffolder-react@1.8.0-next.1 + - @backstage/plugin-adr@0.6.13-next.1 + - @backstage/plugin-catalog-graph@0.3.4-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/plugin-scaffolder@1.18.0-next.1 + - @backstage/cli@0.25.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-explore@0.4.16-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/plugin-azure-sites@0.1.19-next.1 + - @backstage/plugin-home@0.6.2-next.1 + - @backstage/plugin-catalog-import@0.10.6-next.1 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-devtools@0.1.9-next.1 + - @backstage/plugin-graphiql@0.3.3-next.1 + - @backstage/plugin-linguist@0.1.15-next.1 + - @backstage/plugin-search@1.4.6-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/plugin-stack-overflow@0.1.25-next.1 + - @backstage/plugin-tech-radar@0.6.13-next.1 + - @backstage/plugin-user-settings@0.8.1-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-airbrake@0.3.30-next.1 + - @backstage/plugin-apache-airflow@0.2.20-next.0 + - @backstage/plugin-api-docs@0.10.4-next.1 + - @backstage/plugin-azure-devops@0.3.12-next.1 + - @backstage/plugin-badges@0.2.54-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.8-next.0 + - @backstage/plugin-cloudbuild@0.4.0-next.1 + - @backstage/plugin-code-coverage@0.2.23-next.1 + - @backstage/plugin-cost-insights@0.12.19-next.1 + - @backstage/plugin-dynatrace@8.0.4-next.1 + - @backstage/plugin-entity-feedback@0.2.13-next.1 + - @backstage/plugin-gcalendar@0.3.23-next.0 + - @backstage/plugin-gcp-projects@0.3.46-next.0 + - @backstage/plugin-github-actions@0.6.11-next.1 + - @backstage/plugin-gocd@0.1.36-next.1 + - @backstage/plugin-jenkins@0.9.5-next.1 + - @backstage/plugin-kafka@0.3.30-next.1 + - @backstage/plugin-kubernetes@0.11.5-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.6-next.1 + - @backstage/plugin-lighthouse@0.4.15-next.1 + - @backstage/plugin-microsoft-calendar@0.1.12-next.0 + - @backstage/plugin-newrelic@0.3.45-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.5-next.1 + - @backstage/plugin-nomad@0.1.11-next.1 + - @backstage/plugin-octopus-deploy@0.2.12-next.1 + - @backstage/plugin-org@0.6.20-next.1 + - @backstage/plugin-pagerduty@0.7.2-next.1 + - @backstage/plugin-playlist@0.2.4-next.1 + - @backstage/plugin-puppetdb@0.1.13-next.1 + - @backstage/plugin-rollbar@0.4.30-next.1 + - @backstage/plugin-sentry@0.5.15-next.1 + - @backstage/plugin-shortcuts@0.3.19-next.0 + - @backstage/plugin-signals@0.0.1-next.1 + - @backstage/plugin-stackstorm@0.1.11-next.0 + - @backstage/plugin-tech-insights@0.3.22-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.5-next.1 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/plugin-todo@0.2.34-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-search-common@1.2.10 + +## example-app-next@0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/frontend-app-api@0.6.0-next.1 + - @backstage/plugin-scaffolder-react@1.8.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/plugin-adr@0.6.13-next.1 + - @backstage/plugin-catalog-graph@0.3.4-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/plugin-scaffolder@1.18.0-next.1 + - @backstage/cli@0.25.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-explore@0.4.16-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/plugin-azure-sites@0.1.19-next.1 + - @backstage/plugin-home@0.6.2-next.1 + - app-next-example-plugin@0.0.6-next.1 + - @backstage/plugin-app-visualizer@0.1.1-next.1 + - @backstage/plugin-catalog-import@0.10.6-next.1 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-devtools@0.1.9-next.1 + - @backstage/plugin-graphiql@0.3.3-next.1 + - @backstage/plugin-linguist@0.1.15-next.1 + - @backstage/plugin-search@1.4.6-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/plugin-tech-radar@0.6.13-next.1 + - @backstage/plugin-user-settings@0.8.1-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-airbrake@0.3.30-next.1 + - @backstage/plugin-apache-airflow@0.2.20-next.0 + - @backstage/plugin-api-docs@0.10.4-next.1 + - @backstage/plugin-azure-devops@0.3.12-next.1 + - @backstage/plugin-badges@0.2.54-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.8-next.0 + - @backstage/plugin-cloudbuild@0.4.0-next.1 + - @backstage/plugin-code-coverage@0.2.23-next.1 + - @backstage/plugin-cost-insights@0.12.19-next.1 + - @backstage/plugin-dynatrace@8.0.4-next.1 + - @backstage/plugin-entity-feedback@0.2.13-next.1 + - @backstage/plugin-gcalendar@0.3.23-next.0 + - @backstage/plugin-gcp-projects@0.3.46-next.0 + - @backstage/plugin-github-actions@0.6.11-next.1 + - @backstage/plugin-gocd@0.1.36-next.1 + - @backstage/plugin-jenkins@0.9.5-next.1 + - @backstage/plugin-kafka@0.3.30-next.1 + - @backstage/plugin-kubernetes@0.11.5-next.1 + - @backstage/plugin-lighthouse@0.4.15-next.1 + - @backstage/plugin-microsoft-calendar@0.1.12-next.0 + - @backstage/plugin-newrelic@0.3.45-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.5-next.1 + - @backstage/plugin-octopus-deploy@0.2.12-next.1 + - @backstage/plugin-org@0.6.20-next.1 + - @backstage/plugin-pagerduty@0.7.2-next.1 + - @backstage/plugin-playlist@0.2.4-next.1 + - @backstage/plugin-puppetdb@0.1.13-next.1 + - @backstage/plugin-rollbar@0.4.30-next.1 + - @backstage/plugin-sentry@0.5.15-next.1 + - @backstage/plugin-shortcuts@0.3.19-next.0 + - @backstage/plugin-stackstorm@0.1.11-next.0 + - @backstage/plugin-tech-insights@0.3.22-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.5-next.1 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/plugin-todo@0.2.34-next.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-search-common@1.2.10 + +## app-next-example-plugin@0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + +## example-backend@0.2.92-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.21.0-next.1 + - @backstage/plugin-azure-devops-backend@0.5.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend@0.20.4-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-azure-sites-common@0.1.2-next.0 + - example-app@0.2.92-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-adr-backend@0.4.7-next.1 + - @backstage/plugin-app-backend@0.3.58-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-badges-backend@0.3.7-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.7-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-code-coverage-backend@0.2.24-next.1 + - @backstage/plugin-devtools-backend@0.2.7-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.7-next.1 + - @backstage/plugin-events-backend@0.2.19-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-explore-backend@0.0.20-next.1 + - @backstage/plugin-jenkins-backend@0.3.4-next.1 + - @backstage/plugin-kafka-backend@0.3.8-next.1 + - @backstage/plugin-kubernetes-backend@0.14.2-next.1 + - @backstage/plugin-lighthouse-backend@0.4.2-next.1 + - @backstage/plugin-linguist-backend@0.5.7-next.1 + - @backstage/plugin-nomad-backend@0.1.12-next.1 + - @backstage/plugin-permission-backend@0.5.33-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-playlist-backend@0.3.14-next.1 + - @backstage/plugin-proxy-backend@0.4.8-next.1 + - @backstage/plugin-rollbar-backend@0.1.55-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.27-next.1 + - @backstage/plugin-search-backend@1.5.0-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.14-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.13-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.14-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.19-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.14-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + - @backstage/plugin-signals-backend@0.0.1-next.1 + - @backstage/plugin-signals-node@0.0.1-next.1 + - @backstage/plugin-tech-insights-backend@0.5.24-next.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.42-next.1 + - @backstage/plugin-tech-insights-node@0.4.16-next.1 + - @backstage/plugin-techdocs-backend@1.9.3-next.1 + - @backstage/plugin-todo-backend@0.3.8-next.1 + +## example-backend-next@0.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.21.0-next.1 + - @backstage/plugin-azure-devops-backend@0.5.2-next.1 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-defaults@0.2.10-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/plugin-adr-backend@0.4.7-next.1 + - @backstage/plugin-app-backend@0.3.58-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-badges-backend@0.3.7-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.3-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.1.27-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.7-next.1 + - @backstage/plugin-devtools-backend@0.2.7-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.7-next.1 + - @backstage/plugin-jenkins-backend@0.3.4-next.1 + - @backstage/plugin-kubernetes-backend@0.14.2-next.1 + - @backstage/plugin-lighthouse-backend@0.4.2-next.1 + - @backstage/plugin-linguist-backend@0.5.7-next.1 + - @backstage/plugin-nomad-backend@0.1.12-next.1 + - @backstage/plugin-permission-backend@0.5.33-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.7-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-playlist-backend@0.3.14-next.1 + - @backstage/plugin-proxy-backend@0.4.8-next.1 + - @backstage/plugin-search-backend@1.5.0-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.14-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.14-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.14-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-sonarqube-backend@0.2.12-next.1 + - @backstage/plugin-techdocs-backend@1.9.3-next.1 + - @backstage/plugin-todo-backend@0.3.8-next.1 + +## e2e-test@0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.11-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + +## techdocs-cli-embedded-app@0.2.91-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/cli@0.25.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/test-utils@1.5.0-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + +## @internal/plugin-todo-list@1.0.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + +## @internal/plugin-todo-list-backend@1.0.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 diff --git a/package.json b/package.json index 2b16580f84..1e53b7c00f 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@material-ui/pickers@^3.3.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch", "@material-ui/pickers@^3.2.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch" }, - "version": "1.23.0-next.0", + "version": "1.23.0-next.1", "dependencies": { "@backstage/errors": "workspace:^", "@manypkg/get-packages": "^1.1.3", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index 9ac73e575a..5a94ee1e0f 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/app-defaults +## 1.4.8-next.1 + +### Patch Changes + +- 7da67ce: Change `defaultScopes` for Bitbucket auth from invalid `team` to `account`. +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/theme@0.5.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + ## 1.4.8-next.0 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 4b15d5875a..450c9899e6 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/app-defaults", "description": "Provides the default wiring of a Backstage App", - "version": "1.4.8-next.0", + "version": "1.4.8-next.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 4e41b9690a..6369de1cb5 100644 --- a/packages/app-next-example-plugin/CHANGELOG.md +++ b/packages/app-next-example-plugin/CHANGELOG.md @@ -1,5 +1,13 @@ # app-next-example-plugin +## 0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + ## 0.0.6-next.0 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index 05d7e20823..02c4a8fc9e 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,7 +1,7 @@ { "name": "app-next-example-plugin", "description": "Backstage internal example plugin", - "version": "0.0.6-next.0", + "version": "0.0.6-next.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index e641aa8f71..74f3baa8b6 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,81 @@ # example-app-next +## 0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/frontend-app-api@0.6.0-next.1 + - @backstage/plugin-scaffolder-react@1.8.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/plugin-adr@0.6.13-next.1 + - @backstage/plugin-catalog-graph@0.3.4-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/plugin-scaffolder@1.18.0-next.1 + - @backstage/cli@0.25.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-explore@0.4.16-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/plugin-azure-sites@0.1.19-next.1 + - @backstage/plugin-home@0.6.2-next.1 + - app-next-example-plugin@0.0.6-next.1 + - @backstage/plugin-app-visualizer@0.1.1-next.1 + - @backstage/plugin-catalog-import@0.10.6-next.1 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-devtools@0.1.9-next.1 + - @backstage/plugin-graphiql@0.3.3-next.1 + - @backstage/plugin-linguist@0.1.15-next.1 + - @backstage/plugin-search@1.4.6-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/plugin-tech-radar@0.6.13-next.1 + - @backstage/plugin-user-settings@0.8.1-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-airbrake@0.3.30-next.1 + - @backstage/plugin-apache-airflow@0.2.20-next.0 + - @backstage/plugin-api-docs@0.10.4-next.1 + - @backstage/plugin-azure-devops@0.3.12-next.1 + - @backstage/plugin-badges@0.2.54-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.8-next.0 + - @backstage/plugin-cloudbuild@0.4.0-next.1 + - @backstage/plugin-code-coverage@0.2.23-next.1 + - @backstage/plugin-cost-insights@0.12.19-next.1 + - @backstage/plugin-dynatrace@8.0.4-next.1 + - @backstage/plugin-entity-feedback@0.2.13-next.1 + - @backstage/plugin-gcalendar@0.3.23-next.0 + - @backstage/plugin-gcp-projects@0.3.46-next.0 + - @backstage/plugin-github-actions@0.6.11-next.1 + - @backstage/plugin-gocd@0.1.36-next.1 + - @backstage/plugin-jenkins@0.9.5-next.1 + - @backstage/plugin-kafka@0.3.30-next.1 + - @backstage/plugin-kubernetes@0.11.5-next.1 + - @backstage/plugin-lighthouse@0.4.15-next.1 + - @backstage/plugin-microsoft-calendar@0.1.12-next.0 + - @backstage/plugin-newrelic@0.3.45-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.5-next.1 + - @backstage/plugin-octopus-deploy@0.2.12-next.1 + - @backstage/plugin-org@0.6.20-next.1 + - @backstage/plugin-pagerduty@0.7.2-next.1 + - @backstage/plugin-playlist@0.2.4-next.1 + - @backstage/plugin-puppetdb@0.1.13-next.1 + - @backstage/plugin-rollbar@0.4.30-next.1 + - @backstage/plugin-sentry@0.5.15-next.1 + - @backstage/plugin-shortcuts@0.3.19-next.0 + - @backstage/plugin-stackstorm@0.1.11-next.0 + - @backstage/plugin-tech-insights@0.3.22-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.5-next.1 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/plugin-todo@0.2.34-next.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-search-common@1.2.10 + ## 0.0.6-next.0 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 09cd302a4e..3907661e5f 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.6-next.0", + "version": "0.0.6-next.1", "private": true, "backstage": { "role": "frontend" diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 370100920f..107eaba754 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,82 @@ # example-app +## 0.2.92-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.6.0-next.1 + - @backstage/plugin-scaffolder-react@1.8.0-next.1 + - @backstage/plugin-adr@0.6.13-next.1 + - @backstage/plugin-catalog-graph@0.3.4-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/plugin-scaffolder@1.18.0-next.1 + - @backstage/cli@0.25.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-explore@0.4.16-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/plugin-azure-sites@0.1.19-next.1 + - @backstage/plugin-home@0.6.2-next.1 + - @backstage/plugin-catalog-import@0.10.6-next.1 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-devtools@0.1.9-next.1 + - @backstage/plugin-graphiql@0.3.3-next.1 + - @backstage/plugin-linguist@0.1.15-next.1 + - @backstage/plugin-search@1.4.6-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/plugin-stack-overflow@0.1.25-next.1 + - @backstage/plugin-tech-radar@0.6.13-next.1 + - @backstage/plugin-user-settings@0.8.1-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-airbrake@0.3.30-next.1 + - @backstage/plugin-apache-airflow@0.2.20-next.0 + - @backstage/plugin-api-docs@0.10.4-next.1 + - @backstage/plugin-azure-devops@0.3.12-next.1 + - @backstage/plugin-badges@0.2.54-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.1.8-next.0 + - @backstage/plugin-cloudbuild@0.4.0-next.1 + - @backstage/plugin-code-coverage@0.2.23-next.1 + - @backstage/plugin-cost-insights@0.12.19-next.1 + - @backstage/plugin-dynatrace@8.0.4-next.1 + - @backstage/plugin-entity-feedback@0.2.13-next.1 + - @backstage/plugin-gcalendar@0.3.23-next.0 + - @backstage/plugin-gcp-projects@0.3.46-next.0 + - @backstage/plugin-github-actions@0.6.11-next.1 + - @backstage/plugin-gocd@0.1.36-next.1 + - @backstage/plugin-jenkins@0.9.5-next.1 + - @backstage/plugin-kafka@0.3.30-next.1 + - @backstage/plugin-kubernetes@0.11.5-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.6-next.1 + - @backstage/plugin-lighthouse@0.4.15-next.1 + - @backstage/plugin-microsoft-calendar@0.1.12-next.0 + - @backstage/plugin-newrelic@0.3.45-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.5-next.1 + - @backstage/plugin-nomad@0.1.11-next.1 + - @backstage/plugin-octopus-deploy@0.2.12-next.1 + - @backstage/plugin-org@0.6.20-next.1 + - @backstage/plugin-pagerduty@0.7.2-next.1 + - @backstage/plugin-playlist@0.2.4-next.1 + - @backstage/plugin-puppetdb@0.1.13-next.1 + - @backstage/plugin-rollbar@0.4.30-next.1 + - @backstage/plugin-sentry@0.5.15-next.1 + - @backstage/plugin-shortcuts@0.3.19-next.0 + - @backstage/plugin-signals@0.0.1-next.1 + - @backstage/plugin-stackstorm@0.1.11-next.0 + - @backstage/plugin-tech-insights@0.3.22-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.5-next.1 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/plugin-todo@0.2.34-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-search-common@1.2.10 + ## 0.2.92-next.0 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 7b81260b8b..31be0e1610 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.92-next.0", + "version": "0.2.92-next.1", "private": true, "backstage": { "role": "frontend" diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index 6096ba5451..00c8a7ce99 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/backend-app-api +## 0.5.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 0.5.11-next.0 ### Patch Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index b3b338031e..0d8065ef5c 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-app-api", "description": "Core API used by Backstage backend apps", - "version": "0.5.11-next.0", + "version": "0.5.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index ca05772aa5..d35884e425 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/backend-common +## 0.21.0-next.1 + +### Patch Changes + +- 1f020fe: Support `token` in `readTree`, `readUrl` and `search` +- e27b7f3: Fix rate limit detection by looking for HTTP status code 429 and updating the header `x-ratelimit-remaining` to look for in case of a 403 code is returned +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-app-api@0.5.11-next.1 + - @backstage/backend-dev-utils@0.1.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + ## 0.21.0-next.0 ### Minor Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 4df64738ce..2aab5c979c 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.21.0-next.0", + "version": "0.21.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 1b50491f54..479589a0d4 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-defaults +## 0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-app-api@0.5.11-next.1 + ## 0.2.10-next.0 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 3e8793cd3e..bf9b70a6a2 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-defaults", "description": "Backend defaults used by Backstage backend apps", - "version": "0.2.10-next.0", + "version": "0.2.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index fa60b2d208..8615607ec8 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/backend-dynamic-feature-service +## 0.1.1-next.1 + +### Patch Changes + +- 8723c5a: Fix wrong `alpha` support in dynamic plugins support: the `alpha` sub-package should not be required for the dynamic plugins to be loaded under the new backend system. +- Updated dependencies + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-events-backend@0.2.19-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 0.1.1-next.0 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 51f7184c49..5a3b88eda6 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-dynamic-feature-service", "description": "Backstage dynamic feature service", - "version": "0.1.1-next.0", + "version": "0.1.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-next/CHANGELOG.md b/packages/backend-next/CHANGELOG.md index 25474b6c54..2354345909 100644 --- a/packages/backend-next/CHANGELOG.md +++ b/packages/backend-next/CHANGELOG.md @@ -1,5 +1,46 @@ # example-backend-next +## 0.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.21.0-next.1 + - @backstage/plugin-azure-devops-backend@0.5.2-next.1 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-defaults@0.2.10-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/plugin-adr-backend@0.4.7-next.1 + - @backstage/plugin-app-backend@0.3.58-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-badges-backend@0.3.7-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.3-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.1.27-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.7-next.1 + - @backstage/plugin-devtools-backend@0.2.7-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.7-next.1 + - @backstage/plugin-jenkins-backend@0.3.4-next.1 + - @backstage/plugin-kubernetes-backend@0.14.2-next.1 + - @backstage/plugin-lighthouse-backend@0.4.2-next.1 + - @backstage/plugin-linguist-backend@0.5.7-next.1 + - @backstage/plugin-nomad-backend@0.1.12-next.1 + - @backstage/plugin-permission-backend@0.5.33-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.7-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-playlist-backend@0.3.14-next.1 + - @backstage/plugin-proxy-backend@0.4.8-next.1 + - @backstage/plugin-search-backend@1.5.0-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.14-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.14-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.14-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-sonarqube-backend@0.2.12-next.1 + - @backstage/plugin-techdocs-backend@1.9.3-next.1 + - @backstage/plugin-todo-backend@0.3.8-next.1 + ## 0.0.20-next.0 ### Patch Changes diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index d230227889..eca36208a9 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-next", - "version": "0.0.20-next.0", + "version": "0.0.20-next.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 0b21b9aff3..fcddc4c1b8 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/backend-openapi-utils +## 0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + ## 0.1.3-next.0 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index a9ce8c56e2..c551f1410b 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-openapi-utils", "description": "OpenAPI typescript support.", - "version": "0.1.3-next.0", + "version": "0.1.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index aa8a153f41..d2f90ff3d5 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/backend-plugin-api +## 0.6.10-next.1 + +### Patch Changes + +- 1f020fe: Support `token` in `readTree`, `readUrl` and `search` +- Updated dependencies + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + ## 0.6.10-next.0 ### Patch Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 2d099e7383..a2946bd803 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-plugin-api", "description": "Core API used by Backstage backend plugins", - "version": "0.6.10-next.0", + "version": "0.6.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index 654d092591..5b0f4a899c 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/backend-tasks +## 0.5.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.5.15-next.0 ### Patch Changes diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index 12ed0dc414..4089c3b520 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.5.15-next.0", + "version": "0.5.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 4b369454a0..ed2df1d75e 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/backend-test-utils +## 0.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-app-api@0.5.11-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 2e517d542f..85a8588bfe 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.3.0-next.0", + "version": "0.3.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 4b90e2631e..ff53bb5c58 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,65 @@ # example-backend +## 0.2.92-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-backend@1.21.0-next.1 + - @backstage/plugin-azure-devops-backend@0.5.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend@0.20.4-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-azure-sites-common@0.1.2-next.0 + - example-app@0.2.92-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-adr-backend@0.4.7-next.1 + - @backstage/plugin-app-backend@0.3.58-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-badges-backend@0.3.7-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.7-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-code-coverage-backend@0.2.24-next.1 + - @backstage/plugin-devtools-backend@0.2.7-next.1 + - @backstage/plugin-entity-feedback-backend@0.2.7-next.1 + - @backstage/plugin-events-backend@0.2.19-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-explore-backend@0.0.20-next.1 + - @backstage/plugin-jenkins-backend@0.3.4-next.1 + - @backstage/plugin-kafka-backend@0.3.8-next.1 + - @backstage/plugin-kubernetes-backend@0.14.2-next.1 + - @backstage/plugin-lighthouse-backend@0.4.2-next.1 + - @backstage/plugin-linguist-backend@0.5.7-next.1 + - @backstage/plugin-nomad-backend@0.1.12-next.1 + - @backstage/plugin-permission-backend@0.5.33-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-playlist-backend@0.3.14-next.1 + - @backstage/plugin-proxy-backend@0.4.8-next.1 + - @backstage/plugin-rollbar-backend@0.1.55-next.1 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.27-next.1 + - @backstage/plugin-search-backend@1.5.0-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.14-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.13-next.1 + - @backstage/plugin-search-backend-module-explore@0.1.14-next.1 + - @backstage/plugin-search-backend-module-pg@0.5.19-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.1.14-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + - @backstage/plugin-signals-backend@0.0.1-next.1 + - @backstage/plugin-signals-node@0.0.1-next.1 + - @backstage/plugin-tech-insights-backend@0.5.24-next.1 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.42-next.1 + - @backstage/plugin-tech-insights-node@0.4.16-next.1 + - @backstage/plugin-techdocs-backend@1.9.3-next.1 + - @backstage/plugin-todo-backend@0.3.8-next.1 + ## 0.2.92-next.0 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index a7c80adfdb..2386d799a9 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.92-next.0", + "version": "0.2.92-next.1", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 0d1e91dc99..c1dbd87a5e 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/catalog-client +## 1.6.0-next.1 + +### Minor Changes + +- 43dad25: Add API to get location by entity + +### Patch Changes + +- c04c42b: Internal updates to auto-generated files. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/errors@1.2.3 + ## 1.6.0-next.0 ### Minor Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index da9611e8d4..56103e83f9 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-client", "description": "An isomorphic client for the catalog backend", - "version": "1.6.0-next.0", + "version": "1.6.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 6f374cdbc5..e5c97f5220 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/catalog-model +## 1.4.4-next.0 + +### Patch Changes + +- 07e7d12: Fix wording in API reference +- Updated dependencies + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 1.4.3 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 746ef803ff..cf6a5f3cc3 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/catalog-model", "description": "Types and validators that help describe the model of a Backstage Catalog", - "version": "1.4.3", + "version": "1.4.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 1d79e73dfb..6aada1492d 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/cli +## 0.25.2-next.1 + +### Patch Changes + +- b58673e: Upgrade jest +- 08804c3: Fixed an issue that would cause an invalid `__backstage-autodetected-plugins__.js` to be written when using experimental module discovery. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/eslint-plugin@0.1.5-next.0 + - @backstage/release-manifests@0.0.11 + - @backstage/types@1.1.1 + ## 0.25.2-next.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index edf03167ec..e02616574a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.25.2-next.0", + "version": "0.25.2-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 9e32db5016..456feeaa0f 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-app-api +## 1.11.4-next.0 + +### Patch Changes + +- 7da67ce: Change `defaultScopes` for Bitbucket auth from invalid `team` to `account`. +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 1.11.3 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index f0654f7596..30fe485957 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "1.11.3", + "version": "1.11.4-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index a8abcbc8fc..63c30cfe3c 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/core-compat-api +## 0.2.0-next.1 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- edfd3a5: Updated dependency `@oriflame/backstage-plugin-score-card` to `^0.8.0`. +- bc621aa: Updates to use the new `RouteResolutionsApi`. +- 46b63de: Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/version-bridge@1.0.7 + ## 0.1.2-next.0 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 157e95d6b2..dff05e9774 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.1.2-next.0", + "version": "0.2.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index c6ffec5679..46c92e85bc 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/core-components +## 0.14.0-next.0 + +### Minor Changes + +- 281e8c6: **BREAKING**: Removed the `SidebarIntro` component as it was providing instructions for features that do not exist, along with `IntroCard`. If you were relying on this component and want to keep using it you can refer to the original implementations of [`SidebarIntro`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L149) and [`IntroCard`](https://github.com/backstage/backstage/blob/80f2413334ed9b221ec3c2b7c22fa737ad8d8885/packages/core-components/src/layout/Sidebar/Intro.tsx#L100). + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/version-bridge@1.0.7 + ## 0.13.10 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 9dc3e8cb8f..2ff68c3dae 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.13.10", + "version": "0.14.0-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 4cb681e4aa..9705dad2c3 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/core-plugin-api +## 1.8.3-next.0 + +### Patch Changes + +- e586f79: Throw a more specific exception `NotImplementedError` when an API implementation cannot be found. +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 1.8.2 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 68d5ad68c3..db679f7328 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "1.8.2", + "version": "1.8.3-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 14ff49ff5c..ce03e3fcf4 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/create-app +## 0.5.11-next.1 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.13 + ## 0.5.11-next.0 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 6d3fe75f1d..aa5fae0c5e 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.5.11-next.0", + "version": "0.5.11-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 8ebf1aece5..1f07f4da83 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/theme@0.5.0 + ## 1.0.27-next.0 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index c74550550c..d04aed3823 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "1.0.27-next.0", + "version": "1.0.27-next.1", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 2adedfd43d..e89643470e 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.11-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + ## 0.2.12-next.0 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index c1731de8ea..e00be1d7cc 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.2.12-next.0", + "version": "0.2.12-next.1", "private": true, "backstage": { "role": "cli" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 0ef44ba882..1ddc9110db 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/frontend-app-api +## 0.6.0-next.1 + +### Minor Changes + +- bdf4a8e: **BREAKING**: Removed the experimental `createExtensionTree` API. + +### Patch Changes + +- bc621aa: Updates to use the new `RouteResolutionsApi`. +- e586f79: Wrap the root element with the analytics context to ensure it always exists for all extensions. +- fb9b5e7: The default `ComponentsApi` implementation now uses the `ComponentRef` ID as the component key, rather than the reference instance. This fixes a bug where duplicate installations of `@backstage/frontend-plugin-api` would break the app. +- 46b63de: Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 0.6.0-next.0 ### Minor Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index a95702aed5..cb57faaef8 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-app-api", - "version": "0.6.0-next.0", + "version": "0.6.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 07f74e3ff4..01479c692d 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/frontend-plugin-api +## 0.6.0-next.1 + +### Minor Changes + +- e586f79: **BREAKING**: Replace default plugin extension and plugin ids to be `app` instead of `root`. + +### Patch Changes + +- bc621aa: Added `RouteResolutionsApi` as a replacement for the routing context. +- 1e61ad3: App component extensions are no longer wrapped in an `ExtensionBoundary`, allowing them to inherit the outer context instead. +- 46b63de: Allow external route refs in the new system to have a `defaultTarget` pointing to a route that it'll resolve to by default if no explicit bindings were made by the adopter. +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 0.5.1-next.0 ### Patch Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 1d97626cc8..739c3d3594 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-plugin-api", - "version": "0.5.1-next.0", + "version": "0.6.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 31799581f6..7fe034bf04 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/frontend-test-utils +## 0.1.2-next.1 + +### Patch Changes + +- bc621aa: Updates to use the new `RouteResolutionsApi`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/frontend-app-api@0.6.0-next.1 + - @backstage/test-utils@1.5.0-next.1 + - @backstage/types@1.1.1 + ## 0.1.2-next.0 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 8b9b4382e8..a64cd68846 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-test-utils", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index fd6a47e4e7..03881f6da2 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration-react +## 1.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + ## 1.1.23 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index f1592628ab..a8371bf4c0 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "1.1.23", + "version": "1.1.24-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 8dd9ec3915..8ddda692c4 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/integration +## 1.9.0-next.0 + +### Minor Changes + +- e27b7f3: Fix rate limit detection by looking for HTTP status code 429 and updating the header `x-ratelimit-remaining` to look for in case of a 403 code is returned + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 1.8.0 ### Minor Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 29a4273698..4191e0da34 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration", "description": "Helpers for managing integrations towards external systems", - "version": "1.8.0", + "version": "1.9.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 3d54277485..9b495d8db1 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/repo-tools +## 0.6.0-next.1 + +### Patch Changes + +- c04c42b: Fixes an issue where comments would be duplicated in the template. Also, removes a header with the title and version of the OpenAPI spec from generated code. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/cli-node@0.2.2 + - @backstage/errors@1.2.3 + ## 0.6.0-next.0 ### Minor Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 98af4149c3..95de5b6774 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/repo-tools", "description": "CLI for Backstage repo tooling ", - "version": "0.6.0-next.0", + "version": "0.6.0-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index c55d4a3e6e..996442157a 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,24 @@ # techdocs-cli-embedded-app +## 0.2.91-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/cli@0.25.2-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/test-utils@1.5.0-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/app-defaults@1.4.8-next.1 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + ## 0.2.91-next.0 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 1641562040..6d74e17c29 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,6 +1,6 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.91-next.0", + "version": "0.2.91-next.1", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 9639950663..5a3fd6703a 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,17 @@ # @techdocs/cli +## 1.8.2-next.1 + +### Patch Changes + +- d8d243c: fix: mkdocs parameter casing +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-techdocs-node@1.11.2-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + ## 1.8.2-next.0 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index f4cf7e9dc6..025cc85df2 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.8.2-next.0", + "version": "1.8.2-next.1", "publishConfig": { "access": "public" }, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 101720cb30..b750ef2e66 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/test-utils +## 1.5.0-next.1 + +### Patch Changes + +- 07e7d12: Fix wording in API reference +- 7da67ce: Change `defaultScopes` for Bitbucket auth from invalid `team` to `account`. +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + ## 1.5.0-next.0 ### Minor Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 3e1d751677..cf902eb994 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "1.5.0-next.0", + "version": "1.5.0-next.1", "publishConfig": { "access": "public" }, diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index e4986f0ca2..9afeca089b 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-adr-backend +## 0.4.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-adr-common@0.2.20-next.0 + - @backstage/plugin-search-common@1.2.10 + ## 0.4.7-next.0 ### Patch Changes diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index 9f30c95581..dea8d125ed 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.4.7-next.0", + "version": "0.4.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr-common/CHANGELOG.md b/plugins/adr-common/CHANGELOG.md index 84c285a1a8..6ce94e4d8f 100644 --- a/plugins/adr-common/CHANGELOG.md +++ b/plugins/adr-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-adr-common +## 0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-search-common@1.2.10 + ## 0.2.19 ### Patch Changes diff --git a/plugins/adr-common/package.json b/plugins/adr-common/package.json index d05b585c7d..2ca69b288a 100644 --- a/plugins/adr-common/package.json +++ b/plugins/adr-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-adr-common", "description": "Common functionalities for the adr plugin", - "version": "0.2.19", + "version": "0.2.20-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index 4586b14522..592477bc85 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-adr +## 0.6.13-next.1 + +### Patch Changes + +- 987f565: Fix alignment of text in `AdrSearchResultListItem`. Update size and font to match other `SearchResultListItem`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-adr-common@0.2.20-next.0 + - @backstage/plugin-search-common@1.2.10 + ## 0.6.13-next.0 ### Patch Changes diff --git a/plugins/adr/package.json b/plugins/adr/package.json index d2157dcae7..3861b31b14 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.6.13-next.0", + "version": "0.6.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake-backend/CHANGELOG.md b/plugins/airbrake-backend/CHANGELOG.md index 85f5e412ed..cc518f1be4 100644 --- a/plugins/airbrake-backend/CHANGELOG.md +++ b/plugins/airbrake-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-airbrake-backend +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + ## 0.3.7-next.0 ### Patch Changes diff --git a/plugins/airbrake-backend/package.json b/plugins/airbrake-backend/package.json index d3424d130d..4d40a51881 100644 --- a/plugins/airbrake-backend/package.json +++ b/plugins/airbrake-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake-backend", - "version": "0.3.7-next.0", + "version": "0.3.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake/CHANGELOG.md b/plugins/airbrake/CHANGELOG.md index bf89516a38..def87da93f 100644 --- a/plugins/airbrake/CHANGELOG.md +++ b/plugins/airbrake/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-airbrake +## 0.3.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/test-utils@1.5.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/dev-utils@1.0.27-next.1 + ## 0.3.30-next.0 ### Patch Changes diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 904dbcef76..0d06a18fed 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake", - "version": "0.3.30-next.0", + "version": "0.3.30-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index 4b99d120fb..b1d8970d7b 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-allure +## 0.1.46-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.1.46-next.0 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 9254b6c0c8..f12c985962 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.46-next.0", + "version": "0.1.46-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index 5135b15ac1..693f313a99 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-analytics-module-ga +## 0.2.0-next.0 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + ## 0.1.37 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index cabff881ad..5774bd99c2 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.37", + "version": "0.2.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga4/CHANGELOG.md b/plugins/analytics-module-ga4/CHANGELOG.md index 6529fc3711..8406fe5c1b 100644 --- a/plugins/analytics-module-ga4/CHANGELOG.md +++ b/plugins/analytics-module-ga4/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-analytics-module-ga4 +## 0.2.0-next.0 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + ## 0.1.8 ### Patch Changes diff --git a/plugins/analytics-module-ga4/package.json b/plugins/analytics-module-ga4/package.json index 89113efe6d..3ec2e762d1 100644 --- a/plugins/analytics-module-ga4/package.json +++ b/plugins/analytics-module-ga4/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga4", - "version": "0.1.8", + "version": "0.2.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-newrelic-browser/CHANGELOG.md b/plugins/analytics-module-newrelic-browser/CHANGELOG.md index 0f9b210d9a..9a5b34adc1 100644 --- a/plugins/analytics-module-newrelic-browser/CHANGELOG.md +++ b/plugins/analytics-module-newrelic-browser/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-analytics-module-newrelic-browser +## 0.1.0-next.0 + +### Minor Changes + +- e586f79: Add support to the new analytics api. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + ## 0.0.6 ### Patch Changes diff --git a/plugins/analytics-module-newrelic-browser/package.json b/plugins/analytics-module-newrelic-browser/package.json index 44610dd026..b3571e8bcf 100644 --- a/plugins/analytics-module-newrelic-browser/package.json +++ b/plugins/analytics-module-newrelic-browser/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-newrelic-browser", - "version": "0.0.6", + "version": "0.1.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md index 9c2a434030..3b534c978a 100644 --- a/plugins/apache-airflow/CHANGELOG.md +++ b/plugins/apache-airflow/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-apache-airflow +## 0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.2.19 ### Patch Changes diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index 489fcde5e8..26d48954e3 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.2.19", + "version": "0.2.20-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 4a293884f1..ce6cecd924 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-api-docs +## 0.10.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + ## 0.10.4-next.0 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 3147c080a8..32676f39c7 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.10.4-next.0", + "version": "0.10.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apollo-explorer/CHANGELOG.md b/plugins/apollo-explorer/CHANGELOG.md index 1523721718..c3aa23748b 100644 --- a/plugins/apollo-explorer/CHANGELOG.md +++ b/plugins/apollo-explorer/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-apollo-explorer +## 0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.1.19 ### Patch Changes diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index 37895e3343..83332c0c3e 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apollo-explorer", - "version": "0.1.19", + "version": "0.1.20-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 4a5361361d..b1ca09491d 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-app-backend +## 0.3.58-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.10-next.1 + ## 0.3.58-next.0 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 3722512874..4678001fb8 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.58-next.0", + "version": "0.3.58-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index cef24b16ff..a61ea14c0d 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-app-node +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 345b3fbdc0..8da14adf64 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-node", "description": "Node.js library for the app plugin", - "version": "0.1.10-next.0", + "version": "0.1.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index ee679a979b..7a1b1085f9 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-visualizer +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.1.1-next.0 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 49ebffd3f0..60ea6a1e4b 100644 --- a/plugins/app-visualizer/package.json +++ b/plugins/app-visualizer/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-visualizer", "description": "Visualizes the Backstage app structure", - "version": "0.1.1-next.0", + "version": "0.1.1-next.1", "publishConfig": { "access": "public" }, diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 580d58ae43..427ec61299 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index 311122fe54..5be3261783 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", "description": "The atlassian-provider backend module for the auth plugin.", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md new file mode 100644 index 0000000000..6d8e5e3968 --- /dev/null +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -0,0 +1,16 @@ +# @backstage/plugin-auth-backend-module-aws-alb-provider + +## 0.1.0-next.0 + +### Minor Changes + +- 23a98f8: Migrated the AWS ALB auth provider to new `@backstage/plugin-auth-backend-module-aws-alb-provider` module package. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend@0.20.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index 6d4a5835bf..f6238cca5d 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", "description": "The aws-alb provider module for the Backstage auth backend.", - "version": "0.0.0", + "version": "0.1.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index 87ccf9ad8e..dfde5de3fc 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.2.4-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index 95fea863f3..ecb597a396 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", "description": "A GCP IAP auth provider module for the Backstage auth backend", - "version": "0.2.4-next.0", + "version": "0.2.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index 11053b8487..82ea28d5c1 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 8113a9e03f..4bc9c00904 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", "description": "The github-provider backend module for the auth plugin.", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index 15688da6ee..d5888c773c 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index e25ccfd547..371f39b83b 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", "description": "The gitlab-provider backend module for the auth plugin.", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 78a6e68eb4..addaaca1d9 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index c985192df6..c3a558cc8b 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", "description": "A Google auth provider module for the Backstage auth backend", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index e61ab20c6b..39506e9b32 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index e20f2d8a4c..eda0b1af65 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", "description": "The microsoft-provider backend module for the auth plugin.", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index 0b70811413..9c9b71d17d 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index 83c14c7821..d1c1079c04 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", "description": "The oauth2-provider backend module for the auth plugin.", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index 809e58fd6c..927c39c1c7 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json index d0dabe9321..46473424b2 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/package.json +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", "description": "The oauth2-proxy-provider backend module for the auth plugin.", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 4581738ffb..7c110f9102 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend@0.20.4-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.0-next.0 ### Minor Changes diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 2b133f2f21..b47c80099f 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", "description": "The oidc-provider backend module for the auth plugin.", - "version": "0.1.0-next.0", + "version": "0.1.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index 313259dd3d..da62fc2c9d 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.0.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.0.3-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 2d2d3f857c..4c12f3638b 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", "description": "The okta-provider backend module for the auth plugin.", - "version": "0.0.3-next.0", + "version": "0.0.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 8b7c0a8214..9fcd872ff6 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index a414483000..d4f712734a 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", "description": "The pinniped-provider backend module for the auth plugin.", - "version": "0.1.4-next.0", + "version": "0.1.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md index 9613bb1499..9b508762b2 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index 3a1cbe4aa5..914f88b37c 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", "description": "The vmware-cloud-provider backend module for the auth plugin.", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 27b912aca1..2defa050dc 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-auth-backend +## 0.20.4-next.1 + +### Patch Changes + +- 23a98f8: Migrated the AWS ALB auth provider to new `@backstage/plugin-auth-backend-module-aws-alb-provider` module package. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-auth-backend-module-aws-alb-provider@0.1.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-backend-module-atlassian-provider@0.1.2-next.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.4-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-google-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-microsoft-provider@0.1.5-next.1 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.7-next.1 + - @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.1.2-next.1 + - @backstage/plugin-auth-backend-module-oidc-provider@0.1.0-next.1 + - @backstage/plugin-auth-backend-module-okta-provider@0.0.3-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.20.4-next.0 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 29c34b84e1..eb0d30a9ba 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.20.4-next.0", + "version": "0.20.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 97b69b0618..61fa20fab5 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-auth-node +## 0.4.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.4.4-next.0 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 0af59ecc53..ce71dc8f68 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.4.4-next.0", + "version": "0.4.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index 736fa6276b..7c9b73e2c2 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-azure-devops-backend +## 0.5.2-next.1 + +### Patch Changes + +- 25bda45: Fixed bug with `extractPartsFromAsset` that resulted in a leading `.` being removed from the path in an otherwise valid path (ex. `.assets/image.png`). The leading `.` will now only be moved for paths beginning with `./`. +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-devops-common@0.3.2 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.5.2-next.0 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 7b210e5759..43b7796fc9 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.5.2-next.0", + "version": "0.5.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index a2c1f6f118..c41a67c936 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-azure-devops +## 0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-azure-devops-common@0.3.2 + ## 0.3.12-next.0 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index d0b5932b31..dc9ef1f8de 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.3.12-next.0", + "version": "0.3.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites-backend/CHANGELOG.md b/plugins/azure-sites-backend/CHANGELOG.md index 1e9cd2162e..a8a6d35410 100644 --- a/plugins/azure-sites-backend/CHANGELOG.md +++ b/plugins/azure-sites-backend/CHANGELOG.md @@ -1,5 +1,77 @@ # @backstage/plugin-azure-sites-backend +## 0.2.0-next.1 + +### Minor Changes + +- 28610f4: **BREAKING**: `catalogApi` and `permissionsApi` are now a requirement to be passed through to the `createRouter` function. + + You can fix the typescript issues by passing through the required dependencies like the below `diff` shows: + + ```diff + import { + createRouter, + AzureSitesApi, + } from '@backstage/plugin-azure-sites-backend'; + import { Router } from 'express'; + import { PluginEnvironment } from '../types'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + + const catalogClient = new CatalogClient({ + + discoveryApi: env.discovery, + + }); + + return await createRouter({ + logger: env.logger, + azureSitesApi: AzureSitesApi.fromConfig(env.config), + + catalogApi: catalogClient, + + permissionsApi: env.permissions, + }); + } + ``` + +### Patch Changes + +- 5a409bb: Azure Sites `start` and `stop` action is now protected with the Permissions framework. + + The below example describes an action that forbids anyone but the owner of the catalog entity to trigger actions towards a site tied to an entity. + + ```typescript + // packages/backend/src/plugins/permission.ts + import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common'; + ... + class TestPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise { + if (isPermission(request.permission, azureSitesActionPermission)) { + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.identity.ownershipEntityRefs ?? [], + }), + ); + } + ... + return { + result: AuthorizeResult.ALLOW, + }; + } + ... + } + ``` + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-azure-sites-common@0.1.2-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/azure-sites-backend/package.json b/plugins/azure-sites-backend/package.json index 2599bfc360..85ede81120 100644 --- a/plugins/azure-sites-backend/package.json +++ b/plugins/azure-sites-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites-backend", - "version": "0.1.20-next.0", + "version": "0.2.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites-common/CHANGELOG.md b/plugins/azure-sites-common/CHANGELOG.md index 99dfef3e5b..a2d5876e62 100644 --- a/plugins/azure-sites-common/CHANGELOG.md +++ b/plugins/azure-sites-common/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-azure-sites-common +## 0.1.2-next.0 + +### Patch Changes + +- 5a409bb: Azure Sites `start` and `stop` action is now protected with the Permissions framework. + + The below example describes an action that forbids anyone but the owner of the catalog entity to trigger actions towards a site tied to an entity. + + ```typescript + // packages/backend/src/plugins/permission.ts + import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common'; + ... + class TestPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise { + if (isPermission(request.permission, azureSitesActionPermission)) { + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.identity.ownershipEntityRefs ?? [], + }), + ); + } + ... + return { + result: AuthorizeResult.ALLOW, + }; + } + ... + } + ``` + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + ## 0.1.1 ### Patch Changes diff --git a/plugins/azure-sites-common/package.json b/plugins/azure-sites-common/package.json index 0365daa844..909e0e42bc 100644 --- a/plugins/azure-sites-common/package.json +++ b/plugins/azure-sites-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-azure-sites-common", "description": "Common functionalities for the azure plugin", - "version": "0.1.1", + "version": "0.1.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites/CHANGELOG.md b/plugins/azure-sites/CHANGELOG.md index 66a6d4ecaa..9309ce0f53 100644 --- a/plugins/azure-sites/CHANGELOG.md +++ b/plugins/azure-sites/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/plugin-azure-sites +## 0.1.19-next.1 + +### Patch Changes + +- 5a409bb: Azure Sites `start` and `stop` action is now protected with the Permissions framework. + + The below example describes an action that forbids anyone but the owner of the catalog entity to trigger actions towards a site tied to an entity. + + ```typescript + // packages/backend/src/plugins/permission.ts + import { azureSitesActionPermission } from '@backstage/plugin-azure-sites-common'; + ... + class TestPermissionPolicy implements PermissionPolicy { + async handle(request: PolicyQuery, user?: BackstageIdentityResponse): Promise { + if (isPermission(request.permission, azureSitesActionPermission)) { + return createCatalogConditionalDecision( + request.permission, + catalogConditions.isEntityOwner({ + claims: user?.identity.ownershipEntityRefs ?? [], + }), + ); + } + ... + return { + result: AuthorizeResult.ALLOW, + }; + } + ... + } + ``` + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-azure-sites-common@0.1.2-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/azure-sites/package.json b/plugins/azure-sites/package.json index c9777e73da..4073f44d1a 100644 --- a/plugins/azure-sites/package.json +++ b/plugins/azure-sites/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites", - "version": "0.1.19-next.0", + "version": "0.1.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 61fd11ec9e..ab0009845a 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-badges-backend +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.3.7-next.0 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index cf9051198e..ff96dda96c 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.3.7-next.0", + "version": "0.3.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 4a85075f2f..483a122f31 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-badges +## 0.2.54-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.2.54-next.0 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 3d8e7d101d..b1edbd0f83 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.54-next.0", + "version": "0.2.54-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index 9cf47046c5..39ae91b7ae 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-bazaar-backend +## 0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.3.8-next.0 ### Patch Changes diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index 1dfa398e26..5da80c4abf 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.3.8-next.0", + "version": "0.3.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index 0f7122a74d..0ba74375b7 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-bazaar +## 0.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.2.22-next.0 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index dd7629a14f..d3b8401356 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.2.22-next.0", + "version": "0.2.22-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index f175f210ed..932417b389 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.2.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.9.0-next.0 + ## 0.2.16-next.0 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 3c94cb30c3..8c702a9253 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", "description": "Common functionalities for bitbucket-cloud plugins", - "version": "0.2.16-next.0", + "version": "0.2.16-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index 68defbcc7f..26550f8256 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-bitrise +## 0.1.57-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.1.57-next.0 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 7c1677919e..c4cf31c4bf 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.57-next.0", + "version": "0.1.57-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index afb57360f9..ca262679da 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + ## 0.3.4-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 613dea2f2f..6c4bfbcc24 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.3.4-next.0", + "version": "0.3.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index bb760f1136..e0a5015f45 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.1.29-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index c3c9ac8a81..25689ee227 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", "description": "A Backstage catalog backend module that helps integrate towards Azure", - "version": "0.1.29-next.0", + "version": "0.1.29-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index ee80978b65..d753d9c329 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.1.3-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index 4f2b1a5b11..d1f802ad9c 100644 --- a/plugins/catalog-backend-module-backstage-openapi/package.json +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", - "version": "0.1.3-next.0", + "version": "0.1.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index a9c1aa2945..54af617791 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.16-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.1.25-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 7dd551e15a..22b2dced18 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.1.25-next.0", + "version": "0.1.25-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index c6eeebe0e6..cf949be883 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.1.23-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index ab8febdc20..87cc04f44b 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.1.23-next.0", + "version": "0.1.23-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index 27fb897ae2..072a012f3c 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.16-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.2.25-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index 8db2d2dabe..a1da43ef67 100644 --- a/plugins/catalog-backend-module-bitbucket/package.json +++ b/plugins/catalog-backend-module-bitbucket/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket", - "version": "0.2.25-next.0", + "version": "0.2.25-next.1", "deprecated": true, "main": "src/index.ts", "types": "src/index.ts", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index bdf72f7171..803edca6c0 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index ad5bdfa3f5..c7ba34b81d 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", "description": "A Backstage catalog backend module that helps integrate towards GCP", - "version": "0.1.10-next.0", + "version": "0.1.10-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 2b965a3267..583bb808c7 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.1.26-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 91097235d4..15d50fe373 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.1.26-next.0", + "version": "0.1.26-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index a8448fe5af..af239ea8cd 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.5.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index 585f032f05..fff0740a8c 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", "description": "The github-org backend module for the catalog plugin.", - "version": "0.1.4-next.0", + "version": "0.1.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index b00dfda365..d9ed9a3cf7 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog-backend-module-github +## 0.5.0-next.1 + +### Minor Changes + +- a950ed0: Prevent Entity Providers from eliminating Users and Groups from the DB when the synchronisation fails + +### Patch Changes + +- 9477133: Decreased number of teams fetched by GraphQL Query responsible for fetching Teams and Members in organization, due to timeouts when running against big organizations +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.4.8-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 10c95c6c44..b330fdedab 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.4.8-next.0", + "version": "0.5.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index b78079c4e8..f6b436e45b 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.3.7-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index a364ddb584..691e5b2397 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.3.7-next.0", + "version": "0.3.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index 042dfc6bb1..5de87bd35b 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.4.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-permission-common@0.7.12 + ## 0.4.14-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 96ccb49130..e9e8ee9a07 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", "description": "An entity provider for streaming large asset sources into the catalog", - "version": "0.4.14-next.0", + "version": "0.4.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index b6f74f007c..7029f009fe 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.5.25-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index c2ad4a8aa3..191dc3a71a 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend module that helps integrate towards LDAP", - "version": "0.5.25-next.0", + "version": "0.5.25-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 99e9e39a76..6c6bea04ce 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.5.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.5.17-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 56cf638689..c248d653f2 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", - "version": "0.5.17-next.0", + "version": "0.5.17-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 498291b002..021843ec33 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-catalog-backend@1.17.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.1.27-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 92e7bdf618..7d6ff2cbe1 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", - "version": "0.1.27-next.0", + "version": "0.1.27-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index b2dd64e934..b8a6f0bb07 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.1.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 4aaf20ba8b..16b267c1e4 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", - "version": "0.1.15-next.0", + "version": "0.1.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index de3ff6553a..e79aad72b7 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index 66d3c3d26f..3d108cb573 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 3c34f951b5..1eeb5a275f 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.3.7-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 22786152c9..11bb465476 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", "description": "Backstage Catalog module to view unprocessed entities", - "version": "0.3.7-next.0", + "version": "0.3.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index fcbad7031b..487782260d 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-catalog-backend +## 1.17.0-next.1 + +### Minor Changes + +- 43dad25: Add API to get location by entity + +### Patch Changes + +- 89b674c: Minor performance improvement for `queryEntities` when the limit is 0. +- efa8160: Rollback the change for wildcard discovery, this fixes a bug with the `AzureUrlReader` not working with wildcard paths +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-search-backend-module-catalog@0.1.14-next.1 + ## 1.17.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index d9ba73fe9e..f062f51f0c 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "1.17.0-next.0", + "version": "1.17.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index 9f2b511676..4a56bd8a66 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-common +## 1.0.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-common@1.2.10 + ## 1.0.20 ### Patch Changes diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 9642746fd7..de79fd4a73 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-common", "description": "Common functionalities for the catalog plugin", - "version": "1.0.20", + "version": "1.0.21-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index a377f5ca68..10a77816fd 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-graph +## 0.3.4-next.1 + +### Patch Changes + +- f937aae: use `CatalogClient.getEntitiesByRefs()` to reduce the number of backend requests from plugin `catalog-graph` +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/types@1.1.1 + ## 0.3.4-next.0 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 709e764531..a136efeb64 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.3.4-next.0", + "version": "0.3.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index b77f3faf2b..8fc2ae334e 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.10.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + ## 0.10.6-next.0 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 066a073c48..81d162c0dd 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.10.6-next.0", + "version": "0.10.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 7327411bf2..94101eca61 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-node +## 1.6.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 1.6.2-next.0 ### Patch Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index 944474bb9b..6df027bbff 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-node", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", - "version": "1.6.2-next.0", + "version": "1.6.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 4bd71d05fe..0c2e6d2e3b 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-react +## 1.9.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + ## 1.9.4-next.0 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index d4114efc59..b3a18f9021 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "1.9.4-next.0", + "version": "1.9.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index 9a2be34228..633e77565b 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.1.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + ## 0.1.7 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index d3b037788a..88f323d7f2 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities", - "version": "0.1.7", + "version": "0.1.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 4283ae0a74..7c746f50a0 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog +## 1.17.0-next.1 + +### Patch Changes + +- 987f565: Add line clamping to `CatalogSearchResultListItem` +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 1.17.0-next.0 ### Minor Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index c30cddc08f..ad0b287824 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog", "description": "The Backstage plugin for browsing the Backstage catalog", - "version": "1.17.0-next.0", + "version": "1.17.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md index 1d134138ba..a55094f17d 100644 --- a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md +++ b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-cicd-statistics-module-gitlab +## 0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-cicd-statistics@0.1.32-next.1 + ## 0.1.26-next.0 ### Patch Changes diff --git a/plugins/cicd-statistics-module-gitlab/package.json b/plugins/cicd-statistics-module-gitlab/package.json index 9ab05f6aac..b5b3df2e90 100644 --- a/plugins/cicd-statistics-module-gitlab/package.json +++ b/plugins/cicd-statistics-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics-module-gitlab", "description": "CI/CD Statistics plugin module; Gitlab CICD", - "version": "0.1.26-next.0", + "version": "0.1.26-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cicd-statistics/CHANGELOG.md b/plugins/cicd-statistics/CHANGELOG.md index d1099764d9..2244ecd8b7 100644 --- a/plugins/cicd-statistics/CHANGELOG.md +++ b/plugins/cicd-statistics/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-cicd-statistics +## 0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.1.32-next.0 ### Patch Changes diff --git a/plugins/cicd-statistics/package.json b/plugins/cicd-statistics/package.json index a989af1820..5145d3e861 100644 --- a/plugins/cicd-statistics/package.json +++ b/plugins/cicd-statistics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics", "description": "A frontend plugin visualizing CI/CD pipeline statistics (build time)", - "version": "0.1.32-next.0", + "version": "0.1.32-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index 413ae8b12f..fd6c349f91 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-circleci +## 0.3.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.3.30-next.0 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 9eb43648af..415414ef69 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-circleci", "description": "A Backstage plugin that integrates towards Circle CI", - "version": "0.3.30-next.0", + "version": "0.3.30-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index ff87a63e08..c068750a40 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-cloudbuild +## 0.4.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.4.0-next.0 ### Minor Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 00f0a7ea11..cbdf15041c 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cloudbuild", "description": "A Backstage plugin that integrates towards Google Cloud Build", - "version": "0.4.0-next.0", + "version": "0.4.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-climate/CHANGELOG.md b/plugins/code-climate/CHANGELOG.md index 9e653f5879..af22228aac 100644 --- a/plugins/code-climate/CHANGELOG.md +++ b/plugins/code-climate/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-code-climate +## 0.1.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.1.30-next.0 ### Patch Changes diff --git a/plugins/code-climate/package.json b/plugins/code-climate/package.json index b142f7bb40..15e2f6955c 100644 --- a/plugins/code-climate/package.json +++ b/plugins/code-climate/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-code-climate", - "version": "0.1.30-next.0", + "version": "0.1.30-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-coverage-backend/CHANGELOG.md b/plugins/code-coverage-backend/CHANGELOG.md index ed75e028d1..41ccb0983f 100644 --- a/plugins/code-coverage-backend/CHANGELOG.md +++ b/plugins/code-coverage-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-code-coverage-backend +## 0.2.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.2.24-next.0 ### Patch Changes diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index d929fd4006..c734ff5baf 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage-backend", "description": "A Backstage backend plugin that helps you keep track of your code coverage", - "version": "0.2.24-next.0", + "version": "0.2.24-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index 534fbc0d52..966f1ababd 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-code-coverage +## 0.2.23-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.2.23-next.0 ### Patch Changes diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 52dd2a18ec..7adb998df7 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage", "description": "A Backstage plugin that helps you keep track of your code coverage", - "version": "0.2.23-next.0", + "version": "0.2.23-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/codescene/CHANGELOG.md b/plugins/codescene/CHANGELOG.md index df9ed3199f..798bf1c1ce 100644 --- a/plugins/codescene/CHANGELOG.md +++ b/plugins/codescene/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-codescene +## 0.1.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.21 ### Patch Changes diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index f0dfd20394..d4bd52a35d 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-codescene", - "version": "0.1.21", + "version": "0.1.22-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 0ed7d3afd5..73c81c8fc8 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-config-schema +## 0.1.50-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.1.49 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 15860d4e89..5c11b6e094 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-config-schema", "description": "A Backstage plugin that lets you browse the configuration schema of your app", - "version": "0.1.49", + "version": "0.1.50-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index 7f0a7dfd21..dccffc5cd3 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-cost-insights +## 0.12.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + - @backstage/plugin-cost-insights-common@0.1.2 + ## 0.12.19-next.0 ### Patch Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 798853d219..93b4736685 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cost-insights", "description": "A Backstage plugin that helps you keep track of your cloud spend", - "version": "0.12.19-next.0", + "version": "0.12.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index ca367deadd..b7810ca045 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-devtools-backend +## 0.2.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/config-loader@1.6.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-devtools-common@0.1.8 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 0.2.7-next.0 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index e35d8c4112..97823cf828 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.2.7-next.0", + "version": "0.2.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index be753cb664..fdf1491613 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-devtools +## 0.1.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + - @backstage/plugin-devtools-common@0.1.8 + - @backstage/plugin-permission-react@0.4.20-next.0 + ## 0.1.9-next.0 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 7639df94b8..1a480454d1 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.9-next.0", + "version": "0.1.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/dynatrace/CHANGELOG.md b/plugins/dynatrace/CHANGELOG.md index 6d363c3bc3..2a0b88c042 100644 --- a/plugins/dynatrace/CHANGELOG.md +++ b/plugins/dynatrace/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-dynatrace +## 8.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 8.0.4-next.0 ### Patch Changes diff --git a/plugins/dynatrace/package.json b/plugins/dynatrace/package.json index 23ff57d6c0..47d7857fcf 100644 --- a/plugins/dynatrace/package.json +++ b/plugins/dynatrace/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-dynatrace", - "version": "8.0.4-next.0", + "version": "8.0.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-feedback-backend/CHANGELOG.md b/plugins/entity-feedback-backend/CHANGELOG.md index 9248c16404..c0385a703c 100644 --- a/plugins/entity-feedback-backend/CHANGELOG.md +++ b/plugins/entity-feedback-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-entity-feedback-backend +## 0.2.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-entity-feedback-common@0.1.3 + ## 0.2.7-next.0 ### Patch Changes diff --git a/plugins/entity-feedback-backend/package.json b/plugins/entity-feedback-backend/package.json index 013aff7146..fe5499ab26 100644 --- a/plugins/entity-feedback-backend/package.json +++ b/plugins/entity-feedback-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-feedback-backend", - "version": "0.2.7-next.0", + "version": "0.2.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-feedback/CHANGELOG.md b/plugins/entity-feedback/CHANGELOG.md index 69f503b584..0d8ead8022 100644 --- a/plugins/entity-feedback/CHANGELOG.md +++ b/plugins/entity-feedback/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-entity-feedback +## 0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-entity-feedback-common@0.1.3 + ## 0.2.13-next.0 ### Patch Changes diff --git a/plugins/entity-feedback/package.json b/plugins/entity-feedback/package.json index d16702c230..30453d84a8 100644 --- a/plugins/entity-feedback/package.json +++ b/plugins/entity-feedback/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-feedback", - "version": "0.2.13-next.0", + "version": "0.2.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-validation/CHANGELOG.md b/plugins/entity-validation/CHANGELOG.md index 3c7c1579cf..452521c446 100644 --- a/plugins/entity-validation/CHANGELOG.md +++ b/plugins/entity-validation/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-entity-validation +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + ## 0.1.15-next.0 ### Patch Changes diff --git a/plugins/entity-validation/package.json b/plugins/entity-validation/package.json index cdf3dc88da..33b62b4412 100644 --- a/plugins/entity-validation/package.json +++ b/plugins/entity-validation/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-validation", - "version": "0.1.15-next.0", + "version": "0.1.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index 32f5f8f1d7..c2d54c9cf9 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.2.13-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 2d27225196..fbf35e693c 100644 --- a/plugins/events-backend-module-aws-sqs/package.json +++ b/plugins/events-backend-module-aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-aws-sqs", - "version": "0.2.13-next.0", + "version": "0.2.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index ff61ec0c12..3ad7d00f96 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-azure +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 94b71afc70..494b3da8d5 100644 --- a/plugins/events-backend-module-azure/package.json +++ b/plugins/events-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-azure", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index 5fb99e39d2..e54472e962 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 2ae9b32ac9..4d8cfa22aa 100644 --- a/plugins/events-backend-module-bitbucket-cloud/package.json +++ b/plugins/events-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-cloud", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index b641a9a8f9..c58d7bfac2 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 941570b43b..b101fd58ac 100644 --- a/plugins/events-backend-module-gerrit/package.json +++ b/plugins/events-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gerrit", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index 7fd8bd9bc9..93146d860b 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-github +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index 4c56579e6f..2c9dc3bee6 100644 --- a/plugins/events-backend-module-github/package.json +++ b/plugins/events-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-github", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index 1b6f9f2cef..1f434b21f4 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index ca5fd57c59..a1f61b3ad6 100644 --- a/plugins/events-backend-module-gitlab/package.json +++ b/plugins/events-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gitlab", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index 26fe7dc08b..8c9d9201a1 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index 9a570e7863..71681b860b 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-events-backend-test-utils", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index db5c82102d..0f5b012294 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend +## 0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.2.19-next.0 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index 6980590c2f..1a0d62f7fe 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.2.19-next.0", + "version": "0.2.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index febba33d77..ae2127dfaf 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-node +## 0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + ## 0.2.19-next.0 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 0c4b63f5e2..22f9388d0f 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-events-node", "description": "The plugin-events-node module for @backstage/plugin-events-backend", - "version": "0.2.19-next.0", + "version": "0.2.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index 09e9b21b55..bc84f88be7 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @internal/plugin-todo-list-backend +## 1.0.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 1.0.22-next.0 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index d3b38bbaf2..a3e086079a 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-backend", - "version": "1.0.22-next.0", + "version": "1.0.22-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 8dfacdd105..e59692d964 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/plugin-todo-list +## 1.0.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 1.0.21 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index 63634879df..82561eda37 100644 --- a/plugins/example-todo-list/package.json +++ b/plugins/example-todo-list/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list", - "version": "1.0.21", + "version": "1.0.22-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore-backend/CHANGELOG.md b/plugins/explore-backend/CHANGELOG.md index 7d78e292e3..f8527001f9 100644 --- a/plugins/explore-backend/CHANGELOG.md +++ b/plugins/explore-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-explore-backend +## 0.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-module-explore@0.1.14-next.1 + ## 0.0.20-next.0 ### Patch Changes diff --git a/plugins/explore-backend/package.json b/plugins/explore-backend/package.json index 6589a40e29..01f46b1f15 100644 --- a/plugins/explore-backend/package.json +++ b/plugins/explore-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore-backend", - "version": "0.0.20-next.0", + "version": "0.0.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore-react/CHANGELOG.md b/plugins/explore-react/CHANGELOG.md index d9668070c2..ea65de3bae 100644 --- a/plugins/explore-react/CHANGELOG.md +++ b/plugins/explore-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-explore-react +## 0.0.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-explore-common@0.0.2 + ## 0.0.35 ### Patch Changes diff --git a/plugins/explore-react/package.json b/plugins/explore-react/package.json index a3128511ac..ffd368ca96 100644 --- a/plugins/explore-react/package.json +++ b/plugins/explore-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore-react", "description": "A frontend library for Backstage plugins that want to interact with the explore plugin", - "version": "0.0.35", + "version": "0.0.36-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index 643d1674d0..b42173f965 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-explore +## 0.4.16-next.1 + +### Patch Changes + +- 07e7d12: Fix wording in API reference +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-explore-react@0.0.36-next.0 + - @backstage/plugin-search-common@1.2.10 + ## 0.4.16-next.0 ### Patch Changes diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 9293c39b10..3b51925ae1 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore", "description": "A Backstage plugin for building an exploration page of your software ecosystem", - "version": "0.4.16-next.0", + "version": "0.4.16-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/firehydrant/CHANGELOG.md b/plugins/firehydrant/CHANGELOG.md index 9d3abff382..16bdbe4200 100644 --- a/plugins/firehydrant/CHANGELOG.md +++ b/plugins/firehydrant/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-firehydrant +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index 0171f83c7d..b0f2005cc2 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-firehydrant", "description": "A Backstage plugin that integrates towards FireHydrant", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index eb6388bee3..40cd2087d1 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-fossa +## 0.2.62-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.2.62-next.0 ### Patch Changes diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 7eee0592f5..ce92837285 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-fossa", "description": "A Backstage plugin that integrates towards FOSSA", - "version": "0.2.62-next.0", + "version": "0.2.62-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gcalendar/CHANGELOG.md b/plugins/gcalendar/CHANGELOG.md index cb9865d96c..f58ac8a65f 100644 --- a/plugins/gcalendar/CHANGELOG.md +++ b/plugins/gcalendar/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-gcalendar +## 0.3.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + ## 0.3.22 ### Patch Changes diff --git a/plugins/gcalendar/package.json b/plugins/gcalendar/package.json index c8a5b0bd0a..62c507aeef 100644 --- a/plugins/gcalendar/package.json +++ b/plugins/gcalendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcalendar", - "version": "0.3.22", + "version": "0.3.23-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index bcdc653349..05632ab18e 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-gcp-projects +## 0.3.46-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.3.45 ### Patch Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 571267216c..9925b5d853 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gcp-projects", "description": "A Backstage plugin that helps you manage projects in GCP", - "version": "0.3.45", + "version": "0.3.46-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/git-release-manager/CHANGELOG.md b/plugins/git-release-manager/CHANGELOG.md index 2ce671930b..335119c73a 100644 --- a/plugins/git-release-manager/CHANGELOG.md +++ b/plugins/git-release-manager/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-git-release-manager +## 0.3.42-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + ## 0.3.41 ### Patch Changes diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 7fc2a142ab..e9fd5d75d6 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-git-release-manager", "description": "A Backstage plugin that helps you manage releases in git", - "version": "0.3.41", + "version": "0.3.42-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index 8212bb5350..2125324bfc 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-github-actions +## 0.6.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + ## 0.6.11-next.0 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 5d0e243c9a..aba63452d2 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.6.11-next.0", + "version": "0.6.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md index 0378efa14f..37793d24f1 100644 --- a/plugins/github-deployments/CHANGELOG.md +++ b/plugins/github-deployments/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-github-deployments +## 0.1.61-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + ## 0.1.61-next.0 ### Patch Changes diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 713b8d16f6..62ba0c1b76 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-deployments", "description": "A Backstage plugin that integrates towards GitHub Deployments", - "version": "0.1.61-next.0", + "version": "0.1.61-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-issues/CHANGELOG.md b/plugins/github-issues/CHANGELOG.md index 5920fad0d3..27ff142acf 100644 --- a/plugins/github-issues/CHANGELOG.md +++ b/plugins/github-issues/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-github-issues +## 0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.2.19-next.0 ### Patch Changes diff --git a/plugins/github-issues/package.json b/plugins/github-issues/package.json index d2d47af426..22ca27a0d2 100644 --- a/plugins/github-issues/package.json +++ b/plugins/github-issues/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-issues", - "version": "0.2.19-next.0", + "version": "0.2.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-pull-requests-board/CHANGELOG.md b/plugins/github-pull-requests-board/CHANGELOG.md index 886bdc7652..6512e26ef7 100644 --- a/plugins/github-pull-requests-board/CHANGELOG.md +++ b/plugins/github-pull-requests-board/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-github-pull-requests-board +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.1.24-next.0 ### Patch Changes diff --git a/plugins/github-pull-requests-board/package.json b/plugins/github-pull-requests-board/package.json index 87737e4294..29d2a374b9 100644 --- a/plugins/github-pull-requests-board/package.json +++ b/plugins/github-pull-requests-board/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-pull-requests-board", "description": "A Backstage plugin that allows you to see all open Pull Requests for all the repositories owned by your team", - "version": "0.1.24-next.0", + "version": "0.1.24-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index cd312c04bf..77b13f0d28 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-gitops-profiles +## 0.3.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.3.44 ### Patch Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index d9e509a49b..5d7ef94879 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gitops-profiles", "description": "A Backstage plugin that helps you manage GitOps profiles", - "version": "0.3.44", + "version": "0.3.45-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gocd/CHANGELOG.md b/plugins/gocd/CHANGELOG.md index eb4276a060..d2bad7c04d 100644 --- a/plugins/gocd/CHANGELOG.md +++ b/plugins/gocd/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-gocd +## 0.1.36-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.1.36-next.0 ### Patch Changes diff --git a/plugins/gocd/package.json b/plugins/gocd/package.json index 86b5f1eb54..3e3f7154ea 100644 --- a/plugins/gocd/package.json +++ b/plugins/gocd/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gocd", "description": "A Backstage plugin that integrates towards GoCD", - "version": "0.1.36-next.0", + "version": "0.1.36-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index 62965f5a24..91a4be36c4 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-graphiql +## 0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.3.3-next.0 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index ed4b6ee72b..bc1a2178bc 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.3.3-next.0", + "version": "0.3.3-next.1", "publishConfig": { "access": "public" }, diff --git a/plugins/graphql-voyager/CHANGELOG.md b/plugins/graphql-voyager/CHANGELOG.md index f65e23b8bb..2d8af5adcb 100644 --- a/plugins/graphql-voyager/CHANGELOG.md +++ b/plugins/graphql-voyager/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-graphql-voyager +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.1.11 ### Patch Changes diff --git a/plugins/graphql-voyager/package.json b/plugins/graphql-voyager/package.json index 2a2941b033..b2d6f30579 100644 --- a/plugins/graphql-voyager/package.json +++ b/plugins/graphql-voyager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphql-voyager", "description": "Backstage plugin for GraphQL Voyager", - "version": "0.1.11", + "version": "0.1.12-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index 4470b9218e..aba096163f 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-home-react +## 0.1.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.1.8-next.0 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index 9d2e6b9dad..dcaed77089 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home-react", "description": "A Backstage plugin that contains react components helps you build a home page", - "version": "0.1.8-next.0", + "version": "0.1.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 32a9c81161..665d41c119 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-home +## 0.6.2-next.1 + +### Patch Changes + +- 384c132: Added filter support for HomePageVisitedByType in order to enable filtering entities from the list +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-home-react@0.1.8-next.1 + - @backstage/config@1.1.1 + - @backstage/theme@0.5.0 + ## 0.6.2-next.0 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index e8fa2b666c..de4067f707 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home", "description": "A Backstage plugin that helps you build a home page", - "version": "0.6.2-next.0", + "version": "0.6.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index f7b769c3e5..055b169f7d 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-ilert +## 0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.2.19-next.0 ### Patch Changes diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index de1dd4797a..e095114989 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-ilert", "description": "A Backstage plugin that integrates towards iLert", - "version": "0.2.19-next.0", + "version": "0.2.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md index 01502bc0a2..650b5919ed 100644 --- a/plugins/jenkins-backend/CHANGELOG.md +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-jenkins-backend +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-jenkins-common@0.1.24-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 0.3.4-next.0 ### Patch Changes diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index c7a97f56ef..0012912d95 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins-backend", "description": "A Backstage backend plugin that integrates towards Jenkins", - "version": "0.3.4-next.0", + "version": "0.3.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/jenkins-common/CHANGELOG.md b/plugins/jenkins-common/CHANGELOG.md index 6d364dc40b..0b2ecf6b46 100644 --- a/plugins/jenkins-common/CHANGELOG.md +++ b/plugins/jenkins-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-jenkins-common +## 0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + ## 0.1.23 ### Patch Changes diff --git a/plugins/jenkins-common/package.json b/plugins/jenkins-common/package.json index d61bd6cede..77a2073f72 100644 --- a/plugins/jenkins-common/package.json +++ b/plugins/jenkins-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-jenkins-common", - "version": "0.1.23", + "version": "0.1.24-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index d781ac91f4..9431caa947 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-jenkins +## 0.9.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-jenkins-common@0.1.24-next.0 + ## 0.9.5-next.0 ### Patch Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 5a94f1f3ec..8bec07c2f1 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins", "description": "A Backstage plugin that integrates towards Jenkins", - "version": "0.9.5-next.0", + "version": "0.9.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index 187033c6de..649bda3e86 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kafka-backend +## 0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.3.8-next.0 ### Patch Changes diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index 283f24f001..9d19680c91 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka-backend", "description": "A Backstage backend plugin that integrates towards Kafka", - "version": "0.3.8-next.0", + "version": "0.3.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index 29c761cddc..02d34327fb 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kafka +## 0.3.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.3.30-next.0 ### Patch Changes diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 16b2d5124b..fa1cbd76ef 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka", "description": "A Backstage plugin that integrates towards Kafka", - "version": "0.3.30-next.0", + "version": "0.3.30-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index f3ab4fcab6..c0cb236c5b 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-kubernetes-backend +## 0.14.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-kubernetes-node@0.1.4-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 0.14.2-next.0 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 307bb1c42c..7a2ec98bec 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-backend", "description": "A Backstage backend plugin that integrates towards Kubernetes", - "version": "0.14.2-next.0", + "version": "0.14.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index d68450a87c..be9e7e7a11 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-kubernetes-react@0.3.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + ## 0.0.6-next.0 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 0d7b7dce47..52a9816456 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-cluster", "description": "A Backstage plugin that shows details of Kubernetes clusters", - "version": "0.0.6-next.0", + "version": "0.0.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index ab8fd5cb9a..7150d5a457 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kubernetes-common +## 0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + ## 0.7.4-next.0 ### Patch Changes diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 647618a62d..71b488a330 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-common", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", - "version": "0.7.4-next.0", + "version": "0.7.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 8f320e583c..69eb5c10f1 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes-node +## 0.1.4-next.1 + +### Patch Changes + +- cceed8a: Introduced `PinnipedHelper` class to enable authentication to Kubernetes clusters through Pinniped +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index f09d1165e2..d1141e970e 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-node", "description": "Node.js library for the kubernetes plugin", - "version": "0.1.4-next.0", + "version": "0.1.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index 643fea0159..7f528287a5 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kubernetes-react +## 0.3.0-next.1 + +### Patch Changes + +- 3c184af: Extracted common dialog component. +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 0d722402ba..0dd40d4c0e 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-react", "description": "Web library for the kubernetes-react plugin", - "version": "0.3.0-next.0", + "version": "0.3.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 7cee4eda45..cdf9fbcb0b 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes +## 0.11.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/plugin-kubernetes-react@0.3.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-kubernetes-common@0.7.4-next.1 + ## 0.11.5-next.0 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 39a7a938a2..1bde0aaf09 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.11.5-next.0", + "version": "0.11.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/lighthouse-backend/CHANGELOG.md b/plugins/lighthouse-backend/CHANGELOG.md index ddfbe1a7c5..50bf314090 100644 --- a/plugins/lighthouse-backend/CHANGELOG.md +++ b/plugins/lighthouse-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-lighthouse-backend +## 0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-lighthouse-common@0.1.4 + ## 0.4.2-next.0 ### Patch Changes diff --git a/plugins/lighthouse-backend/package.json b/plugins/lighthouse-backend/package.json index 9006c24488..90470d60d5 100644 --- a/plugins/lighthouse-backend/package.json +++ b/plugins/lighthouse-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse-backend", "description": "Backend functionalities for lighthouse", - "version": "0.4.2-next.0", + "version": "0.4.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index e93b449c5e..ffb6f6c7bc 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-lighthouse +## 0.4.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-lighthouse-common@0.1.4 + ## 0.4.15-next.0 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 104396d6d4..cc1057dd26 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse", "description": "A Backstage plugin that integrates towards Lighthouse", - "version": "0.4.15-next.0", + "version": "0.4.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/linguist-backend/CHANGELOG.md b/plugins/linguist-backend/CHANGELOG.md index dd2ca24981..86d4a7adec 100644 --- a/plugins/linguist-backend/CHANGELOG.md +++ b/plugins/linguist-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-linguist-backend +## 0.5.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-linguist-common@0.1.2 + ## 0.5.7-next.0 ### Patch Changes diff --git a/plugins/linguist-backend/package.json b/plugins/linguist-backend/package.json index 7acae2049c..5e3f0ddff4 100644 --- a/plugins/linguist-backend/package.json +++ b/plugins/linguist-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-linguist-backend", - "version": "0.5.7-next.0", + "version": "0.5.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/linguist/CHANGELOG.md b/plugins/linguist/CHANGELOG.md index 866d1f94f6..6fd2da3adf 100644 --- a/plugins/linguist/CHANGELOG.md +++ b/plugins/linguist/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-linguist +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-linguist-common@0.1.2 + ## 0.1.15-next.0 ### Patch Changes diff --git a/plugins/linguist/package.json b/plugins/linguist/package.json index 6f20f2b007..b10fe86bf8 100644 --- a/plugins/linguist/package.json +++ b/plugins/linguist/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-linguist", - "version": "0.1.15-next.0", + "version": "0.1.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/microsoft-calendar/CHANGELOG.md b/plugins/microsoft-calendar/CHANGELOG.md index 86ea39fcff..abc8583592 100644 --- a/plugins/microsoft-calendar/CHANGELOG.md +++ b/plugins/microsoft-calendar/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-microsoft-calendar +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + ## 0.1.11 ### Patch Changes diff --git a/plugins/microsoft-calendar/package.json b/plugins/microsoft-calendar/package.json index aec094c029..a0dad40670 100644 --- a/plugins/microsoft-calendar/package.json +++ b/plugins/microsoft-calendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-microsoft-calendar", - "version": "0.1.11", + "version": "0.1.12-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/newrelic-dashboard/CHANGELOG.md b/plugins/newrelic-dashboard/CHANGELOG.md index ac5d043efe..5eb5c9ac4f 100644 --- a/plugins/newrelic-dashboard/CHANGELOG.md +++ b/plugins/newrelic-dashboard/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-newrelic-dashboard +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.3.5-next.0 ### Patch Changes diff --git a/plugins/newrelic-dashboard/package.json b/plugins/newrelic-dashboard/package.json index e5a63a6971..fc393e1d57 100644 --- a/plugins/newrelic-dashboard/package.json +++ b/plugins/newrelic-dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic-dashboard", - "version": "0.3.5-next.0", + "version": "0.3.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index 8935f6c619..df27bd6150 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-newrelic +## 0.3.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.3.44 ### Patch Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index a67a2203d5..991a820109 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-newrelic", "description": "A Backstage plugin that integrates towards New Relic", - "version": "0.3.44", + "version": "0.3.45-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/nomad-backend/CHANGELOG.md b/plugins/nomad-backend/CHANGELOG.md index 2c93b3a3d0..64a12844ca 100644 --- a/plugins/nomad-backend/CHANGELOG.md +++ b/plugins/nomad-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-nomad-backend +## 0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.12-next.0 ### Patch Changes diff --git a/plugins/nomad-backend/package.json b/plugins/nomad-backend/package.json index c44721a581..00b505f139 100644 --- a/plugins/nomad-backend/package.json +++ b/plugins/nomad-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-nomad-backend", - "version": "0.1.12-next.0", + "version": "0.1.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/nomad/CHANGELOG.md b/plugins/nomad/CHANGELOG.md index e9d5afb1a1..624193fcd2 100644 --- a/plugins/nomad/CHANGELOG.md +++ b/plugins/nomad/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-nomad +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.1.11-next.0 ### Patch Changes diff --git a/plugins/nomad/package.json b/plugins/nomad/package.json index 17c345c761..ba30b70263 100644 --- a/plugins/nomad/package.json +++ b/plugins/nomad/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-nomad", - "version": "0.1.11-next.0", + "version": "0.1.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/octopus-deploy/CHANGELOG.md b/plugins/octopus-deploy/CHANGELOG.md index ce7de86809..e515085bab 100644 --- a/plugins/octopus-deploy/CHANGELOG.md +++ b/plugins/octopus-deploy/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-octopus-deploy +## 0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.2.12-next.0 ### Patch Changes diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index f37205e60b..03bfc735ac 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-octopus-deploy", - "version": "0.2.12-next.0", + "version": "0.2.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/opencost/CHANGELOG.md b/plugins/opencost/CHANGELOG.md index 16d73b63d2..2052e8331c 100644 --- a/plugins/opencost/CHANGELOG.md +++ b/plugins/opencost/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-opencost +## 0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/opencost/package.json b/plugins/opencost/package.json index 1e1b4fb674..6fda71b1ba 100644 --- a/plugins/opencost/package.json +++ b/plugins/opencost/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-opencost", - "version": "0.2.4", + "version": "0.2.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 294d0f50d5..ae22815dba 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index f07229e557..7a50fde6e3 100644 --- a/plugins/org-react/package.json +++ b/plugins/org-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org-react", - "version": "0.1.19-next.0", + "version": "0.1.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 1d2534ee4d..8eafdd7a7d 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org +## 0.6.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + ## 0.6.20-next.0 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 1050876a7b..4355e265aa 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.6.20-next.0", + "version": "0.6.20-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index a97f2a9085..4911f9d43a 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-pagerduty +## 0.7.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-home-react@0.1.8-next.1 + - @backstage/errors@1.2.3 + ## 0.7.2-next.0 ### Patch Changes diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 383a7191bb..4729323462 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-pagerduty", "description": "This plugin has been deprecated, consider using [@pagerduty/backstage-plugin](https://github.com/pagerduty/backstage-plugin) instead.", - "version": "0.7.2-next.0", + "version": "0.7.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/periskop-backend/CHANGELOG.md b/plugins/periskop-backend/CHANGELOG.md index a524779ac9..c7d498b8c0 100644 --- a/plugins/periskop-backend/CHANGELOG.md +++ b/plugins/periskop-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-periskop-backend +## 0.2.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + ## 0.2.8-next.0 ### Patch Changes diff --git a/plugins/periskop-backend/package.json b/plugins/periskop-backend/package.json index c342cf6ad4..e4bcc6172a 100644 --- a/plugins/periskop-backend/package.json +++ b/plugins/periskop-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop-backend", - "version": "0.2.8-next.0", + "version": "0.2.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/periskop/CHANGELOG.md b/plugins/periskop/CHANGELOG.md index 2b9409f9fa..c9738e0447 100644 --- a/plugins/periskop/CHANGELOG.md +++ b/plugins/periskop/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-periskop +## 0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.1.28-next.0 ### Patch Changes diff --git a/plugins/periskop/package.json b/plugins/periskop/package.json index f8f5f02fbd..3e3c76c6a0 100644 --- a/plugins/periskop/package.json +++ b/plugins/periskop/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop", - "version": "0.1.28-next.0", + "version": "0.1.28-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 14855fcb35..2a94cdf1ad 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 0.1.7-next.0 ### Patch Changes diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index e607bc84f3..e386e655ce 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", "description": "Allow all policy backend module for the permission plugin.", - "version": "0.1.7-next.0", + "version": "0.1.7-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 287d320d61..1453b6e88c 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-permission-backend +## 0.5.33-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + ## 0.5.33-next.0 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index eb1f04273f..23ec23e1b5 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.5.33-next.0", + "version": "0.5.33-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index 9a954e9aca..920d52be43 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-node +## 0.7.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + ## 0.7.21-next.0 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 168fbdaa81..50ddb4287e 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.7.21-next.0", + "version": "0.7.21-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index a432970e3f..f2ae9d50dd 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-permission-react +## 0.4.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + ## 0.4.19 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index 7949433aa9..24d39d2dfa 100644 --- a/plugins/permission-react/package.json +++ b/plugins/permission-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-react", - "version": "0.4.19", + "version": "0.4.20-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist-backend/CHANGELOG.md b/plugins/playlist-backend/CHANGELOG.md index a4428bc5fe..27d02ed8c3 100644 --- a/plugins/playlist-backend/CHANGELOG.md +++ b/plugins/playlist-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-playlist-backend +## 0.3.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-playlist-common@0.1.14 + ## 0.3.14-next.0 ### Patch Changes diff --git a/plugins/playlist-backend/package.json b/plugins/playlist-backend/package.json index 976ae79ce1..e745337fbf 100644 --- a/plugins/playlist-backend/package.json +++ b/plugins/playlist-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist-backend", - "version": "0.3.14-next.0", + "version": "0.3.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist/CHANGELOG.md b/plugins/playlist/CHANGELOG.md index 7c90e8f956..fc1147d6c0 100644 --- a/plugins/playlist/CHANGELOG.md +++ b/plugins/playlist/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-playlist +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-playlist-common@0.1.14 + ## 0.2.4-next.0 ### Patch Changes diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json index 9f9cbd6724..3447ec6bc0 100644 --- a/plugins/playlist/package.json +++ b/plugins/playlist/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist", - "version": "0.2.4-next.0", + "version": "0.2.4-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 49c650ce3e..5daf444787 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-proxy-backend +## 0.4.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + ## 0.4.8-next.0 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index eac4fa43e6..4ac471b616 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-proxy-backend", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", - "version": "0.4.8-next.0", + "version": "0.4.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/puppetdb/CHANGELOG.md b/plugins/puppetdb/CHANGELOG.md index 255ac0f314..7e5d214b74 100644 --- a/plugins/puppetdb/CHANGELOG.md +++ b/plugins/puppetdb/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-puppetdb +## 0.1.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.1.13-next.0 ### Patch Changes diff --git a/plugins/puppetdb/package.json b/plugins/puppetdb/package.json index 005950f3a6..8c763341d9 100644 --- a/plugins/puppetdb/package.json +++ b/plugins/puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-puppetdb", "description": "Backstage plugin to visualize resource information and Puppet facts from PuppetDB.", - "version": "0.1.13-next.0", + "version": "0.1.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index c52f70327b..f3d64642c8 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-rollbar-backend +## 0.1.55-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + ## 0.1.55-next.0 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index de6526701b..0d5544d7b7 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar-backend", "description": "A Backstage backend plugin that integrates towards Rollbar", - "version": "0.1.55-next.0", + "version": "0.1.55-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index ced8ca89fa..18a05c34b3 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-rollbar +## 0.4.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.4.30-next.0 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index a3898e64d3..a396ef8b78 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar", "description": "A Backstage plugin that integrates towards Rollbar", - "version": "0.4.30-next.0", + "version": "0.4.30-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index 50b2070b72..0f36ab54c8 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 2613104d7a..136588dbfd 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-azure", "description": "The azure module for @backstage/plugin-scaffolder-backend", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index 9a0344eed8..35554b3810 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index 6f02655692..411f282834 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 1b943777b0..6df8c3e217 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.2.11-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index cf23ba1ba4..c45b596f16 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", - "version": "0.2.11-next.0", + "version": "0.2.11-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 021b21be3e..954d0ea4c8 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.2.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.2.34-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 32536047d1..443c446dfe 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", - "version": "0.2.34-next.0", + "version": "0.2.34-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index 0b0d09dbbd..abcefe70db 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index a1d3110bff..e8753bbd36 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gerrit", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", - "version": "0.1.2-next.0", + "version": "0.1.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 88f68d392e..26b0e1da61 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.2.0-next.1 + +### Minor Changes + +- fd5eb1c: Allow to force the creation of a pull request from a forked repository + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 8e0272c876..a0cccb536c 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", "description": "The github module for @backstage/plugin-scaffolder-backend", - "version": "0.1.2-next.0", + "version": "0.2.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index fe7abefeda..b0b9db42c9 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.2.13-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index bf709c6b7f..d0b2fd7dd0 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitlab", - "version": "0.2.13-next.0", + "version": "0.2.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index e3aa1f0fe9..82deacf251 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.4.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.4.27-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 6c52665976..823051df9e 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", "description": "A module for the scaffolder backend that lets you template projects using Rails", - "version": "0.4.27-next.0", + "version": "0.4.27-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index a6a79787c2..407356fa96 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.1.18-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.18-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 0039ad35a6..242aa5dc6f 100644 --- a/plugins/scaffolder-backend-module-sentry/package.json +++ b/plugins/scaffolder-backend-module-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-sentry", - "version": "0.1.18-next.0", + "version": "0.1.18-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index b57b5a4c6e..96d8622ea2 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.2.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/types@1.1.1 + ## 0.2.31-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 68739803df..a10b73c931 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.2.31-next.0", + "version": "0.2.31-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 5e8c96ed91..04643f3cb3 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-scaffolder-backend +## 1.21.0-next.1 + +### Minor Changes + +- 78c100b: Support providing an overriding token for `fetch:template`, `fetch:plain` and `fetch:file` when interacting with upstream integrations + +### Patch Changes + +- 09f8b31: Simple typo fix in the fetch:template action example on the word 'skeleton'. +- f6792c6: Move the `NODE_OPTIONS` messaging for `--no-node-snapshot` to the `SecureTemplater` in order to get better messaging at runtime +- e1c479d: When using node 20+ the `scaffolder-backend` will now throw an error at startup if the `--no-node-snapshot` option was + not provided to node. +- e0e5afe: Add option to configure nunjucks with the `trimBlocks` and `lstripBlocks` options in the fetch:template action +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-github@0.2.0-next.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-scaffolder-node@0.3.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.7-next.1 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.1.2-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.1.2-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.1.2-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.2.13-next.1 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + ## 1.21.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 2325f7d17e..274a78f844 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "1.21.0-next.0", + "version": "1.21.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index 1e25a2c66e..1bd8a1f192 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-common +## 1.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.12 + ## 1.5.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 29a2f9bb74..df6f15f2ac 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-common", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", - "version": "1.5.0-next.0", + "version": "1.5.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 6f47b13296..cd1d4d9c61 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-scaffolder-node +## 0.3.0-next.1 + +### Minor Changes + +- 78c100b: Support providing an overriding token for `fetch:template`, `fetch:plain` and `fetch:file` when interacting with upstream integrations + +### Patch Changes + +- e0e5afe: Add option to configure nunjucks with the `trimBlocks` and `lstripBlocks` options in the fetch:template action +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + ## 0.3.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index e0e780131f..2a3a230507 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-node", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", - "version": "0.3.0-next.0", + "version": "0.3.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index ba40bc17fb..9bd46e6edc 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-scaffolder-react +## 1.8.0-next.1 + +### Minor Changes + +- b07ec70: Use more distinguishable icons for link (`Link`) and text output (`Description`). + +### Patch Changes + +- 3f60ad5: fix for: converting circular structure to JSON error +- 31f0a0a: Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages +- 82affc7: Fix issue where `ui:schema` was replaced with an empty object if `dependencies` is defined +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + ## 1.8.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 0f6da12d6c..2fe33481f2 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-react", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", - "version": "1.8.0-next.0", + "version": "1.8.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 2309a0554d..ec45f92be2 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-scaffolder +## 1.18.0-next.1 + +### Minor Changes + +- 9b9c05c: Updating the `EntityPicker` to only select `kind` `metadata.name` and `metadata.namespace` by default to improve performance on larger datasets. + +### Patch Changes + +- 31f0a0a: Added `ScaffolderPageContextMenu` to `ActionsPage`, `ListTaskPage`, and `TemplateEditorPage` so that you can more easily navigate between these pages +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/plugin-scaffolder-react@1.8.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-react@0.4.20-next.0 + - @backstage/plugin-scaffolder-common@1.5.0-next.1 + ## 1.18.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index eb6634218d..c81cb90e1f 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "1.18.0-next.0", + "version": "1.18.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index a9123460c3..ec647da935 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-search-backend-module-catalog +## 0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 0.1.14-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 4fdecf1062..abf5d340af 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-catalog", "description": "A module for the search backend that exports catalog modules", - "version": "0.1.14-next.0", + "version": "0.1.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index 5be019c3f9..7b46360c04 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 1.3.13-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 81bc2fc72a..6a17403efd 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", "description": "A module for the search backend that implements search using ElasticSearch", - "version": "1.3.13-next.0", + "version": "1.3.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 50f12e9a3f..7dfcf2edcc 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-backend-module-explore +## 0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 0.1.14-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 4c1fab7f04..7c06d9c5d7 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-explore", "description": "A module for the search backend that exports explore modules", - "version": "0.1.14-next.0", + "version": "0.1.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index 1ea165328c..2bf2792985 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 0.5.19-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 8a39be16be..f36ea26d63 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-pg", "description": "A module for the search backend that implements search using PostgreSQL", - "version": "0.5.19-next.0", + "version": "0.5.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index be37af50b7..d872cae111 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 0.1.3-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index 865e277adc..d225b03381 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", "description": "A module for the search backend that exports stack overflow modules", - "version": "0.1.3-next.0", + "version": "0.1.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 811c7ccf5c..e624727b8f 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.1.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-techdocs-node@1.11.2-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-catalog-node@1.6.2-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 0.1.14-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index dc349aa4fd..f32900ddf4 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", "description": "A module for the search backend that exports techdocs modules", - "version": "0.1.14-next.0", + "version": "0.1.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index c3a8297475..170b57d5ea 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-backend-node +## 1.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-common@1.2.10 + ## 1.2.14-next.0 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index aa38da0003..1a8b6e9404 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-node", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", - "version": "1.2.14-next.0", + "version": "1.2.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 8ddbf86643..48fc23f81a 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-search-backend +## 1.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-permission-node@0.7.21-next.1 + - @backstage/plugin-search-backend-node@1.2.14-next.1 + - @backstage/plugin-search-common@1.2.10 + ## 1.5.0-next.0 ### Minor Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index e66c92a05a..574f849963 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend", "description": "The Backstage backend plugin that provides your backstage app with search", - "version": "1.5.0-next.0", + "version": "1.5.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index c2971227a6..75106c4925 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-react +## 1.7.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-search-common@1.2.10 + ## 1.7.6-next.0 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 02fb2c130b..75e3d2906d 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.7.6-next.0", + "version": "1.7.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 3611b01d8a..072f56c675 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search +## 1.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/version-bridge@1.0.7 + - @backstage/plugin-search-common@1.2.10 + ## 1.4.6-next.0 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index cf368caf87..078f7f9989 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search", "description": "The Backstage plugin that provides your backstage app with search", - "version": "1.4.6-next.0", + "version": "1.4.6-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index fd317e88c8..ad158e756a 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-sentry +## 0.5.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.5.15-next.0 ### Patch Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 7bf5a82661..d393bf3e4e 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sentry", "description": "A Backstage plugin that integrates towards Sentry", - "version": "0.5.15-next.0", + "version": "0.5.15-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/shortcuts/CHANGELOG.md b/plugins/shortcuts/CHANGELOG.md index d819ae5921..72e9a3731f 100644 --- a/plugins/shortcuts/CHANGELOG.md +++ b/plugins/shortcuts/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-shortcuts +## 0.3.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + ## 0.3.18 ### Patch Changes diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 867e28dfa4..0813cc22bb 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-shortcuts", "description": "A Backstage plugin that provides a shortcuts feature to the sidebar", - "version": "0.3.18", + "version": "0.3.19-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/signals-backend/CHANGELOG.md b/plugins/signals-backend/CHANGELOG.md index 81ad27a986..2c91d975e7 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-signals-backend +## 0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + - @backstage/plugin-signals-node@0.0.1-next.1 + ## 0.0.1-next.0 ### Patch Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index 58ab25c320..eeb8ff3a6b 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-backend", - "version": "0.0.1-next.0", + "version": "0.0.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index ee01a4a078..8bd9dbad8d 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-signals-node +## 0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + - @backstage/plugin-events-node@0.2.19-next.1 + ## 0.0.1-next.0 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index bdfdfe396c..e06cd5e1c6 100644 --- a/plugins/signals-node/package.json +++ b/plugins/signals-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-signals-node", "description": "Node.js library for the signals plugin", - "version": "0.0.1-next.0", + "version": "0.0.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/signals-react/CHANGELOG.md b/plugins/signals-react/CHANGELOG.md index 4370801b4f..4b68c623be 100644 --- a/plugins/signals-react/CHANGELOG.md +++ b/plugins/signals-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-signals-react +## 0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/types@1.1.1 + ## 0.0.1-next.0 ### Patch Changes diff --git a/plugins/signals-react/package.json b/plugins/signals-react/package.json index 07fbd6dfbc..6c4a4be79e 100644 --- a/plugins/signals-react/package.json +++ b/plugins/signals-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-signals-react", "description": "Web library for the signals plugin", - "version": "0.0.1-next.0", + "version": "0.0.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/signals/CHANGELOG.md b/plugins/signals/CHANGELOG.md index f180c1cc6f..4001bda476 100644 --- a/plugins/signals/CHANGELOG.md +++ b/plugins/signals/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-signals +## 0.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + - @backstage/plugin-signals-react@0.0.1-next.1 + ## 0.0.1-next.0 ### Patch Changes diff --git a/plugins/signals/package.json b/plugins/signals/package.json index ea7953b0d4..dae18b6a92 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals", - "version": "0.0.1-next.0", + "version": "0.0.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube-backend/CHANGELOG.md b/plugins/sonarqube-backend/CHANGELOG.md index 6d992b0442..73da93941e 100644 --- a/plugins/sonarqube-backend/CHANGELOG.md +++ b/plugins/sonarqube-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-sonarqube-backend +## 0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.2.12-next.0 ### Patch Changes diff --git a/plugins/sonarqube-backend/package.json b/plugins/sonarqube-backend/package.json index 3819087933..dad40ad3e6 100644 --- a/plugins/sonarqube-backend/package.json +++ b/plugins/sonarqube-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube-backend", - "version": "0.2.12-next.0", + "version": "0.2.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube-react/CHANGELOG.md b/plugins/sonarqube-react/CHANGELOG.md index b011d429fd..85d97d871f 100644 --- a/plugins/sonarqube-react/CHANGELOG.md +++ b/plugins/sonarqube-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-sonarqube-react +## 0.1.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.1.12 ### Patch Changes diff --git a/plugins/sonarqube-react/package.json b/plugins/sonarqube-react/package.json index a2c3b8268d..dada16198c 100644 --- a/plugins/sonarqube-react/package.json +++ b/plugins/sonarqube-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube-react", - "version": "0.1.12", + "version": "0.1.13-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 00ac3e138b..b64fe9286f 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-sonarqube +## 0.7.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-sonarqube-react@0.1.13-next.0 + ## 0.7.12-next.0 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 99e333d170..c66f4bdc1c 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sonarqube", "description": "", - "version": "0.7.12-next.0", + "version": "0.7.12-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index 2311bf211e..6429b1378b 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-splunk-on-call +## 0.4.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + ## 0.4.19-next.0 ### Patch Changes diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index e2045cfa8c..2f3e5d20f5 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-splunk-on-call", "description": "A Backstage plugin that integrates towards Splunk On-Call", - "version": "0.4.19-next.0", + "version": "0.4.19-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow-backend/CHANGELOG.md b/plugins/stack-overflow-backend/CHANGELOG.md index 103593ccc2..76cdaab37b 100644 --- a/plugins/stack-overflow-backend/CHANGELOG.md +++ b/plugins/stack-overflow-backend/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-stack-overflow-backend +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.3-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/stack-overflow-backend/package.json b/plugins/stack-overflow-backend/package.json index 991eeff9f6..150b407a3d 100644 --- a/plugins/stack-overflow-backend/package.json +++ b/plugins/stack-overflow-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-stack-overflow-backend", "description": "Deprecated, consider using @backstage/plugin-search-backend-module-stack-overflow-collator instead", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow/CHANGELOG.md b/plugins/stack-overflow/CHANGELOG.md index fd2c61a6c8..34854e634b 100644 --- a/plugins/stack-overflow/CHANGELOG.md +++ b/plugins/stack-overflow/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-stack-overflow +## 0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/plugin-home-react@0.1.8-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.10 + ## 0.1.25-next.0 ### Patch Changes diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json index de0aec1e61..60afbcb7a3 100644 --- a/plugins/stack-overflow/package.json +++ b/plugins/stack-overflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-stack-overflow", - "version": "0.1.25-next.0", + "version": "0.1.25-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stackstorm/CHANGELOG.md b/plugins/stackstorm/CHANGELOG.md index dcade807b8..ffaa1ea330 100644 --- a/plugins/stackstorm/CHANGELOG.md +++ b/plugins/stackstorm/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-stackstorm +## 0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + ## 0.1.10 ### Patch Changes diff --git a/plugins/stackstorm/package.json b/plugins/stackstorm/package.json index d22b17be40..37fab203b5 100644 --- a/plugins/stackstorm/package.json +++ b/plugins/stackstorm/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-stackstorm", "description": "A Backstage plugin that integrates towards StackStorm", - "version": "0.1.10", + "version": "0.1.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md index e5ea8b3a9d..a8bd564c12 100644 --- a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md +++ b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-tech-insights-backend-module-jsonfc +## 0.1.42-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.16-next.1 + ## 0.1.42-next.0 ### Patch Changes diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index 1c34a2157b..0a9b723186 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend-module-jsonfc", - "version": "0.1.42-next.0", + "version": "0.1.42-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend/CHANGELOG.md b/plugins/tech-insights-backend/CHANGELOG.md index 8a07042a2a..3eb8c122f7 100644 --- a/plugins/tech-insights-backend/CHANGELOG.md +++ b/plugins/tech-insights-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-tech-insights-backend +## 0.5.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.16-next.1 + ## 0.5.24-next.0 ### Patch Changes diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 50d86931ee..2ac6746b3e 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend", - "version": "0.5.24-next.0", + "version": "0.5.24-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-node/CHANGELOG.md b/plugins/tech-insights-node/CHANGELOG.md index c0208f7245..2a39acdeca 100644 --- a/plugins/tech-insights-node/CHANGELOG.md +++ b/plugins/tech-insights-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-tech-insights-node +## 0.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + ## 0.4.16-next.0 ### Patch Changes diff --git a/plugins/tech-insights-node/package.json b/plugins/tech-insights-node/package.json index 4cd03c38a7..04f084b129 100644 --- a/plugins/tech-insights-node/package.json +++ b/plugins/tech-insights-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-node", - "version": "0.4.16-next.0", + "version": "0.4.16-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights/CHANGELOG.md b/plugins/tech-insights/CHANGELOG.md index bd1b16e46f..5d5289fa73 100644 --- a/plugins/tech-insights/CHANGELOG.md +++ b/plugins/tech-insights/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-tech-insights +## 0.3.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + ## 0.3.22-next.0 ### Patch Changes diff --git a/plugins/tech-insights/package.json b/plugins/tech-insights/package.json index 4977053132..a31c0242b7 100644 --- a/plugins/tech-insights/package.json +++ b/plugins/tech-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights", - "version": "0.3.22-next.0", + "version": "0.3.22-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index d1eeb41b84..93f4ae95fc 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-tech-radar +## 0.6.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + ## 0.6.13-next.0 ### Patch Changes diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index ad7ca45e41..1cac749ab6 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-tech-radar", "description": "A Backstage plugin that lets you display a Tech Radar for your organization", - "version": "0.6.13-next.0", + "version": "0.6.13-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index a311522241..fee49918a1 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/test-utils@1.5.0-next.1 + - @backstage/plugin-catalog@1.17.0-next.1 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-techdocs@1.10.0-next.1 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + ## 1.0.27-next.0 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 8f1e8e5156..f047a481e7 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-addons-test-utils", - "version": "1.0.27-next.0", + "version": "1.0.27-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index b5f6815373..43a64a9a07 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-techdocs-backend +## 1.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-techdocs-node@1.11.2-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.21-next.0 + - @backstage/plugin-permission-common@0.7.12 + - @backstage/plugin-search-backend-module-techdocs@0.1.14-next.1 + ## 1.9.3-next.0 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index d2e42c48ce..d1a52cd566 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-backend", "description": "The Backstage backend plugin that renders technical documentation for your components", - "version": "1.9.3-next.0", + "version": "1.9.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 0405fc8948..7263e16e3c 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + ## 1.1.5-next.0 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index e19c9f3137..c32729e6ea 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", "description": "Plugin module for contributed TechDocs Addons", - "version": "1.1.5-next.0", + "version": "1.1.5-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 5da0d4f870..60b4346eb6 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-node +## 1.11.2-next.1 + +### Patch Changes + +- 77e3050: Update to a newer version of `@trendyol-js/openstack-swift-sdk` +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-aws-node@0.1.8 + - @backstage/plugin-search-common@1.2.10 + ## 1.11.2-next.0 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index dd7450bf64..6f4df8abf5 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-node", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "1.11.2-next.0", + "version": "1.11.2-next.1", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 7017882a65..d7ec7f943b 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-react +## 1.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/config@1.1.1 + - @backstage/version-bridge@1.0.7 + ## 1.1.15 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 1bdb2a29c8..904a992b54 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-react", "description": "Shared frontend utilities for TechDocs and Addons", - "version": "1.1.15", + "version": "1.1.16-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index b1b9b27eda..150ac0b6d6 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-techdocs +## 1.10.0-next.1 + +### Minor Changes + +- af4d147: Updated the styling for tags to avoid word break. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-common@1.2.10 + ## 1.9.4-next.0 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 829aaa9475..4a38bc2a44 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs", "description": "The Backstage plugin that renders technical documentation for your components", - "version": "1.9.4-next.0", + "version": "1.10.0-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/todo-backend/CHANGELOG.md b/plugins/todo-backend/CHANGELOG.md index fb9f17ab83..7a93b93001 100644 --- a/plugins/todo-backend/CHANGELOG.md +++ b/plugins/todo-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-todo-backend +## 0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/catalog-client@1.6.0-next.1 + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/integration@1.9.0-next.0 + - @backstage/backend-openapi-utils@0.1.3-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.6.2-next.1 + ## 0.3.8-next.0 ### Patch Changes diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index 53c80f3b64..d3c10811d0 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo-backend", "description": "A Backstage backend plugin that lets you browse TODO comments in your source code", - "version": "0.3.8-next.0", + "version": "0.3.8-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index 6217ff3d99..345818accb 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-todo +## 0.2.34-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.2.34-next.0 ### Patch Changes diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 539a1ead3d..9a3e0c95dc 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo", "description": "A Backstage plugin that lets you browse TODO comments in your source code", - "version": "0.2.34-next.0", + "version": "0.2.34-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 96bc93a65c..7bed83d817 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-user-settings-backend +## 0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.4-next.1 + ## 0.2.9-next.0 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index 8cabb70468..f3f70d9613 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings-backend", "description": "The Backstage backend plugin to manage user settings", - "version": "0.2.9-next.0", + "version": "0.2.9-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 2c4a5edf5d..68454cddf3 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-user-settings +## 0.8.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/core-app-api@1.11.4-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/types@1.1.1 + ## 0.8.1-next.0 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 015a13e143..c04f949783 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings", "description": "A Backstage plugin that provides a settings page", - "version": "0.8.1-next.0", + "version": "0.8.1-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault-backend/CHANGELOG.md b/plugins/vault-backend/CHANGELOG.md index 1bc3dc2bf3..4068bd2af2 100644 --- a/plugins/vault-backend/CHANGELOG.md +++ b/plugins/vault-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-vault-backend +## 0.4.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/backend-tasks@0.5.15-next.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-vault-node@0.1.3-next.1 + ## 0.4.3-next.0 ### Patch Changes diff --git a/plugins/vault-backend/package.json b/plugins/vault-backend/package.json index 247925856c..d3fd7d205c 100644 --- a/plugins/vault-backend/package.json +++ b/plugins/vault-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault-backend", "description": "A Backstage backend plugin that integrates towards Vault", - "version": "0.4.3-next.0", + "version": "0.4.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault-node/CHANGELOG.md b/plugins/vault-node/CHANGELOG.md index 0105ba6667..74b84a6077 100644 --- a/plugins/vault-node/CHANGELOG.md +++ b/plugins/vault-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-vault-node +## 0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.10-next.1 + ## 0.1.3-next.0 ### Patch Changes diff --git a/plugins/vault-node/package.json b/plugins/vault-node/package.json index 4b0f3cc630..aa38af4c49 100644 --- a/plugins/vault-node/package.json +++ b/plugins/vault-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault-node", "description": "Node.js library for the vault plugin", - "version": "0.1.3-next.0", + "version": "0.1.3-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault/CHANGELOG.md b/plugins/vault/CHANGELOG.md index 888c06fc68..8e7254fed4 100644 --- a/plugins/vault/CHANGELOG.md +++ b/plugins/vault/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-vault +## 0.1.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/errors@1.2.3 + ## 0.1.25-next.0 ### Patch Changes diff --git a/plugins/vault/package.json b/plugins/vault/package.json index 5d2b3c5c7b..930a83dd6c 100644 --- a/plugins/vault/package.json +++ b/plugins/vault/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault", "description": "A Backstage plugin that integrates towards Vault", - "version": "0.1.25-next.0", + "version": "0.1.25-next.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/xcmetrics/CHANGELOG.md b/plugins/xcmetrics/CHANGELOG.md index 1fc98ffa8b..8d0c42acc2 100644 --- a/plugins/xcmetrics/CHANGELOG.md +++ b/plugins/xcmetrics/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-xcmetrics +## 0.2.48-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.0-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/errors@1.2.3 + ## 0.2.47 ### Patch Changes diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 9ee51bb5c5..dd8c404276 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-xcmetrics", "description": "A Backstage plugin that shows XCode build metrics for your components", - "version": "0.2.47", + "version": "0.2.48-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/yarn.lock b/yarn.lock index 91a5836b28..75fde2bc1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3493,7 +3493,19 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-model@^1.4.3, @backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": +"@backstage/catalog-model@npm:^1.4.3": + version: 1.4.3 + resolution: "@backstage/catalog-model@npm:1.4.3" + dependencies: + "@backstage/errors": ^1.2.3 + "@backstage/types": ^1.1.1 + ajv: ^8.10.0 + lodash: ^4.17.21 + checksum: 56a844d0c78adf62cefc09cf50f5aa221cdcf085e3558512758df9b1ea4ac088c7dbdf8274467a841170177d6cdd83d299ac412ccce5177c907fd6d6e2b2011c + languageName: node + linkType: hard + +"@backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": version: 0.0.0-use.local resolution: "@backstage/catalog-model@workspace:packages/catalog-model" dependencies: @@ -3816,7 +3828,58 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@^0.13.10, @backstage/core-components@^0.13.8, @backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": +"@backstage/core-components@npm:^0.13.10, @backstage/core-components@npm:^0.13.8": + version: 0.13.10 + resolution: "@backstage/core-components@npm:0.13.10" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.8.2 + "@backstage/errors": ^1.2.3 + "@backstage/theme": ^0.5.0 + "@backstage/version-bridge": ^1.0.7 + "@date-io/core": ^1.3.13 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^23.0.0 + "@types/react": ^16.13.1 || ^17.0.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-text-truncate": ^0.14.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + linkify-react: 4.1.3 + linkifyjs: 4.1.3 + lodash: ^4.17.21 + pluralize: ^8.0.0 + qs: ^6.9.4 + rc-progress: 3.5.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-idle-timer: 5.6.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-text-truncate: ^0.19.0 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.11 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ^3.22.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: ec2a0d0a27bc4d6b9d4da97f0b4df600148529ee51bf2c8e7d3adc45c3950adac662535d3beabc451db346f2c7e3c412ceaa756fc774012b43dff5e2695f2271 + languageName: node + linkType: hard + +"@backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": version: 0.0.0-use.local resolution: "@backstage/core-components@workspace:packages/core-components" dependencies: @@ -3889,7 +3952,24 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-plugin-api@^1.8.0, @backstage/core-plugin-api@^1.8.2, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": +"@backstage/core-plugin-api@npm:^1.8.0, @backstage/core-plugin-api@npm:^1.8.2": + version: 1.8.2 + resolution: "@backstage/core-plugin-api@npm:1.8.2" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/types": ^1.1.1 + "@backstage/version-bridge": ^1.0.7 + "@types/react": ^16.13.1 || ^17.0.0 + history: ^5.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 48aeccba7efa29e6989839216fdd2bc212b96334cc94a9ac8feb713038074bdb10c9caa9a2e696c0c2282faa5473883908655148b59c8f071158b3b59c1a9629 + languageName: node + linkType: hard + +"@backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": version: 0.0.0-use.local resolution: "@backstage/core-plugin-api@workspace:packages/core-plugin-api" dependencies: @@ -4117,7 +4197,25 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-react@^1.1.21, @backstage/integration-react@^1.1.23, @backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": +"@backstage/integration-react@npm:^1.1.21, @backstage/integration-react@npm:^1.1.23": + version: 1.1.23 + resolution: "@backstage/integration-react@npm:1.1.23" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.8.2 + "@backstage/integration": ^1.8.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@types/react": ^16.13.1 || ^17.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 84861ed10d2081d53451862a68c63ad123e2b3bf3bbcb541c2b938669ecf93b2244020edb6a505536f811c37344c2b1c639d1a29978df829d819fc5df43fba63 + languageName: node + linkType: hard + +"@backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": version: 0.0.0-use.local resolution: "@backstage/integration-react@workspace:packages/integration-react" dependencies: @@ -4141,6 +4239,22 @@ __metadata: languageName: unknown linkType: soft +"@backstage/integration@npm:^1.8.0": + version: 1.8.0 + resolution: "@backstage/integration@npm:1.8.0" + dependencies: + "@azure/identity": ^4.0.0 + "@backstage/config": ^1.1.1 + "@octokit/auth-app": ^4.0.0 + "@octokit/rest": ^19.0.3 + cross-fetch: ^4.0.0 + git-url-parse: ^13.0.0 + lodash: ^4.17.21 + luxon: ^3.0.0 + checksum: 2cf2956cf2d37e7e0a1c21e60e5bb2e435c4d1a70f63fe915932ee618f3b673c3907e0dc5832a8221a1592e3c8338f333768a9f9693b7ed3bd8e7af1d7ebb2cb + languageName: node + linkType: hard + "@backstage/integration@workspace:^, @backstage/integration@workspace:packages/integration": version: 0.0.0-use.local resolution: "@backstage/integration@workspace:packages/integration" @@ -5615,7 +5729,18 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-common@^1.0.20, @backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": +"@backstage/plugin-catalog-common@npm:^1.0.20": + version: 1.0.20 + resolution: "@backstage/plugin-catalog-common@npm:1.0.20" + dependencies: + "@backstage/catalog-model": ^1.4.3 + "@backstage/plugin-permission-common": ^0.7.12 + "@backstage/plugin-search-common": ^1.2.10 + checksum: 9691e4e022d39b6e2799fe95cbf6397c2f03ff7f0ce7859bb1d90d5df91215a94ee1eed5c174aceaaf2d51d0500eb87c441ae3786a939aae602402f828349dd4 + languageName: node + linkType: hard + +"@backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-common@workspace:plugins/catalog-common" dependencies: @@ -7897,7 +8022,24 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-react@^0.4.19, @backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": +"@backstage/plugin-permission-react@npm:^0.4.19": + version: 0.4.19 + resolution: "@backstage/plugin-permission-react@npm:0.4.19" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.8.2 + "@backstage/plugin-permission-common": ^0.7.12 + "@types/react": ^16.13.1 || ^17.0.0 + swr: ^2.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 256ea54e395faeacb827d3e691dfd584e9f863c4a97af34468b3a49d252506e2d6f5672c011f8d05725374fa11f98ce50ed359c6c078855f13b5cce2e6a52bc7 + languageName: node + linkType: hard + +"@backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": version: 0.0.0-use.local resolution: "@backstage/plugin-permission-react@workspace:plugins/permission-react" dependencies: @@ -8693,7 +8835,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-search-common@workspace:^, @backstage/plugin-search-common@workspace:plugins/search-common": +"@backstage/plugin-search-common@^1.2.10, @backstage/plugin-search-common@workspace:^, @backstage/plugin-search-common@workspace:plugins/search-common": version: 0.0.0-use.local resolution: "@backstage/plugin-search-common@workspace:plugins/search-common" dependencies: From db68a288dff6f620e2204bbcb0796e3a209fbf1e Mon Sep 17 00:00:00 2001 From: tduniec Date: Tue, 30 Jan 2024 14:43:57 +0100 Subject: [PATCH 177/468] update time-saver Signed-off-by: tduniec --- microsite/data/plugins/time-saver.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/time-saver.yaml b/microsite/data/plugins/time-saver.yaml index 054598c66e..d7a3223646 100644 --- a/microsite/data/plugins/time-saver.yaml +++ b/microsite/data/plugins/time-saver.yaml @@ -2,7 +2,7 @@ title: TimeSaver author: tduniec authorUrl: https://github.com/tduniec -category: Discovery, Templates +category: Discovery description: Visualise your time saved using Backstage Scaffolder templates documentation: https://github.com/tduniec/backstage-timesaver-plugin/blob/main/README.md iconUrl: img/tsLogo.png From 3448af6e170b4e054a3eeb42e713c4880e48b41f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 30 Jan 2024 17:03:46 +0100 Subject: [PATCH 178/468] beps/0003: add a few usage patterns for HttpAuthService MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Patrik Oldsberg --- .../README.md | 104 +++++++++++++++++- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/beps/0003-auth-architecture-evolution/README.md b/beps/0003-auth-architecture-evolution/README.md index cc076acae1..fb188e05fd 100644 --- a/beps/0003-auth-architecture-evolution/README.md +++ b/beps/0003-auth-architecture-evolution/README.md @@ -71,7 +71,7 @@ For service-to-service communication we will move away from reusing user tokens ## Design Details -### AuthService (WIP) +### `AuthService` (WIP) The new `AuthService` interface is defined as follows: @@ -96,9 +96,11 @@ export interface AuthService { } ``` -TODO: add usage patterns for this service +#### Usage Patterns -### HttpAuthService (WIP) +TODO + +### `HttpAuthService` (WIP) The new `HttpAuthService` interface is defined as follows: @@ -119,13 +121,105 @@ export interface HttpAuthService { options?: HttpAuthServiceMiddlewareOptions, ): BackstageCredentials; - requestHeaders(credentials: BackstageCredentials): Record; + requestHeaders( + credentials: BackstageCredentials, + ): Promise>; issueUserCookie(res: Response): Promise; } ``` -TODO: add usage patterns for this service +#### Usage Patterns + +All of these usages patterns are from the perspective of a plugin backend. + +**Authenticate incoming request that requires user authentication** + +```ts +// Adding the middleware separately like this makes it apply to ALL +// routes added below it. You can also add it between the path and the +// handler below, to make it apply only for that particular path. +router.use(httpAuth.middleware({ allow: ['user'] })); + +router.get('/read-data', (req, res) => { + // TODO: user can currently be undefined, figure out best pattern to avoid that + const { user } = httpAuth.credentials(req); + console.log( + `User ref=${user.userEntityRef} ownership=${user.ownershipEntityRefs}`, + ); + // ... +}); +``` + +**Forward the user credentials from an incoming requests to upstream plugin backend** + +```ts +router.get( + '/read-data', + // Here, the middleware applies to the current path only + httpAuth.middleware({ allow: ['user'] }), + (req, res) => { + const entity = await catalogClient.getEntityByRef(req.params.entityRef, { + credentials: httpAuth.credentials(req), + }); + // ... + }, +); +``` + +**Allow both user and service request** + +```ts +router.get( + '/read-data', + httpAuth.middleware({ allow: ['user', 'service'] }), + (req, res) => { + const credentials = httpAuth.credentials(req); + if (credentials.user) { + res.json( + // Silly example just to highlight separate code paths for user and + // service requests + todoStore.listOwnedTodos({ owner: credentials.user.userEntityRef }), + ); + } else { + res.json(todoStore.listTodos()); + } + }, +); +``` + +**Issuing a cookie and allowing user cookie auth on a separate endpoint** + +```ts +router.get('/cookie', httpAuth.middleware({ allow: ['user'] }), (req, res) => { + await httpAuth.issueUserCookie(res); + res.json({ ok: true }); +}); + +// Separate endpoint that serves static content, allowing user cookie auth as +// well as regular user auth +router.use( + '/static', + httpAuth.middleware({ allow: ['user', 'user-cookie'] }), + express.static(staticContentDir), +); +``` + +**Passing along user identity from a cookie in an upstream request** + +```ts +router.get( + '/read-data', + httpAuth.middleware({ allow: ['user-cookie'] }), + (req, res) => { + const { cookieUser } = httpAuth.credentials(req); + console.log( + `User ref=${user.userEntityRef} ownership=${user.ownershipEntityRefs}`, + ); + // ... + }, +); +``` ## Release Plan From 6a575f9fb418cd1d2c82621d783d232c6db977c2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 30 Jan 2024 18:00:55 +0100 Subject: [PATCH 179/468] beps/0003: add goal to make sure auth is opt-out Signed-off-by: Patrik Oldsberg --- beps/0003-auth-architecture-evolution/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/beps/0003-auth-architecture-evolution/README.md b/beps/0003-auth-architecture-evolution/README.md index fb188e05fd..9b982ea236 100644 --- a/beps/0003-auth-architecture-evolution/README.md +++ b/beps/0003-auth-architecture-evolution/README.md @@ -50,6 +50,7 @@ The following goals are the primary focus of this BEP: - Protection of the frontend app bundle from being accessed by unauthenticated users. - Basic rate limiting of non-authenticated requests. - Cookie-based authentication of requests for static assets that protects against CSRF attacks and does not unnecessarily expose user tokens. + - A solution where plugin builders need to opt-out of endpoint protection, for example allowing cookie or unauthenticated access. - Basic improvements to the service-to-service auth service interfaces such that we are confident that we do not need to break them in the near future. - If possible we will keep using the existing symmetrical keys that are used today, but it is likely that we will need to break compatibility of existing tokens. - Encapsulation of user credentials in upstream service requests, avoiding the pattern where backend plugins re-use the user token directly for their outgoing requests. @@ -129,6 +130,10 @@ export interface HttpAuthService { } ``` +#### Opt-out behavior + +The `HttpAuthService` must be implemented in such a way that any requests that are handled by the plugin must always touch at least one `httpAuth.middleware(...)`, or it is rejected. This is implemented by overriding the necessary response methods on the `res` object to make it an error to call any of them, but to then restore the original methods in the `httpAuth.middleware(...)`. + #### Usage Patterns All of these usages patterns are from the perspective of a plugin backend. @@ -225,7 +230,7 @@ router.get( The existing `IdentityService` and `TokenManagerService` will be deprecated and instead implemented in terms of the new `AuthService`. -The release plan for the `HttpAuthService` is TBD, but is likely to be shipped as a no-op for plugins using the old backend system. +The release plan for the `HttpAuthService` is TBD, but is likely to be shipped as a no-op for plugins using the old backend system. The goal is for all plugins using the new backend system to have endpoint security be opt-out, which will be a breaking change. ## Dependencies From 736173af2dc004bdf39d6d94e39b983f1b59d168 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 30 Jan 2024 18:03:36 +0100 Subject: [PATCH 180/468] Update beps/0003-auth-architecture-evolution/README.md Co-authored-by: Ben Lambert Signed-off-by: Patrik Oldsberg --- beps/0003-auth-architecture-evolution/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beps/0003-auth-architecture-evolution/README.md b/beps/0003-auth-architecture-evolution/README.md index fb188e05fd..99a800f474 100644 --- a/beps/0003-auth-architecture-evolution/README.md +++ b/beps/0003-auth-architecture-evolution/README.md @@ -214,7 +214,7 @@ router.get( (req, res) => { const { cookieUser } = httpAuth.credentials(req); console.log( - `User ref=${user.userEntityRef} ownership=${user.ownershipEntityRefs}`, + `User ref=${cookieUser.userEntityRef} ownership=${cookieUser.ownershipEntityRefs}`, ); // ... }, From 2ceee849b2a5943a1c58b150629b2fb0b9c01227 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 30 Jan 2024 20:58:15 +0100 Subject: [PATCH 181/468] ci: pass --no-node-snapshot option for node 20 Signed-off-by: Vincenzo Scamporlino --- .github/workflows/deploy_packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 6cd6caecf9..0dc17bbae6 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -53,7 +53,7 @@ jobs: env: CI: true - NODE_OPTIONS: --max-old-space-size=4096 + NODE_OPTIONS: ${{ matrix.node-version == '20.x' && '--max-old-space-size=4096 --no-node-snapshot' || '--max-old-space-size=4096' }} INTEGRATION_TEST_GITHUB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITHUB_TOKEN }} INTEGRATION_TEST_GITLAB_TOKEN: ${{ secrets.INTEGRATION_TEST_GITLAB_TOKEN }} INTEGRATION_TEST_BITBUCKET_TOKEN: ${{ secrets.INTEGRATION_TEST_BITBUCKET_TOKEN }} From 8fe56a816d723c6e6296a3b82998d74968b9b1e7 Mon Sep 17 00:00:00 2001 From: Frank Showalter <842058+fshowalter@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:44:45 -0500 Subject: [PATCH 182/468] expand to full repo Signed-off-by: Frank Showalter <842058+fshowalter@users.noreply.github.com> --- .changeset/olive-boats-agree.md | 93 +++++++++ .changeset/quick-beans-own.md | 5 - packages/core-app-api/package.json | 2 +- packages/core-components/package.json | 2 +- packages/core-plugin-api/package.json | 2 +- packages/dev-utils/package.json | 2 +- packages/frontend-app-api/package.json | 2 +- packages/frontend-plugin-api/package.json | 2 +- packages/test-utils/package.json | 2 +- plugins/adr/package.json | 2 +- plugins/airbrake/package.json | 2 +- plugins/allure/package.json | 2 +- plugins/apache-airflow/package.json | 2 +- .../package.json | 2 +- plugins/api-docs/package.json | 2 +- plugins/apollo-explorer/package.json | 2 +- plugins/azure-devops/package.json | 2 +- plugins/azure-sites/package.json | 2 +- plugins/badges/package.json | 2 +- plugins/bazaar/package.json | 2 +- plugins/bitrise/package.json | 2 +- plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/package.json | 2 +- plugins/catalog-react/package.json | 2 +- .../catalog-unprocessed-entities/package.json | 2 +- plugins/catalog/package.json | 2 +- plugins/circleci/package.json | 2 +- plugins/cloudbuild/package.json | 2 +- plugins/code-climate/package.json | 2 +- plugins/code-coverage/package.json | 2 +- plugins/codescene/package.json | 2 +- plugins/config-schema/package.json | 2 +- plugins/cost-insights/package.json | 2 +- plugins/devtools/package.json | 2 +- plugins/dynatrace/package.json | 2 +- plugins/entity-feedback/package.json | 2 +- plugins/entity-validation/package.json | 2 +- plugins/explore/package.json | 2 +- plugins/firehydrant/package.json | 2 +- plugins/fossa/package.json | 2 +- plugins/gcalendar/package.json | 2 +- plugins/git-release-manager/package.json | 2 +- plugins/github-actions/package.json | 2 +- plugins/github-deployments/package.json | 2 +- plugins/github-issues/package.json | 2 +- .../github-pull-requests-board/package.json | 2 +- plugins/gitops-profiles/package.json | 2 +- plugins/graphiql/package.json | 2 +- plugins/graphql-voyager/package.json | 2 +- plugins/home/package.json | 2 +- plugins/ilert/package.json | 2 +- plugins/jenkins/package.json | 2 +- plugins/kafka/package.json | 2 +- plugins/kubernetes-cluster/package.json | 2 +- plugins/kubernetes-react/package.json | 2 +- plugins/kubernetes/package.json | 2 +- plugins/lighthouse/package.json | 2 +- plugins/linguist/package.json | 2 +- plugins/microsoft-calendar/package.json | 2 +- plugins/newrelic-dashboard/package.json | 2 +- plugins/newrelic/package.json | 2 +- plugins/nomad/package.json | 2 +- plugins/octopus-deploy/package.json | 2 +- plugins/opencost/package.json | 2 +- plugins/org-react/package.json | 2 +- plugins/org/package.json | 2 +- plugins/pagerduty/package.json | 2 +- plugins/periskop/package.json | 2 +- plugins/permission-react/package.json | 2 +- plugins/playlist/package.json | 2 +- plugins/puppetdb/package.json | 2 +- plugins/rollbar/package.json | 2 +- plugins/scaffolder-react/package.json | 2 +- plugins/scaffolder/package.json | 2 +- plugins/search-react/package.json | 2 +- plugins/search/package.json | 2 +- plugins/sentry/package.json | 2 +- plugins/shortcuts/package.json | 2 +- plugins/sonarqube/package.json | 2 +- plugins/splunk-on-call/package.json | 2 +- plugins/stack-overflow/package.json | 2 +- plugins/stackstorm/package.json | 2 +- plugins/tech-insights/package.json | 2 +- plugins/tech-radar/package.json | 2 +- .../techdocs-addons-test-utils/package.json | 2 +- plugins/techdocs-react/package.json | 2 +- plugins/techdocs/package.json | 2 +- plugins/user-settings/package.json | 2 +- plugins/vault/package.json | 2 +- plugins/xcmetrics/package.json | 2 +- yarn.lock | 176 +++++++++--------- 91 files changed, 269 insertions(+), 181 deletions(-) create mode 100644 .changeset/olive-boats-agree.md delete mode 100644 .changeset/quick-beans-own.md diff --git a/.changeset/olive-boats-agree.md b/.changeset/olive-boats-agree.md new file mode 100644 index 0000000000..8f5b4cfad0 --- /dev/null +++ b/.changeset/olive-boats-agree.md @@ -0,0 +1,93 @@ +--- +'@backstage/core-app-api': patch +'@backstage/core-components': patch +'@backstage/core-plugin-api': patch +'@backstage/dev-utils': patch +'@backstage/frontend-app-api': patch +'@backstage/frontend-plugin-api': patch +'@backstage/plugin-adr': patch +'@backstage/plugin-airbrake': patch +'@backstage/plugin-allure': patch +'@backstage/plugin-apache-airflow': patch +'@backstage/plugin-api-docs-module-protoc-gen-doc': patch +'@backstage/plugin-api-docs': patch +'@backstage/plugin-apollo-explorer': patch +'@backstage/plugin-azure-devops': patch +'@backstage/plugin-azure-sites': patch +'@backstage/plugin-badges': patch +'@backstage/plugin-bazaar': patch +'@backstage/plugin-bitrise': patch +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-catalog-unprocessed-entities': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-circleci': patch +'@backstage/plugin-cloudbuild': patch +'@backstage/plugin-code-climate': patch +'@backstage/plugin-code-coverage': patch +'@backstage/plugin-codescene': patch +'@backstage/plugin-config-schema': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-devtools': patch +'@backstage/plugin-dynatrace': patch +'@backstage/plugin-entity-feedback': patch +'@backstage/plugin-entity-validation': patch +'@backstage/plugin-explore': patch +'@backstage/plugin-firehydrant': patch +'@backstage/plugin-fossa': patch +'@backstage/plugin-gcalendar': patch +'@backstage/plugin-git-release-manager': patch +'@backstage/plugin-github-actions': patch +'@backstage/plugin-github-deployments': patch +'@backstage/plugin-github-issues': patch +'@backstage/plugin-github-pull-requests-board': patch +'@backstage/plugin-gitops-profiles': patch +'@backstage/plugin-graphiql': patch +'@backstage/plugin-graphql-voyager': patch +'@backstage/plugin-home': patch +'@backstage/plugin-ilert': patch +'@backstage/plugin-jenkins': patch +'@backstage/plugin-kafka': patch +'@backstage/plugin-kubernetes-cluster': patch +'@backstage/plugin-kubernetes-react': patch +'@backstage/plugin-kubernetes': patch +'@backstage/plugin-lighthouse': patch +'@backstage/plugin-linguist': patch +'@backstage/plugin-microsoft-calendar': patch +'@backstage/plugin-newrelic-dashboard': patch +'@backstage/plugin-newrelic': patch +'@backstage/plugin-nomad': patch +'@backstage/plugin-octopus-deploy': patch +'@backstage/plugin-opencost': patch +'@backstage/plugin-org-react': patch +'@backstage/plugin-org': patch +'@backstage/plugin-pagerduty': patch +'@backstage/plugin-periskop': patch +'@backstage/plugin-permission-react': patch +'@backstage/plugin-playlist': patch +'@backstage/plugin-puppetdb': patch +'@backstage/plugin-rollbar': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-search-react': patch +'@backstage/plugin-search': patch +'@backstage/plugin-sentry': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-splunk-on-call': patch +'@backstage/plugin-stack-overflow': patch +'@backstage/plugin-stackstorm': patch +'@backstage/plugin-tech-insights': patch +'@backstage/plugin-tech-radar': patch +'@backstage/plugin-techdocs-addons-test-utils': patch +'@backstage/plugin-techdocs-react': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-vault': patch +'@backstage/plugin-xcmetrics': patch +'@backstage/test-utils': patch +'@backstage/theme': patch +--- + +Widen @types/react range to include 18. diff --git a/.changeset/quick-beans-own.md b/.changeset/quick-beans-own.md deleted file mode 100644 index d5939c14ac..0000000000 --- a/.changeset/quick-beans-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/theme': patch ---- - -Widen @types/react range to include 18. diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index f0654f7596..b9e5f32fa1 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -47,7 +47,7 @@ "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", "@types/prop-types": "^15.7.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "history": "^5.0.0", "i18next": "^22.4.15", "lodash": "^4.17.21", diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 9dc3e8cb8f..0db498a3c2 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -57,7 +57,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@react-hookz/web": "^23.0.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@types/react-sparklines": "^1.7.0", "@types/react-text-truncate": "^0.14.0", "ansi-regex": "^6.0.1", diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index d676260227..eff339cc6a 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -49,7 +49,7 @@ "@backstage/config": "workspace:^", "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "history": "^5.0.0" }, "peerDependencies": { diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index c74550550c..5e55b08339 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -43,7 +43,7 @@ "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index a95702aed5..81b5e962b5 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -45,7 +45,7 @@ "@backstage/version-bridge": "workspace:^", "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.11.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21" }, "peerDependencies": { diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 1d97626cc8..a630931006 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -28,7 +28,7 @@ "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", "@material-ui/core": "^4.12.4", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "zod": "^3.22.4", "zod-to-json-schema": "^3.21.4" diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 3e1d751677..2712c50780 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -55,7 +55,7 @@ "@backstage/types": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cross-fetch": "^4.0.0", "i18next": "^22.4.15", "zen-observable": "^0.10.0" diff --git a/plugins/adr/package.json b/plugins/adr/package.json index d2157dcae7..c2a46ade85 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -53,7 +53,7 @@ "@backstage/plugin-search-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "react-markdown": "^8.0.0", "react-use": "^17.2.4", diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 904dbcef76..4b38ccaa6e 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -37,7 +37,7 @@ "@backstage/test-utils": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 9254b6c0c8..3bb02dd69b 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -34,7 +34,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index 489fcde5e8..e248e9b665 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -34,7 +34,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cross-fetch": "^4.0.0", "qs": "^6.10.1", "react-use": "^17.2.4" diff --git a/plugins/api-docs-module-protoc-gen-doc/package.json b/plugins/api-docs-module-protoc-gen-doc/package.json index 3adf16d6ff..7ed0edf39b 100644 --- a/plugins/api-docs-module-protoc-gen-doc/package.json +++ b/plugins/api-docs-module-protoc-gen-doc/package.json @@ -32,7 +32,7 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "grpc-docs": "^1.1.2" }, "peerDependencies": { diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 3147c080a8..b263f304b3 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -45,7 +45,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "graphiql": "3.1.0", "graphql": "^16.0.0", "graphql-config": "^5.0.2", diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index 37895e3343..1efe1df84a 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -33,7 +33,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "use-deep-compare-effect": "^1.8.1" }, "peerDependencies": { diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index d0b5932b31..0dba49df0c 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -37,7 +37,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "humanize-duration": "^3.27.0", "luxon": "^3.0.0", "react-use": "^17.2.4" diff --git a/plugins/azure-sites/package.json b/plugins/azure-sites/package.json index c9777e73da..ac31e5a0af 100644 --- a/plugins/azure-sites/package.json +++ b/plugins/azure-sites/package.json @@ -44,7 +44,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 3d8e7d101d..df3faed27c 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -36,7 +36,7 @@ "@backstage/errors": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index dd7629a14f..b601846323 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -41,7 +41,7 @@ "@material-ui/lab": "4.0.0-alpha.61", "@material-ui/pickers": "^3.3.10", "@testing-library/jest-dom": "^6.0.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "material-ui-search-bar": "^1.0.0", "react-hook-form": "^7.13.0", diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 7c1677919e..7b55c4eab5 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -37,7 +37,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "luxon": "^3.0.0", "qs": "^6.9.6", diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 709e764531..d481b9d80c 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -38,7 +38,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "classnames": "^2.3.1", "lodash": "^4.17.15", "p-limit": "^3.1.0", diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 066a073c48..dc4b5fbf15 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -62,7 +62,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@octokit/rest": "^19.0.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "git-url-parse": "^13.0.0", "js-base64": "^3.6.0", "lodash": "^4.17.21", diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index d4114efc59..562aa0b4ff 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -62,7 +62,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@react-hookz/web": "^23.0.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "classnames": "^2.2.6", "lodash": "^4.17.21", "material-ui-popup-state": "^1.9.3", diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index d3b037788a..f8d6c9e267 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -36,7 +36,7 @@ "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.60", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index c30cddc08f..e5826df839 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -65,7 +65,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@mui/utils": "^5.14.15", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "dataloader": "^2.0.0", "expiry-map": "^2.0.0", "history": "^5.0.0", diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 9eb43648af..7a9b367c93 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -40,7 +40,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "circleci-api": "^4.0.0", "humanize-duration": "^3.27.0", "lodash": "^4.17.21", diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 00f0a7ea11..378d6b1218 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -40,7 +40,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "qs": "^6.9.4", "react-use": "^17.2.4" diff --git a/plugins/code-climate/package.json b/plugins/code-climate/package.json index b142f7bb40..48f15ce58c 100644 --- a/plugins/code-climate/package.json +++ b/plugins/code-climate/package.json @@ -34,7 +34,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "humanize-duration": "^3.27.1", "luxon": "^3.0.0", "react-use": "^17.2.4" diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 52dd2a18ec..d15654a824 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -56,7 +56,7 @@ "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0", "@types/highlightjs": "^10.1.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@types/recharts": "^1.8.15" }, "files": [ diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index f0dfd20394..c494d398a3 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -35,7 +35,7 @@ "@backstage/errors": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "rc-progress": "3.5.1", "react-use": "^17.2.4" }, diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 15860d4e89..1fc561b375 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -37,7 +37,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "jsonschema": "^1.2.6", "react-use": "^17.2.4", "zen-observable": "^0.10.0" diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 798853d219..cd2f766e22 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -44,7 +44,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@material-ui/styles": "^4.9.6", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@types/recharts": "^1.8.14", "classnames": "^2.2.6", "history": "^5.0.0", diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 7639df94b8..d67a75dfc6 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -56,7 +56,7 @@ "react-use": "^17.2.4" }, "peerDependencies": { - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-router-dom": "6.0.0-beta.0 || ^6.3.0" diff --git a/plugins/dynatrace/package.json b/plugins/dynatrace/package.json index 23ff57d6c0..c0ce3b2bca 100644 --- a/plugins/dynatrace/package.json +++ b/plugins/dynatrace/package.json @@ -33,7 +33,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/entity-feedback/package.json b/plugins/entity-feedback/package.json index d16702c230..68475516c1 100644 --- a/plugins/entity-feedback/package.json +++ b/plugins/entity-feedback/package.json @@ -37,7 +37,7 @@ "@backstage/plugin-entity-feedback-common": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/entity-validation/package.json b/plugins/entity-validation/package.json index cdf3dc88da..eb6925ac86 100644 --- a/plugins/entity-validation/package.json +++ b/plugins/entity-validation/package.json @@ -43,7 +43,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@react-hookz/web": "^23.0.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@uiw/react-codemirror": "^4.9.3", "lodash": "^4.17.21", "react-use": "^17.2.4", diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 9293c39b10..6da2c9a5e3 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -58,7 +58,7 @@ "@backstage/plugin-search-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "classnames": "^2.2.6", "pluralize": "^8.0.0", "react-use": "^17.2.4" diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index 0171f83c7d..0864f791e1 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -36,7 +36,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 7eee0592f5..afcd66633f 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -41,7 +41,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cross-fetch": "^4.0.0", "luxon": "^3.0.0", "p-limit": "^3.0.2", diff --git a/plugins/gcalendar/package.json b/plugins/gcalendar/package.json index c8a5b0bd0a..9f420754d8 100644 --- a/plugins/gcalendar/package.json +++ b/plugins/gcalendar/package.json @@ -36,7 +36,7 @@ "@material-ui/icons": "^4.9.1", "@maxim_mazurok/gapi.client.calendar": "^3.0.20220408", "@tanstack/react-query": "^4.1.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "classnames": "^2.3.1", "dompurify": "^2.3.6", "lodash": "^4.17.21", diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 7fc2a142ab..9939c8b5ed 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -37,7 +37,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@octokit/rest": "^19.0.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "qs": "^6.10.1", "react-use": "^17.2.4", diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 5d0e243c9a..d6adfb6fcd 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -45,7 +45,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@octokit/rest": "^19.0.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "git-url-parse": "^13.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 713b8d16f6..a3a815c8f3 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -40,7 +40,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@octokit/graphql": "^5.0.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/github-issues/package.json b/plugins/github-issues/package.json index d2d47af426..c4b1636f7f 100644 --- a/plugins/github-issues/package.json +++ b/plugins/github-issues/package.json @@ -38,7 +38,7 @@ "@material-ui/core": "^4.12.4", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "octokit": "^3.0.0", "react-use": "^17.4.0" diff --git a/plugins/github-pull-requests-board/package.json b/plugins/github-pull-requests-board/package.json index 87737e4294..5a77e97b36 100644 --- a/plugins/github-pull-requests-board/package.json +++ b/plugins/github-pull-requests-board/package.json @@ -44,7 +44,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@octokit/rest": "^19.0.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "p-limit": "^4.0.0" }, diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index d9e509a49b..ab06cb0a5e 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -39,7 +39,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index ed4b6ee72b..79b24e495e 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -51,7 +51,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "graphiql": "^3.0.6", "graphql": "^16.0.0", "graphql-ws": "^5.4.1", diff --git a/plugins/graphql-voyager/package.json b/plugins/graphql-voyager/package.json index 2a2941b033..dd245c0384 100644 --- a/plugins/graphql-voyager/package.json +++ b/plugins/graphql-voyager/package.json @@ -33,7 +33,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "graphql-voyager": "^1.0.0-rc.31", "react-use": "^17.2.4" }, diff --git a/plugins/home/package.json b/plugins/home/package.json index c64ef1919e..4bf821d168 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -64,7 +64,7 @@ "@rjsf/material-ui": "5.16.1", "@rjsf/utils": "5.16.1", "@rjsf/validator-ajv8": "5.16.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "luxon": "^3.4.3", "react-grid-layout": "1.3.4", diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index de1dd4797a..05b6e8685f 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -40,7 +40,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@material-ui/pickers": "^3.3.10", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "humanize-duration": "^3.26.0", "luxon": "^3.0.0", "react-use": "^17.2.4" diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 5a94f1f3ec..2067b05b09 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -42,7 +42,7 @@ "@backstage/plugin-jenkins-common": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 16b2d5124b..6bfea0a236 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -37,7 +37,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 0d7b7dce47..f80e91ec22 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -44,7 +44,7 @@ "@kubernetes-models/base": "^4.0.1", "@material-ui/core": "^4.12.2", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cronstrue": "^2.2.0", "js-yaml": "^4.0.0", "kubernetes-models": "^4.1.0", diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 0d722402ba..3ae4e39f5b 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -37,7 +37,7 @@ "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.11.3", "@material-ui/lab": "^4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cronstrue": "^2.32.0", "js-yaml": "^4.1.0", "kubernetes-models": "^4.3.1", diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 39a7a938a2..019538883d 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -44,7 +44,7 @@ "@kubernetes-models/base": "^4.0.1", "@kubernetes/client-node": "0.20.0", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cronstrue": "^2.2.0", "js-yaml": "^4.0.0", "kubernetes-models": "^4.1.0", diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index 104396d6d4..75c372117d 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -42,7 +42,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/linguist/package.json b/plugins/linguist/package.json index 6f20f2b007..20f5c0d6a9 100644 --- a/plugins/linguist/package.json +++ b/plugins/linguist/package.json @@ -52,7 +52,7 @@ "@backstage/plugin-linguist-common": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^2.0.2", "react-use": "^17.2.4", "slugify": "^1.6.4" diff --git a/plugins/microsoft-calendar/package.json b/plugins/microsoft-calendar/package.json index aec094c029..72219153f3 100644 --- a/plugins/microsoft-calendar/package.json +++ b/plugins/microsoft-calendar/package.json @@ -55,7 +55,7 @@ "@material-ui/icons": "^4.9.1", "@microsoft/microsoft-graph-types": "^2.25.0", "@tanstack/react-query": "^4.1.3", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "classnames": "^2.3.1", "dompurify": "^2.3.6", "lodash": "^4.17.21", diff --git a/plugins/newrelic-dashboard/package.json b/plugins/newrelic-dashboard/package.json index e5a63a6971..1282870764 100644 --- a/plugins/newrelic-dashboard/package.json +++ b/plugins/newrelic-dashboard/package.json @@ -36,7 +36,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index a67a2203d5..4bfa8b1d36 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -38,7 +38,7 @@ "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "parse-link-header": "^2.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/nomad/package.json b/plugins/nomad/package.json index 17c345c761..5f118fef3f 100644 --- a/plugins/nomad/package.json +++ b/plugins/nomad/package.json @@ -35,7 +35,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.3.0", "react-use": "^17.2.4" }, diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index f37205e60b..e6f3bff801 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -34,7 +34,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.9.13", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/opencost/package.json b/plugins/opencost/package.json index 1e1b4fb674..eb78e7f812 100644 --- a/plugins/opencost/package.json +++ b/plugins/opencost/package.json @@ -30,7 +30,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/pickers": "^3.3.10", "@material-ui/styles": "^4.11.5", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "axios": "^1.4.0", "date-fns": "^2.30.0", "lodash": "^4.17.21", diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index f07229e557..aa3243722e 100644 --- a/plugins/org-react/package.json +++ b/plugins/org-react/package.json @@ -40,7 +40,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/org/package.json b/plugins/org/package.json index 1050876a7b..e62288918f 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -38,7 +38,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "p-limit": "^3.1.0", "pluralize": "^8.0.0", diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 383a7191bb..8f39893864 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -43,7 +43,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/periskop/package.json b/plugins/periskop/package.json index f8f5f02fbd..faf8e124b0 100644 --- a/plugins/periskop/package.json +++ b/plugins/periskop/package.json @@ -34,7 +34,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index 7949433aa9..cecfb1436d 100644 --- a/plugins/permission-react/package.json +++ b/plugins/permission-react/package.json @@ -35,7 +35,7 @@ "@backstage/config": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "swr": "^2.0.0" }, "peerDependencies": { diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json index 9f9cbd6724..60d1306b20 100644 --- a/plugins/playlist/package.json +++ b/plugins/playlist/package.json @@ -42,7 +42,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "pluralize": "^8.0.0", "qs": "^6.9.4", diff --git a/plugins/puppetdb/package.json b/plugins/puppetdb/package.json index 005950f3a6..ff808538c6 100644 --- a/plugins/puppetdb/package.json +++ b/plugins/puppetdb/package.json @@ -41,7 +41,7 @@ "@backstage/errors": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index a3898e64d3..7c521f41a8 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -40,7 +40,7 @@ "@backstage/plugin-catalog-react": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "react-sparklines": "^1.7.0", "react-use": "^17.2.4" diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index bb038cd2ee..376d5f5043 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -64,7 +64,7 @@ "@rjsf/utils": "5.16.1", "@rjsf/validator-ajv8": "5.16.1", "@types/json-schema": "^7.0.9", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "classnames": "^2.2.6", "humanize-duration": "^3.25.1", "immer": "^9.0.1", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index eb6634218d..d7b5a09972 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -72,7 +72,7 @@ "@rjsf/material-ui": "5.16.1", "@rjsf/utils": "5.16.1", "@rjsf/validator-ajv8": "5.16.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@uiw/react-codemirror": "^4.9.3", "classnames": "^2.2.6", "event-source-polyfill": "^1.0.31", diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 02fb2c130b..58f59f9c9b 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -55,7 +55,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "qs": "^6.9.4", "react-use": "^17.3.2" diff --git a/plugins/search/package.json b/plugins/search/package.json index cf368caf87..3658755793 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -58,7 +58,7 @@ "@backstage/version-bridge": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "qs": "^6.9.4", "react-use": "^17.2.4" }, diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 7bf5a82661..be37751044 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -41,7 +41,7 @@ "@date-io/core": "^1.3.13", "@material-table/core": "^3.1.0", "@material-ui/core": "^4.12.2", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-sparklines": "^1.7.0", "react-use": "^17.2.4" diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 867e28dfa4..09f413dfc0 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -36,7 +36,7 @@ "@backstage/types": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-hook-form": "^7.12.2", "react-use": "^17.2.4", "uuid": "^8.3.2", diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 99e333d170..1a6863e0c6 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -43,7 +43,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/styles": "^4.10.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cross-fetch": "^4.0.0", "luxon": "^3.0.0", "rc-progress": "3.5.1", diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index e2045cfa8c..b1a00ff065 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -41,7 +41,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "luxon": "^3.0.0", "react-use": "^17.2.4" }, diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json index de0aec1e61..52b3bdc712 100644 --- a/plugins/stack-overflow/package.json +++ b/plugins/stack-overflow/package.json @@ -52,7 +52,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@testing-library/jest-dom": "^6.0.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "cross-fetch": "^4.0.0", "lodash": "^4.17.21", "qs": "^6.9.4", diff --git a/plugins/stackstorm/package.json b/plugins/stackstorm/package.json index d22b17be40..7a1f9a18a5 100644 --- a/plugins/stackstorm/package.json +++ b/plugins/stackstorm/package.json @@ -40,7 +40,7 @@ "@backstage/errors": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/tech-insights/package.json b/plugins/tech-insights/package.json index 4977053132..8243a5e3da 100644 --- a/plugins/tech-insights/package.json +++ b/plugins/tech-insights/package.json @@ -39,7 +39,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "qs": "^6.9.4", "react-use": "^17.2.4" }, diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index ad7ca45e41..edacccb3fd 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -52,7 +52,7 @@ "@backstage/frontend-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "color": "^4.0.1", "d3-force": "^3.0.0", "react-use": "^17.2.4" diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 8f1e8e5156..e6618d38be 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -42,7 +42,7 @@ "@backstage/plugin-techdocs": "workspace:^", "@backstage/plugin-techdocs-react": "workspace:^", "@backstage/test-utils": "workspace:^", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "testing-library__dom": "^7.29.4-beta.1" }, "peerDependencies": { diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 1bdb2a29c8..a65691ced5 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -41,7 +41,7 @@ "@backstage/version-bridge": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/styles": "^4.11.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "jss": "~10.10.0", "lodash": "^4.17.21", "react-helmet": "6.1.0", diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 829aaa9475..9952b546b6 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -65,7 +65,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@material-ui/styles": "^4.10.0", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "dompurify": "^2.2.9", "event-source-polyfill": "1.0.25", "git-url-parse": "^13.0.0", diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 015a13e143..b37d659ccb 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -58,7 +58,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4", "zen-observable": "^0.10.0" }, diff --git a/plugins/vault/package.json b/plugins/vault/package.json index 5d2b3c5c7b..8c6e1a346a 100644 --- a/plugins/vault/package.json +++ b/plugins/vault/package.json @@ -42,7 +42,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 9ee51bb5c5..71d3d1d3d1 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -36,7 +36,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@types/react": "^16.13.1 || ^17.0.0", + "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "luxon": "^3.0.0", "react-use": "^17.2.4", diff --git a/yarn.lock b/yarn.lock index 71909cf568..83e9705408 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3770,7 +3770,7 @@ __metadata: "@testing-library/react-hooks": ^8.0.0 "@testing-library/user-event": ^14.0.0 "@types/prop-types": ^15.7.3 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/zen-observable": ^0.8.0 history: ^5.0.0 i18next: ^22.4.15 @@ -3845,7 +3845,7 @@ __metadata: "@types/d3-zoom": ^3.0.1 "@types/dagre": ^0.7.44 "@types/google-protobuf": ^3.7.2 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/react-helmet": ^6.1.0 "@types/react-sparklines": ^1.7.0 "@types/react-syntax-highlighter": ^15.0.0 @@ -3902,7 +3902,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 history: ^5.0.0 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -3957,7 +3957,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 zen-observable: ^0.10.0 peerDependencies: @@ -4024,7 +4024,7 @@ __metadata: "@material-ui/icons": ^4.11.3 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 lodash: ^4.17.21 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -4067,7 +4067,7 @@ __metadata: "@material-ui/core": ^4.12.4 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 history: ^5.3.0 lodash: ^4.17.21 zod: ^3.22.4 @@ -4219,7 +4219,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 lodash: ^4.17.21 react-markdown: ^8.0.0 react-use: ^17.2.4 @@ -4268,7 +4268,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react-use: ^17.2.4 peerDependencies: @@ -4292,7 +4292,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react-use: ^17.2.4 peerDependencies: @@ -4378,7 +4378,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cross-fetch: ^4.0.0 msw: ^1.0.0 qs: ^6.10.1 @@ -4396,7 +4396,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@testing-library/jest-dom": ^6.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 grpc-docs: ^1.1.2 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -4429,7 +4429,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/swagger-ui-react": ^4.18.0 graphiql: 3.1.0 graphql: ^16.0.0 @@ -4457,7 +4457,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 use-deep-compare-effect: ^1.8.1 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -4921,7 +4921,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 humanize-duration: ^3.27.0 luxon: ^3.0.0 react-use: ^17.2.4 @@ -4991,7 +4991,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 msw: ^1.0.0 react-use: ^17.2.4 @@ -5043,7 +5043,7 @@ __metadata: "@backstage/test-utils": "workspace:^" "@material-ui/core": ^4.12.2 "@testing-library/jest-dom": ^6.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -5089,7 +5089,7 @@ __metadata: "@material-ui/lab": 4.0.0-alpha.61 "@material-ui/pickers": ^3.3.10 "@testing-library/jest-dom": ^6.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 material-ui-search-bar: ^1.0.0 react-hook-form: ^7.13.0 @@ -5133,7 +5133,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/recharts": ^1.8.15 lodash: ^4.17.21 luxon: ^3.0.0 @@ -5643,7 +5643,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 classnames: ^2.3.1 lodash: ^4.17.15 p-limit: ^3.1.0 @@ -5684,7 +5684,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 git-url-parse: ^13.0.0 js-base64: ^3.6.0 lodash: ^4.17.21 @@ -5780,7 +5780,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/zen-observable": ^0.8.0 classnames: ^2.2.6 lodash: ^4.17.21 @@ -5812,7 +5812,7 @@ __metadata: "@material-ui/lab": ^4.0.0-alpha.60 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -5853,7 +5853,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 dataloader: ^2.0.0 expiry-map: ^2.0.0 history: ^5.0.0 @@ -5929,7 +5929,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@types/humanize-duration": ^3.25.1 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 circleci-api: ^4.0.0 humanize-duration: ^3.27.0 lodash: ^4.17.21 @@ -5958,7 +5958,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 qs: ^6.9.4 react-use: ^17.2.4 @@ -5986,7 +5986,7 @@ __metadata: "@testing-library/react": ^14.0.0 "@types/humanize-duration": ^3.27.1 "@types/luxon": ^3.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 humanize-duration: ^3.27.1 luxon: ^3.0.0 react-use: ^17.2.4 @@ -6045,7 +6045,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/highlightjs": ^10.1.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/recharts": ^1.8.15 highlight.js: ^10.6.0 luxon: ^3.0.0 @@ -6075,7 +6075,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 rc-progress: 3.5.1 react-use: ^17.2.4 @@ -6102,7 +6102,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 jsonschema: ^1.2.6 react-use: ^17.2.4 zen-observable: ^0.10.0 @@ -6145,7 +6145,7 @@ __metadata: "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 "@types/pluralize": ^0.0.33 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/recharts": ^1.8.14 "@types/regression": ^2.0.0 "@types/yup": ^0.29.13 @@ -6234,7 +6234,7 @@ __metadata: react-json-view: ^1.21.3 react-use: ^17.2.4 peerDependencies: - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react: ^16.13.1 || ^17.0.0 || ^18.0.0 react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 react-router-dom: 6.0.0-beta.0 || ^6.3.0 @@ -6256,7 +6256,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 peerDependencies: "@backstage/plugin-catalog-react": "workspace:^" @@ -6317,7 +6317,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react-use: ^17.2.4 peerDependencies: @@ -6351,7 +6351,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@uiw/react-codemirror": ^4.9.3 lodash: ^4.17.21 react-use: ^17.2.4 @@ -6563,7 +6563,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 classnames: ^2.2.6 msw: ^1.0.0 pluralize: ^8.0.0 @@ -6592,7 +6592,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 react-use: ^17.2.4 peerDependencies: @@ -6620,7 +6620,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cross-fetch: ^4.0.0 luxon: ^3.0.0 msw: ^1.0.0 @@ -6651,7 +6651,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/dompurify": ^2.3.3 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/sanitize-html": ^2.6.2 classnames: ^2.3.1 dompurify: ^2.3.6 @@ -6704,7 +6704,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/recharts": ^1.8.15 luxon: ^3.0.0 qs: ^6.10.1 @@ -6738,7 +6738,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 git-url-parse: ^13.0.0 luxon: ^3.0.0 react-use: ^17.2.4 @@ -6770,7 +6770,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 msw: ^1.0.0 react-use: ^17.2.4 @@ -6800,7 +6800,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 octokit: ^3.0.0 react-use: ^17.4.0 @@ -6829,7 +6829,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 p-limit: ^4.0.0 peerDependencies: @@ -6855,7 +6855,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -6914,7 +6914,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/codemirror": ^5.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 graphiql: ^3.0.6 graphql: ^16.0.0 graphql-ws: ^5.4.1 @@ -6938,7 +6938,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 graphql-voyager: ^1.0.0-rc.31 react-use: ^17.2.4 peerDependencies: @@ -7013,7 +7013,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/react-grid-layout": ^1.3.2 lodash: ^4.17.21 luxon: ^3.4.3 @@ -7047,7 +7047,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 humanize-duration: ^3.26.0 luxon: ^3.0.0 react-use: ^17.2.4 @@ -7114,7 +7114,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/testing-library__jest-dom": ^5.9.1 luxon: ^3.0.0 react-use: ^17.2.4 @@ -7163,7 +7163,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 jest-when: ^3.1.0 react-use: ^17.2.4 peerDependencies: @@ -7247,7 +7247,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/node": ^16.11.26 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cronstrue: ^2.2.0 js-yaml: ^4.0.0 kubernetes-models: ^4.1.0 @@ -7318,7 +7318,7 @@ __metadata: "@material-ui/lab": ^4.0.0-alpha.61 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cronstrue: ^2.32.0 jest-websocket-mock: ^2.5.0 js-yaml: ^4.1.0 @@ -7355,7 +7355,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cronstrue: ^2.2.0 js-yaml: ^4.0.0 kubernetes-models: ^4.1.0 @@ -7417,7 +7417,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react-use: ^17.2.4 peerDependencies: @@ -7488,7 +7488,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^2.0.2 react-use: ^17.2.4 slugify: ^1.6.4 @@ -7515,7 +7515,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 classnames: ^2.3.1 dompurify: ^2.3.6 lodash: ^4.17.21 @@ -7544,7 +7544,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@testing-library/jest-dom": ^6.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -7568,7 +7568,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/parse-link-header": ^2.0.1 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.2.3 parse-link-header: ^2.0.0 react-use: ^17.2.4 @@ -7613,7 +7613,7 @@ __metadata: "@material-ui/icons": ^4.9.1 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cross-fetch: ^4.0.0 luxon: ^3.3.0 react-use: ^17.2.4 @@ -7638,7 +7638,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -7661,7 +7661,7 @@ __metadata: "@material-ui/pickers": ^3.3.10 "@material-ui/styles": ^4.11.5 "@testing-library/jest-dom": ^6.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 axios: ^1.4.0 date-fns: ^2.30.0 lodash: ^4.17.21 @@ -7691,7 +7691,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 react-use: ^17.2.4 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -7725,7 +7725,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 lodash: ^4.17.21 p-limit: ^3.1.0 pluralize: ^8.0.0 @@ -7758,7 +7758,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 react-use: ^17.2.4 peerDependencies: @@ -7802,7 +7802,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 react-use: ^17.2.4 peerDependencies: @@ -7902,7 +7902,7 @@ __metadata: "@backstage/test-utils": "workspace:^" "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 swr: ^2.0.0 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 @@ -7975,7 +7975,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 lodash: ^4.17.21 msw: ^1.0.0 pluralize: ^8.0.0 @@ -8035,7 +8035,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.1 react-use: ^17.2.4 peerDependencies: @@ -8086,7 +8086,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 lodash: ^4.17.21 react-sparklines: ^1.7.0 react-use: ^17.2.4 @@ -8419,7 +8419,7 @@ __metadata: "@types/humanize-duration": ^3.18.1 "@types/json-schema": ^7.0.9 "@types/luxon": ^3.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 classnames: ^2.2.6 humanize-duration: ^3.25.1 immer: ^9.0.1 @@ -8481,7 +8481,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/humanize-duration": ^3.18.1 "@types/json-schema": ^7.0.9 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@uiw/react-codemirror": ^4.9.3 classnames: ^2.2.6 event-source-polyfill: ^1.0.31 @@ -8719,7 +8719,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 lodash: ^4.17.21 qs: ^6.9.4 react-use: ^17.3.2 @@ -8754,7 +8754,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 history: ^5.0.0 qs: ^6.9.4 react-use: ^17.2.4 @@ -8783,7 +8783,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 react-sparklines: ^1.7.0 react-use: ^17.2.4 @@ -8810,7 +8810,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 "@types/zen-observable": ^0.8.2 react-hook-form: ^7.12.2 react-use: ^17.2.4 @@ -8968,7 +8968,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cross-fetch: ^4.0.0 luxon: ^3.0.0 msw: ^1.0.0 @@ -9000,7 +9000,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 luxon: ^3.0.0 react-use: ^17.2.4 peerDependencies: @@ -9042,7 +9042,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cross-fetch: ^4.0.0 lodash: ^4.17.21 msw: ^1.0.0 @@ -9071,7 +9071,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react-use: ^17.2.4 peerDependencies: @@ -9176,7 +9176,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 qs: ^6.9.4 react-use: ^17.2.4 peerDependencies: @@ -9206,7 +9206,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/color": ^3.0.1 "@types/d3-force": ^3.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 color: ^4.0.1 d3-force: ^3.0.0 react-use: ^17.2.4 @@ -9233,7 +9233,7 @@ __metadata: "@backstage/test-utils": "workspace:^" "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 testing-library__dom: ^7.29.4-beta.1 peerDependencies: "@testing-library/react": ^12.1.3 || ^13.0.0 || ^14.0.0 @@ -9364,7 +9364,7 @@ __metadata: "@material-ui/styles": ^4.11.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 jss: ~10.10.0 lodash: ^4.17.21 react-helmet: 6.1.0 @@ -9409,7 +9409,7 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/dompurify": ^2.2.2 "@types/event-source-polyfill": ^1.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 canvas: ^2.10.2 dompurify: ^2.2.9 event-source-polyfill: 1.0.25 @@ -9520,7 +9520,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react-use: ^17.2.4 zen-observable: ^0.10.0 @@ -9588,7 +9588,7 @@ __metadata: "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 msw: ^1.0.0 react-use: ^17.2.4 peerDependencies: @@ -9616,7 +9616,7 @@ __metadata: "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 "@types/luxon": ^3.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 lodash: ^4.17.21 luxon: ^3.0.0 react-use: ^17.2.4 @@ -9710,7 +9710,7 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@testing-library/jest-dom": ^6.0.0 - "@types/react": ^16.13.1 || ^17.0.0 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 cross-fetch: ^4.0.0 i18next: ^22.4.15 msw: ^1.0.0 From 2a50cbf40558e508a8abce025e8b95f096bf11a2 Mon Sep 17 00:00:00 2001 From: armandocomellas1 Date: Tue, 30 Jan 2024 20:37:18 -0600 Subject: [PATCH 183/468] Add key and values to the GkeClusterLocator.ts script and add an unit test to check if those are there Signed-off-by: armandocomellas1 --- .changeset/mighty-tomatoes-visit.md | 5 +++++ .../cluster-locator/GkeClusterLocator.test.ts | 19 +++++++++++++++++++ .../src/cluster-locator/GkeClusterLocator.ts | 7 ++++++- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .changeset/mighty-tomatoes-visit.md diff --git a/.changeset/mighty-tomatoes-visit.md b/.changeset/mighty-tomatoes-visit.md new file mode 100644 index 0000000000..08e443d932 --- /dev/null +++ b/.changeset/mighty-tomatoes-visit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': minor +--- + +At Line 91, in the second parameter enter a value for the property libName and libVersion to post headers to the key `x-goog-api-client` diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts index 849a7839e2..ba46e03ae2 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts @@ -18,6 +18,7 @@ import { ANNOTATION_KUBERNETES_AUTH_PROVIDER } from '@backstage/plugin-kubernete import '@backstage/backend-common'; import { ConfigReader, Config } from '@backstage/config'; import { GkeClusterLocator } from './GkeClusterLocator'; +import { Duration } from 'luxon'; const mockedListClusters = jest.fn(); @@ -485,5 +486,23 @@ describe('GkeClusterLocator', () => { }, ]); }); + it('Check if new container.v1.ClusterManagerClient has key and values as parameter', async () => { + const configs: Config = new ConfigReader({ + type: 'gke', + projectId: 'some-project', + }); + + const refreshIntervals: Duration | undefined = undefined; + const getHeaders: GkeClusterLocator = GkeClusterLocator.fromConfig( + configs, + refreshIntervals, + ); + const getKeyName = getHeaders.client._opts.hasOwnProperty(['libName']); + const getKeyVersion = getHeaders.client._opts.hasOwnProperty([ + 'libVersion', + ]); + expect(getKeyName).toBe(true); + expect(getKeyVersion).toBe(true); + }); }); }); diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts index 03f5bda103..33b9ee74b9 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.ts @@ -21,6 +21,7 @@ import * as container from '@google-cloud/container'; import { Duration } from 'luxon'; import { runPeriodically } from '../service/runPeriodically'; import { ClusterDetails, KubernetesClustersSupplier } from '../types/types'; +import packageinfo from '../../package.json'; interface MatchResourceLabelEntry { key: string; @@ -80,13 +81,17 @@ export class GkeClusterLocator implements KubernetesClustersSupplier { return gkeClusterLocator; } + // At Line 91, in the second parameter enter a value for the property libName and libVersion to post headers to the key 'x-goog-api-client' static fromConfig( config: Config, refreshInterval: Duration | undefined = undefined, ): GkeClusterLocator { return GkeClusterLocator.fromConfigWithClient( config, - new container.v1.ClusterManagerClient(), + new container.v1.ClusterManagerClient({ + libName: 'backstage/gke', + libVersion: packageinfo.version, + }), refreshInterval, ); } From 77b42e63c433c2ca4d1cd13611e3cec9cf90b411 Mon Sep 17 00:00:00 2001 From: armandocomellas1 Date: Tue, 30 Jan 2024 20:51:12 -0600 Subject: [PATCH 184/468] Used special chars to identify some sensitive variable names by the linter errors tools Signed-off-by: armandocomellas1 --- .changeset/mighty-tomatoes-visit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/mighty-tomatoes-visit.md b/.changeset/mighty-tomatoes-visit.md index 08e443d932..7c12a0fa00 100644 --- a/.changeset/mighty-tomatoes-visit.md +++ b/.changeset/mighty-tomatoes-visit.md @@ -2,4 +2,4 @@ '@backstage/plugin-kubernetes-backend': minor --- -At Line 91, in the second parameter enter a value for the property libName and libVersion to post headers to the key `x-goog-api-client` +At Line 91, in the second parameter enter a value for the property `libName` and `libVersion` to post headers to the key `x-goog-api-client` From 136d1666ada725ecb64de6d1370c59f64ef0da18 Mon Sep 17 00:00:00 2001 From: armandocomellas1 Date: Tue, 30 Jan 2024 21:08:12 -0600 Subject: [PATCH 185/468] add an unit test to check if those are theres Signed-off-by: armandocomellas1 --- .../src/cluster-locator/GkeClusterLocator.test.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts index ba46e03ae2..6b06fd26a2 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts @@ -497,10 +497,9 @@ describe('GkeClusterLocator', () => { configs, refreshIntervals, ); - const getKeyName = getHeaders.client._opts.hasOwnProperty(['libName']); - const getKeyVersion = getHeaders.client._opts.hasOwnProperty([ - 'libVersion', - ]); + const getClientMethod = getHeaders.client._opts; + const getKeyName = getClientMethod.hasOwnProperty('libName'); + const getKeyVersion = getClientMethod.hasOwnProperty('libVersion'); expect(getKeyName).toBe(true); expect(getKeyVersion).toBe(true); }); From e749ed8aad03848cee9a8732b5de44c4fd4939ff Mon Sep 17 00:00:00 2001 From: armandocomellas1 Date: Tue, 30 Jan 2024 21:25:23 -0600 Subject: [PATCH 186/468] Private fn doesnt not allow acces method, change into expect unit test Signed-off-by: armandocomellas1 --- .../src/cluster-locator/GkeClusterLocator.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts index 6b06fd26a2..54b90b6033 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts @@ -497,11 +497,9 @@ describe('GkeClusterLocator', () => { configs, refreshIntervals, ); - const getClientMethod = getHeaders.client._opts; - const getKeyName = getClientMethod.hasOwnProperty('libName'); - const getKeyVersion = getClientMethod.hasOwnProperty('libVersion'); - expect(getKeyName).toBe(true); - expect(getKeyVersion).toBe(true); + + expect(getHeaders.client._opts).toHaveProperty('libName'); + expect(getHeaders.client._opts).toHaveProperty('libVersion'); }); }); }); From a3df97fc4106affa90389c428c9b1aed94475a89 Mon Sep 17 00:00:00 2001 From: armandocomellas1 Date: Tue, 30 Jan 2024 21:27:39 -0600 Subject: [PATCH 187/468] Private fn doesnt not allow acces method, change into expect unit test Signed-off-by: armandocomellas1 --- .../src/cluster-locator/GkeClusterLocator.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts index 54b90b6033..de77000249 100644 --- a/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts +++ b/plugins/kubernetes-backend/src/cluster-locator/GkeClusterLocator.test.ts @@ -498,8 +498,8 @@ describe('GkeClusterLocator', () => { refreshIntervals, ); - expect(getHeaders.client._opts).toHaveProperty('libName'); - expect(getHeaders.client._opts).toHaveProperty('libVersion'); + expect(getHeaders).toHaveProperty('client._opts.libName'); + expect(getHeaders).toHaveProperty('client._opts.libVersion'); }); }); }); From 4944f29f2cc61dbd29019cd5693c051301f8497f Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 30 Jan 2024 22:28:02 -0500 Subject: [PATCH 188/468] update documentation and add a simple guide. Signed-off-by: Aramis --- docs/permissions/concepts.md | 4 +- docs/permissions/custom-rules.md | 2 +- .../02-adding-a-basic-permission-check.md | 4 +- .../03-adding-a-resource-permission-check.md | 2 +- docs/references/glossary.md | 118 ++++++++---------- docs/references/writing-a-glossary-entry.md | 83 ++++++++++++ 6 files changed, 141 insertions(+), 72 deletions(-) create mode 100644 docs/references/writing-a-glossary-entry.md diff --git a/docs/permissions/concepts.md b/docs/permissions/concepts.md index 556d752c52..8e348373cf 100644 --- a/docs/permissions/concepts.md +++ b/docs/permissions/concepts.md @@ -10,10 +10,10 @@ Two important responsibilities of any authorization system are to decide if a us ### Resources and rules -In many cases, a permission represents a user's interaction with another object. This object likely has information that policy authors can use to define more granular access. The permission framework introduces two abstractions to account for this: [resources](../references/glossary.md#permission-resource) and [rules](../references/glossary.md#permission-rule). For example, the catalog plugin defines a resource for catalog entities and a rule to check if an entity has a given annotation. +In many cases, a permission represents a user's interaction with another object. This object likely has information that policy authors can use to define more granular access. The permission framework introduces two abstractions to account for this: [resources](../references/glossary.md#resource-permission-plugin) and [rules](../references/glossary.md#rule-permission-plugin). For example, the catalog plugin defines a resource for catalog entities and a rule to check if an entity has a given annotation. ### Conditional decisions -See [Conditional decisions](../references/glossary.md#conditional-decisions). +[Rules](../references/glossary.md#rule-permission-plugin) need additional data before they can be used in a decision. Once a [rule](../references/glossary.md#rule-permission-plugin) is bound to relevant information it forms a [condition](../references/glossary.md#condition-permission-plugin). Conditional decisions tell the [permission framework](#permission) to delegate evaluation to the [plugin](#plugin) that owns the corresponding [resource](#resource-permission-plugin). Permission requests that result in a conditional decision are allowed if all of the provided conditions evaluate to be true. A good example would be the catalog plugin's "has annotation" rule which needs to know what annotation to look for on a given entity. The permission framework would respond to a request by the catalog plugin in this case with a condition decision. The catalog plugin would then need to correctly filter for entities matching the "has annotations" condition. This conditional behavior avoids coupling between policies and resource schemas, and allows plugins to evaluate complex rules in an efficient way. For example, a plugin may convert a conditional decision to a database query instead of loading and filtering objects in memory. diff --git a/docs/permissions/custom-rules.md b/docs/permissions/custom-rules.md index 4b38c8986d..9063ed7257 100644 --- a/docs/permissions/custom-rules.md +++ b/docs/permissions/custom-rules.md @@ -4,7 +4,7 @@ title: Defining custom permission rules description: How to define custom permission rules for existing resources --- -For some use cases, you may want to define custom [rules](../references/glossary.md#permission-rules) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of. +For some use cases, you may want to define custom [rules](../references/glossary.md#rule-permission-plugin) in addition to the ones provided by a plugin. In the [previous section](./writing-a-policy.md) we used the `isEntityOwner` rule to control access for catalog entities. Let's extend this policy with a custom rule that checks what [system](https://backstage.io/docs/features/software-catalog/system-model#system) an entity is part of. ## Define a custom rule diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 0ef50670ef..f3380bdb6f 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -4,9 +4,9 @@ title: 2. Adding a basic permission check description: Explains how to add a basic permission check to a Backstage plugin --- -If the outcome of a permission check doesn't need to change for different [resources](../../references/glossary.md#permission-resource), you can use a _basic permission check_. For this kind of check, we simply need to define a [permission](../../references/glossary.md#permission), and call `authorize` with it. +If the outcome of a permission check doesn't need to change for different [resources](../../references/glossary.md#resource-permission-plugin), you can use a _basic permission check_. For this kind of check, we simply need to define a permission, and call `authorize` with it. -For this tutorial, we'll use a basic permission check to authorize the `create` endpoint in our todo-backend. This will allow Backstage integrators to control whether each of their users is authorized to create todos by adjusting their [permission policy](../../references/glossary.md#policy). +For this tutorial, we'll use a basic permission check to authorize the `create` endpoint in our todo-backend. This will allow Backstage integrators to control whether each of their users is authorized to create todos by adjusting their [permission policy](../../references/glossary.md#policy-permission-plugin). We'll start by creating a new permission, and then we'll use the permission api to call `authorize` with it during todo creation. diff --git a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md index 3feecc7342..76c1a85e24 100644 --- a/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md +++ b/docs/permissions/plugin-authors/03-adding-a-resource-permission-check.md @@ -4,7 +4,7 @@ title: 3. Adding a resource permission check description: Explains how to add a resource permission check to a Backstage plugin --- -When performing updates (or other operations) on specific [resources](../../references/glossary.md#permission-resource), the permissions framework allows for the decision to be based on characteristics of the resource itself. This means that it's possible to write policies that (for example) allow the operation for users that own a resource, and deny the operation otherwise. +When performing updates (or other operations) on specific [resources](../../references/glossary.md#resource-permission-plugin), the permissions framework allows for the decision to be based on characteristics of the resource itself. This means that it's possible to write policies that (for example) allow the operation for users that own a resource, and deny the operation otherwise. ## Creating the update permission diff --git a/docs/references/glossary.md b/docs/references/glossary.md index c5d991292a..165c000fc8 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -5,11 +5,9 @@ title: Glossary description: List of terms, abbreviations, and phrases used in Backstage, together with their explanations. --- -## Access token +## Access Token -A [token](#token) that gives access to perform actions on behalf of a user. It will commonly have a short expiry time, and be limited to a set of [scopes](#scope). Part of the [OAuth](#oauth) protocol. - -https://oauth.net/2/access-tokens/ +A [token](#token) that gives access to perform actions on behalf of a user. It will commonly have a short expiry time, and be limited to a set of [scopes](#scope). Part of the [OAuth](#oauth) protocol, see [their docs](https://oauth.net/2/access-tokens/) for more information. ## Administrator @@ -17,9 +15,7 @@ Someone responsible for installing and maintaining a Backstage [app](#app) for a ## API (catalog plugin) -In the Backstage [Catalog](#catalog), an API is an [entity](#entity) representing a boundary between two [components](#component). - -https://backstage.io/docs/features/software-catalog/system-model +An [entity](#entity) representing a schema that two [components](#component) use to communicate. See [the catalog docs](https://backstage.io/docs/features/software-catalog/system-model) for more information. ## App @@ -27,33 +23,29 @@ An installed instance of Backstage. An app can be local, intended for a single d ## Authorization Code -A type of [OAuth flow](#oauth) used by confidential and public clients to get an [access token](#access-token). - -https://oauth.net/2/grant-types/authorization-code/ +A type of [OAuth flow](#oauth) used by confidential and public clients to get an [access token](#access-token). See [the OAuth docs](https://oauth.net/2/grant-types/authorization-code/) for more details. ## Backstage -A platform for creating and deploying [developer portals](#developer-portal), originally created at Spotify. +1. A platform for creating and deploying [developer portals](#developer-portal), originally created at Spotify. Backstage is an incubation-stage open source project of the [Cloud Native Computing Foundation](#cloud-native-computing-foundation). -Backstage is an incubation-stage open source project of the [Cloud Native Computing Foundation](#cloud-native-computing-foundation). +2. [The Backstage Framework](#backstage-framework). -Can also refer to: [Backstage framework](#backstage-framework). - -## Backstage framework +## Backstage Framework The actual framework that Backstage [plugins](#plugin) sit on. This spans both the frontend and the backend, and includes core functionality such as declarative integration, config reading, database management, and many more. ## Bundle -A collection of [deployment artifacts](#deployment-artifacts). +1. A collection of [deployment artifacts](#deployment-artifacts). -Can also be: The output of the bundling process, which brings a collection of [packages](#package) into a single collection of [deployment artifacts](#deployment-artifacts). +2. The output of the bundling process, which brings a collection of [packages](#package) into a single collection of [deployment artifacts](#deployment-artifacts). ## Catalog -An organization's portfolio of software products managed in Backstage. +1. The core Backstage plugin that handle ingestion and display of your organizations software products. -Can also be: The core Backstage plugin that handle ingestion and display of your organizations software products. +2. An organization's portfolio of software products managed in Backstage. ## Cloud Native Computing @@ -73,39 +65,37 @@ Cloud Native Computing Foundation. [OAuth](#oauth) flow where the client receives an [authorization code](#code) that is passed to the backend to be exchanged for an [access token](#access-token) and possibly a [refresh token](#refresh-token). -## Collators (search plugin) +## Collator (search plugin) -Collators transform streams of [documents](#documents) into searchable texts. They're usually responsible for the data transformation and definition and collection process for specific [documents](#documents). Part of [Backstage Search](#search). +A transformer that takes streams of [documents](#documents) and outputs searchable texts. They're usually responsible for the data transformation and definition and collection process for specific [documents](#documents). ## Component (catalog plugin) -A software product that is managed in the Backstage [Software Catalog](#software-catalog). A component can be a service, website, library, data pipeline, or any other piece of software managed as a single project. - -https://backstage.io/docs/features/software-catalog/system-model +A software product that is managed in the Backstage [Software Catalog](#software-catalog). A component can be a service, website, library, data pipeline, or any other piece of software managed as a single project. See [the catalog docs](https://backstage.io/docs/features/software-catalog/system-model) for more information. ## Condition (permission plugin) Conditions are used to return a conditional decision from a policy. They contain information about a given entity and restrictions on what types of users can view that entity. -## Conditional decisions +A mapping from a given entity to criteria a user must fulfill to perform an action on that entity. Examples include `isOwner`, `hasRole`, etc. -[Rules](#permission-rules) need additional data before they can be used in a decision. Once a [rule](#permission-rule) is bound to relevant information it forms a [condition](#condition). Conditional decisions tell the [permission framework](#permission) to delegate evaluation to the [plugin](#plugin) that owns the corresponding [resource](#permission-resource). Permission requests that result in a conditional decision are allowed if all of the provided conditions evaluate to be true. +## Conditional Decision (permission plugin) + +A type of [decision](#policy-decision-permission-plugin) that allows for per-user evaluation of [conditions](#condition-permission-plugin) against a [resource](#resource-permission-plugin). See [Conditional Decisions](../permissions/concepts.md#conditional-decisions) ## Contributor A volunteer who helps to improve an OSS product such as Backstage. This volunteer effort includes coding, testing, technical writing, user support, and other work. A [user role](#user-role). -## Declarative integration +## Declarative Integration -A new paradigm for Backstage frontend plugins, allowing definition in config files instead of hosting complete React pages. +A new paradigm for Backstage frontend plugins, allowing definition in config files instead of hosting complete React pages. See [the Frontend System](https://backstage.io/docs/frontend-system). -https://backstage.io/docs/frontend-system +## Decorator (search plugin) -## Decorators (search plugin) +A transform stream. Decorators allow you to add additional information to documents outside of the [collator](#collator-search-plugin). They sit between the collators and the [indexers](#indexer-search-plugin) and can add extra fields to documents as they're being collated and indexed. -A transform stream. Decorators allow you to add additional information to documents outside of the [collator](#collators). They sit between the [collators](#collators) and the [indexers](#indexer) and can add extra fields to documents as they're being collated and indexed. - -Possible use cases for a decorator could be to bias search results or otherwise improve the search experience in your Backstage instance. Decorators can also be used to remove [metadata](#metadata), filter out, or even add extra documents at index-time. Part of [Backstage Search](#search). +Possible use cases for a decorator could be to bias search results or otherwise improve the search experience in your Backstage instance. Decorators can also be used to remove [metadata](#metadata), filter out, or even add extra documents at index-time. ## Deployment Artifacts @@ -121,19 +111,17 @@ A [user role](#user-role) defined as someone who uses a Backstage [app](#app). M A centralized system comprising a user interface and database used to facilitate and document all the software projects within an organization. Backstage is both a developer portal and (by virtue of being based on plugins) a platform for creating developer portals. -## Documents (search plugin) +## Document (search plugin) -An abstract concept representing something that can be found by searching for it. A document can represent a software entity, a TechDocs page, etc. Documents are made up of metadata fields, at a minimum -- a title, text, and location (as in a URL). Part of [Backstage Search](#search). +An abstract concept representing something that can be found by searching for it. A document can represent a software entity, a TechDocs page, etc. Documents are made up of metadata fields, at a minimum -- a title, text, and location (as in a URL). ## Domain -In the Backstage Catalog, a domain is an area that relates systems or entities to a business unit. - -https://backstage.io/docs/features/software-catalog/system-model +An area that relates systems or entities to a business unit. See [the catalog docs](https://backstage.io/docs/features/software-catalog/system-model) for more information. ## Entity -What is cataloged in the Backstage Software Catalog. An entity is identified by a unique combination of [kind](#Kind), [namespace](#Namespace), and name. +What is cataloged in the Backstage Software Catalog. An entity is identified by a unique combination of [kind](#Kind), [namespace](#Namespace), and name. See [the catalog docs](https://backstage.io/docs/features/software-catalog/system-model) for more information. ## Evaluator @@ -145,11 +133,11 @@ A [JWT](#jwt) used to prove a user's identity, containing for example the user's ## Index (search plugin) -An index is a collection of [documents](#documents) of a given type. Part of [Backstage Search](#search). +An index is a collection of [documents](#documents) of a given type. ## Indexer (search plugin) -A write stream of [documents](#documents). Part of [Backstage Search](#search). +A write stream of [documents](#documents). ## Integrator @@ -159,15 +147,19 @@ Someone who develops one or more plugins that enable Backstage to interoperate w JSON Web Token. -A popular JSON based token format that is commonly encrypted and/or signed, see [en.wikipedia.org/wiki/JSON_Web_Token](https://en.wikipedia.org/wiki/JSON_Web_Token) +A popular JSON based token format that is commonly encrypted and/or signed, see [the Wikipedia article](https://en.wikipedia.org/wiki/JSON_Web_Token) for more details. ## Kind Classification of an [entity](#Entity) in the Backstage Software Catalog, for example _service_, _database_, and _team_. -## Kubernetes Plugin +## Kubernetes (CNCF Project) -A plugin enabling configuration of Backstage on a Kubernetes cluster. Kubernetes plugin has been promoted to a Backstage core feature. +An open-source system for automating deployment, scaling, and management of containerized applications. + +## Kubernetes (Backstage plugin) + +A core Backstage plugin enabling a service owner-focused view of Kubernetes resources. ## Local Package @@ -175,13 +167,13 @@ One of the [packages](#package) within a [monorepo](#monorepo). These package ma ## Monorepo -A single repository for a collection of related software projects, such as all projects belonging to an organization. +1. A single repository for a collection of related software projects, such as all projects belonging to an organization. -Can also mean: A project layout that consists of multiple [packages](#package) within a single project, where packages are able to have local dependencies on each other. Often enabled through tooling such as [lerna](https://lerna.js.org/) and [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) +2. A project layout that consists of multiple [packages](#package) within a single project, where packages are able to have local dependencies on each other. Often enabled through tooling such as [lerna](https://lerna.js.org/) and [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) -## Namespace +## Namespace (catalog plugin) -In the Backstage Software Catalog, an optional attribute that can be used to organize [entities](#entity). +An optional attribute that can be used to organize [entities](#entity). ## Objective @@ -217,12 +209,10 @@ The declared role of a package, see [package roles](../local-dev/cli-build-syste ## Permission -A core Backstage plugin and framework that allows restriction of actions to specific users. +A core Backstage plugin and framework that allows restriction of actions to specific users. See [their docs](https://backstage.io/docs/permissions/overview) for more information. Any action that a user performs within Backstage may be represented as a permission. More complex actions, like executing a [software template](#software-templates), may require [authorization](#authorization) for multiple permissions throughout the flow. Permissions are identified by a unique name and optionally include a set of attributes that describe the corresponding action. [Plugins](#plugin) are responsible for defining and exposing the permissions they enforce as well as enforcing restrictions from the permission framework. -https://backstage.io/docs/permissions/overview - ## Persona (use cases) Alternative term for a [User Role](#user-role). @@ -235,9 +225,9 @@ A module in Backstage that adds a feature. All functionality outside of [the Bac User [permissions](#permission) are authorized by a central, user-defined [permission](#permission) policy. At a high level, a policy is a function that receives a Backstage user and [permission](#permission), and returns a decision to allow or deny. Policies are expressed as code, which decouples the framework from any particular [authorization](#authorization) model, like role-based access control (RBAC) or attribute-based access control (ABAC). -## Policy decision (permission plugin) +## Policy Decision (permission plugin) -Two important responsibilities of any authorization system are to decide if a user can do something, and to enforce that decision. In the Backstage permission framework, [policies](#policy) are responsible for decisions and [plugins](#plugin) (typically backends) are responsible for enforcing them. +A specific response to a user's request to perform an action on a list of [resources](#resource-permission-plugin). Can be either `Approve`, `Deny` or [`Conditional`](#conditional-decision-permission-plugin). ## Popup @@ -249,9 +239,9 @@ A set of actions that accomplish a goal, usually as part of a [use case](#Use-Ca ## Query Translators (search plugin) -An abstraction layer between a search engine and the [Backstage Search](#search) backend. Allows for translation into queries against your search engine. Part of [Backstage Search](#search). +An abstraction layer between a search engine and the [Backstage Search](#search) backend. Allows for translation into queries against your search engine. -## Refresh Token +## Refresh token A special token that an [OAuth](#oauth) client can use to get a new [access token](#access-token) when the latter expires. @@ -259,9 +249,7 @@ https://oauth.net/2/refresh-tokens/ ## Resource (catalog plugin) -In the Backstage Catalog, an [entity](#entity) that represents a piece of physical or virtual infrastructure, for example a database, required by a component. - -https://backstage.io/docs/features/software-catalog/system-model +An [entity](#entity) that represents a piece of physical or virtual infrastructure, for example a database, required by a component. See [the catalog docs](https://backstage.io/docs/features/software-catalog/system-model) for more information. ## Resource (permission plugin) @@ -287,9 +275,9 @@ A string that describes a certain type of access that can be granted to a user u A Backstage plugin that provides a framework for searching a Backstage [app](#app), including the [Software Catalog](#Software-Catalog) and [TechDocs](#TechDocs). A core feature of Backstage. -## Search Engine +## Search Engine (Backstage search) -Existing search technology that [Backstage Search](#search) can take advantage of through its modular design. Lunr is the default search in [Backstage Search](#search). Part of [Backstage Search](#search). +Existing search technology that [Backstage Search](#search) can take advantage of through its modular design. Lunr is the default search in Backstage Search. ## Software Catalog @@ -297,15 +285,13 @@ A Backstage plugin that provides a framework to keep track of ownership and meta ## Software Templates -A Backstage plugin with which to create [components](#component) in Backstage. A core feature of Backstage. Also known as the scaffolder. +1. A Backstage plugin with which to create [components](#component) in Backstage. A core feature of Backstage. Also known as the scaffolder. -Can also refer to: A "skeleton" software project created and managed in the Backstage Software Templates tool. +2. A "skeleton" software project created and managed in the Backstage Software Templates tool. -## System +## System (catalog plugin) -In the Backspace Catalog, a system is a collection of [entities](#entity) that cooperate to perform a function. A system generally provides one or a few public APIs and consists of a handful of components, resources and private APIs. - -https://backstage.io/docs/features/software-catalog/system-model +A system is a collection of [entities](#entity) that cooperate to perform a function. A system generally provides one or a few public APIs and consists of a handful of components, resources and private APIs. See [the catalog docs](https://backstage.io/docs/features/software-catalog/system-model) for more information. ## Task (use cases) diff --git a/docs/references/writing-a-glossary-entry.md b/docs/references/writing-a-glossary-entry.md new file mode 100644 index 0000000000..ecc9f22bd1 --- /dev/null +++ b/docs/references/writing-a-glossary-entry.md @@ -0,0 +1,83 @@ + + +## Entry format + +A glossary entry should consist of two required things and two optional things, + +1. a header, +2. a sentence defining what the thing is, and +3. an optional additional sentence or two giving more context into what the thing is and possible pointers on where to find more information, and possibly +4. links out to additional information. + +### The header + +The header (and first sentence) are the way users will discover your entry. The header has two parts, + +1. The actual term, this should be as minimal as possible. You can fit more information into the body of the entry. +2. A disambiguator, this allows users to understand when certain entries are context specific or may have different meanings in different contexts. + +### The term + +Think of this as a dictionary. Single words are the base units and most definitions refer to a single word. Acronyms are acceptable. An adjective and a noun are also useful, `conditional decision`, `backstage framework`, etc. After 3 or _maybe_ 4 words, you should be trying to simplify and place more content in your entry instead. + +In the title, your term should be in Title Case. It should also be in the singular. + +### The disambiguator + +The goal of a disambiguator is to differentiate terms that may have context specific meanings. This can have two interpretations, either + +1. There are multiple terms and we need to create clear boundaries between their contexts, or +2. There are single terms that have meanings that are specific to a single context. + +A good example for the first would be resources. Both the catalog plugin and permission plugin have the idea of resources, but they do not refer to the same thing. + +A good example for the second would be `Query translators`. In our case, this refers to _search_ query translators, but it may refer to database query translators or the latter. By disambiguating early, we avoid confusion. + +Beyond the above advice, there are no strong rules for when or when not to use a disambiguator. It is up to the entry writer and the reviewer. + +Your disambiguator should be short, but need not be a single word -- examples include "use cases", "search plugin", "catalog plugin". When used the disambiguator should have the following form, `({disambiguator})` (a parenthesis enclosed term) and will sit to the right of the title. Your disambiguator should use lower case. + +### Putting it together + +Your title should look like `{word} ({disambiguator})`. Entries are not nested besides the disambiguator and should sit at `##`. + +## The first sentence + +Your first sentence should include the what for your word. Your goal should be to answer the question, "What is x?". Do _not_ use the word in your first sentence. If you are using other words in the glossary in your definition, you should reference them following [the Referencing section](#referencing). + +If you have a term that could mean multiple things in the same context or the context is difficult to add boundaries for, you should separate each meaning into a separate section of the entry using an ordered list. For example, + +```md +## Bundle + +1. A deployment artifact. +2. A collection of packages. +``` + +## Additional sentences + +You may not be able to fully define what you want in a single sentence. Use more sentences to flush out the meaning; however, if you start to get into the weeds, you should reconsider rehousing that information into a plugin specific "concepts" section. It's okay for words to be duplicated across both if the concepts section adds meaningful technical or architectural discussion. + +## Linking out to additional resources + +If the term you're defining has a better or more in depth source for that information, link to it. This can include plugin specific concept documents, external documentation, or core framework documentation. + +You should format these links as `See [link1 title](link1.url) for more details`. Additional links beyond the first one should be appended with `and` or `or` as necessary. + +## Putting it all together + +```md +## Component (catalog plugin) + +A software product that is managed in the Backstage [Software Catalog](#software-catalog). A component can be a service, website, library, data pipeline, or any other piece of software managed as a single project. See [the catalog docs](https://backstage.io/docs/features/software-catalog/system-model) for more information. +``` + +## Referencing + +### In the glossary + +You should reference often. Words are defined recursively, especially in tech and un-nesting some terms requires additional glossary items. It's okay if your terms require multiple other terms to build on. Your goal with referencing is to provide small reusable words that you can trust users know the definition of. + +### In the text + +You should reference (and create a new entry if it doesn't exist) whenever you see a new word that a reasonable reader may not know. If you've already added a reference in your current passage -- in the glossary this will be your entry -- don't add a new reference. References should point initially to the glossary and if there is additional information (like a concepts page), the glossary should have a link to that page. From f0192acb7092ab85f57264a795de2df18ea17ff9 Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 30 Jan 2024 22:29:46 -0500 Subject: [PATCH 189/468] fix: condition definition Signed-off-by: Aramis --- docs/references/glossary.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/references/glossary.md b/docs/references/glossary.md index 165c000fc8..773e7bfb50 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -75,8 +75,6 @@ A software product that is managed in the Backstage [Software Catalog](#software ## Condition (permission plugin) -Conditions are used to return a conditional decision from a policy. They contain information about a given entity and restrictions on what types of users can view that entity. - A mapping from a given entity to criteria a user must fulfill to perform an action on that entity. Examples include `isOwner`, `hasRole`, etc. ## Conditional Decision (permission plugin) From ddfeada98095ee2fd1de4f4fa844f31c85f036bd Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 30 Jan 2024 22:43:23 -0500 Subject: [PATCH 190/468] remove lengthy definitions Signed-off-by: Aramis --- docs/permissions/concepts.md | 8 ++++++++ docs/references/glossary.md | 12 ++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/permissions/concepts.md b/docs/permissions/concepts.md index 8e348373cf..7c8b9e9f69 100644 --- a/docs/permissions/concepts.md +++ b/docs/permissions/concepts.md @@ -4,6 +4,14 @@ title: Concepts description: A list of important permission framework concepts --- +### Permission + +Any action that a user performs within Backstage may be represented as a permission. More complex actions, like executing a [software template](../references/glossary.md#software-templates), may require [authorization](../references/glossary.md#authorization) for multiple permissions throughout the flow. Permissions are identified by a unique name and optionally include a set of attributes that describe the corresponding action. [Plugins](../references/glossary.md#plugin) are responsible for defining and exposing the permissions they enforce as well as enforcing restrictions from the permission framework. + +### Policy + +User [permissions](../references/glossary.md#permission-permission-plugin) are authorized by a central, user-defined permission policy. At a high level, a policy is a function that receives a Backstage user and permission, and returns a decision to allow or deny. Policies are expressed as code, which decouples the framework from any particular [authorization](../references/glossary.md#authorization) model, like role-based access control (RBAC) or attribute-based access control (ABAC). + ### Policy decision versus enforcement Two important responsibilities of any authorization system are to decide if a user can do something, and to enforce that decision. In the Backstage permission framework, policies are responsible for decisions and plugins (typically backends) are responsible for enforcing them. diff --git a/docs/references/glossary.md b/docs/references/glossary.md index 773e7bfb50..45c2bbcd35 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -91,9 +91,7 @@ A new paradigm for Backstage frontend plugins, allowing definition in config fil ## Decorator (search plugin) -A transform stream. Decorators allow you to add additional information to documents outside of the [collator](#collator-search-plugin). They sit between the collators and the [indexers](#indexer-search-plugin) and can add extra fields to documents as they're being collated and indexed. - -Possible use cases for a decorator could be to bias search results or otherwise improve the search experience in your Backstage instance. Decorators can also be used to remove [metadata](#metadata), filter out, or even add extra documents at index-time. +A transform stream that allows you to add additional information to [documents](#document-search-plugin). ## Deployment Artifacts @@ -205,11 +203,13 @@ A service that hosts [packages](#package). The most prominent example is [NPM](h The declared role of a package, see [package roles](../local-dev/cli-build-system.md#package-roles). -## Permission +## Permission (core Backstage plugin) A core Backstage plugin and framework that allows restriction of actions to specific users. See [their docs](https://backstage.io/docs/permissions/overview) for more information. -Any action that a user performs within Backstage may be represented as a permission. More complex actions, like executing a [software template](#software-templates), may require [authorization](#authorization) for multiple permissions throughout the flow. Permissions are identified by a unique name and optionally include a set of attributes that describe the corresponding action. [Plugins](#plugin) are responsible for defining and exposing the permissions they enforce as well as enforcing restrictions from the permission framework. +## Permission (permission plugin) + +A restriction on any action that a user can perform against a specific [resource](#resource-permission-plugin) or set of resources. See [the permission framework docs](../permissions/concepts.md#permission) for more details. ## Persona (use cases) @@ -221,7 +221,7 @@ A module in Backstage that adds a feature. All functionality outside of [the Bac ## Policy (permission plugin) -User [permissions](#permission) are authorized by a central, user-defined [permission](#permission) policy. At a high level, a policy is a function that receives a Backstage user and [permission](#permission), and returns a decision to allow or deny. Policies are expressed as code, which decouples the framework from any particular [authorization](#authorization) model, like role-based access control (RBAC) or attribute-based access control (ABAC). +A construct that takes in a Backstage user and a [permission](#permission-permission-plugin) and returns a [policy decision](#policy-decision-permission-plugin). ## Policy Decision (permission plugin) From a5f3d839ecff924a764bfeaa579973dec3329465 Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 30 Jan 2024 22:43:30 -0500 Subject: [PATCH 191/468] fix links issue Signed-off-by: Aramis --- docs/references/writing-a-glossary-entry.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/references/writing-a-glossary-entry.md b/docs/references/writing-a-glossary-entry.md index ecc9f22bd1..90a13e9675 100644 --- a/docs/references/writing-a-glossary-entry.md +++ b/docs/references/writing-a-glossary-entry.md @@ -62,7 +62,13 @@ You may not be able to fully define what you want in a single sentence. Use more If the term you're defining has a better or more in depth source for that information, link to it. This can include plugin specific concept documents, external documentation, or core framework documentation. -You should format these links as `See [link1 title](link1.url) for more details`. Additional links beyond the first one should be appended with `and` or `or` as necessary. +You should format these links as + +```md +See [the glossary](./glossary.md) for more details. +``` + +. Additional links beyond the first one should be appended with `and` or `or` as necessary. ## Putting it all together From 09a9c95f7963a2ea91d45a014280a11f68252739 Mon Sep 17 00:00:00 2001 From: AmbrishRamachandiran Date: Wed, 31 Jan 2024 12:07:05 +0530 Subject: [PATCH 192/468] Add intro to azure sites plugin Signed-off-by: AmbrishRamachandiran --- .changeset/cool-islands-impress.md | 5 +++++ plugins/azure-sites/README.md | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/cool-islands-impress.md diff --git a/.changeset/cool-islands-impress.md b/.changeset/cool-islands-impress.md new file mode 100644 index 0000000000..df6df1ada2 --- /dev/null +++ b/.changeset/cool-islands-impress.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-sites': patch +--- + +Updated README diff --git a/plugins/azure-sites/README.md b/plugins/azure-sites/README.md index a19b314b77..21e97d348e 100644 --- a/plugins/azure-sites/README.md +++ b/plugins/azure-sites/README.md @@ -1,5 +1,7 @@ # Azure Sites Plugin +Azure Sites (Apps & Functions) plugin support for a given entity. View the current status of the site, quickly jump to site's Overview page, or Log Stream page. + ![preview of Azure table](docs/functions-table.png) _Inspired by [roadie.io AWS Lamda plugin](https://roadie.io/backstage/plugins/aws-lambda/)_ From 86f2ff6180972aee8db52f9c383231cd76366df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 31 Jan 2024 10:37:04 +0100 Subject: [PATCH 193/468] make it possible to force releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .github/workflows/deploy_packages.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 0dc17bbae6..9cd1b22008 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -1,6 +1,12 @@ name: Deploy Packages on: workflow_dispatch: + inputs: + force_release: + description: 'Unconditionally trigger the release job to run' + required: true + default: false + type: boolean push: branches: [master, patch/*] @@ -124,7 +130,7 @@ jobs: release: needs: build - if: needs.build.outputs.needs_release == 'true' + if: needs.build.outputs.needs_release == 'true' || inputs.force_release == 'true' runs-on: ubuntu-latest From 6ba563769cb1b816da0b135ed50f93838fdaae7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 31 Jan 2024 10:48:05 +0100 Subject: [PATCH 194/468] update release script and docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .github/workflows/deploy_packages.yml | 1 + docs/publishing.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 9cd1b22008..5d0952bf87 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -82,6 +82,7 @@ jobs: run: git fetch origin '${{ github.event.before }}' - name: Check if release + if: inputs.force_release != 'true' id: release_check run: node scripts/check-if-release.js env: diff --git a/docs/publishing.md b/docs/publishing.md index 15813275de..f90766e7eb 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -140,4 +140,4 @@ process is used to release an emergency fix as version `6.5.1` in the patch rele ### When the release workflow is not triggered for some reason, such as a GitHub incident -Ask one of the maintainers to force push master back to a previous commit and then push the release merge commit again. +Ask one of the maintainers to trigger [the Deploy packages](https://github.com/backstage/backstage/actions/workflows/deploy_packages.yml) workflow with the "Unconditionally trigger the release job to run" checkbox set, on the `master` branch. Please validate first that nothing substantial has been pushed to master since the original failed release attempt! For this reason, it is wise to have the master branch locked until each release has gone through. From 34c25a91653d511e5ac59a18879a4460c05855eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 31 Jan 2024 11:06:11 +0100 Subject: [PATCH 195/468] string comparisons for bools - who'd've thunk it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .github/workflows/deploy_packages.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 5d0952bf87..ca037d50b6 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -4,8 +4,7 @@ on: inputs: force_release: description: 'Unconditionally trigger the release job to run' - required: true - default: false + required: false type: boolean push: branches: [master, patch/*] From 78887c60236ddc639e38836b9a1abe8efccfb671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 31 Jan 2024 11:18:45 +0100 Subject: [PATCH 196/468] alright let's try the proper booleans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .github/workflows/deploy_packages.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index ca037d50b6..5bb48b92f5 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: inputs: force_release: - description: 'Unconditionally trigger the release job to run' + description: Unconditionally trigger the release job required: false type: boolean push: @@ -81,7 +81,7 @@ jobs: run: git fetch origin '${{ github.event.before }}' - name: Check if release - if: inputs.force_release != 'true' + if: inputs.force_release != true id: release_check run: node scripts/check-if-release.js env: @@ -130,7 +130,7 @@ jobs: release: needs: build - if: needs.build.outputs.needs_release == 'true' || inputs.force_release == 'true' + if: needs.build.outputs.needs_release == 'true' || inputs.force_release == true runs-on: ubuntu-latest From 998ccf6620f35cd9a576350fb004bf7884e7ef19 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Wed, 31 Jan 2024 10:47:22 +0000 Subject: [PATCH 197/468] app-backend: replace all instances when injecting config Signed-off-by: MT Lewis --- .changeset/red-eggs-serve.md | 5 ++ plugins/app-backend/src/lib/config.test.ts | 73 ++++++++++++++++++++++ plugins/app-backend/src/lib/config.ts | 6 +- 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 .changeset/red-eggs-serve.md diff --git a/.changeset/red-eggs-serve.md b/.changeset/red-eggs-serve.md new file mode 100644 index 0000000000..1b2c9b3b07 --- /dev/null +++ b/.changeset/red-eggs-serve.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app-backend': patch +--- + +Support injecting config multiple times in a single bundle diff --git a/plugins/app-backend/src/lib/config.test.ts b/plugins/app-backend/src/lib/config.test.ts index f36052116a..ed2b8b8777 100644 --- a/plugins/app-backend/src/lib/config.test.ts +++ b/plugins/app-backend/src/lib/config.test.ts @@ -62,6 +62,28 @@ describe('injectConfig', () => { expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]))).toEqual([]); }); + it('should inject config repeatedly if marker appears multiple times', async () => { + fsMock.readdir.mockResolvedValue(['main.js'] as any); + readFileMock.mockImplementation( + async () => + '({a:"__APP_INJECTED_RUNTIME_CONFIG__",b:"__APP_INJECTED_RUNTIME_CONFIG__"})', + ); + await injectConfig(baseOptions); + expect(fsMock.readdir).toHaveBeenCalledTimes(1); + expect(fsMock.readFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledWith( + resolvePath(MOCK_DIR, 'main.js'), + '({a:/*__APP_INJECTED_CONFIG_MARKER__*/"[]"/*__INJECTED_END__*/,b:/*__APP_INJECTED_CONFIG_MARKER__*/"[]"/*__INJECTED_END__*/})', + 'utf8', + ); + + // eslint-disable-next-line no-eval + expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]).a)).toEqual([]); + // eslint-disable-next-line no-eval + expect(JSON.parse(eval(fsMock.writeFile.mock.calls[0][1]).b)).toEqual([]); + }); + it('should find the correct file to inject', async () => { fsMock.readdir.mockResolvedValue([ 'before.js', @@ -152,4 +174,55 @@ describe('injectConfig', () => { { data: { x: 1, y: 2 }, context: 'test' }, ]); }); + + it('should re-inject config repeatedly if needed', async () => { + fsMock.readdir.mockResolvedValue(['main.js'] as any); + readFileMock.mockResolvedValue( + '({ a: JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__"), b: JSON.parse("__APP_INJECTED_RUNTIME_CONFIG__") })', + ); + + await injectConfig({ + ...baseOptions, + appConfigs: [{ data: { x: 0 }, context: 'test' }], + }); + + expect(fsMock.writeFile).toHaveBeenCalledTimes(1); + expect(fsMock.writeFile).toHaveBeenCalledWith( + resolvePath(MOCK_DIR, 'main.js'), + '({ a: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/), b: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":0},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/) })', + 'utf8', + ); + + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[0][1]).a).toEqual([ + { data: { x: 0 }, context: 'test' }, + ]); + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[0][1]).b).toEqual([ + { data: { x: 0 }, context: 'test' }, + ]); + + readFileMock.mockResolvedValue(fsMock.writeFile.mock.calls[0][1]); + + await injectConfig({ + ...baseOptions, + appConfigs: [{ data: { x: 1, y: 2 }, context: 'test' }], + }); + + expect(fsMock.writeFile).toHaveBeenCalledTimes(2); + expect(fsMock.writeFile).toHaveBeenLastCalledWith( + resolvePath(MOCK_DIR, 'main.js'), + '({ a: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":1,\\"y\\":2},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/), b: JSON.parse(/*__APP_INJECTED_CONFIG_MARKER__*/"[{\\"data\\":{\\"x\\":1,\\"y\\":2},\\"context\\":\\"test\\"}]"/*__INJECTED_END__*/) })', + 'utf8', + ); + + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[1][1]).a).toEqual([ + { data: { x: 1, y: 2 }, context: 'test' }, + ]); + // eslint-disable-next-line no-eval + expect(eval(fsMock.writeFile.mock.calls[1][1]).b).toEqual([ + { data: { x: 1, y: 2 }, context: 'test' }, + ]); + }); }); diff --git a/plugins/app-backend/src/lib/config.ts b/plugins/app-backend/src/lib/config.ts index 05b304075e..63912df5c4 100644 --- a/plugins/app-backend/src/lib/config.ts +++ b/plugins/app-backend/src/lib/config.ts @@ -49,7 +49,7 @@ export async function injectConfig( if (content.includes('__APP_INJECTED_RUNTIME_CONFIG__')) { logger.info(`Injecting env config into ${jsFile}`); - const newContent = content.replace( + const newContent = content.replaceAll( '"__APP_INJECTED_RUNTIME_CONFIG__"', injected, ); @@ -58,8 +58,8 @@ export async function injectConfig( } else if (content.includes('__APP_INJECTED_CONFIG_MARKER__')) { logger.info(`Replacing injected env config in ${jsFile}`); - const newContent = content.replace( - /\/\*__APP_INJECTED_CONFIG_MARKER__\*\/.*\/\*__INJECTED_END__\*\//, + const newContent = content.replaceAll( + /\/\*__APP_INJECTED_CONFIG_MARKER__\*\/.*?\/\*__INJECTED_END__\*\//g, injected, ); await fs.writeFile(path, newContent, 'utf8'); From 4d3cbf7877a9eb100c995a7c9ec3472487a17aee Mon Sep 17 00:00:00 2001 From: Martin Marosi Date: Thu, 25 Jan 2024 15:59:44 +0100 Subject: [PATCH 198/468] bep: 0002 module federation dependency sharing testing Signed-off-by: Martin Marosi --- beps/0002-dynamic-frontend-plugins/README.md | 51 +++++++++++++++++- .../scope-sharing.png | Bin 0 -> 256797 bytes 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 beps/0002-dynamic-frontend-plugins/scope-sharing.png diff --git a/beps/0002-dynamic-frontend-plugins/README.md b/beps/0002-dynamic-frontend-plugins/README.md index fc56e74c83..17eca54a40 100644 --- a/beps/0002-dynamic-frontend-plugins/README.md +++ b/beps/0002-dynamic-frontend-plugins/README.md @@ -149,10 +149,57 @@ The outcome of initial testing is positive and it is possible to mix and match d The experimental code can be found in [this repository](https://github.com/scalprum/mf-mixing-experiments). -**The testing so far was done only on very simple modules**. Although core React features are working (Context API and hooks), more testings needs to be done in order to declare this approach 100% reliable. - So far a lot of custom code needs to be written to bridge Webpack, Rspack, @module-federation/enhanced with Vite. The first three are compatible out of the box, but Vite requires extra bridge to be able to consume/provide modules with/to other builds. +#### React context and singleton sharing + +We can share React context and its values. Meaning a shell application (or a plugin parent) can have a context provider and a plugin will consume the context value. + +An example is this [package](https://github.com/scalprum/mf-mixing-experiments/tree/master/shared-package) in the experiment repo. + +The shell apps supply the provider and remote modules consume it. There are no issues with any combination of tooling. + +#### Optimized module sharing + +Module sharing optimizations are also working nicely. ([Optimization description](https://medium.com/@marvusm.mmi/webpack-module-federation-think-twice-before-sharing-a-dependency-18b3b0e352cb)) + +Mixing shared scope is working between various modules using various build tools. + +Sample configuration: +https://github.com/scalprum/mf-mixing-experiments/blob/master/mixed-remote-modules-collection/webpack.config.js#L27-L41 + +```js +const plugin = new ModuleFederationPlugin({ + ... + shared: { + '@mui/material/Button': { + requiredVersion: '>=5.0.0', + version: '5.15.6', + }, + '@mui/material/TextField': { + requiredVersion: '>=5.0.0', + version: '5.15.6', + }, + '@mui/material/Typography': { + requiredVersion: '>=5.0.0', + version: '5.15.6', + }, + '@mui/material/Divider': { + requiredVersion: '>=5.0.0', + version: '5.15.6', + }, + ... + }, +}); + +``` + +This config ensures that only those modules (from @mui/material) that are used in the code will be shared. If a relative imports and the entire dependency name is used, the entire dependency will be shared, regardless of which modules are consumed. Tree shaking does not work when an entire dependency is shared! Explanation of why is described [here](https://medium.com/@marvusm.mmi/webpack-module-federation-think-twice-before-sharing-a-dependency-18b3b0e352cb). + +This can be checked by debugging network traffic and shared scopes: + +![notifications system architecture diagram](./scope-sharing.png) + ### Plugin manifest Each plugin should have a manifest file with important metadata. This metadata is used to load the remote assets to browser. The plugin manifest should be part of a build output. diff --git a/beps/0002-dynamic-frontend-plugins/scope-sharing.png b/beps/0002-dynamic-frontend-plugins/scope-sharing.png new file mode 100644 index 0000000000000000000000000000000000000000..40acfa5769619a776cb09fd22e79ce87c0fb7cf9 GIT binary patch literal 256797 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%0Db9LjRTDJK*= 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
  • {)*Nwk|Mc5wWMGKRhZzzz**T6V{aUa?F?bq5+of?nD>?S;GPh zt!{T7VvCB`3~EA&L?ua>>{k1x^rkP-Xf{WW#qRFtYN+lDiHnRZYe<%UI1o@yO5N`? zH2*OM+Y9Bj?-9`3pM3*$(sMXcp_ol!$(pyis6R8ajW{<$V&*W$QeAPn<+mAp=aW~? zplc#<>bB2;hr{>yaZ?w0^!{u5={vfdSFb`a3>+^PbL}0ATZ_X=ou)@PdYcNyFW+(-Y>@JT-mT%JnnKK7kXYGfL7lm)P=FM z4bFmMAwN+!WwGb0^yOSGTl zuP(;DR_Vlp>AvuIs);urVUGj?7wZe}RZO8XiRw`Kx1=bXYFNJGKLNRF(c%7Tggq@s zg}ntJ7u|UEmzAFns58U-l0#e%j{b>5S_ZA~`&gc|DiCiI!s94AEg< zT*QhK(p+B-Ez64V1AeSCJO*f#q^ZjBX)TmA;XT9KD5qIqFUZ1_a{>CC(lVc+#t%?8<@5oq)lEKe9(;Ec-At^c`L-4QG9EGubT--Puyu3d4@+Da_6|}=u zICDm45`fm?vQIrB4^ed6T*9hrWdqfMbe_bj_41Rqje|q_U(?glEl-?ZFjj1N_N-+mkOD+1 z8qubdGT*TaL)I9g1`>%69={|(&4MMnBqb#*98?PwTY5f=fF7#sB2K^!#`4g9mB%%sS>OTQ&;u9pLKpW)8|YVwqtHxyKSHT5gT& zu)&7kE9B9Fs6?6k{RRq*`wRIH$baMbCWOTB+X{d&a)b-ef}R^+AX z@E!Iqw~lek1#CH=dwtz#chnScA09l3Y-@8gKPnF(t1qmW(o%s~G{7bVizC#jMKIOv z3kq&3PZL^?%!oRIC)W%}ZJ{Stfl|ChvUCRLjoBn)p}S27m2rEU(d%`4F08HO8O(L0 z?)OxA3RcPJ!Cb)#WKVA=aux_`))!oJto`h!U(I$PHwT;=QVt#vGhBqg8N11*tWLxI z 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 literal 0 HcmV?d00001 From 8b269e1879b580bc29b5852421cb33b29e3b8d40 Mon Sep 17 00:00:00 2001 From: Martin Wallgren Date: Wed, 31 Jan 2024 10:06:30 +0100 Subject: [PATCH 199/468] Scaffolder-backend-gerrit: Set branches to defaultBranch When a project is created in `publish:gerrit` the default branch needs to be provided to the Gerrit API that creates the project. If the branches attribute is not provided, the default branch for the project will be set to whatever is configured as default on the Gerrit host. Reference: https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#project-input Signed-off-by: Martin Wallgren --- .changeset/short-cherries-mix.md | 5 ++ .../src/actions/gerrit.test.ts | 55 +++++++++++++++++++ .../src/actions/gerrit.ts | 5 +- 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .changeset/short-cherries-mix.md diff --git a/.changeset/short-cherries-mix.md b/.changeset/short-cherries-mix.md new file mode 100644 index 0000000000..866f7a0971 --- /dev/null +++ b/.changeset/short-cherries-mix.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gerrit': patch +--- + +Provide default branch when creating repositories. diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.test.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.test.ts index d05c8727c4..2bd7783cb6 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.test.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.test.ts @@ -101,6 +101,7 @@ describe('publish:gerrit', () => { 'Basic Z2Vycml0dXNlcjp1c2VydG9rZW4=', ); expect(req.body).toEqual({ + branches: ['master'], create_empty_commit: false, owners: ['owner'], description, @@ -150,6 +151,7 @@ describe('publish:gerrit', () => { 'Basic Z2Vycml0dXNlcjp1c2VydG9rZW4=', ); expect(req.body).toEqual({ + branches: ['master'], create_empty_commit: false, owners: ['owner'], description, @@ -200,6 +202,7 @@ describe('publish:gerrit', () => { 'Basic Z2Vycml0dXNlcjp1c2VydG9rZW4=', ); expect(req.body).toEqual({ + branches: ['master'], create_empty_commit: false, owners: [], description, @@ -241,6 +244,58 @@ describe('publish:gerrit', () => { 'https://gerrithost.org/repo/+/refs/heads/master', ); }); + + it('can correctly create a new project with main as default branch', async () => { + expect.assertions(5); + server.use( + rest.put('https://gerrithost.org/a/projects/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic Z2Vycml0dXNlcjp1c2VydG9rZW4=', + ); + expect(req.body).toEqual({ + branches: ['main'], + create_empty_commit: false, + owners: [], + description, + parent: 'workspace', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'gerrithost.org?workspace=workspace&repo=repo', + defaultBranch: 'main', + }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gerrithost.org/a/repo', + defaultBranch: 'main', + auth: { username: 'gerrituser', password: 'usertoken' }, + logger: mockContext.logger, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: {}, + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://gerrithost.org/a/repo', + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gerrithost.org/repo/+/refs/heads/main', + ); + }); + it('should not create new projects on dryRun', async () => { await action.handler({ ...mockContext, diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts index 6f729227e6..102d48a706 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts @@ -38,15 +38,17 @@ const createGerritProject = async ( parent: string; owner?: string; description: string; + defaultBranch: string; }, ): Promise => { - const { projectName, parent, owner, description } = options; + const { projectName, parent, owner, description, defaultBranch } = options; const fetchOptions: RequestInit = { method: 'PUT', body: JSON.stringify({ parent, description, + branches: [defaultBranch], owners: owner ? [owner] : [], create_empty_commit: false, }), @@ -221,6 +223,7 @@ export function createPublishGerritAction(options: { owner: owner, projectName: repo, parent: workspace, + defaultBranch, }); const auth = { username: integrationConfig.config.username!, From e04887887c5d2bb4c2527d6091d85a6e128f4e4c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 31 Jan 2024 13:26:42 +0100 Subject: [PATCH 200/468] beps/0002: refine summary/motivation/goals Signed-off-by: Patrik Oldsberg --- beps/0002-dynamic-frontend-plugins/README.md | 33 +++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/beps/0002-dynamic-frontend-plugins/README.md b/beps/0002-dynamic-frontend-plugins/README.md index fc56e74c83..0f38b635c8 100644 --- a/beps/0002-dynamic-frontend-plugins/README.md +++ b/beps/0002-dynamic-frontend-plugins/README.md @@ -37,11 +37,11 @@ When editing BEPs, aim for tightly-scoped, single-topic PRs to keep discussions The summary of the BEP is a few paragraphs long and give a high-level overview of the features to be implemented. It should be possible to read *only* the summary and understand what the BEP is proposing to accomplish and what impact it has for users. --> -The dynamic frontend plugins feature is a way of loading additional frontend plugins at runtime, without the requirement of rebuilding and restarting a running backstage instance. +The dynamic frontend plugins feature is a way of loading additional frontend plugins at runtime, without the requirement of rebuilding and restarting a running Backstage instance. It also provides a method for packaging and distributing plugins as standalone artifacts, which can be installed directly into Backstage. -This system should significantly improve frontend plugin management for backstage instances. +This system should significantly improve frontend plugin management for Backstage instances, and makes it possible to deploy changes to the app without rebuilding the app itself. -The dynamic plugins leverage the declarative UI system to define what a plugin is and how it should be represented in the browser. +The dynamic plugins leverage the declarative nature of the new frontend system to define what a plugin is and how it is integrated into the rest of the app. ## Motivation @@ -50,6 +50,10 @@ This section is for explicitly listing the motivation, goals, and non-goals of this BEP. Describe why the change is important and the benefits to users. --> +Being able to dynamically install plugins unlocks new ways of deploying and managing Backstage, and has the potential to hugely improve adoption by lowering the barrier of entry. A Backstage installation currently requires quite a lot of care to maintain, meaning it may not be worth the investment for smaller organizations. By making it possible to set up and maintain a Backstage instance without the need to manage a codebase, we can make Backstage more accessible to a wider audience. + +The ability to dynamically install plugins also allows for more isolated development and deployment of plugins. This can benefit organizations with large Backstage projects, where splitting the codebase into multiple smaller projects can improve development experience and autonomy. + ### Goals -- discover and choose tooling to enable dynamic frontend plugins -- easy way of converting existing frontend plugins to be dynamic -- turning on and off UI plugins at runtime without the requirement of rebuilding or restarting backstage -- manage dynamic plugins declaratively +The overarching goal of this proposal is to outline the full path of how frontend plugin code in a repository makes it way into an existing Backstage app. As part of this, we define the following: -An additional goal might be exploring the scope of a "on demand" rending and loading. Currently all plugins, or at least some portion of them must be loaded at UI bootstrap. This approach is not fully dynamic and might be considered a waste of resources. Fully optimizing dynamic plugins loading would require additional changes to the new UI system. +- **Bundling**: How plugin code is packaged into a portable artifact. +- **Distribution**: How these artifacts are deployed or published and made available for apps to load. +- **Loading**: How an app is able to load these artifacts from remote or local sources at runtime. + +Each of these may have multiple possible solutions, in particular the loading and distribution steps. This proposal should aim to provide the minimum solutions that avoids the need for each adopter to have to re-bundle open source plugins, while still making it simple to use dynamic installation for their own internal plugins. + +There are a couple of sub-goals that are important for this to work: + +- Discover and choose the underlying tooling to enable dynamic frontend plugins. +- An easy way to package existing frontend plugins for dynamic installation. +- Reconfiguring the installed plugins at runtime without rebuilding the app, either declaratively or through code. ### Non-Goals @@ -71,6 +82,12 @@ What is out of scope for this BEP? Listing non-goals helps to focus discussion and make progress. --> +The **integration** of installed plugins and features is not in scope for this proposal, that is the responsibility of the new frontend system. + +This proposal does not contain any form of visual interface for managing dynamically installed plugins. The scope of this proposal only includes configuration of dynamic plugins through static configuration and TypeScript interfaces. + +This proposal does not aim to make it possible to add or remove plugins into an already running frontend app instance as created by `createApp` from the Backstage core APIs. The page must be reloaded for any updates to take effect. + ## Proposal +- Notification frontend utilizes [Web Notification API](https://developer.mozilla.org/en-US/docs/Web/API/Notification) to notify user for new notifications +- Unread notifications count is displayed in the title of the page +- Configuration can be used to enable or disable features in the notification system (Web Notification API, title change, etc.) +- Replace absent signal service with long polling. This requires changes to the `signals` plugin as well. +- Notifications can have priority that is used to determine how notifications are shown to the user ## Release Plan @@ -251,12 +372,12 @@ The notification and signal plugins are released as two new plugins in the Backs For the notification plugin to reach a stable release we much reach the following: - A stable notifications payload format. -- A stable notifications receiver filter format. +- A stable notifications recipient filter format. - The event broker must have at least one implementation that supports scaled deployments. For the signal plugin to reach a stable release we much reach the following: -- A stable signal receiver filter format. +- A stable signal recipient filter format. - A stable signal channel API in the frontend. If any changes are required to the frontend framework to facilitate the implementation of notifications or signals, these will be released as experimental alpha features. They will stay in alpha until they are deemed stable enough, which must happen before a stable release of the notifications system. @@ -265,6 +386,8 @@ If any changes are required to the frontend framework to facilitate the implemen Since the signal plugin relies on the event broker for communication, it is a dependency for the notifications system as a whole. The event broker does not currently implement any transport for scaled deployments, which is a requirement for scaled deployments of the notification system. +Alternatively the notifications can work without the signals, but in this case the notifications are updated only during page refresh. + ## Alternatives ### Signal Plugin Separation From 3af10366c3ce9944b74aa512a91999b1cf6e2cb7 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Fri, 2 Feb 2024 12:16:58 +0200 Subject: [PATCH 230/468] beps: fix review comments Signed-off-by: Heikki Hellgren --- beps/0001-notifications-system/README.md | 65 ++++++++++++------------ 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/beps/0001-notifications-system/README.md b/beps/0001-notifications-system/README.md index f3e98f107d..726a9ff9a6 100644 --- a/beps/0001-notifications-system/README.md +++ b/beps/0001-notifications-system/README.md @@ -80,6 +80,14 @@ In the frontend the signal plugin has a persistent connection to the signal back In order to route signals from the sender to the intended recipient the signal plugin uses the concept of signaling channels. They are just like the topics on the message bus, but we use separate terminology in order to avoid confusion. They also sit one layer beneath the even topic in the protocol stack, where the signal channel is communicated as part of the event payload. In the frontend, the signal plugin exposes an API to subscribe to a signal channel. Each time the user receives a signal on the specified channel, a listener is called with the signal payload. +Upon creating a new notification, a signal will be emitted by the `notification-backend`. + +The channel for notifications will be named `notifications` and the message will contain necessary information about the notification for rendering as well as action telling to refresh the notifications using the REST API. + +The notification payload must be in the signal to be able to show notification in the UI immediately after receiving the signal. + +If the notification status is updated, the signal service shall emit a signal without the notification payload and only with the action. + ### Notifications Plugin The role of the notifications plugin is to manage the lifecycle of notifications. The backend plugin provides an API for other backends to send notifications, as well as an accompanying [backend service](https://backstage.io/docs/backend-system/architecture/services). It also provides a separate API for the frontend plugin to read notifications for an individual user and manage the read status of notifications. @@ -99,7 +107,7 @@ The notification backend stores notification using the [database service](https: - Origin - Link - Recipients - - Topic (optional) + - Scope (optional) - Icon (optional) - Image URL (optional) @@ -113,12 +121,12 @@ The priority is one of the predefined values to help with presentation of the no The origin is a string identifying the creator of the notification, i.e. the BE plugin or external system. -If a topic is provided for a notification, it will either: +If a scope is provided for a notification, it will either: -- Create a new notification if user doesn't have existing notification in that topic -- Update existing notification in that topic so that it's unread considered as a new notification +- Create a new notification if user doesn't have existing notification in that scope and origin +- Update existing notification in that scope and origin so that it's unread considered as a new notification -The idea of topic is to be able to reuse notifications about same information for example specific failing CI build. +The idea of scope is to be able to reuse notifications about same information for example specific failing CI build. The timestamp of notification creation is auto-generated by the notifications backend at the time of receiving a request to create the notification. @@ -130,7 +138,9 @@ The link is a relative or absolute URL. As an example, it can be used: - by an external system to request an action within an asynchronous task - by a BE plugin to provide link to other part of the Backstage UI (i.e. to the Catalog) -The notification backend does not provide any new permissions, since creating notifications can only be done by other backend plugins, while reading notifications can only be done by the authenticated user. It is possible that we want to add a permissions for reading notifications, in particular for admin and impersonation use cases, but that is not part of this proposal or the initial implementation. +The `notification-backend` does not provide any new permissions, since creating notifications can only be done by other backend plugins, while reading notifications can only be done by the authenticated user. It is possible that we want to add a permissions for reading notifications, in particular for admin and impersonation use cases, but that is not part of this proposal or the initial implementation. + +The `notification-backend` shall provide necessary parameters for paging and filtering notifications from the frontend. The notification frontend plugin provides a UI for viewing notifications, which in the initial implementation can be as simple as needed. The only requirement is that a user is able to view recent notifications and distinguish between read and unread notifications. A notification as marked as read once it has been interacted with. The frontend plugin also subscribes to the notifications signal channel and alerts the user when a new notification is received. @@ -177,8 +187,6 @@ export type NotificationRecipients = type: 'broadcast'; // all users }; -export type NotificationType = 'undone' | 'done' | 'saved'; - export type Notification = { id: string; userRef: string; @@ -189,7 +197,7 @@ export type Notification = { created: Date; saved: boolean; read?: Date; - topic?: string; + scope?: string; updated?: Date; icon?: string; image?: string; @@ -201,34 +209,24 @@ interface SendNotificationRequest { description: string; link: string; origin: string; - topic?: string; + scope?: string; icon?: string; image?: string; } interface NotificationService { - sendNotification(request: SendNotificationRequest): Promise; + sendNotification(request: SendNotificationRequest): Promise; } ``` -Notification is always targeted to a single user unless it's a broadcast. The `notification-backend` will figure out which users will receive notification based on the `recipients` parameter. +Each notification is always routed to individual users unless it's a broadcast. The `notification-backend` will figure out which users will receive a notification based on the `recipients` parameter, resolving it to the concrete list of users at the time of sending the notification. Each notification contains a `title`, a `description` and a `link` for further information. The `created`, `id`, `read` and `saved` properties are handled by the backend based and cannot be changed during the notification creation. Calling `sendNotification` should never throw an error so that it doesn't block the current processing. Notifications should be considered as second-level citizens that are not critical if not delivered. -The `notification-backend` shall provide necessary parameters for paging and filtering notifications from the frontend. - #### `SignalService` -Upon creating a new notification, a signal will be emitted by the `notification-backend`. - -The channel for notifications will be named `notifications` and the message will contain necessary information about the notification for rendering as well as action telling to refresh the notifications using the REST API. - -The notification payload must be in the signal to be able to show notification in the UI immediately after receiving the signal. - -If the notification status is updated, the signal service shall emit a signal without the notification payload and only with the action. - ```ts export type SignalPayload = { recipients: string[] | string | null; @@ -286,6 +284,8 @@ The following frontend API is added as part of this proposal. #### `NotificationsApi` ```ts +export type NotificationType = 'undone' | 'done' | 'saved'; + export type GetNotificationsOptions = { type?: NotificationType; offset?: number; @@ -293,6 +293,13 @@ export type GetNotificationsOptions = { search?: string; }; +export type NotificationUpdateOptions = { + ids: string[]; + done?: boolean; + read?: boolean; + saved?: boolean; +}; + export type NotificationStatus = { unread: number; read: number; @@ -307,17 +314,9 @@ interface NotificationsApi { getStatus(): Promise; - markDone(ids: string[]): Promise; - - markUndone(ids: string[]): Promise; - - markRead(ids: string[]): Promise; - - markUnread(ids: string[]): Promise; - - markSaved(ids: string[]): Promise; - - markUnsaved(ids: string[]): Promise; + updateNotifications( + options: UpdateNotificationsOptions, + ): Promise; } ``` From c420081a209c734a9b8f29d7645bff93c0126f72 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 2 Feb 2024 11:50:54 +0100 Subject: [PATCH 231/468] create-app: add a yarn.lock seed file Signed-off-by: Patrik Oldsberg --- .changeset/lucky-bugs-bow.md | 5 ++ packages/create-app/package.json | 4 +- packages/create-app/scripts/add-lock-seed.js | 80 ++++++++++++++++++++ packages/create-app/seed-yarn.lock | 24 ++++++ packages/create-app/src/createApp.ts | 17 +++++ packages/create-app/src/lib/tasks.ts | 42 ++++++++++ 6 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 .changeset/lucky-bugs-bow.md create mode 100644 packages/create-app/scripts/add-lock-seed.js create mode 100644 packages/create-app/seed-yarn.lock diff --git a/.changeset/lucky-bugs-bow.md b/.changeset/lucky-bugs-bow.md new file mode 100644 index 0000000000..ee17883408 --- /dev/null +++ b/.changeset/lucky-bugs-bow.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Add a seed file for `yarn.lock` in newly created apps. This file is downloaded directly from `https://github.com/backstage/backstage` at the time of creating a new project, ensuring that users always receive the latest version. The purpose of the seed file is to initialize the lock file with known good versions of individual dependencies that have had bad new releases published. The seed file will have no effect if the dependency is not present, it can not be used to install additional packages. diff --git a/packages/create-app/package.json b/packages/create-app/package.json index aa5fae0c5e..e566281f6f 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -29,7 +29,8 @@ "clean": "backstage-cli package clean", "prepack": "node scripts/prepack.js", "postpack": "node scripts/postpack.js", - "start": "yarn nodemon --" + "start": "yarn nodemon --", + "add-lock-seed": "node scripts/add-lock-seed.js" }, "dependencies": { "@backstage/cli-common": "workspace:^", @@ -49,6 +50,7 @@ "@types/inquirer": "^8.1.3", "@types/node": "^18.17.8", "@types/recursive-readdir": "^2.2.0", + "msw": "^1.0.0", "nodemon": "^3.0.1", "ts-node": "^10.0.0" }, diff --git a/packages/create-app/scripts/add-lock-seed.js b/packages/create-app/scripts/add-lock-seed.js new file mode 100644 index 0000000000..faa01fca9b --- /dev/null +++ b/packages/create-app/scripts/add-lock-seed.js @@ -0,0 +1,80 @@ +#!/usr/bin/env node +/* + * 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 fs = require('fs-extra'); +const path = require('path'); + +const YARN_REGISTRY = 'https://registry.yarnpkg.com'; +const NPM_REGISTRY = 'https://registry.npmjs.org'; +const SEED_FILE = 'seed-yarn.lock'; + +function formatLockEntry(package, query, version, distData) { + let header = `${package}@${query}`; + if (package.includes('@')) { + header = `"${header}"`; + } + header += ':'; + + return [ + '', + header, + ` version "${version}"`, + ` resolved "${distData.tarball.replace(NPM_REGISTRY, YARN_REGISTRY)}#${ + distData.shasum + }"`, + ` integrity ${distData.integrity}`, + '', + ].join('\n'); +} + +async function main(package, query, version) { + if (!package || !query || !version) { + console.error( + `Usage: yarn add-lock-seed + +Example: yarn lock-seed @backstage/cli ^1.0.0 1.2.3`, + ); + return false; + } + + const res = await fetch(`${YARN_REGISTRY}/${package}/${version}`); + if (!res.ok) { + console.error( + `Failed to fetch package info for ${package} v${version}: ${await res.text()}`, + ); + return false; + } + + const data = await res.json(); + + const entry = formatLockEntry(package, query, version, data.dist); + + const lockSeedPath = path.resolve(__dirname, `../${SEED_FILE}`); + + await fs.appendFile(lockSeedPath, entry, 'utf8'); + + console.log(`Added the following entry to ${SEED_FILE}:\n${entry}`); + + return true; +} + +main(...process.argv.slice(2)) + .then(ok => process.exit(ok ? 0 : 1)) + .catch(err => { + console.error(err.stack); + process.exit(1); + }); diff --git a/packages/create-app/seed-yarn.lock b/packages/create-app/seed-yarn.lock new file mode 100644 index 0000000000..ec5d2e6c89 --- /dev/null +++ b/packages/create-app/seed-yarn.lock @@ -0,0 +1,24 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +// This file is used to seed the yarn.lock file in newly created projects. +// It can be used to work around issues in new dependency versions that would otherwise +// both break new apps and the E2E tests in this repo. +// +// In @backstage/create-app this file is read directly from the repo, rather than from +// the published package. In the E2E tests it's instead loaded directly from the workspace. +// Lines starting with "//" are trimmed, so they can be used to add extra context to the entries. +// +// To add a new entry, run and commit the result: +// +// yarn add-lock-seed +// +// package: the name of the package, e.g. @testing-library/react +// query: the version query to pin the version for, e.g. ^14.0.0 +// version: the version to pin to, must be in range of the query, e.g. 14.11.0 + +// Fix for https://github.com/testing-library/jest-dom/issues/574 +"@testing-library/jest-dom@^6.0.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.3.0.tgz#e8d308e0c0e91d882340cbbfdea0e4daa7987d36" + integrity sha512-hJVIrkFizEQxoWsGBlycTcQhrpoCH4DhXfrnHFFXgkx3Xdm15zycsq5Ep+vpw4W8S0NJa8cxDHcuJib+1tEbhg== diff --git a/packages/create-app/src/createApp.ts b/packages/create-app/src/createApp.ts index af6b0c9c70..22b84ba5cd 100644 --- a/packages/create-app/src/createApp.ts +++ b/packages/create-app/src/createApp.ts @@ -30,6 +30,7 @@ import { templatingTask, tryInitGitRepository, readGitConfig, + fetchYarnLockSeedTask, } from './lib/tasks'; const DEFAULT_BRANCH = 'master'; @@ -110,6 +111,8 @@ export default async (opts: OptionValues): Promise => { await moveAppTask(tempDir, appDir, answers.name); } + const fetchedYarnLockSeed = await fetchYarnLockSeedTask(appDir); + if (gitConfig) { if (await tryInitGitRepository(appDir)) { // Since we don't know whether we were able to init git before we @@ -128,6 +131,20 @@ export default async (opts: OptionValues): Promise => { chalk.green(`🥇 Successfully created ${chalk.cyan(answers.name)}`), ); Task.log(); + + if (!fetchedYarnLockSeed) { + Task.log( + chalk.yellow( + [ + 'Warning: Failed to fetch the yarn.lock seed file.', + ' You may end up with incompatible dependencies that break the app.', + ' If you run into any errors, please search the issues at', + ' https://github.com/backstage/backstage/issues for potential solutions', + ].join('\n'), + ), + ); + } + Task.section('All set! Now you might want to'); if (opts.skipInstall) { Task.log( diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index 90e6f6ae53..745dd7312c 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -304,3 +304,45 @@ export async function tryInitGitRepository(dir: string) { return false; } } + +/** + * This fetches the yarn.lock seed file at https://github.com/backstage/backstage/blob/master/packages/create-app/seed-yarn.lock + * Its purpose is to lock individual dependencies with broken releases to known working versions. + * This flow is decoupled from the release of the create-app package in order to avoid + * the need to re-publish the create-app package whenever we want to update the seed file. + * + * @returns true if the yarn.lock seed file was fetched successfully + */ +export async function fetchYarnLockSeedTask(dir: string) { + try { + await Task.forItem('fetching', 'yarn.lock seed', async () => { + const controller = new AbortController(); + setTimeout(() => controller.abort(), 5000); + const res = await fetch( + 'https://raw.githubusercontent.com/backstage/backstage/master/packages/create-app/seed-yarn.lock', + { + signal: controller.signal, + }, + ); + if (!res.ok) { + throw new Error( + `Request failed with status ${res.status} ${res.statusText}`, + ); + } + + const initialYarnLockContent = await res.text(); + + await fs.writeFile( + resolvePath(dir, 'yarn.lock'), + initialYarnLockContent + .split('\n') + .filter(l => !l.startsWith('//')) + .join('\n'), + 'utf8', + ); + }); + return true; + } catch { + return false; + } +} From d99e713970db46db866903b5c446904d3f3c1a75 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 2 Feb 2024 11:51:12 +0100 Subject: [PATCH 232/468] e2e-test: override yarn.lock seed to use the one in repo Signed-off-by: Patrik Oldsberg --- packages/e2e-test/src/commands/run.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/e2e-test/src/commands/run.ts b/packages/e2e-test/src/commands/run.ts index a9256695ec..cadd7b878d 100644 --- a/packages/e2e-test/src/commands/run.ts +++ b/packages/e2e-test/src/commands/run.ts @@ -263,6 +263,9 @@ async function createApp( const appDir = resolvePath(rootDir, appName); + print('Overriding yarn.lock with seed file from the create-app package'); + overrideYarnLockSeed(appDir); + print('Rewriting module resolutions of app to use workspace packages'); await overrideModuleResolutions(appDir, workspaceDir); @@ -305,6 +308,22 @@ async function createApp( } } +/** + * Overrides the downloaded yarn.lock file with the seed file packages/create-app/seed-yarn.lock + * This ensures that the E2E tests use the same seed file as users would receive when creating a new app + */ +async function overrideYarnLockSeed(appDir: string) { + const content = await fs.readFile( + paths.resolveOwnRoot('packages/create-app/seed-yarn.lock'), + 'utf8', + ); + const trimmedContent = content + .split('\n') + .filter(l => !l.startsWith('//')) + .join('\n'); + await fs.writeFile(resolvePath(appDir, 'yarn.lock'), trimmedContent, 'utf8'); +} + /** * This points dependency resolutions into the workspace for each package that is present there */ From 58c0f2bcf066bb96f378108df592279677b6b557 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 2 Feb 2024 11:57:16 +0100 Subject: [PATCH 233/468] create-app: add tests for fetchYarnLockSeedTask Signed-off-by: Patrik Oldsberg --- packages/create-app/src/lib/tasks.test.ts | 64 ++++++++++++++++++++++- yarn.lock | 1 + 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index e7c406f51c..f0546cfa5f 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -27,8 +27,14 @@ import { templatingTask, tryInitGitRepository, readGitConfig, + fetchYarnLockSeedTask, } from './tasks'; -import { createMockDirectory } from '@backstage/backend-test-utils'; +import { + createMockDirectory, + setupRequestMockHandlers, +} from '@backstage/backend-test-utils'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; jest.spyOn(Task, 'log').mockReturnValue(undefined); jest.spyOn(Task, 'error').mockReturnValue(undefined); @@ -410,4 +416,60 @@ describe('tasks', () => { ); }); }); + + describe('fetchYarnLockSeedTask', () => { + const worker = setupServer(); + setupRequestMockHandlers(worker); + + it('should fetch the yarn.lock seed file', async () => { + worker.use( + rest.get( + 'https://raw.githubusercontent.com/backstage/backstage/master/packages/create-app/seed-yarn.lock', + (_, res, ctx) => + res( + ctx.status(200), + ctx.text(`# the-lockfile-header + +// some comments +// in the file +// that should +// be removed + +// a comment about the entry +"@backstage/cli@1.0.0": + some info +`), + ), + ), + ); + + mockDir.clear(); + + await expect(fetchYarnLockSeedTask(mockDir.path)).resolves.toBe(true); + + expect(mockDir.content({ shouldReadAsText: true })).toEqual({ + 'yarn.lock': `# the-lockfile-header + + +"@backstage/cli@1.0.0": + some info +`, + }); + }); + + it('should fail gracefully', async () => { + worker.use( + rest.get( + 'https://raw.githubusercontent.com/backstage/backstage/master/packages/create-app/seed-yarn.lock', + (_, res, ctx) => res(ctx.status(404)), + ), + ); + + mockDir.clear(); + + await expect(fetchYarnLockSeedTask(mockDir.path)).resolves.toBe(false); + + expect(mockDir.content()).toEqual({}); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index 19e38d0aaf..5e7f2a996d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4010,6 +4010,7 @@ __metadata: fs-extra: 10.1.0 handlebars: ^4.7.3 inquirer: ^8.2.0 + msw: ^1.0.0 nodemon: ^3.0.1 ora: ^5.3.0 recursive-readdir: ^2.2.2 From e80a7338df1bad049e342b3ac9bb664e7a7ccdf5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 2 Feb 2024 12:43:28 +0100 Subject: [PATCH 234/468] create-app: fix fetchYarnLockSeedTask timeout + add test Signed-off-by: Patrik Oldsberg --- packages/create-app/package.json | 1 + packages/create-app/src/lib/tasks.test.ts | 15 +++++++++++++++ packages/create-app/src/lib/tasks.ts | 5 ++++- yarn.lock | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/create-app/package.json b/packages/create-app/package.json index e566281f6f..1485fa8474 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -39,6 +39,7 @@ "fs-extra": "10.1.0", "handlebars": "^4.7.3", "inquirer": "^8.2.0", + "node-fetch": "^2.6.7", "ora": "^5.3.0", "recursive-readdir": "^2.2.2" }, diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index f0546cfa5f..b1f5e17ba0 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -471,5 +471,20 @@ describe('tasks', () => { expect(mockDir.content()).toEqual({}); }); + + it('should time out if it takes too long to fetch', async () => { + worker.use( + rest.get( + 'https://raw.githubusercontent.com/backstage/backstage/master/packages/create-app/seed-yarn.lock', + (_, res, ctx) => res(ctx.delay(5000)), + ), + ); + + mockDir.clear(); + + await expect(fetchYarnLockSeedTask(mockDir.path)).resolves.toBe(false); + + expect(mockDir.content()).toEqual({}); + }); }); }); diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index 745dd7312c..d366bd5cc7 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -25,6 +25,7 @@ import { resolve as resolvePath, relative as relativePath, } from 'path'; +import fetch from 'node-fetch'; import { exec as execCb } from 'child_process'; import { packageVersions } from './versions'; import { promisify } from 'util'; @@ -317,13 +318,15 @@ export async function fetchYarnLockSeedTask(dir: string) { try { await Task.forItem('fetching', 'yarn.lock seed', async () => { const controller = new AbortController(); - setTimeout(() => controller.abort(), 5000); + const timeout = setTimeout(() => controller.abort(), 3000); const res = await fetch( 'https://raw.githubusercontent.com/backstage/backstage/master/packages/create-app/seed-yarn.lock', { signal: controller.signal, }, ); + clearTimeout(timeout); + if (!res.ok) { throw new Error( `Request failed with status ${res.status} ${res.statusText}`, diff --git a/yarn.lock b/yarn.lock index 5e7f2a996d..612fda606c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4011,6 +4011,7 @@ __metadata: handlebars: ^4.7.3 inquirer: ^8.2.0 msw: ^1.0.0 + node-fetch: ^2.6.7 nodemon: ^3.0.1 ora: ^5.3.0 recursive-readdir: ^2.2.2 From 89bb6cc6c83784391e9f667cf35475c1aafc562c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 2 Feb 2024 13:06:59 +0100 Subject: [PATCH 235/468] Update .changeset/olive-boats-agree.md Signed-off-by: Patrik Oldsberg --- .changeset/olive-boats-agree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/olive-boats-agree.md b/.changeset/olive-boats-agree.md index 8f5b4cfad0..e489476bcc 100644 --- a/.changeset/olive-boats-agree.md +++ b/.changeset/olive-boats-agree.md @@ -90,4 +90,4 @@ '@backstage/theme': patch --- -Widen @types/react range to include 18. +Widen `@types/react` dependency range to include version 18. From bc6641d87d701d57f3da365286230fc5d95af92b Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Fri, 2 Feb 2024 12:24:17 +0200 Subject: [PATCH 236/468] beps: restore topic to the document Signed-off-by: Heikki Hellgren --- beps/0001-notifications-system/README.md | 64 ++++++++++++------------ 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/beps/0001-notifications-system/README.md b/beps/0001-notifications-system/README.md index 726a9ff9a6..c25d78b5df 100644 --- a/beps/0001-notifications-system/README.md +++ b/beps/0001-notifications-system/README.md @@ -99,14 +99,15 @@ The notification backend stores notification using the [database service](https: - Done date - Saved status - Creation date -- Updated date (optional, for topic notifications) +- Updated date (optional, for scoped notifications) - Payload: - Title - - Description - - Priority + - Description (optional) - Origin - Link - Recipients + - Severity (optional, default normal) + - Topic (optional) - Scope (optional) - Icon (optional) - Image URL (optional) @@ -117,10 +118,15 @@ The title is mandatory human-readable text shortly describing the notifications. The description is an optional human-readable text providing more details to the user. -The priority is one of the predefined values to help with presentation of the notifications or their filtering. Allowed values are: `critical`, `high`, `normal` (default), and `low`. +The severity is one of the predefined values to help with presentation of the notifications or their filtering. Allowed values are: `critical`, `high`, `normal` (default), and `low`. The origin is a string identifying the creator of the notification, i.e. the BE plugin or external system. +The topic is an optional string helping to group notifications of particular context. Its use cases include: + +- Several notifications emitted by an asynchronous external task can be grouped by a single topic +- A backend plugin can group several related messages to a particular processing, i.e. asynchronous progress monitoring + If a scope is provided for a notification, it will either: - Create a new notification if user doesn't have existing notification in that scope and origin @@ -130,7 +136,7 @@ The idea of scope is to be able to reuse notifications about same information fo The timestamp of notification creation is auto-generated by the notifications backend at the time of receiving a request to create the notification. -The icon and image URL are optional and is meant to improve UX. A string referencing an icon name from MUI icon library of a defined version. If missing, it will be determined from the priority. +The icon and image URL are optional and is meant to improve UX. A string referencing an icon name from MUI icon library of a defined version. If missing, it will be determined from the severity. The link is a relative or absolute URL. As an example, it can be used: @@ -187,31 +193,33 @@ export type NotificationRecipients = type: 'broadcast'; // all users }; +export type NotificationSeverity = 'critical' | 'high' | 'normal' | 'low'; + +export type NotificationPayload = { + title: string; + description?: string; + link: string; + additionalLinks?: string[]; + severity: NotificationSeverity; + topic?: string; + scope?: string; + icon?: string; +}; + export type Notification = { id: string; userRef: string; - title: string; - description: string; - origin: string; - link: string; created: Date; - saved: boolean; + saved?: Date; read?: Date; - scope?: string; updated?: Date; - icon?: string; - image?: string; + origin: string; + payload: NotificationPayload; }; interface SendNotificationRequest { recipients: NotificationRecipients; - title: string; - description: string; - link: string; - origin: string; - scope?: string; - icon?: string; - image?: string; + payload: NotificationPayload; } interface NotificationService { @@ -221,7 +229,7 @@ interface NotificationService { Each notification is always routed to individual users unless it's a broadcast. The `notification-backend` will figure out which users will receive a notification based on the `recipients` parameter, resolving it to the concrete list of users at the time of sending the notification. -Each notification contains a `title`, a `description` and a `link` for further information. The `created`, `id`, `read` and `saved` properties are handled by the backend based and cannot be changed during the notification creation. +Each notification contains a `title` and a `link` for user to see further information. The `created`, `id`, `read` and `saved` properties are handled by the backend based and cannot be changed during the notification creation. Calling `sendNotification` should never throw an error so that it doesn't block the current processing. Notifications should be considered as second-level citizens that are not critical if not delivered. @@ -305,10 +313,6 @@ export type NotificationStatus = { read: number; }; -export type NotificationIds = { - ids: string[]; -}; - interface NotificationsApi { getNotifications(options?: GetNotificationsOptions): Promise; @@ -329,13 +333,7 @@ interface NotificationsApi { ```ts export type NotificationSignal = { action: string; - notification?: { - title: string; - description: string; - link: string; - icon?: string; - image?: string; - }; + notification?: NotificationPayload; }; const { lastSignal, isSignalsAvailable } = @@ -362,7 +360,7 @@ interface SignalApi { - Unread notifications count is displayed in the title of the page - Configuration can be used to enable or disable features in the notification system (Web Notification API, title change, etc.) - Replace absent signal service with long polling. This requires changes to the `signals` plugin as well. -- Notifications can have priority that is used to determine how notifications are shown to the user +- Notifications can have severity that is used to determine how notifications are shown to the user ## Release Plan From 4841d6db89c6678b44f730109512b10294a1596e Mon Sep 17 00:00:00 2001 From: Alex <52247724+Elya29@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:19:22 +0100 Subject: [PATCH 237/468] Update writing.md apply suggestions Signed-off-by: Alex <52247724+Elya29@users.noreply.github.com> --- docs/conf/writing.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/conf/writing.md b/docs/conf/writing.md index b5515f8618..e0eb72f828 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -218,7 +218,10 @@ webhookUrl: https://smee.io/foo clientId: someGithubAppClientId clientSecret: someGithubAppClientSecret webhookSecret: someWebhookSecret -privateKey: someSecretRsaPrivateKey +privateKey: | + -----BEGIN RSA PRIVATE KEY----- + SomeRsaPrivateKeySecurelyStored + -----END RSA PRIVATE KEY----- ``` -**Warning: RSA private keys should not be hard coded**. We recommend that this entire file should be a secret and stored as such in a secure storage solution like Vault, to ensure they are neither exposed nor misused. +**Warning: Sensitive information, such as private keys, should not be hard coded**. We recommend that this entire file should be a secret and stored as such in a secure storage solution like Vault, to ensure they are neither exposed nor misused. This example key part only shows the format on how to use the yaml | syntax to make sure that the key is valid. From c8a5f640df148ee709bdf664a190abe594d256c5 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Fri, 2 Feb 2024 15:37:33 +0200 Subject: [PATCH 238/468] beps: remove image from the notification payload relates to #22213 and #22641 Signed-off-by: Heikki Hellgren --- beps/0001-notifications-system/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/beps/0001-notifications-system/README.md b/beps/0001-notifications-system/README.md index c25d78b5df..5b37016a10 100644 --- a/beps/0001-notifications-system/README.md +++ b/beps/0001-notifications-system/README.md @@ -110,7 +110,6 @@ The notification backend stores notification using the [database service](https: - Topic (optional) - Scope (optional) - Icon (optional) - - Image URL (optional) The recipients is **not** a list of users, but rather a filter that describes who should receive the notification. It must be possible to evaluate this filter in a database query, so that we can efficiently fetch all notifications for a given user. The same filter will also be used by the signal backend to determine which users should receive a signal. @@ -136,7 +135,7 @@ The idea of scope is to be able to reuse notifications about same information fo The timestamp of notification creation is auto-generated by the notifications backend at the time of receiving a request to create the notification. -The icon and image URL are optional and is meant to improve UX. A string referencing an icon name from MUI icon library of a defined version. If missing, it will be determined from the severity. +The icon is optional and is meant to improve UX. A string referencing an icon name from MUI icon library of a defined version. If missing, it will be determined from the severity. The link is a relative or absolute URL. As an example, it can be used: @@ -265,7 +264,6 @@ Example signal payload for a new notification: #### Future considerations - Add icon for the notification request (for UX purposes) -- Add image url for the notification request (for Web Notification API purposes) - Broadcast messages are to be saved to a separate table for performance reasons - OpenAPI tooling is taken into use for the notification router and client From 29851861600c5998c3ce51365dbad78a0809c40d Mon Sep 17 00:00:00 2001 From: Alec Jacobs Date: Tue, 30 Jan 2024 16:17:29 -0800 Subject: [PATCH 239/468] fix(scaffolder-react): prevent erroneous render when empty links are provided Signed-off-by: Alec Jacobs --- .changeset/weak-news-jam.md | 5 ++ .../TemplateCard/TemplateCard.test.tsx | 82 +++++++++++++++++++ .../components/TemplateCard/TemplateCard.tsx | 3 +- 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 .changeset/weak-news-jam.md diff --git a/.changeset/weak-news-jam.md b/.changeset/weak-news-jam.md new file mode 100644 index 0000000000..a1f44082da --- /dev/null +++ b/.changeset/weak-news-jam.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Fix bug that erroneously caused a separator or a 0 to render in the TemplateCard for Templates with empty links diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx index 5c373411fa..2e2a147ad1 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx @@ -149,6 +149,88 @@ describe('TemplateCard', () => { } }); + it('should not render links section when empty links are defined', async () => { + const mockTemplate: TemplateEntityV1beta3 = { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { name: 'bob', tags: [], links: [] }, + spec: { + steps: [], + type: 'service', + }, + relations: [ + { + targetRef: 'group:default/my-test-user', + type: RELATION_OWNED_BY, + }, + ], + }; + + const { queryByRole, queryByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:kind/:namespace/:name': entityRouteRef, + }, + }, + ); + + expect(queryByRole('separator')).not.toBeInTheDocument(); + expect(queryByText('0')).not.toBeInTheDocument(); + }); + + it('should not render links section when empty additional links are defined', async () => { + const mockTemplate: TemplateEntityV1beta3 = { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { name: 'bob', tags: [], links: [] }, + spec: { + steps: [], + type: 'service', + }, + relations: [ + { + targetRef: 'group:default/my-test-user', + type: RELATION_OWNED_BY, + }, + ], + }; + + const { queryByRole, queryByText } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:kind/:namespace/:name': entityRouteRef, + }, + }, + ); + + expect(queryByRole('separator')).not.toBeInTheDocument(); + expect(queryByText('0')).not.toBeInTheDocument(); + }); + it('should render a link to the owner', async () => { const mockTemplate: TemplateEntityV1beta3 = { apiVersion: 'scaffolder.backstage.io/v1beta3', diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx index 7850137945..6bcff0b0ce 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx @@ -136,7 +136,8 @@ export const TemplateCard = (props: TemplateCardProps) => { )} - {(props.additionalLinks || template.metadata.links?.length) && ( + {(!!props.additionalLinks?.length || + !!template.metadata.links?.length) && ( <> From d530f574da4944b5a0e45710535d08139ef6394b Mon Sep 17 00:00:00 2001 From: Alec Jacobs Date: Wed, 31 Jan 2024 08:19:34 -0800 Subject: [PATCH 240/468] fix(scaffolder-react): always display a single divider below template description Signed-off-by: Alec Jacobs --- .../TemplateCard/TemplateCard.test.tsx | 102 ++++++++++++++++-- .../components/TemplateCard/TemplateCard.tsx | 20 ++-- 2 files changed, 110 insertions(+), 12 deletions(-) diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx index 2e2a147ad1..9ff1dd1b17 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx @@ -70,7 +70,7 @@ describe('TemplateCard', () => { }, }; - const { getByText } = await renderInTestApp( + const { getByText, getByTestId } = await renderInTestApp( { const description = getByText('hello'); expect(description.querySelector('strong')).toBeInTheDocument(); + expect(getByTestId('template-card-separator')).toBeInTheDocument(); }); it('should render no description if none is provided through the template', async () => { @@ -118,6 +119,41 @@ describe('TemplateCard', () => { expect(getByText('No description')).toBeInTheDocument(); }); + it('should not render extra separators when tags or links are not present', async () => { + const mockTemplate: TemplateEntityV1beta3 = { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { name: 'bob' }, + spec: { + steps: [], + type: 'service', + }, + }; + + const { queryByTestId } = await renderInTestApp( + + + , + ); + + expect(queryByTestId('template-card-separator')).toBeInTheDocument(); + expect( + queryByTestId('template-card-separator--tags'), + ).not.toBeInTheDocument(); + expect( + queryByTestId('template-card-separator--links'), + ).not.toBeInTheDocument(); + }); + it('should render the tags', async () => { const mockTemplate: TemplateEntityV1beta3 = { apiVersion: 'scaffolder.backstage.io/v1beta3', @@ -129,7 +165,7 @@ describe('TemplateCard', () => { }, }; - const { getByText } = await renderInTestApp( + const { getByText, queryByTestId } = await renderInTestApp( { for (const tag of mockTemplate.metadata.tags!) { expect(getByText(tag)).toBeInTheDocument(); } + expect(queryByTestId('template-card-separator')).not.toBeInTheDocument(); + expect(queryByTestId('template-card-separator--tags')).toBeInTheDocument(); }); it('should not render links section when empty links are defined', async () => { @@ -166,7 +204,7 @@ describe('TemplateCard', () => { ], }; - const { queryByRole, queryByText } = await renderInTestApp( + const { queryByTestId, queryByText } = await renderInTestApp( { }, ); - expect(queryByRole('separator')).not.toBeInTheDocument(); + expect(queryByTestId('template-card-separator')).toBeInTheDocument(); + expect( + queryByTestId('template-card-separator--links'), + ).not.toBeInTheDocument(); expect(queryByText('0')).not.toBeInTheDocument(); }); @@ -207,7 +248,7 @@ describe('TemplateCard', () => { ], }; - const { queryByRole, queryByText } = await renderInTestApp( + const { queryByTestId, queryByText } = await renderInTestApp( { }, ); - expect(queryByRole('separator')).not.toBeInTheDocument(); + expect(queryByTestId('template-card-separator')).toBeInTheDocument(); + expect( + queryByTestId('template-card-separator--links'), + ).not.toBeInTheDocument(); expect(queryByText('0')).not.toBeInTheDocument(); }); + it('should render links section when links are defined', async () => { + const mockTemplate: TemplateEntityV1beta3 = { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + name: 'bob', + tags: [], + links: [{ url: '/some/url', title: 'Learn More' }], + }, + spec: { + steps: [], + type: 'service', + }, + relations: [ + { + targetRef: 'group:default/my-test-user', + type: RELATION_OWNED_BY, + }, + ], + }; + + const { queryByTestId, getByRole } = await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:kind/:namespace/:name': entityRouteRef, + }, + }, + ); + + expect(queryByTestId('template-card-separator')).not.toBeInTheDocument(); + expect(queryByTestId('template-card-separator--links')).toBeInTheDocument(); + expect(getByRole('link', { name: 'Learn More' })).toBeInTheDocument(); + }); + it('should render a link to the owner', async () => { const mockTemplate: TemplateEntityV1beta3 = { apiVersion: 'scaffolder.backstage.io/v1beta3', diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx index 6bcff0b0ce..2d8ff1e458 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.tsx @@ -101,6 +101,10 @@ export const TemplateCard = (props: TemplateCardProps) => { const app = useApp(); const iconResolver = (key?: string): IconComponent => key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon; + const hasTags = !!template.metadata.tags?.length; + const hasLinks = + !!props.additionalLinks?.length || !!template.metadata.links?.length; + const displayDefaultDivider = !hasTags && !hasLinks; return ( @@ -115,15 +119,20 @@ export const TemplateCard = (props: TemplateCardProps) => { /> - {(template.metadata.tags?.length ?? 0) > 0 && ( + {displayDefaultDivider && ( + + + + )} + {hasTags && ( <> - + {template.metadata.tags?.map(tag => ( - + { )} - {(!!props.additionalLinks?.length || - !!template.metadata.links?.length) && ( + {hasLinks && ( <> - + From c4aaad016da9386eddd4fadd20091edfe5015d01 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 26 Jan 2024 14:02:55 -0500 Subject: [PATCH 241/468] backfill tests for makeProfileInfo Signed-off-by: Jamie Klassen --- .../passport/PassportStrategyHelper.test.ts | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.test.ts b/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.test.ts index dd0c1579a9..4d94634473 100644 --- a/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.test.ts +++ b/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.test.ts @@ -15,17 +15,118 @@ */ import express from 'express'; +import { UnsecuredJWT } from 'jose'; import passport from 'passport'; import { InternalOAuthError } from 'passport-oauth2'; import { executeRedirectStrategy, executeFrameHandlerStrategy, executeRefreshTokenStrategy, + makeProfileInfo, } from './PassportStrategyHelper'; +import { PassportProfile } from './types'; const mockRequest = {} as unknown as express.Request; describe('PassportStrategyHelper', () => { + describe('makeProfileInfo', () => { + it('retrieves email from passport profile', () => { + const profile: PassportProfile = { + emails: [{ value: 'email' }], + provider: '', + id: '', + displayName: '', + }; + + const profileInfo = makeProfileInfo(profile); + + expect(profileInfo.email).toEqual('email'); + }); + + it('retrieves picture from passport profile avatarUrl', () => { + const profile: PassportProfile = { + avatarUrl: 'avatarUrl', + provider: '', + id: '', + displayName: '', + }; + + const profileInfo = makeProfileInfo(profile); + + expect(profileInfo.picture).toEqual('avatarUrl'); + }); + + it('falls back to picture from passport profile photos field', () => { + const profile: PassportProfile = { + photos: [{ value: 'picture' }], + provider: '', + id: '', + displayName: '', + }; + + const profileInfo = makeProfileInfo(profile); + + expect(profileInfo.picture).toEqual('picture'); + }); + + it('falls back to email from ID token', async () => { + const profile: PassportProfile = { + provider: '', + id: '', + displayName: '', + }; + + const profileInfo = makeProfileInfo( + profile, + await new UnsecuredJWT({ email: 'email' }).encode(), + ); + + expect(profileInfo.email).toEqual('email'); + }); + + it('falls back to picture from ID token', async () => { + const profile: PassportProfile = { + provider: '', + id: '', + displayName: '', + }; + + const profileInfo = makeProfileInfo( + profile, + await new UnsecuredJWT({ picture: 'picture' }).encode(), + ); + + expect(profileInfo.picture).toEqual('picture'); + }); + + it('falls back to name from ID token', async () => { + const profile: PassportProfile = { + provider: '', + id: '', + displayName: '', + }; + + const profileInfo = makeProfileInfo( + profile, + await new UnsecuredJWT({ name: 'name' }).encode(), + ); + + expect(profileInfo.displayName).toEqual('name'); + }); + + it('fails when attempting to fall back to invalid JWT', () => { + const profile: PassportProfile = { + provider: '', + id: '', + displayName: '', + }; + + expect(() => makeProfileInfo(profile, 'invalid JWT')).toThrow( + 'Failed to parse id token and get profile info', + ); + }); + }); + class MyCustomRedirectStrategy extends passport.Strategy { authenticate() { this.redirect('a', 302); From d4cc552ab1c1057fa1c7c6dee864c88e79dba54a Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 26 Jan 2024 14:05:24 -0500 Subject: [PATCH 242/468] refactor auth plugins to use jose Signed-off-by: Jamie Klassen --- .changeset/red-bottles-swim.md | 7 +++++ plugins/auth-backend/package.json | 1 - .../lib/passport/PassportStrategyHelper.ts | 8 +++-- .../auth-node/src/passport/PassportHelpers.ts | 31 ++++--------------- yarn.lock | 1 - 5 files changed, 19 insertions(+), 29 deletions(-) create mode 100644 .changeset/red-bottles-swim.md diff --git a/.changeset/red-bottles-swim.md b/.changeset/red-bottles-swim.md new file mode 100644 index 0000000000..eebdc85298 --- /dev/null +++ b/.changeset/red-bottles-swim.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-auth-node': patch +--- + +The helper function `makeProfileInfo` and `PassportHelpers.transformProfile` +were refactored to use the `jose` library. diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index eb0d30a9ba..25e0651361 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -64,7 +64,6 @@ "fs-extra": "10.1.0", "google-auth-library": "^8.0.0", "jose": "^4.6.0", - "jwt-decode": "^3.1.0", "knex": "^3.0.0", "lodash": "^4.17.21", "luxon": "^3.0.0", diff --git a/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts b/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts index 7589254862..44feb916c5 100644 --- a/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts +++ b/plugins/auth-backend/src/lib/passport/PassportStrategyHelper.ts @@ -16,7 +16,7 @@ import express from 'express'; import passport from 'passport'; -import jwtDecoder from 'jwt-decode'; +import { decodeJwt } from 'jose'; import { InternalOAuthError } from 'passport-oauth2'; import { PassportProfile } from './types'; @@ -51,7 +51,11 @@ export const makeProfileInfo = ( if ((!email || !picture || !displayName) && idToken) { try { - const decoded: Record = jwtDecoder(idToken); + const decoded = decodeJwt(idToken) as { + email?: string; + name?: string; + picture?: string; + }; if (!email && decoded.email) { email = decoded.email; } diff --git a/plugins/auth-node/src/passport/PassportHelpers.ts b/plugins/auth-node/src/passport/PassportHelpers.ts index 6c13523811..d7b554d56a 100644 --- a/plugins/auth-node/src/passport/PassportHelpers.ts +++ b/plugins/auth-node/src/passport/PassportHelpers.ts @@ -15,6 +15,7 @@ */ import { Request } from 'express'; +import { decodeJwt } from 'jose'; import { Strategy } from 'passport'; import { PassportProfile } from './types'; import { ProfileInfo } from '../types'; @@ -27,30 +28,6 @@ interface InternalOAuthError extends Error { }; } -/** @internal */ -function decodeJwtPayload(token: string): Record { - const payloadStr = token.split('.')[1]; - if (!payloadStr) { - throw new Error('Invalid JWT token'); - } - - let payload: unknown; - try { - payload = JSON.parse( - Buffer.from( - payloadStr.replace(/-/g, '+').replace(/_/g, '/'), - 'base64', - ).toString('utf8'), - ); - } catch (e) { - throw new Error('Invalid JWT token'); - } - if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { - throw new Error('Invalid JWT token'); - } - return payload as Record; -} - /** @public */ export class PassportHelpers { private constructor() {} @@ -78,7 +55,11 @@ export class PassportHelpers { if ((!email || !picture || !displayName) && idToken) { try { - const decoded: Record = decodeJwtPayload(idToken); + const decoded = decodeJwt(idToken) as { + email?: string; + name?: string; + picture?: string; + }; if (!email && decoded.email) { email = decoded.email; } diff --git a/yarn.lock b/yarn.lock index 8c0ddcabc2..de0169115f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4932,7 +4932,6 @@ __metadata: fs-extra: 10.1.0 google-auth-library: ^8.0.0 jose: ^4.6.0 - jwt-decode: ^3.1.0 knex: ^3.0.0 lodash: ^4.17.21 luxon: ^3.0.0 From d309cadf47de530e4dcca9592bd6ed95a491b600 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Tue, 30 Jan 2024 11:25:04 -0500 Subject: [PATCH 243/468] remove jwt-decode from aws-alb provider Signed-off-by: Jamie Klassen --- .changeset/nasty-days-jog.md | 5 + .../package.json | 7 +- .../src/authenticator.test.ts | 113 ++++++++++++------ .../src/helpers.test.ts | 69 ++++++----- .../src/helpers.ts | 15 ++- plugins/auth-backend/package.json | 1 - yarn.lock | 20 +--- 7 files changed, 130 insertions(+), 100 deletions(-) create mode 100644 .changeset/nasty-days-jog.md diff --git a/.changeset/nasty-days-jog.md b/.changeset/nasty-days-jog.md new file mode 100644 index 0000000000..09454609b2 --- /dev/null +++ b/.changeset/nasty-days-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-aws-alb-provider': patch +--- + +Refactored to use the `jose` library for JWT handling. diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index f6238cca5d..d4658dd7ea 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -38,14 +38,15 @@ "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "jose": "^4.6.0", - "jwt-decode": "^3.1.0", - "node-cache": "^5.1.2" + "node-cache": "^5.1.2", + "node-fetch": "^2.6.7" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/config": "workspace:^", - "express": "^4.18.2" + "express": "^4.18.2", + "msw": "^2.0.8" }, "files": [ "dist" diff --git a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts index bfcbfce622..5f19d800a0 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/authenticator.test.ts @@ -14,36 +14,30 @@ * limitations under the License. */ +import express from 'express'; +import { SignJWT } from 'jose'; import { ALB_ACCESS_TOKEN_HEADER, ALB_JWT_HEADER, awsAlbAuthenticator, } from './authenticator'; -import { jwtVerify } from 'jose'; -import express from 'express'; -import { AuthenticationError } from '@backstage/errors'; import { Config } from '@backstage/config'; +import { AuthenticationError } from '@backstage/errors'; -const jwtMock = jwtVerify as jest.Mocked; -const mockJwt = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IktFWV9JRCIsImlzcyI6IklTU1VFUl9VUkwifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlVzZXIgTmFtZSIsImlhdCI6MTUxNjIzOTAyMn0.uMCSBGhij1xn5pnot8XgD-huQuTIBOFGs6kkW_p_X94'; -const mockAccessToken = 'ACCESS_TOKEN'; -const mockClaims = { - sub: '1234567890', - name: 'User Name', - family_name: 'Name', - given_name: 'User', - picture: 'PICTURE_URL', - email: 'user.name@email.test', - exp: 1632833763, - iss: 'ISSUER_URL', -}; -jest.mock('jose'); - -beforeEach(() => { - jest.clearAllMocks(); -}); describe('AwsAlbProvider', () => { + const mockAccessToken = 'ACCESS_TOKEN'; + const mockClaims = { + sub: '1234567890', + name: 'User Name', + family_name: 'Name', + given_name: 'User', + picture: 'PICTURE_URL', + email: 'user.name@email.test', + exp: Date.now() + 10000, + iss: 'ISSUER_URL', + }; + const signingKey = new TextEncoder().encode('signingKey'); + let mockJwt: string; const mockRequest = { header: jest.fn(name => { if (name === ALB_JWT_HEADER) { @@ -54,6 +48,16 @@ describe('AwsAlbProvider', () => { return undefined; }), } as unknown as express.Request; + const mockRequestWithInvalidJwt = { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return 'invalid.jwt'; + } else if (name === ALB_ACCESS_TOKEN_HEADER) { + return mockAccessToken; + } + return undefined; + }), + } as unknown as express.Request; const mockRequestWithoutJwt = { header: jest.fn(name => { if (name === ALB_ACCESS_TOKEN_HEADER) { @@ -71,13 +75,20 @@ describe('AwsAlbProvider', () => { }), } as unknown as express.Request; + beforeEach(async () => { + mockJwt = await new SignJWT(mockClaims) + .setProtectedHeader({ alg: 'HS256' }) + .sign(signingKey); + }); + describe('should transform to type AwsAlbResponse', () => { it('when JWT is valid and identity is resolved successfully', async () => { - jwtMock.mockReturnValueOnce(Promise.resolve({ payload: mockClaims })); - const response = await awsAlbAuthenticator.authenticate( { req: mockRequest }, - { issuer: 'ISSUER_URL', getKey: jest.fn() }, + { + issuer: 'ISSUER_URL', + getKey: jest.fn().mockResolvedValue(signingKey), + }, ); expect(response).toEqual({ result: { @@ -119,27 +130,38 @@ describe('AwsAlbProvider', () => { }); it('JWT is invalid', async () => { - jwtMock.mockImplementationOnce(() => { - throw new Error('bad JWT'); - }); - await expect( awsAlbAuthenticator.authenticate( - { req: mockRequest }, + { req: mockRequestWithInvalidJwt }, { issuer: 'ISSUER_URL', getKey: jest.fn() }, ), ).rejects.toThrow( - 'Exception occurred during JWT processing: Error: bad JWT', + 'Exception occurred during JWT processing: JWSInvalid: Invalid Compact JWS', ); }); it('issuer is missing', async () => { - jwtMock.mockReturnValueOnce({}); + const jwt = await new SignJWT({}) + .setProtectedHeader({ alg: 'HS256' }) + .sign(signingKey); + const req = { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return jwt; + } else if (name === ALB_ACCESS_TOKEN_HEADER) { + return mockAccessToken; + } + return undefined; + }), + } as unknown as express.Request; await expect( awsAlbAuthenticator.authenticate( - { req: mockRequest }, - { issuer: 'ISSUER_URL', getKey: jest.fn() }, + { req }, + { + issuer: 'ISSUER_URL', + getKey: jest.fn().mockResolvedValue(signingKey), + }, ), ).rejects.toThrow( 'Exception occurred during JWT processing: AuthenticationError: Issuer mismatch on JWT token', @@ -147,14 +169,27 @@ describe('AwsAlbProvider', () => { }); it('issuer is invalid', async () => { - jwtMock.mockReturnValueOnce({ - iss: 'INVALID_ISSUE_URL', - }); + const jwt = await new SignJWT({ iss: 'INVALID_ISSUER_URL' }) + .setProtectedHeader({ alg: 'HS256' }) + .sign(signingKey); + const req = { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return jwt; + } else if (name === ALB_ACCESS_TOKEN_HEADER) { + return mockAccessToken; + } + return undefined; + }), + } as unknown as express.Request; await expect( awsAlbAuthenticator.authenticate( - { req: mockRequest }, - { issuer: 'ISSUER_URL', getKey: jest.fn() }, + { req }, + { + issuer: 'ISSUER_URL', + getKey: jest.fn().mockResolvedValue(signingKey), + }, ), ).rejects.toThrow( 'Exception occurred during JWT processing: AuthenticationError: Issuer mismatch on JWT token', diff --git a/plugins/auth-backend-module-aws-alb-provider/src/helpers.test.ts b/plugins/auth-backend-module-aws-alb-provider/src/helpers.test.ts index 07ad118cea..cb103debb0 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/helpers.test.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/helpers.test.ts @@ -1,10 +1,3 @@ -import NodeCache from 'node-cache'; -import { makeProfileInfo, provisionKeyCache } from './helpers'; -import * as crypto from 'crypto'; -import { JWTHeaderParameters } from 'jose'; -import { PassportProfile } from '@backstage/plugin-auth-node'; -import jwtDecoder from 'jwt-decode'; - /* * Copyright 2020 The Backstage Authors * @@ -21,40 +14,47 @@ import jwtDecoder from 'jwt-decode'; * limitations under the License. */ -const mockKey = async () => { - return `-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnuN4LlaJhaUpx+qZFTzYCrSBLk0I -yOlxJ2VW88mLAQGJ7HPAvOdylxZsItMnzCuqNzZvie8m/NJsOjhDncVkrw== ------END PUBLIC KEY----- -`; -}; +import * as crypto from 'crypto'; +import { JWTHeaderParameters, UnsecuredJWT } from 'jose'; +import NodeCache from 'node-cache'; +import { http, HttpResponse } from 'msw'; +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { PassportProfile } from '@backstage/plugin-auth-node'; +import { makeProfileInfo, provisionKeyCache } from './helpers'; + jest.mock('crypto'); const cryptoMock = crypto as jest.Mocked; -jest.mock('node-fetch', () => ({ - __esModule: true, - default: async () => { - return { - text: async () => { - return mockKey(); - }, - }; - }, -})); - -const jwtMock = jwtDecoder as jest.Mocked; -jest.mock('jwt-decode'); describe('helpers', () => { + const server = setupServer(); + setupRequestMockHandlers(server); + const nodeCache = jest.fn() as unknown as NodeCache; nodeCache.set = jest.fn(); beforeEach(() => { jest.clearAllMocks(); + server.use( + http.get( + 'https://public-keys.auth.elb.eu-west-1.amazonaws.com/kid', + () => + new HttpResponse( + `-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnuN4LlaJhaUpx+qZFTzYCrSBLk0I +yOlxJ2VW88mLAQGJ7HPAvOdylxZsItMnzCuqNzZvie8m/NJsOjhDncVkrw== +-----END PUBLIC KEY----- +`, + ), + ), + ); }); + it('should create a key', () => { const getKey = provisionKeyCache('eu-west-1', nodeCache); expect(getKey).toBeDefined(); }); + it('should return a key from cache', async () => { const getKey = provisionKeyCache('eu-west-1', nodeCache); @@ -65,6 +65,7 @@ describe('helpers', () => { expect(key).toBe('key'); }); + it('should update cache if key is not found', async () => { const getKey = provisionKeyCache('eu-west-1', nodeCache); @@ -77,6 +78,7 @@ describe('helpers', () => { await getKey({ kid: 'kid' } as unknown as JWTHeaderParameters); expect(nodeCache.set).toHaveBeenCalledWith('kid', 'key'); }); + it('should throw error if key is not found', async () => { const getKey = provisionKeyCache('eu-west-1', nodeCache); @@ -87,6 +89,7 @@ describe('helpers', () => { getKey({ kid: 'kid' } as unknown as JWTHeaderParameters), ).rejects.toThrow(); }); + it('should throw if key is not present in request header', async () => { const getKey = provisionKeyCache('eu-west-1', nodeCache); @@ -119,19 +122,19 @@ describe('makeProfileInfo', () => { }; expect(makeProfileInfo(profile, accessToken)).toEqual(result); }); + it('should return profile info from id token', () => { - jwtMock.mockReturnValueOnce({ - email: 'email', - picture: 'picture', - name: 'displayName', - }); const profile = { name: { familyName: 'familyName', givenName: 'givenName', }, } as PassportProfile; - const idToken = 'idToken'; + const idToken = new UnsecuredJWT({ + email: 'email', + picture: 'picture', + name: 'displayName', + }).encode(); const result = { email: 'email', picture: 'picture', diff --git a/plugins/auth-backend-module-aws-alb-provider/src/helpers.ts b/plugins/auth-backend-module-aws-alb-provider/src/helpers.ts index d97146bb93..e846b2484e 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/helpers.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/helpers.ts @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { PassportProfile } from '@backstage/plugin-auth-node/'; -import { ProfileInfo } from '@backstage/plugin-auth-node'; import { KeyObject } from 'crypto'; -import jwtDecoder from 'jwt-decode'; -import NodeCache from 'node-cache'; import * as crypto from 'crypto'; -import { JWTHeaderParameters } from 'jose'; +import { JWTHeaderParameters, decodeJwt } from 'jose'; +import NodeCache from 'node-cache'; +import fetch from 'node-fetch'; +import { PassportProfile, ProfileInfo } from '@backstage/plugin-auth-node'; import { AuthenticationError } from '@backstage/errors'; export const makeProfileInfo = ( @@ -45,7 +44,11 @@ export const makeProfileInfo = ( if ((!email || !picture || !displayName) && idToken) { try { - const decoded: Record = jwtDecoder(idToken); + const decoded: Record = decodeJwt(idToken) as { + email?: string; + picture?: string; + name?: string; + }; if (!email && decoded.email) { email = decoded.email; } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 25e0651361..f7494247dc 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -92,7 +92,6 @@ "@types/body-parser": "^1.19.0", "@types/cookie-parser": "^1.4.2", "@types/express-session": "^1.17.2", - "@types/jwt-decode": "^3.1.0", "@types/passport-auth0": "^1.0.5", "@types/passport-github2": "^1.2.4", "@types/passport-google-oauth20": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index de0169115f..363823b103 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4677,8 +4677,9 @@ __metadata: "@backstage/plugin-auth-node": "workspace:^" express: ^4.18.2 jose: ^4.6.0 - jwt-decode: ^3.1.0 + msw: ^2.0.8 node-cache: ^5.1.2 + node-fetch: ^2.6.7 languageName: unknown linkType: soft @@ -4913,7 +4914,6 @@ __metadata: "@types/cookie-parser": ^1.4.2 "@types/express": ^4.17.6 "@types/express-session": ^1.17.2 - "@types/jwt-decode": ^3.1.0 "@types/passport": ^1.0.3 "@types/passport-auth0": ^1.0.5 "@types/passport-github2": ^1.2.4 @@ -18482,15 +18482,6 @@ __metadata: languageName: node linkType: hard -"@types/jwt-decode@npm:^3.1.0": - version: 3.1.0 - resolution: "@types/jwt-decode@npm:3.1.0" - dependencies: - jwt-decode: "*" - checksum: 82ff0b3826e5d9da48be1f11e16fec96e56dd946995edaa100682202b9b7beb30fb04a353cbabd378d3b13a24241b48a0b662a4f181bae7f758147d92368d930 - languageName: node - linkType: hard - "@types/keyv@npm:*, @types/keyv@npm:^3.1.1": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" @@ -32363,13 +32354,6 @@ __metadata: languageName: node linkType: hard -"jwt-decode@npm:*, jwt-decode@npm:^3.1.0": - version: 3.1.2 - resolution: "jwt-decode@npm:3.1.2" - checksum: 20a4b072d44ce3479f42d0d2c8d3dabeb353081ba4982e40b83a779f2459a70be26441be6c160bfc8c3c6eadf9f6380a036fbb06ac5406b5674e35d8c4205eeb - languageName: node - linkType: hard - "kafkajs@npm:^2.0.0": version: 2.2.4 resolution: "kafkajs@npm:2.2.4" From 5fe8caaecbdd68c44b0553bfb90017c20a111527 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 2 Feb 2024 17:23:24 +0100 Subject: [PATCH 244/468] cli: messages fixes for the /public entry point Signed-off-by: Patrik Oldsberg --- .changeset/large-tables-wonder.md | 2 +- packages/cli/src/lib/bundler/bundle.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/large-tables-wonder.md b/.changeset/large-tables-wonder.md index f54402dbd4..9c10f172a1 100644 --- a/.changeset/large-tables-wonder.md +++ b/.changeset/large-tables-wonder.md @@ -2,4 +2,4 @@ '@backstage/cli': patch --- -Add experimental support for optional `auth` app entry point. +Add experimental support for an optional `public` app entry point that lets users sign-in before being able to access the full app. diff --git a/packages/cli/src/lib/bundler/bundle.ts b/packages/cli/src/lib/bundler/bundle.ts index d0624c93ff..c5dda4b1cd 100644 --- a/packages/cli/src/lib/bundler/bundle.ts +++ b/packages/cli/src/lib/bundler/bundle.ts @@ -68,7 +68,7 @@ export async function buildBundle(options: BuildOptions) { if (publicPaths) { console.log( chalk.yellow( - `⚠️ WARNING: The app /auth entry point is an experimental feature that may receive immediate breaking changes.`, + `⚠️ WARNING: The app /public entry point is an experimental feature that may receive immediate breaking changes.`, ), ); configs.push( From 79062f9c48ffe4b604ebb457d44771ac42fd2b0e Mon Sep 17 00:00:00 2001 From: Adi-Sin Date: Fri, 2 Feb 2024 23:05:47 +0530 Subject: [PATCH 245/468] Update github-codespaces.yaml Update author name Signed-off-by: Adi-Sin --- microsite/data/plugins/github-codespaces.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/github-codespaces.yaml b/microsite/data/plugins/github-codespaces.yaml index de2659c1e1..d846235bd6 100644 --- a/microsite/data/plugins/github-codespaces.yaml +++ b/microsite/data/plugins/github-codespaces.yaml @@ -1,6 +1,6 @@ --- title: GitHub Codespaces -author: Aditya Singhal +author: Aditya Singhal - Lab45 authorUrl: https://github.com/adityasinghal26 category: Development description: Integrates GitHub Codespaces for a Backstage component with the Authenticated User. From 294fab8315a7a26e8a6786b7343da215e2c12644 Mon Sep 17 00:00:00 2001 From: Danyelle AC <90638175+Danyelleac@users.noreply.github.com> Date: Fri, 2 Feb 2024 17:01:50 -0300 Subject: [PATCH 246/468] Update .changeset/fresh-gifts-smile.md Co-authored-by: Sydney Achinger <78113809+squid-ney@users.noreply.github.com> Signed-off-by: Danyelle AC <90638175+Danyelleac@users.noreply.github.com> --- .changeset/fresh-gifts-smile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fresh-gifts-smile.md b/.changeset/fresh-gifts-smile.md index ffa680bb8e..446e9f3c54 100644 --- a/.changeset/fresh-gifts-smile.md +++ b/.changeset/fresh-gifts-smile.md @@ -2,4 +2,4 @@ '@backstage/plugin-techdocs-module-addons-contrib': patch --- -textsize-fix value label text color +Fixed the value label text color in dark mode for the TextSize addon. From f71352cfe9137d266860603992f6583978315a5d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 29 Jan 2024 11:51:48 +0100 Subject: [PATCH 247/468] align typescript versions to 5.1 + 5.3 Signed-off-by: Patrik Oldsberg --- .changeset/mighty-steaks-shave.md | 5 +++++ microsite/package.json | 2 +- microsite/yarn.lock | 18 +++++++-------- package.json | 2 +- .../src/layout/Sidebar/Page.tsx | 12 +++++----- .../templates/default-app/package.json.hbs | 2 +- plugins/git-release-manager/api-report.md | 16 +++++++------- yarn.lock | 22 +------------------ 8 files changed, 32 insertions(+), 47 deletions(-) create mode 100644 .changeset/mighty-steaks-shave.md diff --git a/.changeset/mighty-steaks-shave.md b/.changeset/mighty-steaks-shave.md new file mode 100644 index 0000000000..b85447e5c0 --- /dev/null +++ b/.changeset/mighty-steaks-shave.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped TypeScript to version `5.3`. diff --git a/microsite/package.json b/microsite/package.json index 53309fb358..d90c455167 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -25,7 +25,7 @@ "@types/webpack-env": "^1.18.0", "js-yaml": "^4.1.0", "prettier": "^2.6.2", - "typescript": "~5.0.0", + "typescript": "~5.1.0", "yaml-loader": "^0.8.0" }, "prettier": "@spotify/prettier-config", diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 50465ec651..4c9f348fe3 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -3907,7 +3907,7 @@ __metadata: react-dom: ^18.0.0 sass: ^1.57.1 swc-loader: ^0.2.3 - typescript: ~5.0.0 + typescript: ~5.1.0 yaml-loader: ^0.8.0 languageName: unknown linkType: soft @@ -11692,23 +11692,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:~5.0.0": - version: 5.0.4 - resolution: "typescript@npm:5.0.4" +"typescript@npm:~5.1.0": + version: 5.1.6 + resolution: "typescript@npm:5.1.6" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 82b94da3f4604a8946da585f7d6c3025fff8410779e5bde2855ab130d05e4fd08938b9e593b6ebed165bda6ad9292b230984f10952cf82f0a0ca07bbeaa08172 + checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 languageName: node linkType: hard -"typescript@patch:typescript@~5.0.0#~builtin": - version: 5.0.4 - resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=a1c5e5" +"typescript@patch:typescript@~5.1.0#~builtin": + version: 5.1.6 + resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=a1c5e5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 6a1fe9a77bb9c5176ead919cc4a1499ee63e46b4e05bf667079f11bf3a8f7887f135aa72460a4c3b016e6e6bb65a822cb8689a6d86cbfe92d22cc9f501f09213 + checksum: 21e88b0a0c0226f9cb9fd25b9626fb05b4c0f3fddac521844a13e1f30beb8f14e90bd409a9ac43c812c5946d714d6e0dee12d5d02dfc1c562c5aacfa1f49b606 languageName: node linkType: hard diff --git a/package.json b/package.json index 1e53b7c00f..6deae7611e 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "semver": "^7.5.3", "shx": "^0.3.2", "ts-node": "^10.4.0", - "typescript": "~5.2.0" + "typescript": "~5.1.0" }, "prettier": "@spotify/prettier-config", "lint-staged": { diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 09d5bd910c..3d618fc5a0 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -33,23 +33,23 @@ import { SidebarPinStateProvider } from './SidebarPinStateContext'; export type SidebarPageClassKey = 'root'; -const useStyles = makeStyles< - Theme, - { sidebarConfig: SidebarConfig; isPinned: boolean } ->( +type StyleProps = { sidebarConfig: SidebarConfig; isPinned: boolean }; + +const useStyles = makeStyles( theme => ({ root: { width: '100%', transition: 'padding-left 0.1s ease-out', isolation: 'isolate', [theme.breakpoints.up('sm')]: { - paddingLeft: props => + paddingLeft: (props: StyleProps) => props.isPinned ? props.sidebarConfig.drawerWidthOpen : props.sidebarConfig.drawerWidthClosed, }, [theme.breakpoints.down('xs')]: { - paddingBottom: props => props.sidebarConfig.mobileSidebarHeight, + paddingBottom: (props: StyleProps) => + props.sidebarConfig.mobileSidebarHeight, }, '@media print': { padding: '0px !important', diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index 40cc8282d9..f2f35360cc 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -39,7 +39,7 @@ "lerna": "^7.3.0", "node-gyp": "^9.0.0", "prettier": "^2.3.2", - "typescript": "~5.2.0" + "typescript": "~5.3.0" }, "resolutions": { "@types/react": "^18", diff --git a/plugins/git-release-manager/api-report.md b/plugins/git-release-manager/api-report.md index d2dd7e909f..3e4c0847a7 100644 --- a/plugins/git-release-manager/api-report.md +++ b/plugins/git-release-manager/api-report.md @@ -317,42 +317,42 @@ function LinearProgressWithLabel(props: { // Warning: (ae-missing-release-tag) "MOCK_RELEASE_BRANCH_NAME_CALVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_BRANCH_NAME_CALVER = 'rc/2020.01.01_1'; +const MOCK_RELEASE_BRANCH_NAME_CALVER: string; // Warning: (ae-missing-release-tag) "MOCK_RELEASE_BRANCH_NAME_SEMVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_BRANCH_NAME_SEMVER = 'rc/1.2.3'; +const MOCK_RELEASE_BRANCH_NAME_SEMVER: string; // Warning: (ae-missing-release-tag) "MOCK_RELEASE_CANDIDATE_TAG_NAME_CALVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_CANDIDATE_TAG_NAME_CALVER = 'rc-2020.01.01_1'; +const MOCK_RELEASE_CANDIDATE_TAG_NAME_CALVER: string; // Warning: (ae-missing-release-tag) "MOCK_RELEASE_CANDIDATE_TAG_NAME_SEMVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_CANDIDATE_TAG_NAME_SEMVER = 'rc-1.2.3'; +const MOCK_RELEASE_CANDIDATE_TAG_NAME_SEMVER: string; // Warning: (ae-missing-release-tag) "MOCK_RELEASE_NAME_CALVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_NAME_CALVER = 'Version 2020.01.01_1'; +const MOCK_RELEASE_NAME_CALVER: string; // Warning: (ae-missing-release-tag) "MOCK_RELEASE_NAME_SEMVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_NAME_SEMVER = 'Version 1.2.3'; +const MOCK_RELEASE_NAME_SEMVER: string; // Warning: (ae-missing-release-tag) "MOCK_RELEASE_VERSION_TAG_NAME_CALVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_VERSION_TAG_NAME_CALVER = 'version-2020.01.01_1'; +const MOCK_RELEASE_VERSION_TAG_NAME_CALVER: string; // Warning: (ae-missing-release-tag) "MOCK_RELEASE_VERSION_TAG_NAME_SEMVER" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -const MOCK_RELEASE_VERSION_TAG_NAME_SEMVER = 'version-1.2.3'; +const MOCK_RELEASE_VERSION_TAG_NAME_SEMVER: string; // Warning: (ae-missing-release-tag) "mockBumpedTag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/yarn.lock b/yarn.lock index 8c0ddcabc2..fa8ea9170c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40134,7 +40134,7 @@ __metadata: semver: ^7.5.3 shx: ^0.3.2 ts-node: ^10.4.0 - typescript: ~5.2.0 + typescript: ~5.1.0 languageName: unknown linkType: soft @@ -43196,16 +43196,6 @@ __metadata: languageName: node linkType: hard -"typescript@npm:~5.2.0": - version: 5.2.2 - resolution: "typescript@npm:5.2.2" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 7912821dac4d962d315c36800fe387cdc0a6298dba7ec171b350b4a6e988b51d7b8f051317786db1094bd7431d526b648aba7da8236607febb26cf5b871d2d3c - languageName: node - linkType: hard - "typescript@patch:typescript@~5.0.4#~builtin": version: 5.0.4 resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=a1c5e5" @@ -43226,16 +43216,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@~5.2.0#~builtin": - version: 5.2.2 - resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=a1c5e5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 07106822b4305de3f22835cbba949a2b35451cad50888759b6818421290ff95d522b38ef7919e70fb381c5fe9c1c643d7dea22c8b31652a717ddbd57b7f4d554 - languageName: node - linkType: hard - "ua-parser-js@npm:^0.7.30": version: 0.7.33 resolution: "ua-parser-js@npm:0.7.33" From ea2b5218868a093fd37d9922c907bdb7077a1f17 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:31:32 +0000 Subject: [PATCH 248/468] chore(deps): update dependency @types/jest to v29.5.12 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d0070ef970..b66ca12848 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18371,12 +18371,12 @@ __metadata: linkType: hard "@types/jest@npm:*, @types/jest@npm:^29.0.0, @types/jest@npm:^29.5.11": - version: 29.5.11 - resolution: "@types/jest@npm:29.5.11" + version: 29.5.12 + resolution: "@types/jest@npm:29.5.12" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: f892a06ec9f0afa9a61cd7fa316ec614e21d4df1ad301b5a837787e046fcb40dfdf7f264a55e813ac6b9b633cb9d366bd5b8d1cea725e84102477b366df23fdd + checksum: 19b1efdeed9d9a60a81edc8226cdeae5af7479e493eaed273e01243891c9651f7b8b4c08fc633a7d0d1d379b091c4179bbaa0807af62542325fd72f2dd17ce1c languageName: node linkType: hard From 0add87ca6a05a6f632cf4474c5bc38e85b38502e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 11:32:01 +0000 Subject: [PATCH 249/468] chore(deps): update dependency @types/react to v18.2.52 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d0070ef970..d586cee159 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19011,13 +19011,13 @@ __metadata: linkType: hard "@types/react@npm:^18": - version: 18.2.48 - resolution: "@types/react@npm:18.2.48" + version: 18.2.52 + resolution: "@types/react@npm:18.2.52" dependencies: "@types/prop-types": "*" "@types/scheduler": "*" csstype: ^3.0.2 - checksum: c9ca43ed2995389b7e09492c24e6f911a8439bb8276dd17cc66a2fbebbf0b42daf7b2ad177043256533607c2ca644d7d928fdfce37a67af1f8646d2bac988900 + checksum: 4abc9bd63879e57c3df43a9cacff54c079e21b61ef934d33801e0177c26f582aa7d1d88a0769a740a4fd273168e3b150ff61de23e0fa85d1070e82ddce2b7fd2 languageName: node linkType: hard From 87dea686bfa75d852d84276f32d3f8191dbfd0b9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 3 Feb 2024 12:34:06 +0100 Subject: [PATCH 250/468] auth-backend: refactor to remove internal hardcoded default session expiration Signed-off-by: Patrik Oldsberg --- .../auth-backend/src/lib/session/constants.ts | 19 ----- plugins/auth-backend/src/lib/session/index.ts | 17 ---- .../readBackstageTokenExpiration.test.ts | 77 +++++++++++++++++++ .../service/readBackstageTokenExpiration.ts | 44 +++++++++++ .../auth-backend/src/service/router.test.ts | 67 +--------------- plugins/auth-backend/src/service/router.ts | 31 +------- 6 files changed, 125 insertions(+), 130 deletions(-) delete mode 100644 plugins/auth-backend/src/lib/session/constants.ts delete mode 100644 plugins/auth-backend/src/lib/session/index.ts create mode 100644 plugins/auth-backend/src/service/readBackstageTokenExpiration.test.ts create mode 100644 plugins/auth-backend/src/service/readBackstageTokenExpiration.ts diff --git a/plugins/auth-backend/src/lib/session/constants.ts b/plugins/auth-backend/src/lib/session/constants.ts deleted file mode 100644 index 0d14987ab2..0000000000 --- a/plugins/auth-backend/src/lib/session/constants.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// BACKSTAGE_SESSION_EXPIRATION the default session expiration time -// TODO: find a less hard-coded way to access this, perhaps by reading it from the configuration. -export const BACKSTAGE_SESSION_EXPIRATION = 3600; diff --git a/plugins/auth-backend/src/lib/session/index.ts b/plugins/auth-backend/src/lib/session/index.ts deleted file mode 100644 index 306d483ae0..0000000000 --- a/plugins/auth-backend/src/lib/session/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { BACKSTAGE_SESSION_EXPIRATION } from './constants'; diff --git a/plugins/auth-backend/src/service/readBackstageTokenExpiration.test.ts b/plugins/auth-backend/src/service/readBackstageTokenExpiration.test.ts new file mode 100644 index 0000000000..e1f863678a --- /dev/null +++ b/plugins/auth-backend/src/service/readBackstageTokenExpiration.test.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2024 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 { ConfigReader } from '@backstage/config'; +import { readBackstageTokenExpiration } from './readBackstageTokenExpiration'; + +describe('Test for default backstage token expiry time', () => { + it('Will return default backstage session expiration', () => { + const config = new ConfigReader({ + app: { + baseUrl: 'http://example.com/extra-path', + }, + }); + expect(readBackstageTokenExpiration(config)).toBe(3600); + }); + + it('Will return user defined 120 minutes as backstage session expiration', () => { + const config = new ConfigReader({ + app: { + baseUrl: 'http://example.com/extra-path', + }, + auth: { + backstageTokenExpiration: { minutes: 120 }, + }, + }); + expect(readBackstageTokenExpiration(config)).toBe(7200); + }); + + it('Will return minimum duration of 10 minutes as backstage session expiration', () => { + const config = new ConfigReader({ + app: { + baseUrl: 'http://example.com/extra-path', + }, + auth: { + backstageTokenExpiration: { minutes: 2 }, + }, + }); + expect(readBackstageTokenExpiration(config)).toBe(600); + }); + + it('Will return user configured value as backstage session expiration', () => { + const config = new ConfigReader({ + app: { + baseUrl: 'http://example.com/extra-path', + }, + auth: { + backstageTokenExpiration: { minutes: 20 }, + }, + }); + expect(readBackstageTokenExpiration(config)).toBe(1200); + }); + + it('Will return maximum of 24 hour as backstage session expiration if user configured value is more than a day', () => { + const config = new ConfigReader({ + app: { + baseUrl: 'http://example.com/extra-path', + }, + auth: { + backstageTokenExpiration: { minutes: 1500 }, + }, + }); + expect(readBackstageTokenExpiration(config)).toBe(86400); + }); +}); diff --git a/plugins/auth-backend/src/service/readBackstageTokenExpiration.ts b/plugins/auth-backend/src/service/readBackstageTokenExpiration.ts new file mode 100644 index 0000000000..c687ecdfc5 --- /dev/null +++ b/plugins/auth-backend/src/service/readBackstageTokenExpiration.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2024 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 { RootConfigService } from '@backstage/backend-plugin-api'; +import { readDurationFromConfig } from '@backstage/config'; +import { durationToMilliseconds } from '@backstage/types'; + +const TOKEN_EXP_DEFAULT_S = 3600; +const TOKEN_EXP_MIN_S = 600; +const TOKEN_EXP_MAX_S = 86400; + +export function readBackstageTokenExpiration(config: RootConfigService) { + const processingIntervalKey = 'auth.backstageTokenExpiration'; + + if (!config.has(processingIntervalKey)) { + return TOKEN_EXP_DEFAULT_S; + } + + const duration = readDurationFromConfig(config, { + key: processingIntervalKey, + }); + + const durationS = Math.round(durationToMilliseconds(duration) / 1000); + + if (durationS < TOKEN_EXP_MIN_S) { + return TOKEN_EXP_MIN_S; + } else if (durationS > TOKEN_EXP_MAX_S) { + return TOKEN_EXP_MAX_S; + } + return durationS; +} diff --git a/plugins/auth-backend/src/service/router.test.ts b/plugins/auth-backend/src/service/router.test.ts index 8805a60e56..3a1fde881e 100644 --- a/plugins/auth-backend/src/service/router.test.ts +++ b/plugins/auth-backend/src/service/router.test.ts @@ -15,11 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; -import { - createOriginFilter, - getDefaultBackstageTokenExpiryTime, -} from './router'; -import { BACKSTAGE_SESSION_EXPIRATION } from '../lib/session'; +import { createOriginFilter } from './router'; describe('Auth origin filtering', () => { const config = new ConfigReader({ @@ -56,64 +52,3 @@ describe('Auth origin filtering', () => { expect(createOriginFilter(config)(origin)).toBeTruthy(); }); }); - -describe('Test for default backstage token expiry time', () => { - it('Will return default backstage session expiration', () => { - const config = new ConfigReader({ - app: { - baseUrl: 'http://example.com/extra-path', - }, - }); - expect(getDefaultBackstageTokenExpiryTime(config)).toBe( - BACKSTAGE_SESSION_EXPIRATION, - ); - }); - - it('Will return user defined 120 minutes as backstage session expiration', () => { - const config = new ConfigReader({ - app: { - baseUrl: 'http://example.com/extra-path', - }, - auth: { - backstageTokenExpiration: { minutes: 120 }, - }, - }); - expect(getDefaultBackstageTokenExpiryTime(config)).toBe(7200); - }); - - it('Will return minimum duration of 10 minutes as backstage session expiration', () => { - const config = new ConfigReader({ - app: { - baseUrl: 'http://example.com/extra-path', - }, - auth: { - backstageTokenExpiration: { minutes: 2 }, - }, - }); - expect(getDefaultBackstageTokenExpiryTime(config)).toBe(600); - }); - - it('Will return user configured value as backstage session expiration', () => { - const config = new ConfigReader({ - app: { - baseUrl: 'http://example.com/extra-path', - }, - auth: { - backstageTokenExpiration: { minutes: 20 }, - }, - }); - expect(getDefaultBackstageTokenExpiryTime(config)).toBe(1200); - }); - - it('Will return maximum of 24 hour as backstage session expiration if user configured value is more than a day', () => { - const config = new ConfigReader({ - app: { - baseUrl: 'http://example.com/extra-path', - }, - auth: { - backstageTokenExpiration: { minutes: 1500 }, - }, - }); - expect(getDefaultBackstageTokenExpiryTime(config)).toBe(86400); - }); -}); diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index dddac82c2d..44861207ed 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -36,12 +36,11 @@ import passport from 'passport'; import { Minimatch } from 'minimatch'; import { CatalogAuthResolverContext } from '../lib/resolvers'; import { AuthDatabase } from '../database/AuthDatabase'; -import { BACKSTAGE_SESSION_EXPIRATION } from '../lib/session'; +import { readBackstageTokenExpiration } from './readBackstageTokenExpiration'; import { TokenIssuer } from '../identity/types'; import { StaticTokenIssuer } from '../identity/StaticTokenIssuer'; import { StaticKeyStore } from '../identity/StaticKeyStore'; -import { Config, readDurationFromConfig } from '@backstage/config'; -import { durationToMilliseconds } from '@backstage/types'; +import { Config } from '@backstage/config'; /** @public */ export type ProviderFactories = { [s: string]: AuthProviderFactory }; @@ -77,7 +76,7 @@ export async function createRouter( const appUrl = config.getString('app.baseUrl'); const authUrl = await discovery.getExternalBaseUrl('auth'); - const backstageTokenExpiration = getDefaultBackstageTokenExpiryTime(config); + const backstageTokenExpiration = readBackstageTokenExpiration(config); const authDb = AuthDatabase.create(database); const keyStore = await KeyStores.fromConfig(config, { @@ -249,27 +248,3 @@ export function createOriginFilter( return allowedOriginPatterns.some(pattern => pattern.match(origin)); }; } - -/** @internal */ -export function getDefaultBackstageTokenExpiryTime(config: Config) { - const processingIntervalKey = 'auth.backstageTokenExpiration'; - - if (!config.has(processingIntervalKey)) { - return BACKSTAGE_SESSION_EXPIRATION; - } - - const duration = readDurationFromConfig(config, { - key: processingIntervalKey, - }); - - const roundedDuration = Math.round(durationToMilliseconds(duration) / 1000); - - const minSeconds = Math.max(600, roundedDuration); - - const maxSeconds = Math.min(86400, roundedDuration); - - if (roundedDuration < minSeconds) { - return minSeconds - } - return maxSeconds; -} From c6a5c47885409ce2940bde993e66743a2cf175f7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 12:00:35 +0000 Subject: [PATCH 251/468] chore(deps): update docker/metadata-action action to v5.5.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/uffizzi-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index 6eac098448..c1b3bc50ea 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -61,7 +61,7 @@ jobs: - name: Docker metadata id: meta - uses: docker/metadata-action@dbef88086f6cef02e264edb7dbf63250c17cef6c # v5.5.0 + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: registry.uffizzi.com/${{ env.UUID_TAG_APP }} tags: type=raw,value=60d From 6bdc749a628f20a7f98473c6f5f730aa0e0a1f63 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 12:00:42 +0000 Subject: [PATCH 252/468] chore(deps): update microsoft/setup-msbuild action to v1.3.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_e2e-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 802247341f..eb9da86c52 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -61,7 +61,7 @@ jobs: python-version: '3.10' - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.3.2 + uses: microsoft/setup-msbuild@v1.3.3 - name: Setup gyp env run: | From f9819a4f9120b0215c6b8283d2efce45e99aa026 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 13:55:59 +0000 Subject: [PATCH 253/468] chore(deps): update dependency @types/express-serve-static-core to v4.17.43 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 60502016df..cb0d9d01f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18153,14 +18153,14 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33, @types/express-serve-static-core@npm:^4.17.5": - version: 4.17.42 - resolution: "@types/express-serve-static-core@npm:4.17.42" + version: 4.17.43 + resolution: "@types/express-serve-static-core@npm:4.17.43" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" "@types/send": "*" - checksum: 58273f80fcc94de42691f48e22542e69f0b17863378e3216ce8b782ace012f32241bfeb02a2be837f0e2b4ef96e916979adc30bbfea13f6545bd3ab81b7d2773 + checksum: 08e940cae52eb1388a7b5f61d65f028e783add77d1854243ae920a6a2dfb5febb6acaafbcf38be9d678b0411253b9bc325893c463a93302405f24135664ab1e4 languageName: node linkType: hard From 3a0038f22e85e3e30cdbd576e0906eedff61cbc4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 14:11:47 +0000 Subject: [PATCH 254/468] fix(deps): update dependency @graphiql/react to v0.20.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 60502016df..0d5555649d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11402,8 +11402,8 @@ __metadata: linkType: hard "@graphiql/react@npm:^0.20.0, @graphiql/react@npm:^0.20.2": - version: 0.20.2 - resolution: "@graphiql/react@npm:0.20.2" + version: 0.20.3 + resolution: "@graphiql/react@npm:0.20.3" dependencies: "@graphiql/toolkit": ^0.9.1 "@headlessui/react": ^1.7.15 @@ -11424,7 +11424,7 @@ __metadata: graphql: ^15.5.0 || ^16.0.0 react: ^16.8.0 || ^17 || ^18 react-dom: ^16.8.0 || ^17 || ^18 - checksum: 76bd00fd144b1e3044e3239d80fbda49795e5eb41d222da373af11819d06f657a430eff6853e93724134538f3c5e619e0ecf9111b0818fe14569edb290106c84 + checksum: e8b362bd67ff5499c8db64097f9a4050782be53420e27b4c84ad1eba99c9615984257b7a19d0fd9c6f72ce92366321014b04762b400403ee52f53248dd7a8785 languageName: node linkType: hard From f46da34bea3e1c8e6df1a1c3494f165314ea4058 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 15:30:29 +0000 Subject: [PATCH 255/468] fix(deps): update dependency graphiql to v3.1.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2c7f3f3bcd..d74930d0fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11400,7 +11400,7 @@ __metadata: languageName: node linkType: hard -"@graphiql/react@npm:^0.20.0, @graphiql/react@npm:^0.20.2": +"@graphiql/react@npm:^0.20.0, @graphiql/react@npm:^0.20.2, @graphiql/react@npm:^0.20.3": version: 0.20.3 resolution: "@graphiql/react@npm:0.20.3" dependencies: @@ -28702,7 +28702,7 @@ __metadata: languageName: node linkType: hard -"graphiql@npm:3.1.0, graphiql@npm:^3.0.6": +"graphiql@npm:3.1.0": version: 3.1.0 resolution: "graphiql@npm:3.1.0" dependencies: @@ -28718,6 +28718,22 @@ __metadata: languageName: node linkType: hard +"graphiql@npm:^3.0.6": + version: 3.1.1 + resolution: "graphiql@npm:3.1.1" + dependencies: + "@graphiql/react": ^0.20.3 + "@graphiql/toolkit": ^0.9.1 + graphql-language-service: ^5.2.0 + markdown-it: ^12.2.0 + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + checksum: fa0e6a6854b688a80d2d560c07c042c4d63a45ab1ebdb5b56a081a5a2aea6f77b2ef10afb73e071bbb22eb293048a9b72760e91459fe66704afce56271b13ba5 + languageName: node + linkType: hard + "graphlib@npm:^2.1.8": version: 2.1.8 resolution: "graphlib@npm:2.1.8" From c66312a3919ec840185b89d8f9dd47c03ef9b92f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:17:44 +0000 Subject: [PATCH 256/468] fix(deps): update dependency react-virtualized-auto-sizer to v1.0.22 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d74930d0fb..2c26cb1a1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39048,12 +39048,12 @@ __metadata: linkType: hard "react-virtualized-auto-sizer@npm:^1.0.11": - version: 1.0.21 - resolution: "react-virtualized-auto-sizer@npm:1.0.21" + version: 1.0.22 + resolution: "react-virtualized-auto-sizer@npm:1.0.22" peerDependencies: react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 - checksum: 9c0929a0363b3b3b10ad65ac57d7562a20966a9c38c28c10dfd296d0a6a3e678319dce2384c5177f43965c2b7f226f5766deffc4b7eaaab0ee7c2cfe0c7d6b48 + checksum: dc3fc29437b7179de71f77e5be3514e8789944132d7eb03f7157f783ec167ad9bebf1d371c135cf74fc5787dfac313a2914a5d23b45e978ae77be36c673342e5 languageName: node linkType: hard From 353d6e054fb0e20020c08d1931132bb4cde36c6a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:19:00 +0000 Subject: [PATCH 257/468] fix(deps): update dependency swagger-ui-react to v5.11.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index d74930d0fb..3f1ac93bc4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3110,13 +3110,13 @@ __metadata: languageName: node linkType: hard -"@babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.15, @babel/runtime-corejs3@npm:^7.23.7": - version: 7.23.8 - resolution: "@babel/runtime-corejs3@npm:7.23.8" +"@babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.15, @babel/runtime-corejs3@npm:^7.23.8": + version: 7.23.9 + resolution: "@babel/runtime-corejs3@npm:7.23.9" dependencies: core-js-pure: ^3.30.2 regenerator-runtime: ^0.14.0 - checksum: e786b79bcb3031bd7433fb4523e43f0acbd386cd7bb5b0a6df6e627c7965706b3d5612211ea3d729ce4459ba1d1b654ccdd8aefe791c6413f70882ee1be903b9 + checksum: 715d916b6cf60013597aa9a5823fd04a9c6cc6ba6221bb8611e76c369cbf1b4baf1d0ad63b6522736593a38e89a6502213a38f57a9c24e5586628e930c4fd52c languageName: node linkType: hard @@ -25116,10 +25116,10 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:=3.0.6": - version: 3.0.6 - resolution: "dompurify@npm:3.0.6" - checksum: e5c6cdc5fe972a9d0859d939f1d86320de275be00bbef7bd5591c80b1e538935f6ce236624459a1b0c84ecd7c6a1e248684aa4637512659fccc0ce7c353828a6 +"dompurify@npm:=3.0.8": + version: 3.0.8 + resolution: "dompurify@npm:3.0.8" + checksum: cac660ccae15a9603f06a85344da868a4c3732d8b57f7998de0f421eb4b9e67d916be52e9bb2a57b2f95b49e994cc50bcd06bb87f2cb2849cf058bdf15266237 languageName: node linkType: hard @@ -38726,7 +38726,7 @@ __metadata: languageName: node linkType: hard -"react-redux@npm:^9.0.4": +"react-redux@npm:^9.1.0": version: 9.1.0 resolution: "react-redux@npm:9.1.0" dependencies: @@ -39359,7 +39359,7 @@ __metadata: languageName: node linkType: hard -"redux@npm:^5.0.0": +"redux@npm:^5.0.1": version: 5.0.1 resolution: "redux@npm:5.0.1" checksum: e74affa9009dd5d994878b9a1ce30d6569d986117175056edb003de2651c05b10fe7819d6fa94aea1a94de9a82f252f986547f007a2fbeb35c317a2e5f5ecf2c @@ -39669,7 +39669,7 @@ __metadata: languageName: node linkType: hard -"reselect@npm:^5.0.1": +"reselect@npm:^5.1.0": version: 5.1.0 resolution: "reselect@npm:5.1.0" checksum: 5bc9c5d03d7caea00d0c0e24330bf23d91801227346fec1cef6a60988ab8d3dd7cee76e6994ca0915bc1c20845bb2bd929b95753763e0a9db74c0f9dff5cb845 @@ -42086,16 +42086,16 @@ __metadata: linkType: hard "swagger-ui-react@npm:^5.0.0": - version: 5.11.0 - resolution: "swagger-ui-react@npm:5.11.0" + version: 5.11.2 + resolution: "swagger-ui-react@npm:5.11.2" dependencies: - "@babel/runtime-corejs3": ^7.23.7 + "@babel/runtime-corejs3": ^7.23.8 "@braintree/sanitize-url": =7.0.0 base64-js: ^1.5.1 classnames: ^2.5.1 css.escape: 1.5.1 deep-extend: 0.6.0 - dompurify: =3.0.6 + dompurify: =3.0.8 ieee754: ^1.2.1 immutable: ^3.x.x js-file-download: ^0.4.12 @@ -42110,12 +42110,12 @@ __metadata: react-immutable-proptypes: 2.2.0 react-immutable-pure-component: ^2.2.0 react-inspector: ^6.0.1 - react-redux: ^9.0.4 + react-redux: ^9.1.0 react-syntax-highlighter: ^15.5.0 - redux: ^5.0.0 + redux: ^5.0.1 redux-immutable: ^4.0.0 remarkable: ^2.0.1 - reselect: ^5.0.1 + reselect: ^5.1.0 serialize-error: ^8.1.0 sha.js: ^2.4.11 swagger-client: ^3.25.0 @@ -42126,7 +42126,7 @@ __metadata: peerDependencies: react: ">=16.8.0 <19" react-dom: ">=16.8.0 <19" - checksum: f46acfe503f80566f5a28c5a7a27091f80a764d4602e81ef2dc59466097bdf0e6f6568b866a0a90ab23db39a88fe6f5e4d96b3d5367d05b8ecedb256477fb1cb + checksum: 741780967d82cebb12754c1d085f182a2e19e8015aa80b04f9f8c985671a9a447ef7db7f1cf79a2c2a32078d6ababd3436262d81d9e531e5c74f74f1cb392b11 languageName: node linkType: hard From 5fbfcc43ea0cf9ed2e3bb486b930a32f64f01e85 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:33:28 +0000 Subject: [PATCH 258/468] fix(deps): update dependency zod-to-json-schema to v3.22.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2c26cb1a1b..748abe6c79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45364,11 +45364,11 @@ __metadata: linkType: hard "zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4": - version: 3.22.3 - resolution: "zod-to-json-schema@npm:3.22.3" + version: 3.22.4 + resolution: "zod-to-json-schema@npm:3.22.4" peerDependencies: zod: ^3.22.4 - checksum: 2747a3d1514f579006939c0edd6a420acae65ad016df223b09c4a542cbc8c0ae61b4d7b391228a211cde973635ed49c47b1449791982f3b32d799319bb174f42 + checksum: 22f89d505cc3d93020de38e4471362fbecd73a8df58017553ad57fb14e69b6f2f88bcdfe9f84b291442ed0654f97344d517af291d23848e43e6e208ee23dac2b languageName: node linkType: hard From 7f11009c50a30bcff2cc98be9fd97e5f4c86765f Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Wed, 10 Jan 2024 17:45:29 -0500 Subject: [PATCH 259/468] [feat] include stack trace display option in error page Signed-off-by: Jithen Shriyan --- .changeset/real-grapes-sing.md | 5 ++ .../src/layout/ErrorPage/ErrorPage.test.tsx | 11 +++ .../src/layout/ErrorPage/ErrorPage.tsx | 70 ++++++++++++------- 3 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 .changeset/real-grapes-sing.md diff --git a/.changeset/real-grapes-sing.md b/.changeset/real-grapes-sing.md new file mode 100644 index 0000000000..80fb9edab0 --- /dev/null +++ b/.changeset/real-grapes-sing.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Included stack trace display option in ErrorPage component diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx index 2efdc5da33..64a209e4b8 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx @@ -99,4 +99,15 @@ describe('', () => { 'https://error-page-test-support-url.com', ); }); + + it('should render with stack trace if stack is provided', async () => { + const { getByText } = await renderInTestApp( + , + ); + expect(getByText(/this is my stack trace!/i)).toBeInTheDocument(); + }); }); diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 4639e8de59..830abe6ec3 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -15,11 +15,13 @@ */ import Grid from '@material-ui/core/Grid'; +import Box from '@material-ui/core/Box'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import React from 'react'; import { useNavigate } from 'react-router-dom'; import { Link } from '../../components/Link'; +import { LogViewer } from '../../components'; import { useSupportConfig } from '../../hooks'; import { MicDrop } from './MicDrop'; @@ -28,6 +30,7 @@ interface IErrorPageProps { statusMessage: string; additionalInfo?: React.ReactNode; supportUrl?: string; + stack?: string; } /** @public */ @@ -35,12 +38,18 @@ export type ErrorPageClassKey = 'container' | 'title' | 'subtitle'; const useStyles = makeStyles( theme => ({ - container: { + parent: { padding: theme.spacing(8), [theme.breakpoints.down('xs')]: { padding: theme.spacing(2), }, }, + container: { + marginBottom: theme.spacing(5), + [theme.breakpoints.down('xs')]: { + marginBottom: theme.spacing(4), + }, + }, title: { paddingBottom: theme.spacing(5), [theme.breakpoints.down('xs')]: { @@ -62,37 +71,44 @@ const useStyles = makeStyles( * */ export function ErrorPage(props: IErrorPageProps) { - const { status, statusMessage, additionalInfo, supportUrl } = props; + const { status, statusMessage, additionalInfo, supportUrl, stack } = props; const classes = useStyles(); const navigate = useNavigate(); const support = useSupportConfig(); return ( - - - - ERROR {status}: {statusMessage} - - - {additionalInfo} - - - Looks like someone dropped the mic! - - - navigate(-1)}> - Go back - - ... or please{' '} - contact support if you - think this is a bug. - + + + + + ERROR {status}: {statusMessage} + + + {additionalInfo} + + + Looks like someone dropped the mic! + + + navigate(-1)} + > + Go back + + ... or please{' '} + contact support if you + think this is a bug. + + + - - + {stack && } + ); } From 589006c37d75cbb7055aa4ebf3533709278f211f Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Thu, 11 Jan 2024 16:09:14 -0500 Subject: [PATCH 260/468] [feat] include stack trace in accordion by default and update existing error refs Signed-off-by: Jithen Shriyan --- .../app-defaults/src/defaults/components.tsx | 2 +- .../src/layout/ErrorPage/ErrorPage.tsx | 33 +++++++++++++++++-- .../components/PlaylistPage/PlaylistPage.tsx | 1 + .../components/ActionsPage/ActionsPage.tsx | 1 + 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/app-defaults/src/defaults/components.tsx b/packages/app-defaults/src/defaults/components.tsx index 1e2fddca07..9b58694f2b 100644 --- a/packages/app-defaults/src/defaults/components.tsx +++ b/packages/app-defaults/src/defaults/components.tsx @@ -49,7 +49,7 @@ const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => { // TODO: figure out a nicer way to handle routing on the error page, when it can be done. return ( - + ); }; diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 830abe6ec3..823571d8b3 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -21,7 +21,7 @@ import Typography from '@material-ui/core/Typography'; import React from 'react'; import { useNavigate } from 'react-router-dom'; import { Link } from '../../components/Link'; -import { LogViewer } from '../../components'; +import { CopyTextButton, WarningPanel } from '../../components'; import { useSupportConfig } from '../../hooks'; import { MicDrop } from './MicDrop'; @@ -60,6 +60,17 @@ const useStyles = makeStyles( subtitle: { color: theme.palette.textSubtle, }, + text: { + fontFamily: 'monospace', + whiteSpace: 'pre', + overflowX: 'auto', + marginRight: theme.spacing(2), + }, + copyTextContainer: { + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'flex-start', + }, }), { name: 'BackstageErrorPage' }, ); @@ -108,7 +119,25 @@ export function ErrorPage(props: IErrorPageProps) { - {stack && } + {stack && ( + + + + Stack Trace + + {stack} + + + + + + + + )} ); } diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistPage.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistPage.tsx index 4c9a0edbb7..b17fbd2fb7 100644 --- a/plugins/playlist/src/components/PlaylistPage/PlaylistPage.tsx +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistPage.tsx @@ -84,6 +84,7 @@ export const PlaylistPage = () => { ); } diff --git a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx index 9957d22456..9c0276fa28 100644 --- a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx +++ b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx @@ -142,6 +142,7 @@ export const ActionsPage = () => { ); } From e5117941859506885885b830d0bb8fcb9427bd85 Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Thu, 11 Jan 2024 23:10:00 -0500 Subject: [PATCH 261/468] [feat] reposition stack trace accordion below content grid item and adjust colors Signed-off-by: Jithen Shriyan --- .../src/layout/ErrorPage/ErrorPage.tsx | 110 +++++++++--------- .../src/layout/ErrorPage/MicDrop.tsx | 2 +- 2 files changed, 54 insertions(+), 58 deletions(-) diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 823571d8b3..9bbf600aee 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -15,7 +15,6 @@ */ import Grid from '@material-ui/core/Grid'; -import Box from '@material-ui/core/Box'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import React from 'react'; @@ -38,18 +37,12 @@ export type ErrorPageClassKey = 'container' | 'title' | 'subtitle'; const useStyles = makeStyles( theme => ({ - parent: { + container: { padding: theme.spacing(8), [theme.breakpoints.down('xs')]: { padding: theme.spacing(2), }, }, - container: { - marginBottom: theme.spacing(5), - [theme.breakpoints.down('xs')]: { - marginBottom: theme.spacing(4), - }, - }, title: { paddingBottom: theme.spacing(5), [theme.breakpoints.down('xs')]: { @@ -60,6 +53,13 @@ const useStyles = makeStyles( subtitle: { color: theme.palette.textSubtle, }, + goBackTitle: { + color: theme.palette.textSubtle, + marginBottom: theme.spacing(5), + [theme.breakpoints.down('xs')]: { + marginBottom: theme.spacing(4), + }, + }, text: { fontFamily: 'monospace', whiteSpace: 'pre', @@ -88,56 +88,52 @@ export function ErrorPage(props: IErrorPageProps) { const support = useSupportConfig(); return ( - - - - - ERROR {status}: {statusMessage} - - - {additionalInfo} - - - Looks like someone dropped the mic! - - - navigate(-1)} - > - Go back - - ... or please{' '} - contact support if you - think this is a bug. - - + + + + ERROR {status}: {statusMessage} + + + {additionalInfo} + + + Looks like someone dropped the mic! + + + navigate(-1)}> + Go back + + ... or please{' '} + contact support if you + think this is a bug. + + {stack && ( + + + + Stack Trace + + {stack} + + + + + + + + )} + + - {stack && ( - - - - Stack Trace - - {stack} - - - - - - - - )} - + ); } diff --git a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx index d55c50bd29..a16de3f5e6 100644 --- a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx +++ b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx @@ -21,7 +21,7 @@ import MicDropSvgUrl from './mic-drop.svg'; const useStyles = makeStyles( theme => ({ micDrop: { - maxWidth: '60%', + maxWidth: '80%', bottom: theme.spacing(2), right: theme.spacing(2), [theme.breakpoints.down('xs')]: { From 969daf7d6b0a754430cd68875b7ecfb586c74bd1 Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Thu, 11 Jan 2024 23:49:44 -0500 Subject: [PATCH 262/468] [feat] add overflow scroll with fixed max accordion height Signed-off-by: Jithen Shriyan --- packages/core-components/src/layout/ErrorPage/ErrorPage.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 9bbf600aee..9b10067fa6 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -60,7 +60,9 @@ const useStyles = makeStyles( marginBottom: theme.spacing(4), }, }, - text: { + stackTraceText: { + maxHeight: '30vh', + overflow: 'scroll', fontFamily: 'monospace', whiteSpace: 'pre', overflowX: 'auto', @@ -117,7 +119,7 @@ export function ErrorPage(props: IErrorPageProps) { Stack Trace From 39579f03b74972f17b505b96f20b7c61ca06522d Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Mon, 22 Jan 2024 16:18:24 -0500 Subject: [PATCH 263/468] [update] use code snippet component, remove warning panel Signed-off-by: Jithen Shriyan --- .changeset/real-grapes-sing.md | 5 +- .../src/layout/ErrorPage/ErrorPage.tsx | 50 ++---------- .../src/layout/ErrorPage/MicDrop.tsx | 2 +- .../src/layout/ErrorPage/StackDetails.tsx | 77 +++++++++++++++++++ 4 files changed, 87 insertions(+), 47 deletions(-) create mode 100644 packages/core-components/src/layout/ErrorPage/StackDetails.tsx diff --git a/.changeset/real-grapes-sing.md b/.changeset/real-grapes-sing.md index 80fb9edab0..4656d57a2d 100644 --- a/.changeset/real-grapes-sing.md +++ b/.changeset/real-grapes-sing.md @@ -1,5 +1,8 @@ --- '@backstage/core-components': patch +'@backstage/app-defaults': minor +'@backstage/playlist': patch +'@backstage/scaffolder': minor --- -Included stack trace display option in ErrorPage component +Added stack trace display to `ErrorPage` and updated existing refs diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 9b10067fa6..131df3da3a 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -20,9 +20,9 @@ import Typography from '@material-ui/core/Typography'; import React from 'react'; import { useNavigate } from 'react-router-dom'; import { Link } from '../../components/Link'; -import { CopyTextButton, WarningPanel } from '../../components'; import { useSupportConfig } from '../../hooks'; import { MicDrop } from './MicDrop'; +import { StackDetails } from './StackDetails'; interface IErrorPageProps { status?: string; @@ -53,26 +53,6 @@ const useStyles = makeStyles( subtitle: { color: theme.palette.textSubtle, }, - goBackTitle: { - color: theme.palette.textSubtle, - marginBottom: theme.spacing(5), - [theme.breakpoints.down('xs')]: { - marginBottom: theme.spacing(4), - }, - }, - stackTraceText: { - maxHeight: '30vh', - overflow: 'scroll', - fontFamily: 'monospace', - whiteSpace: 'pre', - overflowX: 'auto', - marginRight: theme.spacing(2), - }, - copyTextContainer: { - display: 'flex', - justifyContent: 'flex-end', - alignItems: 'flex-start', - }, }), { name: 'BackstageErrorPage' }, ); @@ -91,7 +71,7 @@ export function ErrorPage(props: IErrorPageProps) { return ( - + Looks like someone dropped the mic! - + navigate(-1)}> Go back @@ -113,29 +93,9 @@ export function ErrorPage(props: IErrorPageProps) { contact support if you think this is a bug. - {stack && ( - - - - Stack Trace - - {stack} - - - - - - - - )} - - - + {stack && } + ); } diff --git a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx index a16de3f5e6..d55c50bd29 100644 --- a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx +++ b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx @@ -21,7 +21,7 @@ import MicDropSvgUrl from './mic-drop.svg'; const useStyles = makeStyles( theme => ({ micDrop: { - maxWidth: '80%', + maxWidth: '60%', bottom: theme.spacing(2), right: theme.spacing(2), [theme.breakpoints.down('xs')]: { diff --git a/packages/core-components/src/layout/ErrorPage/StackDetails.tsx b/packages/core-components/src/layout/ErrorPage/StackDetails.tsx new file mode 100644 index 0000000000..b46a937485 --- /dev/null +++ b/packages/core-components/src/layout/ErrorPage/StackDetails.tsx @@ -0,0 +1,77 @@ +/* + * Copyright 2024 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 Typography from '@material-ui/core/Typography'; +import React from 'react'; +import { useState } from 'react'; +import { Link } from '../../components/Link'; +import { CodeSnippet } from '../../components'; +import { makeStyles } from '@material-ui/core/styles'; + +interface IStackDetailsProps { + stack: string; +} + +const useStyles = makeStyles( + theme => ({ + title: { + paddingBottom: theme.spacing(5), + [theme.breakpoints.down('xs')]: { + paddingBottom: theme.spacing(4), + fontSize: theme.typography.h3.fontSize, + }, + }, + }), + { name: 'BackstageErrorPageStackDetails' }, +); + +/** + * Error page details with stack trace + * + * @public + * + */ +export function StackDetails(props: IStackDetailsProps) { + const { stack } = props; + const classes = useStyles(); + + const [detailsOpen, setDetailsOpen] = useState(false); + + if (!detailsOpen) { + return ( + + setDetailsOpen(true)}> + Show more details + + + ); + } + + return ( + <> + + setDetailsOpen(false)}> + Show less details + + + + + ); +} From 6fc1ed6b012b9c78f309ccf471ebb1c5427314c1 Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Mon, 22 Jan 2024 16:28:38 -0500 Subject: [PATCH 264/468] [update] update show details test case Signed-off-by: Jithen Shriyan --- .../core-components/src/layout/ErrorPage/ErrorPage.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx index 64a209e4b8..d7036d0a5b 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.test.tsx @@ -100,7 +100,7 @@ describe('', () => { ); }); - it('should render with stack trace if stack is provided', async () => { + it('should render show details if stack is provided', async () => { const { getByText } = await renderInTestApp( ', () => { stack="this is my stack trace!" />, ); - expect(getByText(/this is my stack trace!/i)).toBeInTheDocument(); + expect(getByText(/Show more details/i)).toBeInTheDocument(); }); }); From c70ab42a31f16b4f76e9923068d5fa16f8b7fc36 Mon Sep 17 00:00:00 2001 From: Jithen Shriyan Date: Fri, 26 Jan 2024 20:05:58 -0500 Subject: [PATCH 265/468] [fix] changeset package names Signed-off-by: Jithen Shriyan --- .changeset/real-grapes-sing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/real-grapes-sing.md b/.changeset/real-grapes-sing.md index 4656d57a2d..484fc7969a 100644 --- a/.changeset/real-grapes-sing.md +++ b/.changeset/real-grapes-sing.md @@ -1,8 +1,8 @@ --- '@backstage/core-components': patch '@backstage/app-defaults': minor -'@backstage/playlist': patch -'@backstage/scaffolder': minor +'@backstage/plugin-playlist': patch +'@backstage/plugin-scaffolder': minor --- Added stack trace display to `ErrorPage` and updated existing refs From 27adf613006a808c47fe78da216a2ed41e13cabd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 18:22:26 +0000 Subject: [PATCH 266/468] fix(deps): update swc monorepo Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 6 +++--- storybook/yarn.lock | 6 +++--- yarn.lock | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 4c9f348fe3..8bbef32fa1 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -11500,12 +11500,12 @@ __metadata: linkType: hard "swc-loader@npm:^0.2.3": - version: 0.2.3 - resolution: "swc-loader@npm:0.2.3" + version: 0.2.4 + resolution: "swc-loader@npm:0.2.4" peerDependencies: "@swc/core": ^1.2.147 webpack: ">=2" - checksum: 010d84d399525c0185d36d62c86c55ae017e7a90046bc8a39be4b7e07526924037868049f6037bc966da98151cb2600934b96a66279b742d3c413a718b427251 + checksum: f23bfe8900b35abdcb9910a2749f3c9d66edf5c660afc67fcf7983647eaec322e024d1edd3bd9fd48bd3191eea0616f67b5f8b5f923e3a648fa5b448683c3213 languageName: node linkType: hard diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 54f50cbbb5..dff62bdc51 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -10906,12 +10906,12 @@ __metadata: linkType: hard "swc-loader@npm:^0.2.3": - version: 0.2.3 - resolution: "swc-loader@npm:0.2.3" + version: 0.2.4 + resolution: "swc-loader@npm:0.2.4" peerDependencies: "@swc/core": ^1.2.147 webpack: ">=2" - checksum: 010d84d399525c0185d36d62c86c55ae017e7a90046bc8a39be4b7e07526924037868049f6037bc966da98151cb2600934b96a66279b742d3c413a718b427251 + checksum: f23bfe8900b35abdcb9910a2749f3c9d66edf5c660afc67fcf7983647eaec322e024d1edd3bd9fd48bd3191eea0616f67b5f8b5f923e3a648fa5b448683c3213 languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index 748abe6c79..a8da5c8972 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17307,14 +17307,14 @@ __metadata: linkType: hard "@swc/jest@npm:^0.2.22": - version: 0.2.31 - resolution: "@swc/jest@npm:0.2.31" + version: 0.2.34 + resolution: "@swc/jest@npm:0.2.34" dependencies: "@jest/create-cache-key-function": ^29.7.0 jsonc-parser: ^3.2.0 peerDependencies: "@swc/core": "*" - checksum: 1dae629a81a623de6a662dcac34a97f91986457f172bec1c8fc360d4407c2499dcbf73d420123996809366900ef785b2cbfda4f2f6acf2f1729e85f6f2b791b7 + checksum: 8f92f9f45661dd728876d6b9bb619e96b463c9d2940c9038ab7fb1cd48f04e4b1763da39a0d0473750fee0e825bb9a9e3653b9a884d928da8f40004e7ee9554f languageName: node linkType: hard @@ -42131,12 +42131,12 @@ __metadata: linkType: hard "swc-loader@npm:^0.2.3": - version: 0.2.3 - resolution: "swc-loader@npm:0.2.3" + version: 0.2.4 + resolution: "swc-loader@npm:0.2.4" peerDependencies: "@swc/core": ^1.2.147 webpack: ">=2" - checksum: 010d84d399525c0185d36d62c86c55ae017e7a90046bc8a39be4b7e07526924037868049f6037bc966da98151cb2600934b96a66279b742d3c413a718b427251 + checksum: f23bfe8900b35abdcb9910a2749f3c9d66edf5c660afc67fcf7983647eaec322e024d1edd3bd9fd48bd3191eea0616f67b5f8b5f923e3a648fa5b448683c3213 languageName: node linkType: hard From 9ed855a0f10623989b1fda0a19f1e30129b285af Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:03:08 +0000 Subject: [PATCH 267/468] chore(deps): update dependency @testing-library/react to v14.2.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 00e414c8d5..ed4022ac6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17475,8 +17475,8 @@ __metadata: linkType: hard "@testing-library/react@npm:^14.0.0": - version: 14.1.2 - resolution: "@testing-library/react@npm:14.1.2" + version: 14.2.1 + resolution: "@testing-library/react@npm:14.2.1" dependencies: "@babel/runtime": ^7.12.5 "@testing-library/dom": ^9.0.0 @@ -17484,7 +17484,7 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 0269903e53412cf96fddb55c8a97a9987a89c3308d71fa1418fe61c47d275445e7044c5387f57cf39b8cda319a41623dbad2cce7a17016aed3a9e85185aac75a + checksum: 7054ae69a0e06c0777da8105fa08fac7e8dac570476a065285d7b993947acda5c948598764a203ebaac759c161c562d6712f19f5bd08be3f09a07e23baee5426 languageName: node linkType: hard From 2982c8f55bd729b119b67462d3ca520421986b35 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:04:08 +0000 Subject: [PATCH 268/468] chore(deps): update dependency @types/pg to v8.11.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 00e414c8d5..f11346e094 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18820,13 +18820,13 @@ __metadata: linkType: hard "@types/pg@npm:^8.6.6": - version: 8.10.9 - resolution: "@types/pg@npm:8.10.9" + version: 8.11.0 + resolution: "@types/pg@npm:8.11.0" dependencies: "@types/node": "*" pg-protocol: "*" pg-types: ^4.0.1 - checksum: c0c750af1f0945fe31c2793931da33bf596476ec8b52babfdb20f61006570c1c6c7f62f2dedd0cb9aefd2cddab238f2ed279337d3b2aa734ddd1ee8958e2355e + checksum: 8ae18abce86a012afdd68b2fb85a9fd0e9529f2dae8ca64311a4804fc8423441d605df51f547170efa4584c6ee9f919b4f5f731d5a6221386c5d04560de4334c languageName: node linkType: hard From 867d9f5bcaf58f171372deff9a6d1d7982853cca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 01:24:01 +0000 Subject: [PATCH 269/468] chore(deps): update dependency better-sqlite3 to v9.4.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ed4022ac6c..640b349a22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21557,13 +21557,13 @@ __metadata: linkType: hard "better-sqlite3@npm:^9.0.0": - version: 9.3.0 - resolution: "better-sqlite3@npm:9.3.0" + version: 9.4.0 + resolution: "better-sqlite3@npm:9.4.0" dependencies: bindings: ^1.5.0 node-gyp: latest prebuild-install: ^7.1.1 - checksum: 03dedaeef92aaa5e963e9fad5849afca3568c987b4d6f36957f9879137c994aa18f9654fe7bcc3804620ede55eabd19ff9bcba03ad0bf985aa8bd5561e4aaa43 + checksum: a1a470fae20dfba82d6e74ae90b35ea8996c60922e95574162732d6e076e84c0c90fc4ff77ab8c27554671899eb15f284e2c8de5e4ee406aa9f7eb170eca5bee languageName: node linkType: hard From 52ae6b9b6e1b0d32d1f2c4c70be804e2ce16ffa1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 04:58:59 +0000 Subject: [PATCH 270/468] chore(deps): update dependency esbuild to ^0.20.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-f4b1d70.md | 6 + packages/cli/package.json | 2 +- plugins/scaffolder-backend/package.json | 2 +- yarn.lock | 247 +++++++++++++++++++++++- 4 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 .changeset/renovate-f4b1d70.md diff --git a/.changeset/renovate-f4b1d70.md b/.changeset/renovate-f4b1d70.md new file mode 100644 index 0000000000..7b07ee8364 --- /dev/null +++ b/.changeset/renovate-f4b1d70.md @@ -0,0 +1,6 @@ +--- +'@backstage/cli': patch +'@backstage/plugin-scaffolder-backend': patch +--- + +Updated dependency `esbuild` to `^0.20.0`. diff --git a/packages/cli/package.json b/packages/cli/package.json index e02616574a..682e7dc09d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -78,7 +78,7 @@ "css-loader": "^6.5.1", "ctrlc-windows": "^2.1.0", "diff": "^5.0.0", - "esbuild": "^0.19.0", + "esbuild": "^0.20.0", "esbuild-loader": "^2.18.0", "eslint": "^8.6.0", "eslint-config-prettier": "^8.3.0", diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 274a78f844..356c10bf47 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -96,7 +96,7 @@ "@types/nunjucks": "^3.1.4", "@types/supertest": "^2.0.8", "@types/zen-observable": "^0.8.0", - "esbuild": "^0.19.0", + "esbuild": "^0.20.0", "strip-ansi": "^7.1.0", "supertest": "^6.1.3", "wait-for-expect": "^3.0.2" diff --git a/yarn.lock b/yarn.lock index ed4022ac6c..5e05f600af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3625,7 +3625,7 @@ __metadata: ctrlc-windows: ^2.1.0 del: ^7.0.0 diff: ^5.0.0 - esbuild: ^0.19.0 + esbuild: ^0.20.0 esbuild-loader: ^2.18.0 eslint: ^8.6.0 eslint-config-prettier: ^8.3.0 @@ -8475,7 +8475,7 @@ __metadata: "@types/nunjucks": ^3.1.4 "@types/supertest": ^2.0.8 "@types/zen-observable": ^0.8.0 - esbuild: ^0.19.0 + esbuild: ^0.20.0 express: ^4.17.1 express-promise-router: ^4.1.0 fs-extra: 10.1.0 @@ -10634,6 +10634,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/aix-ppc64@npm:0.20.0" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-arm64@npm:0.16.17" @@ -10655,6 +10662,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/android-arm64@npm:0.20.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-arm@npm:0.16.17" @@ -10676,6 +10690,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/android-arm@npm:0.20.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-x64@npm:0.16.17" @@ -10697,6 +10718,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/android-x64@npm:0.20.0" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/darwin-arm64@npm:0.16.17" @@ -10718,6 +10746,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/darwin-arm64@npm:0.20.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/darwin-x64@npm:0.16.17" @@ -10739,6 +10774,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/darwin-x64@npm:0.20.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/freebsd-arm64@npm:0.16.17" @@ -10760,6 +10802,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/freebsd-arm64@npm:0.20.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/freebsd-x64@npm:0.16.17" @@ -10781,6 +10830,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/freebsd-x64@npm:0.20.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-arm64@npm:0.16.17" @@ -10802,6 +10858,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-arm64@npm:0.20.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-arm@npm:0.16.17" @@ -10823,6 +10886,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-arm@npm:0.20.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-ia32@npm:0.16.17" @@ -10844,6 +10914,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-ia32@npm:0.20.0" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-loong64@npm:0.16.17" @@ -10865,6 +10942,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-loong64@npm:0.20.0" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-mips64el@npm:0.16.17" @@ -10886,6 +10970,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-mips64el@npm:0.20.0" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-ppc64@npm:0.16.17" @@ -10907,6 +10998,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-ppc64@npm:0.20.0" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-riscv64@npm:0.16.17" @@ -10928,6 +11026,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-riscv64@npm:0.20.0" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-s390x@npm:0.16.17" @@ -10949,6 +11054,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-s390x@npm:0.20.0" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/linux-x64@npm:0.16.17" @@ -10970,6 +11082,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/linux-x64@npm:0.20.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/netbsd-x64@npm:0.16.17" @@ -10991,6 +11110,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/netbsd-x64@npm:0.20.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/openbsd-x64@npm:0.16.17" @@ -11012,6 +11138,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/openbsd-x64@npm:0.20.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/sunos-x64@npm:0.16.17" @@ -11033,6 +11166,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/sunos-x64@npm:0.20.0" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-arm64@npm:0.16.17" @@ -11054,6 +11194,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/win32-arm64@npm:0.20.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-ia32@npm:0.16.17" @@ -11075,6 +11222,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/win32-ia32@npm:0.20.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/win32-x64@npm:0.16.17" @@ -11096,6 +11250,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.20.0": + version: 0.20.0 + resolution: "@esbuild/win32-x64@npm:0.20.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -25925,7 +26086,87 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.19.0, esbuild@npm:~0.19.10": +"esbuild@npm:^0.20.0": + version: 0.20.0 + resolution: "esbuild@npm:0.20.0" + dependencies: + "@esbuild/aix-ppc64": 0.20.0 + "@esbuild/android-arm": 0.20.0 + "@esbuild/android-arm64": 0.20.0 + "@esbuild/android-x64": 0.20.0 + "@esbuild/darwin-arm64": 0.20.0 + "@esbuild/darwin-x64": 0.20.0 + "@esbuild/freebsd-arm64": 0.20.0 + "@esbuild/freebsd-x64": 0.20.0 + "@esbuild/linux-arm": 0.20.0 + "@esbuild/linux-arm64": 0.20.0 + "@esbuild/linux-ia32": 0.20.0 + "@esbuild/linux-loong64": 0.20.0 + "@esbuild/linux-mips64el": 0.20.0 + "@esbuild/linux-ppc64": 0.20.0 + "@esbuild/linux-riscv64": 0.20.0 + "@esbuild/linux-s390x": 0.20.0 + "@esbuild/linux-x64": 0.20.0 + "@esbuild/netbsd-x64": 0.20.0 + "@esbuild/openbsd-x64": 0.20.0 + "@esbuild/sunos-x64": 0.20.0 + "@esbuild/win32-arm64": 0.20.0 + "@esbuild/win32-ia32": 0.20.0 + "@esbuild/win32-x64": 0.20.0 + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 501b0f540ab68b3843cb9b1be7efa2d90353c8743e99e84931baa1ef5fe1b87934e29becb23cc635a8af45fab223875efa62200589e18d796f0881a655cb9c07 + languageName: node + linkType: hard + +"esbuild@npm:~0.19.10": version: 0.19.12 resolution: "esbuild@npm:0.19.12" dependencies: From 2d6f274efd5e99926eec3ec516f3d913940f214f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 05:08:46 +0000 Subject: [PATCH 271/468] chore(deps): update dependency webpack to v5.90.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- storybook/yarn.lock | 138 ++++++++++++++++++++++---------------------- yarn.lock | 24 ++++---- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 54f50cbbb5..398b0e2052 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -1604,10 +1604,10 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0": - version: 3.1.0 - resolution: "@jridgewell/resolve-uri@npm:3.1.0" - checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.1 + resolution: "@jridgewell/resolve-uri@npm:3.1.1" + checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 languageName: node linkType: hard @@ -1618,30 +1618,30 @@ __metadata: languageName: node linkType: hard -"@jridgewell/source-map@npm:^0.3.2": - version: 0.3.2 - resolution: "@jridgewell/source-map@npm:0.3.2" +"@jridgewell/source-map@npm:^0.3.3": + version: 0.3.5 + resolution: "@jridgewell/source-map@npm:0.3.5" dependencies: "@jridgewell/gen-mapping": ^0.3.0 "@jridgewell/trace-mapping": ^0.3.9 - checksum: 1b83f0eb944e77b70559a394d5d3b3f98a81fcc186946aceb3ef42d036762b52ef71493c6c0a3b7c1d2f08785f53ba2df1277fe629a06e6109588ff4cdcf7482 + checksum: 1ad4dec0bdafbade57920a50acec6634f88a0eb735851e0dda906fa9894e7f0549c492678aad1a10f8e144bfe87f238307bf2a914a1bc85b7781d345417e9f6f languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.14 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" - checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.18 - resolution: "@jridgewell/trace-mapping@npm:0.3.18" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" dependencies: - "@jridgewell/resolve-uri": 3.1.0 - "@jridgewell/sourcemap-codec": 1.4.14 - checksum: 0572669f855260808c16fe8f78f5f1b4356463b11d3f2c7c0b5580c8ba1cbf4ae53efe9f627595830856e57dbac2325ac17eb0c3dd0ec42102e6f227cc289c02 + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: ac7dd2cfe0b479aa1b81776d40d789243131cc792dc8b6b6a028c70fcd6171958ae1a71bf67b618ffe3c0c3feead9870c095ee46a5e30319410d92976b28f498 languageName: node linkType: hard @@ -3031,10 +3031,10 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": - version: 1.0.0 - resolution: "@types/estree@npm:1.0.0" - checksum: 910d97fb7092c6738d30a7430ae4786a38542023c6302b95d46f49420b797f21619cdde11fa92b338366268795884111c2eb10356e4bd2c8ad5b92941e9e6443 +"@types/estree@npm:*, @types/estree@npm:^1.0.5": + version: 1.0.5 + resolution: "@types/estree@npm:1.0.5" + checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a languageName: node linkType: hard @@ -3466,12 +3466,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.5.0, acorn@npm:^8.7.1": - version: 8.8.0 - resolution: "acorn@npm:8.8.0" +"acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.8.2": + version: 8.11.3 + resolution: "acorn@npm:8.11.3" bin: acorn: bin/acorn - checksum: 7270ca82b242eafe5687a11fea6e088c960af712683756abf0791b68855ea9cace3057bd5e998ffcef50c944810c1e0ca1da526d02b32110e13c722aa959afdc + checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c languageName: node linkType: hard @@ -4201,17 +4201,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.12.0, browserslist@npm:^4.14.5, browserslist@npm:^4.20.2, browserslist@npm:^4.21.3": - version: 4.21.3 - resolution: "browserslist@npm:4.21.3" +"browserslist@npm:^4.12.0, browserslist@npm:^4.20.2, browserslist@npm:^4.21.10, browserslist@npm:^4.21.3": + version: 4.22.3 + resolution: "browserslist@npm:4.22.3" dependencies: - caniuse-lite: ^1.0.30001370 - electron-to-chromium: ^1.4.202 - node-releases: ^2.0.6 - update-browserslist-db: ^1.0.5 + caniuse-lite: ^1.0.30001580 + electron-to-chromium: ^1.4.648 + node-releases: ^2.0.14 + update-browserslist-db: ^1.0.13 bin: browserslist: cli.js - checksum: ff512a7bcca1c530e2854bbdfc7be2791d0fb524097a6340e56e1d5924164c7e4e0a9b070de04cdc4c149d15cb4d4275cb7c626ebbce954278a2823aaad2452a + checksum: e62b17348e92143fe58181b02a6a97c4a98bd812d1dc9274673a54f73eec53dbed1c855ebf73e318ee00ee039f23c9a6d0e7629d24f3baef08c7a5b469742d57 languageName: node linkType: hard @@ -4399,10 +4399,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001370": - version: 1.0.30001378 - resolution: "caniuse-lite@npm:1.0.30001378" - checksum: 19f1774da1f62d393ddde55dc091eb3e4f5c5b0ce43f9a9d20e75307a0f329cf8591c836a35a9f6f9fd7c27db7a75e0682245a194acec2e2ba1bc25ef1c3300c +"caniuse-lite@npm:^1.0.30001109, caniuse-lite@npm:^1.0.30001580": + version: 1.0.30001583 + resolution: "caniuse-lite@npm:1.0.30001583" + checksum: 35d34eac99c1f55a2232180254e9b674dd6326a89310e6896863bb0484278c6cdf1e934c9fbfb8ef7b215c47467354051f46dfce571f9b34c413f53b67c55aa1 languageName: node linkType: hard @@ -5300,10 +5300,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.202": - version: 1.4.225 - resolution: "electron-to-chromium@npm:1.4.225" - checksum: 54b5c5550e33ce5df1d2ab71543b9dc24e4dd55dc4650b29cc19b2911b932b072317e662ce3236c500a498ad69e90d2ceebe2433a772e53a337e97bd53cc7dc9 +"electron-to-chromium@npm:^1.4.648": + version: 1.4.656 + resolution: "electron-to-chromium@npm:1.4.656" + checksum: b9e00c81e74ee307141a216ef971efeff8784232a9eaa9074a65687b631029725f8f4ddeefd98c43287339cdadb6c7aba92c01806122f9cae813a95735fcd432 languageName: node linkType: hard @@ -8453,10 +8453,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.6": - version: 2.0.6 - resolution: "node-releases@npm:2.0.6" - checksum: e86a926dc9fbb3b41b4c4a89d998afdf140e20a4e8dbe6c0a807f7b2948b42ea97d7fd3ad4868041487b6e9ee98409829c6e4d84a734a4215dff060a7fbeb4bf +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 languageName: node linkType: hard @@ -10997,15 +10997,15 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.0.3, terser-webpack-plugin@npm:^5.3.7": - version: 5.3.7 - resolution: "terser-webpack-plugin@npm:5.3.7" +"terser-webpack-plugin@npm:^5.0.3, terser-webpack-plugin@npm:^5.3.10": + version: 5.3.10 + resolution: "terser-webpack-plugin@npm:5.3.10" dependencies: - "@jridgewell/trace-mapping": ^0.3.17 + "@jridgewell/trace-mapping": ^0.3.20 jest-worker: ^27.4.5 schema-utils: ^3.1.1 serialize-javascript: ^6.0.1 - terser: ^5.16.5 + terser: ^5.26.0 peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -11015,7 +11015,7 @@ __metadata: optional: true uglify-js: optional: true - checksum: 095e699fdeeb553cdf2c6f75f983949271b396d9c201d7ae9fc633c45c1c1ad14c7257ef9d51ccc62213dd3e97f875870ba31550f6d4f1b6674f2615562da7f7 + checksum: bd6e7596cf815f3353e2a53e79cbdec959a1b0276f5e5d4e63e9d7c3c5bb5306df567729da287d1c7b39d79093e56863c569c42c6c24cc34c76aa313bd2cbcea languageName: node linkType: hard @@ -11032,17 +11032,17 @@ __metadata: languageName: node linkType: hard -"terser@npm:^5.10.0, terser@npm:^5.16.5, terser@npm:^5.3.4": - version: 5.16.9 - resolution: "terser@npm:5.16.9" +"terser@npm:^5.10.0, terser@npm:^5.26.0, terser@npm:^5.3.4": + version: 5.27.0 + resolution: "terser@npm:5.27.0" dependencies: - "@jridgewell/source-map": ^0.3.2 - acorn: ^8.5.0 + "@jridgewell/source-map": ^0.3.3 + acorn: ^8.8.2 commander: ^2.20.0 source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: b373693ee01ce08cc9b9595d57df889acbbc899d929a7fe53662330ce954d53145582f6715c9cc4839d555f5769a28fbeb203155b54e3a9c6c646db292002edd + checksum: c165052cfea061e8512e9b9ba42a098c2ff6382886ae122b040fd5b6153443070cc2dcb4862269f1669c09c716763e856125a355ff984aa72be525d6fffd8729 languageName: node linkType: hard @@ -11477,17 +11477,17 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.5": - version: 1.0.5 - resolution: "update-browserslist-db@npm:1.0.5" +"update-browserslist-db@npm:^1.0.13": + version: 1.0.13 + resolution: "update-browserslist-db@npm:1.0.13" dependencies: escalade: ^3.1.1 picocolors: ^1.0.0 peerDependencies: browserslist: ">= 4.21.0" bin: - browserslist-lint: cli.js - checksum: 7e425fe5dbbebdccf72a84ce70ec47fc74dce561d28f47bc2b84a1c2b84179a862c2261b18ab66a5e73e261c7e2ef9e11c6129112989d4d52e8f75a56bb923f8 + update-browserslist-db: cli.js + checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322 languageName: node linkType: hard @@ -11754,17 +11754,17 @@ __metadata: linkType: hard "webpack@npm:^5.73.0": - version: 5.89.0 - resolution: "webpack@npm:5.89.0" + version: 5.90.1 + resolution: "webpack@npm:5.90.1" dependencies: "@types/eslint-scope": ^3.7.3 - "@types/estree": ^1.0.0 + "@types/estree": ^1.0.5 "@webassemblyjs/ast": ^1.11.5 "@webassemblyjs/wasm-edit": ^1.11.5 "@webassemblyjs/wasm-parser": ^1.11.5 acorn: ^8.7.1 acorn-import-assertions: ^1.9.0 - browserslist: ^4.14.5 + browserslist: ^4.21.10 chrome-trace-event: ^1.0.2 enhanced-resolve: ^5.15.0 es-module-lexer: ^1.2.1 @@ -11778,7 +11778,7 @@ __metadata: neo-async: ^2.6.2 schema-utils: ^3.2.0 tapable: ^2.1.1 - terser-webpack-plugin: ^5.3.7 + terser-webpack-plugin: ^5.3.10 watchpack: ^2.4.0 webpack-sources: ^3.2.3 peerDependenciesMeta: @@ -11786,7 +11786,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 43fe0dbc30e168a685ef5a86759d5016a705f6563b39a240aa00826a80637d4a3deeb8062e709d6a4b05c63e796278244c84b04174704dc4a37bedb0f565c5ed + checksum: a7be844d5720a0c6282fec012e6fa34b1137dff953c5d48bf2ef066a6c27c1dbc92a9b9effc05ee61c9fe269499266db9782073f2d82a589d3c5c966ffc56584 languageName: node linkType: hard diff --git a/yarn.lock b/yarn.lock index ed4022ac6c..2e1e271bf7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18123,10 +18123,10 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": - version: 1.0.0 - resolution: "@types/estree@npm:1.0.0" - checksum: 910d97fb7092c6738d30a7430ae4786a38542023c6302b95d46f49420b797f21619cdde11fa92b338366268795884111c2eb10356e4bd2c8ad5b92941e9e6443 +"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5": + version: 1.0.5 + resolution: "@types/estree@npm:1.0.5" + checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a languageName: node linkType: hard @@ -21943,7 +21943,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.16.0, browserslist@npm:^4.16.6, browserslist@npm:^4.18.1, browserslist@npm:^4.21.10, browserslist@npm:^4.21.9": +"browserslist@npm:^4.0.0, browserslist@npm:^4.16.0, browserslist@npm:^4.16.6, browserslist@npm:^4.18.1, browserslist@npm:^4.21.10, browserslist@npm:^4.21.9": version: 4.21.10 resolution: "browserslist@npm:4.21.10" dependencies: @@ -42327,7 +42327,7 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:*, terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.7": +"terser-webpack-plugin@npm:*, terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.10": version: 5.3.10 resolution: "terser-webpack-plugin@npm:5.3.10" dependencies: @@ -44455,17 +44455,17 @@ __metadata: linkType: hard "webpack@npm:^5, webpack@npm:^5.70.0": - version: 5.89.0 - resolution: "webpack@npm:5.89.0" + version: 5.90.1 + resolution: "webpack@npm:5.90.1" dependencies: "@types/eslint-scope": ^3.7.3 - "@types/estree": ^1.0.0 + "@types/estree": ^1.0.5 "@webassemblyjs/ast": ^1.11.5 "@webassemblyjs/wasm-edit": ^1.11.5 "@webassemblyjs/wasm-parser": ^1.11.5 acorn: ^8.7.1 acorn-import-assertions: ^1.9.0 - browserslist: ^4.14.5 + browserslist: ^4.21.10 chrome-trace-event: ^1.0.2 enhanced-resolve: ^5.15.0 es-module-lexer: ^1.2.1 @@ -44479,7 +44479,7 @@ __metadata: neo-async: ^2.6.2 schema-utils: ^3.2.0 tapable: ^2.1.1 - terser-webpack-plugin: ^5.3.7 + terser-webpack-plugin: ^5.3.10 watchpack: ^2.4.0 webpack-sources: ^3.2.3 peerDependenciesMeta: @@ -44487,7 +44487,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 43fe0dbc30e168a685ef5a86759d5016a705f6563b39a240aa00826a80637d4a3deeb8062e709d6a4b05c63e796278244c84b04174704dc4a37bedb0f565c5ed + checksum: a7be844d5720a0c6282fec012e6fa34b1137dff953c5d48bf2ef066a6c27c1dbc92a9b9effc05ee61c9fe269499266db9782073f2d82a589d3c5c966ffc56584 languageName: node linkType: hard From d612a173be5e80262be9bb2c12ba614d835420c0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 05:13:22 +0000 Subject: [PATCH 272/468] chore(deps): update github/codeql-action action to v3.24.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/verify_codeql.yml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index d8dd0d9a6a..ea52de9ebe 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -66,6 +66,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2 + uses: github/codeql-action/upload-sarif@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index ddf6d2cf28..4c50295f78 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -58,6 +58,6 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} NODE_OPTIONS: --max-old-space-size=7168 - name: Upload Snyk report - uses: github/codeql-action/upload-sarif@v3.23.2 + uses: github/codeql-action/upload-sarif@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 with: sarif_file: snyk.sarif diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index df0dd80118..485414594f 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3.23.2 + uses: github/codeql-action/init@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3.23.2 + uses: github/codeql-action/autobuild@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +80,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3.23.2 + uses: github/codeql-action/analyze@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0 From adc4ca42b7d09d3e9a1f7760abed8668b2dc1fc2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 06:53:45 +0000 Subject: [PATCH 273/468] chore(deps): update step-security/harden-runner action to v2.7.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/automate_area-labels.yml | 2 +- .github/workflows/automate_changeset_feedback.yml | 2 +- .github/workflows/automate_merge_message.yml | 2 +- .github/workflows/automate_stale.yml | 2 +- .github/workflows/ci-noop.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/cron.yml | 2 +- .github/workflows/deploy_docker-image.yml | 2 +- .github/workflows/deploy_microsite.yml | 2 +- .github/workflows/deploy_nightly.yml | 2 +- .github/workflows/deploy_packages.yml | 2 +- .github/workflows/issue.yaml | 2 +- .github/workflows/pr-review-comment-trigger.yaml | 2 +- .github/workflows/pr-review-comment.yaml | 2 +- .github/workflows/pr.yaml | 2 +- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_code-formatting.yml | 2 +- .github/workflows/sync_dependabot-changesets.yml | 2 +- .github/workflows/sync_release-manifest.yml | 2 +- .github/workflows/sync_renovate-changesets.yml | 2 +- .github/workflows/sync_snyk-github-issues.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/sync_version-packages.yml | 2 +- .github/workflows/uffizzi-build.yml | 6 +++--- .github/workflows/uffizzi-preview.yaml | 2 +- .github/workflows/verify_accessibility-noop.yml | 2 +- .github/workflows/verify_accessibility.yml | 2 +- .github/workflows/verify_codeql.yml | 2 +- .github/workflows/verify_docs-quality.yml | 2 +- .github/workflows/verify_e2e-kubernetes-noop.yml | 2 +- .github/workflows/verify_e2e-kubernetes.yml | 2 +- .github/workflows/verify_e2e-linux-noop.yml | 2 +- .github/workflows/verify_e2e-linux.yml | 2 +- .github/workflows/verify_e2e-techdocs.yml | 2 +- .github/workflows/verify_e2e-windows-noop.yml | 2 +- .github/workflows/verify_e2e-windows.yml | 2 +- .github/workflows/verify_fossa.yml | 2 +- .github/workflows/verify_microsite-noop.yml | 2 +- .github/workflows/verify_microsite.yml | 2 +- .github/workflows/verify_storybook-noop.yml | 2 +- .github/workflows/verify_storybook.yml | 2 +- .github/workflows/verify_windows.yml | 2 +- 42 files changed, 45 insertions(+), 45 deletions(-) diff --git a/.github/workflows/automate_area-labels.yml b/.github/workflows/automate_area-labels.yml index ecaf464903..e45596ea8b 100644 --- a/.github/workflows/automate_area-labels.yml +++ b/.github/workflows/automate_area-labels.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index bf58b4b49d..8bde6e63fe 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml index 71e3450235..b36da6976a 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml index 48b329a7b7..452879a33b 100644 --- a/.github/workflows/automate_stale.yml +++ b/.github/workflows/automate_stale.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/ci-noop.yml b/.github/workflows/ci-noop.yml index f7c6c40832..2c66da768f 100644 --- a/.github/workflows/ci-noop.yml +++ b/.github/workflows/ci-noop.yml @@ -40,7 +40,7 @@ jobs: name: Test ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71d6b2e56a..6026c79028 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: name: Install ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit @@ -64,7 +64,7 @@ jobs: name: Verify ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 4a9e4522f4..9b1bb04889 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 037d4a88d7..048c61ee31 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 8343b92a66..f231d7b278 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index 33dc17f867..3bffa4ce64 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 5bb48b92f5..ec172ef671 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -144,7 +144,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index c81447e9c0..7f1854e61b 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -10,7 +10,7 @@ jobs: if: github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml index bfd1ffef3d..1a1cf80ae2 100644 --- a/.github/workflows/pr-review-comment-trigger.yaml +++ b/.github/workflows/pr-review-comment-trigger.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml index 7d9869d378..8b3737db13 100644 --- a/.github/workflows/pr-review-comment.yaml +++ b/.github/workflows/pr-review-comment.yaml @@ -17,7 +17,7 @@ jobs: steps: # Inspired by https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 6c2ffcfe77..79db7e9b6d 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -18,7 +18,7 @@ jobs: if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request ) steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index d8dd0d9a6a..e0cf745e22 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index 9b5a04198a..0571779ead 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml index f61d8709a5..d60fdb4317 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -11,7 +11,7 @@ jobs: if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index 1cab7797e6..d5b8b97b3a 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index 1e93a12f85..1868ffea27 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -11,7 +11,7 @@ jobs: if: github.actor == 'renovate[bot]' && github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index ec7c00111d..e1f4f62400 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index ddf6d2cf28..03031eeff7 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index fc1d7a68f1..1d149d3e81 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index c1b3bc50ea..ef1e4afc49 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -26,7 +26,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit @@ -84,7 +84,7 @@ jobs: - build-backstage steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit @@ -116,7 +116,7 @@ jobs: if: ${{ github.event.action == 'closed' }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/uffizzi-preview.yaml b/.github/workflows/uffizzi-preview.yaml index 47029df5e0..6a1844e986 100644 --- a/.github/workflows/uffizzi-preview.yaml +++ b/.github/workflows/uffizzi-preview.yaml @@ -23,7 +23,7 @@ jobs: action: ${{ steps.event.outputs.ACTION }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/verify_accessibility-noop.yml b/.github/workflows/verify_accessibility-noop.yml index 79c9962fce..280f3006bd 100644 --- a/.github/workflows/verify_accessibility-noop.yml +++ b/.github/workflows/verify_accessibility-noop.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 1310fc15f4..a7ffe69982 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index df0dd80118..9690068d80 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -42,7 +42,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index d4b83ccefc..4ddf1a014c 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-kubernetes-noop.yml b/.github/workflows/verify_e2e-kubernetes-noop.yml index 7606f78f07..8d8b14d5ba 100644 --- a/.github/workflows/verify_e2e-kubernetes-noop.yml +++ b/.github/workflows/verify_e2e-kubernetes-noop.yml @@ -23,7 +23,7 @@ jobs: name: Kubernetes ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml index db3651cfe1..4d28750c34 100644 --- a/.github/workflows/verify_e2e-kubernetes.yml +++ b/.github/workflows/verify_e2e-kubernetes.yml @@ -22,7 +22,7 @@ jobs: name: Kubernetes ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-linux-noop.yml b/.github/workflows/verify_e2e-linux-noop.yml index 19b2508d27..93d48caf22 100644 --- a/.github/workflows/verify_e2e-linux-noop.yml +++ b/.github/workflows/verify_e2e-linux-noop.yml @@ -29,7 +29,7 @@ jobs: name: E2E Linux ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 0fe258b740..cc26e5ee31 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -41,7 +41,7 @@ jobs: name: E2E Linux ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 6fd912d67d..04bc26a81f 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -30,7 +30,7 @@ jobs: name: Techdocs steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-windows-noop.yml b/.github/workflows/verify_e2e-windows-noop.yml index e24f7820fe..a7ebdba4b5 100644 --- a/.github/workflows/verify_e2e-windows-noop.yml +++ b/.github/workflows/verify_e2e-windows-noop.yml @@ -25,7 +25,7 @@ jobs: name: E2E Windows ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index eb9da86c52..ee1f7af15d 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -31,7 +31,7 @@ jobs: name: E2E Windows ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index e91a04d6ed..8df9d74a95 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite-noop.yml b/.github/workflows/verify_microsite-noop.yml index d730c7aba6..ad3e67ba70 100644 --- a/.github/workflows/verify_microsite-noop.yml +++ b/.github/workflows/verify_microsite-noop.yml @@ -21,7 +21,7 @@ jobs: name: Microsite steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 5c76148b92..b442a9e1f2 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -24,7 +24,7 @@ jobs: name: Microsite steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_storybook-noop.yml b/.github/workflows/verify_storybook-noop.yml index 3c92f0eaa2..74aed57f5c 100644 --- a/.github/workflows/verify_storybook-noop.yml +++ b/.github/workflows/verify_storybook-noop.yml @@ -28,7 +28,7 @@ jobs: name: Storybook steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 49a0c76df3..474c70b9d4 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -28,7 +28,7 @@ jobs: name: Storybook steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index 5b188770b4..eb79a1896f 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 with: egress-policy: audit From 7eb8636b62c0fcc622f96bdee07e77551728fa46 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 07:03:54 +0000 Subject: [PATCH 274/468] fix(deps): update aws-sdk-js-v3 monorepo Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 638 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 330 insertions(+), 308 deletions(-) diff --git a/yarn.lock b/yarn.lock index 640b349a22..7630762a0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -363,25 +363,25 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/client-cognito-identity@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.496.0" +"@aws-sdk/client-cognito-identity@npm:3.504.0": + version: 3.504.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.504.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.496.0 + "@aws-sdk/client-sts": 3.504.0 "@aws-sdk/core": 3.496.0 - "@aws-sdk/credential-provider-node": 3.496.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-signing": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 + "@aws-sdk/credential-provider-node": 3.504.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-signing": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 "@smithy/config-resolver": ^2.1.1 "@smithy/core": ^1.3.1 "@smithy/fetch-http-handler": ^2.4.1 @@ -407,29 +407,29 @@ __metadata: "@smithy/util-retry": ^2.1.1 "@smithy/util-utf8": ^2.1.1 tslib: ^2.5.0 - checksum: ee21d840134279f2d9c5bd113912fe89a48dcc6f56798894282886f48096743cd46499b908a659176cb1bfd10a771ec599694ae728d7fc53cb8a388124aff412 + checksum: e941df21af6e53a408a1b92db33ad4575a459eae983320959be9a3e152a9293eea93fc62579c60282a3454a520b493624a44f9529b7f9eca66ca2ebd9615004a languageName: node linkType: hard "@aws-sdk/client-eks@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/client-eks@npm:3.496.0" + version: 3.504.0 + resolution: "@aws-sdk/client-eks@npm:3.504.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.496.0 + "@aws-sdk/client-sts": 3.504.0 "@aws-sdk/core": 3.496.0 - "@aws-sdk/credential-provider-node": 3.496.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-signing": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 + "@aws-sdk/credential-provider-node": 3.504.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-signing": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 "@smithy/config-resolver": ^2.1.1 "@smithy/core": ^1.3.1 "@smithy/fetch-http-handler": ^2.4.1 @@ -457,29 +457,29 @@ __metadata: "@smithy/util-waiter": ^2.1.1 tslib: ^2.5.0 uuid: ^8.3.2 - checksum: 44042c4e746a27764db92f1966674034d7e9bb3a0cc6db3cedac1143472556d63e591a25048ebb8c11e07f3e807c7dd0b67b653c4f9bde30ef6fb7017eb82546 + checksum: 40650c95b683fd9adedd1aedfffd23cab73c22f971d82d3e5547732c01d332dfab981d7d47fe81fae2f1db406b58f899e62bf5844b3ee42e7005b0a1ece86227 languageName: node linkType: hard "@aws-sdk/client-organizations@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/client-organizations@npm:3.496.0" + version: 3.504.0 + resolution: "@aws-sdk/client-organizations@npm:3.504.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.496.0 + "@aws-sdk/client-sts": 3.504.0 "@aws-sdk/core": 3.496.0 - "@aws-sdk/credential-provider-node": 3.496.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-signing": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 + "@aws-sdk/credential-provider-node": 3.504.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-signing": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 "@smithy/config-resolver": ^2.1.1 "@smithy/core": ^1.3.1 "@smithy/fetch-http-handler": ^2.4.1 @@ -505,37 +505,37 @@ __metadata: "@smithy/util-retry": ^2.1.1 "@smithy/util-utf8": ^2.1.1 tslib: ^2.5.0 - checksum: a94ab6fadd8442223fee939559c72ea341ff8ed310f06c3420f4f7fab3fe0d033e6bdcf8be9984b92b1114b91abd8020201f727b96977e4d4c85357f01898b38 + checksum: 15daecdad337d503a672ec36d177aeb66ea5061ffcd559a66ae8dfb8bafcc2c7428fe855c2b7b832f222edf1fc2917d39ecd31e71ad19d6f9805f7b46b3cb929 languageName: node linkType: hard "@aws-sdk/client-s3@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/client-s3@npm:3.496.0" + version: 3.504.0 + resolution: "@aws-sdk/client-s3@npm:3.504.0" dependencies: "@aws-crypto/sha1-browser": 3.0.0 "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.496.0 + "@aws-sdk/client-sts": 3.504.0 "@aws-sdk/core": 3.496.0 - "@aws-sdk/credential-provider-node": 3.496.0 - "@aws-sdk/middleware-bucket-endpoint": 3.496.0 - "@aws-sdk/middleware-expect-continue": 3.496.0 - "@aws-sdk/middleware-flexible-checksums": 3.496.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-location-constraint": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-sdk-s3": 3.496.0 - "@aws-sdk/middleware-signing": 3.496.0 - "@aws-sdk/middleware-ssec": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/signature-v4-multi-region": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 + "@aws-sdk/credential-provider-node": 3.504.0 + "@aws-sdk/middleware-bucket-endpoint": 3.502.0 + "@aws-sdk/middleware-expect-continue": 3.502.0 + "@aws-sdk/middleware-flexible-checksums": 3.502.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-location-constraint": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-sdk-s3": 3.502.0 + "@aws-sdk/middleware-signing": 3.502.0 + "@aws-sdk/middleware-ssec": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/signature-v4-multi-region": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 "@aws-sdk/xml-builder": 3.496.0 "@smithy/config-resolver": ^2.1.1 "@smithy/core": ^1.3.1 @@ -571,29 +571,29 @@ __metadata: "@smithy/util-waiter": ^2.1.1 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 6b87d1edd89376bfb0c27cd270e57cfdfedb3fbec725fddce4eff5df31daa84b7e89099d44503b5bd19dd2021da1a18f08374cb839e588fa7bb819cd6f3cf25a + checksum: 22f4bb61196d5a567295e4ad42248f132a5e16ed1eb66bee9fcb7273dad13a9c3a2ebe5b2fb4277e645ede8b0650ddb2665fd450018b56cc29650dbd5d6ce427 languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/client-sqs@npm:3.496.0" + version: 3.504.0 + resolution: "@aws-sdk/client-sqs@npm:3.504.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.496.0 + "@aws-sdk/client-sts": 3.504.0 "@aws-sdk/core": 3.496.0 - "@aws-sdk/credential-provider-node": 3.496.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-sdk-sqs": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 + "@aws-sdk/credential-provider-node": 3.504.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-sdk-sqs": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 "@smithy/config-resolver": ^2.1.1 "@smithy/core": ^1.3.1 "@smithy/fetch-http-handler": ^2.4.1 @@ -621,26 +621,28 @@ __metadata: "@smithy/util-retry": ^2.1.1 "@smithy/util-utf8": ^2.1.1 tslib: ^2.5.0 - checksum: 42f064fb83b443b1de55cbc56941f4b4b7c2b06fb467fcef4d556e75e0613b5a99a72a63a6f464d4101a26047c6d97baa842d3189433a34904d208bde6c0a829 + checksum: 4b533cc79693ef82ead2c02ebe530a7cc8d73eeb11ca2e95ee21d84b479a5d277d42c152ecf08727e4f323a4ada995d3ff8ba248a845c85214ca06843996357c languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/client-sso@npm:3.496.0" +"@aws-sdk/client-sso-oidc@npm:3.504.0": + version: 3.504.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.504.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.504.0 "@aws-sdk/core": 3.496.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-signing": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 "@smithy/config-resolver": ^2.1.1 "@smithy/core": ^1.3.1 "@smithy/fetch-http-handler": ^2.4.1 @@ -666,27 +668,73 @@ __metadata: "@smithy/util-retry": ^2.1.1 "@smithy/util-utf8": ^2.1.1 tslib: ^2.5.0 - checksum: b775052f1f1fe85d411b8f84572eb68e05526afec0e3ad47b6383073351b2bc30cb3f8ad8bb2f33157c0c590e9f5deafd2522f543ae470bb0234fde6850118b5 + peerDependencies: + "@aws-sdk/credential-provider-node": ^3.504.0 + checksum: 93e816246e1f3ebf00c696f9f1cc2b3afd8c49ffc67020ce8aba0e752051afce4bfe9521bf6f4ad8dc55eeef5d320a903353829704215ec5838bbc08ec48974c languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.496.0, @aws-sdk/client-sts@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/client-sts@npm:3.496.0" +"@aws-sdk/client-sso@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/client-sso@npm:3.502.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 "@aws-sdk/core": 3.496.0 - "@aws-sdk/credential-provider-node": 3.496.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 + "@smithy/config-resolver": ^2.1.1 + "@smithy/core": ^1.3.1 + "@smithy/fetch-http-handler": ^2.4.1 + "@smithy/hash-node": ^2.1.1 + "@smithy/invalid-dependency": ^2.1.1 + "@smithy/middleware-content-length": ^2.1.1 + "@smithy/middleware-endpoint": ^2.4.1 + "@smithy/middleware-retry": ^2.1.1 + "@smithy/middleware-serde": ^2.1.1 + "@smithy/middleware-stack": ^2.1.1 + "@smithy/node-config-provider": ^2.2.1 + "@smithy/node-http-handler": ^2.3.1 + "@smithy/protocol-http": ^3.1.1 + "@smithy/smithy-client": ^2.3.1 + "@smithy/types": ^2.9.1 + "@smithy/url-parser": ^2.1.1 + "@smithy/util-base64": ^2.1.1 + "@smithy/util-body-length-browser": ^2.1.1 + "@smithy/util-body-length-node": ^2.2.1 + "@smithy/util-defaults-mode-browser": ^2.1.1 + "@smithy/util-defaults-mode-node": ^2.1.1 + "@smithy/util-endpoints": ^1.1.1 + "@smithy/util-retry": ^2.1.1 + "@smithy/util-utf8": ^2.1.1 + tslib: ^2.5.0 + checksum: ef801b4838af20d0d0458b02f0ca8ecb1e08ab1a8c1fe885b446745f730aee1e8a4fd1cbc555a7628390f67955d6d62b06f57f426551210356414f3c15cdd084 + languageName: node + linkType: hard + +"@aws-sdk/client-sts@npm:3.504.0, @aws-sdk/client-sts@npm:^3.350.0": + version: 3.504.0 + resolution: "@aws-sdk/client-sts@npm:3.504.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/core": 3.496.0 + "@aws-sdk/middleware-host-header": 3.502.0 + "@aws-sdk/middleware-logger": 3.502.0 + "@aws-sdk/middleware-recursion-detection": 3.502.0 + "@aws-sdk/middleware-user-agent": 3.502.0 + "@aws-sdk/region-config-resolver": 3.502.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 + "@aws-sdk/util-user-agent-browser": 3.502.0 + "@aws-sdk/util-user-agent-node": 3.502.0 "@smithy/config-resolver": ^2.1.1 "@smithy/core": ^1.3.1 "@smithy/fetch-http-handler": ^2.4.1 @@ -714,7 +762,9 @@ __metadata: "@smithy/util-utf8": ^2.1.1 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: a0427de70194c5391e883856d0c5f01f6bb610133970c6607530193c6961744424901c830872996c28c69300e51f417390e76ab5b967be7c164edc1be89e9e46 + peerDependencies: + "@aws-sdk/credential-provider-node": ^3.504.0 + checksum: f13bd125c5a5f4bf75945c4bb72b2e4e27eec5d666e3fed81014b86f4abbed6b4751bf74448d35e361bab2fb45c1574908e3354ff09d474a64392deeb05f7150 languageName: node linkType: hard @@ -732,36 +782,36 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/credential-provider-cognito-identity@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.496.0" +"@aws-sdk/credential-provider-cognito-identity@npm:3.504.0": + version: 3.504.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.504.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.496.0 - "@aws-sdk/types": 3.496.0 + "@aws-sdk/client-cognito-identity": 3.504.0 + "@aws-sdk/types": 3.502.0 "@smithy/property-provider": ^2.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 8125e31c183be3b9ceceb0e9773a135468b232c9ea28213c7cbdd00f3640124dc4bcd15f52b5a39074d472e74048121313b1d1dc8eb1ea719ad01132bdc42481 + checksum: 20bd84eb8bc1dc9f1ef8eb730cbf6c0d546a8ab43be724f7f16c94b39bf9c7ab46f32f27fcc102be873ff43bf19b1a7793164ec827730b582b5d4493c7fefdae languageName: node linkType: hard -"@aws-sdk/credential-provider-env@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.496.0" +"@aws-sdk/credential-provider-env@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/property-provider": ^2.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 0ff457ce12358c472a126e8994e04158c330c5ecf58d8fde6d431184a1d088d5358edac4e7efe8b1b64aa1c4fd386d570b5fcec30907fdc27cf2061a37910806 + checksum: 9a638955f1fc6bd07c6fe9c749cfea42867b4baf87caa8228e1203f856c989adac235ca1ee5c236d4819c973c44723f46e65bbc957843f025f40c6e24f69d65e languageName: node linkType: hard -"@aws-sdk/credential-provider-http@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-http@npm:3.496.0" +"@aws-sdk/credential-provider-http@npm:3.503.1": + version: 3.503.1 + resolution: "@aws-sdk/credential-provider-http@npm:3.503.1" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/fetch-http-handler": ^2.4.1 "@smithy/node-http-handler": ^2.3.1 "@smithy/property-provider": ^2.1.1 @@ -770,108 +820,111 @@ __metadata: "@smithy/types": ^2.9.1 "@smithy/util-stream": ^2.1.1 tslib: ^2.5.0 - checksum: 2ba3736689e26afe057ce9da11ebb45c73eae13d1179c2783a0f33cf9805644c464f77b4a60da2688e23fb1956e3bcd0dbb44292434d9f162ed7a86b19a03c2f + checksum: c4a6f7fc06a11071987ed803a4ce40c52ad077cbdd0171161030ecb157a76710e1bc95bb9c36acc4f01d32ab82c4c11620bb843d88c61027c011361205537bea languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.496.0" +"@aws-sdk/credential-provider-ini@npm:3.504.0": + version: 3.504.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.504.0" dependencies: - "@aws-sdk/credential-provider-env": 3.496.0 - "@aws-sdk/credential-provider-process": 3.496.0 - "@aws-sdk/credential-provider-sso": 3.496.0 - "@aws-sdk/credential-provider-web-identity": 3.496.0 - "@aws-sdk/types": 3.496.0 + "@aws-sdk/client-sts": 3.504.0 + "@aws-sdk/credential-provider-env": 3.502.0 + "@aws-sdk/credential-provider-process": 3.502.0 + "@aws-sdk/credential-provider-sso": 3.504.0 + "@aws-sdk/credential-provider-web-identity": 3.504.0 + "@aws-sdk/types": 3.502.0 "@smithy/credential-provider-imds": ^2.2.1 "@smithy/property-provider": ^2.1.1 "@smithy/shared-ini-file-loader": ^2.3.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 190449c43d687753966949397cf41215edc4ef2395dd2bb470988465b3f8bd6cf88933a7dd92032c19844d8f0dd4dd185caeaa060449e2b1d4141d47f549f27c + checksum: b8a2b56ee7109608c74b97a0fa7c52759d11b4754e81b4fd32a178d9e2ee2ea4c334e31a657b2f10d042da30f53f7e6003d87c6e4a13f517e6d5d4afaa8fba20 languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.496.0, @aws-sdk/credential-provider-node@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.496.0" +"@aws-sdk/credential-provider-node@npm:3.504.0, @aws-sdk/credential-provider-node@npm:^3.350.0": + version: 3.504.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.504.0" dependencies: - "@aws-sdk/credential-provider-env": 3.496.0 - "@aws-sdk/credential-provider-ini": 3.496.0 - "@aws-sdk/credential-provider-process": 3.496.0 - "@aws-sdk/credential-provider-sso": 3.496.0 - "@aws-sdk/credential-provider-web-identity": 3.496.0 - "@aws-sdk/types": 3.496.0 + "@aws-sdk/credential-provider-env": 3.502.0 + "@aws-sdk/credential-provider-http": 3.503.1 + "@aws-sdk/credential-provider-ini": 3.504.0 + "@aws-sdk/credential-provider-process": 3.502.0 + "@aws-sdk/credential-provider-sso": 3.504.0 + "@aws-sdk/credential-provider-web-identity": 3.504.0 + "@aws-sdk/types": 3.502.0 "@smithy/credential-provider-imds": ^2.2.1 "@smithy/property-provider": ^2.1.1 "@smithy/shared-ini-file-loader": ^2.3.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 16523a15f77d4def6c32fd465a85edc6f65d1e813b52b323f8264bf7b5e0a4bc3ede943104c218344a5152ca8f316658f911e71f8e26775b3f6852079e8ecda7 + checksum: 5d36589c7755313992013c8d2d776f2ab919a1d528eb17ed32d869869595d941d3e0e533e80bc2ea7b49a94fd80f205a4438aba5e5eabfc0a8f4717665490a59 languageName: node linkType: hard -"@aws-sdk/credential-provider-process@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.496.0" +"@aws-sdk/credential-provider-process@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/property-provider": ^2.1.1 "@smithy/shared-ini-file-loader": ^2.3.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 324d86cc4c727faa87038fe67620e38e8aaafce7d97cfcefac80b04aff5d0077babadf881bfd85c51191350d52b040b986dad07d85d3340376cddc0b1689b7eb + checksum: 418a22b5ab08d73b68246e9abc79ebbdc93ca84319dff21f2354af93b157cf798551e7c47a123f9c65e18b2fd1f88987be451914e544b9f74ae5fcdf7fed0437 languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.496.0" +"@aws-sdk/credential-provider-sso@npm:3.504.0": + version: 3.504.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.504.0" dependencies: - "@aws-sdk/client-sso": 3.496.0 - "@aws-sdk/token-providers": 3.496.0 - "@aws-sdk/types": 3.496.0 + "@aws-sdk/client-sso": 3.502.0 + "@aws-sdk/token-providers": 3.504.0 + "@aws-sdk/types": 3.502.0 "@smithy/property-provider": ^2.1.1 "@smithy/shared-ini-file-loader": ^2.3.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: fe4b34a3441777af37b7feeba60a538a59a35d5d0e8899f9d4ed6a482b8241cc0303df25049872773256c7ac19587d58aaf12330969fec7a85fdc6d1a07ed57a + checksum: e3716422ef27e6aa6d50a9e85809c2e51b2f7a3d8bddc4b1b0d61a5cfce2ccb2c7503e9319498861f57b05557027fe59863b3ee292601321b7fb80d772027ddb languageName: node linkType: hard -"@aws-sdk/credential-provider-web-identity@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.496.0" +"@aws-sdk/credential-provider-web-identity@npm:3.504.0": + version: 3.504.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.504.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/client-sts": 3.504.0 + "@aws-sdk/types": 3.502.0 "@smithy/property-provider": ^2.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: e1041486c76c229075c16afc3f0847abe7a30fa7a61bd82dbd4e731508f8a6b64a02cc80d97c105fea8e3efb138fb574a5ddda2f900d5a822033bffcc09830d4 + checksum: daf3c44b6663cd3e63634fce953c8ddd409640f53462c6e625de9b238b98dfc288776264990957f06f3134a6ba96dabd16e82cd7150d5c2579bcc625f9d2933d languageName: node linkType: hard "@aws-sdk/credential-providers@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/credential-providers@npm:3.496.0" + version: 3.504.1 + resolution: "@aws-sdk/credential-providers@npm:3.504.1" dependencies: - "@aws-sdk/client-cognito-identity": 3.496.0 - "@aws-sdk/client-sso": 3.496.0 - "@aws-sdk/client-sts": 3.496.0 - "@aws-sdk/credential-provider-cognito-identity": 3.496.0 - "@aws-sdk/credential-provider-env": 3.496.0 - "@aws-sdk/credential-provider-http": 3.496.0 - "@aws-sdk/credential-provider-ini": 3.496.0 - "@aws-sdk/credential-provider-node": 3.496.0 - "@aws-sdk/credential-provider-process": 3.496.0 - "@aws-sdk/credential-provider-sso": 3.496.0 - "@aws-sdk/credential-provider-web-identity": 3.496.0 - "@aws-sdk/types": 3.496.0 + "@aws-sdk/client-cognito-identity": 3.504.0 + "@aws-sdk/client-sso": 3.502.0 + "@aws-sdk/client-sts": 3.504.0 + "@aws-sdk/credential-provider-cognito-identity": 3.504.0 + "@aws-sdk/credential-provider-env": 3.502.0 + "@aws-sdk/credential-provider-http": 3.503.1 + "@aws-sdk/credential-provider-ini": 3.504.0 + "@aws-sdk/credential-provider-node": 3.504.0 + "@aws-sdk/credential-provider-process": 3.502.0 + "@aws-sdk/credential-provider-sso": 3.504.0 + "@aws-sdk/credential-provider-web-identity": 3.504.0 + "@aws-sdk/types": 3.502.0 "@smithy/credential-provider-imds": ^2.2.1 "@smithy/property-provider": ^2.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 30aac656bf113189b26856ee30175400c425781ca00ab4b7c609f949410cfd116e00a02294b9a86300e3ad9a6c8ede0cb5d6de134f057d0b17f999bfb6f0e400 + checksum: d27d06c982560ed1925573ef14f55fc6784c02103c570834a6fbaab2083c9d006dfc842484d1ed44477f1607da99d30edcbcdf1b61e28ad1a541dc7e639b182d languageName: node linkType: hard @@ -897,8 +950,8 @@ __metadata: linkType: hard "@aws-sdk/lib-storage@npm:^3.350.0": - version: 3.496.0 - resolution: "@aws-sdk/lib-storage@npm:3.496.0" + version: 3.504.0 + resolution: "@aws-sdk/lib-storage@npm:3.504.0" dependencies: "@smithy/abort-controller": ^2.1.1 "@smithy/middleware-endpoint": ^2.4.1 @@ -909,22 +962,22 @@ __metadata: tslib: ^2.5.0 peerDependencies: "@aws-sdk/client-s3": ^3.0.0 - checksum: d8a76e31848543c270a0f986344088eba209f638d8962832aa0872c852ed20cafa046372c25e7105cc23c2bf5e4a183fd89a37d0cd4edba3af4c20e9c35a1309 + checksum: b6b23444c78b45192279b580365460d2aaa0beaef2dc7ca1479d6a927eba59d408ea75bbb0691aa2f31daed9a59d0dedf50688839e406eacde858ebca60a25b8 languageName: node linkType: hard -"@aws-sdk/middleware-bucket-endpoint@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.496.0" +"@aws-sdk/middleware-bucket-endpoint@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@aws-sdk/util-arn-parser": 3.495.0 "@smithy/node-config-provider": ^2.2.1 "@smithy/protocol-http": ^3.1.1 "@smithy/types": ^2.9.1 "@smithy/util-config-provider": ^2.2.1 tslib: ^2.5.0 - checksum: 3b0c8b9cec0202fc8bcea343758d042035d12006da21d60b356c5b7d38c8a0b8bfcbeba93f7ec4f443060815b45cd0a41e71ca9791d338d40776f53eaed8c428 + checksum: cb1f7e61ada2340d62efcfe7e90814f32dbb983139844a00859d767f09cd8201c4b92b9afd5b391e8ca1b5c8318bb998ab93c5bb7baa4cdbe6bfc2cc021cbb66 languageName: node linkType: hard @@ -941,85 +994,85 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-expect-continue@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-expect-continue@npm:3.496.0" +"@aws-sdk/middleware-expect-continue@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/protocol-http": ^3.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: cd9e3b15c425e05a96eaf05c61ff4fd01e1e24d875ade4ab7fccfa95fddb0d925b4a9ac751f9bdf981184cbbb2327eb79710833afb41d64809b25e033d6ab395 + checksum: ef05feafa08721790f969518ceebd6bc78221732557747821665f5148dc979b32820c8c6158feea1cc696c38a1d55c4ab9e835aba3d8ffe84113ea1f0a4df934 languageName: node linkType: hard -"@aws-sdk/middleware-flexible-checksums@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.496.0" +"@aws-sdk/middleware-flexible-checksums@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.502.0" dependencies: "@aws-crypto/crc32": 3.0.0 "@aws-crypto/crc32c": 3.0.0 - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/is-array-buffer": ^2.1.1 "@smithy/protocol-http": ^3.1.1 "@smithy/types": ^2.9.1 "@smithy/util-utf8": ^2.1.1 tslib: ^2.5.0 - checksum: 3740e40ea91b0c9d40c4922e5b46b35ee0cd48cb8388b0721c7e13f63ac2b534b71e5a8c29593fa1cb5a94d0f3eef001d7899766be09ec74728ac133eb9f9114 + checksum: d0615350823b8b4e3f07d4c200d7fa04313ca50c33a6caed0bd794bdde75cf758b73887fd1bf04dd64272e96a2500d2acb59fd5b8a5d828b87f7e0c514756a5f languageName: node linkType: hard -"@aws-sdk/middleware-host-header@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.496.0" +"@aws-sdk/middleware-host-header@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/protocol-http": ^3.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 7958c74aafb729cacfccfa0148d9788b1f621acfb52f5a50bf158f8bf2b76d23e52b8cf1908686e9ea5fdcb2cb6e92b75b6d16b938457133e83947982db3d645 + checksum: 4e55ee901f31b4372a82a0ed429912d96c07369353ab914fd05a2caf1e64fc22288d4214bcec99b36eb1e9336944718267f95a9be87653baac3a7c2365b99663 languageName: node linkType: hard -"@aws-sdk/middleware-location-constraint@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-location-constraint@npm:3.496.0" +"@aws-sdk/middleware-location-constraint@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: bd039384df036f8ebd7c860de3f6a152ec3366216e32d5953d9076fea5a9c9c5021ee9ea78a98670606a56625c2e5f96e6b4c33bb1554f1f9add4e31b7a67e61 + checksum: fdcfb1186cb53b4587b1ca3aa73f4160b2af3643006fcd1d68b41a0acfee4a80457140eefdbaee077da826397682d012ace06faacdbc8bff288831e96a735dc2 languageName: node linkType: hard -"@aws-sdk/middleware-logger@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-logger@npm:3.496.0" +"@aws-sdk/middleware-logger@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-logger@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 3e2859d10342bd99a6855ee1cf229ca54a27d9e16d6613f016718b14efd4586618f553c530eb19b331278b818e75c5f9bc71f02d82c8a92973e486af8da112e7 + checksum: ff8b754373c90e1c5baacf1e2f36b568602d2d1d2aa74da818516fbc6b49acd024204332cb950453b60d3f352b0e2d6c4c1f72b71662f953178a3e069a187681 languageName: node linkType: hard -"@aws-sdk/middleware-recursion-detection@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.496.0" +"@aws-sdk/middleware-recursion-detection@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/protocol-http": ^3.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 50a909552d9b1ef0871929eccfebbee1256305c6d4d62d1d51edd88bd7cd12fbb661c87ad9aaf788b4d8821f0c2780699c29702c42d3035a4fa08c8d7c1cc519 + checksum: 75a4d712b04c63d649e026fe83d252a9c825ca0500042dcd83ad3cdaa58df4c5c25d645206200cbc119f3e7e48dbdb2cccd86f255a699cfa7f8cf155acaac6e4 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-s3@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-sdk-s3@npm:3.496.0" +"@aws-sdk/middleware-sdk-s3@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@aws-sdk/util-arn-parser": 3.495.0 "@smithy/node-config-provider": ^2.2.1 "@smithy/protocol-http": ^3.1.1 @@ -1028,20 +1081,20 @@ __metadata: "@smithy/types": ^2.9.1 "@smithy/util-config-provider": ^2.2.1 tslib: ^2.5.0 - checksum: e341f624e46912d09d14a4a7ad9ccc62cc8aa2cb4cdb683e86a3bfbb3134d07b6e5330344a63bae187448d2f0858679b1847fa2560f2ad2cdc32fa686360a0c0 + checksum: 6d27f5a82c30a1dadaefdbc89cd3eb3e08afc1fb1c627028e2bce2ac22e12bc035ecc8fc3578322386341266c4e801ac410091f38bc6dee5274be8ac0ec5dc39 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sqs@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.496.0" +"@aws-sdk/middleware-sdk-sqs@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/types": ^2.9.1 "@smithy/util-hex-encoding": ^2.1.1 "@smithy/util-utf8": ^2.1.1 tslib: ^2.5.0 - checksum: 6a462b98fc8f9c8eb97e2aba20840ea3bbcda117392cf0a14e6971f2622efe956420f2811d5ab8844fef3dcce25af479dcf56d364032561a4d229f75d6533c4b + checksum: 42bf269bcb5d77586fdc4457eeee40457a956f2235576a32421551e296de8070a85402f32d0d4dd92fed3aec6b68686873f95086817013e70bd3a911456768f3 languageName: node linkType: hard @@ -1055,42 +1108,42 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-signing@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-signing@npm:3.496.0" +"@aws-sdk/middleware-signing@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-signing@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/property-provider": ^2.1.1 "@smithy/protocol-http": ^3.1.1 "@smithy/signature-v4": ^2.1.1 "@smithy/types": ^2.9.1 "@smithy/util-middleware": ^2.1.1 tslib: ^2.5.0 - checksum: e39e9569e5063796729969115a719ede44595fee713fb5b44a6a11580c6810e93466ac48437ee002734ad608edf2701159bcfc4fb563d59f8a95901de78f5456 + checksum: b7595c3db33a62873fa29a8f08a64aca77f484035b42b5e12a8c364e2166b3dd81b1e3443bc3df5c97aaf3e00f14c2b99a8ef34cb3c132118d1e1872a74219e2 languageName: node linkType: hard -"@aws-sdk/middleware-ssec@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-ssec@npm:3.496.0" +"@aws-sdk/middleware-ssec@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 4a6f941346d43fe66bcccdc5da0490a38cae58b4ba0f80e629820e3e84d8f8125f3f1debf9b067b904a4a55fb29b3b58575635e1726543037512a2bb87a84442 + checksum: 0be54e13645bc97c130c61b09abb263def67e03382296aa020c118c672fef39359fddd997d2820ae62cbaeaa34d653ccafe0dfbb06a720e3b02d87e05b217e0f languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.496.0" +"@aws-sdk/middleware-user-agent@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 + "@aws-sdk/types": 3.502.0 + "@aws-sdk/util-endpoints": 3.502.0 "@smithy/protocol-http": ^3.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: a09de52251f0a7bf1a616881e3805fd728ef575d28c68f80b084d8db9342589adc6c95bef6b1b6085d8354459be0cf74516e05035c58e8b52a8a5b9b9133c475 + checksum: a65559d5cb790af73d75400099a2d0b38857cd28f8eb9e81f9250c59bd17a1a0e4c4210aeeb94ceb67716af996bd00655de11456ff73424f50597f6f05d4622d languageName: node linkType: hard @@ -1138,31 +1191,31 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/region-config-resolver@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/region-config-resolver@npm:3.496.0" +"@aws-sdk/region-config-resolver@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/node-config-provider": ^2.2.1 "@smithy/types": ^2.9.1 "@smithy/util-config-provider": ^2.2.1 "@smithy/util-middleware": ^2.1.1 tslib: ^2.5.0 - checksum: b82878109cfa96ae08008531c44def03a33a506c4fc27fdf0cf1081cee0eb17b5d28df6d37279e9fe83c487825672a9a81dc0ba88f375d2dc5b5dc1730d2ae91 + checksum: a13ee3502015baee3f8897a2597ea1ade556da0fcda653344e06624fd65e78fcc91a1b0b49edd4008760cb4284fb0cece1c44ea052cd4a7dca4bb01fb83bffd2 languageName: node linkType: hard -"@aws-sdk/signature-v4-multi-region@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/signature-v4-multi-region@npm:3.496.0" +"@aws-sdk/signature-v4-multi-region@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.502.0" dependencies: - "@aws-sdk/middleware-sdk-s3": 3.496.0 - "@aws-sdk/types": 3.496.0 + "@aws-sdk/middleware-sdk-s3": 3.502.0 + "@aws-sdk/types": 3.502.0 "@smithy/protocol-http": ^3.1.1 "@smithy/signature-v4": ^2.1.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: c2d8c11e30b285b827972de881307e927755d1c48f74c7d4a312fc5b7d0f1939aa9640c5b2b953ecdf8a541acdd42270ac175c026defc19e5ed5f0d49f9e160c + checksum: 745bab5b1daa45b30d62ec3b1939c766fcff42104539b8466245c7b5b7e538c827dd5cdd6c65dbdd8dfe1e7261bffb208729a225b242e4bb2f91df78a0c72a67 languageName: node linkType: hard @@ -1182,48 +1235,17 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/token-providers@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/token-providers@npm:3.496.0" +"@aws-sdk/token-providers@npm:3.504.0": + version: 3.504.0 + resolution: "@aws-sdk/token-providers@npm:3.504.0" dependencies: - "@aws-crypto/sha256-browser": 3.0.0 - "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/middleware-host-header": 3.496.0 - "@aws-sdk/middleware-logger": 3.496.0 - "@aws-sdk/middleware-recursion-detection": 3.496.0 - "@aws-sdk/middleware-user-agent": 3.496.0 - "@aws-sdk/region-config-resolver": 3.496.0 - "@aws-sdk/types": 3.496.0 - "@aws-sdk/util-endpoints": 3.496.0 - "@aws-sdk/util-user-agent-browser": 3.496.0 - "@aws-sdk/util-user-agent-node": 3.496.0 - "@smithy/config-resolver": ^2.1.1 - "@smithy/fetch-http-handler": ^2.4.1 - "@smithy/hash-node": ^2.1.1 - "@smithy/invalid-dependency": ^2.1.1 - "@smithy/middleware-content-length": ^2.1.1 - "@smithy/middleware-endpoint": ^2.4.1 - "@smithy/middleware-retry": ^2.1.1 - "@smithy/middleware-serde": ^2.1.1 - "@smithy/middleware-stack": ^2.1.1 - "@smithy/node-config-provider": ^2.2.1 - "@smithy/node-http-handler": ^2.3.1 + "@aws-sdk/client-sso-oidc": 3.504.0 + "@aws-sdk/types": 3.502.0 "@smithy/property-provider": ^2.1.1 - "@smithy/protocol-http": ^3.1.1 "@smithy/shared-ini-file-loader": ^2.3.1 - "@smithy/smithy-client": ^2.3.1 "@smithy/types": ^2.9.1 - "@smithy/url-parser": ^2.1.1 - "@smithy/util-base64": ^2.1.1 - "@smithy/util-body-length-browser": ^2.1.1 - "@smithy/util-body-length-node": ^2.2.1 - "@smithy/util-defaults-mode-browser": ^2.1.1 - "@smithy/util-defaults-mode-node": ^2.1.1 - "@smithy/util-endpoints": ^1.1.1 - "@smithy/util-retry": ^2.1.1 - "@smithy/util-utf8": ^2.1.1 tslib: ^2.5.0 - checksum: 54ad339923d2e4e4a021a2d21b39c1185b33b02abbecac3cb71786dbc76db84c1b5237d1929a0b39cd7b1420312e1fc523050c4588a3a37c26209e1ed934f069 + checksum: efdda4b1d1976085f2fa3f3733cfc644f8a9a66f07e76e3b250d27b66a4c25ce2e9bfd526b2a7041d9c683d5535d445b7ab511eb3ecefe67376318d019b5e224 languageName: node linkType: hard @@ -1237,13 +1259,13 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/types@npm:3.496.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": - version: 3.496.0 - resolution: "@aws-sdk/types@npm:3.496.0" +"@aws-sdk/types@npm:3.502.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": + version: 3.502.0 + resolution: "@aws-sdk/types@npm:3.502.0" dependencies: "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: d0d3b8a5cf8e9ab588c005d63b39b9c76f15913982cbe055b12ab4ad51c4fecb8faed935e96d4f8b19a38d6668ccdcf3555a1ca69c18831042ec06927bd73c74 + checksum: 11dddc2c1fb7b601adba1df6339b68367a3c77fcdd298c7ceb42541c813ba777ffc9ddfbb68633f9fd9082c883edf1b2fe3139acdf822d7d423c0b5f76ce78dd languageName: node linkType: hard @@ -1277,15 +1299,15 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-endpoints@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/util-endpoints@npm:3.496.0" +"@aws-sdk/util-endpoints@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/util-endpoints@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/types": ^2.9.1 "@smithy/util-endpoints": ^1.1.1 tslib: ^2.5.0 - checksum: d2de596852c6c78b4145f6b152d1e05958415101898ca69e0ade5a2ce63dfc52b1fc4ccaeb1f34d40a4b5cf96cba4ec19f39935515acaa7682fe21a781f9bfb4 + checksum: 051b519c118ef28dd49d60efc21dd3c0a2b032f8b70fdedc831e6c747bd675d51edc3913630ab86a02ecda7a3ea3ea5bec87b20c756700e65e059e2307110859 languageName: node linkType: hard @@ -1337,23 +1359,23 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-user-agent-browser@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.496.0" +"@aws-sdk/util-user-agent-browser@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/types": ^2.9.1 bowser: ^2.11.0 tslib: ^2.5.0 - checksum: 0f7d5530a0f750094af2aa899323189c94f946dd870b37da965df49d705e05da11046da005864f5151cf3f4393d3e0ba39e71a7ca6121fc5702cdd5f3a1a502c + checksum: 89e4d26f79979f30e7b1285b4f073a65c76021b706ab5c7342e2f4e46b6a045cc353dfce9bca98c9d134e92767f1bc3270e9c485d10a0d37e9ec81c21656c2e5 languageName: node linkType: hard -"@aws-sdk/util-user-agent-node@npm:3.496.0": - version: 3.496.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.496.0" +"@aws-sdk/util-user-agent-node@npm:3.502.0": + version: 3.502.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.502.0" dependencies: - "@aws-sdk/types": 3.496.0 + "@aws-sdk/types": 3.502.0 "@smithy/node-config-provider": ^2.2.1 "@smithy/types": ^2.9.1 tslib: ^2.5.0 @@ -1362,7 +1384,7 @@ __metadata: peerDependenciesMeta: aws-crt: optional: true - checksum: 9ea68207f061a04e93cdee7b4f0e47bae3f4afd8bb922179bd4ea284a4756e879c1f76f58c87a3aedb65eebbed82f453bf94dd26095ebd4728169b1e1a6c9d4c + checksum: b90a373d489bd34ce759acb76c91902bb2bd5991aad6a2d316d0b14c86bd7de659d85e9964111fc2e4bc76e67e19fd0d91ebe255e011c1054ca813c97992cc43 languageName: node linkType: hard From 7f8b9e1cec39e9cd9e8f09b6789ff6c11be0835d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 07:16:37 +0000 Subject: [PATCH 275/468] fix(deps): update dependency @gitbeaker/rest to v39.34.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index 640b349a22..c7a47ae34c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11273,14 +11273,14 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/core@npm:^39.30.0": - version: 39.30.0 - resolution: "@gitbeaker/core@npm:39.30.0" +"@gitbeaker/core@npm:^39.34.2": + version: 39.34.2 + resolution: "@gitbeaker/core@npm:39.34.2" dependencies: - "@gitbeaker/requester-utils": ^39.30.0 + "@gitbeaker/requester-utils": ^39.34.2 qs: ^6.11.2 xcase: ^2.0.1 - checksum: 4724659bab57cde13b63bba7ffad52651ebb726c444bb1d6c1c69c70efb89ed3d319abf12c117f9a8ab6741cc26e6a19c23e237bebd07d13b2f8c970171dc069 + checksum: 3c1db0ed169d83c2f6631d34a3dd5b4dde597480e47b1f212fab00e2eb31ff2759e3f983475a4dc368df8811642c0c2822039d87461dcee7ac4b5bd2fa04adab languageName: node linkType: hard @@ -11308,25 +11308,25 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/requester-utils@npm:^39.30.0": - version: 39.30.0 - resolution: "@gitbeaker/requester-utils@npm:39.30.0" +"@gitbeaker/requester-utils@npm:^39.34.2": + version: 39.34.2 + resolution: "@gitbeaker/requester-utils@npm:39.34.2" dependencies: picomatch-browser: ^2.2.6 qs: ^6.11.2 rate-limiter-flexible: ^4.0.0 xcase: ^2.0.1 - checksum: 05a60b027736edd165341c88ddbe095c78e77616f60033a9c9b419b657cd7d9998a9a801470a88d25d96be44b3a5233e3f1a4042aee075872f2882d10122db69 + checksum: ae70d558ce13af56e2b0b31330b76f2b528a503a27e9f152d4224d1c165f08f20109fff6dabea5ac4679a6c07e49f9025149a3fab4aabe3cd03754d8b138b5f9 languageName: node linkType: hard "@gitbeaker/rest@npm:^39.25.0": - version: 39.30.0 - resolution: "@gitbeaker/rest@npm:39.30.0" + version: 39.34.2 + resolution: "@gitbeaker/rest@npm:39.34.2" dependencies: - "@gitbeaker/core": ^39.30.0 - "@gitbeaker/requester-utils": ^39.30.0 - checksum: 2b04dbb28c1a5f47c067c376ff0b4664278d72dcdef7696646a6d9331a400d9dfe75382e041a0a683fdc5ac1805abdb2919455fea786fd97f0eb65a5473d3fb5 + "@gitbeaker/core": ^39.34.2 + "@gitbeaker/requester-utils": ^39.34.2 + checksum: e224214b816b9a9e38c77b0eb604aa456793ea7610e7f426899579ab738abe4b8aeafb0f6b9e94ccde9d5fd82cc4d6af0d51cc50174173184df2e7a8be18af5b languageName: node linkType: hard From 4e94ddaa18c931dee565b9e37ac1ca28dc452bff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 08:37:08 +0000 Subject: [PATCH 276/468] fix(deps): update dependency @google-cloud/container to v5.5.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c7a47ae34c..2f4f2126c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11331,11 +11331,11 @@ __metadata: linkType: hard "@google-cloud/container@npm:^5.0.0": - version: 5.4.1 - resolution: "@google-cloud/container@npm:5.4.1" + version: 5.5.0 + resolution: "@google-cloud/container@npm:5.5.0" dependencies: google-gax: ^4.0.3 - checksum: 93f06b3171805c85d2ec8fa959e3caa147f1560da5c90082e23e1ccbd6ef88756e3271dbf3db139fefaa48874222f7e9e4a7dc5fb7da54a2e7ecd80db472ecf7 + checksum: e88dd56e6911d3bdbb3f63d91536636f9018d421a5e6d3ab8f002c62f1c46d488e83265406c23fe02ae10edab0fc55b32bef726795e04dd52e0b38889d323dd0 languageName: node linkType: hard From 2bdb8ea9c04b6248f3822cd2be13966ce5ec28f2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 08:38:13 +0000 Subject: [PATCH 277/468] fix(deps): update dependency @google-cloud/firestore to v7.3.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c7a47ae34c..5c1df96f30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11340,14 +11340,14 @@ __metadata: linkType: hard "@google-cloud/firestore@npm:^7.0.0": - version: 7.2.0 - resolution: "@google-cloud/firestore@npm:7.2.0" + version: 7.3.0 + resolution: "@google-cloud/firestore@npm:7.3.0" dependencies: fast-deep-equal: ^3.1.1 functional-red-black-tree: ^1.0.1 google-gax: ^4.0.4 protobufjs: ^7.2.5 - checksum: 2f9a5fa24741bb3e93c6dad2a366d8bfdab2a05d1a742a842eb432cc0e9e6cfc05828b0f6215827aa0da875556a329d1a39f06349b5b371b8dec685cd3225d6b + checksum: 6e12f011250ee6e3f8a522ab02d706af7966675b6653e77734929fce1493efc9fed90de398900228da660f4f4738ccafa2bad96f4573e8097267c67f1ab87f90 languageName: node linkType: hard From 9e555f52de25639d0d9336edb501a62cebe9df81 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 09:21:04 +0000 Subject: [PATCH 278/468] fix(deps): update dependency @newrelic/browser-agent to v1.251.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e32a341398..ecdf1bcc35 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13266,14 +13266,14 @@ __metadata: linkType: hard "@newrelic/browser-agent@npm:^1.236.0": - version: 1.250.0 - resolution: "@newrelic/browser-agent@npm:1.250.0" + version: 1.251.1 + resolution: "@newrelic/browser-agent@npm:1.251.1" dependencies: core-js: ^3.26.0 fflate: ^0.7.4 rrweb: 2.0.0-alpha.11 web-vitals: ^3.1.0 - checksum: 4cea599b5544c3981c8a564cd1f31cce33c286130cab8cfdb7adc08adf47499d525f0a555d648f0aa17ccb23749e572fb548a84c061c789a996dc264de5ed7cc + checksum: ae91ac13978131258b245ffebeaad91108cf78732aa7fa4305ca2b57b9d8fc91235b76ae3e0c42f5e6d6b07de5ccf205bce15d7cc8ebaa62e4fe8d1590b53bff languageName: node linkType: hard From 4222858ed8dd0cef67d88847e208cd7473587d55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 09:21:55 +0000 Subject: [PATCH 279/468] fix(deps): update dependency azure-devops-node-api to v12.4.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e32a341398..2ec40cf9a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21233,12 +21233,12 @@ __metadata: linkType: hard "azure-devops-node-api@npm:^12.0.0": - version: 12.3.0 - resolution: "azure-devops-node-api@npm:12.3.0" + version: 12.4.0 + resolution: "azure-devops-node-api@npm:12.4.0" dependencies: tunnel: 0.0.6 typed-rest-client: ^1.8.4 - checksum: 4f76f4633f57b92267b9f841591ee2c9548bc51421d3d5b0072c854f6a3f3bc77b3a334863a22f1b903c620492a8be877ee16ff5af05e6d131b281a683a7f695 + checksum: d7207c2bb0e021a25e11d1f6e4b4ac02efdbe4467b0c28bfb19260b2a1a799393978e6ff0b916cf81e309ba8b9a80015d8638751bed253695ef779a3a904efd2 languageName: node linkType: hard From e2eb3bf3dae153fcabbe1f37016daace20123f8c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 10:42:51 +0000 Subject: [PATCH 280/468] fix(deps): update dependency css-loader to v6.10.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2ec40cf9a4..676530fb23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23907,8 +23907,8 @@ __metadata: linkType: hard "css-loader@npm:^6.5.1": - version: 6.9.1 - resolution: "css-loader@npm:6.9.1" + version: 6.10.0 + resolution: "css-loader@npm:6.10.0" dependencies: icss-utils: ^5.1.0 postcss: ^8.4.33 @@ -23919,8 +23919,14 @@ __metadata: postcss-value-parser: ^4.2.0 semver: ^7.5.4 peerDependencies: + "@rspack/core": 0.x || 1.x webpack: ^5.0.0 - checksum: b0c1776f9c46474219eb471eeb8f4c8986a7735dc8356e578a63303cf292a7c8cb868fa74bf94a1a174e948fcb6523c63fca081cac6bbf246be8275b3cc384f0 + peerDependenciesMeta: + "@rspack/core": + optional: true + webpack: + optional: true + checksum: ee3d62b5f7e4eb24281a22506431e920d07a45bd6ea627731ce583f3c6a846ab8b8b703bace599b9b35256b9e762f9f326d969abb72b69c7e6055eacf39074fd languageName: node linkType: hard From 71d10cd173cd0457de19cf93385bdf4480aaa60c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:16:38 +0000 Subject: [PATCH 281/468] fix(deps): update dependency express-session to v1.18.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2ec40cf9a4..52a0663769 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23466,6 +23466,13 @@ __metadata: languageName: node linkType: hard +"cookie-signature@npm:1.0.7": + version: 1.0.7 + resolution: "cookie-signature@npm:1.0.7" + checksum: 1a62808cd30d15fb43b70e19829b64d04b0802d8ef00275b57d152de4ae6a3208ca05c197b6668d104c4d9de389e53ccc2d3bc6bcaaffd9602461417d8c40710 + languageName: node + linkType: hard + "cookie@npm:0.4.1": version: 0.4.1 resolution: "cookie@npm:0.4.1" @@ -23473,13 +23480,6 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.4.2, cookie@npm:^0.4.2": - version: 0.4.2 - resolution: "cookie@npm:0.4.2" - checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b - languageName: node - linkType: hard - "cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" @@ -23487,13 +23487,20 @@ __metadata: languageName: node linkType: hard -"cookie@npm:~0.6.0": +"cookie@npm:0.6.0, cookie@npm:~0.6.0": version: 0.6.0 resolution: "cookie@npm:0.6.0" checksum: f56a7d32a07db5458e79c726b77e3c2eff655c36792f2b6c58d351fb5f61531e5b1ab7f46987150136e366c65213cbe31729e02a3eaed630c3bf7334635fb410 languageName: node linkType: hard +"cookie@npm:^0.4.2": + version: 0.4.2 + resolution: "cookie@npm:0.4.2" + checksum: a00833c998bedf8e787b4c342defe5fa419abd96b32f4464f718b91022586b8f1bafbddd499288e75c037642493c83083da426c6a9080d309e3bd90fd11baa9b + languageName: node + linkType: hard + "cookiejar@npm:^2.1.4": version: 2.1.4 resolution: "cookiejar@npm:2.1.4" @@ -27089,18 +27096,18 @@ __metadata: linkType: hard "express-session@npm:^1.17.1, express-session@npm:^1.17.3": - version: 1.17.3 - resolution: "express-session@npm:1.17.3" + version: 1.18.0 + resolution: "express-session@npm:1.18.0" dependencies: - cookie: 0.4.2 - cookie-signature: 1.0.6 + cookie: 0.6.0 + cookie-signature: 1.0.7 debug: 2.6.9 depd: ~2.0.0 on-headers: ~1.0.2 parseurl: ~1.3.3 safe-buffer: 5.2.1 uid-safe: ~2.1.5 - checksum: 1021a793433cbc6a1b32c803fcb2daa1e03a8f50dd907e8745ae57994370315a5cfde5b6ef7b062d9a9a0754ff268844bda211c08240b3a0e01014dcf1073ec5 + checksum: 56e52e4f5e09f77b201069f5f977e8c301d1feb324ac545f043e251745bb17ab0b05c6d7b3653f20ae548179afd76eeda9f44c9872ac9ce82d7c2a917a88d885 languageName: node linkType: hard From fb773f8b4c84154528fae2158d0dca9bc0d75c43 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:17:26 +0000 Subject: [PATCH 282/468] fix(deps): update dependency mini-css-extract-plugin to v2.8.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2ec40cf9a4..01112d414d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34349,13 +34349,14 @@ __metadata: linkType: hard "mini-css-extract-plugin@npm:^2.4.2": - version: 2.7.7 - resolution: "mini-css-extract-plugin@npm:2.7.7" + version: 2.8.0 + resolution: "mini-css-extract-plugin@npm:2.8.0" dependencies: schema-utils: ^4.0.0 + tapable: ^2.2.1 peerDependencies: webpack: ^5.0.0 - checksum: 04af0e7d8c1a4ff31c70ac2d0895837dae3d51cce3bfd90e3c1d90d50eef7de21778361a3064531df046d775d80b3bf056324dddea93831c7def2047c5aa8718 + checksum: c1edc3ee0e1b3514c3323fa72ad38e993f357964e76737f1d7bb6cf50a0af1ac071080ec16b4e1a94688d23f78533944badad50cd0f00d2ae176f9c58c1f2029 languageName: node linkType: hard From 4b7c665402f925bb349665219a86eec0abd90896 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:38:43 +0000 Subject: [PATCH 283/468] chore(deps): pin dependencies Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/automate_area-labels.yml | 2 +- .../workflows/automate_changeset_feedback.yml | 4 ++-- .github/workflows/automate_merge_message.yml | 4 ++-- .github/workflows/automate_stale.yml | 2 +- .github/workflows/ci.yml | 24 +++++++++---------- .github/workflows/cron.yml | 2 +- .github/workflows/deploy_docker-image.yml | 12 +++++----- .github/workflows/deploy_microsite.yml | 4 ++-- .github/workflows/deploy_nightly.yml | 2 +- .github/workflows/deploy_packages.yml | 18 +++++++------- .github/workflows/issue.yaml | 2 +- .../workflows/pr-review-comment-trigger.yaml | 2 +- .github/workflows/pr-review-comment.yaml | 4 ++-- .github/workflows/pr.yaml | 2 +- .github/workflows/scorecard.yml | 4 ++-- .github/workflows/sync_code-formatting.yml | 4 ++-- .../workflows/sync_dependabot-changesets.yml | 4 ++-- .github/workflows/sync_release-manifest.yml | 8 +++---- .../workflows/sync_renovate-changesets.yml | 4 ++-- .github/workflows/sync_snyk-github-issues.yml | 6 ++--- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/sync_version-packages.yml | 4 ++-- .github/workflows/uffizzi-build.yml | 14 +++++------ .github/workflows/uffizzi-preview.yaml | 20 ++++++++-------- .github/workflows/verify_accessibility.yml | 6 ++--- .github/workflows/verify_codeql.yml | 2 +- .github/workflows/verify_docs-quality.yml | 2 +- .github/workflows/verify_e2e-kubernetes.yml | 6 ++--- .github/workflows/verify_e2e-linux.yml | 8 +++---- .github/workflows/verify_e2e-techdocs.yml | 4 ++-- .github/workflows/verify_e2e-windows.yml | 10 ++++---- .github/workflows/verify_fossa.yml | 2 +- .github/workflows/verify_microsite.yml | 4 ++-- .github/workflows/verify_storybook.yml | 6 ++--- .github/workflows/verify_windows.yml | 4 ++-- contrib/.devcontainer/Dockerfile | 2 +- .../Dockerfile.dockerbuild | 2 +- .../frontend-with-nginx/Dockerfile.hostbuild | 2 +- .../Rails.dockerfile | 2 +- .../scripts/Cookiecutter.dockerfile | 2 +- 40 files changed, 109 insertions(+), 109 deletions(-) diff --git a/.github/workflows/automate_area-labels.yml b/.github/workflows/automate_area-labels.yml index e45596ea8b..632624bd50 100644 --- a/.github/workflows/automate_area-labels.yml +++ b/.github/workflows/automate_area-labels.yml @@ -17,7 +17,7 @@ jobs: with: egress-policy: audit - - uses: actions/labeler@v5.0.0 + - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' sync-labels: true diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index 8bde6e63fe..89b611f981 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -27,14 +27,14 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: # Fetch the commit that's merged into the base rather than the target ref # This will let us diff only the contents of the PR, without fetching more history ref: 'refs/pull/${{ github.event.pull_request.number }}/merge' - name: fetch base run: git fetch --depth 1 origin ${{ github.base_ref }} - - uses: backstage/actions/changeset-feedback@v0.6.5 + - uses: backstage/actions/changeset-feedback@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 name: Generate feedback with: diff-ref: 'origin/master' diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml index b36da6976a..0c6dacde69 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: '${{ github.event.pull_request.merge_commit_sha }}' @@ -44,7 +44,7 @@ jobs: node generate.js ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} > message.txt - name: Post Message - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: ISSUE_NUMBER: ${{ github.event.pull_request.number }} with: diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml index 452879a33b..b4a7271875 100644 --- a/.github/workflows/automate_stale.yml +++ b/.github/workflows/automate_stale.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/stale@v9.0.0 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 id: stale with: stale-issue-message: > diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6026c79028..9332012290 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,16 +32,16 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -68,16 +68,16 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -155,7 +155,7 @@ jobs: name: Test ${{ matrix.node-version }} services: postgres16: - image: postgres:16 + image: postgres:16@sha256:4d1b17af6f66b852ee3a721f6691a2ca7352f9d28f570a6a48cee4ebe646b2fd env: POSTGRES_PASSWORD: postgres options: >- @@ -166,7 +166,7 @@ jobs: ports: - 5432/tcp postgres12: - image: postgres:12 + image: postgres:12@sha256:aafc7d3faafa5f95fa4709007c742864747a104c3d950f98bb606145208e4a77 env: POSTGRES_PASSWORD: postgres options: >- @@ -177,7 +177,7 @@ jobs: ports: - 5432/tcp mysql8: - image: mysql:8 + image: mysql:8@sha256:d7c20c5ba268c558f4fac62977f8c7125bde0630ff8946b08dde44135ef40df3 env: MYSQL_ROOT_PASSWORD: root options: >- @@ -197,18 +197,18 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: fetch branch master run: git fetch origin master - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 9b1bb04889..6ae0310813 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -13,7 +13,7 @@ jobs: with: egress-policy: audit - - uses: backstage/actions/cron@v0.6.5 + - uses: backstage/actions/cron@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 048c61ee31..ae9c042401 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -21,19 +21,19 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: path: backstage ref: v${{ github.event.client_payload.version }} - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -47,17 +47,17 @@ jobs: working-directory: ./example-app - name: Login to GitHub Container Registry - uses: docker/login-action@v3.0.0 + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.0.0 + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - name: Build and push - uses: docker/build-push-action@v5.1.0 + uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0 with: context: './example-app' file: ./example-app/packages/backend/Dockerfile diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index f231d7b278..4f79debb3b 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -28,10 +28,10 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js 18.x - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index 3bffa4ce64..3553236c9e 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -27,7 +27,7 @@ jobs: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v18.x diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index ec172ef671..ea390a3903 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -23,7 +23,7 @@ jobs: services: postgres16: - image: postgres:16 + image: postgres:16@sha256:4d1b17af6f66b852ee3a721f6691a2ca7352f9d28f570a6a48cee4ebe646b2fd env: POSTGRES_PASSWORD: postgres options: >- @@ -34,7 +34,7 @@ jobs: ports: - 5432/tcp postgres12: - image: postgres:12 + image: postgres:12@sha256:aafc7d3faafa5f95fa4709007c742864747a104c3d950f98bb606145208e4a77 env: POSTGRES_PASSWORD: postgres options: >- @@ -45,7 +45,7 @@ jobs: ports: - 5432/tcp mysql8: - image: mysql:8 + image: mysql:8@sha256:d7c20c5ba268c558f4fac62977f8c7125bde0630ff8946b08dde44135ef40df3 env: MYSQL_ROOT_PASSWORD: root options: >- @@ -65,15 +65,15 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -148,15 +148,15 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 7f1854e61b..54bc8d8782 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -15,4 +15,4 @@ jobs: egress-policy: audit - name: Issue sync - uses: backstage/actions/issue-sync@v0.6.5 + uses: backstage/actions/issue-sync@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml index 1a1cf80ae2..46f7dad8a3 100644 --- a/.github/workflows/pr-review-comment-trigger.yaml +++ b/.github/workflows/pr-review-comment-trigger.yaml @@ -30,7 +30,7 @@ jobs: run: | mkdir -p ./pr echo $PR_NUMBER > ./pr/pr_number - - uses: actions/upload-artifact@v3.1.3 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: pr_number-${{ github.event.pull_request.number }} path: pr/ diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml index 8b3737db13..18300b074e 100644 --- a/.github/workflows/pr-review-comment.yaml +++ b/.github/workflows/pr-review-comment.yaml @@ -23,7 +23,7 @@ jobs: - name: Read PR Number id: pr-number - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | @@ -40,7 +40,7 @@ jobs: const prNumber = artifact.name.slice('pr_number-'.length) core.setOutput('pr-number', prNumber); - - uses: backstage/actions/re-review@v0.6.5 + - uses: backstage/actions/re-review@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 79db7e9b6d..89531bf70a 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -23,7 +23,7 @@ jobs: egress-policy: audit - name: PR sync - uses: backstage/actions/pr-sync@v0.6.5 + uses: backstage/actions/pr-sync@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 684488cd13..32db85a0de 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -34,7 +34,7 @@ jobs: egress-policy: audit - name: 'Checkout code' - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false @@ -58,7 +58,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: 'Upload artifact' - uses: actions/upload-artifact@v3.1.3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index 0571779ead..fd413de0ec 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -20,12 +20,12 @@ jobs: fetch-depth: 0 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml index d60fdb4317..c2a447e369 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 2 ref: ${{ github.head_ref }} @@ -26,7 +26,7 @@ jobs: git config --global user.email noreply@backstage.io git config --global user.name 'Github changeset workflow' - name: Generate changeset - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const { promises: fs } = require('fs'); diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index d5b8b97b3a..b5e0984101 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -13,7 +13,7 @@ jobs: egress-policy: audit # Setup node & install deps before checkout, keeping install quick - - uses: actions/setup-node@v4.0.1 + - uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: 18.x - name: Install dependencies @@ -21,7 +21,7 @@ jobs: run: npm install semver@7.3.5 fs-extra@10.0.0 @manypkg/get-packages@1.1.1 - name: Checkout - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: path: backstage # 'v' prefix is added here for the tag, we keep it out of the manifest logic @@ -29,7 +29,7 @@ jobs: # Checkout backstage/versions into /backstage/versions, which is where store the output - name: Checkout versions - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: repository: backstage/versions path: backstage/versions @@ -53,7 +53,7 @@ jobs: git push - name: Dispatch update-helper update - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} # TODO(Rugvip): Remove the create-app dispatch once we've been on the release version for a while diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index 1868ffea27..83b9f8f574 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 2 ref: ${{ github.head_ref }} @@ -26,7 +26,7 @@ jobs: git config --global user.email noreply@backstage.io git config --global user.name 'Github changeset workflow' - name: Generate changeset - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const { promises: fs } = require("fs"); diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index e1f4f62400..fcd3d922e8 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -16,15 +16,15 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js 18.x - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v18.x diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index bfd68eba0a..45410bdca7 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -29,7 +29,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Monitor and Synchronize Snyk Policies uses: snyk/actions/node@1d672a455ab3339ef0a0021e1ec809165ee12fad # master with: diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 1d149d3e81..3164e471d2 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -18,7 +18,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 20000 fetch-tags: true @@ -26,7 +26,7 @@ jobs: - name: Install Dependencies run: yarn --immutable - name: Create Release Pull Request - uses: backstage/changesets-action@v2.1.0 + uses: backstage/changesets-action@99063c276a7f12e024cb310e7a05a3a5b89449f8 # v2.1.0 with: # Calls out to `changeset version`, but also runs prettier version: yarn release diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index ef1e4afc49..77a54b2dc9 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -31,16 +31,16 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: setup-node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: 18.x registry-url: https://registry.npmjs.org/ - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: linux-v18 @@ -89,7 +89,7 @@ jobs: egress-policy: audit - name: Checkout git repo - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Render Compose File run: | # update image after the build above @@ -98,13 +98,13 @@ jobs: kustomize build . > manifests.rendered.yml cat manifests.rendered.yml - name: Upload Rendered Manifests File as Artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 with: name: preview-spec path: ./.github/uffizzi/k8s/manifests/manifests.rendered.yml retention-days: 2 - name: Upload PR Event as Artifact - uses: actions/upload-artifact@v3.1.3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: preview-spec path: ${{ github.event_path }} @@ -122,7 +122,7 @@ jobs: # If this PR is closing, we will not render a compose file nor pass it to the next workflow. - name: Upload PR Event as Artifact - uses: actions/upload-artifact@v3.1.3 + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: preview-spec path: ${{ github.event_path }} diff --git a/.github/workflows/uffizzi-preview.yaml b/.github/workflows/uffizzi-preview.yaml index 6a1844e986..dc23ad4bf7 100644 --- a/.github/workflows/uffizzi-preview.yaml +++ b/.github/workflows/uffizzi-preview.yaml @@ -32,7 +32,7 @@ jobs: - name: 'Download artifacts' # Fetch output (zip archive) from the workflow run that triggered this workflow. - uses: actions/github-script@v7.0.1 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ @@ -76,7 +76,7 @@ jobs: - name: Cache Manifests File if: ${{ steps.event.outputs.ACTION != 'closed' }} - uses: actions/cache@v4.0.0 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 with: path: manifests.rendered.yml key: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }} @@ -102,11 +102,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 # Identify comment to be updated - name: Find comment for Ephemeral Environment - uses: peter-evans/find-comment@v2 + uses: peter-evans/find-comment@a54c31d7fa095754bfef525c0c8e5e5674c4b4b1 # v2 id: find-comment with: issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} @@ -117,7 +117,7 @@ jobs: # Create/Update comment with action deployment status - name: Create or Update Comment with Deployment Notification id: notification - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3 with: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} @@ -140,7 +140,7 @@ jobs: - name: Fetch cached Manifests File id: cache - uses: actions/cache@v4 + uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4 with: path: manifests.rendered.yml key: ${{ needs.cache-manifests-file.outputs.manifests-cache-key }} @@ -165,7 +165,7 @@ jobs: echo "Access the \`backstage\` endpoint at [\`${BACKSTAGE_HOST}\`](http://${BACKSTAGE_HOST})" >> $GITHUB_STEP_SUMMARY - name: Create or Update Comment with Deployment URL - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3 with: comment-id: ${{ steps.notification.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} @@ -209,7 +209,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - name: Delete Virtual Cluster uses: UffizziCloud/cluster-action@main @@ -220,7 +220,7 @@ jobs: # Identify comment to be updated - name: Find comment for Ephemeral Environment - uses: peter-evans/find-comment@v2 + uses: peter-evans/find-comment@a54c31d7fa095754bfef525c0c8e5e5674c4b4b1 # v2 id: find-comment with: issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} @@ -229,7 +229,7 @@ jobs: direction: last - name: Update Comment with Deletion - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3 with: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index a7ffe69982..7501d768c6 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -24,13 +24,13 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Use Node.js 18.x - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: 18.x - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v18.x - name: run Lighthouse CI diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index 0a95a75f13..4d54a73f85 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -47,7 +47,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 4ddf1a014c..63431d9b00 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 # Vale does not support file excludes, so we use the script to generate a list of files instead # The action also does not allow args or a local config file to be passed in, so the files array diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml index 4d28750c34..a09beaf14f 100644 --- a/.github/workflows/verify_e2e-kubernetes.yml +++ b/.github/workflows/verify_e2e-kubernetes.yml @@ -26,16 +26,16 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index cc26e5ee31..9fd2acafa4 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -19,7 +19,7 @@ jobs: services: postgres: - image: postgres:12 + image: postgres:12@sha256:aafc7d3faafa5f95fa4709007c742864747a104c3d950f98bb606145208e4a77 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -45,7 +45,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure Git run: | @@ -53,12 +53,12 @@ jobs: git config --global user.name 'GitHub e2e user' - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 04bc26a81f..0bce75f720 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -34,8 +34,8 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 - - uses: actions/setup-python@v5.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index ee1f7af15d..1bddd792b7 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -42,7 +42,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Configure Git run: | @@ -50,18 +50,18 @@ jobs: git config --global user.name 'GitHub e2e user' - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: setup python - uses: actions/setup-python@v5.0.0 + uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 with: python-version: '3.10' - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.3.3 + uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3 - name: Setup gyp env run: | @@ -78,7 +78,7 @@ jobs: uses: browser-actions/setup-chrome@803ef6dfb4fdf22089c9563225d95e4a515820a0 # latest - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index 8df9d74a95..fd555159b1 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -19,7 +19,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@v4.1.1 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Install Fossa run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash" diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index b442a9e1f2..ef7eef4524 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -28,10 +28,10 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js 18.x - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: 18.x diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 474c70b9d4..5471555402 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -32,17 +32,17 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 # Required to retrieve git history - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.5 + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: storybook yarn install diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index eb79a1896f..d9eada0f1d 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -33,10 +33,10 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/contrib/.devcontainer/Dockerfile b/contrib/.devcontainer/Dockerfile index 6407aa26dd..05be7a6d7f 100644 --- a/contrib/.devcontainer/Dockerfile +++ b/contrib/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/devcontainers/typescript-node:20 +FROM mcr.microsoft.com/devcontainers/typescript-node:20@sha256:185cde4e033cd68fb4c5c9eef94ba6bde0dac2f334ecd1a7cf6cd2c87f712d85 RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ && apt-get -y install chromium \ diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild index 053b4fb492..a0ab7e7afe 100644 --- a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild +++ b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild @@ -46,7 +46,7 @@ RUN yarn workspace app build -FROM nginx:mainline +FROM nginx:mainline@sha256:5f44022eab9198d75939d9eaa5341bc077eca16fa51d4ef32d33f1bd4c8cbe7d RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/* diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild b/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild index f0fa4a034b..7e58443e66 100644 --- a/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild +++ b/contrib/docker/frontend-with-nginx/Dockerfile.hostbuild @@ -27,7 +27,7 @@ -FROM nginx:mainline +FROM nginx:mainline@sha256:5f44022eab9198d75939d9eaa5341bc077eca16fa51d4ef32d33f1bd4c8cbe7d RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/* diff --git a/plugins/scaffolder-backend-module-rails/Rails.dockerfile b/plugins/scaffolder-backend-module-rails/Rails.dockerfile index 8ea3fc370a..191ddda8e7 100644 --- a/plugins/scaffolder-backend-module-rails/Rails.dockerfile +++ b/plugins/scaffolder-backend-module-rails/Rails.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.3 +FROM ruby:3.3@sha256:79fd4a27fc343abc7372e9082fcfae3750e24cdb519c1cfd89cfa7a4d48a5191 RUN apt-get update -qq && \ apt-get install -y nodejs postgresql-client git && \ diff --git a/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile b/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile index a16248239c..33c6ad3788 100644 --- a/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile +++ b/plugins/scaffolder-backend/scripts/Cookiecutter.dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.18 +FROM alpine:3.18@sha256:11e21d7b981a59554b3f822c49f6e9f57b6068bb74f49c4cd5cc4c663c7e5160 RUN apk add --update \ git \ From 5ab1669d4a10689dfcd5c0bcaa43db59e3697311 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 13:19:07 +0000 Subject: [PATCH 284/468] fix(deps): update dependency recharts to v2.11.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 563c4ff442..a0f3d941d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39267,8 +39267,8 @@ __metadata: linkType: hard "recharts@npm:^2.5.0": - version: 2.10.4 - resolution: "recharts@npm:2.10.4" + version: 2.11.0 + resolution: "recharts@npm:2.11.0" dependencies: clsx: ^2.0.0 eventemitter3: ^4.0.1 @@ -39282,7 +39282,7 @@ __metadata: prop-types: ^15.6.0 react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: f283a21367aa675af83ada6d92ebd7bfa8931ba805bba34ee6247f74cba1b5d37c5fd48d20baa3533bbd3f0a98a1566e5d3733d23825caee8b256dd66ae5ff2c + checksum: 5c81ff38a244901757c594d8cd48de5d0ad3450d357de826a7ca98cfd1658732f320699ae32e497fb51e8c0db3fe6e7338d1990f1278a1c774ee5b9f00146e2d languageName: node linkType: hard From 36625b6d5220dc8e00844cba99e5542c3634fe58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 2 Feb 2024 11:33:30 +0100 Subject: [PATCH 285/468] update the fetch recommendations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../help-im-behind-a-corporate-proxy.md | 115 +++++++++++------- .../adr013-use-node-fetch.md | 30 ++--- 2 files changed, 87 insertions(+), 58 deletions(-) diff --git a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md index 9ec3f1ca64..6ae6d10fac 100644 --- a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md +++ b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md @@ -1,66 +1,65 @@ # Running the backend behind a Corporate Proxy -Let's admit it, we've all been there. Sometimes you've gotta run stuff with no way out to the public internet, only the smallest of corporate proxy tunnels. +This article helps you get your backend installation up and running making calls through corporate proxies. -Whilst this isn't supported natively by Backstage, this might help you get your installation up and running making calls through the said proxy tunnel. +## Background -## backend +Let's admit it, we've all been there. Sometimes you have to run stuff with no way out to the public internet, except via the smallest of corporate proxy tunnels. It's most likely that you're going to run into these issues from the backend part of Backstage as that's the part that isn't helped by your browser or OS settings for the corporate proxy. -Unfortunately, `nodejs` does not respect `HTTP(S)_PROXY` environment variables by default, and the library that we use to provide `fetch` functionality `node-fetch` (provided by `cross-fetch`) does not also respect these environment variables. +Unfortunately, neither the Node.js native `fetch` nor the other frequently used library `node-fetch` (see [ADR013](https://backstage.io/docs/architecture-decisions/adrs-adr013)) respect `HTTP(S)_PROXY` environment variables by default. As an additional complication, there is no single solution for configuring both native `fetch` and `node-fetch` at once, uniformly. -There are however some ways to get this to work without too much effort. It's most likely that you're going to run into these issues from the `backend` part of `backstage` as that's the part that isn't helped by your browser or OS's settings for the corporate proxy. +There are however some ways to get this to work without too much effort. -**Note:** You're gonna want to be in your backend working directory for these solutions as that's where the requests come from that don't go through this proxy. +## Installation -### Using `global-agent` +**Note:** You're going to want to be in your backend working directory for these solutions as that's where the requests come from that don't go through this proxy. -1. Install `global-agent` using `yarn add global-agent` -2. Go to the entry file for the backend (`src/index.ts`) -3. At the top of the file paste the following: +1. Install the required packages in your backend, by running the following command inside your backend directory (typically `packages/backend` under your repository root). -```ts -import 'global-agent/bootstrap'; -``` + ```bash + yarn add undici global-agent + ``` -4. Start the backend with the `global-agent` variables + `undici` exposes the settings for native `fetch`, and `global-agent` can set things up for `node-fetch`. -```sh -export GLOBAL_AGENT_HTTP_PROXY=$HTTP_PROXY -export GLOBAL_AGENT_NO_PROXY=$NO_PROXY -yarn start -``` +1. Go to the entry file for the backend (typically `packages/backend/src/index.ts`), and add the following at the top: -More information and more options for configuring `global-agent` including just using the default environment variables can be found here: https://github.com/gajus/global-agent + ```ts + import 'global-agent/bootstrap'; + import { setGlobalDispatcher, ProxyAgent } from 'undici'; -### Using `proxy-agent` + const proxyEnv = + process.env.GLOBAL_AGENT_HTTP_PROXY || + process.env.GLOBAL_AGENT_HTTPS_PROXY; -`proxy-agent` is a library that you can use to override the `globalAgents` of `node` land with a tunnel to use for each request. + if (proxyEnv) { + const proxyUrl = new URL(proxyEnv); + setGlobalDispatcher( + new ProxyAgent({ + uri: proxyUrl.protocol + proxyUrl.host, + token: + proxyUrl.username && proxyUrl.password + ? `Basic ${Buffer.from( + `${proxyUrl.username}:${proxyUrl.password}`, + ).toString('base64')}` + : undefined, + }), + ); + } + ``` -1. Install `proxy-agent` using `yarn add proxy-agent` -2. Go to the entry file for the backend (`src/index.ts`) -3. At the top of the file paste the following: + The first import automatically bootstraps `global-agent`, which addresses `node-fetch` proxying. The lines of code below that peeks into the same environment variables as `global-agent` uses, and also leverages them to set up the `undici` package which affects native `fetch`. Does that seem weird? Yes, we think so too. But in the current state of the Node.js ecosystem, that's how it works. -```ts -import ProxyAgent from 'proxy-agent'; -import http from 'http'; -import https from 'https'; + This code is kept brief for illustrative purposes. You may want to adjust it slightly if you need support for [no-proxy excludes](https://gist.github.com/zicklag/1bb50db6c5138de347c224fda14286da) or only do proxying in local development etc. Also see [the `global-agent` docs](https://github.com/gajus/global-agent) for information about its configuration options. -/* - Something to note here, this might need different configuration depending on your own setup. - If you only have an http_proxy then you'll need to set that as both the http and https globalAgent instead. -*/ -if (process.env.HTTP_PROXY) { - http.globalAgent = new ProxyAgent(process.env.HTTP_PROXY); -} +1. Start the backend with the correct environment variables set. For example: -if (process.env.HTTPS_PROXY) { - https.globalAgent = new ProxyAgent(process.env.HTTPS_PROXY); -} -``` + ```sh + export GLOBAL_AGENT_HTTP_PROXY=http://username:password@proxy.example.net:8888 + yarn start + ``` -4. Start the backend with `yarn start` - -## config +## Configuration If your development environment is in the cloud (like with [AWS Cloud9](https://aws.amazon.com/cloud9/) or an instance of [Theia](https://theia-ide.org/)), you will need to update your configuration. @@ -82,3 +81,33 @@ backend: ``` The app port must proxy web socket connections in order to make hot reloading work. + +## Alternatives to `global-agent` + +The `proxy-agent` package can be used as an alternative to `global-agent` (do not install both!), and also ensures that the `node-fetch` library correctly respects proxy settings, but [does NOT work](https://github.com/TooTallNate/proxy-agents/issues/239) for modern `undici` based native Node.js `fetch`, so you'll have to also do the `undici` steps in the section above in addition to this. + +`proxy-agent` is a library that you can use to override the `globalAgents` of `node` land with a tunnel to use for each request. + +1. Install `proxy-agent` using `yarn add proxy-agent` +2. Go to the entry file for the backend (`src/index.ts`) +3. At the top of the file paste the following: + + ```ts + import ProxyAgent from 'proxy-agent'; + import http from 'http'; + import https from 'https'; + + /* + Something to note here, this might need different configuration depending on your own setup. + If you only have an http_proxy then you'll need to set that as both the http and https globalAgent instead. + */ + if (process.env.HTTP_PROXY) { + http.globalAgent = new ProxyAgent(process.env.HTTP_PROXY); + } + + if (process.env.HTTPS_PROXY) { + https.globalAgent = new ProxyAgent(process.env.HTTPS_PROXY); + } + ``` + +4. Start the backend with `yarn start` diff --git a/docs/architecture-decisions/adr013-use-node-fetch.md b/docs/architecture-decisions/adr013-use-node-fetch.md index d802988ae4..e7d1494b0b 100644 --- a/docs/architecture-decisions/adr013-use-node-fetch.md +++ b/docs/architecture-decisions/adr013-use-node-fetch.md @@ -2,7 +2,7 @@ id: adrs-adr013 title: 'ADR013: Proper use of HTTP fetching libraries' # prettier-ignore -description: Architecture Decision Record (ADR) for the proper use of fetchApiRef, node-fetch, and cross-fetch for data fetching. +description: Architecture Decision Record (ADR) for the proper use of fetch libraries for data fetching. --- ## Context @@ -12,13 +12,13 @@ support burden of keeping said package up to date. ## Decision -Backend (node) packages should use the `node-fetch` package for HTTP data -fetching. Example: +Newly written backend (Node.js) packages should use the native `fetch` call for HTTP data +fetching. Additionally, in legacy code, using the `node-fetch` library continues to be allowed. Other third party fetching libraries (for example `axios`, `got` etc) are not allowed. Example: ```ts -import fetch from 'node-fetch'; import { ResponseError } from '@backstage/errors'; +// note that this is the global fetch, not imported from anywhere const response = await fetch('https://example.com/api/v1/users.json'); if (!response.ok) { throw await ResponseError.fromResponse(response); @@ -26,19 +26,19 @@ if (!response.ok) { const users = await response.json(); ``` -Frontend plugins and packages should prefer to use the -[`fetchApiRef`](https://backstage.io/docs/reference/core-plugin-api.fetchapiref). -It uses `cross-fetch` internally. Example: +Frontend plugins and packages should use the [`fetchApiRef`](https://backstage.io/docs/reference/core-plugin-api.fetchapiref). Example: ```ts -import { useApi } from '@backstage/core-plugin-api'; -const { fetch } = useApi(fetchApiRef); +import { useApi, fetchApiRef } from '@backstage/core-plugin-api'; -const response = await fetch('https://example.com/api/v1/users.json'); -if (!response.ok) { - throw await ResponseError.fromResponse(response); -} -const users = await response.json(); +const MyComponent = () => { + const { fetch } = useApi(fetchApiRef); + const response = await fetch('https://example.com/api/v1/users.json'); + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + const users = await response.json(); + // ... ``` Isomorphic packages should have a dependency on the `cross-fetch` package for @@ -67,5 +67,5 @@ export class MyClient { ## Consequences We will gradually transition away from third party packages such as `axios`, -`got` and others. Once we have transitioned to `node-fetch` we will add lint +`got` and others. Once we have transitioned to native `fetch` and `node-fetch` we will add lint rules to enforce this decision. From 198affd2599f93c6491eeba1ef1dfe5f78b3384a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sun, 4 Feb 2024 14:45:06 +0100 Subject: [PATCH 286/468] just keep the updated docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../help-im-behind-a-corporate-proxy.md | 2 +- .../adr013-use-node-fetch.md | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md index 6ae6d10fac..deef356d84 100644 --- a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md +++ b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md @@ -84,7 +84,7 @@ The app port must proxy web socket connections in order to make hot reloading wo ## Alternatives to `global-agent` -The `proxy-agent` package can be used as an alternative to `global-agent` (do not install both!), and also ensures that the `node-fetch` library correctly respects proxy settings, but [does NOT work](https://github.com/TooTallNate/proxy-agents/issues/239) for modern `undici` based native Node.js `fetch`, so you'll have to also do the `undici` steps in the section above in addition to this. +The `proxy-agent` package can be used as an alternative to `global-agent` (do not install both!), and also ensures that the `node-fetch` library correctly respects proxy settings, but [does NOT work](https://github.com/TooTallNate/proxy-agents/issues/239) for modern `undici` based native Node.js `fetch`, so you'll still have to also do the `undici` steps in the section above in addition to this. `proxy-agent` is a library that you can use to override the `globalAgents` of `node` land with a tunnel to use for each request. diff --git a/docs/architecture-decisions/adr013-use-node-fetch.md b/docs/architecture-decisions/adr013-use-node-fetch.md index e7d1494b0b..d802988ae4 100644 --- a/docs/architecture-decisions/adr013-use-node-fetch.md +++ b/docs/architecture-decisions/adr013-use-node-fetch.md @@ -2,7 +2,7 @@ id: adrs-adr013 title: 'ADR013: Proper use of HTTP fetching libraries' # prettier-ignore -description: Architecture Decision Record (ADR) for the proper use of fetch libraries for data fetching. +description: Architecture Decision Record (ADR) for the proper use of fetchApiRef, node-fetch, and cross-fetch for data fetching. --- ## Context @@ -12,13 +12,13 @@ support burden of keeping said package up to date. ## Decision -Newly written backend (Node.js) packages should use the native `fetch` call for HTTP data -fetching. Additionally, in legacy code, using the `node-fetch` library continues to be allowed. Other third party fetching libraries (for example `axios`, `got` etc) are not allowed. Example: +Backend (node) packages should use the `node-fetch` package for HTTP data +fetching. Example: ```ts +import fetch from 'node-fetch'; import { ResponseError } from '@backstage/errors'; -// note that this is the global fetch, not imported from anywhere const response = await fetch('https://example.com/api/v1/users.json'); if (!response.ok) { throw await ResponseError.fromResponse(response); @@ -26,19 +26,19 @@ if (!response.ok) { const users = await response.json(); ``` -Frontend plugins and packages should use the [`fetchApiRef`](https://backstage.io/docs/reference/core-plugin-api.fetchapiref). Example: +Frontend plugins and packages should prefer to use the +[`fetchApiRef`](https://backstage.io/docs/reference/core-plugin-api.fetchapiref). +It uses `cross-fetch` internally. Example: ```ts -import { useApi, fetchApiRef } from '@backstage/core-plugin-api'; +import { useApi } from '@backstage/core-plugin-api'; +const { fetch } = useApi(fetchApiRef); -const MyComponent = () => { - const { fetch } = useApi(fetchApiRef); - const response = await fetch('https://example.com/api/v1/users.json'); - if (!response.ok) { - throw await ResponseError.fromResponse(response); - } - const users = await response.json(); - // ... +const response = await fetch('https://example.com/api/v1/users.json'); +if (!response.ok) { + throw await ResponseError.fromResponse(response); +} +const users = await response.json(); ``` Isomorphic packages should have a dependency on the `cross-fetch` package for @@ -67,5 +67,5 @@ export class MyClient { ## Consequences We will gradually transition away from third party packages such as `axios`, -`got` and others. Once we have transitioned to native `fetch` and `node-fetch` we will add lint +`got` and others. Once we have transitioned to `node-fetch` we will add lint rules to enforce this decision. From 9f0a3988b400c24a427822f0f5a8464d69813b21 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 14:25:11 +0000 Subject: [PATCH 287/468] fix(deps): update dependency winston-transport to v4.7.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 95e397c130..dfc7a5c6e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44983,13 +44983,13 @@ __metadata: linkType: hard "winston-transport@npm:^4.5.0": - version: 4.6.0 - resolution: "winston-transport@npm:4.6.0" + version: 4.7.0 + resolution: "winston-transport@npm:4.7.0" dependencies: logform: ^2.3.2 readable-stream: ^3.6.0 triple-beam: ^1.3.0 - checksum: 19f06ebdbb57cb14cdd48a23145d418d3bbe538851053303f84f04a8a849bb530b78b1495a175059c1299f92945dc61d5421c4914fee32d9a41bc397d84f26d7 + checksum: ce074b5c76a99bee5236cf2b4d30fadfaf1e551d566f654f1eba303dc5b5f77169c21545ff5c5e4fdad9f8e815fc6d91b989f1db34161ecca6e860e62fd3a862 languageName: node linkType: hard From 4d450802546b89947d9acfbefa457d0543988135 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 14:26:03 +0000 Subject: [PATCH 288/468] fix(deps): update opentelemetry-js monorepo Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/backend/package.json | 2 +- yarn.lock | 58 +++++++++++++++++------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 2386d799a9..f6967a2381 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -82,7 +82,7 @@ "@gitbeaker/node": "^35.1.0", "@octokit/rest": "^19.0.3", "@opentelemetry/api": "^1.4.1", - "@opentelemetry/exporter-prometheus": "^0.47.0", + "@opentelemetry/exporter-prometheus": "^0.48.0", "@opentelemetry/sdk-metrics": "^1.13.0", "azure-devops-node-api": "^12.0.0", "better-sqlite3": "^9.0.0", diff --git a/yarn.lock b/yarn.lock index 95e397c130..9624eb4b9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14450,59 +14450,59 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:1.20.0": - version: 1.20.0 - resolution: "@opentelemetry/core@npm:1.20.0" +"@opentelemetry/core@npm:1.21.0": + version: 1.21.0 + resolution: "@opentelemetry/core@npm:1.21.0" dependencies: - "@opentelemetry/semantic-conventions": 1.20.0 + "@opentelemetry/semantic-conventions": 1.21.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.8.0" - checksum: 90a01430902926894a3d7ac6a5bd68b3bcb59aede568d08ca09abdd6d9cc46e9423f2fc65a496c7a8c13a2d3df259586f1a0d500fac6271524f189247e97d8fa + checksum: 857eb667732edd1ad20107446935f1860b67602ab78493c2d0fc1711fdff0d8d1b63afcf1ea28468d62605e1237a38feb641ed9a154c3af87adb21b54101ba65 languageName: node linkType: hard -"@opentelemetry/exporter-prometheus@npm:^0.47.0": - version: 0.47.0 - resolution: "@opentelemetry/exporter-prometheus@npm:0.47.0" +"@opentelemetry/exporter-prometheus@npm:^0.48.0": + version: 0.48.0 + resolution: "@opentelemetry/exporter-prometheus@npm:0.48.0" dependencies: - "@opentelemetry/core": 1.20.0 - "@opentelemetry/resources": 1.20.0 - "@opentelemetry/sdk-metrics": 1.20.0 + "@opentelemetry/core": 1.21.0 + "@opentelemetry/resources": 1.21.0 + "@opentelemetry/sdk-metrics": 1.21.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 7b9af3e1912f4ef796f29d34e6a7a35cc51c6c4bef1252678c58180ff4e90a121663acbaf5c4ff90a197258e2ab596f7b873364ca80ea3096bc675e97df5bead + checksum: e003fb18f2a0e2010901b4f2ebd72f8a3b79c6961426e122e428babede7f6926fbba10f7483c524411bb024e809623782812e2a07d937b04c6425d5bf4559671 languageName: node linkType: hard -"@opentelemetry/resources@npm:1.20.0": - version: 1.20.0 - resolution: "@opentelemetry/resources@npm:1.20.0" +"@opentelemetry/resources@npm:1.21.0": + version: 1.21.0 + resolution: "@opentelemetry/resources@npm:1.21.0" dependencies: - "@opentelemetry/core": 1.20.0 - "@opentelemetry/semantic-conventions": 1.20.0 + "@opentelemetry/core": 1.21.0 + "@opentelemetry/semantic-conventions": 1.21.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.8.0" - checksum: 22b851789bccd778901ea095f51006b57b0059ddeb0ff153ce548c92cd83202af5739fc9dd5c1d2e4640c8eacf7a08ffd24d5ae30f2ebae60855b026ecb87d33 + checksum: 79866dd673aa0b3cc4c2bbe614af48b7fa15e09661875c160eb00667832a83f531ea7c4bea8f1d3d97ee01ab5107152125ddc85837bf984c441471707417cacb languageName: node linkType: hard -"@opentelemetry/sdk-metrics@npm:1.20.0, @opentelemetry/sdk-metrics@npm:^1.13.0": - version: 1.20.0 - resolution: "@opentelemetry/sdk-metrics@npm:1.20.0" +"@opentelemetry/sdk-metrics@npm:1.21.0, @opentelemetry/sdk-metrics@npm:^1.13.0": + version: 1.21.0 + resolution: "@opentelemetry/sdk-metrics@npm:1.21.0" dependencies: - "@opentelemetry/core": 1.20.0 - "@opentelemetry/resources": 1.20.0 + "@opentelemetry/core": 1.21.0 + "@opentelemetry/resources": 1.21.0 lodash.merge: ^4.6.2 peerDependencies: "@opentelemetry/api": ">=1.3.0 <1.8.0" - checksum: 6bd2d77ee14d85d9894ba08715969bc18d75582a53d9d31e2fed6a77f46c32e5fbae6daf6f1c32446a3cc5169620b7f29da27ca006d7c16750f9d83966fd864e + checksum: 393d01c15ab000bb2b05c95233e58d9e8341b0e1e2e437b7257291bf6e1edfec2971aaf76f5bc974a2291c4678dfcaed699676223e18a5f33018d3e4825b479b languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.20.0": - version: 1.20.0 - resolution: "@opentelemetry/semantic-conventions@npm:1.20.0" - checksum: c71d466ab37158c57ff15a8fcb55682c812993b9d0d8e05114094abbe70f5ce2735909d70b749fddbc0621e5cdae19fecbb4e087034f606bce98e3bf9c74613e +"@opentelemetry/semantic-conventions@npm:1.21.0": + version: 1.21.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.21.0" + checksum: 8bd477ddabecf87499985de773265b35a09142071b14e1e427237181e90c4e0f5b1959d009acfb81b80319debbcb453f137e6686c63b60af9656aa611f607b77 languageName: node linkType: hard @@ -27171,7 +27171,7 @@ __metadata: "@gitbeaker/node": ^35.1.0 "@octokit/rest": ^19.0.3 "@opentelemetry/api": ^1.4.1 - "@opentelemetry/exporter-prometheus": ^0.47.0 + "@opentelemetry/exporter-prometheus": ^0.48.0 "@opentelemetry/sdk-metrics": ^1.13.0 "@types/dockerode": ^3.3.0 "@types/express": ^4.17.6 From e6f083179d669aa9b9626f4db9905e57edd5a253 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:18:25 +0000 Subject: [PATCH 289/468] fix(deps): update rjsf monorepo to v5.17.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-2d87790.md | 11 +++++ plugins/home-react/package.json | 2 +- plugins/home/package.json | 8 ++-- plugins/scaffolder-react/package.json | 8 ++-- plugins/scaffolder/package.json | 8 ++-- yarn.lock | 68 +++++++++++++-------------- 6 files changed, 58 insertions(+), 47 deletions(-) create mode 100644 .changeset/renovate-2d87790.md diff --git a/.changeset/renovate-2d87790.md b/.changeset/renovate-2d87790.md new file mode 100644 index 0000000000..5230172e2b --- /dev/null +++ b/.changeset/renovate-2d87790.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-home-react': patch +'@backstage/plugin-home': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +--- + +Updated dependency `@rjsf/utils` to `5.17.0`. +Updated dependency `@rjsf/core` to `5.17.0`. +Updated dependency `@rjsf/material-ui` to `5.17.0`. +Updated dependency `@rjsf/validator-ajv8` to `5.17.0`. diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index dcaed77089..f6e6898999 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -38,7 +38,7 @@ "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@rjsf/utils": "5.16.1", + "@rjsf/utils": "5.17.0", "@types/react": "^16.13.1 || ^17.0.0" }, "peerDependencies": { diff --git a/plugins/home/package.json b/plugins/home/package.json index ae314c2e9d..92dd70dffd 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -62,10 +62,10 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", - "@rjsf/core": "5.16.1", - "@rjsf/material-ui": "5.16.1", - "@rjsf/utils": "5.16.1", - "@rjsf/validator-ajv8": "5.16.1", + "@rjsf/core": "5.17.0", + "@rjsf/material-ui": "5.17.0", + "@rjsf/utils": "5.17.0", + "@rjsf/validator-ajv8": "5.17.0", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "lodash": "^4.17.21", "luxon": "^3.4.3", diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 21b3045d87..9852d019f2 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -59,10 +59,10 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@react-hookz/web": "^23.0.0", - "@rjsf/core": "5.16.1", - "@rjsf/material-ui": "5.16.1", - "@rjsf/utils": "5.16.1", - "@rjsf/validator-ajv8": "5.16.1", + "@rjsf/core": "5.17.0", + "@rjsf/material-ui": "5.17.0", + "@rjsf/utils": "5.17.0", + "@rjsf/validator-ajv8": "5.17.0", "@types/json-schema": "^7.0.9", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "classnames": "^2.2.6", diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 4faa2d9198..1c8eecf2da 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -68,10 +68,10 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@react-hookz/web": "^23.0.0", - "@rjsf/core": "5.16.1", - "@rjsf/material-ui": "5.16.1", - "@rjsf/utils": "5.16.1", - "@rjsf/validator-ajv8": "5.16.1", + "@rjsf/core": "5.17.0", + "@rjsf/material-ui": "5.17.0", + "@rjsf/utils": "5.17.0", + "@rjsf/validator-ajv8": "5.17.0", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "@uiw/react-codemirror": "^4.9.3", "classnames": "^2.2.6", diff --git a/yarn.lock b/yarn.lock index dfc7a5c6e0..f09643652c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7129,7 +7129,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 - "@rjsf/utils": 5.16.1 + "@rjsf/utils": 5.17.0 "@types/react": ^16.13.1 || ^17.0.0 "@types/react-grid-layout": ^1.3.2 peerDependencies: @@ -7160,10 +7160,10 @@ __metadata: "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 - "@rjsf/core": 5.16.1 - "@rjsf/material-ui": 5.16.1 - "@rjsf/utils": 5.16.1 - "@rjsf/validator-ajv8": 5.16.1 + "@rjsf/core": 5.17.0 + "@rjsf/material-ui": 5.17.0 + "@rjsf/utils": 5.17.0 + "@rjsf/validator-ajv8": 5.17.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -8580,10 +8580,10 @@ __metadata: "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 "@react-hookz/web": ^23.0.0 - "@rjsf/core": 5.16.1 - "@rjsf/material-ui": 5.16.1 - "@rjsf/utils": 5.16.1 - "@rjsf/validator-ajv8": 5.16.1 + "@rjsf/core": 5.17.0 + "@rjsf/material-ui": 5.17.0 + "@rjsf/utils": 5.17.0 + "@rjsf/validator-ajv8": 5.17.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -8644,10 +8644,10 @@ __metadata: "@material-ui/icons": ^4.9.1 "@material-ui/lab": 4.0.0-alpha.61 "@react-hookz/web": ^23.0.0 - "@rjsf/core": 5.16.1 - "@rjsf/material-ui": 5.16.1 - "@rjsf/utils": 5.16.1 - "@rjsf/validator-ajv8": 5.16.1 + "@rjsf/core": 5.17.0 + "@rjsf/material-ui": 5.17.0 + "@rjsf/utils": 5.17.0 + "@rjsf/validator-ajv8": 5.17.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -15271,32 +15271,32 @@ __metadata: languageName: node linkType: hard -"@rjsf/core@npm:5.16.1": - version: 5.16.1 - resolution: "@rjsf/core@npm:5.16.1" +"@rjsf/core@npm:5.17.0": + version: 5.17.0 + resolution: "@rjsf/core@npm:5.17.0" dependencies: lodash: ^4.17.21 lodash-es: ^4.17.21 - markdown-to-jsx: ^7.4.0 + markdown-to-jsx: ^7.4.1 nanoid: ^3.3.7 prop-types: ^15.8.1 peerDependencies: "@rjsf/utils": ^5.16.x react: ^16.14.0 || >=17 - checksum: 2f88dc6af9dda8ec5c8cbac63f3f9e776a11fe363ce938aa7b5c7a3baaa84a7a2f3796ebf55b361a8cb65267a1715ab880a4743636fb88e06b0240d07f0e4c7b + checksum: adfcbd1d44cef5f9e5de2873096085abd03b146dcef2c9c226060341ce2c935b5399e4ad5f00ad5091394224f5859bd6ac9bac533537dc5c8e2edb16b52b67cf languageName: node linkType: hard -"@rjsf/material-ui@npm:5.16.1": - version: 5.16.1 - resolution: "@rjsf/material-ui@npm:5.16.1" +"@rjsf/material-ui@npm:5.17.0": + version: 5.17.0 + resolution: "@rjsf/material-ui@npm:5.17.0" peerDependencies: "@material-ui/core": ^4.12.3 "@material-ui/icons": ^4.11.2 "@rjsf/core": ^5.16.x "@rjsf/utils": ^5.16.x react: ^16.14.0 || >=17 - checksum: ca237b699d74246622f4a29078674bfbf9c8238adec7fda6a5062e064d11c6f15c6dba117daa21fe3ac778fa2e884fb2366b5c5bb5e9490f91f9b611f7f010fe + checksum: c56fb145042cd1af7c6f0e149f4c7e61a9c4b9cf9e6dce40cd4f691da569e7d14ca1b922a0f54aafd6ae6900772cdccbdf3719ea83a0be2d0cc3a5f0486939f3 languageName: node linkType: hard @@ -15315,9 +15315,9 @@ __metadata: languageName: node linkType: hard -"@rjsf/utils@npm:5.16.1": - version: 5.16.1 - resolution: "@rjsf/utils@npm:5.16.1" +"@rjsf/utils@npm:5.17.0": + version: 5.17.0 + resolution: "@rjsf/utils@npm:5.17.0" dependencies: json-schema-merge-allof: ^0.8.1 jsonpointer: ^5.0.1 @@ -15326,13 +15326,13 @@ __metadata: react-is: ^18.2.0 peerDependencies: react: ^16.14.0 || >=17 - checksum: 0c69527de4ab6f9d6ec4d1a5e05a31a0a38062d40abe2a2da7bc2324b20b08b0e90c188977ac4408f3b004c758c28097444746f3215e21e184c11cad7e9278c1 + checksum: 01d0001f83083764a8552e009aa7df084621df9d1fc6ccdfad9d534513084421b1ad7494cab77b9b8205d680fd915f612d87800e20ab242e7066f33184c73d4f languageName: node linkType: hard -"@rjsf/validator-ajv8@npm:5.16.1": - version: 5.16.1 - resolution: "@rjsf/validator-ajv8@npm:5.16.1" +"@rjsf/validator-ajv8@npm:5.17.0": + version: 5.17.0 + resolution: "@rjsf/validator-ajv8@npm:5.17.0" dependencies: ajv: ^8.12.0 ajv-formats: ^2.1.1 @@ -15340,7 +15340,7 @@ __metadata: lodash-es: ^4.17.21 peerDependencies: "@rjsf/utils": ^5.16.x - checksum: ab26fc02ad86c7ff36e69aa1285b43a67aa65b2a47e3741a67c243131f74848e55f7215a20416c1a636703aa7f95af7d4270eda9818253f7ea2b3c5f86a26980 + checksum: c6e20e04b33e5b37e55a3d378add22c36970c9c92b6d692003acc5cfaff10282ead6ff775b1fa152ead66e451bc3f0cc518730c6f0ed68a7ecfbcd4098d59f32 languageName: node linkType: hard @@ -33770,12 +33770,12 @@ __metadata: languageName: node linkType: hard -"markdown-to-jsx@npm:^7.4.0": - version: 7.4.0 - resolution: "markdown-to-jsx@npm:7.4.0" +"markdown-to-jsx@npm:^7.4.1": + version: 7.4.1 + resolution: "markdown-to-jsx@npm:7.4.1" peerDependencies: react: ">= 0.14.0" - checksum: 59959d14d7927ed8a97e42d39771e2b445b90fa098477fb6ab040f044d230517dc4a95ba38a4f924cfc965a96b32211d93def150a6184f0e51d2cefdc8cb415d + checksum: 2888cb2389cb810ab35454a59d0623474a60a78e28f281ae0081f87053f6c59b033232a2cd269cc383a5edcaa1eab8ca4b3cf639fe4e1aa3fb418648d14bcc7d languageName: node linkType: hard From 3aed9d50cbbfe81a553782a65e3ebee80e28ff81 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:40:13 +0000 Subject: [PATCH 290/468] fix(deps): update react-router monorepo to v6.22.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index dfc7a5c6e0..a4128cf352 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15250,10 +15250,10 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.14.2": - version: 1.14.2 - resolution: "@remix-run/router@npm:1.14.2" - checksum: 8be55596f64563de95dea04c147ab67c4e6c9b72277c92d4de257dbb326e2aa16ad2adbdec32eb2c985808c642933ac895220654b8c899e4f4bd38f9fd97ff6e +"@remix-run/router@npm:1.15.0": + version: 1.15.0 + resolution: "@remix-run/router@npm:1.15.0" + checksum: 0b5ea6b2e7bb7f3266005e033337a6fed29c458aa5ddc9a97a90ff19813ae6dfb4392871722d66d7e8db93a29760a8257830a9af58824b3a3363ed58e1e471d8 languageName: node linkType: hard @@ -39119,15 +39119,15 @@ __metadata: linkType: hard "react-router-dom@npm:^6.3.0": - version: 6.21.3 - resolution: "react-router-dom@npm:6.21.3" + version: 6.22.0 + resolution: "react-router-dom@npm:6.22.0" dependencies: - "@remix-run/router": 1.14.2 - react-router: 6.21.3 + "@remix-run/router": 1.15.0 + react-router: 6.22.0 peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: bcf668aa1428ca3048d7675f1ae3fe983c8792357a0ed59a1414cb1d682227727aad7c44c4222c6774b8d01bf352478845f7f3f668ebfcaa177208ef64e10bdc + checksum: 21cbdda0070dffb50845a97e2688648a9925c7ebabd1f9335523a1f8ae66048c1d9d06442f1b0ec35a266d1c63ed3b56b437db70807f73440a185f3e2d3c632f languageName: node linkType: hard @@ -39142,14 +39142,14 @@ __metadata: languageName: node linkType: hard -"react-router@npm:6.21.3, react-router@npm:^6.3.0": - version: 6.21.3 - resolution: "react-router@npm:6.21.3" +"react-router@npm:6.22.0, react-router@npm:^6.3.0": + version: 6.22.0 + resolution: "react-router@npm:6.22.0" dependencies: - "@remix-run/router": 1.14.2 + "@remix-run/router": 1.15.0 peerDependencies: react: ">=16.8" - checksum: 7e6297d5b67ae07d2e8564e036dbb15ebd706b41c22238940f47eee9b21ffb76d41336803c3b33435f1ebe210226577e32df3838bcbf2cd7c813fa357c0a4fac + checksum: 94f382f3fa6fcb8525c143d83d4c3a3b010979f417cac0bbe7a63f906b3809e2bb56e8c329b9b3fd3212a498670ab278aea72893e921b827dcf00024c3d115dd languageName: node linkType: hard From c3a91f821390e1bfb3a2d1c0f2f37d0e3cac891b Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 26 Jan 2024 17:13:59 -0500 Subject: [PATCH 291/468] fix(auth): add support for additional alg values Signed-off-by: Aramis --- plugins/auth-backend/src/identity/router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend/src/identity/router.ts b/plugins/auth-backend/src/identity/router.ts index d994f44d37..38e48f0a5d 100644 --- a/plugins/auth-backend/src/identity/router.ts +++ b/plugins/auth-backend/src/identity/router.ts @@ -34,7 +34,7 @@ export function createOidcRouter(options: Options) { jwks_uri: `${baseUrl}/.well-known/jwks.json`, response_types_supported: ['id_token'], subject_types_supported: ['public'], - id_token_signing_alg_values_supported: ['RS256'], + id_token_signing_alg_values_supported: ['RS256', 'ES256'], scopes_supported: ['openid'], token_endpoint_auth_methods_supported: [], claims_supported: ['sub'], From f6dfb17b786fc5cedd9db14f249e82f56828b767 Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 26 Jan 2024 17:21:27 -0500 Subject: [PATCH 292/468] add additional scopes Signed-off-by: Aramis --- plugins/auth-backend/src/identity/router.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/auth-backend/src/identity/router.ts b/plugins/auth-backend/src/identity/router.ts index 38e48f0a5d..c9ae9e685a 100644 --- a/plugins/auth-backend/src/identity/router.ts +++ b/plugins/auth-backend/src/identity/router.ts @@ -34,7 +34,18 @@ export function createOidcRouter(options: Options) { jwks_uri: `${baseUrl}/.well-known/jwks.json`, response_types_supported: ['id_token'], subject_types_supported: ['public'], - id_token_signing_alg_values_supported: ['RS256', 'ES256'], + id_token_signing_alg_values_supported: [ + 'RS256', + 'RS384', + 'RS512', + 'ES256', + 'ES384', + 'ES512', + 'PS256', + 'PS384', + 'PS512', + 'EdDSA', + ], scopes_supported: ['openid'], token_endpoint_auth_methods_supported: [], claims_supported: ['sub'], From 97f8724e91c69aabf3f36f28bc6866ca919c7b8b Mon Sep 17 00:00:00 2001 From: Aramis Date: Fri, 26 Jan 2024 17:25:23 -0500 Subject: [PATCH 293/468] add changeset Signed-off-by: Aramis --- .changeset/dirty-cheetahs-shave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/dirty-cheetahs-shave.md diff --git a/.changeset/dirty-cheetahs-shave.md b/.changeset/dirty-cheetahs-shave.md new file mode 100644 index 0000000000..8f9a476a25 --- /dev/null +++ b/.changeset/dirty-cheetahs-shave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +Support additional algorithms in the `/.well-known/openid-configuration` endpoint. From 4a89bc48b16d58d9a030c3e7151d86bffa1d785d Mon Sep 17 00:00:00 2001 From: Aramis Date: Sat, 27 Jan 2024 00:47:01 -0500 Subject: [PATCH 294/468] add support for service-to-service auth external callers Signed-off-by: Aramis --- plugins/auth-backend/src/identity/router.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/auth-backend/src/identity/router.ts b/plugins/auth-backend/src/identity/router.ts index c9ae9e685a..422b7907c0 100644 --- a/plugins/auth-backend/src/identity/router.ts +++ b/plugins/auth-backend/src/identity/router.ts @@ -44,6 +44,9 @@ export function createOidcRouter(options: Options) { 'PS256', 'PS384', 'PS512', + 'HS256', + 'HS384', + 'HS512', 'EdDSA', ], scopes_supported: ['openid'], From ee3cbed39d889ba7661a9f1526611c260489cd6d Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 30 Jan 2024 22:47:16 -0500 Subject: [PATCH 295/468] remove symmetric keys Signed-off-by: Aramis --- plugins/auth-backend/src/identity/router.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/auth-backend/src/identity/router.ts b/plugins/auth-backend/src/identity/router.ts index 422b7907c0..c9ae9e685a 100644 --- a/plugins/auth-backend/src/identity/router.ts +++ b/plugins/auth-backend/src/identity/router.ts @@ -44,9 +44,6 @@ export function createOidcRouter(options: Options) { 'PS256', 'PS384', 'PS512', - 'HS256', - 'HS384', - 'HS512', 'EdDSA', ], scopes_supported: ['openid'], From 903ea055d58e4ea29ca5b442c60247d90c92562d Mon Sep 17 00:00:00 2001 From: Aramis Date: Tue, 30 Jan 2024 22:47:24 -0500 Subject: [PATCH 296/468] update changeset Signed-off-by: Aramis --- .changeset/dirty-cheetahs-shave.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/dirty-cheetahs-shave.md b/.changeset/dirty-cheetahs-shave.md index 8f9a476a25..1691adec2d 100644 --- a/.changeset/dirty-cheetahs-shave.md +++ b/.changeset/dirty-cheetahs-shave.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-auth-backend': minor +'@backstage/plugin-auth-backend': patch --- Support additional algorithms in the `/.well-known/openid-configuration` endpoint. From 43766556fb3e30b8dbd545285333c45a5ccf4915 Mon Sep 17 00:00:00 2001 From: Deepankumar Loganathan Date: Sun, 4 Feb 2024 16:49:58 +0100 Subject: [PATCH 297/468] Added permissionIntegrationRouter for azure-sites-backend routes Signed-off-by: Deepankumar Loganathan --- .changeset/tall-frogs-clap.md | 5 +++++ plugins/azure-sites-backend/src/service/router.ts | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 .changeset/tall-frogs-clap.md diff --git a/.changeset/tall-frogs-clap.md b/.changeset/tall-frogs-clap.md new file mode 100644 index 0000000000..1e87ca316c --- /dev/null +++ b/.changeset/tall-frogs-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-sites-backend': patch +--- + +Added `permissionIntegrationRouter` for azure-sites-backend routes diff --git a/plugins/azure-sites-backend/src/service/router.ts b/plugins/azure-sites-backend/src/service/router.ts index 9ed198cc20..bd5d20c05e 100644 --- a/plugins/azure-sites-backend/src/service/router.ts +++ b/plugins/azure-sites-backend/src/service/router.ts @@ -27,8 +27,10 @@ import { } from '@backstage/plugin-permission-common'; import { azureSitesActionPermission, + azureSitesPermissions, AZURE_WEB_SITE_NAME_ANNOTATION, } from '@backstage/plugin-azure-sites-common'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; import { CatalogApi } from '@backstage/catalog-client'; import { AzureSitesApi } from '../api'; @@ -47,8 +49,13 @@ export async function createRouter( ): Promise { const { logger, azureSitesApi, permissions, catalogApi } = options; + const permissionIntegrationRouter = createPermissionIntegrationRouter({ + permissions: azureSitesPermissions, + }); + const router = Router(); router.use(express.json()); + router.use(permissionIntegrationRouter); router.get('/health', (_, response) => { logger.info('PONG!'); From 2d62a05655be1b46bef0f37ca5678261536e347f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 16:16:42 +0000 Subject: [PATCH 298/468] fix(deps): update typescript-eslint monorepo to v6.20.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 96 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/yarn.lock b/yarn.lock index dfc7a5c6e0..5f84cbbc51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19685,14 +19685,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.12.0": - version: 6.19.1 - resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" + version: 6.20.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.20.0" dependencies: "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.19.1 - "@typescript-eslint/type-utils": 6.19.1 - "@typescript-eslint/utils": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 + "@typescript-eslint/scope-manager": 6.20.0 + "@typescript-eslint/type-utils": 6.20.0 + "@typescript-eslint/utils": 6.20.0 + "@typescript-eslint/visitor-keys": 6.20.0 debug: ^4.3.4 graphemer: ^1.4.0 ignore: ^5.2.4 @@ -19705,25 +19705,25 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: ad04000cd6c15d864ff92655baa3aec99bb0ccf4714fedd145fedde60a27590a5feafe480beb2f0f3864b416098bde1e9431bada7480eb7ca4efad891e1d2f6f + checksum: d002cbe1a99aef5d5b1601702f08a9e3c060ab5355707b4428ec61854b88513e625e5a898dc9fded669a4b33bb71a216aa7799f0e0c58ee00150218c69e7959c languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.7.2": - version: 6.19.1 - resolution: "@typescript-eslint/parser@npm:6.19.1" + version: 6.20.0 + resolution: "@typescript-eslint/parser@npm:6.20.0" dependencies: - "@typescript-eslint/scope-manager": 6.19.1 - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/typescript-estree": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 + "@typescript-eslint/scope-manager": 6.20.0 + "@typescript-eslint/types": 6.20.0 + "@typescript-eslint/typescript-estree": 6.20.0 + "@typescript-eslint/visitor-keys": 6.20.0 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: cd29619da08a2d9b7123ba4d8240989c747f8e0d5672179d8b147e413ee1334d1fa48570b0c37cf0ae4e26a275fd2d268cbe702c6fed639d3331abbb3292570a + checksum: 91c0a715e7a37a0386770b0c4c208d0732736828294a3f58901655f0edf9230d1211dbfb8ac0ea562993506130773131fc1ee241311f43d78007af959bd46b9a languageName: node linkType: hard @@ -19737,22 +19737,22 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/scope-manager@npm:6.19.1" +"@typescript-eslint/scope-manager@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/scope-manager@npm:6.20.0" dependencies: - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 - checksum: 848cdebc16a3803e8a6d6035a7067605309a652bb2425f475f755b5ace4d80d2c17c8c8901f0f4759556da8d0a5b71024d472b85c3f3c70d0e6dcfe2a972ef35 + "@typescript-eslint/types": 6.20.0 + "@typescript-eslint/visitor-keys": 6.20.0 + checksum: 54a06c485d4be6ac95b283fe2e29c2cd8a9a0b159d0f38e5f670dd2e1265358e2ad7b4442a0c61870430b38a6d0bf640843caaaf4c7f122523455221bbb3b011 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/type-utils@npm:6.19.1" +"@typescript-eslint/type-utils@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/type-utils@npm:6.20.0" dependencies: - "@typescript-eslint/typescript-estree": 6.19.1 - "@typescript-eslint/utils": 6.19.1 + "@typescript-eslint/typescript-estree": 6.20.0 + "@typescript-eslint/utils": 6.20.0 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -19760,7 +19760,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: eab1a30f8d85f7c6e2545de5963fbec2f3bb91913d59623069b4b0db372a671ab048c7018376fc853c3af06ea39417f3e7b27dd665027dd812347a5e64cecd77 + checksum: 438702c626706cb62f0fcbbb3e3c5c8946ade84f170c182eaebb43604716d2dbf05fac105bdbcb968f3d3375e8076bed8bf095ab65dc891666c91d9174bced6f languageName: node linkType: hard @@ -19771,10 +19771,10 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/types@npm:6.19.1" - checksum: 598ce222b59c20432d06f60703d0c2dd16d9b2151569c192852136c57b8188e3ef6ef9fddaa2c136c9a756fcc7d873c0e29ec41cfd340564842287ef7b4571cd +"@typescript-eslint/types@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/types@npm:6.20.0" + checksum: a4551ce9ce40119c2401a70d5a0f9fd16eec4771d076933983fd5fd4a42c0d9a1ac883dfa7640ddec0459057005d4ef4fd19d681b14b8cef89b094283a117f5f languageName: node linkType: hard @@ -19796,12 +19796,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" +"@typescript-eslint/typescript-estree@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.20.0" dependencies: - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 + "@typescript-eslint/types": 6.20.0 + "@typescript-eslint/visitor-keys": 6.20.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -19811,24 +19811,24 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: fb71a14aeee0468780219c5b8d39075f85d360b04ccd0ee88f4f0a615d2c232a6d3016e36d8c6eda2d9dfda86b4f4cc2c3d7582940fb29d33c7cf305e124d4e2 + checksum: 256cdeae8c9c365f23ab1cefb29b9bc20451fc7b879651f47fd388e13976b62ecd0da56bf5b7a5d7050de1160b0e05fc841c4e5b0e91d8b03728a52d76e8caf9 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/utils@npm:6.19.1" +"@typescript-eslint/utils@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/utils@npm:6.20.0" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.19.1 - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/scope-manager": 6.20.0 + "@typescript-eslint/types": 6.20.0 + "@typescript-eslint/typescript-estree": 6.20.0 semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: fe72e75c3ea17a85772b83f148555ea94ff5d55d13586f3fc038833197a74f8071e14c2bbf1781c40eec20005f052f4be2513a725eea82a15da3cb9af3046c70 + checksum: 1c248ce34b612e922796c3bbb323d05994f4bca5d49a200ff14f2d7522c9ca5bdf08613c4f2187c9242f67d73f9c2ec5d07b05c5af7034a2e57843b99230b0b0 languageName: node linkType: hard @@ -19860,13 +19860,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" +"@typescript-eslint/visitor-keys@npm:6.20.0": + version: 6.20.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.20.0" dependencies: - "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/types": 6.20.0 eslint-visitor-keys: ^3.4.1 - checksum: bdf057a42e776970a89cdd568e493e3ea7ec085544d8f318d33084da63c3395ad2c0fb9cef9f61ceeca41f5dab54ab064b7078fe596889005e412ec74d2d1ae4 + checksum: 6a360f16b7b28d3cbb539252d17c6ac8519fd26e5f27f895cd7d400e9ec428b67ecda131f2bd2d57144c0436dcdb15022749b163a46c61e62af2312e8e3be488 languageName: node linkType: hard From 6178a67e838500a49f1f591c451676d868b3be33 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:02:06 +0000 Subject: [PATCH 299/468] chore(deps): update dependency husky to v9 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 6deae7611e..4a90b25e7b 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "eslint-plugin-react": "^7.28.0", "eslint-plugin-testing-library": "^6.0.0", "fs-extra": "10.1.0", - "husky": "^8.0.0", + "husky": "^9.0.0", "lint-staged": "^15.0.0", "minimist": "^1.2.5", "node-gyp": "^10.0.0", diff --git a/yarn.lock b/yarn.lock index 5f84cbbc51..868817d2e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29839,12 +29839,12 @@ __metadata: languageName: node linkType: hard -"husky@npm:^8.0.0": - version: 8.0.3 - resolution: "husky@npm:8.0.3" +"husky@npm:^9.0.0": + version: 9.0.10 + resolution: "husky@npm:9.0.10" bin: - husky: lib/bin.js - checksum: 837bc7e4413e58c1f2946d38fb050f5d7324c6f16b0fd66411ffce5703b294bd21429e8ba58711cd331951ee86ed529c5be4f76805959ff668a337dbfa82a1b0 + husky: bin.mjs + checksum: 55f4b6db6706ff0bc181607d6a64f55310cbb18b4d7db2a5b85608c87a80abafc14ac3a8c4d0b44d6272f4a62d4c2d3d5491d6a13e2cc7ac4895309e5e5f0ec2 languageName: node linkType: hard @@ -40403,7 +40403,7 @@ __metadata: eslint-plugin-react: ^7.28.0 eslint-plugin-testing-library: ^6.0.0 fs-extra: 10.1.0 - husky: ^8.0.0 + husky: ^9.0.0 lint-staged: ^15.0.0 minimist: ^1.2.5 node-gyp: ^10.0.0 From ed1dbb456b3671deb1c9da0358e34781846ca969 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:02:12 +0000 Subject: [PATCH 300/468] chore(deps): update microsoft/setup-msbuild action to v2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_e2e-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 1bddd792b7..fdedf56fb7 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -61,7 +61,7 @@ jobs: python-version: '3.10' - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce # v2.0.0 - name: Setup gyp env run: | From d5fec3e43d034d09129c4ded5bed95368f6c6e99 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:25:24 +0000 Subject: [PATCH 301/468] chore(deps): update peter-evans/create-or-update-comment action to v4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/uffizzi-preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/uffizzi-preview.yaml b/.github/workflows/uffizzi-preview.yaml index dc23ad4bf7..d898209e56 100644 --- a/.github/workflows/uffizzi-preview.yaml +++ b/.github/workflows/uffizzi-preview.yaml @@ -117,7 +117,7 @@ jobs: # Create/Update comment with action deployment status - name: Create or Update Comment with Deployment Notification id: notification - uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3 + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 with: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} @@ -165,7 +165,7 @@ jobs: echo "Access the \`backstage\` endpoint at [\`${BACKSTAGE_HOST}\`](http://${BACKSTAGE_HOST})" >> $GITHUB_STEP_SUMMARY - name: Create or Update Comment with Deployment URL - uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3 + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 with: comment-id: ${{ steps.notification.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} @@ -229,7 +229,7 @@ jobs: direction: last - name: Update Comment with Deletion - uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3 + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 with: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} From b3dfcb6da607367814c784dbdd3d9540c9e3825b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:07:28 +0000 Subject: [PATCH 302/468] chore(deps): update peter-evans/repository-dispatch action to v3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/deploy_packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index ea390a3903..7e4f297933 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -196,7 +196,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - name: Dispatch repository event - uses: peter-evans/repository-dispatch@bf47d102fdb849e755b0b0023ea3e81a44b6f570 # v2.1.2 + uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 with: token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} event-type: release-published From 9aac2b0d36bdb8095ea747fe5e5490cfea1c9f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sun, 4 Feb 2024 20:10:44 +0100 Subject: [PATCH 303/468] move cwd as the first argument to yarn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/hot-horses-matter.md | 108 ++++++++++++++++++ .../tutorials/authenticate-api-requests.md | 4 +- .../building-backends/08-migrating.md | 10 +- docs/features/kubernetes/installation.md | 4 +- docs/features/search/getting-started.md | 4 +- docs/features/techdocs/addons.md | 2 +- docs/features/techdocs/getting-started.md | 4 +- docs/getting-started/configuration.md | 2 +- .../configure-app-with-plugins.md | 2 +- docs/getting-started/homepage.md | 2 +- docs/integrations/aws-s3/discovery.md | 2 +- docs/integrations/azure/discovery.md | 2 +- docs/integrations/azure/org.md | 2 +- docs/integrations/bitbucketCloud/discovery.md | 2 +- .../integrations/bitbucketServer/discovery.md | 2 +- docs/integrations/gerrit/discovery.md | 2 +- docs/integrations/github/discovery.md | 4 +- docs/integrations/github/org.md | 4 +- docs/integrations/gitlab/discovery.md | 2 +- docs/integrations/gitlab/org.md | 2 +- docs/integrations/ldap/org.md | 2 +- docs/permissions/getting-started.md | 2 +- docs/permissions/plugin-authors/01-setup.md | 4 +- docs/plugins/backend-plugin.md | 2 +- .../tutorials/configuring-plugin-databases.md | 4 +- docs/tutorials/switching-sqlite-postgres.md | 2 +- ...-04-30-how-to-quickly-set-up-backstage.mdx | 2 +- packages/app-defaults/README.md | 2 +- packages/backend-app-api/README.md | 2 +- packages/backend-common/README.md | 2 +- packages/backend-defaults/README.md | 2 +- packages/backend-dev-utils/README.md | 2 +- packages/backend-plugin-api/README.md | 2 +- packages/backend-tasks/README.md | 2 +- packages/core-app-api/README.md | 2 +- plugins/adr-backend/README.md | 2 +- plugins/airbrake/README.md | 4 +- plugins/allure/README.md | 2 +- plugins/analytics-module-ga/README.md | 2 +- plugins/analytics-module-ga4/README.md | 2 +- .../README.md | 2 +- .../api-docs-module-protoc-gen-doc/README.md | 2 +- plugins/api-docs/README.md | 2 +- plugins/app-backend/README.md | 2 +- plugins/azure-devops-backend/README.md | 2 +- plugins/azure-devops/README.md | 8 +- plugins/azure-sites-backend/README.md | 2 +- plugins/azure-sites/README.md | 2 +- plugins/badges-backend/README.md | 2 +- plugins/badges/README.md | 2 +- plugins/bazaar-backend/README.md | 2 +- plugins/bazaar/README.md | 2 +- plugins/bitrise/README.md | 2 +- .../README.md | 2 +- .../catalog-backend-module-msgraph/README.md | 2 +- .../catalog-backend-module-openapi/README.md | 2 +- .../catalog-backend-module-puppetdb/README.md | 2 +- .../README.md | 2 +- plugins/catalog-backend/README.md | 2 +- plugins/catalog-graph/README.md | 2 +- plugins/catalog-import/README.md | 2 +- .../catalog-unprocessed-entities/README.md | 2 +- plugins/catalog/README.md | 2 +- plugins/circleci/README.md | 2 +- plugins/code-climate/README.md | 2 +- plugins/code-coverage-backend/README.md | 2 +- plugins/code-coverage/README.md | 2 +- plugins/codescene/README.md | 2 +- plugins/cost-insights/README.md | 6 +- .../contrib/aws-cost-explorer-api.md | 2 +- plugins/devtools-backend/README.md | 2 +- plugins/devtools/README.md | 10 +- plugins/dynatrace/README.md | 2 +- plugins/entity-feedback-backend/README.md | 2 +- plugins/entity-validation/README.md | 2 +- .../events-backend-module-aws-sqs/README.md | 2 +- plugins/events-backend-module-azure/README.md | 2 +- .../README.md | 2 +- .../events-backend-module-gerrit/README.md | 2 +- .../events-backend-module-github/README.md | 2 +- .../events-backend-module-gitlab/README.md | 2 +- plugins/events-backend/README.md | 2 +- plugins/explore-backend/README.md | 6 +- plugins/firehydrant/README.md | 2 +- plugins/fossa/README.md | 2 +- plugins/github-actions/README.md | 2 +- plugins/github-deployments/README.md | 2 +- plugins/graphiql/README.md | 2 +- plugins/graphql-voyager/README.md | 2 +- plugins/home/README.md | 2 +- plugins/ilert/README.md | 2 +- plugins/jenkins-backend/README.md | 2 +- plugins/jenkins/README.md | 2 +- plugins/lighthouse-backend/README.md | 2 +- plugins/lighthouse/README.md | 2 +- .../lighthouse/src/components/Intro/index.tsx | 2 +- plugins/linguist-backend/README.md | 2 +- plugins/linguist/README.md | 2 +- plugins/microsoft-calendar/README.md | 2 +- plugins/newrelic/README.md | 2 +- plugins/nomad-backend/README.md | 2 +- plugins/nomad/README.md | 2 +- plugins/octopus-deploy/README.md | 2 +- plugins/opencost/README.md | 2 +- plugins/periskop/README.md | 4 +- plugins/playlist-backend/README.md | 2 +- plugins/rollbar-backend/README.md | 2 +- plugins/rollbar/README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- .../scaffolder-backend-module-rails/README.md | 2 +- .../README.md | 2 +- .../README.md | 2 +- plugins/scaffolder-backend/README.md | 2 +- plugins/scaffolder/README.md | 2 +- .../search-backend-module-catalog/README.md | 2 +- .../search-backend-module-explore/README.md | 2 +- .../README.md | 2 +- .../search-backend-module-techdocs/README.md | 2 +- plugins/sentry/README.md | 2 +- plugins/shortcuts/README.md | 2 +- plugins/sonarqube-backend/README.md | 2 +- plugins/sonarqube/README.md | 2 +- plugins/splunk-on-call/README.md | 2 +- .../README.md | 2 +- plugins/tech-insights-backend/README.md | 4 +- plugins/tech-insights/README.md | 2 +- plugins/tech-radar/README.md | 2 +- plugins/techdocs-react/README.md | 2 +- plugins/vault-backend/README.md | 2 +- plugins/vault/README.md | 2 +- plugins/xcmetrics/README.md | 4 +- scripts/verify-lockfile-duplicates.js | 4 +- 134 files changed, 269 insertions(+), 161 deletions(-) create mode 100644 .changeset/hot-horses-matter.md diff --git a/.changeset/hot-horses-matter.md b/.changeset/hot-horses-matter.md new file mode 100644 index 0000000000..1b544c0854 --- /dev/null +++ b/.changeset/hot-horses-matter.md @@ -0,0 +1,108 @@ +--- +'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch +'@backstage/plugin-search-backend-module-stack-overflow-collator': patch +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch +'@backstage/plugin-events-backend-module-bitbucket-cloud': patch +'@backstage/plugin-tech-insights-backend-module-jsonfc': patch +'@backstage/plugin-catalog-backend-module-unprocessed': patch +'@backstage/plugin-analytics-module-newrelic-browser': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-sentry': patch +'@backstage/plugin-scaffolder-backend-module-yeoman': patch +'@backstage/plugin-catalog-backend-module-puppetdb': patch +'@backstage/plugin-scaffolder-backend-module-rails': patch +'@backstage/plugin-api-docs-module-protoc-gen-doc': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-catalog-backend-module-openapi': patch +'@backstage/plugin-search-backend-module-techdocs': patch +'@backstage/plugin-events-backend-module-aws-sqs': patch +'@backstage/plugin-search-backend-module-catalog': patch +'@backstage/plugin-search-backend-module-explore': patch +'@backstage/plugin-catalog-unprocessed-entities': patch +'@backstage/plugin-events-backend-module-gerrit': patch +'@backstage/plugin-events-backend-module-github': patch +'@backstage/plugin-events-backend-module-gitlab': patch +'@backstage/plugin-events-backend-module-azure': patch +'@backstage/plugin-entity-feedback-backend': patch +'@backstage/plugin-code-coverage-backend': patch +'@backstage/plugin-tech-insights-backend': patch +'@backstage/plugin-analytics-module-ga4': patch +'@backstage/plugin-azure-devops-backend': patch +'@backstage/backend-plugin-api': patch +'@backstage/plugin-analytics-module-ga': patch +'@backstage/plugin-azure-sites-backend': patch +'@backstage/backend-dev-utils': patch +'@backstage/plugin-github-deployments': patch +'@backstage/plugin-lighthouse-backend': patch +'@backstage/plugin-microsoft-calendar': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/backend-defaults': patch +'@backstage/plugin-entity-validation': patch +'@backstage/plugin-sonarqube-backend': patch +'@backstage/backend-app-api': patch +'@backstage/plugin-devtools-backend': patch +'@backstage/plugin-linguist-backend': patch +'@backstage/plugin-playlist-backend': patch +'@backstage/backend-common': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-explore-backend': patch +'@backstage/plugin-graphql-voyager': patch +'@backstage/plugin-jenkins-backend': patch +'@backstage/plugin-rollbar-backend': patch +'@backstage/backend-tasks': patch +'@backstage/plugin-badges-backend': patch +'@backstage/plugin-bazaar-backend': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-events-backend': patch +'@backstage/plugin-github-actions': patch +'@backstage/plugin-octopus-deploy': patch +'@backstage/plugin-splunk-on-call': patch +'@backstage/plugin-techdocs-react': patch +'@backstage/app-defaults': patch +'@backstage/core-app-api': patch +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-code-coverage': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-nomad-backend': patch +'@backstage/plugin-tech-insights': patch +'@backstage/plugin-vault-backend': patch +'@backstage/plugin-azure-devops': patch +'@backstage/plugin-code-climate': patch +'@backstage/plugin-adr-backend': patch +'@backstage/plugin-app-backend': patch +'@backstage/plugin-azure-sites': patch +'@backstage/plugin-firehydrant': patch +'@backstage/plugin-lighthouse': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-tech-radar': patch +'@backstage/plugin-codescene': patch +'@backstage/plugin-dynatrace': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-xcmetrics': patch +'@backstage/plugin-airbrake': patch +'@backstage/plugin-api-docs': patch +'@backstage/plugin-circleci': patch +'@backstage/plugin-devtools': patch +'@backstage/plugin-graphiql': patch +'@backstage/plugin-linguist': patch +'@backstage/plugin-newrelic': patch +'@backstage/plugin-opencost': patch +'@backstage/plugin-periskop': patch +'@backstage/plugin-bitrise': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-jenkins': patch +'@backstage/plugin-rollbar': patch +'@backstage/plugin-allure': patch +'@backstage/plugin-badges': patch +'@backstage/plugin-bazaar': patch +'@backstage/plugin-sentry': patch +'@backstage/plugin-fossa': patch +'@backstage/plugin-ilert': patch +'@backstage/plugin-nomad': patch +'@backstage/plugin-vault': patch +'@backstage/plugin-home': patch +--- + +Use `--cwd` as the first `yarn` argument diff --git a/contrib/docs/tutorials/authenticate-api-requests.md b/contrib/docs/tutorials/authenticate-api-requests.md index 4c44c8ae65..8b71151ee9 100644 --- a/contrib/docs/tutorials/authenticate-api-requests.md +++ b/contrib/docs/tutorials/authenticate-api-requests.md @@ -88,7 +88,7 @@ Install cookie-parser: ```bash # From your Backstage root directory -yarn add --cwd packages/backend cookie-parser +yarn --cwd packages/backend add cookie-parser ``` Update routes in `packages/backend/src/index.ts`: @@ -223,7 +223,7 @@ Install cookie-parser: ```bash # From your Backstage root directory -yarn add --cwd packages/backend cookie-parser @types/cookie-parser +yarn --cwd packages/backend add cookie-parser @types/cookie-parser ``` Create a custom configured `rootHttpRouterService` in `packages/backend/src/customRootHttpRouterService.ts`: diff --git a/docs/backend-system/building-backends/08-migrating.md b/docs/backend-system/building-backends/08-migrating.md index fc1a6f1859..9eada30ef6 100644 --- a/docs/backend-system/building-backends/08-migrating.md +++ b/docs/backend-system/building-backends/08-migrating.md @@ -83,7 +83,7 @@ following command: ```bash # from the repository root -yarn add --cwd packages/backend @backstage/backend-defaults @backstage/backend-plugin-api +yarn --cwd packages/backend add @backstage/backend-defaults @backstage/backend-plugin-api ``` You should now be able to start this up with the familiar `yarn workspace @@ -616,7 +616,7 @@ if you didn't already have one. ```bash # from the repository root -yarn add --cwd packages/backend @backstage/plugin-catalog-node +yarn --cwd packages/backend add @backstage/plugin-catalog-node ``` Here we've placed the module directly in the backend index file just to get @@ -681,7 +681,7 @@ if you didn't already have one. ```bash # from the repository root -yarn add --cwd packages/backend @backstage/plugin-events-node +yarn --cwd packages/backend add @backstage/plugin-events-node ``` Here we've placed the module directly in the backend index file just to get @@ -715,7 +715,7 @@ And of course you'll need to install those separately as well. ```bash # from the repository root -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-github +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-github ``` You can find a list of the available modules under the [plugins directory](https://github.com/backstage/backstage/tree/master/plugins) in the monorepo. @@ -766,7 +766,7 @@ if you didn't already have one. ```bash # from the repository root -yarn add --cwd packages/backend @backstage/plugin-scaffolder-node +yarn --cwd packages/backend add @backstage/plugin-scaffolder-node ``` Here we've placed the module directly in the backend index file just to get diff --git a/docs/features/kubernetes/installation.md b/docs/features/kubernetes/installation.md index 5111b5ad2e..bdd14098ff 100644 --- a/docs/features/kubernetes/installation.md +++ b/docs/features/kubernetes/installation.md @@ -17,7 +17,7 @@ application. ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-kubernetes +yarn --cwd packages/app add @backstage/plugin-kubernetes ``` Once the package has been installed, you need to import the plugin in your app @@ -55,7 +55,7 @@ Navigate to `packages/backend` of your Backstage app, and install the ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-kubernetes-backend +yarn --cwd packages/backend add @backstage/plugin-kubernetes-backend ``` Create a file called `kubernetes.ts` inside `packages/backend/src/plugins/` and diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md index 1787189e91..7989b60ae2 100644 --- a/docs/features/search/getting-started.md +++ b/docs/features/search/getting-started.md @@ -18,7 +18,7 @@ If you haven't setup Backstage already, start ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-search @backstage/plugin-search-react +yarn --cwd packages/app add @backstage/plugin-search @backstage/plugin-search-react ``` Create a new `packages/app/src/components/search/SearchPage.tsx` file in your @@ -135,7 +135,7 @@ Add the following plugins into your backend app: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-search-backend @backstage/plugin-search-backend-node +yarn --cwd packages/backend add @backstage/plugin-search-backend @backstage/plugin-search-backend-node ``` Create a `packages/backend/src/plugins/search.ts` file containing the following diff --git a/docs/features/techdocs/addons.md b/docs/features/techdocs/addons.md index 2e17891917..471e62a57f 100644 --- a/docs/features/techdocs/addons.md +++ b/docs/features/techdocs/addons.md @@ -54,7 +54,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 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` +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 --cwd packages/app add @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 diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index f798384697..eaa6c2de72 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -23,7 +23,7 @@ Navigate to your new Backstage application directory. And then to your ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-techdocs +yarn --cwd packages/app add @backstage/plugin-techdocs ``` Once the package has been installed, you need to import the plugin in your app. @@ -108,7 +108,7 @@ Navigate to `packages/backend` of your Backstage app, and install the ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-techdocs-backend +yarn --cwd packages/backend add @backstage/plugin-techdocs-backend ``` Create a file called `techdocs.ts` inside `packages/backend/src/plugins/` and diff --git a/docs/getting-started/configuration.md b/docs/getting-started/configuration.md index 5d797ecb4b..0c813b1aee 100644 --- a/docs/getting-started/configuration.md +++ b/docs/getting-started/configuration.md @@ -67,7 +67,7 @@ App. Use the following commands to start the PostgreSQL client installation: ```bash # From your Backstage root directory -yarn add --cwd packages/backend pg +yarn --cwd packages/backend add pg ``` Use your favorite editor to open `app-config.yaml` and add your PostgreSQL diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 5611117636..724746ca16 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -23,7 +23,7 @@ to an entity in the software catalog. ```bash # From your Backstage root directory - yarn add --cwd packages/app @circleci/backstage-plugin + yarn --cwd packages/app add @circleci/backstage-plugin ``` Note the plugin is added to the `app` package, rather than the root diff --git a/docs/getting-started/homepage.md b/docs/getting-started/homepage.md index 04dfb13d6a..18f23f6607 100644 --- a/docs/getting-started/homepage.md +++ b/docs/getting-started/homepage.md @@ -30,7 +30,7 @@ Now, let's get started by installing the home plugin and creating a simple homep ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-home +yarn --cwd packages/app add @backstage/plugin-home ``` #### 2. Create a new HomePage component diff --git a/docs/integrations/aws-s3/discovery.md b/docs/integrations/aws-s3/discovery.md index ceb780141f..d664466a16 100644 --- a/docs/integrations/aws-s3/discovery.md +++ b/docs/integrations/aws-s3/discovery.md @@ -64,7 +64,7 @@ the AWS catalog plugin: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-aws +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-aws ``` Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`: diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md index 3b25a9ac75..742fbb6076 100644 --- a/docs/integrations/azure/discovery.md +++ b/docs/integrations/azure/discovery.md @@ -98,7 +98,7 @@ the Azure catalog plugin: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-azure +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-azure ``` Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`: diff --git a/docs/integrations/azure/org.md b/docs/integrations/azure/org.md index 061baf7301..38c2e7618a 100644 --- a/docs/integrations/azure/org.md +++ b/docs/integrations/azure/org.md @@ -16,7 +16,7 @@ The package is not installed by default, therefore you have to add `@backstage/p ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-msgraph +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-msgraph ``` Next add the basic configuration to `app-config.yaml` diff --git a/docs/integrations/bitbucketCloud/discovery.md b/docs/integrations/bitbucketCloud/discovery.md index 53315a4c48..a7477a2a61 100644 --- a/docs/integrations/bitbucketCloud/discovery.md +++ b/docs/integrations/bitbucketCloud/discovery.md @@ -21,7 +21,7 @@ package. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-bitbucket-cloud +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-cloud ``` ### Installation without Events Support diff --git a/docs/integrations/bitbucketServer/discovery.md b/docs/integrations/bitbucketServer/discovery.md index ad59006ff1..f37732b600 100644 --- a/docs/integrations/bitbucketServer/discovery.md +++ b/docs/integrations/bitbucketServer/discovery.md @@ -21,7 +21,7 @@ package. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-bitbucket-server +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server ``` And then add the entity provider to your catalog builder: diff --git a/docs/integrations/gerrit/discovery.md b/docs/integrations/gerrit/discovery.md index ee843d2454..e2922c1f76 100644 --- a/docs/integrations/gerrit/discovery.md +++ b/docs/integrations/gerrit/discovery.md @@ -18,7 +18,7 @@ the Gerrit provider plugin: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-gerrit +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-gerrit ``` Then add the plugin to the plugin catalog `packages/backend/src/plugins/catalog.ts`: diff --git a/docs/integrations/github/discovery.md b/docs/integrations/github/discovery.md index 3c3d90d6e9..461fab8af8 100644 --- a/docs/integrations/github/discovery.md +++ b/docs/integrations/github/discovery.md @@ -23,7 +23,7 @@ package. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-github +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-github ``` And then add the entity provider to your catalog builder: @@ -250,7 +250,7 @@ package, plus `@backstage/integration` for the basic credentials management: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/integration @backstage/plugin-catalog-backend-module-github +yarn --cwd packages/backend add @backstage/integration @backstage/plugin-catalog-backend-module-github ``` And then add the processors to your catalog builder: diff --git a/docs/integrations/github/org.md b/docs/integrations/github/org.md index 1404b20559..263ceda6c6 100644 --- a/docs/integrations/github/org.md +++ b/docs/integrations/github/org.md @@ -27,7 +27,7 @@ to `@backstage/plugin-catalog-backend-module-github` to your backend package. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-github +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-github ``` > Note: When configuring to use a Provider instead of a Processor you do not @@ -308,7 +308,7 @@ install and register it in the catalog plugin: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-github +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-github ``` ```typescript title="packages/backend/src/plugins/catalog.ts" diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md index 235d93ab03..d41f4f8de5 100644 --- a/docs/integrations/gitlab/discovery.md +++ b/docs/integrations/gitlab/discovery.md @@ -39,7 +39,7 @@ the gitlab catalog plugin: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-gitlab +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-gitlab ``` Once you've done that, you'll also need to add the segment below to `packages/backend/src/plugins/catalog.ts`: diff --git a/docs/integrations/gitlab/org.md b/docs/integrations/gitlab/org.md index ca40cefa9e..f2a483c9d1 100644 --- a/docs/integrations/gitlab/org.md +++ b/docs/integrations/gitlab/org.md @@ -15,7 +15,7 @@ As this provider is not one of the default providers, you will first need to ins ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-gitlab +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-gitlab ``` Then add the plugin to the plugin catalog `packages/backend/src/plugins/catalog.ts`: diff --git a/docs/integrations/ldap/org.md b/docs/integrations/ldap/org.md index 7306ab4105..9bc2667aa7 100644 --- a/docs/integrations/ldap/org.md +++ b/docs/integrations/ldap/org.md @@ -26,7 +26,7 @@ to `@backstage/plugin-catalog-backend-module-ldap` to your backend package. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-ldap +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-ldap ``` > Note: When configuring to use a Provider instead of a Processor you do not diff --git a/docs/permissions/getting-started.md b/docs/permissions/getting-started.md index 01a268f7ef..e241f0e38e 100644 --- a/docs/permissions/getting-started.md +++ b/docs/permissions/getting-started.md @@ -48,7 +48,7 @@ The permissions framework uses a new `permission-backend` plugin to accept autho ```bash # From your Backstage root directory - yarn add --cwd packages/backend @backstage/plugin-permission-backend + yarn --cwd packages/backend add @backstage/plugin-permission-backend ``` 2. Add the following to a new file, `packages/backend/src/plugins/permission.ts`. This adds the permission-backend router, and configures it with a policy which allows everything. diff --git a/docs/permissions/plugin-authors/01-setup.md b/docs/permissions/plugin-authors/01-setup.md index 8134fc0c39..a674d82e25 100644 --- a/docs/permissions/plugin-authors/01-setup.md +++ b/docs/permissions/plugin-authors/01-setup.md @@ -41,8 +41,8 @@ The source code is available here: ```sh # From your Backstage root directory - yarn add --cwd packages/backend @internal/plugin-todo-list-backend @internal/plugin-todo-list-common - yarn add --cwd packages/app @internal/plugin-todo-list + yarn --cwd packages/backend add @internal/plugin-todo-list-backend @internal/plugin-todo-list-common + yarn --cwd packages/app add @internal/plugin-todo-list ``` 3. Include the backend and frontend plugin in your application: diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index d2e93bb08c..22d4bae371 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -69,7 +69,7 @@ to your backend. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @internal/plugin-carmen-backend@^0.1.0 # Change this to match the plugin's package.json +yarn --cwd packages/backend add @internal/plugin-carmen-backend@^0.1.0 # Change this to match the plugin's package.json ``` Create a new file named `packages/backend/src/plugins/carmen.ts`, and add the diff --git a/docs/tutorials/configuring-plugin-databases.md b/docs/tutorials/configuring-plugin-databases.md index 8cd5ce99a9..528d2c6325 100644 --- a/docs/tutorials/configuring-plugin-databases.md +++ b/docs/tutorials/configuring-plugin-databases.md @@ -39,10 +39,10 @@ both of them. ```bash # From your Backstage root directory # install pg if you need PostgreSQL -yarn add --cwd packages/backend pg +yarn --cwd packages/backend add pg # install SQLite 3 if you intend to set it as the client -yarn add --cwd packages/backend better-sqlite3 +yarn --cwd packages/backend add better-sqlite3 ``` From an operational perspective, you only need to install drivers for clients diff --git a/docs/tutorials/switching-sqlite-postgres.md b/docs/tutorials/switching-sqlite-postgres.md index 87afabda60..2ef481dbf3 100644 --- a/docs/tutorials/switching-sqlite-postgres.md +++ b/docs/tutorials/switching-sqlite-postgres.md @@ -21,7 +21,7 @@ First, add PostgreSQL to your `backend` package: ```bash # From your Backstage root directory -yarn add --cwd packages/backend pg +yarn --cwd packages/backend add pg ``` ## Add PostgreSQL configuration diff --git a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.mdx b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.mdx index 4884762560..b59f943245 100644 --- a/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.mdx +++ b/microsite/blog/2020-04-30-how-to-quickly-set-up-backstage.mdx @@ -102,7 +102,7 @@ Install in your app’s package folder (`/packages/app`) with: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin- +yarn --cwd packages/app add @backstage/plugin- ``` After that, you inject the plugin into the application where you want it to be exposed. Please read the documentation for the specific plugin you are installing for more information. diff --git a/packages/app-defaults/README.md b/packages/app-defaults/README.md index cfdaf36acb..ebaaacafe3 100644 --- a/packages/app-defaults/README.md +++ b/packages/app-defaults/README.md @@ -8,7 +8,7 @@ Install the package via Yarn: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/app-defaults +yarn --cwd packages/app add @backstage/app-defaults ``` ## Documentation diff --git a/packages/backend-app-api/README.md b/packages/backend-app-api/README.md index 3fd6170b4f..7ac3767ece 100644 --- a/packages/backend-app-api/README.md +++ b/packages/backend-app-api/README.md @@ -8,7 +8,7 @@ Add the library to your backend app package: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/backend-app-api +yarn --cwd packages/backend add @backstage/backend-app-api ``` ## Documentation diff --git a/packages/backend-common/README.md b/packages/backend-common/README.md index 578fbc0830..71a15bfb5a 100644 --- a/packages/backend-common/README.md +++ b/packages/backend-common/README.md @@ -9,7 +9,7 @@ Add the library to your backend package: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/backend-common +yarn --cwd packages/backend add @backstage/backend-common ``` then make use of the handlers and logger as necessary: diff --git a/packages/backend-defaults/README.md b/packages/backend-defaults/README.md index 01e42d42c4..a1bf881929 100644 --- a/packages/backend-defaults/README.md +++ b/packages/backend-defaults/README.md @@ -8,7 +8,7 @@ Add the library to your backend app package: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/backend-defaults +yarn --cwd packages/backend add @backstage/backend-defaults ``` ## Documentation diff --git a/packages/backend-dev-utils/README.md b/packages/backend-dev-utils/README.md index 744a9d2ca0..f3666ea33e 100644 --- a/packages/backend-dev-utils/README.md +++ b/packages/backend-dev-utils/README.md @@ -8,7 +8,7 @@ Add the library to your backend plugin or module package: ```bash # From your Backstage root directory -yarn add --cwd plugins/-backend @backstage/backend-dev-utils +yarn --cwd plugins/-backend add @backstage/backend-dev-utils ``` ## Documentation diff --git a/packages/backend-plugin-api/README.md b/packages/backend-plugin-api/README.md index b827ecc50c..1e5e4cf3b1 100644 --- a/packages/backend-plugin-api/README.md +++ b/packages/backend-plugin-api/README.md @@ -8,7 +8,7 @@ Add the library to your backend plugin or module package: ```bash # From your Backstage root directory -yarn add --cwd plugins/-backend @backstage/backend-plugin-api +yarn --cwd plugins/-backend add @backstage/backend-plugin-api ``` ## Documentation diff --git a/packages/backend-tasks/README.md b/packages/backend-tasks/README.md index afaca06e55..57cd63b0e0 100644 --- a/packages/backend-tasks/README.md +++ b/packages/backend-tasks/README.md @@ -8,7 +8,7 @@ Add the library to your backend package: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/backend-tasks +yarn --cwd packages/backend add @backstage/backend-tasks ``` then make use of its facilities as necessary: diff --git a/packages/core-app-api/README.md b/packages/core-app-api/README.md index ffdc0ef94a..94dd618625 100644 --- a/packages/core-app-api/README.md +++ b/packages/core-app-api/README.md @@ -7,7 +7,7 @@ This package provides the core API used by Backstage apps. Install the package via Yarn: ```bash -yarn add --cwd packages/app @backstage/core-app-api +yarn --cwd packages/app add @backstage/core-app-api ``` ## Documentation diff --git a/plugins/adr-backend/README.md b/plugins/adr-backend/README.md index 6ce2f13ebf..ebf99b4ee1 100644 --- a/plugins/adr-backend/README.md +++ b/plugins/adr-backend/README.md @@ -20,7 +20,7 @@ Here's how to get the backend up and running: ```sh # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-adr-backend +yarn --cwd packages/backend add @backstage/plugin-adr-backend ``` 2. Then we will create a new file named `packages/backend/src/plugins/adr.ts`, and add the diff --git a/plugins/airbrake/README.md b/plugins/airbrake/README.md index 573910a1cf..603311f92c 100644 --- a/plugins/airbrake/README.md +++ b/plugins/airbrake/README.md @@ -8,14 +8,14 @@ The Airbrake plugin provides connectivity between Backstage and Airbrake (https: ```bash # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-airbrake + yarn --cwd packages/app add @backstage/plugin-airbrake ``` 2. Install the Backend plugin: ```bash # From your Backstage root directory - yarn add --cwd packages/backend @backstage/plugin-airbrake-backend + yarn --cwd packages/backend add @backstage/plugin-airbrake-backend ``` 3. Add the `EntityAirbrakeContent` and `isAirbrakeAvailable` to `packages/app/src/components/catalog/EntityPage.tsx` for all the entity pages you want Airbrake to be in: diff --git a/plugins/allure/README.md b/plugins/allure/README.md index 3a1137fc39..22348385a3 100644 --- a/plugins/allure/README.md +++ b/plugins/allure/README.md @@ -6,7 +6,7 @@ Welcome to the Backstage Allure plugin. This plugin add an entity service page t ```shell # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-allure +yarn --cwd packages/app add @backstage/plugin-allure ``` ## Configure diff --git a/plugins/analytics-module-ga/README.md b/plugins/analytics-module-ga/README.md index 6fdfc91306..dfea532403 100644 --- a/plugins/analytics-module-ga/README.md +++ b/plugins/analytics-module-ga/README.md @@ -12,7 +12,7 @@ This plugin contains no other functionality. ```sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-analytics-module-ga +yarn --cwd packages/app add @backstage/plugin-analytics-module-ga ``` 2. Wire up the API implementation to your App: diff --git a/plugins/analytics-module-ga4/README.md b/plugins/analytics-module-ga4/README.md index 2d8c930e30..dad9b88a27 100644 --- a/plugins/analytics-module-ga4/README.md +++ b/plugins/analytics-module-ga4/README.md @@ -12,7 +12,7 @@ This plugin contains no other functionality. ```sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-analytics-module-ga4 +yarn --cwd packages/app add @backstage/plugin-analytics-module-ga4 ``` 2. Wire up the API implementation to your App: diff --git a/plugins/analytics-module-newrelic-browser/README.md b/plugins/analytics-module-newrelic-browser/README.md index 2fe3956d2d..d607a4f446 100644 --- a/plugins/analytics-module-newrelic-browser/README.md +++ b/plugins/analytics-module-newrelic-browser/README.md @@ -10,7 +10,7 @@ This plugin contains no other functionality. ```sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-analytics-module-newrelic-browser +yarn --cwd packages/app add @backstage/plugin-analytics-module-newrelic-browser ``` 2. Wire up the API implementation to your App: diff --git a/plugins/api-docs-module-protoc-gen-doc/README.md b/plugins/api-docs-module-protoc-gen-doc/README.md index a921fafc0b..5bf75766e0 100644 --- a/plugins/api-docs-module-protoc-gen-doc/README.md +++ b/plugins/api-docs-module-protoc-gen-doc/README.md @@ -8,7 +8,7 @@ This package contains ApiDefinitionWidgets for the following projects: ```sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-api-docs-module-protoc-gen-doc +yarn --cwd packages/app add @backstage/plugin-api-docs-module-protoc-gen-doc ``` ## Add the GrpcDocsApiWidget to your apis diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md index 46d9df4628..9b8eb1669e 100644 --- a/plugins/api-docs/README.md +++ b/plugins/api-docs/README.md @@ -29,7 +29,7 @@ To link that a component provides or consumes an API, see the [`providesApis`](h ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-api-docs +yarn --cwd packages/app add @backstage/plugin-api-docs ``` 2. Add the `ApiExplorerPage` extension to the app: diff --git a/plugins/app-backend/README.md b/plugins/app-backend/README.md index 590c6e8eb7..0f0c235ab5 100644 --- a/plugins/app-backend/README.md +++ b/plugins/app-backend/README.md @@ -8,7 +8,7 @@ Add both this package and your local frontend app package as dependencies to you ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-app-backend app +yarn --cwd packages/backend add @backstage/plugin-app-backend app ``` By adding the app package as a dependency we ensure that it is built as part of the backend, and that it can be resolved at runtime. diff --git a/plugins/azure-devops-backend/README.md b/plugins/azure-devops-backend/README.md index 0066e43856..f69951f70d 100644 --- a/plugins/azure-devops-backend/README.md +++ b/plugins/azure-devops-backend/README.md @@ -37,7 +37,7 @@ Here's how to get the backend up and running: ```sh # From your Backstage root directory - yarn add --cwd packages/backend @backstage/plugin-azure-devops-backend + yarn --cwd packages/backend add @backstage/plugin-azure-devops-backend ``` 2. Then we will create a new file named `packages/backend/src/plugins/azure-devops.ts`, and add the diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index 7119cb0980..f897588987 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -146,7 +146,7 @@ To get the Azure Pipelines component working you'll need to do the following two ```bash # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-azure-devops + yarn --cwd packages/app add @backstage/plugin-azure-devops ``` 2. Second we need to add the `EntityAzurePipelinesContent` extension to the entity page in your app. How to do this will depend on which annotation you are using in your entities: @@ -204,7 +204,7 @@ To get the Azure Repos component working you'll need to do the following two ste ```bash # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-azure-devops + yarn --cwd packages/app add @backstage/plugin-azure-devops ``` 2. Second we need to add the `EntityAzurePullRequestsContent` extension to the entity page in your app: @@ -241,7 +241,7 @@ To get the Git Tags component working you'll need to do the following two steps: ```bash # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-azure-devops + yarn --cwd packages/app add @backstage/plugin-azure-devops ``` 2. Second we need to add the `EntityAzureGitTagsContent` extension to the entity page in your app: @@ -277,7 +277,7 @@ To get the README component working you'll need to do the following two steps: ```bash # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-azure-devops + yarn --cwd packages/app add @backstage/plugin-azure-devops ``` 2. Second we need to add the `EntityAzureReadmeCard` extension to the entity page in your app: diff --git a/plugins/azure-sites-backend/README.md b/plugins/azure-sites-backend/README.md index 5017be8a8f..892cbe4024 100644 --- a/plugins/azure-sites-backend/README.md +++ b/plugins/azure-sites-backend/README.md @@ -37,7 +37,7 @@ Here's how to get the backend plugin up and running: ```sh # From the Backstage root directory - yarn add --cwd packages/backend @backstage/plugin-azure-sites-backend + yarn --cwd packages/backend add @backstage/plugin-azure-sites-backend ``` 2. Then we will create a new file named `packages/backend/src/plugins/azure-sites.ts`, and add the following to it: diff --git a/plugins/azure-sites/README.md b/plugins/azure-sites/README.md index a19b314b77..540daeb7df 100644 --- a/plugins/azure-sites/README.md +++ b/plugins/azure-sites/README.md @@ -48,7 +48,7 @@ azure.com/microsoft-web-sites: func-testapp ```sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-azure-sites +yarn --cwd packages/app add @backstage/plugin-azure-sites ``` 2. Add widget component to your Backstage instance: diff --git a/plugins/badges-backend/README.md b/plugins/badges-backend/README.md index 22c3311ff1..1737482080 100644 --- a/plugins/badges-backend/README.md +++ b/plugins/badges-backend/README.md @@ -15,7 +15,7 @@ Install the `@backstage/plugin-badges-backend` package in your backend package: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-badges-backend +yarn --cwd packages/backend add @backstage/plugin-badges-backend ``` Add the plugin using the following default setup for diff --git a/plugins/badges/README.md b/plugins/badges/README.md index 4b8ff38d47..06c0e3e413 100644 --- a/plugins/badges/README.md +++ b/plugins/badges/README.md @@ -80,7 +80,7 @@ Install the `@backstage/plugin-badges` package in your frontend app package: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-badges +yarn --cwd packages/app add @backstage/plugin-badges ``` ### Register plugin diff --git a/plugins/bazaar-backend/README.md b/plugins/bazaar-backend/README.md index 266841b26b..eec1574b7a 100644 --- a/plugins/bazaar-backend/README.md +++ b/plugins/bazaar-backend/README.md @@ -8,7 +8,7 @@ Welcome to the Bazaar backend plugin! ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-bazaar-backend +yarn --cwd packages/backend add @backstage/plugin-bazaar-backend ``` ## Adding the plugin to your `packages/backend` diff --git a/plugins/bazaar/README.md b/plugins/bazaar/README.md index 941979d4d3..c340baec3e 100644 --- a/plugins/bazaar/README.md +++ b/plugins/bazaar/README.md @@ -22,7 +22,7 @@ First install the plugin into your app: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-bazaar +yarn --cwd packages/app add @backstage/plugin-bazaar ``` Modify your app routes in `packages/app/src/App.tsx` to include the `Bazaar` component exported from the plugin, for example: diff --git a/plugins/bitrise/README.md b/plugins/bitrise/README.md index e5d5c2122c..80c3dadac9 100644 --- a/plugins/bitrise/README.md +++ b/plugins/bitrise/README.md @@ -9,7 +9,7 @@ Welcome to the Bitrise plugin! ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-bitrise +yarn --cwd packages/app add @backstage/plugin-bitrise ``` Bitrise Plugin exposes an entity tab component named `EntityBitriseContent`. You can include it in the diff --git a/plugins/catalog-backend-module-incremental-ingestion/README.md b/plugins/catalog-backend-module-incremental-ingestion/README.md index c5ed3e6367..0894d32031 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/README.md +++ b/plugins/catalog-backend-module-incremental-ingestion/README.md @@ -41,7 +41,7 @@ The Incremental Entity Provider backend is designed for data sources that provid ## Installation -1. Install `@backstage/plugin-catalog-backend-module-incremental-ingestion` with `yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-incremental-ingestion` from the Backstage root directory. +1. Install `@backstage/plugin-catalog-backend-module-incremental-ingestion` with `yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-incremental-ingestion` from the Backstage root directory. 2. In your catalog.ts, import `IncrementalCatalogBuilder` from `@backstage/plugin-catalog-backend-module-incremental-ingestion` and instantiate it with `await IncrementalCatalogBuilder.create(env, builder)`. You have to pass `builder` into `IncrementalCatalogBuilder.create` function because `IncrementalCatalogBuilder` will convert an `IncrementalEntityProvider` into an `EntityProvider` and call `builder.addEntityProvider`. ```ts diff --git a/plugins/catalog-backend-module-msgraph/README.md b/plugins/catalog-backend-module-msgraph/README.md index b70134cba3..e3ed302fa9 100644 --- a/plugins/catalog-backend-module-msgraph/README.md +++ b/plugins/catalog-backend-module-msgraph/README.md @@ -104,7 +104,7 @@ By default, all users are loaded. If you want to filter users based on their att ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-msgraph +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-msgraph ``` 4. The `MicrosoftGraphOrgEntityProvider` is not registered by default, so you diff --git a/plugins/catalog-backend-module-openapi/README.md b/plugins/catalog-backend-module-openapi/README.md index 6a8028895d..a79416e901 100644 --- a/plugins/catalog-backend-module-openapi/README.md +++ b/plugins/catalog-backend-module-openapi/README.md @@ -12,7 +12,7 @@ This is useful for OpenAPI and AsyncAPI specifications. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-openapi +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-openapi ``` ### Adding the plugin to your `packages/backend` diff --git a/plugins/catalog-backend-module-puppetdb/README.md b/plugins/catalog-backend-module-puppetdb/README.md index 9319299a06..a3291693e8 100644 --- a/plugins/catalog-backend-module-puppetdb/README.md +++ b/plugins/catalog-backend-module-puppetdb/README.md @@ -12,7 +12,7 @@ to your backend package: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-puppetdb +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-puppetdb ``` Update the catalog plugin initialization in your backend to add the provider and schedule it: diff --git a/plugins/catalog-backend-module-unprocessed/README.md b/plugins/catalog-backend-module-unprocessed/README.md index f2ea7b7741..0d9f9f8cb9 100644 --- a/plugins/catalog-backend-module-unprocessed/README.md +++ b/plugins/catalog-backend-module-unprocessed/README.md @@ -11,7 +11,7 @@ A `pending` entity has not been processed yet. ## Installation ```shell -yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-unprocessed +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-unprocessed ``` ### backend diff --git a/plugins/catalog-backend/README.md b/plugins/catalog-backend/README.md index cd921499d3..7f886de155 100644 --- a/plugins/catalog-backend/README.md +++ b/plugins/catalog-backend/README.md @@ -27,7 +27,7 @@ restoring the plugin, if you previously removed it. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-catalog-backend +yarn --cwd packages/backend add @backstage/plugin-catalog-backend ``` ### Adding the plugin to your `packages/backend` diff --git a/plugins/catalog-graph/README.md b/plugins/catalog-graph/README.md index f00d11810f..1b972082d5 100644 --- a/plugins/catalog-graph/README.md +++ b/plugins/catalog-graph/README.md @@ -27,7 +27,7 @@ To use the catalog graph plugin, you have to add some things to your Backstage a 1. Add a dependency to your `packages/app/package.json`: ```sh # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-catalog-graph + yarn --cwd packages/app add @backstage/plugin-catalog-graph ``` 2. Add the `CatalogGraphPage` to your `packages/app/src/App.tsx`: diff --git a/plugins/catalog-import/README.md b/plugins/catalog-import/README.md index 602e67503d..d4ede8af7b 100644 --- a/plugins/catalog-import/README.md +++ b/plugins/catalog-import/README.md @@ -19,7 +19,7 @@ Some features are not yet available for all supported Git providers. ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-catalog-import +yarn --cwd packages/app add @backstage/plugin-catalog-import ``` 2. Add the `CatalogImportPage` extension to the app: diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index 3c9f649e37..c2e5109d5c 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -29,7 +29,7 @@ Requires the `@backstage/plugin-catalog-backend-module-unprocessed` module to be ## Installation ```shell -yarn add --cwd packages/app @backstage/plugin-catalog-unprocessed-entities +yarn --cwd packages/app add @backstage/plugin-catalog-unprocessed-entities ``` Import into your `App.tsx` and include into the `` component: diff --git a/plugins/catalog/README.md b/plugins/catalog/README.md index b35110053e..1ce7b2bf69 100644 --- a/plugins/catalog/README.md +++ b/plugins/catalog/README.md @@ -20,7 +20,7 @@ plugin, if you previously removed it. ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-catalog +yarn --cwd packages/app add @backstage/plugin-catalog ``` ### Add the plugin to your `packages/app` diff --git a/plugins/circleci/README.md b/plugins/circleci/README.md index 86af7081c7..67e342e8c8 100644 --- a/plugins/circleci/README.md +++ b/plugins/circleci/README.md @@ -15,7 +15,7 @@ ```bash # From your Backstage root directory -yarn add --cwd packages/app @circleci/backstage-plugin +yarn --cwd packages/app add @circleci/backstage-plugin ``` 2. Add the `EntityCircleCIContent` extension to the entity page in your app: diff --git a/plugins/code-climate/README.md b/plugins/code-climate/README.md index 4140f8f1aa..8aff5cd9af 100644 --- a/plugins/code-climate/README.md +++ b/plugins/code-climate/README.md @@ -10,7 +10,7 @@ The Code Climate Plugin displays a few stats from the quality section from [Code ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-code-climate +yarn --cwd packages/app add @backstage/plugin-code-climate ``` 2. Add the `EntityCodeClimateCard` to the EntityPage: diff --git a/plugins/code-coverage-backend/README.md b/plugins/code-coverage-backend/README.md index 2b22b0ad1d..f47e966466 100644 --- a/plugins/code-coverage-backend/README.md +++ b/plugins/code-coverage-backend/README.md @@ -6,7 +6,7 @@ This is the backend part of the `code-coverage` plugin. It takes care of process ```sh # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-code-coverage-backend +yarn --cwd packages/backend add @backstage/plugin-code-coverage-backend ``` First create a `codecoverage.ts` file here: `packages/backend/src/plugins`. Now add the following as its content: diff --git a/plugins/code-coverage/README.md b/plugins/code-coverage/README.md index d4daa6ec00..4298c69b11 100644 --- a/plugins/code-coverage/README.md +++ b/plugins/code-coverage/README.md @@ -6,7 +6,7 @@ This is the frontend part of the code-coverage plugin. It displays code coverage ```sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-code-coverage +yarn --cwd packages/app add @backstage/plugin-code-coverage ``` Finally you need to import and render the code coverage entity, in `packages/app/src/components/catalog/EntityPage.tsx` add the following: diff --git a/plugins/codescene/README.md b/plugins/codescene/README.md index 8c84c9015c..1a713c1589 100644 --- a/plugins/codescene/README.md +++ b/plugins/codescene/README.md @@ -12,7 +12,7 @@ The CodeScene Backstage Plugin provides a page component that displays a list of ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-codescene +yarn --cwd packages/app add @backstage/plugin-codescene ``` 2. Add the routes and pages to your `App.tsx`: diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md index a7bcb0cb0a..b7bfa053cf 100644 --- a/plugins/cost-insights/README.md +++ b/plugins/cost-insights/README.md @@ -17,7 +17,7 @@ Learn more with the Backstage blog post [New Cost Insights plugin: The engineer' ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-cost-insights +yarn --cwd packages/app add @backstage/plugin-cost-insights ``` ## Setup @@ -224,8 +224,8 @@ costInsights: ### Engineer Threshold (Optional; default 0.5) -This threshold determines whether to show 'Negligible', or a percentage with a fraction of 'engineers' for cost savings or cost excess on top of the charts. -A threshold of 0.5 means that `Negligible` is shown when the difference in costs is lower than that fraction of engineers in that time frame, +This threshold determines whether to show 'Negligible', or a percentage with a fraction of 'engineers' for cost savings or cost excess on top of the charts. +A threshold of 0.5 means that `Negligible` is shown when the difference in costs is lower than that fraction of engineers in that time frame, and show `XX% or ~N engineers` when it's above the threshold. ```yaml diff --git a/plugins/cost-insights/contrib/aws-cost-explorer-api.md b/plugins/cost-insights/contrib/aws-cost-explorer-api.md index 4fbf8a0cbe..abe6d10120 100644 --- a/plugins/cost-insights/contrib/aws-cost-explorer-api.md +++ b/plugins/cost-insights/contrib/aws-cost-explorer-api.md @@ -34,7 +34,7 @@ Install the AWS Cost Explorer SDK. The AWS docs recommend using the SDK over mak ```bash # From your Backstage root directory -yarn add --cwd packages/app @aws-sdk/client-cost-explorer +yarn --cwd packages/app add @aws-sdk/client-cost-explorer ``` ## Usage of the SDK diff --git a/plugins/devtools-backend/README.md b/plugins/devtools-backend/README.md index 528e864783..c9226a92ab 100644 --- a/plugins/devtools-backend/README.md +++ b/plugins/devtools-backend/README.md @@ -10,7 +10,7 @@ Here's how to get the DevTools Backend up and running: ```sh # From the Backstage root directory - yarn add --cwd packages/backend @backstage/plugin-devtools-backend + yarn --cwd packages/backend add @backstage/plugin-devtools-backend ``` 2. Then we will create a new file named `packages/backend/src/plugins/devtools.ts`, and add the diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index cb6fbb95fd..f6db8d1417 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -14,7 +14,7 @@ Lists helpful information about your current running Backstage instance such as: #### Backstage Version Reporting -The Backstage Version that is reported requires `backstage.json` to be present at the root of the running backstage instance. +The Backstage Version that is reported requires `backstage.json` to be present at the root of the running backstage instance. You may need to modify your Dockerfile to ensure `backstage.json` is copied into the `WORKDIR` of your image. ```sh @@ -66,7 +66,7 @@ To setup the DevTools frontend you'll need to do the following steps: ```sh # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-devtools + yarn --cwd packages/app add @backstage/plugin-devtools ``` 2. Now open the `packages/app/src/App.tsx` file @@ -206,7 +206,7 @@ To use the permission framework to secure the DevTools sidebar option you'll wan ```sh # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-devtools-common + yarn --cwd packages/app add @backstage/plugin-devtools-common ``` 2. Then open the `packages/app/src/components/Root/Root.tsx` file @@ -236,7 +236,7 @@ To use the permission framework to secure the DevTools route you'll want to do t ```sh # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-devtools-common + yarn --cwd packages/app add @backstage/plugin-devtools-common ``` 2. Then open the `packages/app/src/App.tsx` file @@ -341,7 +341,7 @@ To use this policy you'll need to make sure to add the `@backstage/plugin-devtoo ```sh # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-devtools-common +yarn --cwd packages/backend add @backstage/plugin-devtools-common ``` You'll also need to add these imports: diff --git a/plugins/dynatrace/README.md b/plugins/dynatrace/README.md index 9143b0ff9c..5e2394cee3 100644 --- a/plugins/dynatrace/README.md +++ b/plugins/dynatrace/README.md @@ -28,7 +28,7 @@ The Dynatrace plugin will require the following information, to be used in the c ``` # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-dynatrace +yarn --cwd packages/app add @backstage/plugin-dynatrace ``` 2. We created in our catalog the interface for using the integration with Dynatrace. diff --git a/plugins/entity-feedback-backend/README.md b/plugins/entity-feedback-backend/README.md index 49a8ce20e0..a3aae695e2 100644 --- a/plugins/entity-feedback-backend/README.md +++ b/plugins/entity-feedback-backend/README.md @@ -12,7 +12,7 @@ out of the box, this plugin will not work when you test it. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-entity-feedback-backend +yarn --cwd packages/backend add @backstage/plugin-entity-feedback-backend ``` ### Adding the plugin to your `packages/backend` diff --git a/plugins/entity-validation/README.md b/plugins/entity-validation/README.md index 7816262836..f831007a45 100644 --- a/plugins/entity-validation/README.md +++ b/plugins/entity-validation/README.md @@ -10,7 +10,7 @@ First of all, install the package in the `app` package by running the following ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-entity-validation +yarn --cwd packages/app add @backstage/plugin-entity-validation ``` Add the new route to the app by adding the following line: diff --git a/plugins/events-backend-module-aws-sqs/README.md b/plugins/events-backend-module-aws-sqs/README.md index 397de35c10..7ca4bb280d 100644 --- a/plugins/events-backend-module-aws-sqs/README.md +++ b/plugins/events-backend-module-aws-sqs/README.md @@ -38,7 +38,7 @@ events: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-events-backend-module-aws-sqs +yarn --cwd packages/backend add @backstage/plugin-events-backend-module-aws-sqs ``` ```ts title="packages/backend/src/index.ts" diff --git a/plugins/events-backend-module-azure/README.md b/plugins/events-backend-module-azure/README.md index 457fd81fbc..61b3b63175 100644 --- a/plugins/events-backend-module-azure/README.md +++ b/plugins/events-backend-module-azure/README.md @@ -28,7 +28,7 @@ Install this module: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-events-backend-module-azure +yarn --cwd packages/backend add @backstage/plugin-events-backend-module-azure ``` ### Add to backend diff --git a/plugins/events-backend-module-bitbucket-cloud/README.md b/plugins/events-backend-module-bitbucket-cloud/README.md index d70a70c87e..0a40ab2eea 100644 --- a/plugins/events-backend-module-bitbucket-cloud/README.md +++ b/plugins/events-backend-module-bitbucket-cloud/README.md @@ -28,7 +28,7 @@ Install this module: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-events-backend-module-bitbucket-cloud +yarn --cwd packages/backend add @backstage/plugin-events-backend-module-bitbucket-cloud ``` ### Add to backend diff --git a/plugins/events-backend-module-gerrit/README.md b/plugins/events-backend-module-gerrit/README.md index 34b2d154be..b658fba366 100644 --- a/plugins/events-backend-module-gerrit/README.md +++ b/plugins/events-backend-module-gerrit/README.md @@ -27,7 +27,7 @@ Install this module: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-events-backend-module-gerrit +yarn --cwd packages/backend add @backstage/plugin-events-backend-module-gerrit ``` ### Add to backend diff --git a/plugins/events-backend-module-github/README.md b/plugins/events-backend-module-github/README.md index 7731d10d24..072877ee86 100644 --- a/plugins/events-backend-module-github/README.md +++ b/plugins/events-backend-module-github/README.md @@ -28,7 +28,7 @@ Install this module: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-events-backend-module-github +yarn --cwd packages/backend add @backstage/plugin-events-backend-module-github ``` Add the event router to the `EventsBackend` instance in `packages/backend/src/plugins/events.ts`: diff --git a/plugins/events-backend-module-gitlab/README.md b/plugins/events-backend-module-gitlab/README.md index 13e30659a6..3d4919302f 100644 --- a/plugins/events-backend-module-gitlab/README.md +++ b/plugins/events-backend-module-gitlab/README.md @@ -27,7 +27,7 @@ Install this module: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-events-backend-module-gitlab +yarn --cwd packages/backend add @backstage/plugin-events-backend-module-gitlab ``` Add the event router to the `EventsBackend` instance in `packages/backend/src/plugins/events.ts`: diff --git a/plugins/events-backend/README.md b/plugins/events-backend/README.md index 2fd980f003..b8470101aa 100644 --- a/plugins/events-backend/README.md +++ b/plugins/events-backend/README.md @@ -21,7 +21,7 @@ to the used event broker. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-events-backend @backstage/plugin-events-node +yarn --cwd packages/backend add @backstage/plugin-events-backend @backstage/plugin-events-node ``` ### Add to backend diff --git a/plugins/explore-backend/README.md b/plugins/explore-backend/README.md index aae3ff94b8..6767749eab 100644 --- a/plugins/explore-backend/README.md +++ b/plugins/explore-backend/README.md @@ -13,7 +13,7 @@ Install dependencies ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-explore-backend +yarn --cwd packages/backend add @backstage/plugin-explore-backend ``` Add feature @@ -45,7 +45,7 @@ Install dependencies ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-explore-backend +yarn --cwd packages/backend add @backstage/plugin-explore-backend ``` You'll need to add the plugin to the router in your `backend` package. You can @@ -90,7 +90,7 @@ Install dependencies ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-explore-backend @backstage/plugin-explore-common +yarn --cwd packages/backend add @backstage/plugin-explore-backend @backstage/plugin-explore-common ``` You'll need to add the plugin to the router in your `backend` package. You can diff --git a/plugins/firehydrant/README.md b/plugins/firehydrant/README.md index 4472536245..aa56bd066a 100644 --- a/plugins/firehydrant/README.md +++ b/plugins/firehydrant/README.md @@ -18,7 +18,7 @@ The [FireHydrant](https://firehydrant.io) plugin brings incident management to B ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-firehydrant +yarn --cwd packages/app add @backstage/plugin-firehydrant ``` 2. Add the plugin to `EntityPage.tsx`, inside the `const overviewContent`'s parent `` component: diff --git a/plugins/fossa/README.md b/plugins/fossa/README.md index 10ccba5ba7..2c69a9b309 100644 --- a/plugins/fossa/README.md +++ b/plugins/fossa/README.md @@ -10,7 +10,7 @@ The FOSSA Plugin displays code statistics from [FOSSA](https://fossa.com/). ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-fossa +yarn --cwd packages/app add @backstage/plugin-fossa ``` 2. Add the `EntityFossaCard` to the EntityPage: diff --git a/plugins/github-actions/README.md b/plugins/github-actions/README.md index d0b5ceb54d..4be62346a5 100644 --- a/plugins/github-actions/README.md +++ b/plugins/github-actions/README.md @@ -39,7 +39,7 @@ TBD ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-github-actions +yarn --cwd packages/app add @backstage/plugin-github-actions ``` 2. Add to the app `EntityPage` component: diff --git a/plugins/github-deployments/README.md b/plugins/github-deployments/README.md index 5cc6ccdeaa..111e028eed 100644 --- a/plugins/github-deployments/README.md +++ b/plugins/github-deployments/README.md @@ -14,7 +14,7 @@ The GitHub Deployments Plugin displays recent deployments from GitHub. ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-github-deployments +yarn --cwd packages/app add @backstage/plugin-github-deployments ``` 2. Add the `EntityGithubDeploymentsCard` to the EntityPage: diff --git a/plugins/graphiql/README.md b/plugins/graphiql/README.md index 452dfa807d..ed47b96967 100644 --- a/plugins/graphiql/README.md +++ b/plugins/graphiql/README.md @@ -13,7 +13,7 @@ Start out by installing the plugin in your Backstage app: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-graphiql +yarn --cwd packages/app add @backstage/plugin-graphiql ``` ```diff diff --git a/plugins/graphql-voyager/README.md b/plugins/graphql-voyager/README.md index 66ea63191b..d37ffdecca 100644 --- a/plugins/graphql-voyager/README.md +++ b/plugins/graphql-voyager/README.md @@ -13,7 +13,7 @@ To get started, first install the plugin with the following command: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-graphql-voyager +yarn --cwd packages/app add @backstage/plugin-graphql-voyager ``` ### Adding the page diff --git a/plugins/home/README.md b/plugins/home/README.md index 066e637725..35f9cb0738 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -10,7 +10,7 @@ If you have a standalone app (you didn't clone this repo), then do ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-home +yarn --cwd packages/app add @backstage/plugin-home ``` ### Setting up the Home Page diff --git a/plugins/ilert/README.md b/plugins/ilert/README.md index 814baea507..434cf8c0f0 100644 --- a/plugins/ilert/README.md +++ b/plugins/ilert/README.md @@ -27,7 +27,7 @@ Install the plugin: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-ilert +yarn --cwd packages/app add @backstage/plugin-ilert ``` Add it to the `EntityPage.tsx`: diff --git a/plugins/jenkins-backend/README.md b/plugins/jenkins-backend/README.md index d6f8fa5472..4cca86f86d 100644 --- a/plugins/jenkins-backend/README.md +++ b/plugins/jenkins-backend/README.md @@ -28,7 +28,7 @@ This plugin needs to be added to an existing backstage instance. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-jenkins-backend +yarn --cwd packages/backend add @backstage/plugin-jenkins-backend ``` Typically, this means creating a `src/plugins/jenkins.ts` file and adding a reference to it to `src/index.ts` diff --git a/plugins/jenkins/README.md b/plugins/jenkins/README.md index b62eaab514..bdbe0ac0d9 100644 --- a/plugins/jenkins/README.md +++ b/plugins/jenkins/README.md @@ -14,7 +14,7 @@ Website: [https://jenkins.io/](https://jenkins.io/) ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-jenkins +yarn --cwd packages/app add @backstage/plugin-jenkins ``` 2. Add and configure the [jenkins-backend](../jenkins-backend) plugin according to it's instructions diff --git a/plugins/lighthouse-backend/README.md b/plugins/lighthouse-backend/README.md index 687467f1d5..06455d90b4 100644 --- a/plugins/lighthouse-backend/README.md +++ b/plugins/lighthouse-backend/README.md @@ -8,7 +8,7 @@ Lighthouse Backend allows you to run scheduled lighthouse Tests for each Website ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-lighthouse-backend +yarn --cwd packages/backend add @backstage/plugin-lighthouse-backend ``` 2. Create a `lighthouse.ts` file inside `packages/backend/src/plugins/`: diff --git a/plugins/lighthouse/README.md b/plugins/lighthouse/README.md index e834ad39c1..ee0e1dfd1d 100644 --- a/plugins/lighthouse/README.md +++ b/plugins/lighthouse/README.md @@ -29,7 +29,7 @@ When you have an instance running that Backstage can hook into, first install th ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-lighthouse +yarn --cwd packages/app add @backstage/plugin-lighthouse ``` Modify your app routes in `App.tsx` to include the `LighthousePage` component exported from the plugin, for example: diff --git a/plugins/lighthouse/src/components/Intro/index.tsx b/plugins/lighthouse/src/components/Intro/index.tsx index 8701c473aa..4c102b7587 100644 --- a/plugins/lighthouse/src/components/Intro/index.tsx +++ b/plugins/lighthouse/src/components/Intro/index.tsx @@ -52,7 +52,7 @@ When you have an instance running that Backstage can hook into, first install th \`\`\`sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-lighthouse +yarn --cwd packages/app add @backstage/plugin-lighthouse \`\`\` Modify your app routes in \`App.tsx\` to include the \`LighthousePage\` component exported from the plugin, for example: diff --git a/plugins/linguist-backend/README.md b/plugins/linguist-backend/README.md index d2ed7f5f03..3bc441c238 100644 --- a/plugins/linguist-backend/README.md +++ b/plugins/linguist-backend/README.md @@ -14,7 +14,7 @@ Here's how to get the backend up and running: ```sh # From the Backstage root directory - yarn add --cwd packages/backend @backstage/plugin-linguist-backend + yarn --cwd packages/backend add @backstage/plugin-linguist-backend ``` 2. Then we will create a new file named `packages/backend/src/plugins/linguist.ts`, and add the diff --git a/plugins/linguist/README.md b/plugins/linguist/README.md index e7cad1c8e0..7ad70f1db6 100644 --- a/plugins/linguist/README.md +++ b/plugins/linguist/README.md @@ -55,7 +55,7 @@ To setup the Linguist Card frontend you'll need to do the following steps: ```sh # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-linguist + yarn --cwd packages/app add @backstage/plugin-linguist ``` 2. Second we need to add the `EntityLinguistCard` extension to the entity page in your app: diff --git a/plugins/microsoft-calendar/README.md b/plugins/microsoft-calendar/README.md index 3dcf58c418..f5ba17bde3 100644 --- a/plugins/microsoft-calendar/README.md +++ b/plugins/microsoft-calendar/README.md @@ -25,7 +25,7 @@ The following sections will help you set up the Microsoft calendar plugin. ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-microsoft-calendar +yarn --cwd packages/app add @backstage/plugin-microsoft-calendar ``` 2. Import the Microsoft calendar React component from `@backstage/plugin-microsoft-calendar`. diff --git a/plugins/newrelic/README.md b/plugins/newrelic/README.md index 14049ba39c..14e2c0005d 100644 --- a/plugins/newrelic/README.md +++ b/plugins/newrelic/README.md @@ -48,7 +48,7 @@ APIs. 2. Add a dependency to your `packages/app/package.json`: ```sh # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-newrelic + yarn --cwd packages/app add @backstage/plugin-newrelic ``` 3. Add the `NewRelicPage` to your `packages/app/src/App.tsx`: diff --git a/plugins/nomad-backend/README.md b/plugins/nomad-backend/README.md index 231f1ddccb..9f530bd943 100644 --- a/plugins/nomad-backend/README.md +++ b/plugins/nomad-backend/README.md @@ -22,7 +22,7 @@ In your `packages/backend/src/index.ts` make the following changes: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-nomad-backend +yarn --cwd packages/backend add @backstage/plugin-nomad-backend ``` 2. Create a `nomad.ts` file inside `packages/backend/src/plugins/`: diff --git a/plugins/nomad/README.md b/plugins/nomad/README.md index da14a0a3f3..443669b71a 100644 --- a/plugins/nomad/README.md +++ b/plugins/nomad/README.md @@ -31,7 +31,7 @@ If your Nomad cluster has ACLs enabled, you will need a `token` with at least th ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-nomad +yarn --cwd packages/app add @backstage/plugin-nomad ``` ### Configuration diff --git a/plugins/octopus-deploy/README.md b/plugins/octopus-deploy/README.md index 3699b76678..ee7d9d8543 100644 --- a/plugins/octopus-deploy/README.md +++ b/plugins/octopus-deploy/README.md @@ -14,7 +14,7 @@ To get started, first install the plugin with the following command: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-octopus-deploy +yarn --cwd packages/app add @backstage/plugin-octopus-deploy ``` ### Setup diff --git a/plugins/opencost/README.md b/plugins/opencost/README.md index 05c63206a2..1dec15dcc1 100644 --- a/plugins/opencost/README.md +++ b/plugins/opencost/README.md @@ -11,7 +11,7 @@ All of the code was originally ported from https://github.com/opencost/opencost/ 1. Add the OpenCost dependency to the `packages/app/package.json`: ```sh # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-opencost + yarn --cwd packages/app add @backstage/plugin-opencost ``` 2. Add the `OpenCostPage` to your `packages/app/src/App.tsx`: diff --git a/plugins/periskop/README.md b/plugins/periskop/README.md index 192948402a..85bced7c1b 100644 --- a/plugins/periskop/README.md +++ b/plugins/periskop/README.md @@ -19,7 +19,7 @@ Each of the entries in the table will direct you to the error details in your de ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-periskop +yarn --cwd packages/app add @backstage/plugin-periskop ``` 3. Add to the app `EntityPage` component: @@ -53,7 +53,7 @@ annotations: ### Instances -The periskop plugin can be configured to fetch aggregated errors from multiple deployment instances. +The periskop plugin can be configured to fetch aggregated errors from multiple deployment instances. This is especially useful if you have a multi-zone deployment, or a federated setup and would like to drill deeper into a single instance of the federation. Each of the configured instances will be included in the plugin's UI via a dropdown on the errors table. The plugin requires to configure _at least one_ Periskop API location in the [app-config.yaml](https://github.com/backstage/backstage/blob/master/app-config.yaml): diff --git a/plugins/playlist-backend/README.md b/plugins/playlist-backend/README.md index b5c9b8797b..d4474e2331 100644 --- a/plugins/playlist-backend/README.md +++ b/plugins/playlist-backend/README.md @@ -8,7 +8,7 @@ Welcome to the playlist backend plugin! ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-playlist-backend +yarn --cwd packages/backend add @backstage/plugin-playlist-backend ``` ### Adding the plugin to your `packages/backend` diff --git a/plugins/rollbar-backend/README.md b/plugins/rollbar-backend/README.md index d28ee60181..1f5977f5df 100644 --- a/plugins/rollbar-backend/README.md +++ b/plugins/rollbar-backend/README.md @@ -8,7 +8,7 @@ Simple plugin that proxies requests to the [Rollbar](https://rollbar.com) API. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-rollbar-backend +yarn --cwd packages/backend add @backstage/plugin-rollbar-backend ``` 2. Create a `rollbar.ts` file inside `packages/backend/src/plugins/`: diff --git a/plugins/rollbar/README.md b/plugins/rollbar/README.md index 99a3deb763..2b1fb7e40d 100644 --- a/plugins/rollbar/README.md +++ b/plugins/rollbar/README.md @@ -10,7 +10,7 @@ Website: [https://rollbar.com/](https://rollbar.com/) ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-rollbar +yarn --cwd packages/app add @backstage/plugin-rollbar ``` 3. Add to the app `EntityPage` component: diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/README.md b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md index 87f6acc6fe..1e64004041 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/README.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md @@ -12,7 +12,7 @@ From your Backstage root directory run: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-confluence-to-markdown ``` Then configure the action: diff --git a/plugins/scaffolder-backend-module-cookiecutter/README.md b/plugins/scaffolder-backend-module-cookiecutter/README.md index 0e8e11ef7d..62c6eede97 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/README.md +++ b/plugins/scaffolder-backend-module-cookiecutter/README.md @@ -10,7 +10,7 @@ You need to configure the action in your backend: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-cookiecutter +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-cookiecutter ``` Configure the action: diff --git a/plugins/scaffolder-backend-module-gitlab/README.md b/plugins/scaffolder-backend-module-gitlab/README.md index c946c8b476..bb31769cef 100644 --- a/plugins/scaffolder-backend-module-gitlab/README.md +++ b/plugins/scaffolder-backend-module-gitlab/README.md @@ -10,7 +10,7 @@ Here you can find all Gitlab related features to improve your scaffolder: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-gitlab +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-gitlab ``` Configure the action: diff --git a/plugins/scaffolder-backend-module-rails/README.md b/plugins/scaffolder-backend-module-rails/README.md index f4ec086775..a7afd1429e 100644 --- a/plugins/scaffolder-backend-module-rails/README.md +++ b/plugins/scaffolder-backend-module-rails/README.md @@ -15,7 +15,7 @@ You need to configure the action in your backend: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-rails +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-rails ``` Configure the action (you can check diff --git a/plugins/scaffolder-backend-module-sentry/README.md b/plugins/scaffolder-backend-module-sentry/README.md index 14751185c4..5cb1f1b810 100644 --- a/plugins/scaffolder-backend-module-sentry/README.md +++ b/plugins/scaffolder-backend-module-sentry/README.md @@ -12,7 +12,7 @@ You need to configure the action in your backend: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-sentry +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-sentry ``` Configure the action (you can check diff --git a/plugins/scaffolder-backend-module-yeoman/README.md b/plugins/scaffolder-backend-module-yeoman/README.md index b5c7b4d026..05bbf6064b 100644 --- a/plugins/scaffolder-backend-module-yeoman/README.md +++ b/plugins/scaffolder-backend-module-yeoman/README.md @@ -10,7 +10,7 @@ You need to configure the action in your backend: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-yeoman +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-yeoman ``` Configure the action: diff --git a/plugins/scaffolder-backend/README.md b/plugins/scaffolder-backend/README.md index ba9183c5cc..0aa57fd90c 100644 --- a/plugins/scaffolder-backend/README.md +++ b/plugins/scaffolder-backend/README.md @@ -21,7 +21,7 @@ restoring the plugin, if you previously removed it. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend +yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend ``` ### Adding the plugin to your `packages/backend` diff --git a/plugins/scaffolder/README.md b/plugins/scaffolder/README.md index 80d59160c4..a6af441d62 100644 --- a/plugins/scaffolder/README.md +++ b/plugins/scaffolder/README.md @@ -20,7 +20,7 @@ the plugin, if you previously removed it. ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-scaffolder +yarn --cwd packages/app add @backstage/plugin-scaffolder ``` ### Add the plugin to your `packages/app` diff --git a/plugins/search-backend-module-catalog/README.md b/plugins/search-backend-module-catalog/README.md index a2825c17cd..cbabe13d6d 100644 --- a/plugins/search-backend-module-catalog/README.md +++ b/plugins/search-backend-module-catalog/README.md @@ -8,7 +8,7 @@ Add the module package as a dependency: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-search-backend-module-catalog +yarn --cwd packages/backend add @backstage/plugin-search-backend-module-catalog ``` Add the collator to your backend instance, along with the search plugin itself: diff --git a/plugins/search-backend-module-explore/README.md b/plugins/search-backend-module-explore/README.md index 9ba33ae326..5bccf8c737 100644 --- a/plugins/search-backend-module-explore/README.md +++ b/plugins/search-backend-module-explore/README.md @@ -8,7 +8,7 @@ Add the module package as a dependency: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-search-backend-module-explore +yarn --cwd packages/backend add @backstage/plugin-search-backend-module-explore ``` Add the collator to your backend instance, along with the search plugin itself: diff --git a/plugins/search-backend-module-stack-overflow-collator/README.md b/plugins/search-backend-module-stack-overflow-collator/README.md index 4790917375..e2d516ace5 100644 --- a/plugins/search-backend-module-stack-overflow-collator/README.md +++ b/plugins/search-backend-module-stack-overflow-collator/README.md @@ -73,7 +73,7 @@ Add the module package as a dependency: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-search-backend-module-stack-overflow-collator +yarn --cwd packages/backend add @backstage/plugin-search-backend-module-stack-overflow-collator ``` Add the collator to your backend instance, along with the search plugin itself: diff --git a/plugins/search-backend-module-techdocs/README.md b/plugins/search-backend-module-techdocs/README.md index 04c884efb0..68bb4b956e 100644 --- a/plugins/search-backend-module-techdocs/README.md +++ b/plugins/search-backend-module-techdocs/README.md @@ -8,7 +8,7 @@ Add the module package as a dependency: ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-search-backend-module-techdocs +yarn --cwd packages/backend add @backstage/plugin-search-backend-module-techdocs ``` Add the collator to your backend instance, along with the search plugin itself: diff --git a/plugins/sentry/README.md b/plugins/sentry/README.md index 7cc015eb13..78f31952b2 100644 --- a/plugins/sentry/README.md +++ b/plugins/sentry/README.md @@ -10,7 +10,7 @@ The Sentry Plugin displays issues from [Sentry](https://sentry.io). ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-sentry +yarn --cwd packages/app add @backstage/plugin-sentry ``` 2. Add the `EntitySentryCard` to the EntityPage: diff --git a/plugins/shortcuts/README.md b/plugins/shortcuts/README.md index 46b878a579..18deb7ae37 100644 --- a/plugins/shortcuts/README.md +++ b/plugins/shortcuts/README.md @@ -8,7 +8,7 @@ The shortcuts plugin allows a user to have easy access to pages within a Backsta ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-shortcuts +yarn --cwd packages/app add @backstage/plugin-shortcuts ``` ### Register plugin: diff --git a/plugins/sonarqube-backend/README.md b/plugins/sonarqube-backend/README.md index 4eeac67498..6206cd8257 100644 --- a/plugins/sonarqube-backend/README.md +++ b/plugins/sonarqube-backend/README.md @@ -22,7 +22,7 @@ This plugin needs to be added to an existing backstage instance. ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-sonarqube-backend +yarn --cwd packages/backend add @backstage/plugin-sonarqube-backend ``` Typically, this means creating a `src/plugins/sonarqube.ts` file and adding a reference to it to `src/index.ts` in the backend package. diff --git a/plugins/sonarqube/README.md b/plugins/sonarqube/README.md index ab85d128aa..9d3bc701f9 100644 --- a/plugins/sonarqube/README.md +++ b/plugins/sonarqube/README.md @@ -10,7 +10,7 @@ The SonarQube Plugin displays code statistics from [SonarCloud](https://sonarclo ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-sonarqube +yarn --cwd packages/app add @backstage/plugin-sonarqube ``` 2. Add the `EntitySonarQubeCard` to the EntityPage: diff --git a/plugins/splunk-on-call/README.md b/plugins/splunk-on-call/README.md index fd14b311c6..ceeec2fa4c 100644 --- a/plugins/splunk-on-call/README.md +++ b/plugins/splunk-on-call/README.md @@ -21,7 +21,7 @@ Install the plugin: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-splunk-on-call +yarn --cwd packages/app add @backstage/plugin-splunk-on-call ``` Add it to your `EntityPage`: diff --git a/plugins/tech-insights-backend-module-jsonfc/README.md b/plugins/tech-insights-backend-module-jsonfc/README.md index c405ac34ef..33b3ad7718 100644 --- a/plugins/tech-insights-backend-module-jsonfc/README.md +++ b/plugins/tech-insights-backend-module-jsonfc/README.md @@ -10,7 +10,7 @@ To add this FactChecker into your Tech Insights you need to install the module i ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-tech-insights-backend-module-jsonfc +yarn --cwd packages/backend add @backstage/plugin-tech-insights-backend-module-jsonfc ``` ### Add to the backend diff --git a/plugins/tech-insights-backend/README.md b/plugins/tech-insights-backend/README.md index 29b374c18c..56b927a10e 100644 --- a/plugins/tech-insights-backend/README.md +++ b/plugins/tech-insights-backend/README.md @@ -10,7 +10,7 @@ as well as a framework to run fact retrievers and store fact values in to a data ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-tech-insights-backend +yarn --cwd packages/backend add @backstage/plugin-tech-insights-backend ``` ### Adding the plugin to your `packages/backend` @@ -214,7 +214,7 @@ To add the default FactChecker into your Tech Insights you need to install the m ```bash # From your Backstage root directory -yarn add --cwd packages/backend @backstage/plugin-tech-insights-backend-module-jsonfc +yarn --cwd packages/backend add @backstage/plugin-tech-insights-backend-module-jsonfc ``` and modify the `techInsights.ts` file to contain a reference to the FactChecker implementation. diff --git a/plugins/tech-insights/README.md b/plugins/tech-insights/README.md index 3185d40441..9145e892e7 100644 --- a/plugins/tech-insights/README.md +++ b/plugins/tech-insights/README.md @@ -14,7 +14,7 @@ Main areas covered by this plugin currently are: ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-tech-insights +yarn --cwd packages/app add @backstage/plugin-tech-insights ``` ### Add boolean checks overview (Scorecards) page to the EntityPage: diff --git a/plugins/tech-radar/README.md b/plugins/tech-radar/README.md index 31d90de938..a28ced777e 100644 --- a/plugins/tech-radar/README.md +++ b/plugins/tech-radar/README.md @@ -27,7 +27,7 @@ For either simple or advanced installations, you'll need to add the dependency u ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-tech-radar +yarn --cwd packages/app add @backstage/plugin-tech-radar ``` ### Configuration diff --git a/plugins/techdocs-react/README.md b/plugins/techdocs-react/README.md index 53e9fc6a3a..e0f792064c 100644 --- a/plugins/techdocs-react/README.md +++ b/plugins/techdocs-react/README.md @@ -6,5 +6,5 @@ This package provides frontend utilities for TechDocs and Addons. ```sh # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-techdocs-react +yarn --cwd packages/app add @backstage/plugin-techdocs-react ``` diff --git a/plugins/vault-backend/README.md b/plugins/vault-backend/README.md index 117bcaacbb..2c7dd06e52 100644 --- a/plugins/vault-backend/README.md +++ b/plugins/vault-backend/README.md @@ -16,7 +16,7 @@ To get started, first you need a running instance of Vault. You can follow [this ```bash # From your Backstage root directory - yarn add --cwd packages/backend @backstage/plugin-vault-backend + yarn --cwd packages/backend add @backstage/plugin-vault-backend ``` 2. Create a file in `src/plugins/vault.ts` and add a reference to it in `src/index.ts`: diff --git a/plugins/vault/README.md b/plugins/vault/README.md index ce2df6e47a..fe86a30bd2 100644 --- a/plugins/vault/README.md +++ b/plugins/vault/README.md @@ -18,7 +18,7 @@ To get started, first you need a running instance of Vault. You can follow [this ```bash # From your Backstage root directory - yarn add --cwd packages/app @backstage/plugin-vault + yarn --cwd packages/app add @backstage/plugin-vault ``` 2. Add the Vault card to the overview tab on the EntityPage: diff --git a/plugins/xcmetrics/README.md b/plugins/xcmetrics/README.md index 7c003722a0..0823cbbb24 100644 --- a/plugins/xcmetrics/README.md +++ b/plugins/xcmetrics/README.md @@ -1,6 +1,6 @@ # XCMetrics -[XCMetrics](https://xcmetrics.io) is a tool for collecting build metrics from XCode. +[XCMetrics](https://xcmetrics.io) is a tool for collecting build metrics from XCode. With this plugin, you can view data from XCMetrics directly in Backstage. ![XCMetrics-overview](./docs/XCMetrics-overview.png) @@ -9,7 +9,7 @@ With this plugin, you can view data from XCMetrics directly in Backstage. ```bash # From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-xcmetrics +yarn --cwd packages/app add @backstage/plugin-xcmetrics ``` In `packages/app/src/App.tsx`, add the following: diff --git a/scripts/verify-lockfile-duplicates.js b/scripts/verify-lockfile-duplicates.js index ddf99f4fd6..5c9adb72f0 100644 --- a/scripts/verify-lockfile-duplicates.js +++ b/scripts/verify-lockfile-duplicates.js @@ -95,11 +95,11 @@ async function main() { if (failed) { if (!fix) { - const command = `yarn dedupe${ + const command = `yarn${ lockFile.directoryRelativeToProjectRoot === '.' ? '' : ` --cwd ${lockFile.directoryRelativeToProjectRoot}` - }`; + } dedupe`; const padding = ' '.repeat(Math.max(0, 85 - 6 - command.length)); console.error(''); console.error( From b68248b42b6ad628c295bace2718ae9864f471c4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:06:10 +0000 Subject: [PATCH 304/468] fix(deps): update dependency cron to v3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-b35b354.md | 5 +++++ packages/backend-tasks/package.json | 2 +- yarn.lock | 21 +++++++-------------- 3 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 .changeset/renovate-b35b354.md diff --git a/.changeset/renovate-b35b354.md b/.changeset/renovate-b35b354.md new file mode 100644 index 0000000000..750ef50a74 --- /dev/null +++ b/.changeset/renovate-b35b354.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-tasks': patch +--- + +Updated dependency `cron` to `^3.0.0`. diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index 4089c3b520..4f114cb924 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -38,7 +38,7 @@ "@backstage/types": "workspace:^", "@opentelemetry/api": "^1.3.0", "@types/luxon": "^3.0.0", - "cron": "^2.0.0", + "cron": "^3.0.0", "knex": "^3.0.0", "lodash": "^4.17.21", "luxon": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 5f84cbbc51..1e3713a9cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3450,7 +3450,7 @@ __metadata: "@opentelemetry/api": ^1.3.0 "@types/cron": ^2.0.0 "@types/luxon": ^3.0.0 - cron: ^2.0.0 + cron: ^3.0.0 knex: ^3.0.0 lodash: ^4.17.21 luxon: ^3.0.0 @@ -23914,13 +23914,13 @@ __metadata: languageName: node linkType: hard -"cron@npm:^2.0.0": - version: 2.4.4 - resolution: "cron@npm:2.4.4" +"cron@npm:^3.0.0": + version: 3.1.6 + resolution: "cron@npm:3.1.6" dependencies: "@types/luxon": ~3.3.0 - luxon: ~3.3.0 - checksum: e7dbc39c7c747b4fb198c3e62737bcc049ec8edb2b150eee17d39256982c6688f48b50b72e9b869b18feb186ab5081bcd8b82ad1ef79d9b7a13df499aaa77084 + luxon: ~3.4.0 + checksum: e97bb6f85acf3195577c609f28bbac2e22812d8632802752a13591882deceeeeefd2c91c1293fb5102ef442f96ae17ca687854fd0b005149aae9a25834363e1d languageName: node linkType: hard @@ -33573,20 +33573,13 @@ __metadata: languageName: node linkType: hard -"luxon@npm:^3.0.0, luxon@npm:^3.3.0, luxon@npm:^3.4.3": +"luxon@npm:^3.0.0, luxon@npm:^3.3.0, luxon@npm:^3.4.3, luxon@npm:~3.4.0": version: 3.4.4 resolution: "luxon@npm:3.4.4" checksum: 36c1f99c4796ee4bfddf7dc94fa87815add43ebc44c8934c924946260a58512f0fd2743a629302885df7f35ccbd2d13f178c15df046d0e3b6eb71db178f1c60c languageName: node linkType: hard -"luxon@npm:~3.3.0": - version: 3.3.0 - resolution: "luxon@npm:3.3.0" - checksum: 50cf17a0dc155c3dcacbeae8c0b7e80db425e0ba97b9cbdf12a7fc142d841ff1ab1560919f033af46240ed44e2f70c49f76e3422524c7fc8bb8d81ca47c66187 - languageName: node - linkType: hard - "lz-string@npm:^1.5.0": version: 1.5.0 resolution: "lz-string@npm:1.5.0" From 03c5bbef1f2a8457fa81cb8e627ad0d4ca4cfa17 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:08:26 +0000 Subject: [PATCH 305/468] fix(deps): update dependency date-fns to v3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .changeset/renovate-97e6aa5.md | 5 +++++ plugins/opencost/package.json | 2 +- yarn.lock | 9 ++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .changeset/renovate-97e6aa5.md diff --git a/.changeset/renovate-97e6aa5.md b/.changeset/renovate-97e6aa5.md new file mode 100644 index 0000000000..51f4edccd2 --- /dev/null +++ b/.changeset/renovate-97e6aa5.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-opencost': patch +--- + +Updated dependency `date-fns` to `^3.0.0`. diff --git a/plugins/opencost/package.json b/plugins/opencost/package.json index 2046d04c47..b5b37d6f2b 100644 --- a/plugins/opencost/package.json +++ b/plugins/opencost/package.json @@ -32,7 +32,7 @@ "@material-ui/styles": "^4.11.5", "@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0", "axios": "^1.4.0", - "date-fns": "^2.30.0", + "date-fns": "^3.0.0", "lodash": "^4.17.21", "recharts": "^2.5.0" }, diff --git a/yarn.lock b/yarn.lock index 5f84cbbc51..0519d4cbd5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7818,7 +7818,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 axios: ^1.4.0 - date-fns: ^2.30.0 + date-fns: ^3.0.0 lodash: ^4.17.21 recharts: ^2.5.0 peerDependencies: @@ -24615,6 +24615,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^3.0.0": + version: 3.3.1 + resolution: "date-fns@npm:3.3.1" + checksum: 6245e93a47de28ac96dffd4d62877f86e6b64854860ae1e00a4f83174d80bc8e59bd1259cf265223fb2ddce5c8e586dc9cc210f0d052faba2f7660e265877283 + languageName: node + linkType: hard + "dateformat@npm:^3.0.3": version: 3.0.3 resolution: "dateformat@npm:3.0.3" From 742e0fefde56f7cd4e42cb49fcf53b0ae5d6bdac Mon Sep 17 00:00:00 2001 From: Frida Jacobsson Date: Mon, 5 Feb 2024 09:09:35 +0100 Subject: [PATCH 306/468] Adding Umami analytics plugin to microsite Signed-off-by: Frida Jacobsson --- microsite/data/plugins/umami.yaml | 10 ++++++++++ microsite/static/img/umami-logo.svg | 1 + 2 files changed, 11 insertions(+) create mode 100644 microsite/data/plugins/umami.yaml create mode 100644 microsite/static/img/umami-logo.svg diff --git a/microsite/data/plugins/umami.yaml b/microsite/data/plugins/umami.yaml new file mode 100644 index 0000000000..308b09c90c --- /dev/null +++ b/microsite/data/plugins/umami.yaml @@ -0,0 +1,10 @@ +--- +title: Umami Analytics +author: AxisCommunications +authorUrl: https://github.com/AxisCommunications +category: Monitoring +description: Track usage of your Backstage instance using Umami Analytics. +documentation: https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/analytics-module-umami/README.md +iconUrl: /img/umami-logo.svg +npmPackageName: '@axis-backstage/plugin-analytics-module-umami' +addedDate: '2024-02-05' \ No newline at end of file diff --git a/microsite/static/img/umami-logo.svg b/microsite/static/img/umami-logo.svg new file mode 100644 index 0000000000..b139531324 --- /dev/null +++ b/microsite/static/img/umami-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file From 307474d1a3831d617d2ab9f8506f5f4c7f45e53a Mon Sep 17 00:00:00 2001 From: Frida Jacobsson Date: Mon, 5 Feb 2024 10:17:24 +0100 Subject: [PATCH 307/468] Run prettier Signed-off-by: Frida Jacobsson --- microsite/data/plugins/umami.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/umami.yaml b/microsite/data/plugins/umami.yaml index 308b09c90c..5f1fc208d4 100644 --- a/microsite/data/plugins/umami.yaml +++ b/microsite/data/plugins/umami.yaml @@ -7,4 +7,4 @@ description: Track usage of your Backstage instance using Umami Analytics. documentation: https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/analytics-module-umami/README.md iconUrl: /img/umami-logo.svg npmPackageName: '@axis-backstage/plugin-analytics-module-umami' -addedDate: '2024-02-05' \ No newline at end of file +addedDate: '2024-02-05' From 7fb7a79dce0b40260ffabfde3621eea6b3dddd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 5 Feb 2024 13:11:46 +0100 Subject: [PATCH 308/468] add a config declaration for workingDirectory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/wise-papayas-cough.md | 5 +++++ packages/backend-common/config.d.ts | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 .changeset/wise-papayas-cough.md diff --git a/.changeset/wise-papayas-cough.md b/.changeset/wise-papayas-cough.md new file mode 100644 index 0000000000..955e7fefcc --- /dev/null +++ b/.changeset/wise-papayas-cough.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Add a config declaration for `workingDirectory` diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 8bcc5465d5..700d9a6c6c 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -67,6 +67,22 @@ export interface Config { }; }; + /** + * An absolute path to a directory that can be used as a working dir, for + * example as scratch space for large operations. + * + * @remarks + * + * Note that this must be an absolute path. + * + * If not set, the operating system's designated temporary directory is + * commonly used, but that is implementation defined per plugin. + * + * Plugins are encouraged to heed this config setting if present, to allow + * deployment in severely locked-down or limited environments. + */ + workingDirectory?: string; + /** Database connection configuration, select base database type using the `client` field */ database: { /** Default database client to use */ From da21baa80c5edce1c1ef1f532f147c652f1ed00b Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Mon, 5 Feb 2024 08:44:43 -0500 Subject: [PATCH 309/468] Swapping Red Hat partner logo for all white version Signed-off-by: Corey Daley --- microsite/static/img/partner-logo-redhat.png | Bin 15999 -> 19240 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/microsite/static/img/partner-logo-redhat.png b/microsite/static/img/partner-logo-redhat.png index ad9bc9cebabc138bf0491718a8f5ad3cbdbf1ddc..0b98532c2c5b12348c12e5b39f3f1d9e1db85b4d 100644 GIT binary patch literal 19240 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 literal 15999 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 From c3ec819026179a4b99c74318c34cfdd0d66d8579 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 5 Feb 2024 15:54:54 +0100 Subject: [PATCH 310/468] Adjust to review Signed-off-by: Philipp Hugenroth --- packages/app-next/app-config.yaml | 12 +++++------- packages/app-next/src/App.tsx | 2 -- plugins/org/api-report-alpha.md | 6 ------ plugins/org/src/alpha.tsx | 12 ++++++++---- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 0b842a0b91..4e54d96d7c 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -5,7 +5,7 @@ app: bindings: catalog.viewTechDoc: techdocs.docRoot catalog.createComponent: catalog-import.importPage - # org.catalogIndex: catalog.catalogIndex + org.catalogIndex: catalog.catalogIndex extensions: # - apis.plugin.graphiql.browse.gitlab: true @@ -21,12 +21,10 @@ app: - entity-content:techdocs # Org Plugin - - entity-card:org/group-profile: - config: - filter: kind:group - # - entity-card:org/members-list - # - entity-card:org/ownership - # - entity-card:org/user-profile + - entity-card:org/group-profile + - entity-card:org/members-list + - entity-card:org/ownership + - entity-card:org/user-profile # scmAuthExtension: >- # createScmAuthExtension({ diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index bb39d8d40e..03a6649c27 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -24,7 +24,6 @@ import userSettingsPlugin from '@backstage/plugin-user-settings/alpha'; import homePlugin, { titleExtensionDataRef, } from '@backstage/plugin-home/alpha'; -import orgPlugin from '@backstage/plugin-org/alpha'; import { coreExtensionData, @@ -126,7 +125,6 @@ const app = createApp({ userSettingsPlugin, homePlugin, appVisualizerPlugin, - orgPlugin, ...collectedLegacyPlugins, createExtensionOverrides({ extensions: [ diff --git a/plugins/org/api-report-alpha.md b/plugins/org/api-report-alpha.md index b9605a45f5..3cee584db0 100644 --- a/plugins/org/api-report-alpha.md +++ b/plugins/org/api-report-alpha.md @@ -4,7 +4,6 @@ ```ts import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) @@ -16,10 +15,5 @@ const _default: BackstagePlugin< >; export default _default; -// @alpha (undocumented) -export const EntityGroupProfileCard: ExtensionDefinition<{ - filter?: string | undefined; -}>; - // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/org/src/alpha.tsx b/plugins/org/src/alpha.tsx index 3d02aec88b..3fbf5bcc41 100644 --- a/plugins/org/src/alpha.tsx +++ b/plugins/org/src/alpha.tsx @@ -24,8 +24,9 @@ import { catalogIndexRouteRef } from './routes'; import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha'; /** @alpha */ -export const EntityGroupProfileCard = createEntityCardExtension({ +const EntityGroupProfileCard = createEntityCardExtension({ name: 'group-profile', + filter: 'kind:group', loader: async () => import('./components/Cards/Group/GroupProfile/GroupProfileCard').then(m => compatWrapper(), @@ -33,8 +34,9 @@ export const EntityGroupProfileCard = createEntityCardExtension({ }); /** @alpha */ -export const EntityMembersListCard = createEntityCardExtension({ +const EntityMembersListCard = createEntityCardExtension({ name: 'members-list', + filter: 'kind:group', loader: async () => import('./components/Cards/Group/MembersList/MembersListCard').then(m => compatWrapper(), @@ -42,8 +44,9 @@ export const EntityMembersListCard = createEntityCardExtension({ }); /** @alpha */ -export const EntityOwnershipCard = createEntityCardExtension({ +const EntityOwnershipCard = createEntityCardExtension({ name: 'ownership', + filter: 'kind:group,user', loader: async () => import('./components/Cards/OwnershipCard/OwnershipCard').then(m => compatWrapper(), @@ -51,8 +54,9 @@ export const EntityOwnershipCard = createEntityCardExtension({ }); /** @alpha */ -export const EntityUserProfileCard = createEntityCardExtension({ +const EntityUserProfileCard = createEntityCardExtension({ name: 'user-profile', + filter: 'kind:user', loader: async () => import('./components/Cards/User/UserProfileCard/UserProfileCard').then(m => compatWrapper(), From feea2211231674e5a31e99a66cef7914a1e96273 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 29 Jan 2024 11:54:04 +0100 Subject: [PATCH 311/468] feat: configure alpha subpath exports Signed-off-by: Camila Belo --- plugins/catalog-graph/package.json | 19 ++++++++++++++++--- plugins/catalog-graph/src/alpha.ts | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 plugins/catalog-graph/src/alpha.ts diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 1dba15d840..4aad12b5bc 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -5,9 +5,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.tsx", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.tsx" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "frontend-plugin" diff --git a/plugins/catalog-graph/src/alpha.ts b/plugins/catalog-graph/src/alpha.ts new file mode 100644 index 0000000000..94adcafa53 --- /dev/null +++ b/plugins/catalog-graph/src/alpha.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2024 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. + */ From a36b357b5af88e430f467e77407579dccd8f2a60 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 29 Jan 2024 11:55:36 +0100 Subject: [PATCH 312/468] feat: migrate catalog-graph plugin Signed-off-by: Camila Belo --- plugins/catalog-graph/package.json | 2 ++ plugins/catalog-graph/src/alpha.ts | 14 ++++++++++++++ yarn.lock | 2 ++ 3 files changed, 18 insertions(+) diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 4aad12b5bc..b92cf201e4 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -44,8 +44,10 @@ "dependencies": { "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/types": "workspace:^", "@material-ui/core": "^4.12.2", diff --git a/plugins/catalog-graph/src/alpha.ts b/plugins/catalog-graph/src/alpha.ts index 94adcafa53..345a632dea 100644 --- a/plugins/catalog-graph/src/alpha.ts +++ b/plugins/catalog-graph/src/alpha.ts @@ -13,3 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createPlugin } from '@backstage/frontend-plugin-api'; +import { convertLegacyRouteRef } from '@backstage/core-compat-api'; +import { catalogGraphRouteRef, catalogEntityRouteRef } from './routes'; + +export default createPlugin({ + id: 'catalog-graph', + routes: { + catalogGraph: convertLegacyRouteRef(catalogGraphRouteRef), + }, + externalRoutes: { + catalogEntity: convertLegacyRouteRef(catalogEntityRouteRef), + }, + extensions: [], +}); diff --git a/yarn.lock b/yarn.lock index 5f84cbbc51..5cd5a0cbf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5783,9 +5783,11 @@ __metadata: "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" + "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/test-utils": "workspace:^" From 1b06d313d89ce94f51f0947387ca37f4b529899f Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 30 Jan 2024 10:08:56 +0100 Subject: [PATCH 313/468] feat: migrate catalog graph page Signed-off-by: Camila Belo --- plugins/catalog-graph/src/alpha.ts | 29 -------------- plugins/catalog-graph/src/alpha.tsx | 62 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 29 deletions(-) delete mode 100644 plugins/catalog-graph/src/alpha.ts create mode 100644 plugins/catalog-graph/src/alpha.tsx diff --git a/plugins/catalog-graph/src/alpha.ts b/plugins/catalog-graph/src/alpha.ts deleted file mode 100644 index 345a632dea..0000000000 --- a/plugins/catalog-graph/src/alpha.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2024 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 { createPlugin } from '@backstage/frontend-plugin-api'; -import { convertLegacyRouteRef } from '@backstage/core-compat-api'; -import { catalogGraphRouteRef, catalogEntityRouteRef } from './routes'; - -export default createPlugin({ - id: 'catalog-graph', - routes: { - catalogGraph: convertLegacyRouteRef(catalogGraphRouteRef), - }, - externalRoutes: { - catalogEntity: convertLegacyRouteRef(catalogEntityRouteRef), - }, - extensions: [], -}); diff --git a/plugins/catalog-graph/src/alpha.tsx b/plugins/catalog-graph/src/alpha.tsx new file mode 100644 index 0000000000..bf2b0ffce8 --- /dev/null +++ b/plugins/catalog-graph/src/alpha.tsx @@ -0,0 +1,62 @@ +/* + * Copyright 2024 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 { + createPageExtension, + createPlugin, + createSchemaFromZod, +} from '@backstage/frontend-plugin-api'; +import { + compatWrapper, + convertLegacyRouteRef, +} from '@backstage/core-compat-api'; +import { catalogGraphRouteRef, catalogEntityRouteRef } from './routes'; +import { Direction } from './components'; + +const CatalogGraphPage = createPageExtension({ + defaultPath: '/catalog-graph', + routeRef: convertLegacyRouteRef(catalogGraphRouteRef), + configSchema: createSchemaFromZod(z => + z.object({ + path: z.string().default('/catalog-graph'), + selectedKinds: z.array(z.string()).optional(), + selectedRelations: z.array(z.string()).optional(), + rootEntityRefs: z.array(z.string()).optional(), + maxDepth: z.number().optional(), + unidirectional: z.boolean().optional(), + mergeRelations: z.boolean().optional(), + direction: z.nativeEnum(Direction).optional(), + showFilters: z.boolean().optional(), + curve: z.enum(['curveStepBefore', 'curveMonotoneX']).optional(), + }), + ), + loader: ({ config: { path, ...initialState } }) => + import('./components/CatalogGraphPage').then(m => + compatWrapper(), + ), +}); + +export default createPlugin({ + id: 'catalog-graph', + routes: { + catalogGraph: convertLegacyRouteRef(catalogGraphRouteRef), + }, + externalRoutes: { + catalogEntity: convertLegacyRouteRef(catalogEntityRouteRef), + }, + extensions: [CatalogGraphPage], +}); From 0e7340cb25e9b713579386a7e665c54be9a954fa Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 30 Jan 2024 14:03:33 +0100 Subject: [PATCH 314/468] feat(catalog-react): accepts entity cards config schema Signed-off-by: Camila Belo --- plugins/catalog-react/src/alpha.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/catalog-react/src/alpha.tsx b/plugins/catalog-react/src/alpha.tsx index 330cd573cd..f5f4dfdb74 100644 --- a/plugins/catalog-react/src/alpha.tsx +++ b/plugins/catalog-react/src/alpha.tsx @@ -17,6 +17,7 @@ import { AnyExtensionInputMap, ExtensionBoundary, + PortableSchema, ResolvedExtensionInputs, RouteRef, coreExtensionData, @@ -48,6 +49,7 @@ export const catalogExtensionData = { // TODO: Figure out how to merge with provided config schema /** @alpha */ export function createEntityCardExtension< + TConfig extends { filter?: string }, TInputs extends AnyExtensionInputMap, >(options: { namespace?: string; @@ -55,13 +57,23 @@ export function createEntityCardExtension< attachTo?: { id: string; input: string }; disabled?: boolean; inputs?: TInputs; + configSchema?: PortableSchema; filter?: | typeof catalogExtensionData.entityFilterFunction.T | typeof catalogExtensionData.entityFilterExpression.T; loader: (options: { + config: TConfig; inputs: Expand>; }) => Promise; }) { + const configSchema = + 'configSchema' in options + ? options.configSchema + : (createSchemaFromZod(z => + z.object({ + filter: z.string().optional(), + }), + ) as PortableSchema); return createExtension({ kind: 'entity-card', namespace: options.namespace, @@ -77,15 +89,11 @@ export function createEntityCardExtension< filterExpression: catalogExtensionData.entityFilterExpression.optional(), }, inputs: options.inputs, - configSchema: createSchemaFromZod(z => - z.object({ - filter: z.string().optional(), - }), - ), + configSchema, factory({ config, inputs, node }) { const ExtensionComponent = lazy(() => options - .loader({ inputs }) + .loader({ inputs, config }) .then(element => ({ default: () => element })), ); From c888887b65308af6e16d6b63a6d13a84a7a853ec Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 30 Jan 2024 14:06:30 +0100 Subject: [PATCH 315/468] feat(catalog-graph): create entity card extension Signed-off-by: Camila Belo --- plugins/catalog-graph/src/alpha.tsx | 80 ++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/plugins/catalog-graph/src/alpha.tsx b/plugins/catalog-graph/src/alpha.tsx index bf2b0ffce8..00be9e8e69 100644 --- a/plugins/catalog-graph/src/alpha.tsx +++ b/plugins/catalog-graph/src/alpha.tsx @@ -24,29 +24,85 @@ import { compatWrapper, convertLegacyRouteRef, } from '@backstage/core-compat-api'; +import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha'; import { catalogGraphRouteRef, catalogEntityRouteRef } from './routes'; import { Direction } from './components'; +type Zod = Parameters[0]>[0]; + +function getCompoundEntityRefConfigSchema(z: Zod) { + return z.object({ + kind: z.string(), + namespace: z.string(), + name: z.string(), + }); +} + +function getEntityGraphRelationsConfigSchema(z: Zod) { + // Mapping EntityRelationsGraphProps to config + // TODO: Define how className and render functions will be configured + return z.object({ + rootEntityRef: getCompoundEntityRefConfigSchema(z) + .or(z.array(getCompoundEntityRefConfigSchema(z))) + .optional(), + kinds: z.array(z.string()).optional(), + relations: z.array(z.string()).optional(), + maxDepth: z.number().optional(), + unidirectional: z.boolean().optional(), + mergeRelations: z.boolean().optional(), + direction: z.nativeEnum(Direction).optional(), + relationPairs: z.array(z.tuple([z.string(), z.string()])).optional(), + zoom: z.enum(['enabled', 'disabled', 'enable-on-click']).optional(), + curve: z.enum(['curveStepBefore', 'curveMonotoneX']).optional(), + }); +} + +const CatalogGraphEntityCard = createEntityCardExtension({ + name: 'catalog-graph', + configSchema: createSchemaFromZod(z => + z + .object({ + // Filter is a config required to all entity cards + filter: z.string().optional(), + title: z.string().optional(), + height: z.number().optional(), + variant: z.enum(['flex', 'fullHeight', 'gridItem']).optional(), + }) + .merge(getEntityGraphRelationsConfigSchema(z)), + ), + loader: async ({ config: { filter, ...props } }) => + import('./components/CatalogGraphCard').then(m => + compatWrapper(), + ), +}); + const CatalogGraphPage = createPageExtension({ defaultPath: '/catalog-graph', routeRef: convertLegacyRouteRef(catalogGraphRouteRef), configSchema: createSchemaFromZod(z => z.object({ + // Path is a default config required to all pages path: z.string().default('/catalog-graph'), - selectedKinds: z.array(z.string()).optional(), - selectedRelations: z.array(z.string()).optional(), - rootEntityRefs: z.array(z.string()).optional(), - maxDepth: z.number().optional(), - unidirectional: z.boolean().optional(), - mergeRelations: z.boolean().optional(), - direction: z.nativeEnum(Direction).optional(), - showFilters: z.boolean().optional(), - curve: z.enum(['curveStepBefore', 'curveMonotoneX']).optional(), + // Mapping intialState prop to config + initialState: z + .object({ + selectedKinds: z.array(z.string()).optional(), + selectedRelations: z.array(z.string()).optional(), + rootEntityRefs: z.array(z.string()).optional(), + maxDepth: z.number().optional(), + unidirectional: z.boolean().optional(), + mergeRelations: z.boolean().optional(), + direction: z.nativeEnum(Direction).optional(), + showFilters: z.boolean().optional(), + curve: z.enum(['curveStepBefore', 'curveMonotoneX']).optional(), + }) + .merge(getEntityGraphRelationsConfigSchema(z)) + .optional(), }), ), - loader: ({ config: { path, ...initialState } }) => + loader: ({ config: { path, ...props } }) => import('./components/CatalogGraphPage').then(m => - compatWrapper(), + compatWrapper(), ), }); @@ -58,5 +114,5 @@ export default createPlugin({ externalRoutes: { catalogEntity: convertLegacyRouteRef(catalogEntityRouteRef), }, - extensions: [CatalogGraphPage], + extensions: [CatalogGraphPage, CatalogGraphEntityCard], }); From d73d7dd5b1824eb5c22cb94ac4ad5582c040c89f Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 31 Jan 2024 10:21:06 +0100 Subject: [PATCH 316/468] feat(app-next): install catalog in app-next Signed-off-by: Camila Belo --- packages/app-next/app-config.yaml | 1 + plugins/catalog-graph/src/alpha.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index a79bb866db..7402493179 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -16,6 +16,7 @@ app: config: filter: kind:component has:links - entity-card:linguist/languages + - entity-card:catalog-graph/entity-relations # Entity page content - entity-content:techdocs diff --git a/plugins/catalog-graph/src/alpha.tsx b/plugins/catalog-graph/src/alpha.tsx index 00be9e8e69..6d0c9d2403 100644 --- a/plugins/catalog-graph/src/alpha.tsx +++ b/plugins/catalog-graph/src/alpha.tsx @@ -58,7 +58,7 @@ function getEntityGraphRelationsConfigSchema(z: Zod) { } const CatalogGraphEntityCard = createEntityCardExtension({ - name: 'catalog-graph', + name: 'entity-relations', configSchema: createSchemaFromZod(z => z .object({ From 4d27f6ae69a7c98effc00e6aa621c5f8970fc3dd Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 1 Feb 2024 09:55:51 +0100 Subject: [PATCH 317/468] docs: start drafting the card documentation Signed-off-by: Camila Belo --- plugins/catalog-graph/EXPERIMENTAL.md | 224 ++++++++++++++++++++++++++ plugins/catalog-graph/README.md | 3 + plugins/catalog-graph/src/alpha.tsx | 28 ++-- 3 files changed, 246 insertions(+), 9 deletions(-) create mode 100644 plugins/catalog-graph/EXPERIMENTAL.md diff --git a/plugins/catalog-graph/EXPERIMENTAL.md b/plugins/catalog-graph/EXPERIMENTAL.md new file mode 100644 index 0000000000..5870b30e4b --- /dev/null +++ b/plugins/catalog-graph/EXPERIMENTAL.md @@ -0,0 +1,224 @@ +# catalog-graph + +> **Disclaimer:** +> This documentation is made for those using the experimental new Frontend system. +> If you are not using the new Backstage frontend system, please go [here](./README.md). + +Welcome to the catalog graph plugin! The catalog graph visualizes the relations +between entities, like ownership, grouping or API relationships. + +The plugin comes with these features: + +- Catalog entity relations graph card: + A card that displays the directly related entities to the current entity. + This card is for use on the entity page. + The card can be customized, for example filtering for specific relations. +

    N!RC|bd^*~(wKaYw39MSwD4S69QPk`~RG^};+ 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 lnH6Gs8OD8DVMRtFB;ymE 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 R&NINj-%hA1kbg0{^gr2Fv>*Tg literal 0 HcmV?d00001 diff --git a/plugins/catalog-graph/catalog-graph-entity-relations-page.png b/plugins/catalog-graph/catalog-graph-entity-relations-page.png new file mode 100644 index 0000000000000000000000000000000000000000..21f964b6741638cafd20841fefc2991d6f467b46 GIT binary patch literal 158626 zcmX_nbyQUE_cbuWfTSWIT?!J?-AF4X2#B<_boT(#4bl<=(hbtx-3>!`3_a8g47~VR zzwi6UUH8mg&mVU^v+p_koV}kgRb@Hc=VZ@OP*89c8p+tfqm>Lx2JWrt-eKLB`bfw zGrd{ezuVIzp1afAeS#EmxI84P6OZC>0o@ z+cZUaAnN_A=l4wVZUAfk`z=@?>RAdoL~wYN6P7E-L1q>|iOFNi`=}3vhSo z6dCF}k-o3xG)RB$0#ZxI*~OI%SU+H;6PgSX8$s9TyrDeg_e-V1yuuE))=5zAi z+)`+MLNL!PT$1%9V(a3CW`S6bR#9g12c*IK`d58#MT7OzahwM@T<)(Bu38zXn47-@ zNwCQx-?D4go_`qd)v9I8B`~OS_Kn$G9#C3}A3_DUgUNPO`9i{IuTrOl!F#W<*L5@|9i^bwD@KZ(xC1lBk? zv^F(Bbv{Bn(TCXwTeTc_LrW!tSldIfP(x*(fo&%ooWd{@vX zQAmYN$#18*L&SK?jv0iuxoIcm-Oy3Kc-8s*r=g_G zQ7A6&4{J`@sNV~`RWgKLaB}pQFNnO*$P(XuruqJgfFPH(^}WncotEq4p70Im^mMrq zIablSl6nJgbs8VBbTT^`Sr&FPBet%c)g)KK>;!ZId~Q0}*afTDg-BTU3l{~p-J0ua zjj!$-zZoV}@5L;J@c<-u(hKdQ7F46`e8 zJE%q0cl;--IJx=wnA3%+%W2R9+dOX0n|TMj-JHmGO+*BZ&CK$#9ku8o)syEOYp?BA z^dY^1pnLt@yP|75sOS}b%aO`X%pK;cZ{Ou|j1(qRkZ#mt@7 zxr-2iqD59`At2RjitrR;rmpEC!oG)Hx1qy&Ar_yO5Qe_uH$uPD=)NKgzIUNBJBH;C5QHzMv53C z7#Kzex?WUkGHjadnRyk1Cc3owE#zK|-GU?_T}_fA9UC_&cMRV?ddJ4rRmL!Pky0CO z`=6gX1b{g|jUcY3UFq=HRk}=c3u(KL{LjPAIpBOF=ryE^e8SwuXYndc*5#bE{91K-!!|clrba zl|^f_T7A_($ZDVcKBLtZ^>p}sB5_Vp4HCMwVy`(s_3qczNRm6ZfO%V>O=ZX>b*kC@aQzi z06FNxM&x%4fN25|7dn&{PF>F%gii2yIsq#;9b@buvzs8kgwQ^7u@KwTHOH2<0hg7( zwQ*ByFOB_pz*6mpLPF+i6YM%Z$0J*E4^?EgD+m{lLE3VEVAJPhvO?_Kv<6_)1dq)o z)uGb$S(S#l>2l3t11hya5P=8>1=Z``p1wat8ypN@Ux=t~qKe2xZP16fyHGIfcoLoc z8E0{KZOA;jMz4rL22l7S4zG7_*u1F6@6N|=TJ9E3y~Yvyb$Po>CSPWnwHGyvWL>pW zGTz?~%vMjHajogpC%ZbdQ@1C>LB*|0KgLVt;v1+&qvw724cFkSFq_-@uf&=i9$)G^ z$VAqms5L_O-jdyPjk)HYPq6v%+C@NH$lGW^J3FM}8QpTwz2X2eyrb9Bq2B0ohyv2Z zu-=CGmzD|3{<&(=blAMV45D~58~=hd+`i8)HiFKJ_~TDy#!MirP2Zs%RNQ6hL|7=S zFjN633UDPneLGXPdr+iaK?`t3KA!<*MC_Pm>fzRd%1RZZ^!d13F}ue8)bO2;@cU>4 za*K6roTDsJeESl}-A4U{UR>5E^saMw_!(0`q+7%ZY$gIr2{KbsDv{efnth|C+`3fGy4}0<3)EHcX%yv}Etiz5A(4@+ zk#)s2@s5+OiLU682*U}=W|ECap!KVL6sqraAh*XUDQC6RhP4ud-a_l~{Q`MfGD_dZtd;E1EOzd0MKln;ORogS#u| zV1lXMI@PvOddSI3S_iRYxysghDhmkT)!c6yACOlT$yUB6RcAlewdn=USR{75!&M7$ zVbg)X!{X`>YwN#hbOEL7{{9{)=KYH{emh43>C{^MH6W&s=$Qd8Ra`ytpz|@Kd*_W6 z$E){ZZ8t1@!l^yX3mAwwa&T$4KJ}W{(rTYkcz?euOJDs_FP!_Y276t84WYwOolvs= zp(r`)<6qd{wu5w2NCv1lZ>9x3_t-KV_wP|l^$BCgP3F~(`DdFd>tTBgii{VAXA%Q!D zrnsfrAPwcdIMRPSo;H=ce-5X-qeE8Jem!RlS(}@tJ~w%sKC%AzN98bmy$1_1uCfzrxEet;B-F6@_cpC7rpTydG^0 z#e&~BUoJ+`NMC+t&;D5PH--f%Z+`v-Ile7cYULFtdTtgIZ2aqR=#_T^4mqaA4#R`Th{NU0BPt7j)!vyknVLji_O z4kmssu9s0RY1h7&B-tY52{`OrTPlBDXzsj-L$Rk1CM}|Ki2ZSo-N2}6EnfETs|C1! zrM-(&_RM@hcdXN3(7!$64)bw6%-xbAx=i#2g|5DLo?;5weU%r#RvY7u6?r^v5l+gqR(ZT{|^0Y=TuY0DjRRtH5MCeLvD?CLO3-mA6W;`!YOkLEuT>W+j6 za*iRA*JCTlaCT!-%|t)hH?|))r={QPkR6hkDAsNE;uAV7>{6KHGJ=wwf6FYg`7&pQ z+8AuZgFI2x{Yh117%*Y|-l+L#JNa}(zjD=MhS_W2y>>wsu*8|Mx4o|4r?lSLwdgp` zFi3~_C@3C&*b`5b$bCuzgJ)A<5#J9gwi4b|03+W?q-ihFB6Wm|)@2hzCCF@3drjxC zV7U&N#_szIO!pWoG4)Z*uZuk!t^>Q(BLa-;(@kN^S3SaC4 zXGm0LSMf_+Y}m_}K!Rt*LZIT_n^m;%6*b0Q7FeM$)wNe8ot1u5=V_Xo%ERByPEvdN z2;%pi2Be?mv8kS3ItrL1=1fic=*HkHaHnkZdCR|IWg}0i1?u$UD{;u#q)9b{9|o4?EmUsr8|vXX`on;|3PcuOmAB$ zrnxs^gDT`>%e%~!bROd=GsnCpP|=jMeikm3y(kC6-izqLA(~Y_R9UKv^8l)L3FVF) z^GT!g?&W0+Zed}oxiTHQxl%nVGc)r)vs`Ll1*%_nf+`s>miV+Jy>ezoG)Zo=$1A6PO(@$yhcKmS%qZOGRzxS0aB7$pa);aQ?eY;paJOAbY)T<+}$bnB#NHDuA~g_VypRF-AebW3I5H zJ$xR7HVE>7;i*uJ>~CODoqw$JMxN~T?I@nvW9Y_DJ+J8m=v_)oK5w8SvM?bKSZDr2 zetqkHFK}d*w9p^tgf$q;P<2wthFg?6#|jY|;Uiw;14A1?sw_ib@-^p_F$>MgxcN>(coHexaA ze8(@JH$v-y_9~Gv!-oQk#g5;t={~PHTc>ec(1C0jA+J3DE+uy>x%@!DmwgeClkQUk zJ7*@I_UkH22!`cjmW^HUnvd3Gg1AOgW{h~#^4SZqy1bt72FQ0&a*8tX#0rSK%_pa~ zo~fq=o>@1MF2P#6qNi}wyD?}c;U4^APqXjWz|%H_Iekz&lc*4#xDK`(E`SA~0=8%T ze^(6h?!<=(+=b%l+W^}zPfPdXE1JJk5L}$mp&pPGQ0>ckk}8u!I}bC15r-X~Ld{a> z;CbwOoC3U3#ww=7l@KAgF2Qmoqh>z)aFFu?} z=;22Wz5yW4o^sDmARYdCKya^T zySUdHi&LI;tuGdb*xsDKEn_B`zTSKJMf%4bi@Z~O?L&)aPU^+fO7(`{(qa4_2|`Zt zu)cyvniMA0>f$hwh~9#qmfrL6<})dyEVZ3BBI%gOMdRSD9`O&Lsd~wG@1;*rwj`;< zzv<3NpSJFy*7cJW++)e?g4aYcmd!xN_K2;b07#3pq05k(jZ2hosRvgnp*oC&j@*t9i2S)| zyn?m85{-qaS2}6I1R?|?s>l8&rn<8x6w+54@a&QA)K^gk%)-Gqp5B`!Yawg%ZbYzm z(FN6oF~{1LqsA>4r|^{RG&lC8UJZSO4{Wt^b7U~&+jXQsCd<*ZAHq>-0Nfw?_)9Woo4}`>@m{svpElW z12?QMFK8Ih&xl?h@YvD=Z}E>4@3x-PFNC40l2T+H1Qm0B4iTC4m-#R-e))u~m7q&u z*kmWY7YFI?;wNt7TH4EVAj4tB&sw6(sLE2Y=Bw7S&k0fwPfPt;PK&c_#M8N%BMzy@ zRy&gWCOKN)_)qix2i)fqf1bhsznW2-DBX1D z)J!6BR=|&{Nf!+R&Pd#k{vH zIfi0x*cMly{q;DMp5D z80#iaN7=^{ytSrf+kM{1)J~ij!#?@HZKz$FOS=@tul$~_^X&7g2w`+A+ydPOz4q`B zIZU24yH)e0X?gkFo07Asjl13DR(`8QT;s$RuC9P}l!QE^bOlQ;1<@^6X0MK6ee(@V zeXG!fmJmzy=HR;0FIS`~&okqc?}|a=WQTaSz|%~330#py=PCh$wXS)?Sz6RGN{6G^ zJt)h=24yyI1o|0KajEvxfS1n&coG6|_H}oy);pZUAmipWwoB31?MvBOb*@We-X7VY zy}LYpRtz4E&Koh^dQgToJ~41@zTLx!Df;D#5qkQCdc#w5Ru4>~Xs9oghBAnc z{j##SNlXd@EcXay26JUJU=Qx~5mtQMYnx;8q(e((bNo~B%YKen^FyzyK8m*PMSq2y zTC6kGK?=VK06zsm2#<49ucGv9-c&iN{bDINV0w+imFVV$bzqt;L@A?sPdNO1>b#cW zI6|n<0{z=7Fk8$AY+8z|;&(F&>}d_?zUk#9?7f@V$ig;hGY&C-wRX>pvC`?bSGHGj zJKdMBBBuxA{rTuq7nG%soo_k!%%SW(JGqM%r4}-9I!b?zE|Utb#&=A<$zo?8GE{)CQ-oQxm=Aq=KGTSKaB$(UYz#CL+Yv{3j9#iX zWY|ov+;QZV70H!*FNWOcS_hA=h`$D`qw{I@Zt#;-FjTQ@9aUFd;x~-LKFC}t^pT(+ zWyHy6+7}*+cCwiA9 zAn*Vccr#|$jgWHi<0Bx}CMZ$(6&64KoOuw1s1I$QUxM7)lTOrYMOMWKiuU z{)KevGxeS^eD@Yq%q~=+;1f=RTMwi!cijGIijrJ}Tv?4axj@O}q3lf7IJE9rI#igo zMLL-{j*g;Pkdl200{Ef!*lHhLck# zCpr`#+mtk7(?N>s;!(;{gB&J|O{tM$EoZ4Wci7|p8y&|t#R6g@4_9LaN8ZRWkE}W> z3oGHVkhG+gm%AoudhP344y_Pj@mWB-xzAlhovGchS4SJH)y^BR&|(S zhS#n@D3*`zG>iI+JT&Hh#BqkBDdZTJIp39>>m~uZS&VD6M9XLRmZI%V-|%-()W3ZR^S4lo+T$a(t#ow8lp!dpk)lH%+Nbq=P*svu+V3Sr154?9^cJAb zS_XO)dUGR@*2fIg0;T=K*3DdOV%*WP!W^@sJL$MU$5lQLg7IMNx02mLd3GirR$s%F7zx;7Miz~s)@Y|FCjLT&OJ73%?8@n7#^9B{KL zKt7K=s%f!5eQ6}MbX5Rdr1qJ3I7giTF{*`!+yafiPL+BJ0@lx9b7y0IP7Aspy(ZwC z_Nt2BVv;wVhM+|;aH)$;Z+skH1|ruO=ta30I>=ud+)XS_i_;Dau42G#pZ^(DE6Py_ ztbkyQ8FcNJ3TP8Cn)ktdQAK@`l||aoK;(JYpwfFCTJcT*YzW}qII7|Bl~(Ju2toi~ z(<&NN$68bI#>kLe);P`k^6o6@N%{CyaXM?|4-rz>PrH$XM2p}|J=HnZfVl}+c1ht; zgF2dKPvTQbYk{ze&$`l;i`WqrWg?c8J35R1IF?j4#2;BUSAf>;`jXqXLW#BX6Zxj< zoribKYJv_sUOq9v2}b;y)^WIl4CU7ZqWw9TMUR!FGd`y;OK1g(DfLMYC`iXERn??L z&butScUGkZ@3L~#w9K?*_>DmO?_z+{f|X1spQz;i<{)T`lX4o`5p?_E(>2q0zv%;7-XE{ou(y29JwMb%E8Y_9Y0#eZDnfZVyiC5 zDMo9&v~N8ov^dutBXr3Y1*&tL9y)7zD2i-bvTQ>P-!48*?uzJ&mt3S)k5xNu+A#^R zB#$@M(a7Z7b$p;-z7_ zB&(kNXe~{SI@Hs()!q0Q@U9-GACD*g?=SwB%P zgRV>lN%LhNPzkO1#P!{wIdZB}wvO=SXul`;&GpuYWP^&!4dk_^j?HY%kE@|{8h&@f z>L(&aWUoZ|vra5f%-xVm_?t@jRj0b1x_@*7Yt}SXlBy}Ra^V2-dEONBt&K=*LO zLFz6(ni39W=C_%iLkZyu5e{ja*_I0(4!(In>(O0hA|$e=ksHmYZDEvGMEZIJu;A$U z<6JG^y_^*0(>&(2GJa%K6q#brMvH(oy*B?X9_`sLm)PuNA5D!mZx=7v9bz?SAH&v5 zW8+Jn*EZ!JpNl5}|W);;G%MhPwgYLkeac&wb z5)kN)XQXckJQtA{mRwth2U#Cy@v*6V$js*92zCtum!2v7jJKs!l~Pt0Y7P%|EJ}CB zsRFu(D;LBUjH|kJmA3QvyHm<%>VL4Y%A^9#ssne=>G6avr461079j%Y(BXuUDFg%l zlM(;$hvJg9PiVbAHc50!uX@Dj0OCcp*$RY@4oyW5zmh=VUlM-8W@F-TuL8;Xgu9;& z4ldoQiu{Uoz>^GUS5Ckle7+>?&79? zP(d?-kKfzXbuN>%-f;AX*TYm`P3>2gKa5)!!CIvfrVm}h7z2wmXon)?ji+xe+2}pS z$!vX<(w5!VkvMILR(S7ec{}+I^+{I=>XR<=u6m8~wBdW)YAum(p&Q+JQhOTsrHBXr zRKR^Re6;(@CpKB1r~qjola{v1wU#i8eb*?}RdW>0(F)3#i4!aymRQj@eF>GOd89&x z+YyN%ZTov{1C~1o)*P)?^xf;Un%Wp|yyCq}T>s$u0yN;cOB#R4ceuV*hYmS z9DJNc#DjCAw9oLL;g5*puLB0o&pGcl0Ao=w0`d!~aPN2eTEjNExY3-o%DobGvQ4B)FAD}QBJ8#&Dj%ypj2LhE`Iw@KE z2U4i#!Y!nEb8bGA=>KMxK1mX!|MKg5d{!~Tift&(7@}Z37`KRsjkMK*^`ri>o%5|t zcm67jp7K6&yyKViLZeZ4dZnDXgSPIN`QPhLr>5->jt;XWoEb2GVyy$d3O?TqU1QcH zgo5XDt%J#rnYao~z3&;|Z)^4xm_(vgtiIqu-}Gor=yQwCtA;sm84teGOpyCRZOyqT zCK}F2m1jQkT^axz`1NyLtCJ~uKup2h`}rUJ-Ya&YVSIP%inj?k&!Z8^@m25Il!cCS zD1=`1qJcKqOoR-F?1OZWKS|IpTxcm>xL}M|+(VKKy+0zyq>Pa=$~kDC480$TQc2(# zc<-LaY9;FTMBENg>B?NCT%~f@viHQu&!c)wJ7jp#{^`|f-TG^TKV+*}mp?L--EchA zv&2QH=Q3rfs#d>=xxUVhJ)us_7vfx?sD5H7m`g{rnVMO{TO~h7{lidbi(B;sc`roX zUoT%^%Q9c5^cm-hW2GznFvqs+mCjG==l2H;w1eL^!LgawVSTC(qL=u6R=0FyHfIa& zEmI#)+#<1T&RTrqU3K;p41n;-*9v!U+Nk0jwQDRKmO(Nm&RS^k*CpCQvo4Tv9^dw9 zLLap?7dNT951!koNnV>V4fll0)4mB(`v~xPpr}eR4)mP<>gC^d#*?nj0=w9<)m@yr z$KE^4TTJn2Ac6lGNl0M(1ZZQP zGMqmAn&i;DjiT+DDUvaRZ$3$wFc8py_wM=@zOY*Vy`arE2eH=@p$?Z8f)LIKmpTV} zZ%aW3hYgBFi5#sngy~pZM*GmGGzF=z@brjB$uRKgB)y zw}mvZ3O7U6K1#I#emRCC_22!G_hp?+ieclBBY@@+t`sr8zfv#vaXWfG@9vLmyAKOT zg9jbtGM2&~^0yyOWgmHI_gI37>=nX-BG&yMJPiq0)_?D+;vfD#`;$W|ZDDmvW9{OS zRC~jAth#~Zpr9O(Ltmj`@(2M(NMoXnU!W{QG>R@E2mJ zOk^nPo>4IgEKRxzK<|#k!2RNFbx-B3h5y`JnyT=)!Cd6{rtugf!_5HM)&^_bGZKEP z?ICEcRu}o`8`>vgbvo*zS4)E_`5w^+K%0Mr%o94iHg)_W%}(whZ#m_u=O_RGyw3b$ zfX-X--snWw&JNU~Vl6VbHCEJ5#SA&XKIKIke&piv{R5!JC2ROo`u&q&U2GWdgJ`U1 z#}@ny;D`&TSK_WZ|26@ypnpR)?~i7T>w``A4xs<=qnUd~E0OBYIn- zQBQph@iJToeUCK`DBGC(N*^a)>?Il9YAvdRhLN@NNTgy^N-cCA!X8|*&)@G{fBe3S zbPa#N(GdLkAOBkSU-#d*?kH5ekBnyH`ul7+(~2kJJ+mnn8!YJ3-rRWg8$V~`JH@uq zr77%SAbx@u%3kkAz1cCDmoi0NL5~SV-=xw{;W1mTx)3%m*%XmGXXEsRo)g>2AI(bx zm%IHi-82-wS1vlPccrfrb%d`n+!(g^xOLy_9x3^XT$gA!j(_;rln-1*gpEmt}=*!isAas3T8Qub@N9afm< z{P#7u`wUpxEFSx~oxz*4jO~%(sO?G<#y`2l`%TYQj}Rg%X@stO<4yVo9iQi-qmJGE z2R*{*?a5W{H+I;{WoPd^F*0jfTC@Gw`nwKM85yHi9G(`Uh1a`b`KyT%`{5ndTY2 z2e)@x{*}&KNF`jvJyFnF)g^LO(Dq~es}>cf#gfm}-JFiWgX5`YF2dEBXmjZe`^xuE z+>l_-vwu|*2MnC(I2hz0IZf=fA)C`QK)s>?xCp<6=m4iiY!U6{88gQpKzW+Lrj{O~ zfJ`2yE>e{R6I1HrtsyZIIT7(6*}4lMja)1JO#-k9%pd+lkuzFA&Co*?F5Eo6O^;X} zXAuXLP@iR+&Yi*Ra*C91D~wz`w)MMl73Y<2-?qKl75xgF!dcz=Cnfybh6Ntc4zrL( zmVd~T*IAH$(i*541enc(*3C^EDC(Gg*Ty?kGbr}C6eP&X%8qVtCt!AyGk`WJ*Yo%A z=aT<8p13x#u&>7f_a8g&0LD5kXMOlO`5Xl1Wa;%YIH^PT6{Bxn4biqh5 z)a!bzHqh`W|8NFI;`-dFTJ#(qDRAG>P_zWl*p}*kL@>F4z$er|7N$WIOK4R=&QbOGL@wl{QHN$ z)X!?WN`gXULKNScnPt6cFK#0t7nt8|Y1F3i+9GH|7{72Yah2&HXxrn5`!r%?*8Uph zPXCs3;NNCqQk|Xo?RnARk9K#4A2U^P+1i@%S*ss>exv>=X=n~Ss@d*dh+X>68z^<; zA9Uduv!os!CA+qGcg^cQxWu=Pe(a6cc71ntce{dEp1u;RlWI>!X4c)!HTv&T{u`o* z-0x}F4!Mf>E)h9!y_nC%I-gyW*0L2nKSO5O+{~WpoO;8ZhI4Fl{;g$lNFSSi9P>NGX zR{#7^d@mB}?A_a=n(G^v@Tk0GZKcVoW0ud5dw<$Ey!`i2S?6JZ|2CL*2L1y(AUZ#u zN6do-y0;Qj7&`A?gLP4R7v?1eCo7x6^YDvl%j}WzTgbMzqDG%s5bH2eJvd}Ikth$= zskhBYP0}Fwk3!bKo-9p{|Ku)U?)#SGCk_u35TROM_{p-xr)hP@fY^&^T>c@ag=Qr-&7M(3x6Iz9LnX)U)YJVP3}&2Xy!d6u^RCtU{HhgKO*iV4oGwFUiqY)8 zsYl-|J@8X%Ux!3k6l!)HKVFceP*Oe0I~_1Sf*YZKOM-{hJAbeHvw0b&?TP0NzpUPz zVA>6mJ96{$nEc$IYK$i!T~|5)67;$`1diPJ!mMD4)Q{!sw1=U#Ujb9-H=irlQGdGc-WA&!7z8|8Hy z|5EAkWlw+VTH7gjl)C+Tl+Bbk6pQM$82-BH85&&ZS=8%xs>yi%I(1d{B!&L;SLg9# zZPu>aF@x)OxQrs>_d5pr zppo?Ms51WLkCDRk0;7N@a|(@GY^L)^^aB&OVg9f^8T{LRle9nc;xLb0UX>Y8eiz=d ztfutC)%iFdyKh(gXo2s&4=f7&xG<-#*07Uy(Y!Ev*@7s87NqIF0iXK~`sMih9xl{R zUl5s{Qo1UBSwXJ*Q0gg^Hd$nWbqy6hV6`KQ!}ij`i0+4&(6=t7^>@*Rh8S32)53ha z1?sqC_T6wRoOdM3(VKP35Udvi+{jB(kb)=yuQkU0Oa3xK>PfRH?h=GhofMzGP?k^U zqP;r`f%;NIur}_!=(WLxjMb4tk~p^CLFBl^46qau3Dv}ylQ++kMxu%b^agFvXouT;DUfFJ5nM;qIQ;;Td zk1{Bko2Hk>b~6i-^PS%_;%XI75YfS@DZ#Pq!zBGD`@Tq*e%sRi(so+WJL}{c5+4hL zpN|2L@rdASHQ7DrTv`26XVjp!!`2kY&?Oy|Z;0JpCE9;U8oHt(V8Iw4jDGC&J!a;S z?>{!JanOcXx-dtEoEv|)xu#<-R~B*jAo>tU(MKqBFzBATwc2>^rXu9qgzT^t`nova zEV7iHVQ%>FQg?`vJ;OP6E8e%8Mfk0Z1?q1*mDa6 zNm7y1*<~&h(`!pwc&qx-+DE=%Z_#dC*&L+%T6`%!W@o&5;}_ypvXlP9pFMVSUAjwm zynbQ6qmR}4t_7(}90idv>^8{QNDgwcpMeM3WAB0w$nq_iIEad3-(|pois(a1GR%Po zU*e5a)~Lg$^%)+%?ELzZegDM-IOrvIkUOLi125Uu%FpqQbsNQ^Jetk!dI(g3?L%od zV%B)9h7rDHjBB=Vcm)o_ItOWpK}smp*W+0f@*D+20F$DSfB`a#A~((18VYt?zSOH* z>%51ttXfbtK~G+N#v&Ocak3M7&^FUr%s)GAyGFO1{}MvBgijkf-?`pt^BYT$V#IUSf`?6=oKNANeaqHFAtSC{xytImdS?&v4b zcch3DQb)Ys4Dr^98IVs84=Zs_oN2^4pfuRlyIK^twz7DBm;Ws0n3i&yaG9IZbD_V{ zhkVO2B5myU)lv1MT9g0f=f}{N%1(S#Li_4!?(1a5zQtF22Gqw+JaQH>S1|(-uwKU# za;f~xCgZe)%s{qWo%y#9m=6kf^fNIv;(S#IPHthwC1g1#wj43!n2FJZEBXeFYwSO) zzZUm0(O1-|&_-?fD0yi3czErD-ui9=d^QZE!*n?wN3gG&P0oH25xwv2 z)_8?^?vR@K%1|U*7_{r2qB=x*qyS<*`%>C{qg>GM@i1czJ}L%H>qqG|*pO7Y*g(M~TNn&15%2s>6d z(T40XN1uy41d{L&-LD>;-_pkcd=wV1Bz-GjB*WCV&(br!r{irR_lg@dNc1S`@*?e` zQTTB27b+BTjpTBogX=I|>FagaR=T`zj;U)1nLIA|=9k7bg@l2`c>b zG$*AZ*b<4FRwnsQQ`-J<+WoWeFC41g?{)k^ef)|O#`KkHAj$<*4Hie6wVsqGBLa(L z2;5pghe*IZnc`nd@XSPVJ1WXZ1+yE9TE|&aiD`aMLt|sSBq!1VUq|IZDMqcfqw?c! zqojySBu|MVuSH0n*Et(+r}PzI^G|R*LlLc%)cjDE7LPx*{q}ZY zp>Tx>ty!i)Qe3^k(&en>8~|T_XnoFTKq!ygF8bd&iPK=nzKTozKY45AqL5y8oC?zM;W@vr082xaS3~Ks|W-V}l4;&%c8T2BLocSRaUvWFPb&BlmWd+tROx!M~kAZrSjyE0&#( zT^CooTdC{Cwfhw(eG|L|%lU-E`tV5DqkB=~_nlfkD!{8}BoKKbm8at;l-Snq{?6Bq z=Q%k{ROdkX0E9dcu6jsP zx^I>l-yY?#gYp{BhCYpo`aU=@gT&}q*=@YOqzQBO9GG|E$-onzPyls@BmxzH7C;Mh z0?kE9YrNcEMaESddHCE|xNG@R6Lg?Ea3`Vkux3dOZ)4HkVfwd89;SHiEDpHvUs;rq3|q2l@eaDQa%E|Bx^&dj z=h#)zc>Ax}TwRH=T*39HvRhT&>g1fD%5gLY z^yw|Ef#QI>NWkI1ya!!tu+3$>!LY64sGF3MyIReWt$N%n=CP|0uer2jGwKHarVBj& z2gCJ}xx#J^gQBK|(c2^(_GNFLPMt)rbksE8*ApBQVwJU%;M?)%7KOS4+WKszEKF76 zY!%Pg3H;vo7vvMCo`*R!zu&1jQrjY$7;C`K$9-1!zFr#rVx?t)$ZJNQ;RP=H`jdYr zzc`)j_hh=r3H5L8F_q}zpi0rAPoBl#J+dP8xw*f}u8Lph{N!9nuXAjlN{M2Hw8D)5XTifL!%cz0(VfXF_L0=VM zX{7g&KR!#jTNV33vaO_zR7H7NJLN_pY1__3;M!-fWZHnEB`uDFOV-=s^Oy_yZ-F^c z5=(063ILDaNdhz$pHgLKOkWd4S1;Fe4GWyTD>z8`IR!2AlUU_CN3@P6&Q%8M z;%_aafj1d49kp&}7rb41^2|@x{fg-#^Atl7>6$H?ZSnQb?-$bV{r3859r>n__{(-w zy44WH;iUEj+WxtahW4qgqd)F$rIa3AkL^_E+8%$#DOGlyTp8!Ie2IJ$6CvR8wG>Te zDSUc~!Vm(J$feGiZJeni-5?HvpBX!Pgqt3?{-$Q&wN0*?^Fe|BUs#&D;N^Qc3iQX} z3nl7Tm2}_s;_I3#14R?@=WRFh?uXjT^&y&l;D$tx*@t!B;f$ZEV~|HSHs_*C3K6E$ zuXy13RP)U*2fINH!3}+Zk5A(>?Ut{7f{t&Lj6#tN=&$2Yn%Y0?-rrBUcD;1P4839w z6+lMBUr1#77A14U#5h3gLIEca@@c@5TbO$jnUT2`*pmXz#cBD zd7DG6PMmD+>DjH^H>wV11f3x5lW?i;(QzQKQb8DMZYa{VInQzm>SP)ukr;X016=@~ zItOH+MSgciW(J?{)s8Rcp6|AknQrTVJA_s)MaBDWjSwO2r^}i|^tNB*K5lm~gu>Q! z^@mmU)>A+6!TnU*r@e8jO+3v&&m$GSW7^K2tmjl~n&UGrcqmYfOd1|tkiQNZkTI3kaAEBbu({!T_ zKx#A4CDeeydw~T}tK#b#z6&HtH@9ts;d!mj!J@tY2tG{Ek5Dbym{&qBs@EPhzhhr! zk5RlAbD!$Q<8Cfp_%>u$(l91scoGR6@klnXW^3jv=Kl8L{-(5_A}?no`H>q!cSZWr zcR}i!Hzq;DJLke>R%Tw`6y&=yyQ{_3=v}ff_r~zoZlD~sB~|UI|L&yMsrgBK$P`Pi zz8+}nqtmYMW3s4vX;=$r>aSvU{JchGxle)=;^|Y=AaJbYiXs4fKC=kr*-YZ-^S}+g zWIz@AGiyJ6LEGgSVB_)Obt&+xmIeB`ZAVK8{=UOjQQG-`j${UWfq;URdgVFYDY!%( zzY;A{Mr;!m;qJ$>MPsZCxmZj+*gg`VZVcGfdqW*=sPX)#vvv`|S%UC$q?GuJUl9%1 zUKuBc*zVis>meIwfK0%7H#Y%N$-v8o>K>RI4W8Z-Hp0(C1p6fR!VTh)!Cg?}Q z)3|IB>7^c^&VC!{5F0kwocj!jsp%PU>jSk~E695EO9Y&<&%8(7f|Y{vw+i0UZ~3NHb&&?e#D!r{bDlXq7?ips09T2lc}{?1 zicaJBOIF_2wNvNiquuU)p?9T4>6Y!lPL2_b@!Pc2Br=A*pP7igypf!ilf-zrEv^SD z<5QW&%isfbVu#^|`AP4yD|@bx!=&wuSd3tjbCOY+`lY|3&<(ki7HtV*_<2MYZ?d5e zi7eR;fI9vO3;i%4>)FlHch!rtv}LoY%!ff{BFR#-44EA!d&R$^U#_F z#xO-4z_nxk;_V-_q+X#q{X*wcRYf)sUu-pcsGZ&07s> zUsl%LzFtI8`&ov0iy8ImwmQug+S5g}@NDaW5C?lP4{QEdS=H5Mlq#_BE-78?rjw`r zokjZy6MR`^(qIJvzW{)5G9O=F)XCt_F{NhUc|{7T`C6Mv-V|y`SipBT5sC$5bi~BO zJ5!awS|KM$08u>2dC*g)`l>1!H8Txd@S+cK!?D981c=*Kk|wk8mdLqom>s^)fd+2~ zS1RY0yJoA>R2|`s2kUWMnZ|aOi~KKr21vp_d$t>ZA^0`ByypR{1RRj`?;b1y!&DxW zw)M7)Tmlo*XoG>VEHc11(*4jhy>+B>!I9tTE0wNEf1r19V72_eq25lkr|W0Tn^pI2 z-@Lmjx@N}vfY57h%8S0z{C>$6*fx?-pG^@QnmMH&Y_$7(b;NZ z$krEH489ilyhJxzn>7>MJxG)y%uumTt-Rtn!Sx%idEgYG*?}xyUh83_tW<%0%kyYJ z@Y{{n>np)`sDOS?rNz2>qlc)g-zqP@QIsbinWJ5At;DyV+z%qo4`{Gg(K{;R{FVTrC;3o#CVvLE^FHHEc4_W!W}jCe;>7m_=&{pJgR}t0O*NgF@Ru!#>R|oybh=$=Q_wdpV0K>9n>vs1rf+PMMQpC+M z@lbM-`Sao zm~DqsNA2q?(m~_n;Y#1yyLESn-PrAdkgO@cZ>mz6?_HGrczRG#>UHAad`$Z#$LJuy z%l;eLlp0wuUzIOTsm~(&;!r2^) z`)_F0y-$BG@uTe0<{HKN=C=PglFJor_`4#xQd0Nf_gv%x;#^jF)hj~cJE>q$RtN4e z*{mrIsvtXY=pIP=AFl-I!R2Fks4R!t;0yRCL(l(GNH5g7WVTU_qb?>-sbF+niW-)x z{%s)lQ*Y-8WD~#FyC`)h^Oi%=`4d0`Z|bh3kPd&;3YdwXSHOg&fM>xU+UM;rM!k6h z-FmaOTHXR&HluUn5yI=B_ zy?`>Ni_itn*r`5^PsQhUn(Ba_-6JJ|bd%040O}Rqk2m^#RqV0}%HnPcvII~Vig#Q@ zGO8uPLRBHH)>}U153wP<`2|aF8#pJwZZGP8Pw;X9C0 z-i{jgU9duax9LMqO@|W`?%^yNL;gv%j~nXsHx|GAxGZn-P|u%uM)0cMXV+IMosDpH zi48q75?UtFTDr+5eXaX8Xez8$w6Yzlb%9(iLz?7=D2M?M3K@3;5k`r>c~pJp8Ae*R zeLIALFdO}rI|dtu?b82o$$mt?sva7C031Kfv+`SsSo(R$&Z>Vey-cv zFgf7^G^>b9P{rGoZRSk>cL>+1?4@9CLjsRNs5n&y#UG3B6-HUL6k{6gl|J2LF=Wfw zn@1m@sKf+Ud}?Q$Vs@qlQDKt|^Bb!DOVPvEj<&Z9irLKZYtsU*i}75SZRONrjW0{ zC^NK4CpB`3vs9};hc-ewYsf-Lt4(<@L^8F*oC`!C7wE!!(MxyBe9Jr?Gkb#}uKShY zXd7v(|D8>6;zeu7>E_uB#&tfo`_6bZQk<1_cPNRre1U%+Al}%IiWAetVel zFxme5sq0o|bt6A`#)WpjjV9~a+%Y=X_a?wy>b0tZUrK^pXD!Rrot+|t$5{OR$e@v? z@MuF7J%S2QcwX>)LWLx;dFw*1aP`fX?H7!Xy)P1??JfOs;7(URB-39X>L*}NTo(<= zo)ae;T|OoPEnXGnJsydCNUFtF%lcfo?6FzzTd3UEqMnX#!Mr(JRt@-E!16gt)JAjP0HgCI5dv~3#`%}H$D^M(dYO^H^Y{Vg##JYu=99E{Sp<7N;S0v*Xz6*( zE6cx+EDQgZLXnlA5YaXQsCWViJ^Vsc|4m7EVqW)koiW>4GyT^00_W8>hL5TkV_Vx{ zZ;e=iVfoE^8pfzxJ1|X1i^w0y@G|T1Z5LhT`<`a;%UtaR5-&yrhTY+| zLmmKMvcAuoyb-xxZGZ>NdgOw})s*PQW#Rt(ed_5{yC7_j*@CGEzI}L-#_@Ps7I?zr zbTtH3<@NLGx&Sq{V$YqtehvG1+2%g4Be!s{1f7Z5VqTlxN)X!o8Nzg_q)NYqO8ulM zU|?^j=W0>7tPnCN7f5-cV>Z^jF6!KrF#68<;6U*`RhMl8oF@3?7A1FVJDD)j zkCK60?wO&`^yf;x&hwBjJvEZI08Us~&D$dyd(9xmq@dSX{vLPJ1umk)bosO-zdaN` z=oj#aqsw>>QIqHQ!(nwCaXW-S|H!O31Ddn*P+}*Kzcxm}jW4d}#lIYKE$rJ?r#~c7 z4%w?VG>wL1KI&xcrK|&vq7Ge8RcD!n&3Vr7$INwG+{$$zpP4qdzQ1Ut>YslGvd2tG zm?GdPCcHf~CR|tX8gl6)-n^T75t8Xdw3lj_52<)WuhGLsJ$j*J6GLR<`4Y^;24b`4jaP#T1Ct5$3?4oOpV?;6jTSe*Vz8J?{qa_{DFm5+i&yr zOQmi`L^ko7*$_*6CC^#4ev-sd5r8-)a-2Z~N zi3z+JJ5Rind>}G0DPhW(zvSU3c*R7=x_&d0bxY@a14{O@4#<<(P<$j~JIrn)-#hgx z(x9O`w9v~SWGBoYR+L%ZiDd^K!k(wMc(U1_D?enFPE@_aMIp9Jp@s5Cr3^q9Y||D>ve7|;P8d*cE%xWZ|FZSo| za>wYYV^PG?`V(7(HgAP7aruR@a9dyzV$Sv1trz1ruUv+xM=d8@p{mP4_!UOhoc^Yi zN>&bzALr4zH+F)N2bVrvMY3J*g##ih(PNwT(SQEvi~%e-dYEovoeamn8o81 zshm&C-}zG`wtM%D@~Z9Bv7gbZv2lMEJhFSV1;*4kZ+E)B8FAzWdK5diuk@i8v(c83 zIcTL)Y7N$>0J*hnnnt`~Gk!<$7N-qaXCxO-n!7tgv}Zvz+^4u%h(Mt6pW8L8QZsCS z)r^ZaMpD-opgKDbhZS9cAsa5uQ|Whwk55-~X{vrVBRFd%67V=7n@XS2Y7FYM{Ef1< z@qw*nv{FFAK4na6{Pf-?12@*dpuj4=QG5*$7S})2Z5Cpor_&kcasx;HakXKIy{Nk` z<^GE$9wkG1weCsae}B*+HNTrjyxVnVBd9_zklYUjrz1~D4=7dXpxWcfY?t9kgL;2;xX3-3zlK>b#JV-K9@_80L+#BliRXB(a&}y zD$VtGb0_h|rKBTX%f-E~39VXI;LXvmZ3WhXW{|eT(BbZPVSqK}VC305ZBwha(4Vc| zTz(Wz1pwHcUCcZEfk*Dl#$csVv=Z%ZVQ(zuR0L7(W<QRf6floQOhfiGNTlKMQTvZ*6xN%HPu+pIsM-@! zFDAJ*KmrRIic%9c%vmxjaXC0F{4 z##tk6(p&O#k3EdNbv_u%g$F^5cN1|)=-M(Wh0Sv^w<&!0K8skZRK;z{o8Lc!S8!iD z&{}$|p7>gyvcAk-@r!920cXO~>Erv(O3m)>b|Hvz9KexuSG;V*Xl;yWA64DOF}bix zFdZE;n9wKvsV$wvw6^9?pAeY=ZDjRV^lBSKq)`@Fx5;gNtVP!TlkDFV(S*bL+*%5J zqA^hfgn?mwai9(BarO=}pw!`Cq|gY3@8JlK*5-e_v%YUV`0YQ|f_P$W0@=r24X+uQ z`T?B?L&DgmQz4#O{Cb3M(u~KmuaO$ zj%7wSLq8ftw%RB4e%moGmC|S2L~#VF@8>PkQ`Jp}97sJcH(J8d)AuI0c9q%9Pwx$= zJUPuZ(W(qCeUmOA5Qa=Fq5babt#PF+VoT8bC&X!TCb>+)yz^>9IMc@f>7nHOpNH~s zihfg;z{Tdm3*8EP`=8JgNe@u&mOM44d-g|4VE0gr&O&{S$FqgiG(zu$7B(b0)Owdj_Y_KuTgHNA$WzYS0CfD#*lg~%;auwG7lf^y_{<~6#~Oy)^A5*AgFu#)4zsrL#WK zRzyG9!Fg8;SEt1HV`?=46C7HSKK$yFzS4bjK~;de*}LNIh+S|Aix5DC2|7FE)k(fU z)SxK|U+G_`_?2QLndMY_=MG5U-jZ#ktZ%|M9FV`DRpue2l;s^gC8urviT&4sBB%6ZK04VO_Uv)JT0>ebfSME$4_ z(HI`l!l||-|3ni3HJR`;9R2yYUg|E*hLr@COzMVkElWNaXIVM90xZ-vsge`pxV=b7 zer#~j7JP)%Z0+o?7fi@_-Yltap$|+uUAao1OfVu>MZ?gO+JobroSKYn8Pgh@hVll! zhr>qDf)&t$L^oVcs}yQo#JJ_^=}2|Bpm#X8K`O*^2*&_SsFfT@th&g&G~%uvuweu7 zxUYCMEJ)Rxf4JLo=+1EeP|O_$6IgZ>;UIqN?EP0#swe-tgozzZ{)umHreP)h^!(Sh zJq8sgMssJ@cQhcf_KbQlbl6pju(XSHepar}{Z4Eeqj5BxWapXAw)+H99ye%M&>Fj1 z>O5rKZY(Yy!_+8Rog{b{M-t?8t;gI(98=0_ubUc8V1uDk?y=yiz2uR6;-!Fx|XFu>;O{7FKzd%{q+Mrd||DurI@s&B9F4$tHJKFf3MJr6;Tx!VYLM$pN?I4K#TX+X;y;iX!T*8GM(L62 z!sRoH=QGA8oZrrzLffr!Y;c9lVPvT~I&BinASXc)Jl1XRZfTfN(hzymzrn%h_;qKJ znuzJj%c~k&5nngi<;FvLu3uU_p4y!xG8A3x!MX6(_Ze1CKNs8i?_2M_OEiy{{Xx=g zHu9Gkc*1jNR~ARzanuck0cF~**$-tSo>Ig}P=VL>(kLKt_NOp}Z-9r<_Gr!%wibC> z6WVHm3p4QBTGxVnd1WmzoZ*2x@Q{Aju$|=$nPS|T>m|o8?hcI{P8x_th7pL(+scA# z0diKqY96hde_1bx{;HXPjk?(eTA(iZd(-i}n0R>O?>`^Ozv4 zS3#t#m^DpwD=?9@`(+fOlV<3{s|psd%Lw)n-Iz;PYbzbD_o>KL2fK>;kEJ-g1&*c- z>2M|5i}<1d(FqMxJ$g$Gh@@P|XVkNYcjOrVoeTgyd-BO;_>s43xi#cRMPV{cQTiu0Xffl!MwFcix&5l>| z=|Byr=6LSd4~B?d!_2|)WTZe(4c$FO>XOIEK~d^+tpwPm7fqvT-Ze#O87E4^*D;V& zcU@}-%gdX=*-$foBip#(#^AYc^dne5ez~A>vdlO*u7(PL0Pt>+tmXxBIIPwN@l0`-_Wz4HMi{Kg-Vn z?#|wRuOt@Tl#B%iQeLYbMvo7CIt;}?wwut$oQjSTrqq)8f5O<64w=7`Po68S*2DHqKGOr5S~etWy^?PZ21QQw|;PGzqniHm{U>t{KH3c?$VYdL&ZV} zCaB+SeuSN&(&9>0kpu8O>D9`usL1KFc7X?FycZ@q)$TYr8BpmEI|=Ovt7f_e>tr~y zgAXc;#5Vu#5PBVEgtu>ujSZEFxh?j3KiHn|e1#Qsr@^e87e?p6)~4vQ06TpvyEbHc zdXaLqLIHV%FlD%ZSHW4yQh^D)i7>KK@wK@Z<;&NH?5j9$+X|#fta#Q6A!3qKlO@hr zQo}kE+FQpyXo=;3de&C(^R-*pGE?eUGdQ#5+xH1|-6A8l4lD(h$tqJ?E>^vk8Ed7P z@FRq`Y`65e1m=0xjcYaBi`Rl!?UM&NnMH5ZVtBF=oDa2z?>w?CzB1^sXF5xHmaaoh zQR}VbZN2vAv2-LEQT=&-u(IF;~dulYtaDeaH2xv!Y1_;j!Ft4pLp{a(IK zu5Dee@^xz_-ku*4jwPVYxX~k8XnNLm1^GSxx)F9i`>MzV*|razyDARNJfl!Im&mTK zme0})89t>aUkT@^B`c*XG!>bHsZqUXTh06{*VZUIQ*PE@1{8$dqga7Og54Y^(UIwH z<0a{cP<*0E($%d0%)~05Jmr7g4=eBz47s;|fnDustM5Dio2Z4rmAaX5mn}Sf6Y-?v{rKH3f)v;}R-pS7<`3dR0(_9*Lf5Y*@buuqlKhNTxy;?Ru>o7E|Uj9LaO(}SpkF(aXUISYRiq%|5N3Xq`y%6 zx? z84kUcm{0fVM8t}shsMNOT!blPkaXXX}2**@;u-jp0FY9!8 zO6get{lve*Z;FT4FwBS-5?i}ru4b|MqiW60>s86y$Th=9zc!`zogflDb7o%q)>;jt zVu6j((rCvZ$Ai(26GJgqg?h2|pAC^Xw=Pl7RU>xB@hZs%yl(dCZBa`Gj7NOE!};2c ztj&(mjzIhJWe`#!p4EJy?X5Ct%<8x**SgOAJah z4k8WQ>1_rOq&MD%9PSW|O|zAG=><1sEd_@4k;W#Wg$g1)&EJmI$_Zn1O6@wTfqwrC zHWHM38W{r6>Y2$1RqPXT6i8gh_1TWr+^^9v(nGQlUd&4hMOy!A7!1h#;E;uDCBqW9 zwqnwX35f*#6iWJ(#Uyscp&58DXY3q@bIGVMzx;hLU1(ylm@34-3)Uo>W6C|`}x#_Qip;hEu z-{tmdfAsfJ;|F-Uci2NF59Ei6A5=u$&U3d`m49wrUQwXuOb54L{y?(sd7aBdB{W00ui~<*w4ejs38|z{; zxoWjqhMtf+MFc%Y)sR~>)mJR^cCAHE7%9u*t9Z@iPr*WSf*_&c!orbGuaocW@TtT& zK!P}@#1)}jzG<1hYlsdrMt81v6&1V&Tpqxq3} zHPBz^g68kN*QYA+HI~5c&-Oc*dMCLp9ZfU0>yAwc4%Q+!n?0l7_dk!ON_E-$SJ*+r{WD<&G>_CZ#?RVKU5q9B-w`Gx&lRNw&*lOu|;QJ}0JL}VM z+}erDwes}AeFQQ^-L>Cd`uilpE?i|LkL_?NW$c#s3khQe!FReg2;BKGs+O-H2KKzL zpHsnob3xwqO@NKJ=cAZuCW#SK zZ&+SG5R#K2WgW^ska z$-@HU?Mz65K*lcBK<#)!i4tpI_q&gT%wL|!6x1iqj542;hd5Z=VU$P5(r=BLB+e@6 zuJq;7w^j6|teG$3xOM65gdHWZMpOgM5G}AClsrj)oz+&C3{~*)tpLLtl{rr!;KSoN z#Ps&varh)(KGErdcMt0Ou@C36k+XIQpoyX(g`vOVYb8!BisR{6WHIB#iFZn;rjN5t zh*WdBi#M3!(FV;fC8Eiy!?Ow2hol>W7o{~Eqa3K8?nAa#FwUuWAz6>BP?;u(sbm_> z?LJP=m7-ihg^Z`wQ?aJFdhbUKl_Tyh1%9QT^$;u?vB3ZZfQ{|I5iSyiq`)yjXUrW9 z`jV0^vQvuq;t{P&c_9>sL7n1)HpWIrhty_isVgqV#ALj|KfRs)0Bn9{HP(zKVhB$< znUVWoFSdq>Xur64vDN~Fh6^Sa;S^D)y{L&J8=AA)XojM=e8#lwxG>p8ClR^kjTDhL zF>fVFSXaBRTz*pSVyQ_bw(mjDY1pho{Bq@Nxkf1l2|IoDc7xF8o%E z!#e_@sY5~*u3f`xFA+{D#uv0k_mv1vBaay{Q5qKT_BPn2|2C=8r}J@Rv9#BsPt@kb zXH=r?@DKMLix>I!^8u-B1X21~pktdbVsZ`a%#nO9!D#?D@_AYs!DxT7&p3~{>yzsr zRZ#V=0S-@Pf%DpkWuG!$@4lO@{NAwU2~HPwPG(Z5h#4#4kgXZOP__w?rK1!Fzm3Ke!k>cWD%R4Fm$S_J1d%* zbP^WkJ#UK;JXZ(VSN()>DNSa0O!9q-S!AkHQ6orchP(za2e2oOd7#TQJAE88i+O@x zKUD}9@`$5B+c}7{zCiZqIjB!)K1;k$P?Yxchq0uGO4eoMeJ3{8?_*{)?S-dDmL9n! zo-H`m^Q%{HDjEb|CWvs$pQ`q~DG6QA`v2eA?=eKS*9GWK7!SiutUS-EQH_pe?9CsG zGmpY2R~)}r9-aila~}(AW<#Nf++C{C_~xCe@VK7ujnx__wmD0oLgi=!ty9^*p=v#H@+zn)vkKfG3?sv5lrL-Nqb3jFWJ{jbSe ze`R!Ak}=&xmaZkrwOvX$wvc@0?el-KYbO$Ax8Hp4-Y&6z8zr9YM=VMXkqzc*`!2yE zLZGLG0$K+c`X=h_imf!RyF-P(xKbfM zYoW`Z`BVD_6_Sfy7GEC&`+h<$TvcB}e$xB%2tj zo=s0HPA4l)@L62*aodLEH9T$tColc);vTaQu(e(5Djw&=#xi?p{W2Sgs_3TUjs=-P ztS~+-EXKPIxS-Z=R?|Hefi+e0feOu0ti*j;2A{{z@0@i6VE5H(cIXp#Swizp{bl~3 zT}|7jzt@p1d&|A*U+vLz*OAR$jh)&`pV~S{n0I94faG|)WHnyV{;Qt;6MoDUA7mz3 zoc0brA7Pj{LFL-fxx-WG1-{#i@iVSyg$vgfC>@JBt)Y!bq(7b~tR1%^#4l0--Xb)y zvWF6gqagF`Fb+;Hn_3@wd*+4N^AQNJVN~P=jc3GA;M;adYK0eAcHgkzo%WeOqP^Fc z)oXHg(KrNq;a}k*LqjcX^-G<`zv|yQST6NNiqGJhb$CEK$_7=t}~EtwBQwFg>DZ$Et(KLf1Q>&Zw(>#6e6P4P{`T4!Og z80npxx4${@nqbw`Kqq-*|K+o8J15#g37VIsLrGM%A}eMN15KSVG1IJ90iPK}9aA88)Hi68a#h&(uaO)$ zFmaEddFl4BEhO;ZEQouxrWDG*h$YmbS&)%ZDlTH@e6BZOjg-eb3Ss_NHK5VD$0`au zVn<+0X`|2_)LfuExp8**%$<0EXM=Zo+$~{T^nX-?`0ll9)qFYOLh1Rb1L z_Qo<%3^CmlC!;;&G9mQpqtTRfD1pT_jGPBFTzl2qe6NwlxQ+ zo9QKA!}Yj0YDi7?y=U0bKt{?IkQqzzIFZ2~Y+u<8GQZa8bUM}u0>RHcq(sT9jgz9@ zHzABRs-An@qvP<6shv5$<`Lh&Znv2aX&Kr!8Tkhh1rde3KbVi$o2&|iY+~9#9%ZOr zcf^@CXA$;|vnm}1HRoxJ8Dwj~rH4_DiravutIQT0tpStmrN-80?%l|ip4yig4~+sp zM62`Y1;8gk$Y$J!$gzLuhxFr6*P0)c)%+@LhJ^^H(dgn%HKlTBUuKTObMtJxR2z)% zedBU5nB2gm-F-w&c;eS^{b{{ zR3c;5GS`XCUn|ExB)P!9|E%>!_II1K+pCFFon70~BW|FGr$5RY9fBe6Q$GagKkpLN z%{ZRa+*sfC(L|4Mx#2Th0bEOsnA6s0Ye?PyP>Jo|}? zh}pvjC5c;yrT;M&g1Y;XtyKG6`M>s0d+}J1=3U7r_w6RZLu(CtA|bZnjK{jH*fZCh zKSnRJd@F|Kgb&qgKOn)6sVNCzjWgzMAEPTv2p9MBERB#MQe3W6d=o)YT3NL}6Z1=- z%Y30dlz}EhVS-wA8~x?XVF7p`!IbsREmG~?KdIgUHcGdR5;kUG0OnX@Vl?LB%PlIK zjUGZ6CKuL6nt|uks2+9E!q?8~L~i~7ly=FVes3uSZM*)|0|e3bSVBOq>FW$p*38<^ z=&AV`?6-16PdjLKz%|cc_~)M?rA5&ab<4^}6!=pfI$(@AZEB-nnU5I|Y%|IsX2=QK z4s$ZRzMi#O7pAf6J>Gg;3urLf>0)|%SkizPj~h>+x+mJcFuG028?D`bK0!-;>7Ta{U!y-!v1z{JjI#g-e`E-FTs6fR3MF5 z{8FWb?JZOu%>!ysI#Mff&+a_Pr+u)ur@IPSw`L8bf161660Zm7r|N@P%764DBmNS= zGmZ$li%|9BmBnnBaJNhSHBp%JG1tam}52*Ncr*m*Zcg8fqK5A z%0Y7W1?cv(%R`EZ&s@pzwD(=2^aQ$R#2)ER5+%4gCa3+g9pN@~#eQFL8$K{?_Vwi^ z|9)o`%D&-3{qKMKf2AOekY?}T2VuK-A_k;f@_$_+jSz*6kq~TBD%*d-IGEJBsz`0d zpMinF?Y!I$5P7(1_P57lI^=yE{s2>Tvo6zKSLrLQ;Aa6y$T|NT{PiXxi=s-y+P;eL z(j~*$oZ9bRag`>OK1D~9p5#XPSuiBgJf?sAm?yk1!ez+=r-X1Qc$QH1*w40sB%uAX z^ooc0)Va=F&_GC{Hrl@E#Ul6a)6`w`tE~sx812pT^W7v$tMd@LK)qLdH7f~MsPL%q2?nrBBO@5pa)qd_X z$5ER5JWOjq_jLiF*p7>MJJs@ddr|=ke5WRxvA%P_DYx2I$&+Aunu)ma?S*BB z2a*tq*wX_{N)CfB2CZRh@b@6~%7>Bu5gveh9dNr^l?UUv&YE3>X|*c%P^QL}Zfxq^ z%M>NgPKma)){7<%{+LxS1kS>cW&;?4xQC=6+75{>VsCtwsTwcrutwD$05&QjyCGQ7 z5w+tI<93v{;ooBfLP|+2Sm>ozo$r%JheYgoDn*XT%?ER3OH6TAA=eREDXM-a;GI~X zYaY2{_s{XsaA%l1HrcyOAuDrR=aO-m)mpJ_T9woJu+n#hdcpr`$ z6@RoSylqsaAsuVl64-&R!J7-*&8#2c$jVS9d^!Zlx5)<%jJ2daxvz9DGYC8EEFaYf zW1C1+PvMqs6D({UupS4L(_wr>C)h^ZkAJ$8fMe|>_EXv8y?u+YK5m@xe#BVgmVoS` zT6ei_TH7e=GBdJkqHzzCL0Rpa2GcJG@l)JIA7q1EkvlEkuJX_sbmqrz@oTQ|`4&Qp2U zS5_|lY6Q8uKi~IfJcn+MFhC8`n+e7b=;c@ayM76SZ({%UsFVeR)6%%-&B;QfzP~wi zKsQguTy6L!F7^pUhtKz?O_*Z4Vy&P>xJ8!j)W{?#x) z&={lVC?HoTA6i!Ve+(?vF6gb=f)?&BE5oZRf7!D`ztgkV4W)rp-c)@6twCf(_MfM! z#KSnx`%CCWLQ#>KY`P6)h^8Z^?2&1P$Bj+_pgq?>*)?uF$aVWPHL=QZHN(VrrYFQY z*;?Repr`-o59PSUSIPOz(|+o2#I12hD(&UMH(WiVWnC-Y1lo)R$-uiG>Tc6=17|WN z>KS`m5@#92Syh5()8J(MQ{E`m%js76OJ}ODogp_4x1ULu)GLw!Pv}C*k1Bk+ew6<# zQ>pfn?%s>Kmd1y_G4gHxy}b`NEpcbR`rAv;#}tXXeEB zj-!5GZ40!knrx=S2Fr6hX3VR?hy(I1(ya4J$vg-0u8_Ep!nYY`Rao%%@%TcEq_)gv zKZAjJQA)!gMc|@)Xm`GW8ZZu{nc#fuBMW#9!KNbG>{__MwI%iP4;m?dPo&Gb8*4q+ z!(k0rhi*5;L~(%9v71i|KG81g$YaN}dGpYM84 zV`K_2o!2vcYf|HgHeXD>yuEQ{7iew=?m-mbn({ zNWYrBDOAsT2)hACR5o|@ZQxY2A(uv*TAQXO-7$ey&Hay`*geI|i=J~_RciRTKIJ-= z$swn-1-*m=UmfF!QCuDqLEq>))B9k?aHVeal1!sI9G`fD{X<5@Zw*WB*+Io1a$t$E z{jYbyT`s!)pOi#9OfH^f`nzcB|9iKZ_%DNr{`F0yi5n`~@lqP}ze5RW18S2XjGsY} zi6bVtqTS_vxD~N(u z%#Zf}e!+Pq`W8x)q(M2)zvjJT-@7(@(%cHGeDPxMezp%Ticl9jspR%X%iM+T(zb&1 z0umc=$3j@ARNKUY)a?{rq{L;u!|C!7tNOC4ckDih%GVGvz_^Bs3Mrw_3wYWi>~va$gy}kDtn7dx>RZATs`~!$syoU*o~jS zLSgOp5Hw;0KpY0zel$!Fy+XEQSk2z#P^$PWd5u7Cj>=X=c5ld11-y@;>^d*AVy<#N zy2@|I4bVLdJzv&aRh*U#{;_+22KWXee!3TLx#LafCTVm`E zc7L~Ox5<@kR=!ordOT?T-jMd^{IA!_#fEXxP9-qOq>Oc@pl}4oQP4TTI^U!8)+hWY3ZNnx-Bi9rTfS3@PWsRkA0)90vG11 ztv6ave|-+N!iWiXdjN5SapdHLEN&?ux?@0mDIUlSU_;Ia&Whh%d<`C3*U_gSFcqrk zL6n~QB$3jXBatR?!H0quS5an*l>UL#F}oXF#SJwTOp#MXo|4{$Cy*rh8BULnN2urA z{SJKFIv6?(;*f8HY8BF41k#8Rs-HX)@J=U0%{NJJ-=hycd)`(dMp70W5~sa1+V}px zBlVIv%&9{u8$(>Gf89$a;Wc2!W9{4omkQ1)fTt*sYJ8M>mg*8FnC z%&D!fI(Q*`TV)Wf8KSb6n@p~hAK!H8XP6mmx5E0bp(D!TGOV7?Il70wXvr-rphfnzItiAs*QW3dl|{$9&yC$68? z?wn5UCM}jB^*no=&#byNtN-AN*%qjM0X)8J^uYck5)-TUxgJU2b_v!Fi36<`6Izt7 z=B9`5+v)p$E%;f>e)`QNiMnsaNjjVF`Ij8s0ClVlF*SGG=?a6E8lDKIATdKxT<1Yk zeWxTUL!0RR-a|R zzpqKN2yjFv>2mlbS6UKI04_5UwHi9_TJT7FOI>c0WOVupT$38#aLI{=mh&!Q!R*#V zwEK@d5KeUT2B6Uuk&I>%LVhvQ)%f53}VWN_@OC;v{2J@qQeq@m!S;9h)!349wAIz$i9Dg&>CuS++rCC$lnebdJ zRItE!lR=Byy`$mm$^6sTg37Gm)BZ!8gzwhP@5mZy?N0ULGa#nyE5+i=Xb@v%A;ZMRj)UM}7 zyzpXT$mxoD)N|>U)Dw!oPc1mn-{lW_IO-sMJdX8HOgGZ%W}Sqr-|#<&Drh0w0_u;( zkLs-AYgmDGA5o!IV8N+KDbcjB(fUb{MNfG}#lnZ>@CIx*DpYBC2IYI0Hu!#h?Ol;WKuc70M&9z;!j5~~b zZ{<BfsIWkO_$F8D?_X_=61be@I()95x zGm-2jemEpX=Ah&?e zKnuT|s8ZF^q>X0@zIwtzipo3M2VeKHJ`6Y;j?96KjjU%W(-!#(;P2|Jm%=YKOB!DH z(CO>3t5hOqOup&I@6c~mU(s)J9Y!djph`t@$jfrD+rdU@9ZG9MiRcb&jlOr*SdbEr zL*ju75}TJDS%hGGYa0Psj{pV}XHYz!pPk4?&LlK@dbGYNvIw$=L)UQ~$^<%zS9f2g z=@sq9&B0!(Z^{@J@p*e)9;x0p zg};!LFrQR&CZ`~8)8lBN=|y7KOz?HJO^(=MN|Uk1oPmx&wIc@#=9}9I{J;#%LJ#G7 zk%BIghYJ34FXHS_c~*|5|~->@YeQW&nFntX@GEQkccs7+fPyp;6%gqlQbX@8Aniv7=~4nxl`19l1PQ(O7J88$Isrn$8~^Khzh-95%*s0V zo_+S&``qa0XeY<-39Hn^9B%=^mu^EPtdt^kqhcTOG?Q0)>$L(@8RU0E<+-H}8SZg^ zvx&Ij?>(j;KP+VkY!&CXVrNFg6odGX`gt+pI#lyAI#MxjCPrTRr10AwfJ9VO6RsJ`Le_o)$7^9QK{YAT7dYIS zv>2bixO8#=2b=cr;8}Y$=ylMV%~*BSv1uBGS$B+<9|ql7{+Nd}^o2h;X5jzN0_djb zAncaEtjTqxO!QY|c+)-Qq7NxdVGnzYhvGw8eu|gP=Qf>vJ^Lx&dCuhlc3Ap)#W!TM zP$%da>9*iIQ3yt=W4Cs65JDT2*+_AkUqkEOOv(3em2--&_E$~qzx^6U;Q;I9&ccMd zEv<)h1_es_C|Zehj0*|ITcX@$I35$ehn!-$%%gUCjcI&3pu$G3S?iQwCh>pHPNLnw zV)N}rZL>9UADnFb9v6!a2|p{con_d-jr>s#H0)DvCbEfHD!H(hc{UU@PSs!_tT+oC zrx<_+G~F@kvwq{S(D_0JuC9VLKBwLwiUKR}DfA?L$vyY^{B7Oh<0iR}&$+0bfCtlK z%Sp=Y&A}Yj4ZGwXsEa>-tTI3xvHZ34(^lBfbJlb}+!w^b=V;zNfn)9QKmB9{`c11M z#vBy5BmLLC`m()LN^{eyTEk}keixlCHK*^1&o)bD$>H-HI5O9n_^0ZSU{;mKtP@rf z!q?NviHP5eKK^7g-R?}lq69kE*C2P|-CaH&wE@t|_*K7e<`msjGqBIOsJlCUn@Gl4 z3AX_nHuT2}B#E_Zcikl`L3{73w)yPJq7sWWQ&=)z*6R%gAZ40AIISYQ<|^cR6K!y;3H8?$ z=%J(=FBZzFzepYjsoEz82(xE+&_iC}jxTrLvTq|vg?EgnRNv){y;&EupQArC0bSnO zImo;P@hzv8aprSzkW%k3XdX+EcNDS(;IJi=;!Eg^;2tX$*T0j+-B+vy+Kt7*@|)UU z{$t&w-mez-`#o0LV};S?A2>9FN1Wq2amuN0sE=%=uuxW~Z94Zh>jZ&BWkx+ahuN&M zmSzkenDKuhcf-`TIqR2~Q@4ueWaz8PJV1SVquENA#O@yofnUkA>jsY7$`Vf_UR zbJMG-d}n8|A7${f4W)vKyn)bc-MLM(>$_bT{zUv^#$CD`z_*KYuNUW6->oH8KkS~- zy?#99B#-6O7D(1+$~iC#B4Pb$-Bx_r@4XX!l6|`>n)t8vW&BBY7iL53ad1^jI^C}~ zyukz5^M;pVC!L$?n&_j!1~@gGg?isCm-NOJmtcJ+Hvl^9^KL#|84%fo9?o9y-2tp^ zK;ucS)3xV%Hm$GfGa!{Do+;8Xl$SbFbHC~&Iyh;dQ^*kkul;`O7q^q-|45L3Y8xN| zQqM>z3oL(C8ZW%NbvKx8*w2+`?yJj3o~!TtM{M5XFJYc9RW<=I*P^(#A@ z>Ah}mkjv}Ir%g1|R=CbK_XpF3vH3HMT^V(FQ2CA%4r<*FN$J!~373SXVG*$?td{FS70McDJ zf+BA&%TEHS8=GXg7gFE6gS=meg^EA&ysfndt=;$7wPl5?Ehg$ET}w^w^BkgWgP6u1P@+NQ#VAe%M>PeSRfPLy$fho}& zB~(Hhbl#y!JOm8PGWyJD{AyoK+F7-Z?y_1lW;Nb@4KS7mH&2#jj`NR5K1=5BWz-2b zZ{=PJ?XnHF)_G!lOw%pG8ZoNkF+fAHCJt#)?eW9`CG}zqZ zGQG8+?4I?SA&BVpM_>Q~Tj%X^#}9kWac$I||c@*W{drdvh95qI2R>a^C-RW2Dl z)**1l$!0H?HJgD`n%+*2|IHsCAc!OFd2t@yT6eO!1>|@w^?kg}*IMVhv@deYeS=I~ zJivn?QRmzamj+S97(qLwQCg8~;_gDw+KOu8&hm zPm*wma*#HCBG#AkV_l|=lCryzjmNHsbYi#;kefhrtt9m=@j^0?xVB4+Ju4D-=l)^1e_>Zl{ zx3;GuR;g)e3+?S+N7C<+9#+!`Y*ZPinsQmAo`m#K5)-n~&f}m>mN%!r{hv(uIZca|j6Kai8>Bt}gkLS7)_==>p*Ro4#JDJl*{QTFszP zmmWr)u1LPsi2Hnmz;-)*87QHfxM<{EO2l3{fu=dQwTS9PiAwsuWN)&U*%A4nS>h!( zX8q(j@*5LfE|~eh>O02%BFRUG-Hg5;w;A7+k~xmTYf%=Zq*eQ!Vp(otC8HE9dDYI( z6OR9tjD*6<-#+Dk@yseoNqFI_V)9_ceVy#{r0rV8NZK<(tOHV3<>L$4GzvFcb=A@? zt!947BelMcO}VUTDCEB_`{tH;A{MMG&9TX|MJ8{*VHtg&fZFppf+W{^Mmb?m94G5f zZ1iW)x`jz?f_scRTXbt;kh<04Vfc!;UG@e3D1t=s9rxSd;cT5R0-dgek%v-$Gl#X; z6?Yp%m;5~|??cS+0#6^zDMTI%jA_fgSW$wD$PZO&_um<=2=o4MhMC*JQTA7w3_NvD zhyFU+ba=J%EucdJQ8maW`yZGu{zL39=UsYYd$-zKjtURUj-TJBaDbq-H}*bvr<`Qj zac|oc&vmplxEtNQsD&7~vFh8rVDx;*@3<^u?;({XZJz*sb%#DZ@2^?w^fV{z9xZl= zv4X-TKar~6c$(#5uy3W1q@ZS>_bbvv3(N2GKfw2E95!1AKS6ZJk+#T|xP6cID>WI1 zNe7RC$}I6FonsSno-l@6ENsLM`*92|9t%%VNoqS}Z?w)V-xVa<-wj9wJB1oNYE;7$ zYQp;qi`QfuaTi+q+8kwCv=ywU1ZS2+;e40!3+py;%QOABVv68$YpBWWqZXnUs=8s% zbi#Q6;~!1B#u4e zW@j6_D3v>YRjXXnr#snN{x-*9~TqK9=8D zDV)(!Gwf7=0FM27(DJoM%V~OmhZDS$#p)^S$-*Au+5ZA^JcJ0#`soAV`>E9pSK-2c zncvj11`kTU`7_-ow>KcQ%J{yk2*Rul86_oNmguPyvi8m0Vsy!29ZE&n;W#UgB!GI! zevBF27qtfd?76T*GuZ;M#Zw+IzqRZSH6N-7cG~VZwi7@lankyL+7v7_9Oq*)c875% zB6dBmRb3{0g}#`7*f}>}S{`x8?2&vi`y%l9nE?=03%H~-<^af3TpUwg2+Ah?R97xk zu0Xn41~A{_;-T#aCm-AY0_mgMioaYk&JO!mwUFt|3?Bu@Tg$u9@F`jbk{`_&m@mSf zcbC_Ov)QjpKDZ@I;;76}|4a`2Ke#@(DRvzyuxB2RDu7Nt75X4Xy1Ga$!1V5<+hcjQVcD$3bv2m#NHdKWhtHLK-6 zKWXckn7EPuc;xSe?E_qtxa=)M@CNeuU5yZr>U2eVk_r($xttq>8L)-~nP76aLO70;*dOIW`E@T`uFBEulGvs6M(F<^3^>czHHz?t~ z`^z=^gSP`Ou0)QDvj-ktra!aT0sQ6_6;Y#{jXD1D8r`o@4k{GDkEH||1k{E4p4~nJ zjnZ*$LBD@L3}(gi#?FH@D)#_UgT-`FG!JwN=(75a9#v}Y9Wg&d2tFHolYO%~;QEa~ zTngELjDJQu!enuMvEKGf$nX1~XG0gS-`ud@K7Y+CQ4{~LC($P~ge0&5w)6$t1St(T zL-fM&pg1keg)%NDU?T0x?Oq1RsIVu=i~P#wFJ$28R^Z2_Tr-EBF@(80Qdz#7AZOth+O<}Yw=&PjKe6rOr1F% zm*E*kRCs7`WqvchG)(t2)crPU?(rw&+Ao7>K;+{n4Q3W#&CEC|+ zCVd=YlMo8U$y`%#l z;@!n8!uA0WJ|75YUIxp&5g?ys+5zXol^DnJ1lE)xCLupuC@-I7R!PahqWOOWn8FN< z|EkN*NyJz;A>L@!n9H(XTPKNn+s zlE16Q(dJ^4fhX|VaNED}73^pC_tQfOMvCQ+6Hdu1aRL-qybFp&KWT_jW%8 zbcwUturUIQCnM9s!ersMdgMz@W9+0PLeJACO|O!!8oas1v)ryS;+vAYCURw3C0503 z(w(j*^Eh|u`ZJ{`-?GjP9#as`!oD3kxVlI!Hsm}b0XTpqNmT<+lffTrPURWpzLYC&gS#v0f|D4v1v)7VM|706|c=~j*l z-g061dI%wu9KX{`ydOsASAd-s!TVssL6FSmOY-_Db;nWJ4OP(k3i|3`X)n;fTidR) z9MB-#enz%%w$B%(Gu`#$dDB^DAu7fLW*GX7e2!n1$I6re71eI`7Nq5PHuGK1msEto zp+DMV=P-eXhxC*3i?g2+ygb<>t8r|OKHcxoR%->|Z}_uVqASj8X+B1i%7ZKcFD0|= z$7V=x2Q@juY!O8YP2aM)50Ipb^p5lr`;vb(;epB})Cy(Dd#W-c)lqT#gq+*ua759| z#;TL8#@eItEQIKP%+A^SoW9-|4pAHE_C;yBqQCvD+zhxhP<+v?gS{!j0uw;i>0SxqIbyrs>kVPjLin9v;cJD~ap(zDimxG)!dg zi?$i<16Euhl?`Xpd~0Kmp9E45)z8Ay!5Rir>|zx*suUekgc!bYdHW?r%IjY+|9nN% z`qY=Ei4ft&Z|B-`$_R{mrn~Tht1Aiyu0yRg?PFB#3?EzI2`_PaN~QT%a@J19L6lym z40t54dAM$VGdhiB;QPZi5D3-b862k_n_<67l1ve2)8P=MC*6Y^85^HrHm1rATPYnj ze~k(63;R}-;RZ?L;cZT6#n_Zr_;D#3^2o z`NSDK;y5+@XjBV)}9k$8{op6yKOsx2Uap;DyEH=+Eg6--eg$1#ON5lur zEO-nX122X)veAqpJ{5k+U~b=m>MJbY6mLb*uNoMMH)HmZPB_+I|!H z3!K`J+~)I$B`+sjrdJ~m6q4tM4%2q_LN9FsuTuo(H!I+M+VEEN7}RYEdjy&_-^g63 z0pO&)X2!0&os9PJ=kEbt@_HU=+y1j$!!DcIM~{;#GluNvO(Dkz3yB{#S%h&081%6J zm;yeJPO5p>!xV9;g24|%`T~aC#}&4}b~kJ){H1g}7!&rIjGb=0Iidr&3rxxU`Kx$j zdh;#q%75?U%-_Z1sObhT{F>^*0v6M}DUY`BYAw8(tu#?%Kqp#*I&eGUIR_6n>zN&= zn-LdSx&{X9qub~SKj^B-`Kfht1ZdgaPstPAFJ-hTEPjbkAu)k>pJM_p#i5%+tv4Cy zt+`*#*M|Dd7!$l$z1{S0x&6iBCewg(>Uk+aD8%hjb%ErK%;2F9%x}iF*zX$XH#of@ z2Ri5Qn%cH*GQGAQcFlAUJ^IE2oKzU=+lybq=%e93h3GWEjbjcz&9knL>uzs;zy7wd z)=myx3#>kH^vWnaJJD|J2)MS_Z`6itXe?$uleaXCzA!%%Ph~6|EjM+I=R(;lxk==# z?7ZJv4hmp;^|8ssFySI0o)4)VUfugk-IfcPbnrEGvU3LNpP}`;h0r%Dd64AfoQvn^ zc|pzhc{#cSL$hv0I2CU5NW!UFXyaGitm}Q1$8ecG@2hyF%X@h-_iptORz~z2p<8=m zHfDsS#qIu)PE`iX+$C;g=>DfGupdUE)ZlAAaqg4frEGA>^_khqM~6@nnLbw}GW-*U zLWMP4M9SF5A1U2m0G2>9ST%<@{6{rLMYCZW=5w#E{*nHXjJJ|rEYJM5#IwINt+sNE zzDXzX{IJcq>gkeo&N%yC6{2%3dtTkjp#?Db#Y2DR^_yib3Zr$?E~oZ&!@D_`YVv*? z5;5^>V%MU5{zfEIsB+BBvhGb^_7~Zkziv?1`Z>uckXE+7>2|7vT(180i8U&8Ib^{Lza4?zU26WL zuus3)NWOr1?sch;*sD2zkU4`*8*ZHl*fBKqGHp1|MDuOtZ04N)M?K$-IDS=+gg<0{ z0BHuxxZfll?89bdUIS|67tIV`piN9XD}Db`FZ3lM(f!kb{n?Ny<#`!QGs16vmd*`| zJC~Z5BIsBZFr$o(s`6Wc7`4o*b^|)dg*{^Bd`2S+M~f zh?s}mj4WM=?BJKD1ILej+FMV?0d5$kqG7**PRE*A?0zP~8pX9QIsjOPpW!m+r=6Pq z1T6Kf(+#99{1h@3H_Tz4`RanYxg68~#0;Ei#YAa0XF~il{lZd4w|Srz8bk&BO81uP& zP3VKBLr&8$?!0DfbOZxFUvH6Gmv`+$-fKFMdn0hJnZu+={@;eQ$f&l@gcYm(bqrSE znzXAslM`Q2L=$Y%pUkB9zrJt6Z!R7Y*+;k9Tzb3In)m$g)OzU74J%qEH67VXtm$qr zzxhCsi#Btm6A3)m%{Zb{P6g5>Aq z&oU#yb%e~7n5b5>s1H@K`L!hQYldysAep1^SZbjV_=xaR1=n!+k~e)m)y^PBtr zDwVpAsaQFK#PPMatzx3QIYc`taxK4dc6vKW`0RG zoPZ}ML*6u2`XM1$5p+%frU5!1kmff&jQ=LLQIIsh@3n|V3eJ5VEnPZ?GzX+Uod3b* zdUM^a@CUbG4MYpWQrt1mvJoD$H@l7v8-6B%)4@1&ohN$E04B6<{Bx*s@xmB0gt##^ zfg>ONVg%ufuNfa-T&)#qw{~#fz?T=zOIwzm+2;%)XCCNLg5YA|0txU&R9`?Z#x}u| za~YB{ur1)>w)RC;#D2m!2!Dv3k2izN9a%c1*9R^up!@9ME6BZ?$SQr*(}meDYhbU% zZTx}@j#5x856wYIuFoGqDg zeg(xZ&9jA%AGGd@TvNT!e2j*v^$1kl6k!MS;d?;vN=3bzQdt)EPdsEDztSgg9Xii2 zZM+7wy#MXvQ3OLMl+Hn>-{DW6T#Puo{}nTO1tT|(8&zH06s~U|fCxl)>&hi)t@5bt zu)0_t8E~k+oUt5$D-Yikxe50>MGl>9u5+-(EvXa+9^cs}TKcd3?$l4fo8rp2M3C=IOoph1XVwzwUk~*sO%swnbzm zC{8~Wbrd`SI+{2CrF?kyjj=qwO%Ho$@8i=8Aqt;yk(*`E?*Caypt64KvIJLYS0=rm z;6W4?zN-G&2rro*yH~oAi@Z7fB#myqvglJL^n3Uz%4V6hy7vUFvhGCmx=6!L#LB;! zFnyW8KA)1~2h6?Od~~JqTOli}eO(vaO>C?wz@3cjok*%=TF7a+-EVpc^FM4^ zI~n#L36j0$Jq){2-Qw&!Ry~Orr8#&GWd;d8*?2fSEw@@bv~|8u5Fr}eDF*WyY}Ke} z+uU-Tiqbr<;3ORptG49r)4P3{`=@uiIBVxUu1M?4sCmDlqufP153qAz&YL&WUzw?l z$W4;iD_pC3!Dm8)$@pp*fft(f4ADgIn4yN0r1{J_nw=;Tu-X*=@Cx)iG8{=8@A zb0n3l=G*LAw=}<%Bxuf^n7ikKhRj`*4Z99?(53I4aI#je*!W(dnO6hpK-k)Kf!3Bw zZu-a?`VlgA^VFD<1$yc4b>p|tZETNsBIJO^8nDKYwLCf!9bCLPtDmfy`JFqqHbPmGu8b=P!@aTvr*S@i8L z_5EHU_r+JoEh7yt9KZZ-IxX37O454#WZ~_Qdjg1N-omGgE(GQdEm39 z5qsikZjnzGIH0d0)N10t&hIMYW1@m%2hH4;v;4lA8PDuYGKR_f-s9$i9MjUEGY!1l zXRaB0p7N4WzoFmH->eS7`ZU6>*=OvOS$r(1mlI-hJGCq`^&`8X#*}sUs?WgvoLR23 zGm$#LN~_jSzL??bym<>LUPuf`6LM^&z`YuG)hi7PGtvKIQzo;)rA;mSC!{Xa#tQS@ z=VIsXd*twRCIN|39~V41KQ$a%;PWYWLLFyvVF7wIIRJH zUBK~}6(xC%L$TjpWj8L@6&o4A<`)XN`2oyMj4UtWu-hfEY4xl%lRra-!dXUcyn8IJ z|KqgPICfyQ+x`tH%Lm11kK1)(v~7FG)q!vq^MGE;*vQ?0fwcvCSauAqx7ZcVc{~AP z;)?3aS+Y?{%zCCBk*pn#`tP!Mq4D}QnJBJJz^nIUpPBag0mX;KG^2;0swHj7Z}mr< zaTl+1+Ap8a?B2Q@F)39`8uhG0b!8eT_U(^`?I%d~Un z;jbJ~?JXcn!Q`WuYB0<$_Ra&q5EG&9`Q*CYEw%OY7_I8Hkl|f$zUCk!?J(E?Hn-Qd z<}ZFzmYjbgP$U}kth>^7M8_#p8!l)#-$X!z!r~3s1Te@aA8erJH9+V5{x)C2UJrej zp_bT%4UucdP!aMo6C2s2bDMkdHjWN)EV8drFN5*PtZM0km2`wI<1mZ;5eC(f?KsK2 zaN=96r{NRg=j5n2Cf0kP#*D*-5ZAkoiw>7rv&ZFzj_!Wt@6TbRY&7rzOP_n%ht#l7^rg7Bl74$! z3~WPZ&3wkKstDktf+e01Rb?3$%Q?Y{m^q@MQZZBo=$sJmDS>VA4$zg+FI?Bc176EkFoysM98Dg@J;h|_^$c!XIGJ!uG4pqwzSQ9 z`o(@zst)))*Ahp{uKcN{ROcPvy(|_RcUF+WDL?vKS(Q$8${F}jGGW+|CAj8gJiGB( zlFMR{pjnATL#jLx(s|HDvGp~MZRyE`}52U4erSeH>=|8d~PPKM*6AF#L zvrGWEn)dscaZBt=fRS-CFN!HiC!BQq@pV6}U-dUm`@N7ZM30f+ z24RgE%F?P(8ORyU0eQAl>zBUyu|_OU@4kMkbu2XSvvFCzj0v=x})0g?7PWl|9fSOZ;mbI|9ejf{3%^( zDh4<17Y}H8ZEJL6%M()WcKKbw30k$bctp>F7F)DtW*Z6goCSV4FW687mYNDyrmL(l z52NC$TrN{PwOL5vNn4%qoG;~f-@hcVD`Q?Ft>@ga!-xJ-_ea+|6EaNS0c(F~F4jP! zm^if~8Bgv+_5K>T6PMF!xYKX?ooIY`ELvDphOFB;hiu31j_(68D=DzLzBk!MM2q#M z`Bm`zu7*$G-S(Q57r4)CyA1td{+gUV&=loEjlr$xRG)#j{_@b_;e$HPAlgP{a=MwR zD#Eyi9pxp#y8H&^R*l6R z_%?@W9A7}e(HdsQrlwdG48L5+{vb?fvwFo9KTGE^z389R+&(m!Srfc$54o~lc<=B8 zjes0Fwcc3)%pLE5uC#-NHm@F6Z9oT18fp$8P32#j=ja;P{cp(ALCV01g zul=~Fd7bc^QuUuTo)3ygI=mVJE+?WL`6lLXW6dn1>!M2~>FZMi6B6PLvZLJ$|JP96 zHTd+u7C!~?2iFfqvd2jUwP8{p{&A87VR5aH@1e^*)6*(M?ydh_!5I;*5GX}T(zl?a z{C@83E%OHe+pEn-_TettR-lU?cT(@)Vxb}SKlR-!0jt>pDp$(7Mi|cI^8DwgUtP`8 zO%~?QJlX;9!RrJ9lJq?8+08t?V>kYvX`+k`XAOgCD#_u1s_~~}{9DkvTwojXqo0gs z?=~Lpu!TAekBwG_7cSMwPw96#Ex~O{S02vy^+m~rE;Otcu?KI7_x-HbC!-4X2zeoW z8LoMISQ;3=9?c82B^$MOypF^E26JBar^$C`TO>J4)9o?l_(wK(>U~2is zpMPg;naOU!e1?3PuLPdlM)P>WP}3DRo?qwytv20Nw;C`N#^_C9p9)l~eZAj>!Da&# zzEw=9$>F0gjTV%#<7TQ)_Aumk^R!yzkZDcLWbVoCw5gn+Ey3Tx;dLohd2X8%@RtPl zFbd?uAIFVGpsZ4KA`RM_|$G0?2pY2$!*WE zoL1QP{Y3O7Bk)f^?~q*x@&&cyq~0uXlGp64cf2%C)9dJV{1Lv=Z%Dg&`Vp`=%ijsa z5s?xs5JB6FRHen%EZ^N?&3qZcnk{DZB^rF@vzS5W8U3aIM|QSXHdx9BwcypPudo}2 zUOf?U*OopQMQ> z{nlGFiT*Nk1^=CK>rTOZ-EmgS2Im&&g7!~%Iv}4pCFz{RWG-zrs#jWis_|Kr=zpeZ zQ?VhZ_gThdKecfVtS>mE0HZ4Q^xDaZnby~COV!UX-!`4{=@KGzo_?nIG%if-U%qhy#Uj=-}2h^3!#us+zUJ=zpTB?cuqy-p5bU`PO{(8C=OA0*_# z<`;y!)^d9gfi)3-q<55(p1V{If?+>pns2Ih$dhOBt)vKF)V88HHIL6}s)haJxNV;&tj^??qPxMEdJjscO1Qv69kXxPvp%UZ*20|5cV zA)JqT-1AzxRZ;%xgM&t+-+`LrV$S#mL4nlFcmlQ3{9XP`i=s@5wCYyHK%PrCQ6WJW zM{-}@$!FRMeJ2ecQ(o=cikwQ+H}Z=0o94OsquPjsMfs1Ln*0r1^AYj9+4n$f;NbQs zY4|1E!6_>CsFl8KUBV-gCk#}Ha5>RJ2TC@cZ{F-IU72^q*5i6`X$Y)6R((v*C}T+K zX3`4nQFNkxW2L?6gb*L?JzS27un~lIjVaDqm^$W5B@=U(157&4kU3~iEo!lVD zyH2NpSX%blsv9Jmtp7~FkY16(89ifzRLdckN%%DmsgGFCu{L~#?{hO<$8WVlw(&UZ z;))42OJiXi;1k^3y&AanOJLr*0zQ_;C+QfB>;ul6CK5P(W7ownv_AgQZNa{>wF`&G z-i)#>i1vD3D2vtJ5m&efW%{4O|K=q1SfAcJ#!XH zg#y18pLkz`^i1k!&eL^`Q+)fbj3frlSNweybEN~0!zN6aItZQ(Y*Y-kvM&m>gJBSA zfLa3YFov29SUl?HF?|{R-lmgrM%RUDn)5TmC$3h0^Ivlhi*-NfuT2Jqd304d{f(Eb z{*;+RH*GkYc7eH&b^;$Equ4~|A3C5uwK&Mx0k~O_8Dg#qq(qO=y8pi^B^lbXHNn{? zQfO;KAlQX@!GAO!Y~@G$T*YS!&#CHaXvkgn2h=9HzuFIAJbu=CTsalrwW0s7dD}$) zqP4rS$E9VhzOSALzb7+t3j8a~OT#1ECtW=kP<;NAm1k<9`bwX>Ta2Xy>D?{L57%s_ z{*;W6^=0XFPJcm#1M{$Ux`>z^U?E_?ySn&<47g@kPH5c_}YYjY!19CR1Q$W zuC-Z#rICtpO+&7B>KPR_9;Am3aZ7!De->fMllcOl87XG4hyNt<`wBnZ^FGLi2HicR z7yzA6hP|fQU8dr^b7WL~(y9kWRdJqI*Ag1AmE4CZQ&CzECp}igCs7Vh4)jp4BT&o9 zvwFALAscZH7(&qX;vET=A^ykx?G@^NtN!T)m@lnJP>7dsz>X3NUsiXc3ha?%0?J%K zq3YY7r^5zQU9z7;*wXjy1&A3!+hQ24AgSop!v<56l`Xr~p_h3igNN_AACV?C{gG2WR zPU4CY0rlAH>5AjPix80y_vH`HFZ)Q9g}Zc2BT5{&g@4h!;!{ayyv>|xs>{;-<;7WO zwCT?e@k_crwk5?!0jz%m9_>-@GY2jR_?)tps>Vxawen4K+)8rDmmMhj;1TgcI%6t( zYE%Ne@a({Irh}!+*0m?zT~}%Qr`q z)(E^jQT)Aa$2v(R? zy=n~ef)U!~R$))vB=6nYn)MYCOJdGBwP&Lv*}y%BC?O1tV9*I-ZpEVgT3Q$z7bg=rCG)&Vt<&## z$*_zstL_PM$_{uv*3=Y}@os}Zp~Nv0QUvgsyVtCb<8S1{q;>GaKMPw}tkCZSz0`-|8o;fM>p8fs=y~HQ4pUsV)8;pjg~-TJ>FzI@ zs*h+*If7$~QmBNcwwNkAIaa3CJZykpN%6KygAUuc)%Cr_w41wrWUF@Dk zNz6Hl39GHUwk+paAj6~LS#HxWre&6QQ)!}W?>e?r=u_EP!yINE0X}6{rc31HTpm33 zwY&%4?y*iS{L6UnLx|W1j;w@?39DD_?Oc~gP;WFWqRkU^%fC1~)3Clg8rA(z#!12z zwMR$jtVL4=p9K-^V5_ifYjm}jctk8;yS>@+TJ+Egf zHJ@45hDd(cV5Y5&qh9O88Kk&7gWR%Sed02CNcOjjE-1tK;#DGUK$RiNH@MfJ=soOf zOzMM;v`cQQ8FhD>%gO?&J0FsvvPTRsR$|R_%il;C80GM4KG5sb8RzA{@l#`rl(;;| z{%g)ERWKPva(Pfc!i2pb7jw(*ZHcfI;;?M9Q4ZWW9Rb4r1lA$hzh6Dk9I@OxsE`|A z(OYTViq!U?t8-h^1Fxc+L05TxH=FoT?J*CVu7RK53$J#p|7%uh_^Uki(xEJ}_q48L zgE@WbDl+1=10;rFx}jqIS9oc1`YlT0*3ZVn9%iN4nCN|O7uZCB@rI=1ShzZZpc`Vo z)!>919vJ*(=Yo(uJvp`#TFDbWp+75S?Bw~&D}ndZ7EZ+Gg3_jfE_x(~4BE`wX{J=)`v?yv zn7f1ruAJSKITNQkv-a1*u3i{;eEBNIX0hZO2b~89-V|h;;|8`yj-77sy-~b^nx+Qt zjZeE1mYM+^MJoO~*+|xKfH?x?XTn9gX1qob@kIglP{{W;EJuE4y>re>AGwIIB4%2k z4VjtV54@mGC%v296~+ZRgUb=n56i?F*N^akt95GLLB6{_mA`wXW<8Vo9cT66lSrv& z&yga>JOYh};F+7Bgql6@xJ!!{+Pc**2JS*#+!;0@c-a%>jdUW2$0IFa?XbT}K;`4EnB6YZG`StstNC70bzX^NA7E&DqZ{F?K*Mo1Pjn^%Io%in4oSD)K z3x-s0YV^4n4H7CLtq!(a{hX_-l*Jc5YVB&kci1#V+Y2F-nBw=K{=1`Lcefq}Us?m< zhG8bCAQg{WQ#9`)TTV*7AKN91_Pu8l3U7RQ!VuQqS&@VaW(w?SorO zOM6sW9qLfi1FYE>Lt??P9l-chXDAd%wzhA>KaM$1Dv7_>1(egEdQx*Qq zfRcDh=k1c0pTWhVr8b~EK`bKwZKmI5JxjjD{p@h z++Pp0@~?-z7g23a3I)NO)`W@kpVi-4w`q}i*)rT>4_-S@8T^8!;wfT+uHoERlHZCe z+>pK)CQe?7=FJ}k9)rjKkUVJ7qg5qdkG4UjZj7@ySl@dX;t*P@lp%|xBN#!cI`uVl z+ZiS`Cf>hU&NT^@2k%8aoYa`=PWQAL7u-^u)pya|u}$%PF32|5?!1hXfZr8#p)m|g zeLDKf3-F^XYeET5WtH{N78;;4_ezL+^}(e${|3@K6v=*iLVbegLjJmQ;R5Gk$5SDU zz{}c(>B2sv1}}2UR7MGI)`p`uy#IPNw4{sb{Gb^c{g0Y;J?aycHbw2)|;T2HU zC!SUEPl)&=$d5j*R?wrC(K&ZE+WDmKdT{!tQrbjhobzT|mB=bC`^jHtf(R#7DTJQe zag&-Pt~co_;r%98U#Nt5%IJR>b2v9sd*sd{o-!LABn48WSI^MpmFMSSlmcZKaD z%k0v~IrpXBJo@fOV?z(}^8>F4I<~zx+hkL!T(Dw&;1Ijj>U|E-~RO&=Vq}WCAnRs?W4(8skGLhbva7*o+*Jd zMAzm$btb6i&acYaxU(-z!_FbR&dg8a_swwyH;;49J=6?DekS?TTcyXU=q%<-r(QPj zflqwDG6YO${NJO%hsruuTm{dLbxS%kP}Q9`MSf4>z(V2#wc|gT`wV#kW40%$^i*Cp zfu3p3tqrRO|3lr|?d_7FBS{d{K}yEXaH()`jEgk1?dnC)@Vx;o-JF%VqAC(O%E!i8 zdCvs{9I$t~84sjSBISoWlpZ;=TKj%sI~m}y%_7Jc;|$*@aC8#>rv1}SR1BfL z{Sih3a(uL76}YqIzzX`f-Nhzqr_;>XvY^>b*G9G;gbX0#fX=@=E!RX|>&HDH?FbmU zCR>~oQN~6nn2;WiIHoq+w0h}`e`Or{F%RDS#%5yVPENb$Wv}Y-`Q;glO=!Zou#v#B zN}HBW=nEDKhsO0EdA+$VISkX`KexsI%v8^Z$6AV4h^`p$-)z?~ki2Y)^%N0XB1|y= zCqq6$=ep|dVCP^zpSM0_8IzC$I8F{lApgv>44=&v$EJC0c(YQF8ZSzJ1K58z+~XpG zWzPQ&=>B-ddlVbiCXL;X4J&YMUc9^9nyBNKalocFm*@N}e)TLIS-WAKpFxi#NeMhyHKmJk;Gs>$_Y-cy?wF-%vk;0GAi(vGS~eEV3}^z*E2pPv9e19rJ25=4H` zAg?sxTC#W5-+%ZZvgX@f!)433Ax406I&y88mZl3a{qF8Qy>!y?D! z%uPS0%>|3pDa-iyk7nvU6VbjDGOVAubas=ZOw;Vht`Z3MOKcT`z<2if z*0z^!7YaET{!>-KB8Wu0329WLzfpwHuoj(7zM9i69kBsz_pP5oNejUaoBwY6r$6$e zsA79k{*9zo)wuxw{XXNi`jfh(8Ix4Sl?PRiLXfyQa2Sia(SSqe4IQ(El0{pzy6ogh&s?BNQ% z{z-ZSeJI)W%x&{cebwMlM9rkLDjmQ?#31vRzs>FW4-648v#7UKG zB-`34AGI+x>+C7ZdLIReufEBe`A=Zg;(?T~>Dxj{VWwT_O@c_%i%|d0Ur&8*?)Au; z>@pCrvu5G>MM@|y@7`kLXpKxjmK}$sQM%Z7AgAmcGrGyxqfBlJu`GQC z)Q4v{9Xv^3oMY`;T68qI5HzPhXr=Hpv9VqmcZ&j0(r+8Owr<+ zmdNTb^y>KS-=k;O{)*|3JK`^&z|C9c&w@?^8eW@p!{18nQ0bqD^{#ctVJnq;F{4Hc z=Gl}sw|(d`pR%6}HL>(3HD$l}q*tFome~^%FY=9_6?^WTMCTv$>n>v52RJ&abgi?G zYVs((+3}hr!`j0nHYg()ToPFmUk15#Bk!&yloi+n1diiZVFMxo}p7+q9(H9L zY4!ok(zX|DI`4={ta~_OD~!%ms;S zGytU)=@w~bhHe-_Km|di8%gONdMN1z$)UT3?ihZX=Q-zn-|z3J=lKIZFbsR&v+h{e zy4G6PZmV;aQC?_dojP7`O4})UXg(^LVf4L@ro>q~-d*A(hkH@Dmy|KRySrQK3v-O= zMZ3<>JX_|Zvr5gswEz-sg(X%9*$`heu3tYdVS2itctHA5GS}+~NtcH(-3?oRjgZ(2 z$3?9vTc1SP-R0}+>CA+*N>P%-F&suj;i}VJdK*0rt%=;Jli#MiA z;_qs|SGJyh^f8ocwtTFz>kNE-v#$fQsTsrQWSK*~w%9HOh=HkZlc3@y zkxXcR8KL_cM-{7)F;(?xlcAIlCnP{Y;CbMzcDYiSo*a-Y(V1RZ?UrLmcy{q4x+pvD zou?W&Zqdf};zy9*P-+gukr?;M2wO5c<~OB;*y~{?E&O*MxR*;Y#vXq+3j_;P))=$9 ze;cKFe(Q;$9NCJyt2nb$JJD4S=v}g@-pX|w58C|_qA zHd|JP%q-NW`M?8X$_KuW1myNvY*`YISXwwYU*EPFg{O)cE;r@f66aNO>W+QbS$()d zRH4PmKUVEUHlso5`SX!w&5XZf9q+bBpHJ(#`FCu58T0Rt zt=2*7j0=6YELV2W>p9l`^co#smNXo0Pu}zJlle30uzB!@>V=$~u zli3BT>PltLQ0vOoaU`U` zx?9iUPu{Nlb%0v1_9{=*8d5*!goF$LTBsdr`Sh3}wKr1!)_F0O1Qb&YUgO~bZomS& zPru)PWFO1KU9Jx3|0{kli>=5nBYi$Pl+Y&%*&T=M`n5ZIfaoZq5kij* zkOWKHNNAW1fc2glm)P=7n=0ke4G>faVybCBNxlZMzpUxzo3Uier{%wa`!iVN!%S_% z!M)Ovep-^0b&}|{6^rYFrA03u>HSa>Cw>We#1{MM?D+xT{k;Pis(ij^Z&rOoeFSDY zVowq$sJu=a=NK(yEmZjysmKkq%Li7*q3(!`u*#xf8@j@y!DOo6P(=A2jap6gMR?v`~3ahk7Vf zZupHQX8}lH)}geY@IdWb6ZkuI%9Ip7#ho@CD!B_HWrPHSwQ;;xs4^mD{v#s#P+f&s z;%?|{2fIDCr!Z?UcmMSKsvJ( zuBpbSYM>HV(p77+Lmk5%Kdq*G0Gl~7c4U4`MR1hLxy_RdnoRzX4?OY5(IGD%>S@+I zsB1hgs$jqWH>Ri&XbVyj2c18O4v4&JG9HLL4q5~nP+#;_cH5)lg>v0;1`>^GUJB6f zHEmxlIsYpkTH=j?d_1llVOC+0T&joS`|@4JM`NWgX*s^=m<(LvkF>tv?7c(M^lp*y zbZ=Zz2nWmgG9<7u{M2r5i+&fBHu;md1gd#zu<0uGyVsh#H^2*u}ukk%*pft@os!dNh$uK zlvSyc=gx75RO!Sk$Phe3%*i@W{|Eeu)&2zaS2 za^!>AcV*6BI#T3*XeDJ)HCHt&o!ygF-FVcQNvS9fI%YFWQfy=pD}=3q6Ru%7UYp<# z_q84^#53G}xvz$a@eM{2pF*y1(|b0jPd7H~jc-B0sd`^O%Yt(-9~sunzU5QbZu2Pamo%&ah>en89to-CHcBb`qkmeVFy0nsn`Z0l}y1)-hago`STV5r>m# zl^&h?Xa40HVANFy9M6v1)BSbE|rF2XD~MH&Uzt0+j?N3ME8N4owkaKgb*d7w zOj^S^i-qxBBzQ<}5P!h5bpAPRFT-JIK3iS*7wTDAt@PuJP`4K)$3^1OHND==4@&p| zdGXMQRF#`5J^?2MWV(;vNrADq?<3qtATWsuAFQ*9{w7(=%%X31l2-x68)CJEjt$D& zg(s__v~dDnhJ5g!Its${X=TwH!e1@lSU#Gf57esUj~k7a|4wo3Zr`f&jR(r9P06tk zZE%`rnkoTvH{psyq^n_aoptI&lN)8`bU3sL@kr2+Ii6*1jY%-EaF+1vj=4(qU;u`C z6-ravNnX@RAR6feDVT&bjqC)&#L)b%4_+)YreK9cn$TK$Xry}9@7TRS+fECkxsUw={V zOhBhaawR21XG&Tyi*&Fn@?=mag=`!sPqU4#DO^hts7lq>)hU=YJ z`?(3-mo)P2zD3Y=x(Yi2Qa))kKfA~v6VU3EYwGCC#&7nESnzbvevz*k6p@&!gp4 zzT5>KG+Ove#!i3vyVa6YRno=%PzyRcb+Re1pTxITRh2G3-?>!1lLW9B>NQ-|weE*i z{wJ7}U{&AJ=GFkE7u;y@>s-oyLp2=vQq;1v=(u~yUzz8MYpHv^^WPj8Dkif|u>eaNcu zlt{{|^@@k?@WmpkJB}U1l6I&TR_eStiAc8?SGtMH#9SrXbna*30cAit)&t>UjfU5$ zdPx%9=-uoBI?X8=kC_sABu8;^ub&rJ6k=UkOQ&4Yj5k|7TXe)E-Xm4>48a*=eJ*|v zx87uBVNJs*nYJF<&|^6b2lq4Z>Fm6;?&887-|F$pCmuUZm9(2Lj?QC zHEi3ZzRR^8HoYz@^6b8wCj8x=;dZ61bWCZ?N?>5+3#4!tqNZltGdy%qJh0*5e#IkG z^(=YtQe_AjyvD$lr^UYIg!&N~E_~y3bllwRhhlLZU0ytuRW|wezG=m>3m;oychl-T zCmpyNG|rE!(3gp#`lqMp!Y5HN%_JOvZ&zbPgGybF`|=GUE7fcBDRaMFw$`uSQYPN3C2$)4o91k&JB-LM+KcFA$fx6zet@ z6u(3J{VBSTxxZ zlYSOuE4siE-ZC)g3Rx&3xckcqh|hlLyJTqi^Qym+-BDXPKlc@d|rdBH7q9Tvq1@XboqQO`>iykM99vC{b~N~R%AzEcieKyWXM;&(BphocWMR3w{H?> z&E*-B@K1T%dRZ@uIV5I9BROVi16=&Wi@DhjUX6SgoA>F7eOjm3>OnM5OG0@dydXky zBWC<#{iD_XVL@}v(9S6>_&2jx78|38CLj#mJVZ?|lZ-;YTa7;;QhPv*xk39FVAr2M zZVUMIzCHuyWE6?g*9do2+>-9IRVw)M!DOYY%0^Lt&B?^rnBFvNqMH4%6`k zip!ybh-(cRZOxPxcRj2qozhonck+Sm2S2t!Yrhb&!bYTdFhLL-accbedk0FoPQ+LC zT^>F{x26V5fC+<uMjlp~GqV?Fde7stbg62T)^?vXxp`Zc$i(SX85lU90+~y+Qg8Xs~8XqJ0ogR4lkKu6hI)WSq z;x}b~We;lc3LE>L{8f-g1qf+grhOj^%wvgix?h>+gm^>4O~3xc;IAa_T)k7}RlCPu zj$7-QH;N0)GruZ{FNYMgJgYH97W9IZdZ;!eZ(er&Ow5$p#XMQo0q74+QXEqPCJ9G3nn3 z4G!|~uXlTlL7^P|1CR+g{7@BOoCYkmg^BMcNe=EJ78j{=87@zlWH20Q%}TdQUQ|Rx zTok!X?rCBQl~+_8FF7WunyBCbi3qdA@iq>k)DADrTQj2T1f^fd@5;2S_u1Tdjv!!o zX=1>?LTb<%xicl%{KbHdI7J}JY{oukcagsICbYWb3u|Ls{azvW%&O#jUd4hah4OHN z9JOzg+L4x>vtX|U-2PFONY1NP9H1)+j54}w{!GNTw`1tE{{1Wny11Yavp zPm594yyuN5z26p+@Wf#5D}34I)$k$EsHwazwWN&j{AjnQHemC_fMTzn$JOwDW|wMI zxFh)^pUFrrdk6{c$Qd6-$k|eNrqb=Q#Fkv;4YNdV?&;fP7TcOTra$%#R8oDt-(41E^)V+Z>1~h`R_M?YXIm?>xb>Aox*d6#kvc zhAzD@qL=;Ij78{X7n`K!ngVj=qpyZ3y5}swUqBBs&+SZ4ZvkTF=O2Z0KQ_tx5wEulNq{w_ z%YsPG4y2cBn#)mUd+V`TFpKp{lt6dTp;2iHNYN|MZ% zR9`-+A0PN;!Y@l;t{+|!O=-6NT<}K|(R3KyK7UH!+V-@E@jPh3$R&fc?vAXdH19E0 zn~~8WpQ{tY>E5HySh&;V0sLh=hvh3D{Lqj2Fpb0K%Oo~-7p+GUl`Y=rDl1831S`V~ z{T)_nCT^S_y{(O|UTvEVpb#G~(?o{xFOnADveziVg4KXf$DTs_?ZB4J{sHFGrxPRQYS6eW# zoee$CGCslH|FL7N9^bCD5?2pQvIjbpwKsDEeqCk;(}Vo zOvE>yUv`bIq)@qrn|8Q8Kc`9gK-T~XbX*9J5&u&UJsg$fp(6~5f%byVtYWj ztc1$XpELe_YVG4hyQuZY5``db2rFwS@=h>iBRtFwFb$`M!KOux;KF?`ILdFf==32O z0@CPq1b9!Nr&80*6-}l%-ctm`^Y#b`Qpu4J#R`p+Ou?SG?V8RGKOHsf)JTR zA6lfi_NMa4)VNvdVT6axrNH8W^YE7BSm>d<6SCJfiV~S+gB3$>2XzHQCazFM1W7~H{Nu+V_ghC~jJ3Bo*9pqURAK?X7EFRRE zdr&`9qUbqUxG~H)94GPU$;mQR^aa+Y!I~NUTJP~|SPPw0E^D&ps^2nUBI+7Jpo17g z1NwpIxNP;Qc{XX)jb2U`NvX}I0~+B}*4Mi}2dve$yWbClXM~c`pF}0r35ngs^-w&3WPr8lQKDS}8xi9QncMxQrgc=V$1wUB}X z{g0%yCSuZ;COcXlGONL9MluTSaJXrTbEBQ&n*`q zFy;@;vk~p1)_0F;=|r~X^ZMm#@j+*#h5{2_b$1oR9ArX}rH^l>xVVnoO28DtzWc$y zew0kuR_27(=EkjAHD&Vj$KhdH;d6O>H@|a~sy2a49%HT5?G_bW7ZbRAl8ELS=(^d2 zkHrv9>s6ok9*_u(O&4hhN^zqdX^r0cIl*x-(19f!RduE8CMnCM)=M*(c599^@tG3h zv-YQ*91a3DaApnQiyunAScJVeIXrIzXcm6aeQ;`SwctpB_fxvhg5TJc`FLR+qUkS? zudz8%bUdh`>I(ggyv!`w8x=6G_~TKg269o-Uwj@$@J-tyU71Vq_Gd#%?$nt5dv1S_8^z_HAg!w!RH zb9RYjZ?*z4wW`PEz9DapffNd*QBO|O zq&aq0|JZ-BRlG|-l*Bnj5D~*Vz_1Y8ft$`XW`nJ{Nd!b4u$%1QWlvQg>u&W(QWCn{ z1TJ?+zta(SOS3vK9?9gJX(1`1#PhMkmN=g!t&WJGa-ONjEU&6sgRMmOYYqY=Hw6{w z3SV7`O8%AZ`{gezf!>$(x79+eJnE|**VQj|iuGgweQF#eb+(K<-AIgO5KBD^* zTbC%(<#rzxC>np6xIF?ItvO%7DViqx-;4lU`+F$H)XrXOgw4c9J4^L?@fl3gZISB5 zNS(E1m51-@iSBr7*-U({pnZthss`1b6!i0F5Bq!b{ocfePw#l+3kyS@emFwATuS!i zS7b^YWNpUV_t-8cxdmTkUeBMGC@2hxny#A`KP(D$gnF*)Ogh({P2TSk=H7HHaIX}qT~*u+po7@J zjssyU+*K=gjtJ|v2{)fIo_;=Sla=5liIB7ZNM5niLRZo*6q|)&{qXKDZu0j=0si8% zD(kJIGVp5K3zI~=Dkc6r1p)Qii`#5sL>Cg4ClWQ;q(H)lY&7&;)1PWDC&9cRpv{M& zuE83xAvKLk_sz;y&n@?9d*=yTT*aGoi@eV`yr=5W>ve!O4{!fLD8^b7wGJjO4Mdby zzW_!V{`8L<;4c%F1W5@WQRq8Y;g|>)U1Mt+0t;hpJ|=bhJneKn3_Lp=o~qQS=Xo&X z1={o_kZq*Fdo-gubvZV%+GCILdGhu5dXOmlA5~x9C z$;r&K{!p){xe!tV5Zc@+III(sCrgqwZZ#`R^Oe zlmZi?`oS5Upjh7=2K5ff8P`9^)-YMFH=08!qZgFVvn#f(g%ui&)>~}SCmtLZHEzr_ z_(9GNQ6ypN(Qn;<+`ToMst3C`*sS-#@@hoomh@aHNA0=Wp@HYCS53pOhnb2xZ|+mL z93hTs8Q>)oweIClM+-brRxy?eqYb@`-_0l>P?sjUiGhKyXFpgaW=R+x0oBVJX4NBz z!?MkCul4d`;hOrx==X#=Dr|*eBowaCDFhphc8M*RBuDh!^V~<9ZY}BW#f}`K1jsLe zyA&v|Qx&-Q*R>4C+6{h^5~k>B7tZsF+vE*%{O<;NgWY&^@!7EohgqYjS;puMcf5I% zfckeT#Zn_)!Z3Ta$s*&{2@S+9)e$h({sR6cZ2Iz~^WCVri6|nL9}n6CUKI4g(0dRQ zlje&RS;<`rV+^b4rydGD7YnSCI}|o}m)n7o6XM%Gr2CxnA-BY}V}DFKPor2*$Yx3h zmF%G&^K_o0btiFkJCr$VD&BRMXLVZ|oknw8(-+mSW!B>dn6iN%YRYRCxe~0iUL>Rd z+P1sFtP^u_4yEB#^8erms5wL}YAvnBkEuZ^8A7*baIvOw zfj!|*qrW0?(yn|8zFB$kmYzDNrSQ`|^|*`8Ep)zt?a?QwN0V|3@UWDYhBFsyqYrs( zy%J~ENBfzQlL@vhwEB!7yD=k!v^K1qWqsg+XC)1VA4!%+jb2^jTzv5Dcw$*XCHeE+pB+v?? zNNa`{rsK%Ka%|IbQ$4fxxS8j$ziuyK>#dGxz^c)kO;Yp>={ZNGtoYhXw6(BO#zoK8 zxe_&hyh2=3Qtx?@2{Zh|M444o^-`=AJ-q%$j6FaIe{6~Y8fO+cFF3^FQHSWGx@p;_ z!*`#J8o7RcYL^ynTiL9bC(-{1FqeOF!v8+T9fGAY&FYP$=~((2^VllW@4+K}sE5ze zLT8=^R+v!O8&w>;fx+EZC()*DsC#@<+D+a5!?5jE?3*n3ux&raieAr6d$08u@vBZr zXA`YZ!ge*AnbpGDdkj5MP1KQ$r+tAFvJ$)GTc7w)*^d;+-W*J$%BMHEt2cN9LB3CK z%>>%dK41@waI(P*!mqTkl2;h`7Z3I)WB)Y`3fC~}3bW;h;-%*A(X&)P=)VT*DC8ia zu%ET8e91Z;EfkLxh%a1GjAFgr^lqdVz0rHRH#mhpEEiR(cpQNL077#N?1%BBrhq(- zB9pfpO5z8vCA9)-48IbE*v=O&*aD*C*?vtOm8@d3_>`5L`4x?x8^(-#@Y)R%9zYyv z!|WovDpMR~aZ4z7B@B>t{-X=}*L*~iV7s}~IK3rh9T&#O?O4A<5vzo_t*D@4L`4g? z{2|ce#%YI3LT>{*G=nYa?|zLs&bvo$3hdq5d4{d+oSsd@86>d1hLF3WUL0@=_M_Ys zoQqe^AZbEPLvQic<#DUKNaKXVprRM2oI<{lPRG+g5~{2NFHKL`z`eEYRKG+Xk6A(p zAoJ^GcYV2`L0#d8=L{C3;e(g|Xu;t5;NSJ0Bow-G_&?G%>etFs+JzK^0T<*rmR*sL zYO)lFr^WStphW{+uK*(jc|={d4U;T_gYfI_xEj2Ry`{kDWF(L@v2n)+a6&!#JH@*H z;D}&*hM}XPB|r)^&gKe>+%6<1>%*RqVKZ5L`QI)ecoU>4HFQE9Zo*_Wmq$c< z0zAy+pKOjU0???D?OyH;PSO7cy#77)^`&>cx!43Sg;44D&i=j0{tF;Bz2;L^CZJf~ ztzglv@{b0F=aVH1XqsB!O#Gw?Bcl9=D~}4>;o3xQc=*OY{Re#P5Jt&Oc-Omh-d6yI z0%S((V0G01@|pyI#i~tHr{XnC4^*M^mi|#au0$Uf507#S9#EYA=Z)Z_L^fJ#5r%xr zHJU{O_hM_4ihk7t`k)d>*I!d{fgr; z23a3joQ#;R8S>m4*RhxD z^=!MC@L!(lyk@mFR{afXa(*GDm zUWK<25U6MC@+r$yG?hZzY+!rk^tpe~q$-baLW=f&+gfZjI`T59_6#E|`_ZK^8uIOE z$~}xloEn?mmz>O36Mg4H3fY4jR4Vt2o_&&2`B3!L@cyWj^kW=Kc0pfXN=)gr7ZhI* z@@AbdSnGtXQ*>TeSI?$b@7_jFf_ty{(L}FFpPByD(MEZ6sdk{ErAFZ!=@iyw4rYNN z1hRO9Ba+={h%=(2-;?THPkVbi_6$Lz-@+|2ys~V;E>1+&Vv!# zjOaO!0S(fo)3D3hNv|`MdX3AD>XbXH#NnuMWOVdOt@xtjXra;JYGzVZk&;L>Jk@@= zXE;ZLhcI4QK_NJT?Pa6oP_{{}`w^T&ExTgyXmiqv&$O!$jD(*As3rm3+B#*HLmp?N zQW%)nWdHHar;#gp23c60k^WSrB@P6(Oe&ao!-F{RN2-%4c_y79sYFN`#Sh164(IlH zP;LG5kleZEpm8;AFv`*HWAg7P>xQ-pigY zP7slwm61GSkf>#7FL5G>s-B^M7%X;}py2CONakfB`>g{`8?gj~DhGV4{?dR+(VD>K z=H^lVCf2i4TnMFn^)w)FA!6hFx;8Y?#{6~B16ph{yeL4qh!SHDT2xebUp6N8u;9+Q z&W!t(CSWntj}HduUHvMDEOx03?-(JalikH`(Q>oiMD?`i=q)k{m#PuLvKP%F#bJ`1OMc4oeFt$MYgoa=xG zD{s0|apEX1f+Mm>l2oMk;DwS>8B_>WY&N)5bU8!rzI}SjY2o+&99z9?mR4#7 zCaHESd<${bgEQ0fXg#+SMn%4i+yXnS{bV1+h~Y&6H*-anL!HO$JBCGFchj!;pKOf+ zNS=0?9cJa}!Me_3M+A7eC&B6bct@f2XsLk{gvaEK@gLtPtugaXs4J%8hFu4}cxE!J zg;0A2bV|$Lm7PZ3o!K4mL{#;Nl!Q219m$_`5IzztysXja%vR$_;9LkO;~d|DA;+CJ zwYV&iTj-iGiBr2U!unOI)mP1f2mJ_A2|cSM&oXv!PT-_~#W(PC!j9VgZ9~z5?t&L& zG)W^rlvpxH>kKZtdaQItdcbP(nb#Pg&CZhJa|ME$cG}@K3-u)VNnVN`v&K1^2!|?9 zgx|}GZ(G^z-a(DHP2ps0Z6E?ocJrm_*F6t)t+MkB>d@gt6)cLe*u=vYO1PxAO1X64 zn*Ib-xo_LQLSi}iP5^>s4EO?@CAwx@1+l})kFk#CVT{&ye_d=-6_xI~to<~Q z75Yrgmsb-W2V=Z^=nFoQ$SLwHwD~l7y5cT)6tv{jF!m`|LmLC~GdZN8YT0r+Y8q|F zqt;JH#I-=j znR&DW3fC&jUd)p_F+ju}ge2uHTeVj*HZfT|c=4lNv8q7KE>VC*_ zdK&*+4sK%M`MPJfBWS!rqS}eEw8O%L!>=qh0yb6$UdEiBWrU4Gmq{-}-3z`(N}|Ti z76(9Hp;H4RBdm20^U8aGK@L_QLb31M`#fkDu63X>lMS8ThxugJ7*~v&MCqAI2T>~u z)qt$^gF-`{7nu(vL`23S;(tD}sdhpYS2?a%K?sJgaIr;Dz%Bd76=hZ<q<`%ep0E|#oJ8K=8rLCJ(z0!cZNZCVpvm3p^8%w{5nyohsotT{xEt7h zK<+X3`IfWOMgP3uK|`lUCbrHN*HG!4_9@&-*_YwD@SIa5+smJi%D{%HxJkyuR*^@J zVs0B1T>3?0wV}#7et9o1vYjlp)DK~`+Et#59gzw;d#LDwy2}>y4}(LE?M`b>ZT=~3 z{_LeH2FVy}0OUT`k=!iwtUg&LDp@#h4L$^eZ2Hmfq8ULr3Q`RZBD{lNTmAh0GQ+Br zy~1fVJ?bl=sw90acx2Ai>h4PlXAhm1s>ksE#bz-TR zt`5%#_5=A>jkBq&O0>J9MHK2O&x#z*aQCgj6a&S}yjG5uYH*y(o3Tbw;{qK>4Nf6s zs5^;PcgN~TiVgx$;L{HqkMLOL)Y%+Eo}El>NjgmQ)GZktCY48y7X~3u4wqX$rKR2D zo!OoDGw#8A_KGPd82-G6`T7Wleoe(tz8(^1%Apn@Vn+dYJ&5*u-B{&>T8s1bwY6;i zn0`qbQGiAF%af8x#T>i1Lszr_EN=EiYnvgmB3@saK7_qj@PT`vrVO>}M+a!*#3IwDEZOkyrFE_h-+>XN>Kf???Soym6oy7!7;>sZi(<*Q~cX6B(oR{FA!i*|@eJ(+nKH<+%juEX2< zRYH{Qd`sn7(gP!3@^?Y|j5zs$QVxV32SXZ}3M$OEODMbvogi_7U|!;S3q7GS;B#vM z$b0e>T4LH=j6+C$y!1^13zx)U%np%Y{VSgdIEHUjE2A9q7`mo0P|#rI7}$7l*i_Y` zUC6YcFb;hfBFZHw*wS;RsOs*qA&oFXi@-xC4;N$)S83Ka!kX77(61~o^8sv7>a{Gm zb3nM*5(lfB*mN;Pcy70sAEIm2AS-fI^2m3#Sk@}#rds-P=>kI%`jtc6B9iN9Es|(i zhS$El8E=|DX6D0b8t_kWr)xTuk|ZXnxph}+iN~zI+>y?G;!hCy`Lm(+@`nH^eFEUY zfa1APNs=&xYfSpL8WqDA9rZ`wYSc?VMMOuN1HRH>k!=vYsUYvu<4RJB_Icc!SeIcS zixlT&{Pmu1tF8BbSb)>*J9yK^vH@h<;T0!hYZJobcK-G&82zw;gi+E0IMjq9Y>0-h z4-UW6sq?IhP}HHx_bRiq7;1Ur=SaAU2rnfto<Oc8hnvmqT9t5;3<9fFt14-i zl*wx6^2dJH671BV2H$;chH^B@Z$AyttaUr^Fo-VDF6)@`Is+q1%zD{JNa0Uo1iRye z$^mL_tje+O*+;*Fz3HQ)y30wq2o94}pma2hRr!?h4IL)-?l*6_murCm|A~eEnrm!| z3fJdmbWS6shJm~jnf7*rAE`c+^R20M?f&Ft4$?l$_-3%jRYKUB!OT-?slr^sjdQA* zn2#Cta=V*?RE;bja6N)=e&C6Eoma5meLlvimZM%~-zz?nhuUwu8#=luTe2m5}pIppf?eSKOnW`0@D}T zI9#b+LO9b7BzcDTX=!N-iq~}28-zkn9QL%2RzAyD$!{ z%(CRA?l`mNAewRjEdUrEr5g~$J1=puuX;3CYN1N^z|dP%gZTndwaVfSY#cp7U72OJ z+6RT~b7QHpr0YMk!Z`g;nn)!_yJGmj0P~5+M@TvuhH3AiHm#~<9Q1YX+kxczKDyv{Y+Rcb3mC+2 z1Ps~zk($d=^77oiY9_8lztsH8*-pOwRYGwF*%84BZVDjgR&g3Li5d%)M4JO1u@KnD z6$hoSm zvJw6D{BqOoo9h#mCG77tb2W=fV{^%2uMw^#KEGKwSF1;aVw<#(OBd0cpa9qDA=u|!pH^w*2 zlBZ_dH&4HIynEIM_m<7svHu?idZOxGuY>l0R3?;tt+;d3JC5JtYn+u5u#K_zdOBA> z-2(hV!mHx9?W#&@`!8cmvYT8tMjyHZoMNum#VPlCu4#AdaFMaBvy#kuMb?kEN_8r+awseQZ4IMGf=$1eO1A*0elKu zWfd)ROCaG~Qh+$^NXWs6@6a-J3=!Rd)lm|~i@6qOO_**?)q;<=XSuN;Ye zaj3G`&bv;p!2^Ad9qF^p>aFuYBjAAl^}+cUnrG_3c{3n)D?`RD1VDfa8m4xQ@9J;%?>Rdd9jjQ8{SZ75(!4X0*<) z!>oVJB0;2-bwd@y=Cwc0Duz!D6%$5OIa-&&rx5=~%kB;d-KGUmm%RwFn9}dAzw09< zC--rO*i*tqMSZi$Coqt84<_^=jg=F%wIPd z@Uc#S46Cor%iU$E1|e}$ahl(57O4j6_b)nIo^y?sw@uzAO8@~h4(ZvC>l75=!3;$> zbXr2SgOdx2yUS1kx&z9}%96q0zVlNT+ea1W4!O7JLH5Su zUq-KgUBt(R;74=wOJCmj`ruE0I*plBDzLwXh2hsz<;(U=-udIn{Z5BTaDv9r1G^~V z)&9S=0Dil!|M~+#5HJG-v^M^WM)pa+Svdb1-YgBkmTlA7izkB+rT1edF9LNQ4<{eX9L{8Y4hd`R!(r2`Mo`Xd*cO3y?S+>*2d_IiOQdE{e=B$ zh$o|!7Jq)94L&eOUodI7bz6S7g8#NIn+GU7`MyVQyy$-!Vycv-B?mCXeG~IP-}(tl zvcSw7;h4C6_208XDU5j$bn`bW`mHh(3k!=ogZ&sMGqXR2E;mZ}Z!hcfolIq0QSvmj zist>Ee(D=v1<Jn3Bn!B=0ruaP z^Hth&&CAx?pz{@x)TzrekeY^u2EH}GFNj%DC?<35Z#y#8fb_Q& zW1uFTk;KT$`|pmpZ!r9FTy1!4oO$N`DVvN%tN>063gE=cgz;a>C+fZ3{FL)=uZM?C z>9L1yCeec4#xTMAg<*Z3+)b8>N2sa_#vh66e240fFk#SGs)|gij$`QmMhp!&jTe%54Fw(gzq}Dah*Sl__%2rd%sK6> z(yxZ{zptf8Nv(G_FE@+HgwR_6h;LWV?R0+?ZZrF2+Wj|qg#X#NZJ{97NK7HXb2{kz zT_Y~eOmyp58_tV^NJ9Vj2EY8N4GIFB@C5LLLok3BGBpoY2baS=H-2HX|NHMulK9W= zV`LVGp}924luX#$efKo30?_~ap8px#-`4{E1qk7uL5PKMMkwC_p!`*a%%__Iyg18G z`se27Q9G@S%0fSZ@-%Cp0Pk@65aROG6-7$QuCHeG!uf6}@z5nOewx^hRm^6$Z*4?$h=*Pk+Iw8me zdlYmyH-<~MVmtgIv)XuPuGwj!jhs`js#E|x{xctOYH2l?@u~KBE+{fQJbZoDar69S zZ}Q{{N9F5PSD}EQIL6o05(vp`3eKPW&#iuCLAGNMIez_#S*hz|;o56f>%)13@Uzk* z&+P_WI8YcN6v8_n9x)6D>s-ZVp1bX=5Eu}E<^e%~db%8yPMue6cl5(1p#IqyV0GaD zfl%3115|Cc9;jvL*Lhv&QYVxvkJ;#BD2-X_59_plWky}1w=Q#m5oUZ}9xt;3FLlLS zdGl${_EhJsDfJRlMP#LYCYNSmJ+MhNMTbzT5ZFh&P|9ZI9aPC8%m8IIHx5eK!a&pUf5XX(D zM_XGU{hK;IC7nEV6JDpQtjHSIlFksTiHaie{l2Ggs}XLizGR;>SAgNTiav$`1WY3q zE$;4=p{iO4-54!W&(jv5`8ildWv!AijYTRpL$^L|Rp8hV0!5s(n5;8cx3w_mt^obdEY(4r8OqPq+l_>J-fLH-tUfcij2?ZF+g z_=uCx$$UN0tBTy^;wMxS>P%a}#U?q;o`=q)L4l{;H~y0^oS3 zY0sldDSr^Fk79$A0FIc$1*6w-!`jcR`OUk9JX_={?P?|th>JsHMty!Px(49ZdQD6? zgl~;bdF(0D7!D^50!7_ZUg#6E9PP5~^^7ioyu9zc`KdCWih&-3>R0(4+ciK)h7~Ax zgaF_%_w(gCRUbi|sP-T07FxCJx4>=)_;|Uz{L1e2|kvOG2b9qW9SIvZT%Q}Y?6E(IYd@3*f>LfM^rL-*RSF|yN{(5_EX!Y zYRLi3wDYv&us8x%QN15x}n!!)yCpZm;$_^xF?m3a9;HmvH#J z&&OPrwBY%^RO{ASldLmyR_`^nGf~6QmQnnfgEcFIDNgQ1cGmrAd7jz@{R3%)uLC|l z{$}*4#~G>|!ccun+^r%vd8_VnilK8OYB*dqN3R-A7;Awfe7zD4!tHpFtyPjic#DlJ zlPS4eCPL1{4f#l8Z{(&OP^y1b)S#)W8-JBYJ&c<^Zvi;m7Z^&c$6YQ|20XS!1)sl5?1qTagpq^T* z`6E?r)0o$gw!LKs$3ZObM)-%dX`pyz@p3pKSPlL?wpjXrRz*Z1BBSets#6jyS?4Il zam)PoG1G#KR8ib}(|QxZ7d&m$`AieQF{GOeFpsYg!9qB<0g`AjBmH_9K^MFG62QL{ zmGnv=aggYP0@xLEy0Si)t;VV0`-+*d_I_E{ru=#-``LGZY0R(l1rr3Us9_S<_9RJ8 zn-uEVcMD|jx^-A{s%8WOIKvDvlk7~4^Snp$(j!N|whE{UvcD2fG#E~#+1NYX{piiA zlCLYWK3bGodx<`SObX#d9%vDEMgLq@(fVfjG=OMF$+>{wHT!W+hILtHlGnIN8~d4J zj0NDpGdpb@8SJ*@lY|6tdTP9#0bDd;cBjOsbP9PlRP3u(lr@9ugQ?DEL-^szUDR|5 zzlB-6-+3{AV+9{9y0sL+I^>XDpeb|K~4Uy`M;^P1y|A5?I9qfip zvYXVqcI!~|xF_}JfC!Yu55fseUD9{1Fb0rTIE*Nd=Q9iGd<#I8I8NKFpU?98({ob| zQ76ku|vg3#)i@b_O+RP5tX<4>2GRx zvj$Gq4K6JSsqYtFHH%o6=s;_SfF1%%pbS}Z187`%a6y|W>YRV}Qsn1`AWl4$`S7EF zSY;*lBu*E>7TM-cH?y`^}1*A(Qr9?{lIZpT5>%QOTVz{37kKNC%%MLTY zIpa9Kb!?O9@)y#=?nKa;$3UP~@|k-qpsI4ExOD`#yE0B-r2z@NnztH6=}#QdT~#9T z=6ncJSmU2oYE@IMwIq*T=C`zkVi?E#Vs=#hjj-J|sY!jq9Y5%xF0h^Nc8N}Ov`Cjr z5Ey!8tPQnl3LsrQ<`Wtf(%1?0HGf8PwRq#7$dyx8Z7ujio_Ybx)q#D1qv1`j@wMy8u&WTb z#&P7=&ikX-%{`b(^@du#CV?N?>GQjUlSFPrR+hfukNKIT%WDl~Tw*1S6QN!gqB;d<8Xkbc7`%o>>cV((viF+}as$od)nK-6lv^(NuMN4qV3 z%eZe!!5Run2Il8zoBrM}PK02Y>NnzaNeMu|uK~>wc8~*|##}wl-~y-M-Cf z)YtXzK0G27AxX6PO=tT(|Lbdpg#76@-}B=>4F;>y2S%q{^Clj^;G+4zDxU@&@=`v6uc0`&T)puH)-q>G%pZ0CMB$h!vjMUF?`a(Ox#sBtpU; z6%wm>F7yj-k2)mQCy1(>um3ny)9HW}&Nc34ak)W*i7E$$V+wSSJ$mq!a8c-H7Mh0w zs1y^}i(F!2fi(IR?k~HxVagc!-UntrD|1i3h0kUfo=&Xir$U@C2GP*w`_9S~?B4LP z1IBar;Y03CxX017!SWk-f5w<(TCMVZe0lI0*8iNpH;}&z#~p?b&~e#i8!_9Xnu{P&_Hr#su#8`~? z^pt@&_$#4i+s2RPj3SqmR49%9yX*qyjlMgOPcQ?Xdul4A;KIi1<~)7+X8pqf1yuBD z|A27Mcii>!i=`tzckAflL%+G4A7j~?nj^p8YbIcYb6nLZ@-(%FAH()cI_Gp0xSTPv5zE%xLZq34aqiSQlvt6xVAYlFt32=>I|fNZ(~&E!6T#E%wL_JHREq zoc^G))tEmja@B9LLzF$}!j}eLS&svmitl0I8%;5(d?XGOvo!qGqjXd75Hl?~+1HM) z_uvoae7beN3G8_;etv#i$S!S7wbnB7n&p4#1LL(7E1{_Lm%wxvI%OetaiVO^+p_WH zg&sxUq=U>S$q#M!Pew)J2244#|s+w@mMgX4%EzLja(-KF{g+9ZhGk^VZMH&wThm?-gMV;uJD4tf{ zeZoDv+8f%%&~3ZLYf#6%`!pxRF5ZNc^G^a7ke4_(1Gk517-@BLbbD&z! z-aa$j;T>-|cH`5;{;+>o*sfI?{k{k4hLP2Id!V3I0wC_^+*2$z!IJys9v-Nh4&m(Vr_7JIQ(^~uE;_t1)8bm9`{k9 zrC;A3t8m7Eq{ex&WWG?;3>Bqa*NHT*&m@R$0or+FZtro&k8BakV_Efz+m)5P)H59b zE1dhDlve2VH}3bbgan;MsIA4~mi-+GHIh_!(O!f?hDSb1Xp#@L00ip#>13m_9cvoRWSJ~g^IopzK%enc91OC z$UoX6HISM;2_loDvu+Cm!3O2yusL#p%nkcMu|c4!pi$z3%zLc79e{WBOKXJM0)2bk zDD`zsT+0jTwdLt?klthiM4=muGZ_1FXTSJa{w;tHwWJg5tN;w1TV0wgnjK?nE&>F| zfRq6&hI5bGkj|AWFRC4vzWND!wl?1WumA{`nOU?~?p_(!2Bf8uIG1tdeX>E3^Y6U- zolz4yty;Hxrt9SmsF#jxZ4C z61(DjEmp_#%2raGzBgAXN6DBYSq+>t;Vmzl9)D<%6-yfnl@PYw8W^d*pqz7i+iv$F zx8LlLq!Ju)wms>lZ`@dl*T3xW&U68P?u?Fn^n4-wh~- zL&X%d><^m<^8di>U~g~_U%bQ0m`?m$tSGs`bx~dQp#ld z?%*;DhmHuA} z0==)CJ~tzF?HP{}rRC4UlY#!l3xnY)7lU!mA2`kOkBAe6`F5*V<`EMHzDgWKC-C1*kpgtS&r0>;Ng*Ddwk*xvXs@R`IKL3`nUwjA@~UaY5?388JikpR`;yIjdaQW*BZ-lFl` zPS4;ru={zKpUawoHthb>BH}Js9g19e(S#L~?SW@x9f$oF)(!<4w;JzXr|g1n zB0>O)l|Rs><+pc1>x4{>S=~He6zkkp@T%vwqsv3I3dAi2J3}dAbrDicJCA7ln{y?D zSI-$>rw)*e8Uq!dgDS;VBcI5mzHrwW;sDew_J`wyrC3>5GM(yxQONo%TombOy@rqT zy=_}EtLdzgK;;en(-ho98m2eD%xCcfK=Lu-2WLuw8Jw8Y7*w;& znT4Vh+nMbh&hr{E$@9B}%Gg3Y0K{_G)9e?taCG~|O-cjf8Pjd~#stl(vQ8a7P16md2OV$1V-yC%YRm||9hb?oaGW1=6x-eZs+iOGlI~|Q9kGG-v@*g``yQnd1-m1J>A0Fnr z2Wg3=BM2svO`t;S@@ZqB+udoDZgZ~)KAlPoeCeRm8?VSGYTP-P(rp4lF; zu|klUK8+Og`~VGyp~<<8S1aEcgo~Wn9{%56u2gR&cc zn1cuXpFTacS^vz-0{RWtGTOagTr|6b$&@lVV<$oct_7`q)$1`qzn zfBe1J|M}akQ-7l}&s`h44sZ05B>Wjz^qf1c;(Ry!)vDTbmk5L^lHif;;Y%B1QS@bi z5B@*s_6{@e-hFYi{sH%%E`g2TA|2h8Lw4J68;S7YalnJ*+LWp)bJd208kPJTL*u%H z;K5G>-%OkC!ub8vdeMI#<$?yD({9R5Y5`0fdH@F{L!C1cp8-ArP@hDCI_9r#@BdP` za50Z#+*nA2c)e&@Fa18H3h|X100wr6S@aig-nOg%5x-=@3cT7wXZgW98-5KW66@VW z?TjlHhlSWYL46WY)DLXc(HeV#a|GtP1uKst{RQUK|1YLckg;$JZ^(emKxw#IC%}cH znO?@ncT_#&9dCGkS~E#A#nW3tasPS`jr@@bNRGD%+PS~?{kwNJ9WjfmxUZac6i))v z*4J*MWp3>@0&)dlBjiYKl0xO@$9IiR@T>*Td&qnEMC zvD^1Zs?tpQZvYYZagc#fHCK`lBjyqF64UCMRFvzQekttTBW@J}9($yw83)Glr-A0c zn)}?CxphZ&29&eyN)p|sph*ySnb6-Yj#GlJbVZQg3v?w}ywJOx6f!s`J~ z+u|;AjCSNCX{746|FK`z3VOZl(4%Ik!!_tbQpD8B=(S}tiM6z{)#FVfUaY2@$jtQF zHr*W@o^!VWxR@Vn(X-8gWz-hHPZ*nRHOi~p@6)x;Sd)SHu6a)NUK{goa*M;U znx&p@w)7hiJs;;W3g_=#+Z++R60}6zs`xBEL;A8vhlzrrj)7n!8zdk*DPb_QJYAoM z5}R%&{R}c}uL7*G&}oRRq1sAO;;%Ah`sbv1Gu6`qZ3wck?95H6-duZR(+qesa@(`a z%%oUTKD#3QOH7n+N0L^Wb9=J82HL?)cBA#HN9$!wKvfihxb0ly3JfwSWbTRngZe4k z&rAD0ZB@dipIs~+d-6)$j!~I11)BE&gV36ivlct zXuiOHjfG46#oEsDZ~-dN4muuh>f@(kYRbsl7r1LvXak!r&z|+N2~#-r8q2ir)sZ-i z$>Ps7i1sM`VpbFKMM3R#u_+u7`VETQ^Tj;Jh}DTVqd=Jy6gk*h;9y25KczIZ*|MFJ zF#Y@hD5J1Xc$LCThaz2~PRuqF+IM-wITTOISCE%o{_(c`xO}95`YU7P{`;*PBMG$4 zL913+J_i=J@fUtLdvd;DTCr%SN#l<7&b8lvMqWP5oIw*2To6Adg2l`jrWsC)_Abx6 zRTgX6Zk_Z3Y;-VOp%-O}5Odsy_>$yxq@mqY$*cPeY9W0N>~O$=m!xoCYeT(^E4MnE zMIL^Fjy_L3$afo{qEp0Qt}81>Sx4h3I*rWqaVU9%MmzS4qt^-_mkTuEZOEtAa(Sli8x@cTU`g^a zF2*$JF1GJx4tTZnL(Ly1c10v*`pYf$M+K4%8YBJd9y^=J&}>A zx(#6KzgA3VW!rMv?vTnzz8@(K>gC!#nzjW4J3XdrC02?|^G{f{{e|FmLEgmKZ<(C| zUa#e*!OP{6z&C9J%^~jBe8rai`Qvb{7UcA0?=wk1@_EeAZzEa5GZIUtx6=6i@*1|W zMqL}wY}C(vE-qaqXO2ZB8+X8sXyBiWunXAIZAf6oakF6&r2yFLSaQN7Uitk6rxEj! z49-#KD(+yES~6a9wBB(EhBJfD@mtFsq!sM;UV9*wRy+_FZ1}z9cD02f_J>W`igVuW zpv~3aONzM4m_MGupX`T7*t*oBiQ`&48l2a_k9Ug4Y}x&MXq3-FwUPcv8vr?Tt(!uv z_%q9Zbk@3`31>R)?M#oyq|<=m=;x%N7ESd;E#ljK0LX%sH>B#?9_pMWxYf=aqe&3% z2&{^KkPFVZ_@=NE9=RixWr^Ux4(b#h$r)^hL>;9x#xs1b@iFZP5Ub*ib_}&zZJp^g zEOyN(0fk=bOb^`r;}T^H54~0whHRpJZ7ByY#dpEA)$e;1h;f$$LCac8=+~XR0?02F zi)%j2)O}$mt1|L^BaH5Sed6cGLpDnpmHUoP)>~?}e*c^l%tT+d{Igoe$*D5hZIBbO z4Q{j}VBZa|)99-&bMx79&6`SZf|z#3`y`f}D~-Nq6Q2|x@EVb|>5_E0KujRAX%hy+ zLCgk)jh*F(0*QpFBvjmhoG92SmgQ-?wXSp$v@$3fNKkDgGD(CTuUc?EaFMCSv2<}H z%Lc7k%G6>^yAEwKeO72cii z^(jNLW)z%Z*;8B~jUc#|JX<3U#fjFmXlZ$qVHu)1pcl;mri3^!!E?Bhr4Uot=Z zkh(+PQ+{gqd#h9J0rWdRiS->@fDcee3ZvBXm&fnNer*=F=^Caqn(KkmQz#)>GLrRv zys%)-WAEuEPWpZ=@_Ezpv(Fy3+$Q|x z1;{wgBmqvYPsdIS`r=Djz-VT40>|UGPEE{w3-hIhL^x*`!jKqrTiff}U($+PJCv=?5L^ zhr!?~QI&OG@PA)4FAVVP$@JxCmk3ujILT0}w{VV2Ts(+Iztl}vk?nuK#DBbFuJ71O zmoCAJt`M&HuD@vh-L#wogU;>$#}@?>^@!|uvno1h4O%f!wt|0rb0>rpa`W;Ix$jXv zLql$07?vwVI&{dAK)~ZEiN6j}K{?dvhnHM2c4||6A#|AM9I8L8M;hZ=AV$oxyD;Z{ z<@ELKC8CE8`a6&zumfrAmpq-nH{avZuVawfwBE-jzuFlCQX{#2Ja@`R82F}@D)Npy zeP{NRKA})|j_c(=3Ym?d5Ae!?z9fAgzsrsCG>m|>4%AIXF6rQvR^yJ@2B6YWE#vRJb2OtxZ}D zlW^lq$UCTUfp*Hi;E#7_n@R?9tZmZve>Z#D>1MZ&+DneZ<9)HhAR-5uK6c=~fHmiH zGc{TKS9(CB&v%xSiR{T2GlsGR z`jbK}OT%0=h2(nlH0Rk;DzlapUpTGSFs-**(W4Ipc<`3O)9cJ16EM0QLb?>?38?S+ zkw=;mEIgTu*q7(Yg9Z>Vu(kE3U%W^451tpx_*c;v0(x;x)HExnc*=L#9f&pMqWc+L z4o-mBD~$jE9K-e$4`Q)7X@G18sc|V!r-1?6MYpguqN@`wC)7YfYy8>Kgl$mbi!0Bq zb{K=Uu9ArS=Fo5Km9c($P~S=*HMoA46F7`#jGD}HwkWUA^e*z>ib~Y z5+C^a3QgKe@*v{7HeNCG3d%ztYE9N@vVocriZ4Rp6e9(6C<`B;8oDOYWp_G0kL{7! z(-ExH-F5B(w2Rod$e_9ag%-a4nchh*d4z>mYp#JU>LZ+OinO@}4ntA)b(7xn(jJQ= z3FFXECv4jz*#KnvRmg*=<6rR|R}Bgb9tS1HEn|Z&*;%z-W1%)Lx=s$)2}D+a>BPqh zFoe0ePTNkzVFEXkB|VY<7SK=jwi0}?5y**4AksCo=VE@;g?NquE-GtYmGm!nZOl+r z3YY&O@|bCvdHHs^{y2Sd^%Hvbz%P6rs zvHsa#g?ly$Jy|A7!DnG%=_;Vi5qM%hG&*h4lZ?L5mqS%;%4SM#^YwJ-+70C2kr>g3 zIysOe(2liWBV97Tfc452j*X`Y^feL~2+@Gnt=OAV+^B2ISnM`hx?pDFwt_Lb(Ji0u zwK~KflE9^FoOmIH)&*%;j9;|)?}n+afjEN>6pJe%Q33%2RoikPX&EJ7F(o4a0xVai z(?j*Y6BmeE1pZz-22iSa(MIlwv|-vbM9*w+ZP-9^IK zcg14OU_mlQ#w*Aj3T;s$Y&HfU$qJZK8*3@sF<4;jph_9*p~zeI^S@P^fA&eNSZp#t zsug(&FzTG{j5Iid!e;EkjN!mZc141C7o=fyiGhh?M9&4>g*vHgD7g*#c!@wZjSq&- zVMF`xn>~#j{j`P3>B>isS?m!n~3d< z$sGj7VSAh9eJ+2ONt212bF092r|AN6*$oK`t0&8$L@iiZjN=MvX=?fvYVek+Jn>yZ znAUv)xfbekU69-e%8mT7IvMOD_gjPDNy)8NuC9g;wm|iReH4b<@Mrb2D^c_}wehq^ z*SOUBe&|*zaWiYgXTZHWk**+=3c3*RX8~*#<}9H4^670UYk}xt;)@aewajPklSBhI z9qkT|)_^uo=rRr>)Pz&OF0|9~)!|_zTmiGE&BNm#Sr&P;8w8H3=MJ$(yhoXg*U3|C(pZ223`S~0^AD47H88S;X9c_{=PF^7%q zRGE4~&tc3lOtc~3dYo~%=oj^g#xnCVp9^PZx3xI#CkthHOfL!;$IZ4HdM^;sQPORL z{RJ@RK0=H_ySJ{75^WaPL_UF86e&j{C9l8P&7+qlP>bx8;hDYa-j^)PM^&EIW->7Mae`+##|kMQ zezu%pk}-s=7!K0U-smO|6e|PHF3VD^IQCFZZdl8eYJn`=5B-w;vYz>ZtqbLwwueL& zC}=Nk5&O)W*Va=drdg0uM$!jJTC=W||kv)RL0)@=}ZTr_CVi_Gvet2T6Yrakrx#l> zCcDM)-l%=6w;R_cQY)vedL&^!erwKyFp37cabw@M_oQ7HEmXEid#+dlorbB$ABL4R zzP8_4ADMkM2-l*r31VWHhNZy@QO^GZiHZrFVr$}Fi;jBvcF)&vxv&u)V8G>9Jxvo~ zqjJ(!m^p1;;KWE^gu2eV2VsNJYJNc;gn}y2^_U&2ebv4Hkr_*lMD;srl`b>mD|elD zo9q}%k-V|{HpYhHsn!%+0uFb3IVhQKbaGIPKn&oVcvGM?4pCeo)%yr;@+lQNw<-B- zH_bEesmSato_0F2YeH!>bzo>G#q4SH-VkXjE5zS`R_Flh6itL^Z!?c#i18{nZ;3Vb z%Rh>t2`|%*Gyn;ZxI-5pUe6V_8OYiTTLI~4sn?U9KLO{?0mcFnf+kyT-Jh8)*oTQC z`8F+9<8{my;sS+@da%t|VYc4IdC3#}x{uA~L(hyZhjLJPH_QUaBjUI1vvE78e7miH zbYhK!aNnR$6h$RfCu=aEF<{lpJ~JiL*5t#3>!Y}lFcgx7?#zi50Q$0KvEIt5!Ou#c z8#V~@w#EyCtq~5s>+M&_OR-ZWnH|UefAtH{ksl1F9SRhkrYLVN3I(d6h$h0 zqOR(i5gb_h{z{=IP(Cu~dkazlT$4!It^tanc|aa|aDW$%Hax#!u6ME=XrB%^U%ICN zwCg0%Zb}A>W#ShCfw*izZ#D7j|6-NG=b@r7xZQ~gK_qrW-Zd}>UjqusamckS;Q3L{ zUv`!q*@Kfu8F6!Dn+ z!(9U{nFQ4|po(l{Y7NLI0&^k#AO(r15lMz%H30gH=D~@V3&dDQQMQ4{cn*jS`%+`M zy4bb!FW1XVKbVx<4OyW03L$KSN|ek9rUm#7r_TE=@`e0owWihl<=c}B7>XC_rNSm^ zeGC`OrQtS^>y~z)7QT`x7}Iv?%qF{@cWmVpi-&nlwcbrRYWPQI#f)>U`nKDd5!1 zwQ3hdfouBy6CnI_UV_^E8Y7*6WI|>znmkrW(*^g#<+b`AFOrs8oX;K~23nS_{;B~7 zwV9dE($PAdJ^S))svrAVBDTBISDRVwvQWOUj!vhM>SdzP@RgJ42duGY>0TBve#ON! z?l6DOP4-`2iR>;=#?A9coG6(0h#Imt15EztG^cO!W= z+xc9LmZ_%~alc*YPlMf!<5u_um1!l35pJ9okl95a5To9Z0?80a`5G*ia6~FFc^OXS zZX)}Utu|qL`^B&m*;j)yzZYAH6W=A$#igb=^?@A(ZEtFS`^2j%Kr8uJtwPKTDHsZu zElc^Chln59dFT_$I}tS&-x+iAhW_So(RSnsc2P=c2hdlvmCe6t9$^w5^Aqu+0X@fU zcIkutpfzFXv%8Ij!hx-y+4`6IpQ-3qDFo&qeSp(&Lj~K%XIf4xWaom^e@C694%~gsp7HzDaeP*x>w<_--|=n7ES|8uXI74V`)iYJ z4`f?twsA#>euAhJog$zvx_8+KTuA)g;{gwCkQ+G{?J*&x6_g2b2ArP~kq1BpOM&f) zT-`A9ZnyNe2S>Yx;Iy?P7ZX%Yd}ga*Y7G$4%D8ftV+;YE)?N9ZQn=?qa(PqwmN%V( z0lp#@1alm&g0tcLgS%B#)reP0E@d#j8!;=4A?IJq5VQ!s;{8yOnnW$&0+|skF3Yyv z??hca3_}%&@7zun`1Ztm7Z?K1j?mib`_q)Oi$l{bHiW^)W&BM_L$6KaM8E%RV!Mfi zuP|yzLM}l>d@~(efKVNxHbwzndl%dWhmm0Pt|6-Kn4A_zS_44Y~WvA>br{kh%=SRo)6lk=? zg!`NeY;5Aq+quVe`onjQNXl)&^8_gcUt8>xZxK(~8hX_NVePtXlV7P39Nu@qk6oI` zHdM*YBK?8tGUSW7nJT)M#OkiL`OAwb?w^YEY?^q^V|2at0+iEhm8T6u?J}-%Bwkcj znxj@nwXyKAT9&`xDW&3vZ)Z_ukS2=1Kh@On*h05b1T=Bh5Q5X@b!YK3PUE#YLlKXD zb{l)nMZSxR)Lu`Um2aBUE@SSLt0A4jC9qfK92VBHXoalR6sBS~i9YOJ_6O2mZn7TH z=Teph0mz(k_0-4MHr@Xv>Wnkqi8jY(O;T=x!b)^%S3U!sf+3z$ zzaYi$7l>{*h3g)I@1ouR!09o7Wzxaa@xjrPnu0TVtnfrrD?#%&cA?u4Xg?y)_)Vl$rW@+GKxtvYOn02qmG$7isNTcdzs zT!QqKF(mazC5G2hWzYxb>jeQHve<^iv{G~fhi0BUsB&Q0y#DF|`!aY8)k~E3Z6+@> z^argBMvsJ;Jjy_qaVnCS5Nk`O>!h7Qqk?8C{02TjsN9V$R6ag(_%|m8{N5a7{d5Am zw?FSu2e77NlvQqIs{+@{}`NW*z(q5ns!?%X3@A3C`iI0bd4%3x)%uW<)EFX`U<$ z6SH(r$hVaO)_nYK>7KnOrI9TQB}7e>meiaemhs!=K`kMnD_gifFy9mU$H?cRtEY5u zU;#t1oE-F@PozR_EcOi8CDo8UNU=U<{+P#{RTA)i-F9!cwZQs}DO#Mgxn6j)*jQ>j z0w{bWqDFD7jHm8cJbA$2w9jk(TY`B7bN$oWU6})*5n)l~eD8Y z`|k`M32YMbY*p9PWTVs^XnAeq_c{e2Qyp^hN%XkJMnb*>RF87ukhK8A^R!ZBVlmel z?fEYv>V6IkXB{9K9|K1WqO4p_)aHS9M+X5j|n@SHaD zb^GR9c3SGz_vz?7knkmGB(>}y9%W}M0(_!d^~7V$<7n^2La5hl;!&f=4U8+MW>K1vVy> zK|2v5S{|^i<;7Vhj%1{@G?!+aEpye09F0DDP5`l6DK^=qdjp!7oy z@qv4TJD5LVisDS5p$MKFQKp}|Gf4QMq!cH>-oVrfvh}?as%@+N{r%~cKOJI_p!0}i z8XDM&%YP*s7(OG~?sy__P`$dgQP5^1r)dn*25io|vN6I8-;RtFX-s9I(I{8-$|DYJQ zQ9Vj+fa8!g#nc0`!Z12|aBlj|H0xLL|2b^C3b}Ch0jnaEdV316=3+j{oMBxhq<{I?$G7>HF4WL;pW6FfMwPe^=X_|B==C%SHgFEDwvvWG zv99Ab0^Er-4Zw8tiS|Jn8b?*LO%3!{-7esiQ6lrCY)cp<-YbYawff}g)2|e1cOtKb z^CzY^3kh=+!s61Y-`goRrzhZvIQS~H9uaiR81-M9n&k(D$59*vrd7lRe53#jk$9-Z zNU$70O16m*-!qiM5Bm4A9)u-p?6Wb-XZteMAFs8RVbF==`uA|Zz zq9&gEz>TAcr@F@wMzzQ%8Q*w2ug5(JQu->ll^_Q&;DNZm5|aO_bpsWUHek1>mDn>? z$h?7<_dw7&QxAVz03_w3=)5zQA-5uCf|XMS;;n?SohVHi99VTC>MN`E4=O@0_ZXib z*i~06S7aio&p!9z19?{?TdoljYk_n_ZkPQsk)8F`nb2L%Q3TWSRRBD5sCnKPffAKZ zN@7pt{GkuUqr-zK=lf0Xi_RVM_88!swOSp&=BXLQu--QZf*9VI-W^)M27)u%Z-w8^ z+JTSZ!cdL~{=05#yZB1IA5O`)`|9gy1J8j!@{YMdelg|f?J%u12~>eRx*V-Gkw{oZo_+BG&LgA( zXK3=bM(iur)a}F5inD1?E2!K)-VJD7v&aJ7mfmAHIcK=^oCbtX*Y1BUEOD{>vFw*H zis8AA`)|1$Esy@O{_|wMdzE&jE*v6A$}UH3Bg@dS4GY|-(cc%$Kl@gJRagI*41GPcU|0^- zhn;Y?`KmDAGNYWvx#ehQ`89J3OFUZtQPW^P9r;b-zF62cZAE{EPZe5&U%m=BTvRUde^clm2{B;H z1mZ^h$_6d-@3*sq5z?lg(CFXP+)9n(|JU2%QX#*+vz?VxO}Ix(<&*sL%g*pYv@rei zKQ~s=lkkTzaG8VB3iJNY|MHOzqQ!iV#pJ&|B2{F4`g&+j^9+2r`tC&p%WZ2-<~~pN(f6wu0}%+d|6WV54L@RKucDG_`};GO`#?JL zH(Lx$z&RY_o&y+1I@IHx5Q)$7Nowl`%E_Hr(W6H%=Ft&B{BzJ5#P443OgKSNfMs?4 zo|rHe;ZT>6Rd-ylk`BR>L{rl?C`+PZqoIXaKMDxe%13_H*PM1xyJjOZ>d@YS3>PTe z2St3}(sQZ)Ne0z{F0dz$=*v+UJ~Y-=Fqdr(;#~D$JlPoNO|y~4-U7xmg=sZM!saQsjkTutRf?XE^C;B=G<3KoA*cqT>0ZD=952Z z2z&;)H8nL~OJ$}Hsq$di{+!>^2MMvO^(kxdv)#?WS`U_%Ee!6K9t<%VgVxh|sb@cg z48pqYu#DtS@OztY2R0UHikOZI;8dB)^X#K_%Ko+Ih^boALl2LI~jZ zl#V=GThx7wYH_6l!mSBQ6cK7D?lu?FJImFPBnko#^q|X20qrQ{DD-wNO_te$4rpsc zFWgryt4O+iJF=w;;z3o7{}5;9yNH24&UrN7ZGHy8%XUt)h$pcsl_-;h(B}@C`W{sj z;kp8TgH*YOstq!CL~yDvxcT1d2>ucWh}iCq66f)=-2qd`JX%3E>{z{Of(`! zpM+wU^MD^Ibjt9aFX}D06hujFl7Y2{db4l^Ljmms(!OovpSYN<`G|OVrXp(&#pEj{ z$gOCcw<-_h&}n#!svD_2=X)kk&H#;dK7TMA z4j|!-cPs~6e33C2-E^DA0b6%4VbcfJ<$fnh-|Ws?Z794hU;s+3)h4iRjJG5$0X}1( z*#$CnOP5}+yh!)&iSaa13Yv}7Pjs}7Ug*Vu*d@-$_tF5*#TWhkpmQ|vAAc45h>@-d zWjV>M%slg`W_*DNETsF-1BJS!Zl?tVM5Jc-13aQXasu0oq*Br@6WI_rqzyU?MIBFp zlkFfO{~ZAW0YIrT(Y{{0cd5_PU7mR#`%3Avrftbc;KWX=`1&R8g3non3wz+s{Tq5-Vw=)*i%yHDfnt zZ0ze~p2cwQBSA37^-@))XR|?ROe?nO5Y-VQlozCT;R3cN`|5ui@iRxqCMW<-ACB`y zAa>@9zXC4y7NV9;|0mgXZ?4<8@IxeFN=B49mA$|wjBZiRmHJ0(}~dp zD(u#oU{VX|&?a1A55LL{Je~{Cec%^a+RT6=p#+h>ul!G)J25A8a zC;PO>&yp@}kZRdKOSXzk2p=5s=(^Px*}(Th|K?~V-WuT(3(#*RL5-9^XzgANX; ztI&vN0g;h!Gy|HDHtBa@^bdGJq9_YYD{cHz!!9IH3y((rJVxKo+#o!UynC^aH!9Eg zWNEqI0nR(MAbOPgsRLg%{sE*}a?|Gj=`kX+K^=yEa*Dy!%nU#*S;MPT3y$BH(OogH z9%RgYa9e-=!9U+P`3gJ4*ufkG?OUPLZ!@%HxCZI8H{k{irMCZ<@g@35vlM(}dgJB# z?j`=}RUU3Wlb^NI4S((s~n`t^#RI% z-XY&{S_Rdek=#eAQrmw&1>>oysbt#9-c1`F-f*AXSLlO;f4aW=jL+Qu`P%>aUH@0z z;txvCsri35m;cv){nx*e53)dz;z3_i*i-BO#J^tsuqx1zqJ6)THez$pH>bv^>YK?Y z7D`Rg{`YU;ipPSQ`XkA2q>NTZ1~YKR7}9>`An&^k_-ID2%GV`OWa%P*>p0}`HB5`p zQeQ6>utu?d>5uVj3=z&9=FY&GY?cN1ITy-Wo!(NfameV)^JoN{;Fd>AmF}I4+HNMd z|JWOrP#36WI5LPT=G{eb#p(~q4FnLnB)cu4Xtb|EqqdNm286sV-fK&}dcfl+18^Qs zifG*jWst-g$ZCpl^C=?kw>xc$gc{bM%Iho`in0VFY(_P%sHWEA?KTPKyG-=dZzWZ2 zGTeH8!efUcfUPC!#QT7M9xVch)B4MsTjX#pnq!YICOEZ2V5KH4NO z19(ln=McaAhOP)kMIbHq>K7`5uTklz$_01Q@~lKJHtfYUWp|J!+ABJup0>>l1i6ej zf0#Oh1mKSt0OYFR_2JP$JrrmUDI&NsX;9)6j_ePDdOF{@6)BOxqFpC4iArOL#*dg1b}U9C!Y5i=$(&OV?!-^3$h{La?0J(@~uo)7z;Hp2d;1 z}}--ZY<}9mqc8`(92&Qinb8m+2swN_)|=T+E_*_^uiKXP0+e$6>-e9s{rc zd*&Pvu9grM$58WpOpNHiAgAjhB=?)@12=e+_fltX;SmX92K&nu2PfOL^?e3ww15=Z7xK8GH`wc)t;!*`aOc%t`F~PyN|7x z>Ir&_MOE~C;!6)l7`bTojUvlnV;GIO-oxvv=sh2ZeM!1eYEJ*|DG6gq+0!IH8tq1Y zRR+=RQh%yHy40SqTAzRYoBe|->mihW0FlIPAUv=V&`UgmB7wqu^%ty>1z0)`w3-K0 zG^8ky0WkEjkD%Etl-G3yBrCc6Z-w&{_2V)}fLI)yu@P)rx+Ym1aUOWVYIX6KH6d#w=q2nvj}v=v=6jTfMB1q`{>)AoN&zLQ>lES9;=+0if|BI}_#i z5J*bEw9Vc%`Q8RHIP>v*Bwms9hV34bIe|2|O)GQ~SgsuL$V?h&&L?uqRu?Vv_H~0M z?Q~G;)~FlQNIf{N?OFMz#QKW+!d(c@L1HhmmiK|5)dt?)>V!-l#93K^*&?3i9SlUV zu8%sA+8-tAfI9Fz!vGG|YHQIH{wZY}c}b#Z%~&2c>BeW`~>A1&-$P-+i@JikQ7Q ztvx=b3$C7V7+skS?5b>KBJ$CaC9o9sJ#ZcOvi2+DV_pbK+TRbKNcO9WC0b?uc|_QFc@o5OS-K$|9Wh7gqpFk zQd#ySWaOivm)3&clr44m3Fj;5g#WYyv1r&M^PaxadbzbO?1;Lr%E=pkxWbRhl3(aJ zeof~}She+Kl99eQRS(iVP^_!Ib0ajDC=y8T18*~z)9x+oL1Hnjgnh7K`!ntL)~w zOigQqk735>#wVV+s4DRGj1o6bNqk>;YnrTWr(AptH=hs8&5^e2BMI)80=g^@7cdAg zVb19M#B;c+oB^!JZssamUMD4SC z0w{((;6<6(6F5j{NQE(ok%TZT=HGJ>!rZy<=tX9Hui2 z@5NY-Y)%V|*OUCM5~%gox|6=XdrUKNzkgr&7qV zvK6_l)XzDCZA$$5*E1ac^IHadhfFp=N6ur}SOq?TSXz-*+{dn#DSyFMx7}`aP&j%B zEa!ThNZXr1yJ@pFoWuH>4_b;F3)Lxqq0SQNCvd;wXQPY7V%hw`aRLD1FosEYO-bjp zuCzkjWV7#5c8mTi1Y9A&Y2+8cX!}1#NP_@nQ$-#cWaDvZL5^4r*^zh)ih83Hyr6tA zL;rR=R}(Pn%3&CplM=07&d)2#xm3t`?E1Fd@z$=`=s?t62c~UZElN}rHy8zjfRhfI z|JxL>LyQ==3P!FxaBTXl_>l-MJbX8k+hpE^{E;J1|W2 z=gh%oGz@4Dtz=r)0Gh)<`7!eA#aX}K_8%D^F8^Q>1$Wzm@}jxY5`pG!;0{uKf{6X0p-UQrGKSKefNwT911_N?-^U zi^q3>I9e%g1gfmB%F+-aVo)VN{>t&?{3qc-u@6R3!g-_p&WhZEpV@9oyZ$gXennYJ zKTq|(1P8$0L!mTTC znjSm5`!l$(k|o86FZr32l}6J`05zSM1}Siug8XK)GPlv90@l@e`B$=QQ+Az{y9B~L zS>ac9Ejnh{+oRqZI09`ULK)@`6}jF&okS*Y42}N1pqL22*>=!MX7Kgx z+~X+7mrq8x(=Fx2HD3(V;fIO{f3<{nO(8DMc@xY7d&PUPtP0DTl!`_PUQm1P&~K90-KD zfw5zdSW^8G2IPrha{|7=j35vIL@-%^AfP&BQ$kEPMA8_jDzf5WxZB)Bw)f^OU z0dq{ofO#>L=pE3<>-eK30OA3VaISj9tl^GbYlz_@*sZ@}MTBDLUID(&v7}aaWR#HAppNHh4I&)LwjKaii zd%Bcm7fk0bHX}KmXb4y~-5wDFpCoLjmU931a*VNJ0`fb^lLi-ZSduH@+w(==dcWIV zfe|23d?;q#_zpXd=8!>y$yt$m&9#8oXg3BJ8}`((qxtDFU5Hk-&H># zHF|Z;BOUsE>%uBzKK6!PcHD-(q6$K8N9cYEss10v-a0PobnPFP5fDKZ6$Akh5JlI01Ud@U;w40J6x2`QHD+dk(3(g-}Q0No@e*@$v)pd&Uu~nun3=- zx$o^gQ`f+s(-nj@n*V@$NfH*b0)Rjj-fS?T)&Yv+P5(BC6GrtBc;oraH`6qGC;i+jR zpSDAKX&DGQD@#kuDLgZ&`wS^R`&cFn;9i`3eBHpi$};8pryLkU*HCZ>w7feT9Me%WtJ42+ zBA+{v3BKG9SNX&EfP|@rl34ZTt2*ukn@z$0vge+M=lo1Qn2$t0%fHwA>F2gmq<5xt z3x?#o&>%2q2grG!2v-Ii4f(8kcbR`X@UXgpF#05Q-`xn%r>QT6rk?`&vjxyjZm9t^ z6|lA?pzIA+1H!)jrC>IIKig25JV^cli$fUvY{#0fTts#Aq|KKC0pPTn22|f;m!*;6 z+=_4PNK%f~ za&2_3T)EO@u0EX@yATr*ank9TxX?9Yx#E>C)Zg}I4}Q-X0AzChaIX9Fcm^~-M_71x z_)Ikt;7(L6?zU)yqyoe6Q|^52+3DY5yt@x3tR>e*goY{Ihb_Hq7cTxjNLH|(Yz!@B zmV10R^eiN1<^PWH6(TAprZ{T@8l*ySa1a%AAhS$ID$KZuwwq{(N&_neSyzoApkaf; zj$pW@TXGsVVCC)u$YmCseHBOxg(jeYZshxn;?q15{TO9>ExR82S3Qvxk7?qtckPo! z?Vr`KIIf18ANV>ll@P8;SOJ>7-T`$_D3nLE+9ywQM63!T?S7b^mwK;a@vwL;1sIVQ zWg(47!Zv+Ii27|CI?WtLbuw&0q#$hBa~@HxlAq?So9;}thkMqc5z!c^FWW)fc5jiT zI#QhRlZy6!JxwoQ_KYlu?d<~aZ-|!KEd_rawAe`S5O-$pJLButefck7zHZGen;^Bn z5n|7?+CHc8II-7LeXLy<-=4}m^PFt2qm|2Da{Is&?cD{?OMQR0-XzI|B5_A)Yio~~ zH7!9MUSEzIMoEAOi-;;JAqvVCC_7L@Yhe>?NbmZ9a~F__)jCg`0ec|hjXDquNN)Kw zU1=VFI9UCJO@A`rV3iyz=tpRMz#$6|2lRq8KzJH;2C$7 zttUOjUY4f6T&B8hAe zD<7i9ezqLe%u=Pdv9~H&=O#O~upwhc>@G$o0p+YiuT1ck(Ql`lXs)XD5=Xmr3kCoa z2c|9;Sm3k~3SQc`Sl6yy*L(p!B*zH5dazh7X6E|#EN4ne%3&&iKHan}piDP-7n0d2l-@c;{}s`W zt{D>6FzYdV++*mfHscZRP_OM!@B9rR%nky=A5wNLjch7Ai88>+;e>_@XKL#q5`v%` zYCF*o%&jWoJgtPZ3}+cGz?A7~%5sNJE#9f+38G6+1^}B|m%FqXYCmc3gJaIox{b{m z*~QT=Uv_5Lf8c=jjkU!iSyJfRww;Hs`2_ z=b8lwaN-BGt_=YOrEk>X@?|BkVDK>oBhnq631ZN8q8}A*ON+hsi6ZtRC(OT9z>Ho1 zCv!W}5rVyJz^*I?WASRiY=|gZrkr&-+Z?3Wn!ayOk&JBeu(HXVHl2PXe(moG z?d2Whm49Uc{I934z?rq&%9CFB0%mO29SoCUqQ6VQtWSzbAin0Os(XQk55!zYyr4LM-I1xI zOe0LcSTBO&V^8BOs6sh(-*{C#TqQKN=KZG`JgdbaCr#UJeEOgt6KKFKLXG+-$I8BX z4EogllEs7R0jH~uwm6+v>1*07YjzueZKdf8utJ7)M2iugcgVo9~LK9zmPC7 zp~2Xu55(zQeaRb&m0g!g1+(oZ>aRG%gz9#6T=9hGt{L+;n2 zZTRbDE&-kPSnUM``ZSg5V=Xws0frS~#XTbPor2AWd-+;_vk~paf!v)Fr7H0mg0i89 zqK|>6z0~*r*O0z6xU$dmZS#~n>`g}1rI~({l1v9IP(f0JLNKa3XFIf-0$|?ML_Op&VJVM0wTw>gfHb0P&4$_mU z)eQ;t7G;4Tf&n5b8(5BA(4`iBy*E;Bp&J2P$9KCEP5c~*xlc+S=(*$LlsUxx*bp)c z{SA$PrbGI^{b&YdQ7t5&F8*mA3NBJP0zkXHqWXj9O*|(1nJOR=@hMF&a=;@mkky%JyAj>Lo0i zrjNI;tyxD#<7A}tIwCT1*cJRpehkNpE>!+G8;6lq{`#Bw%VZDD-s}unB=^#tfORaEQF*rW^?Z7>x zj}q33bHJpJSkDBX_>l*0sUt01AF?u1tdM{NaJiN@!eC$u_?tI6<$AomL5VJ;2K9*v z{+xboXuo-UJ$U*}lF=JinX!T0D-T6WOY1SR1p;z}bL~Y!eW?dvOd1k{hMIh6x~i}> zeLUL-<+f8BwLd1vN^7Ap2{B&Tf-Y9rr0Muv_p88GBlX$@lnyBG;4xHa*ZS@%X?MAE#f0PwcUnPkxe!qy zghF$x=g)!a)Xh3=b{lBfQXn7(s4UbG)*F%RfdcyL1`X%ku^Nb&wu3)?2f3Qskg#r3 z=MO;ko_8Xj@nZ>JB!{{n1T@~IfG7Qw$pSr70{r&?M072pekV;r3Le(WBlqx);KQ+n z&fUdVMrj@8W!e&d|5D{aj$0-`4sci?i6e*K?)I)2M;GqVNsEwdcHEj&srqV56&(2 zlVxtsbd|K2PygTZbC`dG5}X(>XgXe<8>8vEe5l;ij;-tUJU>wz7}?b8-oU<$alWJJ z1Py+<=S_i~(wqH=SXJ4za_9Mnc5tL48@(D{paIYqQ)IYx>z3OTOSE>W{bYVll!%+9N(?j`d75X#|o+ko-z$9O-BS(9wAt;CHbbMAIu)6&nc23aQd? zBxA~D5>ck4NoYyO?^y#^AkT(x9&OJuNi zODPz5F9Zg(NzxKvj^T4H_V~@HA%=fQ&-3iR(C;UkIyy>7Yj;rM^l$-O@*prZKV^=D zkGRbCT$sNA^S=o|>*QWT60qkY$B}V}*vBU24t3AzgE6W zZ6N_>IX*ca1l*ew; zW3RirNAxcL!Hye%><5QGzW!Qch2}+V4Hx*f5B+S;!G*mZ`>la3^U0eghIaP0zPN4E!)m3!~h|(JNL~I;_Ik#vm zu>*D^bPnRzweK4=-;9293}f| z)d7?18@jJ`o*ncZ?#anSPfgsLyzQX?!J!w^PCfxe(D-jy#%nVF591Ai=w*^Yl7=__ zEtjS>&GS*Y%nW zV>td@Lx_N=pc%yv!zKR9DeHgjMvvW(b?t|h;tb7@b-Fk8cw+Kiq?uj2_ISHV7L7<%jt4!Rs-==VAfZeCIV3`4dPGZ-pdL{o zi5?)ced`eE;EE=Svdz4upzs8sx>N3FAI)XnC58r*@}%Kk;IAC?&Od(14=MfL z8=-Z3CKFC-(Js=WLd|B`*X+7DG#i36BtpT(dlvC5_ACv z>}aO=VFz;(s!AwxvU+FufGiHIO}$j^s|hq~8ly)qCK-*R96c}pA7gsj84{2~o~b%- zcLF_eQguUDM@Pp|*>G{XdKQy74{@=DASt=-z2vtu$p|yw@8uze756Q{h`nq?G%m=h zf!*%*dU@bo8O8XYtlGOTQW7*l4)rw7pQOyAR=g zAzFVy(ymD#Eo?GBdXc89@%zA3*|K`&|A@IbBkdh`2$C*tL(+veiG0K^IEkFVK9h>H zq~_LaSs^Bg!&>ezpUpqv$jygn#XgX9+Q65%H6k!l?5Pdl@3nz7S%}fKbFAlL*a2hQ zP|Y;<9pcJzMpTS&d5{ynMNAhPI0$YS@rHTd0ncYtcL)lTo)5cEKJR^XodWToD9u(` zk(waQjBtNUFAlbHBIysvYT&Ey1%1Q__2U+@_XDz)^p+Riyv;2jBBSnuxTM34_YEP< zun59!dy@Tt&_$|>hrfg6yA?)g>eF+hKz6~b4@yYKDb{K}cBy{YKUPuBbaOeBGtax- z&M!4zPFZ+)A`W6Kq(BFQS#sct;2ZBqKL8MVsmsJ&!5Eq$cL?nOh_~<%tX$*}V1}f$ z1c275*%dN1D(gNYdWf~J-LAmF_d|irOn^KH1_If-ilHEprlDu^8)D%?Bt(nCR1Vt^ zq_P0jy{`4yuov_rac45tu=Xu_lWTO{+=l(h?xz|mMEu(&C|M0-yOc3D2qC=-0X9e_ z4>0R3?Rj3^E|8gQ2cVq`K5ve#ZUb?={9H|X0oMfY z2f^(Npq+h(biWY)cIIpDIwU)kO?nCwosts*=v)GGOyp-uM(JYWWw{P|nw?~v7wOqf z-)_>jI6fAW<0|zc3RRc@@I@{2J*Q1mkP84Ltn<-(OKj>Y^@0drT~?>}x4eonU!q72wcBWMqHc(cS%AC?%UE3`CZp8`V&Vi$ zN7#ID9J`sv${~d^9`z7uO9EQH%^6;WrCtIz7*;}ad|dH zutyC-Gm;J9&7?yS0Y0U}u8D(yVN{Y``bN8gt+Ej%8vjJ)y}DK9QDj(Xrfd&$=X#3M zF|&*&!ZkN_Wm#dKuIcs72lg!`Ygct|VPkXa-Y`~ry7H`-JK zdXIp_;r3D*Y}pc@eH~den}BivorSBm^|wsToP=4%)5VZiTR9e6VqY=Kiil(vkSZk0 z6D_c7RM;}K=2%Me7-6<=%XC=7Y3M{C#S@oq?XFBv3sQ{ou`$Swv_(2{dg3bZh$9wZ zg<=`Bpj#Z2tb1l#n*Pe#&Rg(gN)o>nVO3T!_X)&_M&Fy(cpN@8-)gUZJY3V2u&OQr zk}xV)eWZ4cBVR0R6Zx67NG)a#vE{#TdMaKEzc{zC3Yv$A2$7RcH*?9H_GXR_(ZCErV2e`yA3w<#vQrLTkZZF5 zr?4BrJ5WdsLTJl;E|Q5d0O_^*9yd*=XJ17mwuG(b9sb!|qa;rB+nvX&5aQ;BBdG~= z*e3mreQX+9t-IQFDLed-3|;)xddiM*KlfRJUHv{( zru)Yz++}A`vG!miafgzlFJ*d&ILK@SQ9$BG(_|03EP3!RUt*VZbfe|&7^d8??0W|@Ed{>8n8o>~E9hWXX|7DNy+d~bkt2rg_LcXVj>V_ze9`rv|D+vwE&oG1tWi8_QMRfs8pE6n1)DA}(abrn=*(xOKyH>jD<2cX z_VnnnDdx~?#bMUxG|s8TJM3Zj6G+9aq$KR0i|>C|9Rx(DrZo%-eqwcLRO-uaq~2NO zLda~k7FC?;V?973t}qn9El8*Bz-;;Qr9fUP)`22EIeEcXQ{(I%zl#UZRbiJ2cF$_p z%JnW8LYzieZRG0doXsZx*2BSnkjTG9;xwk?TiB<2CnhK5h)3^-^lG&(@$8@ap*F3X ziQyFXAsu`_tnN_9a~ap|BLvM%mov)V{fxwwOPOEb^~uPhE)+NuNqy1|RU>QEnn9EAQ;nyN2aNGD{U^Fk z@;jQ&xRz1E8ca?QEvBTR!u?#)xt$l~$>_=RFMt2V<1{qa59RJu3oo8Vzob#k`eR6e znR@qBJYSOk$l>&hf6R6r7|Q2^K=cZ#F@J89{HT_)H{iH{h_YChafQc{jdvg&ANDEd z{QL*MzAQ+hB+$V79{1;Pvwu#1E$uWB02fpw3V_q3!kLrsa=qx~DOJ(}R0^2Q7v-Q3 z&?o*NlI9W@lHCFQsa)Qlg~WQ=-h4xp2kd8m zZb>qmRH{c?%2qt)hJ^n03&9g}i%CKPvR3-NxQ0&r>(|P7oKii+T~jq8i3f=0!^Qu- zQGM5c7Ei_ruh2~IH$^3-qK6L&OZCT^MGq`>OS?h1R;i0}xO%1eJc)due}FAGhq;%j z8E#Px-+EDJfoxP?Z=MQ*lrv&<*;n5MNrqqlfKi9Rzc)2@!SN|c)WbT^)Y*XB>4GC$ z?rkMh%iX`&jwiG1N(a&%VTu0yV-rsFb$r0)kfXs2bJF|iORXH4HzRBZy@G$*0*s;f*awEKTPyWr~&;%cxLT}<*X4Pqg@i4vEI%Iv+OAbJZ z`;ERo#*ZkfsJIRS%KV#z!BPp(2H8hxG=mNw^qKmOtU>O2@u$#E%wY)$I`{A%KbC*r z2J{;-FEESBzri-uY5&|5ykW$n8oO&GJ+_HOOU@9s01MAFny_KgtHTlv$ zTYcK*$)*72A{y&NwVVSEI3QafC~V;HY8467vLS(KEf8L(#2S$~8y18O5MSPfs}@m( zfM7ecAKXvMI1NPok5pZPzkT>zt?$iSnf#$ro@@DtlpSfX?*oNx#3Z#OveT;tI$IS@$p! zT8G?*NJZEN{6RnEEy?gvMZUAYzIKjZDn*1P*AC?l)kxc&LBBuxIwOO|E`Zk@w>0QMmHIjz9Q z1Yuh%Eq#|eHH=FAwg&NCQBhHKuFLXBXrhAJH+>q`L#0q?5!+t^up|r0e#A!-iqN6% zl`B!uGU7wyKK?RS#Wb`>{8Srx9T>XujwwxO3{WF7`vh}&riU`;`|Xje!Fg!^s&<+0 z>xPg?>lP<9_1c1=sQynmx*~{E!v@;W7W+WqpkH^hUkwEA=^XLytz4yd;d)0H3~qWQ z*>Y&uD07NxdF=@a7f5Mi0Z8)(<2G>&Zo~(#j|7y&8P^~g4j@`VXqFPl^{WJ~LZ5o@Foon;fEisC&=E+ZXEvm2 z{4DN3q~_DAstd3qI9{Okg8_K}(M8nmha&i>=_cFQrOQMwqh4&lzEKgl&30 zRXjhZmYzn>L6+$U<}j)Jo3%^@^@Bdh=({ID`Kf51XnwWs;#l;9HJ7PAjp<%cEf0Gd zKuQ)d7D67hM9d(Zfx8UD^cm9bW>K<;v@G6^GzDRyqEqyWt)l5+GTuJpZ2g?qv;m1H zMuzF1shQB*B24s(>Mk-=jfo=)t$Gfk&tMI|msh=!Ab}}}0jadWaH4{~l_9RTnoYgc zn)C()+Vb(H1{nR7&^VkIU=N{}B_PD9s+6 zCus63fw>y0kr>;Svo2;^or4$AX@-p{gXwpew`}1*NFJ?-j`f5OL~bGhhok+qcAh{* zhQ^L#n3AMhTvyGTkm$x$Puz%m^%0&Qv+J{!NaQ@iJB7B`5{pFB-WkgU=1T0h_|_0a7joE64idg$9{(O=ZUcQ4G~VPwd>nxZ7u!Fc`>5SBH#Gj z442`x9P_HhSXf6}zjT2G1nJNj2HU3;^q%f1mSqd^P%2N+yBM}`zs zO~^5UJ#kv}@=!=D`Al@Y#&Y}IYDafXZ}Mts)kv|Qjcf^iY1Dpen~BU%eL6KozwzMn zm*gw{<-UIAMa`7B8EDfAA}V@32(;g1L79g1Z8V3U`q&LLn-Gx>ouI{7LFJC|eZ6`{ zqQ)p9h(9c&%2q#m(^Q2mZA!?NBW}_(2Z{=r;t~P^*ex?LOama4n^HK(zWl0)E`yFW6G2E|UYwNwl3`UH6U9UA43d|Yv}msr zjlmnww8deyR||WZ2=fW9JgmzpLNCDU^s}xL6>CthAnqSptqPI6&C=?cs+v<@vFjO1 z@h5*+_~wXYn6DlAxHbfYGR2*9SY5m_?ITaK_8b`#;91_MEE+B;KY$H(ck0p~D$BKp z$4Ay-@kC%pGH)tQ_2sU)RR#34P;JU}E-5LgMpYt#^<3ywa#|}qXvCDS1Z>#C?+-~v z)bE5*3(>k*9n+TIu;K;)Hj!a?! zadl`wIy7sO%m?Z$wO`IF&8fZEaz!Iv*>6=L#wthpAOh_;2WpE!7^M7DwyQ}bghw-X zUZSkW2aujHkin%Oh7l@3wX_?uIY_!$w4DZ9<0CSf3Z|q22*dsMg};F+bF~z1o<4-N zZ@KzSkH`bji_-Id+3&BU_`&*i9X)v~0>!1f5hi8ALQnfL?PXdMlLNvL~lFjU6p@)u7K*4>5~42-O664^T5NR*L^Xs+kYf?+9p;_R?T>ivg)m_ zd34xr)r(IO)5wrM9 z!vR5m9IWBA(%$Z4VT;3+y1MPYt=tmI6unHZ&8jW)Z2@Im0d$XetLsRGlqfxq^~a?B zvH9LHEa&?%rvyl1&8QyrI2va;t4c0tm5*KejwA*!n~=9{HGVj#MU&|)Q4xPat9M;u z<)<+BC}{Ge^7r?9UU)-a=K*6QyS+Q;g0322zE9#St7SV52+Qzlz z!=*IH6d7YHsfnF0Ld;BubeAiv_nhx@iB_lNO2uL^jdK+?2@EmoTXgw7V_L5LhGON9 zL$tSX>vMu6v_pdQDtJZ?b)FwT#u`D;8II(%<&xowEU{zy%#5AjyCojGve?#_NerVJ zsYlRAZIY>U6E~qC)2sN~Ae!%Vm8QGD607p*fnZo2pEiK+&16!0aa`+KEyhFaQU65af7YJH$P0&q67~w1lEt z6W2V%U76yw_LaH9m7WWaqEo^;rC7x!V)`xjKn^L%B{j<<=8?@=HAE;Z)P%pK-0HlA zb8Yp^Hmd{Ch7yt=CO|86IQ$@A#QB$S=E+yhwb7!F5wt(+QR3sm#U*Nj>#>_hXOZZB zPUWN}-NRN(tXCpqzkU5=cebpb5wE*M=m9cBwET&#si#B1{MysOhfptw1QoMDi}k{- zK*)?}EhPd?U0525{Y=20m<+rAuk4o6Y;G+)u zC78nYQD;K~g~nD=lR3^EPevth-FbcE1VU`2^geppp?#q@6O1VGIPcb_g2>?XHk*Tf zdSm2A==GNa$!EleByw0^FR1V z`0=hcPK!70E2aMX*O8om>EAka?oe!yNQ&66AN*g4i~O~^$?wPgpC03?{&%_U6YG)&z zLpUMkS3S@Fb10!3PId{6MVz#5Y5Ubt#gvEYsL%+nKCrb=?)5isPMf^DtkL6muBMKcld!kg)mk)U&!!GOebE=R?n5 zq>0#*(rvjqOh?luQ^t-uLA|VOz|_$cD!;*avfr6$(y9tCAD6tis%tdP?igcxaw(4I zgUcw<#G;3jZ>ay=Psp(0g^ZfoNrE!bsywBC1XWMmF#4mU+Nsm8CdMVvwW;Rtw48$u zn#^KZy>`vFYNgv|)9It(%Chosx>Nr>B57e_f0;5u?^{019dqheH@??Ua;t9z-y*}f{rbe7P&)AvG$E|#R`!yoM?zm-$+5}5A}<8X)m#M{u5r7U9YdDXFCH*OpQc2LvBMuwE*OYvuae!5P| z`@<1b-mOIW_iuUcy3xwB+SibQ@J_(!8r|`(Z45s2u?#1vDCLZ*CqK3Q8RL*={xL5< z;Ryfp=p;$XhVA&rR>nTE{q50##!{XL)2^?wp)3E01y^oXKfC*i`FJF1Rjc7kKCK|? z1m&2}2|gWh%r;M;o&M#CSI`GgFJ6Se)fWBVVN^PSI+UWw$pUwYcg zmt;AiiNZ7&$p~ynm(kIoqjhlfFpsDD`|H%bm+n*vW^sve_m%NPFZM_ z7JQ{y_(ACDYQ>aMa)W~PjNz@M=Q7ahSC_cG;f%dVwUu&gDNzlrl6si6 z2Qs2WYwKBgf29hBA1^C1=O`aXMV<)nPT98zsS@$X-M38fEwRy4?wA{rIqN;1ShrbS zDHI*?;YpKj>h3wyTbEe>AWmYprPBzP3#?w*t%~NKuBM%KOpvPac<_e*Ye0$GtDd9$ zZr?pUeBXx_{NAeKHIhl+5mA}4tMK@@ubdLDjA2@rWWP{cN8~ z;h2|fkG=$MzWq|W)EV)B)>vZZ(?=)M&S^~Ty@Gpx{N}OvAk}1K$=YPCedfkf&*-Uq z9EWX!M)Vxx)}^k?6vt)8g8a=Yh*0)sh34FQGOIbGNO-30iw;CrYXM_p>l z*rTaOw=aWB96v$l5niU8KdfD&?ajg(#HqvLq-N@vSQjI(a4Y=Fm5G}CYSBS_&{7WV zxyiQJ+IYMqo}s$o^Gl5A;6~8u2b5#Vw!)V*kEK;S-#P`K{o@Hb_XJx1Amb6gq?uY_ z3Vw0Ec+16csSlrj$UE+hT&ju92=d-e9GX%J9TD>$V zJ$%cMSxt1ec6)efn6t(W^ECU`DcqxspzLARj=KWXes6WI4V5O@ZoOshczRwT&&nV8$RrM<$do=KdGZ!4d?^)+?Wv=y4%Q8AfFV%dIh zw)}D2T%4ak((F@gV^`*zvh}?rz2VcM6Wbo~qOUHW;9JULo-x|f56313ZDR@>T+~j5 z3+9PB$3C@>5;OlWy~ZK*mV42UHq|q_L1dE0v1U_wXjdY5sonBiq`;mOFTUMJ`=a9= z4(kp>=9Rhv3vbp^o3ee)>W;MRB0mnR+4*cCWRJn5?MJ`U z)M3UJ3Np=>S8^4K;qM=PdByhhOWtSQDcps`FV8!t`zJSH5xEX`xbk6}k*6u-o9VJ~ zV~2*jrFQ;^IbZm=<;RPO#X(II{Wqf1t+oo(6{Ebe#qXy>2b5$wq{e&Kc3FSCMhm(v z^-k>FFT3&XeTg%9Sh;vFO{X96lX;0t_eKPUh7c$9euq{5;gm&AS~s(oK0A>h*xl#Qd-OE`_Jd}>G~DRzG)UsE?2o$ z-}s<*?vdmKzbrpq&g@K}&qQ{=S;%U?@#m{~%%5Ae$#Ao?K7Gm4i8DtdFPI1Tc2q0~ zi%$D~Sj*COX07{5>sDCSX~9_&Iulnnpy3$rCnCv{;25QJ^K!M8_4jAh3VK4VgJYPt zI<(&f3+gkzGOZy&UkSwCHh&YIZvPAQK$np=I} zcEg#sUa0daE~vHb>fwl*{aw~vEM4$+Gr=s5P3wx}5}k;1XcEy|8~=8Jt$W);(chOM zTB|y}_I!eaHcRU^U5cefSNBxIThc?HMPL8(E|I@cXTHa9UWoNd+5M+7vSdKZXic}> z+*d3DtS9oQR+q@bIhHWyw0L_uZ`}fu;3CV&?~IFI|3piV|BiN@K0JJ*^a(48@GxPX zX52F}+gAq3?56zEWz{=FF)Ns6(oM5oL)Xzhe8~-Lg-`kec*lpDCMq58VDZZxIA`zc zc|LODy~Idy^j z4I8HnHTqfc2ufG^afIJ)>6mTDEk)b9#zm~L)1YVZNv&kkiR~Vf*x|JbDc!?%xtc;( z6MFN}yjon8r}^^?&HDyjYv-6FKf7vQchvtRn}n+^9c-w=xd$fhvU+t0&Fj%~Itwoz zH%!e?3j0X-!q>OY;NtQ7J%vg(t%Gxpi8p72`rT4W?T+JJh8ytBXB>z7h6x)Y8oWxI zuL)>Z3m^Z{?ckU?tXHM{Hp5W8HueU!ed7_El9aj2TEW{%k=Vj6Uu`>-GsYxi&=wVY zE3qw?YeIK*92cZKCipR=X34LZS1Qbkzf!;b zO}};dR*?WcJHPoyB6G*?#}9;?LrWtj0p2dyMzyLS+UpdKnh8qP2BA;R@X`je@_zLg zLpkaX-A*!RJ@T>bKe4K?$@1?DpYVVBkkjYfaU#?Og{XYSMI?vFw`)nrlmSUaSw`u& zstJq={=ocgYKgOLi6lBQTtsO4deB{cYpw>Z|2moNg)r8HZl}zsxb2gTFqZB!w|zms zq?)5lY#pzfQgacj3;O_sjwP*NBkF3hDd^%UdN2CgE0hrxdrTi5d&zb+pyZ+N-4`}D z*({}BFZ9ozpY>JPt&^p@Z*5dCI-Q^vIafL#gKyuuiJi`gN^M_l#(WvtI~<5=T!HcT z3R&4eoD%1Lg1u+Z(I%qU)KAJI;-z0H2JfeuHQ(fZ;E-#^dfX&Iz%pewYt)-Azr`YV zRd?{Z&R+NE8Om%*O8-dKSpxcf*JgJ^C#9~NiN*>&PqbfM-8g&F9IN6O|4fgMwrp=Y zPAzzkKZYN>bD}!|lQX*Z62n z6UxnJeh0gsz~s@I(pmcUC_ABkAB-60JouUV-3OG)f9JByO6QOgVB}3_(t5J`pnSS4 zo|T?25kIixM`Qk7ho0&Fu$_W;0;k+yTVQ-ivI*(|Moeb}EA7z0C)*%%NChV&w6uF~ zFYf)iePot-!dnxktC2Kx5(lI*B`&1 z`SavNj=;_HygR|2S8-@;Ph_6&mLk(C$55=>+>t`s1wR3r)+I#|g~piT*RM90UBlSA zH+o~qrqU^VqfAz1zuT#=*fyp-qcCn4Q}@+oR@BNcZ?&Y!Vr}KDjIjxDF;E}jFJmZo zTJB{DUFT6LKGnT)A7d+G+8?m(zMputz;e*8-yCI&3*EY_lSnZ;nCM7RTlC7EXO;;U zx~CzWK=FKa0NM}Zt5On{WgF>Ipd zDKi&-m`SvdB$?1>H(wI4?9pBr-$-P;W3BsrEltrV@TheEwBN1o2~;9}tM;pxMJ^{l zh(9bJa`aQVV5LZUe%TIMys$<$%@PxBnR~DKdVnN-XjQD}k${lT#e;%j7-I$TmE36o z+S-!%o-lNYi`$kQ!z%;1&Q@Ej%j{13{&nK5z+o0~IXqJL!poSbMAtgkfG z3XbEi$^Cc-8!AleU1B-g;b+l7OxeI`-Qs27RpBo5wtuJ62>ebw_%3D9wJ)RJ-v$JVLb>2;6vAhu%a zp*L|9*7$ge0HHpY$IaY{LVoE=ytLF?p9>X@%d7n^dMr$)nm*dhUyc1RH`U4*V)Cw7 zknuGpp?GYEOH_1vbb7zM#XC*6GyAaMjL7N>hileW1@tlBJg;LIgc%KJx+w|-5{QZ4 zMse7s+Vb|>hBVu4wh)rw64aar)>ixM7H;tql<2Ev=y=@Z(>`CQr1{KO3cttcVg2Qi z)3xo2m5vz?y!B`HhKd^--@5U(kQbv?Z1wTTLbpg&+}UoB=uCld_Sp-u?55$n${!QW z>K|EguhO*)tlgy-ZZ;)OiW3?xDyK`f5XhLH45t}%X{eGL6_ibRg?}mr-ILQB?g_1g z(+s=DhsnL;T^jXt6mIXw)$Mfq7v5LjS|y&=*%K2Il0An_;+$fX3#liZOe)SYvn}VJ zklZV8dXqC77{3zYYFi?;e`dMmlRa}JUq-#8x`Ec4Xl28>4wDq3gl1Lh$SLc@m_TqY7Y#`^<;h9mg=I_n(c=l31dlT75Hsqc;#`k z=3Dz#)r#x-=W7%{27TRnp`V!WtYm$nYW=~D_;v|`zAeS?Kazp=x`V2)*bcW-)kly;>eKC?gF7#ES4=*A!x z^54-4u-_8_>AL?wLetU+>Ei1?eKu<^923TOt`|U2<7yt7x{4)q>%+ zrCG1h+)!18x0F{oxr4rCug6;x>7?GYXyU4(1wrdK>G2e{mxZQSj--q#P;(kwq;E^O z>S7%gd;39W6aoIZNb5z0qe+{M5qht~^ae|-l|&dV=EPFtBEm!xiv4`~7|-PK*H@)v z2uF!ujQNdlOt48f&D`0;_}=aQch;$TpQ;~4UD9cAb?Y+0C?yoj+m(H#qFwKy>Yg!o zu{sZ%GQTO$ehl@r+?Fo4*awq(A9h_ex2KIJTCY7A`b(T^rS`)%c`KD>U%oUJAM4Eg zPO*0SGKkB(pHV#Qu8g*F4A|N zT(VO~^;F@?c=)aTyfm z*q%1{SRP44PbKmWy3TqzC%7wF%HvsLgO)BE4)#PXIww3e?sbaPetMPeNY3HdcBUj; z4Gk_xdcwD=_Q%KlOLWW56knriO5%xAoO%SAECLjBk8mrb=50P8>YR)XE_ArFwdG3d z8$gkc@f6*Jo<&oXGOG6Z!vzr&TvOk zo8@4>hpq__{PBR*LF;Z!Vx?LQmLgqwyE)t3ZB!_u)q`Q~=FD5eV{6SYM)LAS(I_2N zSe??|un`YDnh>Z%(`N6I?XzcmWlyhEzv{4|AUfA~f^UhXVljTH)$oId#}oZZWt{!0 z!EQj=^~7BYJ)_F5HvUmARtc2{zc+TDDHML4i=Xz`aH1Dni7&<`nP1GnTkz*qu}B~9 zw4He}GfUu_DBu5|3PSa!>sv5k7BJ6KF?`0S+D zx&)x#%!@U)AIy7$oYn9TC2{yI{Kq_d&rJnqhCJdw1S6LU4I5^$y@YQQg5!A1UmUgC zE0fntZmWn-_->xC`e|l@EX8XC{Wsd*W1TG*k4t;X^gj(y4n`o8Kv&pyOeL;9lTfh&N9$_ zTaqo8qoVKFmC?>tWy=n^3E_IBi?5%R7DId^F+9rNK zgQ5DmJ=wV0)Tq9*M z`({KUT;$p>Eehs`j0?D(y4hnm*aDSUTQ`!o&o^_L2o;I19eI|pa_!*g&hW*z1guZGO$Um9qk3XMq!L7>nqxFN3QnHTgOru0AbWF~* ze0?FGH!Urj_^ibgv}TAKk99nf?S;ig=6 zPIWGD&-3JS)>hP4>?9Oh6*R*-Gu54u)I9t1#cO!vA9Ih#pbAo#2_tXj?(Gbzr*fKs$r-cI?d+T)+G^I;(npsHup;3L&?gk@!1cDh)0Xwa_7u7^-mw(Iihs5 zStC-&v8CB%>Qm&~)~Ae^SoEslV(ON3R1E9ZsW%LQvJ${^f!A(`%wnvaxSt)ON8L!dRqfi#_-F5I68Cb@Y?1DI%J5;t2AcUyo&S*`+wg2%K;XGZ zhBL2rg9o`uzss6nU7UWa_!081`0V1a_Nc=9cv0;yPdX;%dNuT-72;QtF?#9EGEuaC zX3|$56nh*!ZMOKx{Ld}miw<1!<*btsRP$fP?PvCqfzVX>8pqke>-)M!{G&rFW9#NO z91E{?DXbDKw%<~Ud9JHOOvLq#2rS?iAwd4EpiQ5uLNtFrPk#|uM{Yn$Lt(!)`xb-v zd9X!AASF>=$0SIv$td3b7F+a&>n!E{)#tkdzconL^q7QxxHniO=WSebvN0+<{pt&U zw%hXywhF4>1{^Nlp*CM6EmRr4A0F)7K)m(Rq3PI9f{y7O;SeiJ>LI;*!_H>^P^LSw zFE{p(JBkbBL`*k%Ma~T5l_i^;QBite2O8pNPMx9~kcnfvIS-bU7U;C)fiaS-?FSg` z!$C&c0zR23aL+|SmIvVm(PhL%iD;)WD2GC`_6V@xH3@V|vOvgKIC#$_crWP2G(}y7 z-#A;(b2h!Fz_hilCeA0#x=w#BJ7_bD%SPLth~QptvUAFA0aI?9kTbhcFWUK|Z#4Fm z*q_~`Xzx=u1foThx=nwf-a0N#K#oKlT%A#;!kr|0}!4>C(upzDr^ z8z~5XLOc!GFp0Ly$`mQku6}=EK*+%$Te7KwdIH3LQ;>oa0fF0UNUQH`-@8|t^$- z5-BJzDKZ8PnJIXLCn$atS>sf|P&;*K7y@_%i1K~+HqCXZSKuFdK+v%XhUW!eE=ou@ zRE9i)Td0}Ef#O!PEX$IZ#mdPL3Dh=HHRb{NEK?9P5e@RQCUB2Vfo&`n5duQ7}_bSTG6(uLj0wN6G%{7 zK>5c7!tl_YHSDboUUDnMT|Km_$p+oM8uCT-FWx>~-#<2^+l z!=+co>yWKez5JexA2PHndCC~2z9OkO!U>yr_=N(Gy@_U1|EQCB9qJ8V!?&DR_q@8V zumv``cQI(Pgd=oypUP)_|K|mknI{3EMzYt38-EuDFv$`sP${c~#ebX;RHWjF;Gx8h zMG$v@^u=ab>98`=Izs;2>>==yo(GHYv1e0n^J)svY~>_|Ljk=18~ zJ;)y&kb&ZgDftIxI=`tW<_eVB^z+j%uj13|FOxsE#PY`>k*MUvR?9g~Q!OhHazPZ3 zaO?7J(Q6E*1^PM8fo>fAwG71aCY{NUH`qRpeJ0E$lvUp}#)Oe-^2V;Ni*E^eZP<9= zhuv(Ct1QJ5GYgGn^at!=8ZElGo1@-7b64c1`mX2z=3E7ToV?O7H!*TF$S2B3@6?uB zB=S%GGguND@a&$i@Z<5HyQOYii_MW4{x(~mWJ5uM{jI&1U8gA1!YD`e#g&qwSGgP* zZL^9c$Ofm#7Ax^ww@Q?C>A88aKkas^w9gj^&<+E?@kL%b{aED()&qz#`oR^t``GzH zP<^)N{;mnNt##iMqFq2^Iygf|h(s#c*0rSiVB(!#=s=#Sd}CJp|Hs%>$3?Y2ZCFA= z6fsbdFaVJh1SEt-uYsg=gVNod3$_S~bgOhqcNrk8fJjNBh_rOqJ4dhH>(7Pz{>Aw$ zyXTy5&pb2pJeuJ_iw#1PADxXB)RGeL&(vEXC86gh^2Mrwi{uJ0M2&C2M%sanAf~rJ zR|FjGwNP4l)G*TeHXE=JiEP4dW_~HEc%;~-m^iA!_>vk@o!A_}DA8Hj5Bsz}kX$Z& z+*}(H(k3)4t1UT=dZomT&>v5 z>9YH_QPe^4cui@L@bTc~Y@*}NEGw57m1#eOg!FziN&#K-_~9=@Oazt0jp%oVc8 zj`EDPBD3e?LRKdOLY_{NknEGQ}v$b>IIQv2J=cuAG7f5300@- z8w6p4m{2n<7KtDpI>4sFfY6(7Di}jE`m-v`;tW!Zxvu17LNab${aAq8LkRL)=bsGd4>nUzRz6rE@m zi5i27`x^y$FlDDye84YbPfo_aK%rxTC&{z1#=bO7${gppbCEf&j7w_NX zh)Tw1@UJ;`HG5<`DiQ#y&2+S zH`%K>M{s~5!I+D)P@gvBTeEFmT%0HS+4}P>RFQ{Iv?c&_`qemAu6+a%9k1-5qpY&b z=r#X~%=wrTQSl{)+l22-tIC;f7`@%OA4%k3C?*76sEpMrqu*u~KC6{x%)-ptlB!dO|hct6X3hRMtBJQGW;&iCPH*dcHfL=E7Q@gT!;1s$7+qpOA-kB_ekH%b0qPJyt z7*m=-5kDWz`wDHW!KeefeX`hT3M*rl%k>J)cu$G@funV^V_B~tKeB>zvk~g0nnYT7 z$InnEnnM)}9AXt$lI7y#K;+ipNLAR);(RUXa6buxXPsxziDDNh0!KqRfiySLZcnhT z#>15Rc4x))MkjAP3%Ne>M4IxhIR0$!(@dh(@Vm97TtcRZ##HJ*+4u00R}cd=yxyI>s@r5j2xn|QG* zAd0BN)`AB#J*B!3X;w8;HNjb+uOqeZyw(L|SI0o^Z*oWy&jvd1Nw`>^n%`O_aq-b`I@Kg z?FsSByh~wu#xJCDy5=MZd=I~EaZS`0UYA<^V9UFdq+8$kmCpY0mD9bBGtH5vU`;wx zd~83zZ`Z@4KT^GJdu?o90tVp$t<%6o^pDkh1&z7pQ(QZtTOM%hLHiR@>NktpJbmH( zNyl6&W^#;WM}VJqUp+U?IhR}tKVZPR1XWo*m0UnsqsNnSX83Dk=ZBr?z^;8iSmd2} z^Ui#z36e!9n6wPr^nsgOE1&2V#=pvw0`th|_>?S=UFQsw!bzu!$-l zTyp{J2}8%bJsJAEP}OG8eyv&V(cvS_=2f%Jye)_>01ai!lU#+wjL8K2+Am+e=q^n5 zCC|3uB^sbsa)yAlgB4NaI`<_lry&67>#&dvZx$=<6AWXPZ#%X4R5S)k9xCN2x{bef zgGiiNviRmM+DX&QGA2YCPStH=VtKk*F)OezrcBPQJ#4hb|B*IJUYwjA-h^OeY0oK?oW`SI#-oHy80XRFXLM%!*Ejq5W(0)S`Mb zqBR_(d6Jbx+$S10r+P%3HM*F?>LBZP_I^NnU3{ zlK4_t!MmNXp}tM7%+5>BHfZMPaGkP}mibi3MY@kw?n;Ux8;ia4J68w!(A($vtxs9C zYdYRDXhSMPGuo;5H=psu>yJ329ckrwFIUv&k%&_}!3bx})yS~ee zxw50bd-~=Zjeo7qf7abV$r=wEqs|D@{vy4n}(RWtB74pk}=atRMZLN?x4p?w&H zibu$RT1O6O$^|ypX7xcf?XykM_y!m(bx;>J2*id#z;j|K$gCj)d@jsDit`x$N`S32 zJ5l`Uu;}_9ul{$&8!W%QKT>I#<&OBEeaY>I+~m~t^vFt9Efm|k%CqMl=o`VxlM^V1M_7Z zywD7sid_Rp&8EQmT=PYquSl6K&sXm(q@Ge>z?UZu_+o9~Hg(|bm^k_$JbfNC(k;^jK7EMDdwy+~PY=TbM7sP1=mX~XYlLq`-!9eTuRzF0O3B+NFTGCYIwOl875Am7= z?qgMw3_oLhtYUMc#Fy@z8Vp68WEi)@Dik-4%S>EQpR&(`08usY!Q`}nOiOekzF<#6 zRTUNE)sF*t4jEeNtM5;5!dbx$tcpzx^jp^1f431DJ9~)EksubrGN){jjlOFWRF~+v zT2qvRV3{m9>(oVX@_%{A-}?wy8s5T4vVw;h2#0@D22G->O}KRGAYMt z7nVUz;skj9K?rq>Nc&DfiZ*@o?+$7?5N?T=gQQ@Xuyc0(>nx`T8MskT_np2fI zX?&Z_iu)4|xpk@)SeSbv_`}8*n%@AwU&|L+W<>0(? z=PS2U`5$&`P8d9yXWW_AK9=VXLiKS#lfyTh542b%>;eKE8}G3E?99nhtMmMux|t?CGTWL);H`%9s2A%J@>7jmA zu0&`oEeADtY%_TkkeZJ+{>60NiprXdh_L~Wel!8%cmeX#0vBPW9tC==0A&4T^zeDC zIYD(#Vo&1wYECO+A|k}eLqyt(K+X0D4Dt>;hn>zrEJKx8ehY zM{4#svh55!eax$JnsoTEu^N-y^KUT413$gGgN1tMN2$_af1653H-w(P0K}&3K(`kN z^w9T-7jsAQVUy@GNJr|9ZGe`ICJj;D?a4GPG0yLXxnv$&Z?SVFLDd3Cr1xlsNN3K2 z+1eG`1JWZbP;$6SFyPMCbNH`JFXDa;{klNwXO4G$cVd?ILxHt9aJIDTB7A88RvrJc zw3|-Y$j7#0qWweAVUjt+*SALXKyB6`y$Cjuo1OK1gj8DSP5gG74A+J6EX>5s9r!r= z0;53&WpmQGht;2+zcp^yRwTS0dPm-SCRBJFikoFohtFc6j=qMPMea-Js=a%mrwM2@ ztR-If_`Boq?~?`H0*cApCYBTgTX#S5E+7J4&{cofD$AkyAm`RA39+W;$l!#9G^gIK zS8unTb;$7Ekb*3Fl@eXw|2T<95&fZ2%RL0Rtcic-$z4W7s2pd1@a{~s4;yo zD)hSlKVI-E`0F=D9ZswK+mHC?iGN>VRFHIhwr<91Tq;`sY&Dd75rw0BU9TDb4GR7_ z)Hg0Nz`_W)q5r}cN*!!CUs!I6b-WNzbeQa5)~X3v-E|h^I$v`1TNlOuHT_Jk==>p; zaJduX>&<`X1OM-A-Iw;g98CE|fmiBrN&$R|^7S&e^p-Rw)x&M6N9p{TZK$6cg5j9u zwJ}iQJhp3fB~5$vFK;V>-~lwwhTpgh1NX5=*>@jCgVgA21aaRfWwwaa*|T6u9Ak~G zvf3Pz(@wMprmJzHku6FwG4121b)j|{`YJ}P7g|L z)XL?hZ%g4+Ibal5cx5bK>JYCW7Tt4K1mE2~n@@MHhx!suMmzLbFiLM-cr3F0pi^?8 zED9oap7q%KAKxQML>R|f;y7{k(a#@aZXUQIA#t35sPOhPQw8C)RgxCmel1?H zLyBvNHJ)u!ad~e4>?u-M-EP5iIQ!$Dv)Jpb-v#0$726!u-$&u51Ps|rMsd;qonZen zMhBsa?K0et^gq6LQ=)RvW{(#+HPbfM?)T->KkWo^UZ!yUc>hP(RyD1-vz4DZZr%82 z7b?f*sk|QesaKt(yi13HdT$9})bVpg$G9(q<(hTKh4Wi-AQjOqEeeN#8hgAmZ!FXu zq;539wsn4|J5y{62b5g=)%{_>I_Xz(5Kw_L=ACn)HA8k5HRCsTcJF7wZvhpQ6_UR@JR1RSR=&*fa|Un0!?D~|Z^8HNPR?Y+`x#Vi0* zSYGI|jE70^0R*$euU-uR_LGj9;$4v$I}+1@Woe-_?f4boE2#XWkVdPOH@D#|du4GZZY~T!q^Vtr2oiZ>5j1s|b!Hm+!YUdj@odDZ;0tCF5i3;+B+vp{j3a_Ig?~kQF9gRWsrQl(0HEBX-v$ z&B2QdxlHGLVu=f?KfR1DX(If*3t*LX>2#;5=EQ+F3H=|W+rTbwD11*9pW^fAWW2|+ zkJXB6GP~)Nlz%*K_Z)o#sR#~Tj8Z*rrGZkTvjOh0{JAe+V6$y-))O}OtD2aMTF=MK z>($FO^L{xqe4slwa1eqh6TChJhVE-X2HDZC-Zr6j%(lv#=mRkMPw(iP3zZ!_{a_yp z6-oKX>G0`qmYouThqW(idRo5 zBvIPLETl^1w0lQH%NJUG>X(u$b()rJoWDohZ+BEm?KA0;kJX0c*$OWa=GpqpD%{i* zRLW?cev(SYZ|jC%Nxq0z(!c-k?u_OtyD1RSYP3n!%xh$3*$y>4U-=0BX-~pfm45qq zqK37al}2p%8^re!up54~$>nj3QjXy)N#YJlH}cxomvC;#R7#amFL(?;19HKPPa$kH zr-hva;cu3yc+)&+zUEQvxvwMV4>7|^eixh<$#1EZU;#106QGQ8wEYRzPKQqhSzGO{ ztFRh*Xs3ZO)xf1;FJbmxl3gz!F9%0ab>Xz9u1Ml@_fyT5^OzF)vAh6E3UF9wuGIvx zsmdXHEfQAq1+W2<1>(V@^zVF>Cjr5pqbISVh9_P;ONRq4#6J?Uk=tkq+R$YRTJaTlVM418w%7A?m;m z9%TqHk*e9%7j)-FHn8)%+nAY=P&z$$xcd4e&X<2HgB+ryb!&t}Y9z1kSAuvR601IE zQV87?2*LGx#INDGOrQl80D+tb!M1RMrMi2xh##Vn792T&040bc&kdd!jSjiJ84|{f zcK!AkA&1f$@+RkafnN||Q7jJ3X3Scd?Zj^ZmSF`*+bW5}rgQ?x>Go;voCO+|kndoJ zat0f)K*j9@-e_WK+sR*{pt5Sg^P8=em$6RPVhk)-R1l+kRz(SH;pF2w*@gxQjdBEfY9P$?YxN%)g zb?KaNxh8U)?TJEux;}m3@MDu!jhIHq?~rr3G5T72 z7OQ4)ZX~D=7G?MAY|(PC<`p30W{nO!q4VUvM+D>$z50l0PzSV+s81BT$bMl&yPf?h zW@}pzok28b`!!A@<_*#ibR-D?0@_HD#cKRmFT!W^0R-~AEv;=l!-%)ACuJ#7TA3ku z8e)~UQsBJN;itdp0_Dlo{pX(FII*Q&k%g#(Ww|XL+=83`Z!-_A0DWZzi1=tQ*TJp3 z0SsV_%7G~-VZ#0!%f#Fc+kbNy;agbwMtb}|w8XA~A1Xzx;oB>8drK*l?g46DqFVq`#-Xn9|Z@4O5S)6-zDGQ%!B{2Y8fcg5%;Z4=vCoQ+aCAyzaa-S9gpy- zpE&x79fw`tAIq+l$-m>W4Q%`SZhDoA%-dhZZQ)~ya_=K{H{f-aM%Lnl8j` zJ^*?XfBFB%55E(1EEUJUBgPy@1+fM`4l?C?dt0jO?MQp=%&D9auo~rvaj2wtjG3^Ev=O-LAg26$yj}6-dQQ_m=(dT&R#cC z@UT%<)AkZ!6sRzwxmW9gyr&F%-|}oT2kwRf82F4(|Fzx7)_^q9;S8S$h3R(V7v3xx zll+fw-uST!{y(=oDHdi`5J~!b#rprwoWJBMt&ec`A0w6Q``>^4K3VQDLL?;X8-LEl zKfU{ZO`f|iei)!HtL?UiXplMiNC)+eFoLsr``e)zZXk2sH`ov7k@5?-pt|;h1?8U8 zKp4*!JID{z0}>%!cuA+lA;Q z1P}S|`J(d!4?*>LCiwk)ZJe`)(g0ELJQVm1caQvBt5oXWmpRp{vRB9U8^TR_1QAIV z*Zys!{&)vc&i+}THkVlHL*Z@z=$B0WHbsC^Nj>pHbTc784-0w$CqQSk?sDDK*1x^0 z34L%}JWI&!5_EyUo+Y5&|8DLX6?4BP(gx*C|s>ON8 zN+9<$7vT-Levriu!6zVo3*pIl!^UWC&=MPIOOHVEY!TZLOk4k2=hNS_RdAn&&lJHR zYH~3=NR7Hq3FvrwH}D%jd%OTKP|a%JL&rB)+AF>chG++aI*J52V)|V8L)m z1-okY>&^EY-4KNt=8I`GjIjiMS`kl(Tk0XPL3PNB)B`0rZs!?2?YXY_t$fvjMWgV=+_xT~=6NUYKPJu3DTOX2F~V!0 z<$Q9T4h{lU9l(eje2DvBwn8A0Nk(}3CdD^nV+nH}!-iZ@NYpl6&4JM9!VqW}7_>)1 zPA2H6gh0HnOT-g0XeT`Wt{(lh9RD>FDE+@9pM_4PS?@^k_n(wc@V)ikEI(}m)UclE zA0yv?YTsFovC~hZ9U%g^GB={@2%?BuXUL@q+z~m#Lzcw@!0!13;GT%nL(@)QkQ5=i z_(os@HQ||^>)evM9-r{@H2{>IN>kyn$7L%#+{a#6^ays8I@>Zmfz(SCX@S9gB_Q3m z%d@r?qJ4yxyM!|!S-3w-WcWk;gL;!yNZT}5Ley8{!83Y>FGBIsI6OT=Y-rP@r#O#Y z`OvA8p~Qa9UA;=#d1!}<;m=~^-W&AIqyTBvR{}3m> zFbAF98B@#=L5y89tr}Ueej0A@f~WwYdrpM>pMWE0tc1~bqk$}7akj=}x zyhLEL@&!WV`CUk2SQB0kC_tV75x+f~YSs?SeYn=ekI(M%fD`?B{5Cf&VK!N$eVghG ztUPg;)@JX`0T|(9O$Ys&8F0)cAU}DW1<|63i;H^;xefZDg$kQRWt!$Kx9GsY6Cg{M zy9!yGjI@GwBu5f41rnTP3QB1zM-jR6iNn_5g9ky-`Ha<0V5<4K760$m-OKn$Ax@-I zAOM`ZPtSu-v)vrZ5%>-&Kl|(6bB=VDET~AJ(i}oEdoNEkjuB5dg3Mw5k%UX@ z#v}|@YbZ5tMCb5X<`^Wi(RtpP`=811_crKFiUiy4Q*&B~?shEFxE@uu5VEh0ZUk~{ zo++XGu7F1#Jh5~pB3(L4u;W|f*scj>o~geRJ#o#n6W(5pL%I||8`bnXZPfp^5zXL< zob;ZNMPyq~5!fBjDAE@U@~9rOg!@}=9>_l^RpHdT)E3gaz4b#S2~nqey!}+;(L9>l z80z5Gk&ro+X`!t4%*C5-|3$)mz)V9}IwE-p_tEfo(;2uyL5=#5vdi}NEf|In_#mzO zf6x$~M_e^Zwx0hmMWVsB366Um0ag7B)`SZaq9h446Gm9&vtjjc<-NDcGIieKhEC&A3n(RiSHKHn`R1qj-N*? z?&U+o`aSHl?LDPmp7#F91@L3B!u{u6NGtze_Xl^wdw%FYUf7!Hd$h;eK_+ZVdEoC2 zK;x5v{0iEQ<3E5A&mX7!Qc3zd65PY{cUQ+U5OotdTW4&4KQFW}DWDxPj!%jhD48&D zE{g+=@fSa#Z*TK!KzX&7uDQZ<)6DjulK`d`MPZJ-4To#5pqz1@JJ98q#dz;w}ak|0kU@=Lpt#D zg)(^Q51>ww3<_v=;~%f(&fTy&pb4TbkcGm+ch?$XH&}7zs1u-gC7crYC2OUrvhC5) z3*&j2MPCmWfffb9Kqyq-7?Da%6hMC7vv_XaN5htxYoe}>C~alMZxl|v#)rsny(Buc zpOW&>McGQy{f91=?m8K3qfc4N&`(78*FJr!zz27|&dxk-|D?5l2b$wp?6YIBF*}dF z-ASZp5|+qU-@e%vHF-wr!;`raQZ~_poadNV$IBMf8tJ^p&P{e|uc~!GUKth&wlp2O zt!q-E)Gdni#bvXy)<$GJPYF$y+KMx9yPunHk*5)j9$J@^U{@4b?Pv^!|Kvn^tc(m6 zk7cletha^p=4zidUzb^qb6bIq8QBpis2K!NWjZdB5QyH?7L(zl2uUaz&|Y}|x|m>P zz-uqB(P|{!d2*`3u{@{F3#)M7UYu(T^4Q#+7fbpyIY*t(LG&|a=ZjV)^>nwdJTWZl z=6QvyhADog^&OB4$!q;u} zDjSqGfz|;+nfedd^OJfFS861QmLb1Lc>*+F&ecICJKa*>1no@=_x zQ+Gr)v{pOYgOB?QXV*y!!9TFCc8hAH+d>dA*s*kgRzFNXm?dVVB{9Y0L=s=cP~pUj zSGyEtC*ScjrbX{4_;8g~(LMdtd}ASHl8?zc(WlXVbwTw^Ar8Tey?^!yA<53GI*@+% zrqsyiSPnO4VUH6?iD-#T-;+`3xK3&oe`3`fEY~z|wV2iJAr~$>&$a1*O8M++5G;r^ zSzhg3EDzA>2EjKCshSYq+`x;}nz?Hrt&-6@6eC>2E>;$3Fe+Z6q~$y~8fIE+)urP@ zkz&4=!L{jumI7pNe&#m*5ND#AJx%pRf`JRaB5K zySaYIDXW0-3Z$!Bhn5Xn6_wdzpx_BBwu4q7V4_W1U@%a=E>(Md^weGsRX2Bi(uIQb z!ROixLslr$^B}CRMTI zL%$ZAs4TkD%FbnRV~db;@NYcp|X+bWiaDm^tdHPU^dqg7_zd3~jlV0VkK?*3)bdyTF#G|EUY zX(U8T-d0I)dY9AV5<=L46uds=hOMti1T>*qlI4fg+WI&zq#NGauauM`N#h#AhMnyW zXBpAH8ht&)n8h^&lMo{e%12kDPdkD~tSnZj*E85*QyV@uK0dA))K^o8)}`do+=KL} z0MEQ^U3$|FxE_uA|O|a^wjznX3;Ry(dz09i|kq^MW!wyYK`}Wg=?^7 zu+{GD0`1r#wO-`Sn8hk!*=MbNrmhH09TlFOxJ_ematumfG_b3yjQIJ&+8l>$Nvm5$ zNk6*}U!Xtbe{Mk~Cc>Z-5wy=72M><1F;Jp%8dOi&5LqfLIycGUoZkU5H;P1|E2Cc_g+rBS6v~j_&A?JC zAXCtU)iwx_QF?Y2vhK?HA`De?=GltA1hnRN)IcV&N||WC7eO7UBdUskI+b#m6RF1p zq$jin2HZJf^Hz9u)Upanc$H_mOJE+S3Pbv3t8M=-q45_1$*jt%`a#Zo*oSB44NdCx z#9A3wrP0%{FJ_L94nmF_8fEEiIWQJFO7X9K`|o`VqeMbdnRosgEn#&QIw4Zhp+iD@ z!P(Mu>5u{NTgo7#-uOBv{mcoy&I8GdR^Mf@gXX5Ug0`l?p{sSVThcVY! zxnQYXH}9giEX^x*n0Kyz}ai8pWH zc92}-Rn~L`2#~kDXi-O{?=74OAH%ZptGK@3@8IYd0)=tGNS03HyLV#BMg%xs#+=Ct z>H`#cV)WM>xx`P@bH$h6t4}kcg|D7rec#ccs$b4q;^8^Qe&rk_qqums?lN&s9X-?{ zoi2%EUcr*p)vDYVsJlTq`secf^+~1Cp5fu)lPIg_gGNR>{v6bwpHvo7QhL!)-?_&8 z$FcqQ@WVGR`BXw~h+gHx+XPQ3{`vTiUkqG;7aJHjJ4*EP_P^fe*G1*YJ;USUf%yDa z{caiHI1ql?4!#8V^0IzDK1t-;Jn;GD*pd=;;_hLABWmYycNX$%t~MfJ0*f?yu?gni zEt7xW`k*2z$FMONpFhR#ZuNFAW{d$Nk)-nB$nm7oHvAgxm{;Uo72d<;#w~KmMZS3uD~{f8n;- z5`IDwUk;2$Op)|{9Mg=jI>d|RWKH{Y3k}MaG%fdKi%omk?!X;%ZVF7@8r&X7Usw^% zOoV8J6p7eIox=6vzMPnE-@b8MyV;9dl29fQ1*^CO3LsDLEInvL$kA3XpE|ERtNg*ylk($q#-_|35F#LgXNSs7|aQ6oa=S z{qxR$KJ>jBexsLZdSd5x&wJ}-yX$8IaDC4x2={fv*YC1jG0JW0?H_UB9d z^GU2CN>fu4WtIL?VfS_;LSm=$KMUiZp+Md+mKPO`-+uUV-gd8@L?|dEq+cmOjjK&R zZ=Wj%yH9$H0`=CfgH|WEv$KOK@?T@m0b@THck0AH?fu_@F;d0=_Fd~`+U}Ft_j{9m z`)WEh7m@y$RO^-BKK<?~Xm0d<)_=i1LtVuO%Qzba+F-L1+hK`U9mGH+cKZ|iqQ$LgaXo6+gi z`o0@xOf;hKerx>iJmn>}B9`!U%p;s%YbFsbDVSi}y$uT?8!=oP$zbg1&tD4qmsD=-ZXc z(4(X;1`WZd8!U~{*5)W#@tY|SqCdq$=UUuZpN(ZxZBwvUnV(PO{&z~7yNY2x6z4yDXrAJR-*7GJH7uLKRLYK4l?{;neGjiSi)vede{LoyT} zHwJ^FG!{H3g9rN@I#nnw&@A*+2TRczH9rlLwCuW5KADvVaP)eoMn9l9tWTI_I3U5) z#FbZwX5ylZeYhgu~KjSE&bA)Hk(6Jm0Hd zwK&WDDg0Ru70wPK@x6|$5z@+sp9T^~2#Fb|KFT+Sm|7H9bURGWaj(}JN-RbipNL?w z|Jr4_VP{x0kZ~uXLodVLW1-QjN7ly*xJ>MdpsX_0EQY#b>5V>AW5uLyG%ZLZ3`K%0 zY?9@Wk!>^Xt<0AiBOINqTWWVnl!S6vyT7wSM7CkAig$1~f3G^7``So?kEJc$lG4m< zyAb^mxv6xEYu;8vT6UWAYlVb!;VjFyE3e<7RYIVSnT3LivQnV4=RjO!ELf}?NN9In zAqz_8LcpNU8(RG2He07$p6#|V8^X;<+|JkA--x#x8*`k8D6I zJ_JI`6^VPG2+?}~+^8zoc}pb_-<%0=FHJDCbUx6|U$I=DT|?&@2>!fs>x^4Dc&hVh z12|j)WVmzhRK8vqPSBz(OO?hJ&?QF1~rHuO^Gun$0PPsy?5pl$<$4mwzJj+hK zB6~Gz-C>fzf4BauNI}tBXIH9kv52UdO6Uml-ghoQPINeUV_*WZUA06Ovi#>;Y=JCB zNnWvy^&22P2w@T@NSG?{?$=B3S$j!cE(;~Lw`Q9BJ#x*8M-muSZJlG*PJZduJd=4s;Q|)KPMiDe!Jx zfmJLbqIOvaLu3)b=Z+bDYxkIkE=CBQ%do%KW$2aExg8eZR_g-+1!t|d&{QQsDuV(o zn?##>Lac^)nrAQ>>@qLtx$14V9u%acmK+J{gRM0n=U9>&+X?CVbx=Ienp2Co-UzM~ z-M6>D3m6_;5^^QI*gDMmma6$vD!V}m*tKn9-JoP5KNaN-Ua^0wms<8XpeFb^{(24U zS+A*DYoHj#KFE1-OnbAKEZ-eQk$uv+(X|c$WCizVkMiM*oTtFM4slT)a>PBg55}Vs zVvoe{xRgWUONKGoRrgd%TR?WTq_{Z6e~9|7x=C_;9iW8r9st#bKyi9jDF9^2+;t-J z$uf%aJR;ybuYY42Yh%O|gP3k{>Bf9!CbeQFNGxmcvg?;|u~fmhJFc6~uT<^!c!5N| z2cc9p+H@JpnkOth)umX&Buk+2`76#)Y*RhIM6jJLD9#X#*|YVA0U#bXsna$ibJK z^1*6m@738W)@BCFCfzOPpTufLV;ul-4*@;cyb}l8l~wf~U;L^jeEsFk4?;fd*uxSc z+7R@op$-Zu{leHf8TQpi3=3+`vKFP&)gmz?!N#X}^bM|-HM)30HBwlj=DeX6ZYMxz zj`YNyc-=W(i3b2v231NZM+wfQP{oA5zm^~wEfmT-M<$`Fo0lA)larJ3%n>gwt2btS z+)y9NH8P_^+6slc#$kQ8+WC7fW3>;9s`W2AUsC_zO(T(zE~#n|rhk}v_lW!G+nk|* zruNmL+-7N3(lZHPI&2;?nqN(b+`^mHy$t8U&zWAYU2ap`a{mJB-m4o0GxsGZ%x6XN zUDjEZ*a?VwN2T~a>iEn6kzKH`e7d9ffo@3iQAHr(UV8TTlg=Vtplr^qOuKw;qQ~kL z#gLjvpf;o$l-aljE}tNIC!CPB=gAP^wG;!?c^yAVsK zS2T4Y<#X{i2q0-*2(sgKSv7QB3h%Q!98~;qn|rsF8F;DyQkpuP8dZ6o`lDG-Pzs;M z%xc$A*bVF0YQ>`k0%FMx4dVJeyus}4ecbM`twXCF7DA<^UpmZY$;(^!>#J2G>oF1l zra=(9wJ9qsU3$aG0fh=K&Hya-<)rPm3|6^uZw9m5E%u00Kc+R%-}%Z{Q|;AG6YcaQ zPXtl}6wP57kWW>jk2VC-34Nr@k+KIg?V3!eNtfK`p3}3#0iqWsU~7=KcK5j7NhExr z)|aYlSPsVtOZPR%1}OYwO5v}HW#uIqdD8V?L3R11dn^*o?!%8wSIBU zH-Scm2!YuSn>UIDfvfak0&%y?4W8`Bj@+q8my8xJav|>4NqFN1e0Nu~-vDuzmXuH! zAfOW24M^O=<`o5h;82WbImBMhDaLZ*zxs3%r0TDTdu3J_Vi}hl;BX0o5S2TA0wt|q zaH?%C$(PP|&ND6YGK!6V9bupNunWhIsC$t?_r;-{=rhK0&G(MiFF>+-j$GSuu3d!O zw1eteyoM$<0;2W7r|=dVF&n!p+@IJCQ+sG-lutJq@MpHUwi0858tImT19s+g+lAMA zr8)*MsASh%N;yGe4xt~qwjN~jA!v)C@kof<>ijVyv-7PtFy+NZ9Y6X=N7aMsECBn1 zbwj&*%g0x~w$=qaaBOf~RAnABh%i&%(ML?<6*1|ThSTl`%j(i&1Z}&|a`JK$epBEA z)xi;#lhl9lhgpHY-P(w(VyUL3i3gdS?S0cu&PLVbgJDA(r=LtvJ&-!d%2slv!>FER z(7nr2LF#E?i=2dFRe>O00mH<>40FSGq1U+CzgQWTQz);tc6q=-VpgG>QS+y3kiP$5 z1Ix)Lce%6j>J@45Q3Kav8@#bO2>Xsm84hQ(R<1EYZqeS%{<3F`|vPC``;E8;&pqU>WoH5khI|EcAuSz%q486@|7^? z2IrIY7UA$=0*mEyU9DaDq(K%`IQ=~|4Y?lKRs2Fe5q!HSI8a7))M#a~XPovjH@MOy zi)L!?D4`$Qi}R;=n552O5Bt4#q`BA1EXmDZYB6c*P~!C4yI!GqhYp;ReEh0$8{ zwglbkVsd3h>+B>WDScSwlTiC-KO&!Sv7wyJm8&CbHd1NqF` z^r!>nBmDMVmsRO;TZG?V;?iFTe9YZ{>90;{-0sNtuXUbBAS&xn`+&+Ra#%niYbSqA zg5NJ5Vx2`R$~7G3X$LJGMHCE#7wqyv^g~p$({jF&4N661DvG>#b(_&RpY$c^ z>%1w3r2DwX;8GsCx``PPgfF#zzQSeP?X(}vgU--VHCi{J#h0QXITenIVIl*`_+1s< z*-x+=lRepMUD7n=kz^+h#p^~cNEIM;oB(qFqG@015s^e*>5 z^0u58p}Hxw_-rg?sD?4NUDCO|q*t*`!?xaLuIch($;xGsna3uO^xB!)XMbQ}$H;}r z7)bw3Ed6Gm$qKcz)v@6PKk=p!a|1ZZJ(gb42SXifwqZn7TT{+c2oK>pXx|5-4Q@Wc zZ{HUbMB=N7A?)MWm`J}ciaxJT8mj3zyMCdw-1<2JA$8c^c}RS2{~-7Ua(<#4OU?)?duYQCWJQj!;&ath^v*6wh0!JT9qpTVTQ3EOr&C*(6P2XvImV3 zMW;z>5TF(w8Tk`n*$)O~y6Gc~Ty<7N{a-sjiFzUc(~6qeA>6N(dj(c6ySD-brs>op z!*bqpJ$jkaEghE4bWWx1%ojH2XG|?Kxktm(uLJc!hPOi4r7TB;cP`cV2(`LdRxf?f zr03?UJ^%r%G-fZvoQH(^REdg4gBk?K(gGM-0Eg4&rX8r*7^_gC?*Z1!K}dPg>9A{- z!Xn20%pj*{QC;aOV&+?_(K&y4oQ(iQ=Fq0}Iv{{jLxh~78+Xha0D#ll66l$(;Y>GlZGne{R%49Clsfc<)zyOVc1wbVT#uwo}bIL#xdpcnIM~Yr)vV0 zD^^+=@t*?=Tt4JI97UkH%(i8~Wwn4fA^+4P2jFc8k*p16IXe5U2uWdcgWGVxztoqj z`OB{n<$Nn!>@_L*fjgZ_3dGYwX(xkEvKYy#(%SrJxfEf~NO3sPAHw}{1^7!kzB^=O zCp;iKB*ps{;0DPFkqEtcYQHW@pC!UPUv=>D;t1TJ`$>Uft?lqkWElWPc_%&|EI=UR zK`(+`Ny;qNf{+DM2TrKkF5#)l0IzOnfRePx@)wgRD7q+&qW~N}qYX(tOSR9D#NCf2 zqXY*!(5vs9K6ele>pRwNMtgcxCB~%DOn;1A_)^Ui8W7u_D7rp^kdH6u zrLGTO7Ci;T%z8oTK|@KcjE7VakSS$n0pWeQUPvbkM)n=IPvqJsE!_raQuUE^3;3!& zEiLUJ)YBxPbewxNP!25LJ!hz}{?Ob@m;%<7Vt{kOYbptau@eJq_%D%cqt=zymm(WQ z0Dz|SD~Y6dhw~T&>iIHq1?wo*>5R!lk^Ysindw|_nj$z8S>S{~53OT$teG0ziiRuN zq68Ug(=nSTWJA34x!bch1D(<#dNC-vIV*|<1X!8qWRBb8iwBaq$!A3v#Yzdq>wx@BA06}i@DUfQM!yTLKL^BLDpsdWTV(lMSJ|avJIBB@sxOsyw zoNLG@BO?Pzk2^)#`py=T^Idwzw3La;M@No%&OC_;MABByNcV-h1G?wQM!J-i*QPcI zL;(}VmlB>qG7k^dR{)b4IL_Z;T|}AyX=4>ENUo*K6gxJ%UP5aopftVC(sQ*d`&;o? zFyzN1Yblgvym(2bXvKwJvT-%LfoePj*)ug~o~(qZnIxM`>x>~;N7@?@g;4Bb;tCx( z2{65-QU$*OdE4sv*gSx98J&;uGLWm?`wCgc>zOS4{iT^%&jT@XlEMs+q-BDzdThDenYI|e9B z`|Q|HG;Rhqo(x0P^{=p{rSvT} z#qHm3dko+ejh~a{6BkMwBM48AnN{ltt0o}mEuvhOmJlnctqggj zCW4K6Ei5>XMc$AETl$v1dlN7<&rSa z$jQ&UjC$nkp`q&xPWIP260^0m%tZk@SkW@6Y_Ieh++j{|(r+%+U>|7BJqt({#F-L8 zlKTK{z`k0uW;*dfcm2eB15`wX+hDZufgOGGq>P*D&Tl*=AD>;C80z^vpQ(~J(l0yE z7S_upR!HbHBbkHj|I-7}i!|oB*IN682iU6$SB8L4FbQR7Kb2|d<5>=|8~YTrip72V zA~*`LNOIez8iFF4gx3@|lzw%#Mh=-g^Tzv9<>E0^kyp{D^Zma2N zQsT{bs|HsSLf-M8MlkszC4c4Ukpqneb&VbiIoS|bP)e;namcgw-2=w~h1mKQeI#@0 zPfN$w9^+d=R{3!LdD1!|F{O&Z%rpHi_V&X%4>6)wnOa>;HzZW;9>=|6R|(5*c{WHO zWTrvXA7207qo#ECWerq$iaCaeM(EHl6+yAbVw^%w8M}EhPR99!emNn zMoP5nB4o;@Fbhpq?Vx9aT%&Kby%K2lW`_k1XJSoM#89NI^z!89#^fn-uK6Qy>gzw` zZ99pi-nv?T7@A~F&y_2hsbj?g5xw?2b7KM4c4elt0&@9ZG@7&Fbh|5^4nT5O<7MSO zl6QL$z_M;3Q`Pl-m{se}KwFemRMeBYVpFz~%HWcTshNP}!>i=p^_DxrK69xWnx^L| zse%;6s^+;yMc39;Ek!U-{R5FRcn~5v9LO<&6phnP z3(6+U84ulfz`o!=)TSSUpqBGH3kjEz-0J|thA5EDW^B}^xhhWF1?n0aM4QCwp5C`f z-6kL&t(ee>1mUE_mmy=bFnnbiY$;b&B$ULeHGSV^Wh7C#@%>op!|AKq-(wnLb!Qou z7NREI5=V6NIt(Hd!<^u}*p%;J9(I6oQKe>G3y31RPqcC4o;Ri#Qjlzzpld(ZB7l0X zz|c5$xOV1Oq?*s%C2ab2j^gc>wS~T(9?jw*J-PbGWwWBsDe86W{_i6DnA|-!!ljhh z66rVc0}M0u)n3N+?Q*QO3jlUjZE>`T#~>tXv#E+UGmC(Ucn&|0zw{G@K3EMSwQQ+l z;%#aPkH*IYZ07~ZX9Po6Z0X*dsVWSl)^d7UwIz`4A2>LI*DYHtPt?C5@IGc_*Y5R~jo4Ox5y7Mst-LCm!IldSCy~fdx}?NWME&!rsip^onB;%vH7TX_?7~o`@$>9+ZL;b*0L_gAI5Z)s@VC z7x$Snp>^~KN71U>X;kRdwW_WVPWQ?7!IynK+R+~Qrj?i2iku8Pj8lWKm`Qi{JoC{j z?)Fgx7Q!#-J*Ir<)1t5ELso!tGp7h-d@CMMJ@;^sgumt+uBHQ=qHl|wsuCv!1T;}U+OxZ#&un$F2(~9vPvorkF%S?8aJJRCF*|`@AF!$3{->y^FQ2Mr?X+_m#N-vn*IG`7yb6$^Db{V~MQ}6dlLZQgT!9^h_?X~#xD6f84X+_-6&0#@SXKkA@OoI8e zKZ(Go$eImyS4Asxs9I4}Lg3ZZC`UUxg$|2(g(u5p9-^h`@<8g1Yip%ENQ)BJ+v7z; zTXUY3#FrE!7^<{mAmA2{_2<%p$1Afd_L6qe$Nzkh?*oE<02-hIme<~-97XG_k+op* zR(LUmfh&vCUAE`8O0FXy*K0eAUx<0=cGU~L7lpEvVo=>8eW&w0gTKwF(SzvG6I;AJ z0{vq406Ir=)TjifUHt=>IJk)ddzZVvdh>eQ1>7C+{Z^0nJ;LV?=v=t?3yJyTf-8dv ztYbz33k~|~QCl?rcqJu}(^X~Nc5#~)?x6yFZjX09%{JdzpbS0dnh&XFXIosv|LA~U^Ph|AM}8O$ zsnIHK4E}qQjCLc9#Q%TX?R(bYaeQ-noHO;uBPNaluDx)!VcQowkLu~^0U+;fC&a$C ze6>zA8ZU!lS2o9X2MBRzV2>oLT`JO&7opQ>&_Z0jC<&xZo(G;SjOt|R8O zjK4bikBfs2VA{TtHrugXM==4eJ>l?rKIG>V`SZCtDnx|+w}X@ix4X}YAGo$@B0CaWC>^AOb+gMqRc(t+2Wd^G0#Y{ud5#G+ zS_RvD@3vTMYN`aD#X$|lZRRl~X~M0lcWAe7b1OBZdFk^C5HZ`TvjFe}ha%iNyS@>> zXPb0%qbwv-cV~*=mUqI#EbNCIe}JsZ?`St}^H#SnpozP!l2V>*J;t%@KlZB(cK=f8 zZ2}WJAHZAPxuqDtRrBBx6Q`i4frTJLb9cGNHn++}su_|A_fKSRc(S+DCB7??gc7lvD8T_*Cx;?%UD#Hl-)ksq~w(I!=P{gp$%XCs6joX+0 zF?H2g-nX_g;rUrI!CSNjV|i{wP7l ze3}VPui?imF*3yXIeaTE-)00M#};bcluS~#NpFvxKqeLh;qpbv@wVP&NpdC8#2VO> zihupN|NQ!-pB|RrkmRpT0LR7uHFoClP_F$S&l1m^q@oRp3YC4ACCllQ>}B6M#|+9c zB4i!Zsh)~t-)SMjjD2S~6~5R9P3}dj@!2{7NixyNps-Bg}UBa@IWqfJ`t4@>jv*a75_z`@h| z`j$t5nVBAz=J0iKR|If_+Z!lq+ipyL%QG(jz_&XR{~Fed5dHcP<^B=U>E}+WJAz1* z|C%{9neC9g7Og@OKF+`=_&9fcwOGdf3T<$<5lNHZtf$m=0MLRg{dbSnE)G#8+#i1( zn>)JDGftbwgVTRo()!sjxG_%WsT@Zw000Fd@mdb6#SasdMG@J#GEF9597Z!x_a_WA zeVl9x!~a#|45{yk+1^Y!jNPx?XMX&$#nTWN_;$xUXHxJ15Q4?S1`T5D_cxZy*y-4H z1bWKJHiM+{<{;?GRslhkpc1o|OY|s&SpP!9-=h zUI$rD@*ou=K=b3kDLe!Q7u7k~%S-@N_F-H6HoT`IlLci6Ku_^u>Nw3Hpq1Ly68Y8ho44#M#`nds$oRX#UHu2PFB5_G< zv1dB0wQfUQOMuepx-+vJj_!d>%*XBmaW*D+`|Q(>aS>981hYQXZ zbqEo{qj4Glw2NL_48@LlnZ@&@yM#EF($PHUL zj~~B%<|(T=lifQ(X+()U@tST8UKDoPaGGutzT0gn{3<)q04)aOs2Rch zPd^!*o8{H})KNhTG6+fJ7>Dey22Uy$ws4|=k)qU{zNNHg{^4rdYAYhyO#OmG)Kiy1 zaB!3Z*a_iLY%(fs`mSCZkj8~19fUzAdPJvLcfR6j4`{ugBrM&RJ*xZNd~HrszU?8{2gGu}X2?9<BhVHd0CmViXUtJT?|!>IwXg>`! zfdUjhs`<_Er6owHLl z7VksS%y65>EVnQ~EukI^r3Y*~BD}0yZN5&qZP>h%PPhyukL*SJ01GnMhGcCqqOf9{j7Ld-cGDd1WL`1aY zM=s$g!u^e(6gHL=hQrc*4vk)Ew2%mJBw4&@a~5gbAtk6uJmng+{cTgPW`1yBzh`!X zTH34UGGx~xvtG?g2vO)CT@)dGIuo1|$ez*ai&_lIXF3OObf-%vwPuGPnrpPX^i~T} z;QVyU= zwJ=ki&+=^q_Lm+YdAjDRRF+^KA9xB5e#TO$pn}gPQB_xaUrVEcgiY%3^m|MfzT~xu zD|du9_7*BM`X~K7JUDoM5;!$%$q0`qm?b&y9yJOG1JpHd0~(2yi;9}i4vd~f{?uR3 z*iXhwt~sTL5K&INTA|D*^_UI;x@RGy{%zaMbK@7cJ=5sEBE7tUfq9D(K&^yA1|Ox z1$s@>^m)VtbIkU-8z&4~L@y;jVZ$|Ngp4oP@mx}g$?ssM@FjZf)WBCqQwWSoqjsgP zvQVS<^TC)17o4~k!^PtMzQ^1rl>>4+2sLsB*ACEfPQgwfVss>#IQMWDeN+vkma6&+gWn;G6*d#<+RnTiw{YKfM*veLSsr!h{o)V1_p$v=cY7F-Y<>$W}f_xeX zu1JGNKEfzx(4<8fs~@*rpA%L#U0iF#%ZJ2Yw$=_v7tbv++~Xx^1y_6*@pR)ykf^Qh8ps@;{hgDDoc6`1T> zhWUFnw=2b5`C)zZoS2JI@-ypZ1u`qgVY>^(V6I24E&d@shlFCf54w`?vgr29 z(E8JeKzaKbqEJp%=8T%B{Myz zTG7CBsDV}{k{)cV;I8@vMjPjh)NCUhBk` zvvQk9i!k9qv~E)I8G>g5k`_VLM~P5ZMlh`0dO9hd+d$B_jPUCuvxihMt`bpy(x7KO z*LX@t5@ix7imvPwrla>!d=^VYx7BV9`PN8Zac*mpK`KWXpEF{rku4S0W|PNP%eMuj zN!Iq*Q)v!%Fi!Y6#j74-pW1n$!Y%_Vy^Hk5*>ZYrAvzs zKC-B@b`dD+nF@~c!l6yti-E)DG>=X4))c<`eB++_I$U${l3WQTM@Ma8z7pqB3|4Cn zjTMPqAdXG0)Jfk=G#HK5HJMN{8TsXorJ*uV#HyO3XR{r$n^&M1lY>k{)KzPe=$>RoC;2EEWRK^+XyfLpGB@Tt*hrJm7jDkkfE|<+AFvN}m6}WU zQDXMd@A>FCIKwV`H6M6mZo_Ah%b?ucg+m~3|4+iLErKw58<3OV48u!(Ykhrn%T zJj(%*z3$S}n8-9A58Tm1A8^BkrOG~YzP&b94YFwMx{6%&40!HHg0;|CspeY<1iCyk)|A{}?=VMR#8~trT4vIu?{W zgS2UDxR<0b{}$;;QxF^)Q@BI?_co8mE8n)R71udoWsl(E=u6^CSYp0pkThw< z7bDRxJRP0_e;)~9FIA>Vu(Uh3!lnBrsYk(WTr%TcQU{6eta{qV<9I=dd0KT@KF4U& zGgX76CU`(pv?Q}zKVbR;ZVFX2Ni9`fRG<@!;+e+T$!eDAeCu9Q>LWq}eV1?H<^fD7 z@}%854hFM)d%#<^l@-|gq6_{>qFSk|sZ!!!_mUU_sy$)W>D(t@qSsP8wNzd?iqW{w z3KOb7YLgvQ3b9(*nqA3eDZa@ySov1M4Oy{nDXV8{JgI^b-m);xA;~W3jxuh40ltem zXwB=vxTVf$HMAY(B~y*;nS1Z`Cx?Xlgqz=c0!qO8tfyVo8xTspNEuDTz3Tm7(&_Ah)2(v#xS*p!p?Vik z0}Ga0MwzFk*~@KMtlXNc+eonqYL(7dUTd9YJTa^GUlfdAz16w!GM)Q@2!_mUXO(n1 zwMpQ8kESqnl6cX>CCp8XjC=C&+g$Pp{Sv?t-w@q^eCv1^+@lH6Z<9?;w5ti;MCr86Q+j_mf_e9#k`L`=k zC_*?Bu%niUHol5AXEDT1^{W9w4Q8`eE)=i$nN_}iDK7EY1b7I%LgD2R&-Z#(wUX}1 zN$&>2y^5{HdeN*sUxi7^um+z9Z$ovTUJ6{)zE@>J9P_pT$qka`4}8czS4hx9ZsUVM^3M~(oo{r zlU+xyuE!TD$pAKDcI_sF2?aYVZ%33Gb3Vdl7`c}!T}f#_RrH2R6}mgRx{e-%`Z+dJ zhY8-sQYIcK#>EKa-OlCeORTkp`b!fG7KDyUSF8AI5?^9;o?a?QS}s^sGHch6B$^gE zm*;n-AvE_1b2&S#xNXqghQl^WETW!hxhXHejTwns{5_01ZI$;^6%16PZLtK_JV>&O zOzlhF;)gD&QE{{F{#X`lG||WNfVWAWSBp^XqOy9U$&Bs;?)OV;`casAEDOWPf zZ)sJ+0N%~_ zHhfMT85UrE^L$;CsUPPTFG&dL2}%+0rn*%5$B8;TuKb98_@SEnk@A%YusL6aiy{0a*x`vNI@XQL1cNe(vRI8lGOJcosKt`p)bA~z+b+nHQ9R>T|A_=BA z*X`gC>y^wBVwPwMekzE^?B(N>@SSP~jgzqnd?~Y;Cxo8$7Pz|zsCKJ{3ssonVxoAK zmCx!9c+pp`M)h67#r$ksBx#Bp3OEkqhBrOe8S^Zb^h@wa@aTck7ZB1z23NT~l1DQ< zoVCd(pw#?f2JU9h@{6RwA$#SDTz&hASyO`%?Xz~6e1?3(R2+r)QH^ZAk}kkxXkOMl z1H09akr0%KGS*j>Bv=}2Go{~Ifn)i|^eRunha6rdQ=?WG<)b{;C|s}c%!?Ig3zm!F znr!CbcRq|nsyNwKCF+8&h4N#@4_Y%#y+f!*9!k&=3<*HLPsoH}@{gcU=z-g?!%5iw zLN z7Qyc|Xv;dKladB~rZx_|KFg9f!1JQ*FF6aA0$a?AoY4<6;JIzD)7_DrshTv3zGd6~ zdkPvuqm_+z5>s|wV|0HjH|&njn@+E0_Tmn^-nHL;;uLE&0h2KtL+qt-Im{p(ist)* z1@Jjn8xt7Pnq7hgCCntO)0(A0f|GGv7);%>XS8|N)XzCzFh01J!9F?AdW6~cfk3)e zQ>&D%lE3}(BEK~fNr+8Au2`jaM)*{tL~evnsxOvKqFljV5+As+Hd5ba!)omh(PV2X zP_g?Qynap@zY1@c;|pVcBb4>-I$I$pDTIWTAqLb5l{kHGT2in;arc)N<`W)aD?^k} zP{|R(Q`~OQ!wq#->&}QVEb{GHuC-JSo`(9j7kfJ4hY`jlT8csB+|V>AyQ8O~TlGAz z=;d>@W;B%Y!Z?(dw-u{gkRw~;X~M|%t%sLOCZ@Gd9E_MBoS(HBamp)6F&N_WJ$ghU zQwP_t1x|N=;(I+jKrObLuNTqN(^Lkw#WvXA6HE4}DCTLg{rZDmD&Ne{XX}di=;5;~U zfc}6I<_AH2xS#^%o4NXCJO~k!ywW}RoCu+7{na7L$DDU~YM`5;u1rwB=3_0CvSQwG z($ay36dk-=rYqnb6T(FK{Kwl?nWiRL1ruE$a^s1G&=vELvu=_LcaU)v@m6{(t{Ucj zIn!ZMPI2;$djq474p{QSDO$W$xV4iY%J0d-g`hmHkqG8cG9hq2sC>4?WT(`qDD0SO z_O7h(z=Jbdi!y=|U60C(hf)ne|DpnGC zpu;vi^cb4?epwU9)@autfAZ}gKpu+ko){BxZDQ17HY>FO1(^UA55dlKo-`QXUR4qG z&1=qJ2<$Y8!D7feW;3L?6gx(T(j%^L4rn3;-xFBJV{@Hvhq3KPaII2qS55%DKO(Pn z^i`*+MuwNo$m)b|a#J9tJ2-9Wd2l z)Tt$hQW^VwrEi$aDlP-fyr z{&8^`SA?&&wi1}@56kp9Mx6gm8n)LwV|n|pZCL553P%K%`}I%a8*d7s7Vt67wH@P; zw~t!M4(+&p(D-EC*D`MZiBqWE7^fd38fg;XQDDhS5hmiM#@CJ6+R7aNhL@`HOL;a& zrX42=l;JEqaHt5+g8Dq;iVxK-go1D$Y?4jtaiQ$KNUDp)%01HrZuoV_q+QBGEU)}a zXFJck1@nCzJvTNM(LArsvIr>MBS~tz`_7$bE{IR?%KqZ*u!D%5s#J;Qqg*+x^85mS zU20VS%$1|BUZvCZqb%ALuFkS@?{fK#k-V0*yQm8udiJ|psKM$nS7aizpd)3w~kqDOy2UTLCawJ^75}vAV&xrZvBm^ zF?KqPjBv84Dc@$+uef|sZ?B>o)ZBY^mmv%e-qqq0AThiW@Qv8-2ID?{6`Ha<;w=eOjs;O)t z5_PL&x#G78M4E_htJ?4tyTPXHpR4vAyY=Ep$6Axz@DyoF$Aj?JPNFAh$5vC6vF+z* zG5)PC38APFx>i)Hx^}}Pv5qC@vvNS}*e6TkM7kMIdoo8Y)NeJIG<})XJH$4gj=mnq z$1JVC6haSRytj_6yxzUCVMihDhe#sR8%QotsaSw5y{8|oH z_peiMjiXnhS6s1`ZQ-O-Q7!{bjAr?ld6NtNzR>b=){ELos&W};FvnilBV{HvUTav9 z^jW^1%t@;3oYDmyFLJ0``RW945oLrkwc6-yj0iK4tk!ge;`45P-5=XB^G;+YJ}iH0 z!UH(6vGap@b-REtsD8uI5LV+XPLfXohs@jV`I8*mdrLo;!;TgSZU+Yfv~XqV@8&2a zGSr1ETudp^kM5I~UIUUSY%^8M(M})_`L;f5{y97cLFw<{+17dcsrP~*TQ8)@tuG}f zv#tso^TOK?8&ZqWZ_aNHW4pz*1LN8AMDw^ey)pPtxqJ)HT*NnG6W1Pn*OigA zuRG&)09azTKHK5@#Q1$aPm$O`$;6Gb86eqg?qbTwSN-&_7yD;8g{(vXMcqVF>e;UP zM^MS!De$)xFa7>3aD%m)4g%NcmDm3+wSFF=k9)iA9Xp4V>(reMP6!3G4zs>=$vSW9 zvu}omgrrHP**}2xr!ye*SOy$$bl^xi(eyiC_=rqmW%Zr=2w6Fgi$YHL{bK|G!QmW{0LS2V*I(WXf`2G_2cSR-D2OX*Y77v~hQ)hA zzQftLf7~V^p7x>UPN5uxKr707_&nVeS*pf8brJT3R&>7u8!~8E=l!Xz(#orC7~@f< zon`XZWcYK20hyDc@8n8#cW(8y4>|GJ63p8q8->raXg^0RLvQy|{9h9LfBb&z+O{E& zi_307-ahc_(i{!`RFKmYRl_OG|gzaSt*9_}OrQHkeu$-n=mo&WiX zKL3r*J26|vGc>J*H(6+kueU01E!;Bht!MmtGdsQK=kU)h8lDMh*Z(YB-(eW;gnxmN z&ZAOP6aVYO{MQe=n?g{ssi=JXW}s>x;E^=4?k3cIQ?LGdGhnOh@9%%11cIKFhymL- zVME0gUqh60sZ?fMV2!BI!gt2?&+@ZBnbRmT;(Z6NNmq`tly<@3W}|#2TmKg%h~Dxu zJRQMyiwM2@Kym*yjw?4m>}Za0e{))vGyIzKt4B5Og&+K??s)64V1>&-d0G_DZ|R4P zJ|r$8JpcMGf34_0E;aj+en`8EeEPA#T{+*+gZ!gVO@(qcaxF&c8Y&&&Kl1X#CImW& z+6v_)kgEyfXrj;V+s_Z5c{mvAwzT)3D;~It_nJxkH%^VyR*$J?o5A-vJEVDU@~Qu| z^V2zTLyOsGI99Znjm_yw$MOegpF`s}5)W>|QHdvVj?RtL`I$a`nbGHY9{vMS)vSz2 z%`lZxI(T>C+A`*w(fN;$@X0`2d$MY2+`s-(^8{nDzQcVdA|XGAK#uxXy|es7YSW2> zcUh4=UDLZpmD5e8tCn0hnNmAHS#SzfAzahETxsj`m$jbz*Hg2{mAxV`kr5o8O&(YI T)H?^afPY4(&3{7w=pOffk3*jS literal 0 HcmV?d00001 From 223464a51a63e6ed4e4ba1664c682d379318339b Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 5 Feb 2024 14:42:11 +0100 Subject: [PATCH 320/468] refactor(catalog-graph): apply review suggestions Signed-off-by: Camila Belo --- packages/app-next/app-config.yaml | 2 +- .../{EXPERIMENTAL.md => README-alpha.md} | 73 +++++++++---------- plugins/catalog-graph/README.md | 2 +- plugins/catalog-graph/src/alpha.tsx | 50 ++++--------- 4 files changed, 52 insertions(+), 75 deletions(-) rename plugins/catalog-graph/{EXPERIMENTAL.md => README-alpha.md} (65%) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index f43fe32faa..2007aaba58 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -16,7 +16,7 @@ app: config: filter: kind:component has:links - entity-card:linguist/languages - - entity-card:catalog-graph/entity-relations: + - entity-card:catalog-graph/relations: config: height: 300 # Entity page content diff --git a/plugins/catalog-graph/EXPERIMENTAL.md b/plugins/catalog-graph/README-alpha.md similarity index 65% rename from plugins/catalog-graph/EXPERIMENTAL.md rename to plugins/catalog-graph/README-alpha.md index 9af7dbea66..52c09c4b50 100644 --- a/plugins/catalog-graph/EXPERIMENTAL.md +++ b/plugins/catalog-graph/README-alpha.md @@ -2,7 +2,7 @@ > [!WARNING] > This documentation is made for those using the experimental new Frontend system. -> If you are not using the new Backstage frontend system, please go [here](./README.md). +> If you are not using the new frontend system, please go [here](./README.md). The Catalog graph plugin helps you to visualize the relations between entities, like ownership, grouping or API relationships. It comes with these features: @@ -44,7 +44,7 @@ app: packages: all extensions: # This is required because the card is not enable by default once you install the plugin - - entity-card:catalog-graph/entity-relations + - entity-card:catalog-graph/relations ``` 3. Then start the app, navigate to an entity's page and see the Relations graph there; @@ -128,9 +128,9 @@ The Catalog graphics plugin provides extensions for each of its features, see be An [entity card](https://backstage.io/docs/frontend-system/building-plugins/extension-types#entitycard---reference) extension that renders the relation graph for an entity on the Catalog entity page and has an action that redirects users to the more advanced [relations graph page](#catalog-entity-relations-graph-page). -| kind | namespace | name | id | Default enabled | -| ----------- | ------------- | ---------------- | -------------------------------------------- | --------------- | -| entity-card | catalog-graph | entity-relations | `entity-card:catalog-graph/entity-relations` | No | +| kind | namespace | name | id | Default enabled | +| ----------- | ------------- | ---------------- | ------------------------------------- | --------------- | +| entity-card | catalog-graph | entity-relations | `entity-card:catalog-graph/relations` | No | ##### Output @@ -142,7 +142,7 @@ There are no inputs available for this extension. ##### Config -The card configurations should be defined under the `app.extensions.entity-card:catalog-graph/entity-relations.config` key: +The card configurations should be defined under the `app.extensions.entity-card:catalog-graph/relations.config` key: ```yaml # app-config.yaml @@ -150,7 +150,7 @@ app: extensions: # this is the extension id and it follows the naming pattern bellow: # /: - - entity-card:catalog-graph/entity-relations: + - entity-card:catalog-graph/relations: config: # example configuring the card title # defaults to "Relations" @@ -159,22 +159,20 @@ app: See below the complete list of available configs: -| Key | Description | Type | Optional | Default value | -| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | -| `filter` | A [text-based query](<(https://github.com/backstage/backstage/pull/21480)>) used to filter whether the extension should be rendered or not. | `string` | yes | - | -| `title` | The card title. text. | `string` | yes | `'Relations'` | -| `variant` | The card layout. variants. | `flex` \| `fullHeight` \| `gridItem` | yes | `'gridItem'` | -| `height` | The card height fixed. size. | `number` | yes | - | -| `rootEntityNames` | A single our multiple compound root entity ref objects. | `{ kind: string, namespace: string, name: string }` \| `{ kind: string, namespace: string, name: string}[]` | yes | Defaults to the entity available in the entity page context. | -| `kinds` | Restricted list of entity [kinds](https://backstage.io/docs/features/software-catalog/descriptor-format/#contents) to display in the graph. | `string[]` | yes | - | -| `relations` | Restricted list of entity [relations](https://backstage.io/docs/features/software-catalog/descriptor-format/#common-to-all-kinds-relations) to display in the graph. | `string[]` | yes | - | -| `maxDepth` | A maximum number of levels of relations to display in the graph. | `number` | yes | `1` | -| `unidirectional` | Shows only relations that from the source to the target entity. | `boolean` | yes | `true` | -| `mergeRelations` | Merge the relations line into a single one. | `boolean` | yes | `true` | -| `direction` | Render direction of the graph. | `TB` \| `BT` \| `LR` \| `RL` | yes | `'LR'` | -| `relationPairs` | A list of [pairs of entity relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations), used to define which relations are merged together and which the primary relation is. | `[string[], string[]]` | yes | Show all entity [relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations). | -| `zoom` | Controls zoom behavior of graph. | `enabled` \| `disabled` \| `enable-on-click` | yes | `'enabled'` | -| `curve` | A factory name for curve generators addressing both lines and areas. | `curveStepBefore` \| `curveMonotoneX` | yes | `'enable-on-click' ` | +| Key | Description | Type | Optional | Default value | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | +| `filter` | A [text-based query](<(https://github.com/backstage/backstage/pull/21480)>) used to filter whether the extension should be rendered or not. | `string` | yes | - | +| `title` | The card title text. | `string` | yes | `'Relations'` | +| `height` | The card height fixed size. | `number` | yes | - | +| `kinds` | Restricted list of entity [kinds](https://backstage.io/docs/features/software-catalog/descriptor-format/#contents) to display in the graph. | `string[]` | yes | - | +| `relations` | Restricted list of entity [relations](https://backstage.io/docs/features/software-catalog/descriptor-format/#common-to-all-kinds-relations) to display in the graph. | `string[]` | yes | - | +| `maxDepth` | A maximum number of levels of relations to display in the graph. | `number` | yes | `1` | +| `unidirectional` | Shows only relations that are from the source to the target entity. | `boolean` | yes | `true` | +| `mergeRelations` | Merge the relations line into a single one. | `boolean` | yes | `true` | +| `direction` | Render direction of the graph. | `TB` \| `BT` \| `LR` \| `RL` | yes | `'LR'` | +| `relationPairs` | A list of [pairs of entity relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations), used to define which relations are merged together and which the primary relation is. | `[string[], string[]]` | yes | Show all entity [relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations). | +| `zoom` | Controls zoom behavior of graph. | `enabled` \| `disabled` \| `enable-on-click` | yes | `'enabled'` | +| `curve` | A factory name for curve generators addressing both lines and areas. | `curveStepBefore` \| `curveMonotoneX` | yes | `'enable-on-click' ` | ##### Disable @@ -187,7 +185,7 @@ app: # this is the extension id and it follows the naming pattern bellow: # /: # example disbaling the graph card extension - - entity-card:catalog-graph/entity-relations: false + - entity-card:catalog-graph/relations: false ``` ##### Override @@ -256,20 +254,19 @@ app: See below the complete list of available configs: -| Key | Description | Type | Optional | Default value | -| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | -| `path` | The page route path. | `string` | true | `'/catalog-graph' ` | -| `initialState` | The page filters initial state. | `{ selectedRelations?: string[],selectedKinds?: string[],rootEntityRefs?: string[],maxDepth?: number,unidirectional?: boolean,mergeRelations?: boolean,direction?: Direction,showFilters?: boolean,curve?: 'curveStepBefore' \| 'curveMonotoneX' }` | true | `{}` | -| `rootEntityNames` | A single our multiple compound root entity ref objects. | `{ kind: string, namespace: string, name: string }` \| `{ kind: string, namespace: string, name: string}[]` | yes | Defaults to the entity available in the entity page .context | -| `kinds` | Restricted list of entity [kinds](https://backstage.io/docs/features/software-catalog/descriptor-format/#contents) to display in the graph. | `string[]` | yes | - | -| `relations` | Restricted list of entity [relations](https://backstage.io/docs/features/software-catalog/descriptor-format/#common-to-all-kinds-relations) to display in the graph. | `string[]` | yes | - | -| `maxDepth` | A maximum number of levels of relations to display in the graph. | `number` | yes | `1` | -| `unidirectional` | Shows only relations that from the source to the target entity. | `boolean` | yes | `true` | -| `mergeRelations` | Merge the relations line into a single one. | `boolean` | yes | `true` | -| `direction` | Render direction of the graph. | `TB` \| `BT` \| `LR` \| `RL` | yes | `'LR'` | -| `relationPairs` | A list of [pairs of entity relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations), used to define which relations are merged together and which the primary relation is. | `[string[], string[]]` | yes | Show all entity [relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations). | -| `zoom` | Controls zoom behavior of graph. | `enabled` \| `disabled` \| `enable-on-click` | yes | `'enabled'` | -| `curve` | A factory name for curve generators addressing both lines and areas. | `curveStepBefore` \| `curveMonotoneX` | yes | `'enable-on-click' ` | +| Key | Description | Type | Optional | Default value | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | +| `path` | The page route path. | `string` | true | `'/catalog-graph' ` | +| `initialState` | These are the initial filter values, as opposed to configuration of the available filter values. | `{ selectedRelations?: string[],selectedKinds?: string[],rootEntityRefs?: string[],maxDepth?: number,unidirectional?: boolean,mergeRelations?: boolean,direction?: Direction,showFilters?: boolean,curve?: 'curveStepBefore' \| 'curveMonotoneX' }` | true | `{}` | +| `kinds` | Restricted list of entity [kinds](https://backstage.io/docs/features/software-catalog/descriptor-format/#contents) to display in the graph. | `string[]` | yes | - | +| `relations` | Restricted list of entity [relations](https://backstage.io/docs/features/software-catalog/descriptor-format/#common-to-all-kinds-relations) to display in the graph. | `string[]` | yes | - | +| `maxDepth` | A maximum number of levels of relations to display in the graph. | `number` | yes | `1` | +| `unidirectional` | Shows only relations that are from the source to the target entity. | `boolean` | yes | `true` | +| `mergeRelations` | Merge the relations line into a single one. | `boolean` | yes | `true` | +| `direction` | Render direction of the graph. | `TB` \| `BT` \| `LR` \| `RL` | yes | `'LR'` | +| `relationPairs` | A list of [pairs of entity relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations), used to define which relations are merged together and which the primary relation is. | `[string[], string[]]` | yes | Show all entity [relations](https://backstage.io/docs/features/software-catalog/well-known-relations#relations). | +| `zoom` | Controls zoom behavior of graph. | `enabled` \| `disabled` \| `enable-on-click` | yes | `'enabled'` | +| `curve` | A factory name for curve generators addressing both lines and areas. | `curveStepBefore` \| `curveMonotoneX` | yes | `'enable-on-click' ` | ##### Disable diff --git a/plugins/catalog-graph/README.md b/plugins/catalog-graph/README.md index 820ed1bdca..0862abbc96 100644 --- a/plugins/catalog-graph/README.md +++ b/plugins/catalog-graph/README.md @@ -1,7 +1,7 @@ # catalog-graph > Disclaimer: -> If you are looking for documentation on the experimental new frontend system support, please go [here](./EXPERIMENTAL.md). +> If you are looking for documentation on the experimental new frontend system support, please go [here](./README-alpha.md). Welcome to the catalog graph plugin! The catalog graph visualizes the relations between entities, like ownership, grouping or API relationships. diff --git a/plugins/catalog-graph/src/alpha.tsx b/plugins/catalog-graph/src/alpha.tsx index 4f92b16b8b..6d8429332e 100644 --- a/plugins/catalog-graph/src/alpha.tsx +++ b/plugins/catalog-graph/src/alpha.tsx @@ -26,57 +26,37 @@ import { } from '@backstage/core-compat-api'; import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha'; import { catalogGraphRouteRef, catalogEntityRouteRef } from './routes'; -import { ALL_RELATION_PAIRS, Direction } from './components'; +import { Direction } from './components'; -type Zod = Parameters[0]>[0]; - -function getCompoundEntityRefConfigSchema(z: Zod) { - return z.object({ - kind: z.string(), - namespace: z.string(), - name: z.string(), - }); -} - -function getEntityGraphRelationsConfigSchema(z: Zod) { +function getEntityGraphRelationsConfigSchema( + z: Parameters[0]>[0], +) { // Mapping EntityRelationsGraphProps to config - // TODO: Define how className and render functions will be configured + // The classname and render functions are configurable only via extension overrides return z.object({ - rootEntityNames: getCompoundEntityRefConfigSchema(z) - .or(z.array(getCompoundEntityRefConfigSchema(z))) - .optional(), kinds: z.array(z.string()).optional(), relations: z.array(z.string()).optional(), - maxDepth: z.number().default(1), + maxDepth: z.number().optional(), unidirectional: z.boolean().optional(), mergeRelations: z.boolean().optional(), - direction: z.nativeEnum(Direction).default(Direction.LEFT_RIGHT), - relationPairs: z - .array(z.tuple([z.string(), z.string()])) - .optional() - .default(ALL_RELATION_PAIRS), - zoom: z - .enum(['enabled', 'disabled', 'enable-on-click']) - .default('enable-on-click'), - curve: z - .enum(['curveStepBefore', 'curveMonotoneX']) - .default('curveMonotoneX'), + direction: z.nativeEnum(Direction).optional(), + relationPairs: z.array(z.tuple([z.string(), z.string()])).optional(), + zoom: z.enum(['enabled', 'disabled', 'enable-on-click']).optional(), + curve: z.enum(['curveStepBefore', 'curveMonotoneX']).optional(), }); } const CatalogGraphEntityCard = createEntityCardExtension({ - name: 'entity-relations', + name: 'relations', configSchema: createSchemaFromZod(z => z .object({ // Filter is a config required to all entity cards filter: z.string().optional(), - title: z.string().optional().default('Relations'), + title: z.string().optional(), height: z.number().optional(), - variant: z - .enum(['flex', 'fullHeight', 'gridItem']) - .optional() - .default('gridItem'), + // Skipping a "variant" config for now, defaulting to "gridItem" in the component + // For more details, see this comment: https://github.com/backstage/backstage/pull/22619#discussion_r1477333252 }) .merge(getEntityGraphRelationsConfigSchema(z)), ), @@ -93,7 +73,7 @@ const CatalogGraphPage = createPageExtension({ z.object({ // Path is a default config required to all pages path: z.string().default('/catalog-graph'), - // Mapping intialState prop to config + // Mapping intialState prop to config, these are the initial filter values, as opposed to configuration of the available filter values initialState: z .object({ selectedKinds: z.array(z.string()).optional(), From 501b750fa27f1874c652446dda7be4bbbfb81e8e Mon Sep 17 00:00:00 2001 From: Radu Ciopraga <1442639+raduciopraga@users.noreply.github.com> Date: Mon, 5 Feb 2024 21:23:20 +0100 Subject: [PATCH 321/468] wait for GroupsExplorerContent test expectations to pass Signed-off-by: Radu Ciopraga <1442639+raduciopraga@users.noreply.github.com> --- .../GroupsExplorerContent.test.tsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx index 0e992c220d..00a6164aa9 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsExplorerContent.test.tsx @@ -17,7 +17,7 @@ import { Entity } from '@backstage/catalog-model'; import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { screen } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import React from 'react'; import { GroupsExplorerContent } from '../GroupsExplorerContent'; @@ -80,9 +80,11 @@ describe('', () => { mountedRoutes, ); - expect( - screen.getByRole('link', { name: 'group:my-namespace/group-a' }), - ).toBeInTheDocument(); + await waitFor(() => + expect( + screen.getByRole('link', { name: 'group:my-namespace/group-a' }), + ).toBeInTheDocument(), + ); }); it('renders a custom title', async () => { @@ -95,9 +97,11 @@ describe('', () => { mountedRoutes, ); - expect( - screen.getByText('Our Teams', { selector: 'h2' }), - ).toBeInTheDocument(); + await waitFor(() => + expect( + screen.getByText('Our Teams', { selector: 'h2' }), + ).toBeInTheDocument(), + ); }); it('renders a friendly error if it cannot collect domains', async () => { @@ -111,6 +115,8 @@ describe('', () => { mountedRoutes, ); - expect(screen.getAllByText(/Error: Network timeout/).length).not.toBe(0); + await waitFor(() => + expect(screen.getAllByText(/Error: Network timeout/).length).not.toBe(0), + ); }); }); From 10f0efdcb7a47a530a4571bb286f370567af3699 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Tue, 6 Feb 2024 08:14:00 +0200 Subject: [PATCH 322/468] test: fix failing template card test Signed-off-by: Heikki Hellgren --- .../components/TemplateCard/TemplateCard.test.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx index 9ff1dd1b17..ec4e4e1b0a 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx @@ -358,12 +358,11 @@ describe('TemplateCard', () => { }, ); - expect( - getByRole('link', { name: 'group:default/my-test-user' }), - ).toBeInTheDocument(); - expect( - getByRole('link', { name: 'group:default/my-test-user' }), - ).toHaveAttribute('href', '/catalog/group/default/my-test-user'); + expect(getByRole('link', { name: /.*my-test-user$/ })).toBeInTheDocument(); + expect(getByRole('link', { name: /.*my-test-user$/ })).toHaveAttribute( + 'href', + '/catalog/group/default/my-test-user', + ); }); it('should call the onSelected handler when clicking the choose button', async () => { From d5b14a0ad52bf362904d1af9c36c0ac1b5b68cb4 Mon Sep 17 00:00:00 2001 From: AmbrishRamachandiran Date: Tue, 6 Feb 2024 12:19:15 +0530 Subject: [PATCH 323/468] conditionally rendering the user name and email in user settings page Signed-off-by: AmbrishRamachandiran --- .changeset/shiny-clocks-greet.md | 5 ++++ .../AuthProviders/ProviderSettingsItem.tsx | 24 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 .changeset/shiny-clocks-greet.md diff --git a/.changeset/shiny-clocks-greet.md b/.changeset/shiny-clocks-greet.md new file mode 100644 index 0000000000..0c112ff81f --- /dev/null +++ b/.changeset/shiny-clocks-greet.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +conditionally rendering the user name and email in user settings page diff --git a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx index e3fb597a15..e9a710036f 100644 --- a/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx +++ b/plugins/user-settings/src/components/AuthProviders/ProviderSettingsItem.tsx @@ -101,16 +101,20 @@ export const ProviderSettingsItem = (props: { - - {profile.displayName} - - - {profile.email} - + {profile.displayName && ( + + {profile.displayName} + + )} + {profile.email && ( + + {profile.email} + + )} {description} From f24a0c1f6a784bc164f78b3d7bb5246f7b90d6ce Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Fri, 15 Dec 2023 14:05:35 +0200 Subject: [PATCH 324/468] feat: initial notifications support Signed-off-by: Heikki Hellgren --- packages/app/package.json | 1 + packages/app/src/App.tsx | 6 +- packages/app/src/components/Root/Root.tsx | 7 +- packages/backend/package.json | 2 + packages/backend/src/index.ts | 11 ++ packages/backend/src/plugins/notifications.ts | 39 +++++ packages/backend/src/types.ts | 6 +- plugins/notifications-backend/.eslintrc.js | 1 + plugins/notifications-backend/README.md | 14 ++ plugins/notifications-backend/api-report.md | 25 ++++ plugins/notifications-backend/package.json | 46 ++++++ plugins/notifications-backend/src/index.ts | 16 +++ plugins/notifications-backend/src/run.ts | 32 +++++ .../src/service/router.test.ts | 66 +++++++++ .../src/service/router.ts | 67 +++++++++ .../src/service/standaloneServer.ts | 99 +++++++++++++ .../notifications-backend/src/setupTests.ts | 16 +++ plugins/notifications-common/.eslintrc.js | 1 + plugins/notifications-common/README.md | 5 + plugins/notifications-common/api-report.md | 24 ++++ plugins/notifications-common/package.json | 32 +++++ plugins/notifications-common/src/index.ts | 23 +++ .../notifications-common/src/setupTests.ts | 16 +++ plugins/notifications-common/src/types.ts | 34 +++++ plugins/notifications-node/.eslintrc.js | 1 + plugins/notifications-node/README.md | 5 + plugins/notifications-node/api-report.md | 70 +++++++++ .../migrations/20231215_init.js | 34 +++++ plugins/notifications-node/package.json | 38 +++++ .../database/DatabaseNotificationsStore.ts | 97 +++++++++++++ .../src/database/NotificationsStore.ts | 36 +++++ .../notifications-node/src/database/index.ts | 17 +++ plugins/notifications-node/src/index.ts | 24 ++++ .../src/service/NotificationService.ts | 136 ++++++++++++++++++ .../notifications-node/src/service/index.ts | 16 +++ plugins/notifications-node/src/setupTests.ts | 16 +++ plugins/notifications/.eslintrc.js | 1 + plugins/notifications/README.md | 13 ++ plugins/notifications/api-report.md | 88 ++++++++++++ plugins/notifications/dev/index.tsx | 27 ++++ plugins/notifications/package.json | 53 +++++++ .../notifications/src/api/NotificationsApi.ts | 34 +++++ .../src/api/NotificationsClient.ts | 57 ++++++++ plugins/notifications/src/api/index.ts | 17 +++ .../NotificationsPage/NotificationsPage.tsx | 82 +++++++++++ .../src/components/NotificationsPage/index.ts | 16 +++ .../NotificationsSideBarItem.tsx | 49 +++++++ .../NotificationsSideBarItem/index.ts | 16 +++ .../NotificationsTable/NotificationsTable.tsx | 100 +++++++++++++ .../components/NotificationsTable/index.ts | 16 +++ plugins/notifications/src/components/index.ts | 17 +++ plugins/notifications/src/hooks/index.ts | 16 +++ .../src/hooks/useNotificationsApi.ts | 31 ++++ plugins/notifications/src/index.ts | 19 +++ plugins/notifications/src/plugin.test.ts | 22 +++ plugins/notifications/src/plugin.ts | 52 +++++++ plugins/notifications/src/routes.ts | 20 +++ plugins/notifications/src/setupTests.ts | 16 +++ yarn.lock | 130 ++++++++++++++++- 59 files changed, 1964 insertions(+), 7 deletions(-) create mode 100644 packages/backend/src/plugins/notifications.ts create mode 100644 plugins/notifications-backend/.eslintrc.js create mode 100644 plugins/notifications-backend/README.md create mode 100644 plugins/notifications-backend/api-report.md create mode 100644 plugins/notifications-backend/package.json create mode 100644 plugins/notifications-backend/src/index.ts create mode 100644 plugins/notifications-backend/src/run.ts create mode 100644 plugins/notifications-backend/src/service/router.test.ts create mode 100644 plugins/notifications-backend/src/service/router.ts create mode 100644 plugins/notifications-backend/src/service/standaloneServer.ts create mode 100644 plugins/notifications-backend/src/setupTests.ts create mode 100644 plugins/notifications-common/.eslintrc.js create mode 100644 plugins/notifications-common/README.md create mode 100644 plugins/notifications-common/api-report.md create mode 100644 plugins/notifications-common/package.json create mode 100644 plugins/notifications-common/src/index.ts create mode 100644 plugins/notifications-common/src/setupTests.ts create mode 100644 plugins/notifications-common/src/types.ts create mode 100644 plugins/notifications-node/.eslintrc.js create mode 100644 plugins/notifications-node/README.md create mode 100644 plugins/notifications-node/api-report.md create mode 100644 plugins/notifications-node/migrations/20231215_init.js create mode 100644 plugins/notifications-node/package.json create mode 100644 plugins/notifications-node/src/database/DatabaseNotificationsStore.ts create mode 100644 plugins/notifications-node/src/database/NotificationsStore.ts create mode 100644 plugins/notifications-node/src/database/index.ts create mode 100644 plugins/notifications-node/src/index.ts create mode 100644 plugins/notifications-node/src/service/NotificationService.ts create mode 100644 plugins/notifications-node/src/service/index.ts create mode 100644 plugins/notifications-node/src/setupTests.ts create mode 100644 plugins/notifications/.eslintrc.js create mode 100644 plugins/notifications/README.md create mode 100644 plugins/notifications/api-report.md create mode 100644 plugins/notifications/dev/index.tsx create mode 100644 plugins/notifications/package.json create mode 100644 plugins/notifications/src/api/NotificationsApi.ts create mode 100644 plugins/notifications/src/api/NotificationsClient.ts create mode 100644 plugins/notifications/src/api/index.ts create mode 100644 plugins/notifications/src/components/NotificationsPage/NotificationsPage.tsx create mode 100644 plugins/notifications/src/components/NotificationsPage/index.ts create mode 100644 plugins/notifications/src/components/NotificationsSideBarItem/NotificationsSideBarItem.tsx create mode 100644 plugins/notifications/src/components/NotificationsSideBarItem/index.ts create mode 100644 plugins/notifications/src/components/NotificationsTable/NotificationsTable.tsx create mode 100644 plugins/notifications/src/components/NotificationsTable/index.ts create mode 100644 plugins/notifications/src/components/index.ts create mode 100644 plugins/notifications/src/hooks/index.ts create mode 100644 plugins/notifications/src/hooks/useNotificationsApi.ts create mode 100644 plugins/notifications/src/index.ts create mode 100644 plugins/notifications/src/plugin.test.ts create mode 100644 plugins/notifications/src/plugin.ts create mode 100644 plugins/notifications/src/routes.ts create mode 100644 plugins/notifications/src/setupTests.ts diff --git a/packages/app/package.json b/packages/app/package.json index 31be0e1610..b960712550 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -59,6 +59,7 @@ "@backstage/plugin-newrelic": "workspace:^", "@backstage/plugin-newrelic-dashboard": "workspace:^", "@backstage/plugin-nomad": "workspace:^", + "@backstage/plugin-notifications": "^0.0.0", "@backstage/plugin-octopus-deploy": "workspace:^", "@backstage/plugin-org": "workspace:^", "@backstage/plugin-pagerduty": "workspace:^", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 90190b50be..3d8bd45e5a 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -67,15 +67,15 @@ import { SearchPage } from '@backstage/plugin-search'; import { TechRadarPage } from '@backstage/plugin-tech-radar'; import { TechDocsIndexPage, - TechDocsReaderPage, techdocsPlugin, + TechDocsReaderPage, } from '@backstage/plugin-techdocs'; import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; import { ExpandableNavigation, + LightBox, ReportIssue, TextSize, - LightBox, } from '@backstage/plugin-techdocs-module-addons-contrib'; import { SettingsLayout, @@ -107,6 +107,7 @@ import { PuppetDbPage } from '@backstage/plugin-puppetdb'; import { DevToolsPage } from '@backstage/plugin-devtools'; import { customDevToolsPage } from './components/devtools/CustomDevToolsPage'; import { CatalogUnprocessedEntitiesPage } from '@backstage/plugin-catalog-unprocessed-entities'; +import { NotificationsPage } from '@backstage/plugin-notifications'; const app = createApp({ apis, @@ -272,6 +273,7 @@ const routes = ( }> {customDevToolsPage} + } /> ); diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 5f11218bba..6294aa7856 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -34,6 +34,7 @@ import { import { SidebarSearchModal } from '@backstage/plugin-search'; import { Shortcuts } from '@backstage/plugin-shortcuts'; import { + Link, Sidebar, sidebarConfig, SidebarDivider, @@ -42,16 +43,16 @@ import { SidebarPage, SidebarScrollWrapper, SidebarSpace, - Link, - useSidebarOpenState, SidebarSubmenu, SidebarSubmenuItem, + useSidebarOpenState, } from '@backstage/core-components'; import { MyGroupsSidebarItem } from '@backstage/plugin-org'; import { SearchModal } from '../search/SearchModal'; import Score from '@material-ui/icons/Score'; import { useApp } from '@backstage/core-plugin-api'; import BuildIcon from '@material-ui/icons/Build'; +import { NotificationsSidebarItem } from '@backstage/plugin-notifications'; const useSidebarLogoStyles = makeStyles({ root: { @@ -166,6 +167,8 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( + + diff --git a/packages/backend/package.json b/packages/backend/package.json index 2386d799a9..1606158a7e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -55,6 +55,8 @@ "@backstage/plugin-lighthouse-backend": "workspace:^", "@backstage/plugin-linguist-backend": "workspace:^", "@backstage/plugin-nomad-backend": "workspace:^", + "@backstage/plugin-notifications-backend": "^0.0.0", + "@backstage/plugin-notifications-node": "workspace:^", "@backstage/plugin-permission-backend": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-permission-node": "workspace:^", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index bade028597..fd29c4a8c7 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -66,6 +66,7 @@ import linguist from './plugins/linguist'; import devTools from './plugins/devtools'; import nomad from './plugins/nomad'; import signals from './plugins/signals'; +import notifications from './plugins/notifications'; import { PluginEnvironment } from './types'; import { ServerPermissionClient } from '@backstage/plugin-permission-node'; import { DefaultIdentityClient } from '@backstage/plugin-auth-node'; @@ -74,6 +75,7 @@ import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; import { MeterProvider } from '@opentelemetry/sdk-metrics'; import { metrics } from '@opentelemetry/api'; import { DefaultSignalService } from '@backstage/plugin-signals-node'; +import { NotificationService } from '@backstage/plugin-notifications-node'; // Expose opentelemetry metrics using a Prometheus exporter on // http://localhost:9464/metrics . See prometheus.yml in packages/backend for @@ -103,6 +105,10 @@ function makeCreateEnv(config: Config) { const signalService = DefaultSignalService.create({ eventBroker, }); + const notificationService = NotificationService.create({ + database: databaseManager.forPlugin('notifications'), + discovery, + }); root.info(`Created UrlReader ${reader}`); @@ -125,6 +131,7 @@ function makeCreateEnv(config: Config) { scheduler, identity, signalService, + notificationService, }; }; } @@ -179,6 +186,9 @@ async function main() { const devToolsEnv = useHotMemoize(module, () => createEnv('devtools')); const nomadEnv = useHotMemoize(module, () => createEnv('nomad')); const signalsEnv = useHotMemoize(module, () => createEnv('signals')); + const notificationsEnv = useHotMemoize(module, () => + createEnv('notifications'), + ); const apiRouter = Router(); apiRouter.use('/catalog', await catalog(catalogEnv)); @@ -206,6 +216,7 @@ async function main() { apiRouter.use('/devtools', await devTools(devToolsEnv)); apiRouter.use('/nomad', await nomad(nomadEnv)); apiRouter.use('/signals', await signals(signalsEnv)); + apiRouter.use('/notifications', await notifications(notificationsEnv)); apiRouter.use(notFoundHandler()); await lighthouse(lighthouseEnv); diff --git a/packages/backend/src/plugins/notifications.ts b/packages/backend/src/plugins/notifications.ts new file mode 100644 index 0000000000..91c01fb860 --- /dev/null +++ b/packages/backend/src/plugins/notifications.ts @@ -0,0 +1,39 @@ +/* + * 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 { createRouter } from '@backstage/plugin-notifications-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + // TODO: Remove this test code + // setInterval(() => { + // env.notificationService.send( + // 'user:default/guest', + // 'Test', + // 'This is test notification', + // '/catalog', + // ); + // }, 60000); + + return await createRouter({ + logger: env.logger, + identity: env.identity, + notificationService: env.notificationService, + }); +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 3dad2f739b..6a48804d1c 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -27,7 +27,8 @@ import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { IdentityApi } from '@backstage/plugin-auth-node'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { EventBroker } from '@backstage/plugin-events-node'; -import { DefaultSignalService } from '@backstage/plugin-signals-node'; +import { SignalService } from '@backstage/plugin-signals-node'; +import { NotificationService } from '@backstage/plugin-notifications-node'; export type PluginEnvironment = { logger: Logger; @@ -41,5 +42,6 @@ export type PluginEnvironment = { scheduler: PluginTaskScheduler; identity: IdentityApi; eventBroker: EventBroker; - signalService: DefaultSignalService; + signalService: SignalService; + notificationService: NotificationService; }; diff --git a/plugins/notifications-backend/.eslintrc.js b/plugins/notifications-backend/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/notifications-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/notifications-backend/README.md b/plugins/notifications-backend/README.md new file mode 100644 index 0000000000..e90d287049 --- /dev/null +++ b/plugins/notifications-backend/README.md @@ -0,0 +1,14 @@ +# notifications + +Welcome to the notifications backend plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn +start` in the root directory, and then navigating to [/notifications](http://localhost:3000/notifications). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. diff --git a/plugins/notifications-backend/api-report.md b/plugins/notifications-backend/api-report.md new file mode 100644 index 0000000000..e571fd521a --- /dev/null +++ b/plugins/notifications-backend/api-report.md @@ -0,0 +1,25 @@ +## API Report File for "@backstage/plugin-notifications-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import express from 'express'; +import { IdentityApi } from '@backstage/plugin-auth-node'; +import { Logger } from 'winston'; +import { NotificationService } from '@backstage/plugin-notifications-node'; + +// @public (undocumented) +export function createRouter(options: RouterOptions): Promise; + +// @public (undocumented) +export interface RouterOptions { + // (undocumented) + identity: IdentityApi; + // (undocumented) + logger: Logger; + // (undocumented) + notificationService: NotificationService; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json new file mode 100644 index 0000000000..ce67513669 --- /dev/null +++ b/plugins/notifications-backend/package.json @@ -0,0 +1,46 @@ +{ + "name": "@backstage/plugin-notifications-backend", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-notifications-node": "workspace:^", + "@types/express": "*", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "knex": "^3.0.0", + "node-fetch": "^2.6.7", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@types/supertest": "^2.0.8", + "msw": "^1.0.0", + "supertest": "^6.2.4" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/notifications-backend/src/index.ts b/plugins/notifications-backend/src/index.ts new file mode 100644 index 0000000000..d2e8d61bad --- /dev/null +++ b/plugins/notifications-backend/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './service/router'; diff --git a/plugins/notifications-backend/src/run.ts b/plugins/notifications-backend/src/run.ts new file mode 100644 index 0000000000..d299ed23e9 --- /dev/null +++ b/plugins/notifications-backend/src/run.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/notifications-backend/src/service/router.test.ts b/plugins/notifications-backend/src/service/router.test.ts new file mode 100644 index 0000000000..c0f09b3ef9 --- /dev/null +++ b/plugins/notifications-backend/src/service/router.test.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { getVoidLogger } from '@backstage/backend-common'; +import express from 'express'; +import request from 'supertest'; + +import { createRouter } from './router'; +import { IdentityApi } from '@backstage/plugin-auth-node'; +import { NotificationService } from '@backstage/plugin-notifications-node'; + +describe('createRouter', () => { + let app: express.Express; + + const identityMock: IdentityApi = { + async getIdentity() { + return { + identity: { + type: 'user', + ownershipEntityRefs: [], + userEntityRef: 'user:default/guest', + }, + token: 'no-token', + }; + }, + }; + + const notificationServiceMock: jest.Mocked> = { + getStore: jest.fn(), + send: jest.fn(), + }; + + beforeAll(async () => { + const router = await createRouter({ + logger: getVoidLogger(), + identity: identityMock, + notificationService: notificationServiceMock as NotificationService, + }); + app = express().use(router); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('GET /health', () => { + it('returns ok', async () => { + const response = await request(app).get('/health'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); +}); diff --git a/plugins/notifications-backend/src/service/router.ts b/plugins/notifications-backend/src/service/router.ts new file mode 100644 index 0000000000..8d9a7472bb --- /dev/null +++ b/plugins/notifications-backend/src/service/router.ts @@ -0,0 +1,67 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { errorHandler } from '@backstage/backend-common'; +import express, { Request } from 'express'; +import Router from 'express-promise-router'; +import { Logger } from 'winston'; +import { NotificationService } from '@backstage/plugin-notifications-node'; +import { IdentityApi } from '@backstage/plugin-auth-node'; + +/** @public */ +export interface RouterOptions { + logger: Logger; + identity: IdentityApi; + notificationService: NotificationService; +} + +/** @public */ +export async function createRouter( + options: RouterOptions, +): Promise { + const { logger, notificationService, identity } = options; + + const store = await notificationService.getStore(); + + const getUser = async (req: Request) => { + const user = await identity.getIdentity({ request: req }); + return user ? user.identity.userEntityRef : 'user:default/guest'; + }; + + const router = Router(); + router.use(express.json()); + + router.get('/health', (_, response) => { + logger.info('PONG!'); + response.json({ status: 'ok' }); + }); + + router.get('/notifications', async (req, res) => { + const user = await getUser(req); + const notifications = await store.getNotifications({ user_ref: user }); + res.send(notifications); + }); + + router.get('/status', async (req, res) => { + const user = await getUser(req); + const status = await store.getStatus({ user_ref: user }); + res.send(status); + }); + + // TODO: Add endpoint to set read/unread by notification id(s) + + router.use(errorHandler()); + return router; +} diff --git a/plugins/notifications-backend/src/service/standaloneServer.ts b/plugins/notifications-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..c19ae01b74 --- /dev/null +++ b/plugins/notifications-backend/src/service/standaloneServer.ts @@ -0,0 +1,99 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + createServiceBuilder, + loadBackendConfig, + PluginDatabaseManager, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './router'; +import Knex from 'knex'; +import { IdentityApi } from '@backstage/plugin-auth-node'; +import { Request } from 'express'; +import { NotificationService } from '@backstage/plugin-notifications-node'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'notifications-backend' }); + logger.debug('Starting application server...'); + + const config = await loadBackendConfig({ logger, argv: process.argv }); + const db = Knex(config.get('backend.database')); + + const dbMock: PluginDatabaseManager = { + async getClient() { + return db; + }, + }; + + const discoveryMock: PluginEndpointDiscovery = { + async getBaseUrl(pluginId: string) { + return `http://localhost:7007/api/${pluginId}`; + }, + + async getExternalBaseUrl(pluginId: string) { + return `http://localhost:7007/api/${pluginId}`; + }, + }; + + const notificationService = NotificationService.create({ + database: dbMock, + discovery: discoveryMock, + }); + + const identityMock: IdentityApi = { + async getIdentity({ request }: { request: Request }) { + const token = request.headers.authorization?.split(' ')[1]; + return { + identity: { + type: 'user', + ownershipEntityRefs: [], + userEntityRef: 'user:default/guest', + }, + token: token || 'no-token', + }; + }, + }; + + const router = await createRouter({ + logger, + identity: identityMock, + notificationService, + }); + + let service = createServiceBuilder(module) + .setPort(options.port) + .addRouter('/notifications', router); + if (options.enableCors) { + service = service.enableCors({ origin: 'http://localhost:3000' }); + } + + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); diff --git a/plugins/notifications-backend/src/setupTests.ts b/plugins/notifications-backend/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/plugins/notifications-backend/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export {}; diff --git a/plugins/notifications-common/.eslintrc.js b/plugins/notifications-common/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/notifications-common/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/notifications-common/README.md b/plugins/notifications-common/README.md new file mode 100644 index 0000000000..673e30288f --- /dev/null +++ b/plugins/notifications-common/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-notifications-common + +Welcome to the common package for the notifications plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/notifications-common/api-report.md b/plugins/notifications-common/api-report.md new file mode 100644 index 0000000000..6d7d74b64b --- /dev/null +++ b/plugins/notifications-common/api-report.md @@ -0,0 +1,24 @@ +## API Report File for "@backstage/plugin-notifications-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +// @public (undocumented) +type Notification_2 = { + id: string; + userRef: string; + title: string; + description: string; + link: string; + icon?: string; + created: Date; + read?: Date; +}; +export { Notification_2 as Notification }; + +// @public (undocumented) +export type NotificationStatus = { + unread: number; + read: number; +}; +``` diff --git a/plugins/notifications-common/package.json b/plugins/notifications-common/package.json new file mode 100644 index 0000000000..91acf171f7 --- /dev/null +++ b/plugins/notifications-common/package.json @@ -0,0 +1,32 @@ +{ + "name": "@backstage/plugin-notifications-common", + "description": "Common functionalities for the notifications plugin", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "sideEffects": false, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/notifications-common/src/index.ts b/plugins/notifications-common/src/index.ts new file mode 100644 index 0000000000..8d9f26f07a --- /dev/null +++ b/plugins/notifications-common/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common functionalities for the notifications plugin. + * + * @packageDocumentation + */ + +export * from './types'; diff --git a/plugins/notifications-common/src/setupTests.ts b/plugins/notifications-common/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/plugins/notifications-common/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export {}; diff --git a/plugins/notifications-common/src/types.ts b/plugins/notifications-common/src/types.ts new file mode 100644 index 0000000000..c9df78e1ff --- /dev/null +++ b/plugins/notifications-common/src/types.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @public */ +export type Notification = { + id: string; + userRef: string; + title: string; + description: string; + link: string; + // TODO: Icon should be typed so that we know what to render + icon?: string; + created: Date; + read?: Date; +}; + +/** @public */ +export type NotificationStatus = { + unread: number; + read: number; +}; diff --git a/plugins/notifications-node/.eslintrc.js b/plugins/notifications-node/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/notifications-node/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/notifications-node/README.md b/plugins/notifications-node/README.md new file mode 100644 index 0000000000..4cf460c3fe --- /dev/null +++ b/plugins/notifications-node/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-notifications-node + +Welcome to the Node.js library package for the notifications plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/notifications-node/api-report.md b/plugins/notifications-node/api-report.md new file mode 100644 index 0000000000..68a53cd535 --- /dev/null +++ b/plugins/notifications-node/api-report.md @@ -0,0 +1,70 @@ +## API Report File for "@backstage/plugin-notifications-node" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Notification as Notification_2 } from '@backstage/plugin-notifications-common'; +import { NotificationStatus } from '@backstage/plugin-notifications-common'; +import { PluginDatabaseManager } from '@backstage/backend-common'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; + +// @public (undocumented) +export class DatabaseNotificationsStore implements NotificationsStore { + // (undocumented) + static create({ + database, + skipMigrations, + }: { + database: PluginDatabaseManager; + skipMigrations?: boolean; + }): Promise; + // (undocumented) + getNotifications(options: NotificationGetOptions): Promise; + // (undocumented) + getStatus(options: NotificationGetOptions): Promise<{ + unread: number; + read: number; + }>; + // (undocumented) + saveNotification(notification: Notification_2): Promise; +} + +// @public (undocumented) +export type NotificationGetOptions = { + user_ref: string; +}; + +// @public (undocumented) +export class NotificationService { + // (undocumented) + static create({ + database, + discovery, + }: NotificationServiceOptions): NotificationService; + // (undocumented) + getStore(): Promise; + // (undocumented) + send( + entityRef: string | string[], + title: string, + description: string, + link: string, + ): Promise; +} + +// @public (undocumented) +export type NotificationServiceOptions = { + database: PluginDatabaseManager; + discovery: PluginEndpointDiscovery; +}; + +// @public (undocumented) +export interface NotificationsStore { + // (undocumented) + getNotifications(options: NotificationGetOptions): Promise; + // (undocumented) + getStatus(options: NotificationGetOptions): Promise; + // (undocumented) + saveNotification(notification: Notification_2): Promise; +} +``` diff --git a/plugins/notifications-node/migrations/20231215_init.js b/plugins/notifications-node/migrations/20231215_init.js new file mode 100644 index 0000000000..a76a8c3cbd --- /dev/null +++ b/plugins/notifications-node/migrations/20231215_init.js @@ -0,0 +1,34 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +exports.up = async function up(knex) { + await knex.schema.createTable('notifications', table => { + table.uuid('id').primary(); + table.string('userRef').notNullable(); + table.string('title').notNullable(); + table.text('description').notNullable(); + table.text('link').notNullable(); + table.datetime('created').notNullable(); + table.datetime('read').nullable(); + }); +}; + +/** + * @param {import('knex').Knex} knex + */ +exports.down = async function down(knex) { + await knex.schema.dropTable('notifications'); +}; diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json new file mode 100644 index 0000000000..4a9e9a2319 --- /dev/null +++ b/plugins/notifications-node/package.json @@ -0,0 +1,38 @@ +{ + "name": "@backstage/plugin-notifications-node", + "description": "Node.js library for the notifications plugin", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "node-library" + }, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ], + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/plugin-notifications-common": "workspace:^", + "knex": "^3.0.0", + "uuid": "^8.0.0" + } +} diff --git a/plugins/notifications-node/src/database/DatabaseNotificationsStore.ts b/plugins/notifications-node/src/database/DatabaseNotificationsStore.ts new file mode 100644 index 0000000000..450450d998 --- /dev/null +++ b/plugins/notifications-node/src/database/DatabaseNotificationsStore.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + PluginDatabaseManager, + resolvePackagePath, +} from '@backstage/backend-common'; +import { + NotificationGetOptions, + NotificationsStore, +} from './NotificationsStore'; +import { Notification } from '@backstage/plugin-notifications-common'; +import { Knex } from 'knex'; + +const migrationsDir = resolvePackagePath( + '@backstage/plugin-notifications-node', + 'migrations', +); + +/** @public */ +export class DatabaseNotificationsStore implements NotificationsStore { + private constructor(private readonly db: Knex) {} + + static async create({ + database, + skipMigrations, + }: { + database: PluginDatabaseManager; + skipMigrations?: boolean; + }): Promise { + const client = await database.getClient(); + + if (!database.migrations?.skip && !skipMigrations) { + await client.migrate.latest({ + directory: migrationsDir, + }); + } + + return new DatabaseNotificationsStore(client); + } + + private mapToInteger = (val: string | number | undefined): number => { + return typeof val === 'string' ? Number.parseInt(val, 10) : val ?? 0; + }; + + async getNotifications(options: NotificationGetOptions) { + const { user_ref } = options; + const notificationQuery = this.db('notifications').where( + 'userRef', + user_ref, + ); + + const notifications = await notificationQuery.select('*'); + + return notifications; + } + + async saveNotification(notification: Notification) { + await this.db.insert(notification).into('notifications'); + } + + async getStatus(options: NotificationGetOptions) { + const { user_ref } = options; + const notificationQuery = this.db('notifications').where( + 'userRef', + user_ref, + ); + + const unreadQuery = await notificationQuery + .clone() + .whereNull('read') + .count('id as UNREAD') + .first(); + const readQuery = await notificationQuery + .clone() + .whereNotNull('read') + .count('id as READ') + .first(); + + return { + unread: this.mapToInteger((unreadQuery as any)?.UNREAD), + read: this.mapToInteger((readQuery as any)?.READ), + }; + } +} diff --git a/plugins/notifications-node/src/database/NotificationsStore.ts b/plugins/notifications-node/src/database/NotificationsStore.ts new file mode 100644 index 0000000000..349e2cd7fa --- /dev/null +++ b/plugins/notifications-node/src/database/NotificationsStore.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Notification, + NotificationStatus, +} from '@backstage/plugin-notifications-common'; + +/** @public */ +export type NotificationGetOptions = { + user_ref: string; +}; + +/** @public */ +export interface NotificationsStore { + getNotifications(options: NotificationGetOptions): Promise; + + saveNotification(notification: Notification): Promise; + + getStatus(options: NotificationGetOptions): Promise; + + // TODO: Mark as read/unread by notification id(s) +} diff --git a/plugins/notifications-node/src/database/index.ts b/plugins/notifications-node/src/database/index.ts new file mode 100644 index 0000000000..6d2f549f38 --- /dev/null +++ b/plugins/notifications-node/src/database/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './DatabaseNotificationsStore'; +export * from './NotificationsStore'; diff --git a/plugins/notifications-node/src/index.ts b/plugins/notifications-node/src/index.ts new file mode 100644 index 0000000000..c686e95d3c --- /dev/null +++ b/plugins/notifications-node/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Node.js library for the notifications plugin. + * + * @packageDocumentation + */ + +export * from './database'; +export * from './service'; diff --git a/plugins/notifications-node/src/service/NotificationService.ts b/plugins/notifications-node/src/service/NotificationService.ts new file mode 100644 index 0000000000..55dcff546f --- /dev/null +++ b/plugins/notifications-node/src/service/NotificationService.ts @@ -0,0 +1,136 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Notification } from '@backstage/plugin-notifications-common'; +import { CatalogApi, CatalogClient } from '@backstage/catalog-client'; +import { NotificationsStore } from '../database/NotificationsStore'; +import { v4 as uuid } from 'uuid'; +import { + Entity, + isGroupEntity, + isUserEntity, + RELATION_HAS_MEMBER, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { + PluginDatabaseManager, + PluginEndpointDiscovery, +} from '@backstage/backend-common'; +import { DatabaseNotificationsStore } from '../database'; + +/** @public */ +export type NotificationServiceOptions = { + database: PluginDatabaseManager; + discovery: PluginEndpointDiscovery; +}; + +/** @public */ +export class NotificationService { + private store: NotificationsStore | null = null; + + private constructor( + private readonly database: PluginDatabaseManager, + private readonly catalog: CatalogApi, + ) {} + + static create({ + database, + discovery, + }: NotificationServiceOptions): NotificationService { + const catalogClient = new CatalogClient({ + discoveryApi: discovery, + }); + + return new NotificationService(database, catalogClient); + } + + async send( + entityRef: string | string[], + title: string, + description: string, + link: string, + ): Promise { + const users = await this.getUsersForEntityRef(entityRef); + const notifications = []; + const store = await this.getStore(); + for (const user of users) { + const notification = { + id: uuid(), + userRef: user, + title, + description, + link, + created: new Date(), + }; + + await store.saveNotification(notification); + notifications.push(notification); + } + + // TODO: Signal service + // TODO: Other senders + + return notifications; + } + + async getStore(): Promise { + if (!this.store) { + this.store = await DatabaseNotificationsStore.create({ + database: this.database, + }); + } + return this.store; + } + + private async getUsersForEntityRef( + entityRef: string | string[], + ): Promise { + const refs = Array.isArray(entityRef) ? entityRef : [entityRef]; + const entities = await this.catalog.getEntitiesByRefs({ entityRefs: refs }); + + const mapEntity = async (entity: Entity | undefined): Promise => { + if (!entity) { + return []; + } + + if (isUserEntity(entity)) { + return [stringifyEntityRef(entity)]; + } else if (isGroupEntity(entity) && entity.relations) { + return entity.relations + .filter( + relation => + relation.type === RELATION_HAS_MEMBER && relation.targetRef, + ) + .map(r => r.targetRef); + } else if (!isGroupEntity(entity) && entity.spec?.owner) { + const owner = await this.catalog.getEntityByRef( + entity.spec.owner as string, + ); + if (owner) { + return mapEntity(owner); + } + } + + return []; + }; + + const users: string[] = []; + for (const entity of entities.items) { + const u = await mapEntity(entity); + users.push(...u); + } + return users; + } +} diff --git a/plugins/notifications-node/src/service/index.ts b/plugins/notifications-node/src/service/index.ts new file mode 100644 index 0000000000..450f4f7dcc --- /dev/null +++ b/plugins/notifications-node/src/service/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './NotificationService'; diff --git a/plugins/notifications-node/src/setupTests.ts b/plugins/notifications-node/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/plugins/notifications-node/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export {}; diff --git a/plugins/notifications/.eslintrc.js b/plugins/notifications/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/notifications/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/notifications/README.md b/plugins/notifications/README.md new file mode 100644 index 0000000000..3eeac7a647 --- /dev/null +++ b/plugins/notifications/README.md @@ -0,0 +1,13 @@ +# notifications + +Welcome to the notifications plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/notifications](http://localhost:3000/notifications). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. diff --git a/plugins/notifications/api-report.md b/plugins/notifications/api-report.md new file mode 100644 index 0000000000..00ff5f4d4c --- /dev/null +++ b/plugins/notifications/api-report.md @@ -0,0 +1,88 @@ +## API Report File for "@backstage/plugin-notifications" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { ApiRef } from '@backstage/core-plugin-api'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { FetchApi } from '@backstage/core-plugin-api'; +import { JSX as JSX_2 } from 'react'; +import { Notification as Notification_2 } from '@backstage/plugin-notifications-common'; +import { NotificationStatus } from '@backstage/plugin-notifications-common'; +import { default as React_2 } from 'react'; +import { RouteRef } from '@backstage/core-plugin-api'; + +// @public (undocumented) +export interface NotificationsApi { + // (undocumented) + getNotifications(): Promise; + // (undocumented) + getStatus(): Promise; +} + +// @public (undocumented) +export const notificationsApiRef: ApiRef; + +// @public (undocumented) +export class NotificationsClient implements NotificationsApi { + constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }); + // (undocumented) + getNotifications(): Promise; + // (undocumented) + getStatus(): Promise; +} + +// @public (undocumented) +export const NotificationsPage: () => JSX_2.Element; + +// @public (undocumented) +export const notificationsPlugin: BackstagePlugin< + { + root: RouteRef; + }, + {} +>; + +// @public (undocumented) +export const NotificationsSidebarItem: () => React_2.JSX.Element; + +// @public (undocumented) +export const NotificationsTable: (props: { + notifications?: Notification_2[]; +}) => React_2.JSX.Element; + +// @public (undocumented) +export function useNotificationsApi( + f: (api: NotificationsApi) => Promise, + deps?: any[], +): + | { + retry: () => void; + loading: boolean; + error?: undefined; + value?: undefined; + } + | { + retry: () => void; + loading: false; + error: Error; + value?: undefined; + } + | { + retry: () => void; + loading: true; + error?: Error | undefined; + value?: T | undefined; + } + | { + retry: () => void; + loading: false; + error?: undefined; + value: T; + }; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/notifications/dev/index.tsx b/plugins/notifications/dev/index.tsx new file mode 100644 index 0000000000..f4ea31f4ab --- /dev/null +++ b/plugins/notifications/dev/index.tsx @@ -0,0 +1,27 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { createDevApp } from '@backstage/dev-utils'; +import { notificationsPlugin } from '../src/plugin'; + +createDevApp() + .registerPlugin(notificationsPlugin) + .addPage({ + element: