From 0d4c4db867d890783bd10b57e8b64e5829e0f35f Mon Sep 17 00:00:00 2001 From: Zeshan Ziya Date: Wed, 27 Aug 2025 12:44:46 +0530 Subject: [PATCH 001/211] fix(home): correct clearAll logic to properly handle deletable flag Signed-off-by: Zeshan Ziya --- .../home/src/components/CustomHomepage/CustomHomepageGrid.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx index 5d85e834e8..807837acf6 100644 --- a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx +++ b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx @@ -275,7 +275,7 @@ export const CustomHomepageGrid = (props: CustomHomepageGridProps) => { }; const clearLayout = () => { - setWidgets(widgets.filter(w => !w.deletable)); + setWidgets(widgets.filter(w => w.deletable === false)); }; const changeEditMode = (mode: boolean) => { From 9a3f11889ab275c777d2a88507324e8417b96fb2 Mon Sep 17 00:00:00 2001 From: Zeshan Ziya Date: Wed, 27 Aug 2025 12:45:32 +0530 Subject: [PATCH 002/211] fix(home): add example to deletable flag on customizable home page Signed-off-by: Zeshan Ziya --- packages/app/src/components/home/CustomizableHomePage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app/src/components/home/CustomizableHomePage.tsx b/packages/app/src/components/home/CustomizableHomePage.tsx index b592490f2e..1d3cab646b 100644 --- a/packages/app/src/components/home/CustomizableHomePage.tsx +++ b/packages/app/src/components/home/CustomizableHomePage.tsx @@ -37,6 +37,7 @@ const defaultConfig = [ y: 0, width: 24, height: 2, + deletable: false, }, { component: 'HomePageRecentlyVisited', From e7d59d3c72589aaca620a80fbd60daf1b5235ecc Mon Sep 17 00:00:00 2001 From: Zeshan Ziya Date: Wed, 27 Aug 2025 12:46:34 +0530 Subject: [PATCH 003/211] chore: add changeset Signed-off-by: Zeshan Ziya --- .changeset/fuzzy-trams-kick.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fuzzy-trams-kick.md diff --git a/.changeset/fuzzy-trams-kick.md b/.changeset/fuzzy-trams-kick.md new file mode 100644 index 0000000000..19e12b1073 --- /dev/null +++ b/.changeset/fuzzy-trams-kick.md @@ -0,0 +1,6 @@ +--- +'example-app': patch +'@backstage/plugin-home': patch +--- + +fix(home): correct clearAll logic to properly handle deletable flag From 28d0c0f43c5b8393aa1a807acb00c804aa563e44 Mon Sep 17 00:00:00 2001 From: Zeshan Ziya Date: Wed, 27 Aug 2025 13:00:59 +0530 Subject: [PATCH 004/211] chore: remove example-app from changeset Signed-off-by: Zeshan Ziya --- .changeset/fuzzy-trams-kick.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/fuzzy-trams-kick.md b/.changeset/fuzzy-trams-kick.md index 19e12b1073..71c06891d0 100644 --- a/.changeset/fuzzy-trams-kick.md +++ b/.changeset/fuzzy-trams-kick.md @@ -1,5 +1,4 @@ --- -'example-app': patch '@backstage/plugin-home': patch --- From 431130cb833e18d81287e7d301537094e8f05593 Mon Sep 17 00:00:00 2001 From: TA31OU Date: Wed, 13 Aug 2025 09:37:50 +0200 Subject: [PATCH 005/211] Add changeset Signed-off-by: TA31OU (cherry picked from commit ce965429e38c3817432085b97a63447289586af3) --- .changeset/flat-peas-run.md | 6 ++++ .../DependencyGraph/DependencyGraph.tsx | 8 +++++ .../src/components/DependencyGraph/types.ts | 34 +++++++++++++++++++ plugins/catalog-graph/package.json | 2 ++ .../DefaultRenderEdge.tsx | 15 ++++++++ .../EntityRelationsGraph.tsx | 3 ++ .../EntityRelationsGraph/useEntityStore.ts | 2 +- yarn.lock | 2 ++ 8 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 .changeset/flat-peas-run.md create mode 100644 plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx diff --git a/.changeset/flat-peas-run.md b/.changeset/flat-peas-run.md new file mode 100644 index 0000000000..116e8eb449 --- /dev/null +++ b/.changeset/flat-peas-run.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': minor +'@backstage/plugin-catalog-graph': minor +--- + +Added `renderEdge` prop to component in @backstage/core-components to allow custom rendering of graph edges. Fixed local development for EntityRelationsGraph plugin. diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index d690253b06..f4b3d6bd58 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -138,6 +138,11 @@ export interface DependencyGraphProps * Weight applied to edges in graph */ edgeWeight?: number; + /** + * Custom edge rendering component + */ + renderEdge?: Types.RenderEdgeFunction; + /** * Custom node rendering component */ @@ -210,6 +215,7 @@ export function DependencyGraph( labelOffset = 10, edgeRanks = 1, edgeWeight = 1, + renderEdge, renderLabel, defs, zoom = 'enabled', @@ -442,6 +448,8 @@ export function DependencyGraph( {graphEdges.map(e => { const edge = graph.current.edge(e) as GraphEdge; if (!edge) return null; + if (renderEdge) return renderEdge({ edge, id: e }); + return ( , ) => ReactNode; + /** + * Properties of {@link DependencyGraphTypes.RenderEdgeFunction} for {@link DependencyGraphTypes.DependencyEdge} + * + * @public + */ + export type RenderEdgeProps = { + edge: DependencyEdge; + id: dagre.Edge; + }; + + /** + * Custom React component for graph {@link DependencyGraphTypes.DependencyEdge} + * + * @public + */ + export type RenderEdgeFunction = (props: { + edge: T & { + points: { x: number; y: number }[]; + label?: string; + labeloffset?: number; + labelpos?: string; + width?: number; + height?: number; + weight?: number; + minlen?: number; + showArrowHeads?: boolean; + from?: string; + to?: string; + relations?: string[]; + }; + id: { v: string; w: string }; + }) => ReactNode; + /** * Graph direction * diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index dd3271f468..7cea775bbe 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -61,6 +61,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "classnames": "^2.3.1", + "d3-shape": "^3.0.0", "lodash": "^4.17.15", "p-limit": "^3.1.0", "qs": "^6.9.4", @@ -76,6 +77,7 @@ "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^16.0.0", "@testing-library/user-event": "^14.0.0", + "@types/d3-shape": "^3.0.1", "@types/react": "^18.0.0", "react": "^18.0.2", "react-dom": "^18.0.2", diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx new file mode 100644 index 0000000000..379ce77f52 --- /dev/null +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx @@ -0,0 +1,15 @@ +/* + * Copyright 2025 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. + */ diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx index 933aa230be..f08cd7a760 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx @@ -89,6 +89,7 @@ export type EntityRelationsGraphProps = { zoom?: 'enabled' | 'disabled' | 'enable-on-click'; renderNode?: DependencyGraphTypes.RenderNodeFunction; renderLabel?: DependencyGraphTypes.RenderLabelFunction; + renderEdge?: DependencyGraphTypes.RenderEdgeFunction; curve?: 'curveStepBefore' | 'curveMonotoneX'; showArrowHeads?: boolean; }; @@ -114,6 +115,7 @@ export const EntityRelationsGraph = (props: EntityRelationsGraphProps) => { zoom = 'enabled', renderNode, renderLabel, + renderEdge, curve, showArrowHeads, } = props; @@ -156,6 +158,7 @@ export const EntityRelationsGraph = (props: EntityRelationsGraphProps) => { edges={edges} renderNode={renderNode || DefaultRenderNode} renderLabel={renderLabel || DefaultRenderLabel} + renderEdge={renderEdge} direction={direction} className={classes.graph} paddingX={theme.spacing(4)} diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts index c55becdd49..be6ea81f57 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts @@ -64,7 +64,7 @@ export function useEntityStore(): { return; } - const { items } = await catalogClient.getEntitiesByRefs({ entityRefs }); + const { items } = await catalogClient.getEntities(); items.forEach(ent => { if (ent) { const entityRef = stringifyEntityRef(ent); diff --git a/yarn.lock b/yarn.lock index 853749b99e..fde37ae4e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4771,8 +4771,10 @@ __metadata: "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" "@testing-library/user-event": "npm:^14.0.0" + "@types/d3-shape": "npm:^3.0.1" "@types/react": "npm:^18.0.0" classnames: "npm:^2.3.1" + d3-shape: "npm:^3.0.0" lodash: "npm:^4.17.15" p-limit: "npm:^3.1.0" qs: "npm:^6.9.4" From cd961c25d181fb907c1481e9af6dce6fed375552 Mon Sep 17 00:00:00 2001 From: TA31OU Date: Wed, 3 Sep 2025 12:17:06 +0200 Subject: [PATCH 006/211] Revert useEntityStore change Signed-off-by: TA31OU --- .changeset/flat-peas-run.md | 2 +- .../src/components/EntityRelationsGraph/useEntityStore.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/flat-peas-run.md b/.changeset/flat-peas-run.md index 116e8eb449..c09eadecf8 100644 --- a/.changeset/flat-peas-run.md +++ b/.changeset/flat-peas-run.md @@ -3,4 +3,4 @@ '@backstage/plugin-catalog-graph': minor --- -Added `renderEdge` prop to component in @backstage/core-components to allow custom rendering of graph edges. Fixed local development for EntityRelationsGraph plugin. +Added `renderEdge` prop to component in @backstage/core-components to allow custom rendering of graph edges. diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts index be6ea81f57..c55becdd49 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/useEntityStore.ts @@ -64,7 +64,7 @@ export function useEntityStore(): { return; } - const { items } = await catalogClient.getEntities(); + const { items } = await catalogClient.getEntitiesByRefs({ entityRefs }); items.forEach(ent => { if (ent) { const entityRef = stringifyEntityRef(ent); From 05b7ac0c560c6daa2f2bb0cbc36bdc4db3deeeab Mon Sep 17 00:00:00 2001 From: TA31OU Date: Wed, 3 Sep 2025 13:25:07 +0200 Subject: [PATCH 007/211] Update catalog-graph report-api Signed-off-by: TA31OU --- plugins/catalog-graph/report.api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog-graph/report.api.md b/plugins/catalog-graph/report.api.md index 25479bd9b7..7f001531b8 100644 --- a/plugins/catalog-graph/report.api.md +++ b/plugins/catalog-graph/report.api.md @@ -129,6 +129,7 @@ export type EntityRelationsGraphProps = { zoom?: 'enabled' | 'disabled' | 'enable-on-click'; renderNode?: DependencyGraphTypes.RenderNodeFunction; renderLabel?: DependencyGraphTypes.RenderLabelFunction; + renderEdge?: DependencyGraphTypes.RenderEdgeFunction; curve?: 'curveStepBefore' | 'curveMonotoneX'; showArrowHeads?: boolean; }; From 3c994731243c37dd37ceaa709760e66dfd2ebd1c Mon Sep 17 00:00:00 2001 From: TA31OU Date: Wed, 3 Sep 2025 13:46:28 +0200 Subject: [PATCH 008/211] Update core-components report-api Signed-off-by: TA31OU --- packages/core-components/report-alpha.api.md | 2 +- packages/core-components/report.api.md | 33 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/core-components/report-alpha.api.md b/packages/core-components/report-alpha.api.md index 32bd2f4939..e258777b9c 100644 --- a/packages/core-components/report-alpha.api.md +++ b/packages/core-components/report-alpha.api.md @@ -24,10 +24,10 @@ export const coreComponentsTranslationRef: TranslationRef< readonly 'signIn.title': 'Sign In'; readonly 'signIn.loginFailed': 'Login failed'; readonly 'signIn.customProvider.title': 'Custom User'; + readonly 'signIn.customProvider.continue': 'Continue'; readonly 'signIn.customProvider.subtitle': 'Enter your own User ID and credentials.\n This selection will not be stored.'; readonly 'signIn.customProvider.userId': 'User ID'; readonly 'signIn.customProvider.tokenInvalid': 'Token is not a valid OpenID Connect JWT Token'; - readonly 'signIn.customProvider.continue': 'Continue'; readonly 'signIn.customProvider.idToken': 'ID Token (optional)'; readonly 'signIn.guestProvider.title': 'Guest'; readonly 'signIn.guestProvider.enter': 'Enter'; diff --git a/packages/core-components/report.api.md b/packages/core-components/report.api.md index fff1d0365a..4998accf01 100644 --- a/packages/core-components/report.api.md +++ b/packages/core-components/report.api.md @@ -18,6 +18,7 @@ import { ComponentType } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; +import dagre from '@dagrejs/dagre'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import IconButton from '@material-ui/core/IconButton'; @@ -273,6 +274,7 @@ export interface DependencyGraphProps paddingY?: number; ranker?: DependencyGraphTypes.Ranker; rankMargin?: number; + renderEdge?: DependencyGraphTypes.RenderEdgeFunction; renderLabel?: DependencyGraphTypes.RenderLabelFunction; renderNode?: DependencyGraphTypes.RenderNodeFunction; showArrowHeads?: boolean; @@ -314,6 +316,33 @@ export namespace DependencyGraphTypes { NETWORK_SIMPLEX = 'network-simplex', TIGHT_TREE = 'tight-tree', } + export type RenderEdgeFunction = (props: { + edge: T & { + points: { + x: number; + y: number; + }[]; + label?: string; + labeloffset?: number; + labelpos?: string; + width?: number; + height?: number; + weight?: number; + minlen?: number; + showArrowHeads?: boolean; + from?: string; + to?: string; + relations?: string[]; + }; + id: { + v: string; + w: string; + }; + }) => ReactNode; + export type RenderEdgeProps = { + edge: DependencyEdge; + id: dagre.Edge; + }; export type RenderLabelFunction = ( props: RenderLabelProps, ) => ReactNode; @@ -1548,8 +1577,8 @@ export type WarningPanelClassKey = // Warnings were encountered during analysis: // -// src/components/DependencyGraph/types.d.ts:22:9 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" -// src/components/DependencyGraph/types.d.ts:26:9 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" +// src/components/DependencyGraph/types.d.ts:23:9 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" +// src/components/DependencyGraph/types.d.ts:27:9 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" // src/components/TabbedLayout/RoutedTabs.d.ts:8:5 - (ae-forgotten-export) The symbol "SubRoute_2" needs to be exported by the entry point index.d.ts // src/components/Table/Table.d.ts:20:5 - (ae-forgotten-export) The symbol "SelectedFilters" needs to be exported by the entry point index.d.ts // src/layout/ErrorBoundary/ErrorBoundary.d.ts:8:5 - (ae-forgotten-export) The symbol "SlackChannel" needs to be exported by the entry point index.d.ts From c0990e2586588b8167f1b95cf9b1ad29c7705ac3 Mon Sep 17 00:00:00 2001 From: TA31OU Date: Wed, 3 Sep 2025 14:00:53 +0200 Subject: [PATCH 009/211] Update core-components report-api (again) Signed-off-by: TA31OU --- packages/core-components/report-alpha.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/report-alpha.api.md b/packages/core-components/report-alpha.api.md index e258777b9c..32bd2f4939 100644 --- a/packages/core-components/report-alpha.api.md +++ b/packages/core-components/report-alpha.api.md @@ -24,10 +24,10 @@ export const coreComponentsTranslationRef: TranslationRef< readonly 'signIn.title': 'Sign In'; readonly 'signIn.loginFailed': 'Login failed'; readonly 'signIn.customProvider.title': 'Custom User'; - readonly 'signIn.customProvider.continue': 'Continue'; readonly 'signIn.customProvider.subtitle': 'Enter your own User ID and credentials.\n This selection will not be stored.'; readonly 'signIn.customProvider.userId': 'User ID'; readonly 'signIn.customProvider.tokenInvalid': 'Token is not a valid OpenID Connect JWT Token'; + readonly 'signIn.customProvider.continue': 'Continue'; readonly 'signIn.customProvider.idToken': 'ID Token (optional)'; readonly 'signIn.guestProvider.title': 'Guest'; readonly 'signIn.guestProvider.enter': 'Enter'; From 01bf98c778cb262799a03150793542ed88071713 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Date: Thu, 4 Sep 2025 14:40:48 +0530 Subject: [PATCH 010/211] Fixed duplicate route warning for TechDocs Addons documentation in Docusaurus Signed-off-by: Aditya Kumar --- docs/features/techdocs/addons--new.md | 2 +- docs/features/techdocs/addons.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/features/techdocs/addons--new.md b/docs/features/techdocs/addons--new.md index 07138d4b8e..a552ad1ace 100644 --- a/docs/features/techdocs/addons--new.md +++ b/docs/features/techdocs/addons--new.md @@ -5,7 +5,7 @@ description: How to find, use, or create TechDocs Addons. --- :::info -This documentation is written for [the new frontend system](../../frontend-system/index.md) which is still in alpha and is only supported by a small number of plugins. +This documentation is written for [the new frontend system](../../frontend-system/index.md) which is still in alpha and is only supported by a small number of plugins. If you are on the [old frontend system](./getting-started.md#adding-techdocs-frontend-plugin) you may want to read [its own article](./addons.md) instead. ::: ## Concepts diff --git a/docs/features/techdocs/addons.md b/docs/features/techdocs/addons.md index 435239794b..f67f1c5660 100644 --- a/docs/features/techdocs/addons.md +++ b/docs/features/techdocs/addons.md @@ -1,5 +1,5 @@ --- -id: addons +id: addons--old title: TechDocs Addons description: How to find, use, or create TechDocs Addons. --- From e0ffe84d55fbf0ee29d8aac0510f90b49f919a8b Mon Sep 17 00:00:00 2001 From: Gaurav Agrawal Date: Sun, 7 Sep 2025 18:05:57 +0000 Subject: [PATCH 011/211] fix(scaffolder): add missing templatingExtensions flag to RouterProps contextMenu Signed-off-by: Gaurav Agrawal --- .changeset/ready-pots-arrive.md | 5 +++ .../components/ActionsPage/ActionsPage.tsx | 6 +++- .../ListTasksPage/ListTasksPage.tsx | 6 +++- .../src/components/Router/Router.tsx | 4 ++- .../TemplatingExtensionsPage.tsx | 34 ++++++++++++++++--- 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 .changeset/ready-pots-arrive.md diff --git a/.changeset/ready-pots-arrive.md b/.changeset/ready-pots-arrive.md new file mode 100644 index 0000000000..96f13a12ae --- /dev/null +++ b/.changeset/ready-pots-arrive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Add missing `templatingExtensions` option to RouterProps.contextMenu to allow global control across scaffolder pages diff --git a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx index 402c0427dc..6be502659a 100644 --- a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx +++ b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.tsx @@ -240,6 +240,7 @@ export type ActionsPageProps = { editor?: boolean; tasks?: boolean; create?: boolean; + templatingExtensions?: boolean; }; }; @@ -265,7 +266,10 @@ export const ActionsPage = (props: ActionsPageProps) => { props?.contextMenu?.create !== false ? () => navigate(createLink()) : undefined, - onTemplatingExtensionsClicked: () => navigate(templatingExtensionsLink()), + onTemplatingExtensionsClicked: + props?.contextMenu?.templatingExtensions !== false + ? () => navigate(templatingExtensionsLink()) + : undefined, }; return ( diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx index d6b5b7116e..6cce6c3727 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -55,6 +55,7 @@ export interface MyTaskPageProps { editor?: boolean; actions?: boolean; create?: boolean; + templatingExtensions?: boolean; }; } @@ -193,7 +194,10 @@ export const ListTasksPage = (props: MyTaskPageProps) => { props?.contextMenu?.create !== false ? () => navigate(createLink()) : undefined, - onTemplatingExtensionsClicked: () => navigate(templatingExtensionsLink()), + onTemplatingExtensionsClicked: + props?.contextMenu?.templatingExtensions !== false + ? () => navigate(templatingExtensionsLink()) + : undefined, }; return ( diff --git a/plugins/scaffolder/src/components/Router/Router.tsx b/plugins/scaffolder/src/components/Router/Router.tsx index a57811a520..7a2496c046 100644 --- a/plugins/scaffolder/src/components/Router/Router.tsx +++ b/plugins/scaffolder/src/components/Router/Router.tsx @@ -103,6 +103,8 @@ export type RouterProps = { tasks?: boolean; /** Whether to show a link to the create page (on /create subroutes) */ create?: boolean; + /** Whether to show a link to the templating extensions page */ + templatingExtensions?: boolean; }; }; @@ -244,7 +246,7 @@ export const InternalRouter = ( /> } + element={} /> } /> diff --git a/plugins/scaffolder/src/components/TemplatingExtensionsPage/TemplatingExtensionsPage.tsx b/plugins/scaffolder/src/components/TemplatingExtensionsPage/TemplatingExtensionsPage.tsx index 720d6204ea..fc2b180596 100644 --- a/plugins/scaffolder/src/components/TemplatingExtensionsPage/TemplatingExtensionsPage.tsx +++ b/plugins/scaffolder/src/components/TemplatingExtensionsPage/TemplatingExtensionsPage.tsx @@ -296,7 +296,18 @@ export const TemplatingExtensionsPageContent = ({ ); }; -export const TemplatingExtensionsPage = () => { +export type TemplatingExtensionsPageProps = { + contextMenu?: { + editor?: boolean; + actions?: boolean; + tasks?: boolean; + create?: boolean; + }; +}; + +export const TemplatingExtensionsPage = ( + props: TemplatingExtensionsPageProps, +) => { const navigate = useNavigate(); const editorLink = useRouteRef(editRouteRef); const tasksLink = useRouteRef(scaffolderListTaskRouteRef); @@ -304,10 +315,23 @@ export const TemplatingExtensionsPage = () => { const actionsLink = useRouteRef(actionsRouteRef); const scaffolderPageContextMenuProps: ScaffolderPageContextMenuProps = { - onEditorClicked: () => navigate(editorLink()), - onActionsClicked: () => navigate(actionsLink()), - onTasksClicked: () => navigate(tasksLink()), - onCreateClicked: () => navigate(createLink()), + onEditorClicked: + props?.contextMenu?.editor !== false + ? () => navigate(editorLink()) + : undefined, + onActionsClicked: + props?.contextMenu?.actions !== false + ? () => navigate(actionsLink()) + : undefined, + onTasksClicked: + props?.contextMenu?.tasks !== false + ? () => navigate(tasksLink()) + : undefined, + onCreateClicked: + props?.contextMenu?.create !== false + ? () => navigate(createLink()) + : undefined, + onTemplatingExtensionsClicked: undefined, }; const { t } = useTranslationRef(scaffolderTranslationRef); From 443c0e4348cc6328cdb82c4d4f6f5135e51cffa5 Mon Sep 17 00:00:00 2001 From: Gaurav Agrawal Date: Sun, 7 Sep 2025 19:51:00 +0000 Subject: [PATCH 012/211] fix: add api report Signed-off-by: Gaurav Agrawal --- plugins/scaffolder/report.api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/scaffolder/report.api.md b/plugins/scaffolder/report.api.md index fbb73f6225..a4a339f01e 100644 --- a/plugins/scaffolder/report.api.md +++ b/plugins/scaffolder/report.api.md @@ -492,6 +492,7 @@ export type RouterProps = { actions?: boolean; tasks?: boolean; create?: boolean; + templatingExtensions?: boolean; }; }; From 8b7351f3dff72cdab67157d7626b116d11e7f533 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 9 Sep 2025 14:48:48 +0200 Subject: [PATCH 013/211] feat(plugins/org): add overwrites to membership and ownership cards Signed-off-by: secustor --- .changeset/giant-weeks-jump.md | 5 +++ packages/app-next/app-config.yaml | 5 +++ plugins/org/report-alpha.api.md | 40 +++++++++++++++++--- plugins/org/src/alpha.tsx | 61 ++++++++++++++++++++++++------- 4 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 .changeset/giant-weeks-jump.md diff --git a/.changeset/giant-weeks-jump.md b/.changeset/giant-weeks-jump.md new file mode 100644 index 0000000000..1b11bd1490 --- /dev/null +++ b/.changeset/giant-weeks-jump.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-org': patch +--- + +Add `initialRelationAggregation` and `showAggregateMembersToggle` options to `EntityMembersListCard` as well to `EntityOwnershipCard` diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index f83e4d4ba0..59fecf7e64 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -22,6 +22,11 @@ app: ownerEntityRefs: [cubic-belugas] extensions: + - entity-card:org/members-list: + config: + showAggregateMembersToggle: true + initialRelationAggregation: aggregated + # - apis.plugin.graphiql.browse.gitlab: true # - graphiql-endpoint:graphiql/gitlab: true diff --git a/plugins/org/report-alpha.api.md b/plugins/org/report-alpha.api.md index 3cebe00d33..3f8fdca05d 100644 --- a/plugins/org/report-alpha.api.md +++ b/plugins/org/report-alpha.api.md @@ -63,13 +63,17 @@ const _default: OverridableFrontendPlugin< }; }>; 'entity-card:org/members-list': ExtensionDefinition<{ - kind: 'entity-card'; - name: 'members-list'; config: { + initialRelationAggregation: 'direct' | 'aggregated' | undefined; + showAggregateMembersToggle: boolean | undefined; + } & { filter: EntityPredicate | undefined; type: 'content' | 'summary' | 'info' | undefined; }; configInput: { + showAggregateMembersToggle?: boolean | undefined; + initialRelationAggregation?: 'direct' | 'aggregated' | undefined; + } & { filter?: EntityPredicate | undefined; type?: 'content' | 'summary' | 'info' | undefined; }; @@ -96,7 +100,17 @@ const _default: OverridableFrontendPlugin< optional: true; } >; - inputs: {}; + inputs: { + [x: string]: ExtensionInput< + ExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }; + kind: 'entity-card'; + name: 'members-list'; params: { loader: () => Promise; filter?: string | EntityPredicate | ((entity: Entity) => boolean); @@ -104,13 +118,17 @@ const _default: OverridableFrontendPlugin< }; }>; 'entity-card:org/ownership': ExtensionDefinition<{ - kind: 'entity-card'; - name: 'ownership'; config: { + initialRelationAggregation: 'direct' | 'aggregated' | undefined; + showAggregateMembersToggle: boolean | undefined; + } & { filter: EntityPredicate | undefined; type: 'content' | 'summary' | 'info' | undefined; }; configInput: { + showAggregateMembersToggle?: boolean | undefined; + initialRelationAggregation?: 'direct' | 'aggregated' | undefined; + } & { filter?: EntityPredicate | undefined; type?: 'content' | 'summary' | 'info' | undefined; }; @@ -137,7 +155,17 @@ const _default: OverridableFrontendPlugin< optional: true; } >; - inputs: {}; + inputs: { + [x: string]: ExtensionInput< + ExtensionDataRef, + { + optional: boolean; + singleton: boolean; + } + >; + }; + kind: 'entity-card'; + name: 'ownership'; params: { loader: () => Promise; filter?: string | EntityPredicate | ((entity: Entity) => boolean); diff --git a/plugins/org/src/alpha.tsx b/plugins/org/src/alpha.tsx index 92ae7d8801..ddb8094688 100644 --- a/plugins/org/src/alpha.tsx +++ b/plugins/org/src/alpha.tsx @@ -36,26 +36,59 @@ const EntityGroupProfileCard = EntityCardBlueprint.make({ }); /** @alpha */ -const EntityMembersListCard = EntityCardBlueprint.make({ +const EntityMembersListCard = EntityCardBlueprint.makeWithOverrides({ name: 'members-list', - params: { - filter: { kind: 'group' }, - loader: async () => - import('./components/Cards/Group/MembersList/MembersListCard').then(m => - compatWrapper(), - ), + config: { + schema: { + initialRelationAggregation: z => + z.enum(['direct', 'aggregated']).optional(), + showAggregateMembersToggle: z => z.boolean().optional(), + }, + }, + factory(originalFactory, { config }) { + return originalFactory({ + filter: { kind: 'group' }, + loader: async () => + import('./components/Cards/Group/MembersList/MembersListCard').then(m => + compatWrapper( + , + ), + ), + }); }, }); /** @alpha */ -const EntityOwnershipCard = EntityCardBlueprint.make({ +const EntityOwnershipCard = EntityCardBlueprint.makeWithOverrides({ name: 'ownership', - params: { - filter: { kind: { $in: ['group', 'user'] } }, - loader: async () => - import('./components/Cards/OwnershipCard/OwnershipCard').then(m => - compatWrapper(), - ), + config: { + schema: { + initialRelationAggregation: z => + z.enum(['direct', 'aggregated']).optional(), + showAggregateMembersToggle: z => z.boolean().optional(), + }, + }, + factory(originalFactory, { config }) { + return originalFactory({ + filter: { kind: { $in: ['group', 'user'] } }, + loader: async () => + import('./components/Cards/OwnershipCard/OwnershipCard').then(m => + compatWrapper( + , + ), + ), + }); }, }); From 555d43996d5d95fc0edb20d0541bc1dfcbacc269 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 9 Sep 2025 15:14:33 +0200 Subject: [PATCH 014/211] fix prettier Signed-off-by: secustor --- packages/app-next/app-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 59fecf7e64..704cc8d8a8 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -26,7 +26,7 @@ app: config: showAggregateMembersToggle: true initialRelationAggregation: aggregated - + # - apis.plugin.graphiql.browse.gitlab: true # - graphiql-endpoint:graphiql/gitlab: true From f7a414425d94bca21d40b1d1af921a4500b6dbd1 Mon Sep 17 00:00:00 2001 From: Adam Letizia Date: Thu, 4 Sep 2025 13:39:11 -0500 Subject: [PATCH 015/211] fix(kubernetes): fixes calculation of pod cpu utilization Signed-off-by: Adam Letizia --- .changeset/twelve-guests-sit.md | 5 +++++ .../src/components/Pods/PodsTable.test.tsx | 2 +- plugins/kubernetes-react/src/utils/pod.test.tsx | 2 +- plugins/kubernetes-react/src/utils/pod.tsx | 12 ++---------- 4 files changed, 9 insertions(+), 12 deletions(-) create mode 100644 .changeset/twelve-guests-sit.md diff --git a/.changeset/twelve-guests-sit.md b/.changeset/twelve-guests-sit.md new file mode 100644 index 0000000000..0d75e07f0c --- /dev/null +++ b/.changeset/twelve-guests-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-react': patch +--- + +Fixes calculation of CPU utilization in the PodTable diff --git a/plugins/kubernetes-react/src/components/Pods/PodsTable.test.tsx b/plugins/kubernetes-react/src/components/Pods/PodsTable.test.tsx index fb90d17e2b..76136641af 100644 --- a/plugins/kubernetes-react/src/components/Pods/PodsTable.test.tsx +++ b/plugins/kubernetes-react/src/components/Pods/PodsTable.test.tsx @@ -74,7 +74,7 @@ describe('PodsTable', () => { limitTotal: '134217728', }, cpu: { - currentUsage: 0.4966115, + currentUsage: 0.0496, requestTotal: 0.05, limitTotal: 0.05, }, diff --git a/plugins/kubernetes-react/src/utils/pod.test.tsx b/plugins/kubernetes-react/src/utils/pod.test.tsx index 3f82c14f86..a75039ceea 100644 --- a/plugins/kubernetes-react/src/utils/pod.test.tsx +++ b/plugins/kubernetes-react/src/utils/pod.test.tsx @@ -42,7 +42,7 @@ describe('pod', () => { const result = podStatusToCpuUtil({ cpu: { // ~50m - currentUsage: 0.4966115, + currentUsage: 0.0496, // 50m requestTotal: 0.05, // 100m diff --git a/plugins/kubernetes-react/src/utils/pod.tsx b/plugins/kubernetes-react/src/utils/pod.tsx index 97b44d4d24..e033b7c3b8 100644 --- a/plugins/kubernetes-react/src/utils/pod.tsx +++ b/plugins/kubernetes-react/src/utils/pod.tsx @@ -139,22 +139,14 @@ export const currentToDeclaredResourceToPerc = ( export const podStatusToCpuUtil = (podStatus: ClientPodStatus): ReactNode => { const cpuUtil = podStatus.cpu; - let currentUsage: number | string = cpuUtil.currentUsage; - - // current usage number for CPU is a different unit than request/limit total - // this might be a bug in the k8s library - if (typeof cpuUtil.currentUsage === 'number') { - currentUsage = cpuUtil.currentUsage / 10; - } - return ( From 4d2816dbe908db34a8ecd13e4cb899d6d1b196b5 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Wed, 10 Sep 2025 13:54:15 +0100 Subject: [PATCH 016/211] First pass improving theming docs Signed-off-by: Charles de Dreuille --- docs/conf/user-interface/icons.md | 124 +++++ docs/conf/user-interface/index.md | 471 +++++++++++++++++ docs/conf/user-interface/logo.md | 25 + docs/conf/user-interface/sidebar.md | 93 ++++ docs/dls/component-design-guidelines.md | 6 +- docs/dls/design.md | 2 +- docs/features/search/how-to-guides.md | 2 +- docs/getting-started/app-custom-theme.md | 613 ----------------------- microsite/sidebars.ts | 11 +- 9 files changed, 729 insertions(+), 618 deletions(-) create mode 100644 docs/conf/user-interface/icons.md create mode 100644 docs/conf/user-interface/index.md create mode 100644 docs/conf/user-interface/logo.md create mode 100644 docs/conf/user-interface/sidebar.md delete mode 100644 docs/getting-started/app-custom-theme.md diff --git a/docs/conf/user-interface/icons.md b/docs/conf/user-interface/icons.md new file mode 100644 index 0000000000..44b8782430 --- /dev/null +++ b/docs/conf/user-interface/icons.md @@ -0,0 +1,124 @@ +--- +id: icons +title: Customizing Icons +sidebar_label: Icons +description: Customizing Icons +--- + +So far you've seen how to create your own theme and add your own logo, in the following sections you'll be shown how to override the existing icons and how to add more icons + +## Custom Icons + +You can also customize the Project's _default_ icons. + +You can change the following [icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx). + +### Requirements + +- Files in `.svg` format +- React components created for the icons + +### Create React Component + +In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `CustomIcons.tsx` file. + +```tsx title="customIcons.tsx" +import { SvgIcon, SvgIconProps } from '@material-ui/core'; + +export const ExampleIcon = (props: SvgIconProps) => ( + + + +); +``` + +### Using the custom icon + +Supply your custom icon in `packages/app/src/App.tsx` + +```tsx title="packages/app/src/App.tsx" +/* highlight-add-next-line */ +import { ExampleIcon } from './assets/icons/CustomIcons' + + +const app = createApp({ + apis, + components: { + {/* ... */} + }, + themes: [ + {/* ... */} + ], + /* highlight-add-start */ + icons: { + github: ExampleIcon, + }, + /* highlight-add-end */ + bindRoutes({ bind }) { + {/* ... */} + } +}) +``` + +## Adding Icons + +You can add more icons, if the [default icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx) do not fit your needs, so that they can be used in other places like for Links in your entities. For this example we'll be using icons from[Material UI](https://v4.mui.com/components/material-icons/) and specifically the `AlarmIcon`. Here's how to do that: + +1. First you will want to open your `App.tsx` in `/packages/app/src` +2. Then you want to import your icon, add this to the rest of your imports: `import AlarmIcon from '@material-ui/icons/Alarm';` +3. Next you want to add the icon like this to your `createApp`: + + ```tsx title="packages/app/src/App.tsx" + const app = createApp({ + apis: ..., + plugins: ..., + /* highlight-add-start */ + icons: { + alert: AlarmIcon, + }, + /* highlight-add-end */ + themes: ..., + components: ..., + }); + ``` + +4. Now we can reference `alert` for our icon in our entity links like this: + + ```yaml + apiVersion: backstage.io/v1alpha1 + kind: Component + metadata: + name: artist-lookup + description: Artist Lookup + links: + - url: https://example.com/alert + title: Alerts + icon: alert + ``` + + And this is the result: + + ![Example Link with Alert icon](../../assets/getting-started/add-icons-links-example.png) + + Another way you can use these icons is from the `AppContext` like this: + + ```ts + import { useApp } from '@backstage/core-plugin-api'; + + const app = useApp(); + const alertIcon = app.getSystemIcon('alert'); + ``` + + You might want to use this method if you have an icon you want to use in several locations. + +:::note Note + +If the icon is not available as one of the default icons or one you've added then it will fall back to Material UI's `LanguageIcon` + +::: diff --git a/docs/conf/user-interface/index.md b/docs/conf/user-interface/index.md new file mode 100644 index 0000000000..9be726d2dc --- /dev/null +++ b/docs/conf/user-interface/index.md @@ -0,0 +1,471 @@ +--- +id: index +title: Customizing Your App's UI +sidebar_label: Introduction +description: Learn how to customize the look and feel of your Backstage app, including theming and branding options. +--- + +Backstage offers built-in support for both light and dark themes, making it easy to get started with a professional look and feel. But many teams want to go further—tailoring the interface to reflect their organization’s unique brand, identity, and experience. + +This section explores the different ways you can customize the appearance of your Backstage instance. You'll learn how the theming system is structured today, how to work with the two coexisting UI systems, and how to define themes that align with your visual language. + +## Theming architecture overview + +Backstage currently supports two parallel UI systems. The original theming and component model is built on Material UI (MUI), a popular React-based framework. More recently, Backstage introduced Backstage UI (BUI), a custom-designed, CSS-first system developed to meet the platform’s evolving needs. Both systems are supported today, with many parts of the ecosystem still using MUI while new components adopt BUI. + +
+
+

MUI (Legacy)

+
    +
  • Theming: JS-based with UnifiedThemeProvider
  • +
  • Coverage: Most existing plugins
  • +
  • Documentation: mui.com
  • +
+
+
+

Backstage UI (New)

+
    +
  • Theming: CSS variables and tokens
  • +
  • Coverage: Growing, focused on new work
  • +
  • Documentation: ui.backstage.io
  • +
+
+
+ +:::info +We recognize that maintaining two separate theming systems is not ideal. Because of the fundamental architectural differences between MUI and Backstage UI, it can be challenging to automate theme updates or know exactly which theme to modify for a given component. Our recommendation is to inspect the component’s code and check its class names: if you see a class name starting with `bui`, you should use the Backstage UI theming approach to style it. +::: + +## Creating custom themes + +During the transition to Backstage UI, you will need to maintain themes in two places: some components and plugins still rely on MUI, while others use Backstage UI. At this time, there is no automated solution to generate MUI themes from your Backstage UI theme, so you will need to manage both separately until the migration is complete. + +```tsx title="packages/app/src/App.tsx" +/* highlight-add-start */ +import { lightTheme, darkTheme } from './themes'; // MUI themes +import './styles.css'; // Backstage UI (BUI) theme +/* highlight-add-end */ + +const app = createApp({ + apis, + components, + /* highlight-add-start */ + themes: [ + { + id: 'light', + title: 'Light theme', + variant: 'light', + icon: , + Provider: ({ children }) => ( + + ), + }, + { + id: 'dark', + title: 'Dark theme', + variant: 'dark', + icon: , + Provider: ({ children }) => ( + + ), + }, + ], + /* highlight-add-end */ +}); +``` + +| Name | Description | +| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | Each theme has a unique `id` | +| `title` | This will be shown in the settings page to select the right theme. | +| `variant` | This can be either `light` or `dark`. This is also refered as `mode`. On the `body` of your app we are inserting a data attribute to set the theme based on this value `data-theme-mode="light"`. | +| `icon` | This will be shown in the settings page as a visual element to complement the title. | +| `Provider` | This is needed to set the legacy theme with MUI only. This will be become redundant later on when we fully replace with BUI but for now you need to have it for MUI to work. BUI is based on CSS and don't rely on any global providers. | + +:::note +Your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `themes.light` and `themes.dark` from [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). Be sure to provide both `light` and `dark` modes so users can choose their preference. +::: + +## Create a theme for Backstage UI (New) + +Backstage UI is built entirely using CSS. By default we are providing a default theme that include all our core CSS variables and component styles. To start customising Backstage UI to match your brand you need to create a new CSS file and import it directly in `packages/app/src/App.tsx`. All styles declared in this file will override the default styles. As your file grow you can organise it the way you want or even import multiple files. + +Backstage UI is using light by default under `:root` but you can target it more specifically using the data attribute for mode + +```css title="packages/app/src/styles.css" +:root { + /* Use :root to set styles for both light and dark themes */ + .bui-Button { + background-color: #000; + color: #fff; + } +} + +[data-theme-mode='light'] { + /* Light theme specific styles */ +  --bui-bg: #f8f8f8; + --bui-fg-primary: #000; +} + +[data-theme-mode='dark'] { + /* Dark theme specific styles */ +  --bui-bg: #333333; + --bui-fg-primary: #fff; +} +``` + +### Available tokens + +### Component class names + +### Custom font + +## Create a theme for MUI (Legacy) + +To customize the appearance of your Backstage app using the legacy MUI theming system, you can define your own theme by extending the built-in light or dark themes. This is done using the createUnifiedTheme utility provided by the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. This function allows you to override key aspects of the theme—such as color palette, typography, spacing, and shape—while preserving Backstage’s base configuration and component compatibility. + +The example below shows how to create a new theme based on the default light theme: + +```ts title="packages/app/src/themes.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + palettes, +} from '@backstage/theme'; + +export const lightTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + }), + fontFamily: 'Comic Sans MS', + defaultPageTheme: 'home', +}); + +export const darkTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.dark, + }), + fontFamily: 'Comic Sans MS', + defaultPageTheme: 'home', +}); +``` + +You can also create a theme from scratch that matches the `BackstageTheme` type exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). See the +[Material UI docs on theming](https://material-ui.com/customization/theming/) for more information about how that can be done. + +
+ Example of a custom MUI theme + +For a more complete example of a custom theme including Backstage and Material UI component overrides, see the [Aperture theme](https://github.com/backstage/demo/blob/master/packages/app/src/theme/aperture.ts) from the [Backstage demo site](https://demo.backstage.io). + +```ts title="packages/app/src/themes.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + genPageTheme, + palettes, + shapes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: { + ...palettes.light, + primary: { + main: '#343b58', + }, + secondary: { + main: '#565a6e', + }, + error: { + main: '#8c4351', + }, + warning: { + main: '#8f5e15', + }, + info: { + main: '#34548a', + }, + success: { + main: '#485e30', + }, + background: { + default: '#d5d6db', + paper: '#d5d6db', + }, + banner: { + info: '#34548a', + error: '#8c4351', + text: '#343b58', + link: '#565a6e', + }, + errorBackground: '#8c4351', + warningBackground: '#8f5e15', + infoBackground: '#343b58', + navigation: { + background: '#343b58', + indicator: '#8f5e15', + color: '#d5d6db', + selectedColor: '#ffffff', + }, + }, + }), + defaultPageTheme: 'home', + fontFamily: 'Comic Sans MS', + /* below drives the header colors */ + pageTheme: { + home: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + documentation: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave2, + }), + tool: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.round }), + service: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave, + }), + website: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave, + }), + library: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave, + }), + other: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + app: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + apis: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + }, +}); +``` + +
+ +
+ Custom Typography + +When creating a custom theme you can also customize various aspects of the default typography, here's an example using simplified theme: + +```ts title="packages/app/src/theme/myTheme.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + palettes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + typography: { + htmlFontSize: 16, + fontFamily: 'Arial, sans-serif', + h1: { + fontSize: 54, + fontWeight: 700, + marginBottom: 10, + }, + h2: { + fontSize: 40, + fontWeight: 700, + marginBottom: 8, + }, + h3: { + fontSize: 32, + fontWeight: 700, + marginBottom: 6, + }, + h4: { + fontWeight: 700, + fontSize: 28, + marginBottom: 6, + }, + h5: { + fontWeight: 700, + fontSize: 24, + marginBottom: 4, + }, + h6: { + fontWeight: 700, + fontSize: 20, + marginBottom: 2, + }, + }, + defaultPageTheme: 'home', + }), +}); +``` + +If you wanted to only override a sub-set of the typography setting, for example just `h1` then you would do this: + +```ts title="packages/app/src/theme/myTheme.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + defaultTypography, + palettes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + typography: { + ...defaultTypography, + htmlFontSize: 16, + fontFamily: 'Roboto, sans-serif', + h1: { + fontSize: 72, + fontWeight: 700, + marginBottom: 10, + }, + }, + defaultPageTheme: 'home', + }), +}); +``` + +
+ +
+ Custom Fonts + +To add custom fonts, you first need to store the font so that it can be imported. We suggest creating the `assets/fonts` directory in your front-end application `src` folder. + +You can then declare the font style following the `@font-face` syntax from [Material UI Typography](https://mui.com/material-ui/customization/typography/). + +After that you can then utilize the `styleOverrides` of `MuiCssBaseline` under components to add a font to the `@font-face` array. + +```ts title="packages/app/src/theme/myTheme.ts" +import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2'; + +const myCustomFont = { + fontFamily: 'My-Custom-Font', + fontStyle: 'normal', + fontDisplay: 'swap', + fontWeight: 300, + src: ` + local('My-Custom-Font'), + url(${MyCustomFont}) format('woff2'), + `, +}; + +export const myTheme = createUnifiedTheme({ + fontFamily: 'My-Custom-Font', + palette: palettes.light, + components: { + MuiCssBaseline: { + styleOverrides: { + '@font-face': [myCustomFont], + }, + }, + }, +}); +``` + +If you want to utilize different or multiple fonts, then you can set the top level `fontFamily` to what you want for your body, and then override `fontFamily` in `typography` to control fonts for various headings. + +```ts title="packages/app/src/theme/myTheme.ts" +import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2'; +import myAwesomeFont from '../assets/fonts/My-Awesome-Font.woff2'; + +const myCustomFont = { + fontFamily: 'My-Custom-Font', + fontStyle: 'normal', + fontDisplay: 'swap', + fontWeight: 300, + src: ` + local('My-Custom-Font'), + url(${MyCustomFont}) format('woff2'), + `, +}; + +const myAwesomeFont = { + fontFamily: 'My-Awesome-Font', + fontStyle: 'normal', + fontDisplay: 'swap', + fontWeight: 300, + src: ` + local('My-Awesome-Font'), + url(${myAwesomeFont}) format('woff2'), + `, +}; + +export const myTheme = createUnifiedTheme({ + fontFamily: 'My-Custom-Font', + components: { + MuiCssBaseline: { + styleOverrides: { + '@font-face': [myCustomFont, myAwesomeFont], + }, + }, + }, + ...createBaseThemeOptions({ + palette: palettes.light, + typography: { + ...defaultTypography, + htmlFontSize: 16, + fontFamily: 'My-Custom-Font', + h1: { + fontSize: 72, + fontWeight: 700, + marginBottom: 10, + fontFamily: 'My-Awesome-Font', + }, + }, + defaultPageTheme: 'home', + }), +}); +``` + +
+ +
+ Overriding Backstage and Material UI components styles + +When creating a custom theme you would be applying different values to component's CSS rules that use the theme object. For example, a Backstage component's styles might look like this: + +```tsx +const useStyles = makeStyles( + theme => ({ + header: { + padding: theme.spacing(3), + boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', + backgroundImage: theme.page.backgroundImage, + }, + }), + { name: 'BackstageHeader' }, +); +``` + +Notice how the `padding` is getting its value from `theme.spacing`, that means that setting a value for spacing in your custom theme would affect this component padding property and the same goes for `backgroundImage` which uses `theme.page.backgroundImage`. However, the `boxShadow` property doesn't reference any value from the theme, that means that creating a custom theme wouldn't be enough to alter the `box-shadow` property or to add css rules that aren't already defined like a margin. For these cases you should also create an override. + +Here's how you would do that: + +```ts title="packages/app/src/theme/myTheme.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + palettes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + }), + fontFamily: 'Comic Sans MS', + defaultPageTheme: 'home', + components: { + BackstageHeader: { + styleOverrides: { + header: ({ theme }) => ({ + width: 'auto', + margin: '20px', + boxShadow: 'none', + borderBottom: `4px solid ${theme.palette.primary.main}`, + }), + }, + }, + }, +}); +``` + +
diff --git a/docs/conf/user-interface/logo.md b/docs/conf/user-interface/logo.md new file mode 100644 index 0000000000..35789d6330 --- /dev/null +++ b/docs/conf/user-interface/logo.md @@ -0,0 +1,25 @@ +--- +id: logo +title: Customizing Your Logo +sidebar_label: Logo +description: Learn how to customize your logo. +--- + +In addition to a custom theme, you can also customize the logo displayed at the far top left of the site. + +In your frontend app, locate `src/components/Root/` folder. You'll find two components: + +- `LogoFull.tsx` - A larger logo used when the Sidebar navigation is opened. +- `LogoIcon.tsx` - A smaller logo used when the Sidebar navigation is closed. + +To replace the images, you can simply replace the relevant code in those components with raw SVG definitions. + +You can also use another web image format such as PNG by importing it. To do this, place your new image into a new subdirectory such as `src/components/Root/logo/my-company-logo.png`, and then add this code: + +```tsx +import MyCustomLogoFull from './logo/my-company-logo.png'; + +const LogoFull = () => { + return ; +}; +``` diff --git a/docs/conf/user-interface/sidebar.md b/docs/conf/user-interface/sidebar.md new file mode 100644 index 0000000000..f66e6bd106 --- /dev/null +++ b/docs/conf/user-interface/sidebar.md @@ -0,0 +1,93 @@ +--- +id: sidebar +title: Customizing Your Sidebar +sidebar_label: Sidebar +description: Learn how to customize the look and feel of your Sidebar. +--- + +As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar. + +## Sidebar Sub-menu + +For this example we'll show you how you can expand the sidebar with a sub-menu: + +1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives +2. Then we want to add the following import for `useApp`: + + ```tsx title="packages/app/src/components/Root/Root.tsx" + import { useApp } from '@backstage/core-plugin-api'; + ``` + +3. Then update the `@backstage/core-components` import like this: + + ```tsx title="packages/app/src/components/Root/Root.tsx" + import { + Sidebar, + sidebarConfig, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, + useSidebarOpenState, + Link, + /* highlight-add-start */ + GroupIcon, + SidebarSubmenu, + SidebarSubmenuItem, + /* highlight-add-end */ + } from '@backstage/core-components'; + ``` + +4. Finally replace `` with this: + + ```tsx title="packages/app/src/components/Root/Root.tsx" + + + + + + + + + + + + + + ``` + +When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this: + +![Sidebar sub-menu example](./../../assets/getting-started/sidebar-submenu-example.png) + +You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar) diff --git a/docs/dls/component-design-guidelines.md b/docs/dls/component-design-guidelines.md index 35e2f987d2..17e9d1f11a 100644 --- a/docs/dls/component-design-guidelines.md +++ b/docs/dls/component-design-guidelines.md @@ -103,6 +103,8 @@ accessibility. [7]: https://v4.mui.com/components/cards/ [8]: https://v4.mui.com/customization/palette/#default-values [9]: https://v4.mui.com/customization/typography/ -[10]: https://backstage.io/docs/getting-started/app-custom-theme -[11]: https://backstage.io/docs/getting-started/app-custom-theme#overriding-backstage-and-material-ui-components-styles +[10]: https://backstage.io/docs/conf/user-interface + + + [12]: https://v4.mui.com/customization/default-theme/#explore diff --git a/docs/dls/design.md b/docs/dls/design.md index 7e2ae90528..e988d1032f 100644 --- a/docs/dls/design.md +++ b/docs/dls/design.md @@ -127,7 +127,7 @@ your own plugin for Backstage. **[Discord](https://discord.gg/backstage-687207715902193673)** - all design questions should be directed to the _#design_ channel. -**[Customize Backstage's look and feel](https://backstage.io/docs/getting-started/app-custom-theme)** - +**[Customizing Your App's UI](https://backstage.io/docs/conf/user-interface)** - How to customize the look and feel of your Backstage instance by extending the theme. diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md index 7055db1138..f0823ac47e 100644 --- a/docs/features/search/how-to-guides.md +++ b/docs/features/search/how-to-guides.md @@ -116,7 +116,7 @@ indexBuilder.addCollator({ The default highlighting styling for matched terms in search results is your browsers default styles for the `` HTML tag. If you want to customize how highlighted terms look you can follow Backstage's guide on how to -[Customize the look-and-feel of your App](https://backstage.io/docs/getting-started/app-custom-theme) +[Customizing Your App's UI](https://backstage.io/docs/conf/user-interface) to create an override with your preferred styling. For example, using the new MUI V4+V5 unified theming method, the following will result diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md deleted file mode 100644 index 38f2fd4c98..0000000000 --- a/docs/getting-started/app-custom-theme.md +++ /dev/null @@ -1,613 +0,0 @@ ---- -id: app-custom-theme -title: Customize the look-and-feel of your App -description: Documentation on Customizing look and feel of the App ---- - -Backstage ships with a default theme with a light and dark mode variant. The themes are provided as a part of the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package, which also includes utilities for customizing the default theme, or creating completely new themes. - -## Creating a Custom Theme - -The easiest way to create a new theme is to use the `createUnifiedTheme` function exported by the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. You can use it to override some basic parameters of the default theme such as the color palette and font. - -For example, you can create a new theme based on the default light theme like this: - -```ts title="packages/app/src/theme/myTheme.ts" -import { - createBaseThemeOptions, - createUnifiedTheme, - palettes, -} from '@backstage/theme'; - -export const myTheme = createUnifiedTheme({ - ...createBaseThemeOptions({ - palette: palettes.light, - }), - fontFamily: 'Comic Sans MS', - defaultPageTheme: 'home', -}); -``` - -:::note Note - -we recommend creating a `theme` folder in `packages/app/src` to place your theme file to keep things nicely organized. - -::: - -You can also create a theme from scratch that matches the `BackstageTheme` type exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). See the -[Material UI docs on theming](https://material-ui.com/customization/theming/) for more information about how that can be done. - -## Using your Custom Theme - -To add a custom theme to your Backstage app, you pass it as configuration to `createApp`. - -For example, adding the theme that we created in the previous section can be done like this: - -```tsx title="packages/app/src/App.tsx" -import { createApp } from '@backstage/app-defaults'; -import { ThemeProvider } from '@material-ui/core/styles'; -import CssBaseline from '@material-ui/core/CssBaseline'; -import LightIcon from '@material-ui/icons/WbSunny'; -import { UnifiedThemeProvider} from '@backstage/theme'; -import { myTheme } from './themes/myTheme'; - -const app = createApp({ - apis: ..., - plugins: ..., - themes: [{ - id: 'my-theme', - title: 'My Custom Theme', - variant: 'light', - icon: , - Provider: ({ children }) => ( - - ), - }] -}) -``` - -Note that your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `themes.light` and `themes.dark` from [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). - -## Example of a custom theme - -```ts title="packages/app/src/theme/myTheme.ts" -import { - createBaseThemeOptions, - createUnifiedTheme, - genPageTheme, - palettes, - shapes, -} from '@backstage/theme'; - -export const myTheme = createUnifiedTheme({ - ...createBaseThemeOptions({ - palette: { - ...palettes.light, - primary: { - main: '#343b58', - }, - secondary: { - main: '#565a6e', - }, - error: { - main: '#8c4351', - }, - warning: { - main: '#8f5e15', - }, - info: { - main: '#34548a', - }, - success: { - main: '#485e30', - }, - background: { - default: '#d5d6db', - paper: '#d5d6db', - }, - banner: { - info: '#34548a', - error: '#8c4351', - text: '#343b58', - link: '#565a6e', - }, - errorBackground: '#8c4351', - warningBackground: '#8f5e15', - infoBackground: '#343b58', - navigation: { - background: '#343b58', - indicator: '#8f5e15', - color: '#d5d6db', - selectedColor: '#ffffff', - }, - }, - }), - defaultPageTheme: 'home', - fontFamily: 'Comic Sans MS', - /* below drives the header colors */ - pageTheme: { - home: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), - documentation: genPageTheme({ - colors: ['#8c4351', '#343b58'], - shape: shapes.wave2, - }), - tool: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.round }), - service: genPageTheme({ - colors: ['#8c4351', '#343b58'], - shape: shapes.wave, - }), - website: genPageTheme({ - colors: ['#8c4351', '#343b58'], - shape: shapes.wave, - }), - library: genPageTheme({ - colors: ['#8c4351', '#343b58'], - shape: shapes.wave, - }), - other: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), - app: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), - apis: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), - }, -}); -``` - -For a more complete example of a custom theme including Backstage and Material UI component overrides, see the [Aperture theme](https://github.com/backstage/demo/blob/master/packages/app/src/theme/aperture.ts) from the [Backstage demo site](https://demo.backstage.io). - -## Custom Typography - -When creating a custom theme you can also customize various aspects of the default typography, here's an example using simplified theme: - -```ts title="packages/app/src/theme/myTheme.ts" -import { - createBaseThemeOptions, - createUnifiedTheme, - palettes, -} from '@backstage/theme'; - -export const myTheme = createUnifiedTheme({ - ...createBaseThemeOptions({ - palette: palettes.light, - typography: { - htmlFontSize: 16, - fontFamily: 'Arial, sans-serif', - h1: { - fontSize: 54, - fontWeight: 700, - marginBottom: 10, - }, - h2: { - fontSize: 40, - fontWeight: 700, - marginBottom: 8, - }, - h3: { - fontSize: 32, - fontWeight: 700, - marginBottom: 6, - }, - h4: { - fontWeight: 700, - fontSize: 28, - marginBottom: 6, - }, - h5: { - fontWeight: 700, - fontSize: 24, - marginBottom: 4, - }, - h6: { - fontWeight: 700, - fontSize: 20, - marginBottom: 2, - }, - }, - defaultPageTheme: 'home', - }), -}); -``` - -If you wanted to only override a sub-set of the typography setting, for example just `h1` then you would do this: - -```ts title="packages/app/src/theme/myTheme.ts" -import { - createBaseThemeOptions, - createUnifiedTheme, - defaultTypography, - palettes, -} from '@backstage/theme'; - -export const myTheme = createUnifiedTheme({ - ...createBaseThemeOptions({ - palette: palettes.light, - typography: { - ...defaultTypography, - htmlFontSize: 16, - fontFamily: 'Roboto, sans-serif', - h1: { - fontSize: 72, - fontWeight: 700, - marginBottom: 10, - }, - }, - defaultPageTheme: 'home', - }), -}); -``` - -## Custom Fonts - -To add custom fonts, you first need to store the font so that it can be imported. We suggest creating the `assets/fonts` directory in your front-end application `src` folder. - -You can then declare the font style following the `@font-face` syntax from [Material UI Typography](https://mui.com/material-ui/customization/typography/). - -After that you can then utilize the `styleOverrides` of `MuiCssBaseline` under components to add a font to the `@font-face` array. - -```ts title="packages/app/src/theme/myTheme.ts" -import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2'; - -const myCustomFont = { - fontFamily: 'My-Custom-Font', - fontStyle: 'normal', - fontDisplay: 'swap', - fontWeight: 300, - src: ` - local('My-Custom-Font'), - url(${MyCustomFont}) format('woff2'), - `, -}; - -export const myTheme = createUnifiedTheme({ - fontFamily: 'My-Custom-Font', - palette: palettes.light, - components: { - MuiCssBaseline: { - styleOverrides: { - '@font-face': [myCustomFont], - }, - }, - }, -}); -``` - -If you want to utilize different or multiple fonts, then you can set the top level `fontFamily` to what you want for your body, and then override `fontFamily` in `typography` to control fonts for various headings. - -```ts title="packages/app/src/theme/myTheme.ts" -import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2'; -import myAwesomeFont from '../assets/fonts/My-Awesome-Font.woff2'; - -const myCustomFont = { - fontFamily: 'My-Custom-Font', - fontStyle: 'normal', - fontDisplay: 'swap', - fontWeight: 300, - src: ` - local('My-Custom-Font'), - url(${MyCustomFont}) format('woff2'), - `, -}; - -const myAwesomeFont = { - fontFamily: 'My-Awesome-Font', - fontStyle: 'normal', - fontDisplay: 'swap', - fontWeight: 300, - src: ` - local('My-Awesome-Font'), - url(${myAwesomeFont}) format('woff2'), - `, -}; - -export const myTheme = createUnifiedTheme({ - fontFamily: 'My-Custom-Font', - components: { - MuiCssBaseline: { - styleOverrides: { - '@font-face': [myCustomFont, myAwesomeFont], - }, - }, - }, - ...createBaseThemeOptions({ - palette: palettes.light, - typography: { - ...defaultTypography, - htmlFontSize: 16, - fontFamily: 'My-Custom-Font', - h1: { - fontSize: 72, - fontWeight: 700, - marginBottom: 10, - fontFamily: 'My-Awesome-Font', - }, - }, - defaultPageTheme: 'home', - }), -}); -``` - -## Overriding Backstage and Material UI components styles - -When creating a custom theme you would be applying different values to component's CSS rules that use the theme object. For example, a Backstage component's styles might look like this: - -```tsx -const useStyles = makeStyles( - theme => ({ - header: { - padding: theme.spacing(3), - boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', - backgroundImage: theme.page.backgroundImage, - }, - }), - { name: 'BackstageHeader' }, -); -``` - -Notice how the `padding` is getting its value from `theme.spacing`, that means that setting a value for spacing in your custom theme would affect this component padding property and the same goes for `backgroundImage` which uses `theme.page.backgroundImage`. However, the `boxShadow` property doesn't reference any value from the theme, that means that creating a custom theme wouldn't be enough to alter the `box-shadow` property or to add css rules that aren't already defined like a margin. For these cases you should also create an override. - -Here's how you would do that: - -```ts title="packages/app/src/theme/myTheme.ts" -import { - createBaseThemeOptions, - createUnifiedTheme, - palettes, -} from '@backstage/theme'; - -export const myTheme = createUnifiedTheme({ - ...createBaseThemeOptions({ - palette: palettes.light, - }), - fontFamily: 'Comic Sans MS', - defaultPageTheme: 'home', - components: { - BackstageHeader: { - styleOverrides: { - header: ({ theme }) => ({ - width: 'auto', - margin: '20px', - boxShadow: 'none', - borderBottom: `4px solid ${theme.palette.primary.main}`, - }), - }, - }, - }, -}); -``` - -## Custom Logo - -In addition to a custom theme, you can also customize the logo displayed at the far top left of the site. - -In your frontend app, locate `src/components/Root/` folder. You'll find two components: - -- `LogoFull.tsx` - A larger logo used when the Sidebar navigation is opened. -- `LogoIcon.tsx` - A smaller logo used when the Sidebar navigation is closed. - -To replace the images, you can simply replace the relevant code in those components with raw SVG definitions. - -You can also use another web image format such as PNG by importing it. To do this, place your new image into a new subdirectory such as `src/components/Root/logo/my-company-logo.png`, and then add this code: - -```tsx -import MyCustomLogoFull from './logo/my-company-logo.png'; - -const LogoFull = () => { - return ; -}; -``` - -## Icons - -So far you've seen how to create your own theme and add your own logo, in the following sections you'll be shown how to override the existing icons and how to add more icons - -### Custom Icons - -You can also customize the Project's _default_ icons. - -You can change the following [icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx). - -#### Requirements - -- Files in `.svg` format -- React components created for the icons - -#### Create React Component - -In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `CustomIcons.tsx` file. - -```tsx title="customIcons.tsx" -import { SvgIcon, SvgIconProps } from '@material-ui/core'; - -export const ExampleIcon = (props: SvgIconProps) => ( - - - -); -``` - -#### Using the custom icon - -Supply your custom icon in `packages/app/src/App.tsx` - -```tsx title="packages/app/src/App.tsx" -/* highlight-add-next-line */ -import { ExampleIcon } from './assets/icons/CustomIcons' - - -const app = createApp({ - apis, - components: { - {/* ... */} - }, - themes: [ - {/* ... */} - ], - /* highlight-add-start */ - icons: { - github: ExampleIcon, - }, - /* highlight-add-end */ - bindRoutes({ bind }) { - {/* ... */} - } -}) -``` - -### Adding Icons - -You can add more icons, if the [default icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx) do not fit your needs, so that they can be used in other places like for Links in your entities. For this example we'll be using icons from[Material UI](https://v4.mui.com/components/material-icons/) and specifically the `AlarmIcon`. Here's how to do that: - -1. First you will want to open your `App.tsx` in `/packages/app/src` -2. Then you want to import your icon, add this to the rest of your imports: `import AlarmIcon from '@material-ui/icons/Alarm';` -3. Next you want to add the icon like this to your `createApp`: - - ```tsx title="packages/app/src/App.tsx" - const app = createApp({ - apis: ..., - plugins: ..., - /* highlight-add-start */ - icons: { - alert: AlarmIcon, - }, - /* highlight-add-end */ - themes: ..., - components: ..., - }); - ``` - -4. Now we can reference `alert` for our icon in our entity links like this: - - ```yaml - apiVersion: backstage.io/v1alpha1 - kind: Component - metadata: - name: artist-lookup - description: Artist Lookup - links: - - url: https://example.com/alert - title: Alerts - icon: alert - ``` - - And this is the result: - - ![Example Link with Alert icon](../assets/getting-started/add-icons-links-example.png) - - Another way you can use these icons is from the `AppContext` like this: - - ```ts - import { useApp } from '@backstage/core-plugin-api'; - - const app = useApp(); - const alertIcon = app.getSystemIcon('alert'); - ``` - - You might want to use this method if you have an icon you want to use in several locations. - -:::note Note - -If the icon is not available as one of the default icons or one you've added then it will fall back to Material UI's `LanguageIcon` - -::: - -## Custom Sidebar - -As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar. - -### Sidebar Sub-menu - -For this example we'll show you how you can expand the sidebar with a sub-menu: - -1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives -2. Then we want to add the following import for `useApp`: - - ```tsx title="packages/app/src/components/Root/Root.tsx" - import { useApp } from '@backstage/core-plugin-api'; - ``` - -3. Then update the `@backstage/core-components` import like this: - - ```tsx title="packages/app/src/components/Root/Root.tsx" - import { - Sidebar, - sidebarConfig, - SidebarDivider, - SidebarGroup, - SidebarItem, - SidebarPage, - SidebarScrollWrapper, - SidebarSpace, - useSidebarOpenState, - Link, - /* highlight-add-start */ - GroupIcon, - SidebarSubmenu, - SidebarSubmenuItem, - /* highlight-add-end */ - } from '@backstage/core-components'; - ``` - -4. Finally replace `` with this: - - ```tsx title="packages/app/src/components/Root/Root.tsx" - - - - - - - - - - - - - - ``` - -When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this: - -![Sidebar sub-menu example](./../assets/getting-started/sidebar-submenu-example.png) - -You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar) - -## Custom Homepage - -In addition to a custom theme, a custom logo, you can also customize the -homepage of your app. Read the full guide on the [next page](homepage.md). - -## Migrating to Material UI v5 - -We now support Material UI v5 in Backstage. Check out our [migration guide](../tutorials/migrate-to-mui5.md) to get started. diff --git a/microsite/sidebars.ts b/microsite/sidebars.ts index 5a408d5d1f..b7e89825ca 100644 --- a/microsite/sidebars.ts +++ b/microsite/sidebars.ts @@ -35,7 +35,6 @@ export default { 'getting-started/config/database', 'getting-started/config/authentication', 'getting-started/configure-app-with-plugins', - 'getting-started/app-custom-theme', 'getting-started/homepage', ], }, @@ -398,6 +397,16 @@ export default { 'conf/reading', 'conf/writing', 'conf/defining', + { + type: 'category', + label: 'User Interface', + items: [ + 'conf/user-interface/index', + 'conf/user-interface/logo', + 'conf/user-interface/icons', + 'conf/user-interface/sidebar', + ], + }, ], Framework: [ { From f23cd8862150f64f109a6cbff2bcce9931fd67fc Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Wed, 10 Sep 2025 13:56:59 +0100 Subject: [PATCH 017/211] Fix spelling mistakes Signed-off-by: Charles de Dreuille --- docs/conf/user-interface/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf/user-interface/index.md b/docs/conf/user-interface/index.md index 9be726d2dc..5f2d355137 100644 --- a/docs/conf/user-interface/index.md +++ b/docs/conf/user-interface/index.md @@ -78,7 +78,7 @@ const app = createApp({ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Each theme has a unique `id` | | `title` | This will be shown in the settings page to select the right theme. | -| `variant` | This can be either `light` or `dark`. This is also refered as `mode`. On the `body` of your app we are inserting a data attribute to set the theme based on this value `data-theme-mode="light"`. | +| `variant` | This can be either `light` or `dark`. This is also referred to as `mode`. On the `body` of your app we are inserting a data attribute to set the theme based on this value: `data-theme-mode="light"`. | | `icon` | This will be shown in the settings page as a visual element to complement the title. | | `Provider` | This is needed to set the legacy theme with MUI only. This will be become redundant later on when we fully replace with BUI but for now you need to have it for MUI to work. BUI is based on CSS and don't rely on any global providers. | From 8b25afdf12c0cbfc54a5e5e57eedb9460bb9b874 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 11 Sep 2025 11:32:32 +0100 Subject: [PATCH 018/211] Fix link error Signed-off-by: Charles de Dreuille --- docs/getting-started/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 252884dc54..876af4f0a5 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -157,7 +157,7 @@ Choose the correct next steps for your user role, if you're likely to be deployi - Configuring Backstage - [Adding plugins](./configure-app-with-plugins.md) - - [Customizing the theme](./app-custom-theme.md) + - [Customizing Your App's UI](../conf/user-interface/index.md) - [Populating the homepage](./homepage.md) ### Developer From 99f7e737d61a67aa8879aa10647fbab1b901e94c Mon Sep 17 00:00:00 2001 From: TA31OU Date: Thu, 11 Sep 2025 14:45:29 +0200 Subject: [PATCH 019/211] Merge Signed-off-by: TA31OU --- .changeset/angry-heads-design.md | 5 + .changeset/better-eagles-tickle.md | 5 + .changeset/big-cameras-turn.md | 5 + .changeset/brave-jars-speak.md | 5 + .changeset/chilly-llamas-attend.md | 5 + .changeset/clear-houses-wonder.md | 5 + .changeset/cold-garlics-care.md | 5 + .changeset/create-app-1757429965.md | 5 + .changeset/dirty-spies-drop.md | 5 + .changeset/eighty-numbers-act.md | 5 + .changeset/eleven-doors-down.md | 5 + .changeset/eleven-doors-own.md | 5 + .changeset/funny-eagles-try.md | 7 + .changeset/giant-buttons-flash.md | 5 + .changeset/gold-words-smoke.md | 5 + .changeset/heavy-cats-unite.md | 6 + .changeset/heavy-lies-listen.md | 5 + .changeset/hot-friends-act.md | 5 + .changeset/hungry-carrots-grow.md | 5 + .changeset/itchy-moons-start.md | 5 + .changeset/kind-eyes-worry.md | 5 + .changeset/late-swans-press.md | 5 + .changeset/lemon-jobs-create.md | 5 + .changeset/lovely-actors-love.md | 5 + .changeset/pre.json | 26 +- .changeset/quiet-papayas-mate.md | 5 + .changeset/red-shrimps-fall.md | 5 + .changeset/silent-results-stick.md | 5 + .changeset/stupid-areas-share.md | 5 + .changeset/tangy-squids-film.md | 5 + .changeset/ten-boxes-lie.md | 5 + .changeset/thin-phones-press.md | 5 + .changeset/tiny-spoons-mix.md | 5 + .changeset/tired-cobras-fly.md | 5 + .changeset/warm-emus-itch.md | 5 + .changeset/wet-onions-sneeze.md | 5 + .changeset/young-doodles-enter.md | 5 + .../config/vocabularies/Backstage/accept.txt | 2 + app-config.yaml | 5 + docs/auth/index.md | 1 + docs/auth/openshift/provider.md | 130 +++ .../core-services/actions-registry.md | 2 +- docs/backend-system/core-services/cache.md | 33 + docs/conf/index.md | 22 +- docs/contribute/project-structure.md | 4 - .../well-known-annotations.md | 2 +- docs/features/techdocs/FAQ.md | 6 + .../building-plugins/01-index.md | 2 +- docs/getting-started/index.md | 10 +- docs/references/glossary.md | 2 +- docs/releases/v1.43.0-next.2-changelog.md | 695 ++++++++++++++ lerna.json | 6 - microsite/data/plugins/datacontract.yaml | 10 + .../pluginsSearch/pluginsSearch.tsx | 19 + microsite/src/pages/plugins/index.tsx | 84 +- .../src/pages/plugins/plugins.module.scss | 14 + package.json | 2 +- packages/app-defaults/src/defaults/apis.ts | 18 + packages/app-next/CHANGELOG.md | 27 + packages/app-next/app-config.yaml | 1 - packages/app-next/package.json | 3 +- packages/app/CHANGELOG.md | 25 + packages/app/package.json | 2 +- packages/app/src/identityProviders.ts | 7 + packages/backend-defaults/CHANGELOG.md | 11 + packages/backend-defaults/package.json | 2 +- .../entrypoints/cache/CacheManager.test.ts | 143 +++ .../src/entrypoints/cache/CacheManager.ts | 24 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- packages/backend/package.json | 1 + packages/backend/src/index.ts | 1 + packages/catalog-client/CHANGELOG.md | 23 + packages/catalog-client/package.json | 2 +- packages/cli/CHANGELOG.md | 8 + packages/cli/package.json | 2 +- .../src/modules/build/lib/bundler/config.ts | 8 + packages/config-loader/CHANGELOG.md | 6 + packages/config-loader/package.json | 2 +- .../config-loader/src/schema/collect.test.ts | 10 - .../src/sources/ConfigSources.test.ts | 13 + .../src/sources/ConfigSources.ts | 28 + packages/core-app-api/report.api.md | 7 + .../src/apis/implementations/auth/index.ts | 1 + .../auth/openshift/OpenShiftAuth.ts | 52 ++ .../implementations/auth/openshift/index.ts | 5 +- packages/core-compat-api/CHANGELOG.md | 7 + packages/core-compat-api/package.json | 2 +- packages/core-components/CHANGELOG.md | 7 + packages/core-components/package.json | 4 +- packages/core-components/report-alpha.api.md | 1 + packages/core-components/report.api.md | 1 + .../DependencyGraph/DependencyGraph.test.tsx | 25 +- .../DependencyGraph/DependencyGraph.tsx | 202 ++-- .../HeaderIconLinkRow/HeaderIconLinkRow.tsx | 1 + packages/core-components/src/translation.ts | 3 + packages/core-plugin-api/report.api.md | 5 + .../src/apis/definitions/auth.ts | 17 + packages/create-app/CHANGELOG.md | 6 + packages/create-app/package.json | 2 +- packages/dev-utils/CHANGELOG.md | 8 + packages/dev-utils/package.json | 2 +- .../frontend-defaults/src/createApp.test.tsx | 1 + packages/frontend-plugin-api/report.api.md | 3 + .../src/apis/definitions/auth.ts | 1 + ...eInstanceGithubCredentialsProvider.test.ts | 36 + ...SingleInstanceGithubCredentialsProvider.ts | 10 +- packages/repo-tools/CHANGELOG.md | 7 + packages/repo-tools/package.json | 2 +- packages/ui/CHANGELOG.md | 7 + packages/ui/package.json | 2 +- packages/yarn-plugin/package.json | 1 + .../util/getCurrentBackstageVersion.test.ts | 16 +- .../src/util/getCurrentBackstageVersion.ts | 14 +- plugins/api-docs/CHANGELOG.md | 10 + plugins/api-docs/package.json | 2 +- plugins/app-backend/CHANGELOG.md | 10 + plugins/app-backend/package.json | 2 +- plugins/app-backend/src/index.ts | 1 - plugins/app-node/CHANGELOG.md | 7 + plugins/app-node/package.json | 2 +- plugins/app/report.api.md | 15 + plugins/app/src/defaultApis.ts | 22 + .../.eslintrc.js | 1 + .../README.md | 5 + .../catalog-info.yaml | 10 + .../config.d.ts | 44 + .../dev/index.ts | 26 + .../package.json | 55 ++ .../report.api.md | 28 + .../src/authenticator.test.ts | 226 +++++ .../src/authenticator.ts | 184 ++++ .../src/index.ts | 25 + .../src/module.ts | 45 + .../src/resolvers.ts | 68 ++ plugins/auth-backend/CHANGELOG.md | 10 + ...20250909120000_oidc_client_registration.js | 159 ++++ plugins/auth-backend/package.json | 4 +- plugins/auth-backend/report.sql.md | 53 ++ plugins/auth-backend/src/authPlugin.ts | 3 + .../src/database/OidcDatabase.test.ts | 293 ++++++ .../auth-backend/src/database/OidcDatabase.ts | 397 ++++++++ plugins/auth-backend/src/migrations.test.ts | 118 +++ .../src/service/OidcRouter.test.ts | 875 +++++++++++++++--- .../auth-backend/src/service/OidcRouter.ts | 383 +++++++- .../src/service/OidcService.test.ts | 789 ++++++++++++++++ .../auth-backend/src/service/OidcService.ts | 347 ++++++- plugins/auth-backend/src/service/router.ts | 11 + plugins/auth-node/CHANGELOG.md | 8 + plugins/auth-node/package.json | 3 +- plugins/auth-react/CHANGELOG.md | 8 + plugins/auth-react/package.json | 3 +- plugins/auth/.eslintrc.js | 1 + plugins/auth/CHANGELOG.md | 12 + plugins/auth/README.md | 16 + plugins/auth/catalog-info.yaml | 9 + plugins/auth/dev/index.tsx | 28 + plugins/auth/package.json | 84 ++ plugins/auth/report.api.md | 52 ++ .../components/ConsentPage/ConsentPage.tsx | 246 +++++ .../auth/src/components/ConsentPage/index.ts | 16 + .../ConsentPage/useConsentSession.ts | 144 +++ plugins/auth/src/components/Router.tsx | 29 + plugins/auth/src/index.ts | 16 + plugins/auth/src/plugin.test.ts | 22 + plugins/auth/src/plugin.tsx | 36 + plugins/auth/src/routes.ts | 18 + plugins/auth/src/setupTests.ts | 16 + .../catalog-backend-module-aws/CHANGELOG.md | 10 + .../catalog-backend-module-aws/package.json | 2 +- .../catalog-backend-module-azure/CHANGELOG.md | 8 + .../catalog-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 7 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../catalog-backend-module-gcp/CHANGELOG.md | 7 + .../catalog-backend-module-gcp/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../catalog-backend-module-gitea/CHANGELOG.md | 8 + .../catalog-backend-module-gitea/package.json | 34 +- .../CHANGELOG.md | 8 + .../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 +- .../catalog-backend-module-ldap/CHANGELOG.md | 7 + .../catalog-backend-module-ldap/package.json | 2 +- .../CHANGELOG.md | 7 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 6 +- .../report-alpha.api.md | 13 - .../report.api.md | 5 + .../src/index.ts | 1 + .../catalogModulePuppetDbEntityProvider.ts | 2 +- .../CHANGELOG.md | 7 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- plugins/catalog-backend/CHANGELOG.md | 10 + plugins/catalog-backend/package.json | 2 +- .../stitcher/markForStitching.test.ts | 135 +++ .../operations/stitcher/markForStitching.ts | 118 ++- plugins/catalog-graph/CHANGELOG.md | 10 + plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/CHANGELOG.md | 11 + plugins/catalog-import/package.json | 2 +- plugins/catalog-node/CHANGELOG.md | 28 + plugins/catalog-node/package.json | 2 +- plugins/catalog-react/CHANGELOG.md | 30 + plugins/catalog-react/package.json | 2 +- plugins/catalog/CHANGELOG.md | 11 + plugins/catalog/package.json | 2 +- plugins/catalog/src/routes.ts | 2 +- plugins/devtools-backend/CHANGELOG.md | 8 + plugins/devtools-backend/package.json | 2 +- plugins/home/CHANGELOG.md | 12 + plugins/home/package.json | 2 +- .../CustomHomepage/CustomHomepageGrid.tsx | 19 +- plugins/kubernetes-backend/CHANGELOG.md | 11 + plugins/kubernetes-backend/package.json | 2 +- .../src/service/KubernetesInitializer.ts | 5 +- plugins/kubernetes-cluster/CHANGELOG.md | 8 + plugins/kubernetes-cluster/package.json | 2 +- plugins/kubernetes/CHANGELOG.md | 9 + plugins/kubernetes/package.json | 2 +- plugins/mcp-actions-backend/CHANGELOG.md | 10 + plugins/mcp-actions-backend/package.json | 2 +- plugins/mcp-actions-backend/src/plugin.ts | 36 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- plugins/notifications-backend/CHANGELOG.md | 9 + plugins/notifications-backend/package.json | 2 +- plugins/notifications-node/CHANGELOG.md | 7 + plugins/notifications-node/package.json | 2 +- plugins/notifications/src/alpha.tsx | 7 +- plugins/org-react/CHANGELOG.md | 9 + plugins/org-react/package.json | 2 +- plugins/org/CHANGELOG.md | 9 + plugins/org/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- plugins/scaffolder-backend/CHANGELOG.md | 12 + plugins/scaffolder-backend/package.json | 2 +- .../notifications-demo/template.yaml | 4 +- .../src/service/router.test.ts | 42 +- .../scaffolder-backend/src/service/router.ts | 9 +- plugins/scaffolder-react/CHANGELOG.md | 10 + plugins/scaffolder-react/package.json | 2 +- .../TaskLogStream/TaskLogStream.tsx | 1 + plugins/scaffolder/CHANGELOG.md | 13 + plugins/scaffolder/package.json | 5 +- .../TemplateListPage.test.tsx | 129 ++- .../TemplateListPage/TemplateListPage.tsx | 24 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- plugins/search/CHANGELOG.md | 9 + plugins/search/package.json | 2 +- .../techdocs-addons-test-utils/CHANGELOG.md | 9 + .../techdocs-addons-test-utils/package.json | 2 +- .../src/test-utils.tsx | 13 + plugins/techdocs-backend/CHANGELOG.md | 11 + .../documented-component/catalog-info.yaml | 14 + .../docs/inner-component-docs/index.md | 5 + .../examples/documented-component/mkdocs.yml | 1 + plugins/techdocs-backend/package.json | 2 +- plugins/techdocs-react/src/helpers.ts | 4 +- plugins/techdocs/CHANGELOG.md | 12 + plugins/techdocs/package.json | 4 +- .../TechDocsReaderPage.test.tsx | 155 +++- .../TechDocsReaderPage/TechDocsReaderPage.tsx | 104 ++- .../TechDocsReaderPageHeader.test.tsx | 33 +- .../TechDocsReaderPageHeader.tsx | 5 +- .../transformers/html/hooks/attributes.ts | 32 + .../reader/transformers/html/hooks/iframes.ts | 7 +- .../reader/transformers/html/hooks/index.ts | 2 + .../reader/transformers/html/hooks/links.ts | 8 +- .../transformers/html/hooks/metatags.ts | 41 + .../reader/transformers/html/transformer.ts | 30 +- .../src/reader/transformers/html/utils.ts | 19 + plugins/user-settings/CHANGELOG.md | 10 + plugins/user-settings/package.json | 2 +- plugins/user-settings/report-alpha.api.md | 2 +- .../AuthProviders/DefaultProviderSettings.tsx | 9 + plugins/user-settings/src/translation.ts | 2 +- yarn.lock | 213 +++-- 301 files changed, 8375 insertions(+), 583 deletions(-) create mode 100644 .changeset/angry-heads-design.md create mode 100644 .changeset/better-eagles-tickle.md create mode 100644 .changeset/big-cameras-turn.md create mode 100644 .changeset/brave-jars-speak.md create mode 100644 .changeset/chilly-llamas-attend.md create mode 100644 .changeset/clear-houses-wonder.md create mode 100644 .changeset/cold-garlics-care.md create mode 100644 .changeset/create-app-1757429965.md create mode 100644 .changeset/dirty-spies-drop.md create mode 100644 .changeset/eighty-numbers-act.md create mode 100644 .changeset/eleven-doors-down.md create mode 100644 .changeset/eleven-doors-own.md create mode 100644 .changeset/funny-eagles-try.md create mode 100644 .changeset/giant-buttons-flash.md create mode 100644 .changeset/gold-words-smoke.md create mode 100644 .changeset/heavy-cats-unite.md create mode 100644 .changeset/heavy-lies-listen.md create mode 100644 .changeset/hot-friends-act.md create mode 100644 .changeset/hungry-carrots-grow.md create mode 100644 .changeset/itchy-moons-start.md create mode 100644 .changeset/kind-eyes-worry.md create mode 100644 .changeset/late-swans-press.md create mode 100644 .changeset/lemon-jobs-create.md create mode 100644 .changeset/lovely-actors-love.md create mode 100644 .changeset/quiet-papayas-mate.md create mode 100644 .changeset/red-shrimps-fall.md create mode 100644 .changeset/silent-results-stick.md create mode 100644 .changeset/stupid-areas-share.md create mode 100644 .changeset/tangy-squids-film.md create mode 100644 .changeset/ten-boxes-lie.md create mode 100644 .changeset/thin-phones-press.md create mode 100644 .changeset/tiny-spoons-mix.md create mode 100644 .changeset/tired-cobras-fly.md create mode 100644 .changeset/warm-emus-itch.md create mode 100644 .changeset/wet-onions-sneeze.md create mode 100644 .changeset/young-doodles-enter.md create mode 100644 docs/auth/openshift/provider.md create mode 100644 docs/releases/v1.43.0-next.2-changelog.md delete mode 100644 lerna.json create mode 100644 microsite/data/plugins/datacontract.yaml create mode 100644 microsite/src/components/pluginsSearch/pluginsSearch.tsx create mode 100644 packages/core-app-api/src/apis/implementations/auth/openshift/OpenShiftAuth.ts rename plugins/catalog-backend-module-puppetdb/src/alpha.ts => packages/core-app-api/src/apis/implementations/auth/openshift/index.ts (84%) create mode 100644 plugins/auth-backend-module-openshift-provider/.eslintrc.js create mode 100644 plugins/auth-backend-module-openshift-provider/README.md create mode 100644 plugins/auth-backend-module-openshift-provider/catalog-info.yaml create mode 100644 plugins/auth-backend-module-openshift-provider/config.d.ts create mode 100644 plugins/auth-backend-module-openshift-provider/dev/index.ts create mode 100644 plugins/auth-backend-module-openshift-provider/package.json create mode 100644 plugins/auth-backend-module-openshift-provider/report.api.md create mode 100644 plugins/auth-backend-module-openshift-provider/src/authenticator.test.ts create mode 100644 plugins/auth-backend-module-openshift-provider/src/authenticator.ts create mode 100644 plugins/auth-backend-module-openshift-provider/src/index.ts create mode 100644 plugins/auth-backend-module-openshift-provider/src/module.ts create mode 100644 plugins/auth-backend-module-openshift-provider/src/resolvers.ts create mode 100644 plugins/auth-backend/migrations/20250909120000_oidc_client_registration.js create mode 100644 plugins/auth-backend/src/database/OidcDatabase.test.ts create mode 100644 plugins/auth-backend/src/database/OidcDatabase.ts create mode 100644 plugins/auth-backend/src/service/OidcService.test.ts create mode 100644 plugins/auth/.eslintrc.js create mode 100644 plugins/auth/CHANGELOG.md create mode 100644 plugins/auth/README.md create mode 100644 plugins/auth/catalog-info.yaml create mode 100644 plugins/auth/dev/index.tsx create mode 100644 plugins/auth/package.json create mode 100644 plugins/auth/report.api.md create mode 100644 plugins/auth/src/components/ConsentPage/ConsentPage.tsx create mode 100644 plugins/auth/src/components/ConsentPage/index.ts create mode 100644 plugins/auth/src/components/ConsentPage/useConsentSession.ts create mode 100644 plugins/auth/src/components/Router.tsx create mode 100644 plugins/auth/src/index.ts create mode 100644 plugins/auth/src/plugin.test.ts create mode 100644 plugins/auth/src/plugin.tsx create mode 100644 plugins/auth/src/routes.ts create mode 100644 plugins/auth/src/setupTests.ts delete mode 100644 plugins/catalog-backend-module-puppetdb/report-alpha.api.md create mode 100644 plugins/techdocs-backend/examples/documented-component/docs/inner-component-docs/index.md create mode 100644 plugins/techdocs/src/reader/transformers/html/hooks/attributes.ts create mode 100644 plugins/techdocs/src/reader/transformers/html/hooks/metatags.ts create mode 100644 plugins/techdocs/src/reader/transformers/html/utils.ts diff --git a/.changeset/angry-heads-design.md b/.changeset/angry-heads-design.md new file mode 100644 index 0000000000..0b4a866fb1 --- /dev/null +++ b/.changeset/angry-heads-design.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Disallow import fallback of critical shared dependencies in module federation. diff --git a/.changeset/better-eagles-tickle.md b/.changeset/better-eagles-tickle.md new file mode 100644 index 0000000000..418f0bcae0 --- /dev/null +++ b/.changeset/better-eagles-tickle.md @@ -0,0 +1,5 @@ +--- +'@backstage/config-loader': patch +--- + +Allow using `BACKSTAGE_ENV` for loading environment specific config files diff --git a/.changeset/big-cameras-turn.md b/.changeset/big-cameras-turn.md new file mode 100644 index 0000000000..6226b17806 --- /dev/null +++ b/.changeset/big-cameras-turn.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Fixed cache namespace and key prefix separator configuration to properly use configured values instead of hardcoded plugin ID. The cache manager now correctly combines the configured namespace with plugin IDs using the configured separator for Redis and Valkey. Memcache and memory store continue to use plugin ID as namespace. diff --git a/.changeset/brave-jars-speak.md b/.changeset/brave-jars-speak.md new file mode 100644 index 0000000000..2060239326 --- /dev/null +++ b/.changeset/brave-jars-speak.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-puppetdb': patch +--- + +**BREAKING ALPHA**: The module has been moved from the `/alpha` export to the root of the package. diff --git a/.changeset/chilly-llamas-attend.md b/.changeset/chilly-llamas-attend.md new file mode 100644 index 0000000000..0a0876ccac --- /dev/null +++ b/.changeset/chilly-llamas-attend.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Updated dependency `dompurify` to `^3.2.4`. diff --git a/.changeset/clear-houses-wonder.md b/.changeset/clear-houses-wonder.md new file mode 100644 index 0000000000..46ada62fbe --- /dev/null +++ b/.changeset/clear-houses-wonder.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +TechDocs page titles have been improved, especially for deeply nested pages. diff --git a/.changeset/cold-garlics-care.md b/.changeset/cold-garlics-care.md new file mode 100644 index 0000000000..960ebe6bd4 --- /dev/null +++ b/.changeset/cold-garlics-care.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications': patch +--- + +Fixed missing app context when rendering the notifications view diff --git a/.changeset/create-app-1757429965.md b/.changeset/create-app-1757429965.md new file mode 100644 index 0000000000..b50d431d4b --- /dev/null +++ b/.changeset/create-app-1757429965.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped create-app version. diff --git a/.changeset/dirty-spies-drop.md b/.changeset/dirty-spies-drop.md new file mode 100644 index 0000000000..88bff91892 --- /dev/null +++ b/.changeset/dirty-spies-drop.md @@ -0,0 +1,5 @@ +--- +'@backstage/app-defaults': minor +--- + +Add and configure the OpenShift authentication provider to the default APIs. diff --git a/.changeset/eighty-numbers-act.md b/.changeset/eighty-numbers-act.md new file mode 100644 index 0000000000..dc7d504adc --- /dev/null +++ b/.changeset/eighty-numbers-act.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-react': patch +--- + +Update to documentation regarding TechDocs redirects. diff --git a/.changeset/eleven-doors-down.md b/.changeset/eleven-doors-down.md new file mode 100644 index 0000000000..0d380c8ddf --- /dev/null +++ b/.changeset/eleven-doors-down.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-mcp-actions-backend': patch +--- + +Proxy `/.well-known/oauth-authorization-server` to `/.well-known/openid-configuration` on `auth-backend` when `auth.experimentalDynamicClientRegistration.enabled` is enabled. diff --git a/.changeset/eleven-doors-own.md b/.changeset/eleven-doors-own.md new file mode 100644 index 0000000000..1da0297e5c --- /dev/null +++ b/.changeset/eleven-doors-own.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Implementing Dynamic Client Registration with the OIDC server. You can enable this by setting `auth.experimentalDynamicClientRegistration.enabled` in `app-config.yaml`. This is highly experimental, but feedback welcome. diff --git a/.changeset/funny-eagles-try.md b/.changeset/funny-eagles-try.md new file mode 100644 index 0000000000..c5550756e0 --- /dev/null +++ b/.changeset/funny-eagles-try.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-auth-react': patch +'@backstage/plugin-auth-node': patch +--- + +Updating plugin metadata diff --git a/.changeset/giant-buttons-flash.md b/.changeset/giant-buttons-flash.md new file mode 100644 index 0000000000..97564d37ac --- /dev/null +++ b/.changeset/giant-buttons-flash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth': minor +--- + +Initial publish of the `auth` frontend package diff --git a/.changeset/gold-words-smoke.md b/.changeset/gold-words-smoke.md new file mode 100644 index 0000000000..85141e402c --- /dev/null +++ b/.changeset/gold-words-smoke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app-backend': patch +--- + +Internal update to not expose the old `createRouter`. diff --git a/.changeset/heavy-cats-unite.md b/.changeset/heavy-cats-unite.md new file mode 100644 index 0000000000..4875093b0a --- /dev/null +++ b/.changeset/heavy-cats-unite.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-home': patch +--- + +Fixed race condition in CustomHomepageGrid by waiting for storage to load before rendering custom layout to prevent +rendering of the default content. diff --git a/.changeset/heavy-lies-listen.md b/.changeset/heavy-lies-listen.md new file mode 100644 index 0000000000..9f408b1ea8 --- /dev/null +++ b/.changeset/heavy-lies-listen.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +--- + +Fix a bug where `getDefault` in the `kubernetesFetcherExtensionPoint` had the wrong `this` value diff --git a/.changeset/hot-friends-act.md b/.changeset/hot-friends-act.md new file mode 100644 index 0000000000..37b2e3f112 --- /dev/null +++ b/.changeset/hot-friends-act.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': minor +--- + +Make `openshiftAuthApiRef` available in `@backstage/core-plugin-api`. diff --git a/.changeset/hungry-carrots-grow.md b/.changeset/hungry-carrots-grow.md new file mode 100644 index 0000000000..748ace8d94 --- /dev/null +++ b/.changeset/hungry-carrots-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': minor +--- + +Adding redirect handling for TechDocs URLs that reference entities that now reference an external entity for TechDocs. Including tests and documentation. diff --git a/.changeset/itchy-moons-start.md b/.changeset/itchy-moons-start.md new file mode 100644 index 0000000000..e5768b58b2 --- /dev/null +++ b/.changeset/itchy-moons-start.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Render a TechDocs link on the Scaffolder Template List page when templates include either `backstage.io/techdocs-ref` or `backstage.io/techdocs-entity` annotations, using the shared `buildTechDocsURL` helper. Also adds tests to verify both annotations and optional `backstage.io/techdocs-entity-path` are respected. diff --git a/.changeset/kind-eyes-worry.md b/.changeset/kind-eyes-worry.md new file mode 100644 index 0000000000..5568a00e28 --- /dev/null +++ b/.changeset/kind-eyes-worry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app': minor +--- + +Add implementation of OpenShift authentication provider. diff --git a/.changeset/late-swans-press.md b/.changeset/late-swans-press.md new file mode 100644 index 0000000000..987672bd77 --- /dev/null +++ b/.changeset/late-swans-press.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Prevent deadlock in catalog deferred stitching diff --git a/.changeset/lemon-jobs-create.md b/.changeset/lemon-jobs-create.md new file mode 100644 index 0000000000..d0e28b9c5c --- /dev/null +++ b/.changeset/lemon-jobs-create.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Add the OpenShift authenticator provider to the default `user-settings` providers page. diff --git a/.changeset/lovely-actors-love.md b/.changeset/lovely-actors-love.md new file mode 100644 index 0000000000..b428a11619 --- /dev/null +++ b/.changeset/lovely-actors-love.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-backend': minor +--- + +Adding new entity that specifies an external entity in the techdocs-entity annotation and updates to documentation regarding TechDocs redirects. diff --git a/.changeset/pre.json b/.changeset/pre.json index dcb0a48960..b1a840849d 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -201,23 +201,42 @@ "@backstage/plugin-techdocs-react": "1.3.2", "@backstage/plugin-user-settings": "0.8.25", "@backstage/plugin-user-settings-backend": "0.3.5", - "@backstage/plugin-user-settings-common": "0.0.1" + "@backstage/plugin-user-settings-common": "0.0.1", + "@backstage/plugin-auth": "0.0.0" }, "changesets": [ + "better-eagles-tickle", + "big-cameras-turn", "brave-bugs-know", + "brave-jars-speak", + "busy-chairs-itch", "cold-donuts-train", "cool-games-rescue", + "create-app-1757429965", "curvy-sites-rhyme", "easy-wings-turn", + "eleven-doors-down", + "eleven-doors-own", "fine-hands-think", + "fix-select-aria-props", "flat-colts-know", + "funny-eagles-try", + "giant-buttons-flash", "giant-zebras-peel", + "gold-words-smoke", + "heavy-cats-unite", + "heavy-lies-listen", "icy-camels-throw", + "itchy-moons-start", + "late-swans-press", "legal-lemons-attend", "lemon-terms-cheer", "lucky-glasses-slide", "mean-sites-cheer", + "olive-moons-burn", "puny-books-fetch", + "quiet-papayas-mate", + "red-shrimps-fall", "ripe-plants-pump", "sharp-carrots-spend", "sixty-pans-prove", @@ -226,9 +245,12 @@ "slick-worms-drum", "social-beers-unite", "sweet-lemons-wonder", + "tangy-squids-film", "tricky-buses-lead", + "warm-emus-itch", "wet-kiwis-strive", "whole-dingos-lay", - "yellow-dragons-float" + "yellow-dragons-float", + "young-doodles-enter" ] } diff --git a/.changeset/quiet-papayas-mate.md b/.changeset/quiet-papayas-mate.md new file mode 100644 index 0000000000..1d2089f4e8 --- /dev/null +++ b/.changeset/quiet-papayas-mate.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Dependency graph can now be opened in full screen mode diff --git a/.changeset/red-shrimps-fall.md b/.changeset/red-shrimps-fall.md new file mode 100644 index 0000000000..1ee3aca7ed --- /dev/null +++ b/.changeset/red-shrimps-fall.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Tool-tip text correction for the Theme selection in settings page diff --git a/.changeset/silent-results-stick.md b/.changeset/silent-results-stick.md new file mode 100644 index 0000000000..6cb501027c --- /dev/null +++ b/.changeset/silent-results-stick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Fixed a regression that prevented uploads greater than 100KB. Uploads up to 10MB are supported again. diff --git a/.changeset/stupid-areas-share.md b/.changeset/stupid-areas-share.md new file mode 100644 index 0000000000..2238f9d942 --- /dev/null +++ b/.changeset/stupid-areas-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Support Techdocs redirect with dompurify 3.2.6+ diff --git a/.changeset/tangy-squids-film.md b/.changeset/tangy-squids-film.md new file mode 100644 index 0000000000..acb5546031 --- /dev/null +++ b/.changeset/tangy-squids-film.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Fix scaffolder task log stream not having a minimum height diff --git a/.changeset/ten-boxes-lie.md b/.changeset/ten-boxes-lie.md new file mode 100644 index 0000000000..751b01e7d9 --- /dev/null +++ b/.changeset/ten-boxes-lie.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-openshift-provider': minor +--- + +Add new `auth-backend-module-openshift-provider`. This authentication provider enables Backstage to sign in with OpenShift. diff --git a/.changeset/thin-phones-press.md b/.changeset/thin-phones-press.md new file mode 100644 index 0000000000..8b3f985772 --- /dev/null +++ b/.changeset/thin-phones-press.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +Fixes issue with Github credentials provider which fails to match organization name if using allowedInstallationOwners diff --git a/.changeset/tiny-spoons-mix.md b/.changeset/tiny-spoons-mix.md new file mode 100644 index 0000000000..bb607aca3f --- /dev/null +++ b/.changeset/tiny-spoons-mix.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-addons-test-utils': minor +--- + +Adding catalogApiRef to test-utils to support catalog API usage by TechDocs reader page. diff --git a/.changeset/tired-cobras-fly.md b/.changeset/tired-cobras-fly.md new file mode 100644 index 0000000000..e2e19803f8 --- /dev/null +++ b/.changeset/tired-cobras-fly.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +Make `openshiftApiRef` available to the new frontend system. diff --git a/.changeset/warm-emus-itch.md b/.changeset/warm-emus-itch.md new file mode 100644 index 0000000000..a7bd51abdb --- /dev/null +++ b/.changeset/warm-emus-itch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Fix incorrect `defaultTarget` on `createComponentRouteRef`. diff --git a/.changeset/wet-onions-sneeze.md b/.changeset/wet-onions-sneeze.md new file mode 100644 index 0000000000..0fd61d764a --- /dev/null +++ b/.changeset/wet-onions-sneeze.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': minor +--- + +Add `OpenShiftAuth` helper to create default OAuth flow for OpenShift. diff --git a/.changeset/young-doodles-enter.md b/.changeset/young-doodles-enter.md new file mode 100644 index 0000000000..63e1f33eda --- /dev/null +++ b/.changeset/young-doodles-enter.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +update about card links style for pretty display with other language diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index 56167d6f9f..516eec516f 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -146,6 +146,7 @@ Expedia facto failover Fargate +faqs featureful Figma firehydrant @@ -460,6 +461,7 @@ subfolders subheader subheaders subkey +subpage subpath subroutes substring diff --git a/app-config.yaml b/app-config.yaml index 60842ee61a..eacf9a96aa 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -209,6 +209,11 @@ scaffolder: defaultCommitMessage: 'Initial commit' auth: + experimentalDynamicClientRegistration: + enabled: true + allowedRedirectUriPatterns: + - cursor://* + ### Add auth.keyStore.provider to more granularly control how to store JWK data when running # the auth-backend. # diff --git a/docs/auth/index.md b/docs/auth/index.md index 9f4c229e7f..bff1f3df26 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -34,6 +34,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) +- [OpenShift](openshift/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/openshift/provider.md b/docs/auth/openshift/provider.md new file mode 100644 index 0000000000..37c5064580 --- /dev/null +++ b/docs/auth/openshift/provider.md @@ -0,0 +1,130 @@ +--- +id: provider +title: OpenShift Authentication Provider +sidebar_label: OpenShift +description: Adding OpenShift OAuth as an authentication provider in Backstage +--- + +The Backstage `core-plugin-api` package comes with a OpenShift authentication +provider that can authenticate users using OpenShift OAuth. + +## Use Case + +This setup enables the [Kubernetes plugin](../../features/kubernetes/index.md) to access OpenShift clusters using the user's permissions, +leveraging OAuth 2.0 _On-Behalf-Of_ flow via the [Kubernetes Client Side Provider](../../features/kubernetes/authentication.md). + +To make this work, the corresponding `User` entities must exist in the Backstage catalog, +and their names must match the OpenShift users. + +Although the OpenShift authentication provider does not support OIDC natively, +you can still configure it for use with the Kubernetes integration by treating it as an OIDC provider +in the `KubernetesAuthProviders` configuration. + +```ts title="packages/app/src/apis.ts" +import { + KubernetesAuthProviders, + kubernetesAuthProvidersApiRef, +} from '@backstage/plugin-kubernetes'; +import { + googleAuthApiRef, + microsoftAuthApiRef, + openshiftAuthApiRef, +} from '@backstage/core-plugin-api'; + +export const apis: AnyApiFactory[] = [ + // ... + createApiFactory({ + api: kubernetesAuthProvidersApiRef, + deps: { + microsoftAuthApi: microsoftAuthApiRef, + googleAuthApi: googleAuthApiRef, + openshiftAuthApi: openshiftAuthApiRef, + }, + factory({ microsoftAuthApi, googleAuthApi, openshiftAuthApi }) { + return new KubernetesAuthProviders({ + microsoftAuthApi, + googleAuthApi, + oidcProviders: { + openshift: { + async getIdToken(_) { + return await openshiftAuthApi.getAccessToken('user:full'); + }, + }, + }, + }); + }, + }), + //... +]; +``` + +:::note Note + +The OpenShift auth API does **not** implement the `OpenIdConnectApi` interface. In other words, it does **not** return an ID token. +Instead, it returns an **access token**, which is used by the Kubernetes integration in place of an ID token. +This is the only functional difference from the standard OIDC-based authentication flow. + +::: + +## Create an OAuth client in OpenShift + +Make sure that an OAuth client exists in the OpenShift cluster. + +To configure the OpenShift integration, create an [`OAuthClient`](https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/authentication_and_authorization/configuring-oauth-clients). + +The redirect URI must be in the following format: `https:///api/auth/openshift/handler/frame`. + +## Configuration + +The provider configuration can then be added to your `app-config.yaml` under the +root `auth` configuration: + +```yaml +auth: + environment: development + providers: + openshift: + development: + clientId: ${AUTH_OPENSHIFT_CLIENT_ID} + clientSecret: ${AUTH_OPENSHIFT_CLIENT_SECRET} + authorizationUrl: ${AUTH_OPENSHIFT_AUTHORIZATION_URL} + tokenUrl: ${AUTH_OPENSHIFT_TOKEN_URL} + openshiftApiServerUrl: ${OPENSHIFT_API_SERVER_URL} + ## uncomment to set lifespan of user session + # sessionDuration: { hours: 24 } # supports `ms` library format (e.g. '24h', '2 days'), ISO duration, "human duration" as used in code + # sessionDuration: 1d + signIn: + resolvers: + - resolver: displayNameMatchingUserEntityName +``` + +The OpenShift provider is a structure with these configuration keys: + +- `clientId`: The client ID of your OpenShift OAuth client, e.g., `my-backstage` +- `clientSecret`: The client secret tied to the OpenShift OAuth client. +- `authorizationUrl`: The OpenShift OAuth client auth endpoint, format: `https:///oauth/authorize`. +- `tokenUrl`: The OpenShift OAuth client token endpoint, format: `https:///oauth/token`. +- `openshiftApiServerUrl`: The OpenShift API server endpoint, format: `https://`. +- `sessionDuration`: (optional): Lifespan of the user session. +- `signIn`: The configuration for the sign-in process, including the **resolvers** + that should be used to match the user from the auth provider with the user + entity in the Backstage catalog (typically a single resolver is sufficient). + +The provider needs to use the scope **user:full**. + +## Backend Installation + +To add the provider to the backend we will first need to install the package by running this command: + +```bash title="from your Backstage root directory" +yarn --cwd packages/backend add @backstage/plugin-auth-backend-module-openshift-provider +``` + +Then we will need to add this line: + +```ts title="in packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-auth-backend')); +/* highlight-add-start */ +backend.add(import('@backstage/plugin-auth-backend-module-openshift-provider')); +/* highlight-add-end */ +``` diff --git a/docs/backend-system/core-services/actions-registry.md b/docs/backend-system/core-services/actions-registry.md index b1e8fe5f3d..9e493e6bb2 100644 --- a/docs/backend-system/core-services/actions-registry.md +++ b/docs/backend-system/core-services/actions-registry.md @@ -45,7 +45,7 @@ When an action is executed, it receives a context object (`ActionsRegistryAction Here's an example of how to register an action with the Actions Registry Service: ```typescript -import { ActionsRegistryService } from '@backstage/backend-plugin-api'; +import { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha'; export function registerMyActions(actionsRegistry: ActionsRegistryService) { // Register a simple read-only action diff --git a/docs/backend-system/core-services/cache.md b/docs/backend-system/core-services/cache.md index 0e26e8e1f5..ad4d04b50a 100644 --- a/docs/backend-system/core-services/cache.md +++ b/docs/backend-system/core-services/cache.md @@ -7,6 +7,39 @@ description: Documentation for the Cache service This service lets your plugin interact with a cache. It is bound to your plugin too, so that you will only set and get values in your plugin's private namespace. +## Configuration + +The cache service can be configured using the `backend.cache` section in your `app-config.yaml`: + +```yaml +backend: + cache: + store: redis # or 'valkey', 'memcache', 'memory' + connection: redis://localhost:6379 + + # Store-specific configuration (Redis/Valkey only) + redis: + client: + # Optional: Global namespace prefix for all cache keys + namespace: 'my-app' + # Optional: Separator used between namespace and plugin ID (default: ':') + keyPrefixSeparator: ':' + # Other Redis-specific options... + clearBatchSize: 1000 + useUnlink: false +``` + +### Namespace Configuration + +For Redis and Valkey stores, you can configure a global namespace that will be prefixed to all cache keys: + +- **Without namespace**: Cache keys use only the plugin ID (e.g., `catalog:some-key`) +- **With namespace**: Cache keys use the format `namespace:pluginId:key` (e.g., `my-app:catalog:some-key`) + +The `keyPrefixSeparator` controls what character is used between the namespace and plugin ID (defaults to `:`). + +**Note**: Memory and Memcache stores do not support namespace configuration and will always use the plugin ID directly. + ## Using the service The following example shows how to get a cache client in your `example` backend plugin and setting and getting values from the cache. diff --git a/docs/conf/index.md b/docs/conf/index.md index 659ffccfe3..029c5b88fd 100644 --- a/docs/conf/index.md +++ b/docs/conf/index.md @@ -16,10 +16,24 @@ allowing for customization. ## Supplying Configuration Configuration is stored in YAML files where the defaults are `app-config.yaml` -and `app-config.local.yaml` for local overrides. Other sets of files can by -loaded by passing `--config ` flags. The configuration files themselves -contain plain YAML, but with support for loading in data and secrets from -various sources using for example `$env` and `$file` keys. +and `app-config.local.yaml` for local overrides. Additionally, it is possible +to define environment based configuration files with `BACKSTAGE_ENV` +environment variable, which will load `app-config..yaml`. + +Loading order of these files is as follows: + +1. `app-config.yaml` +2. `app-config..yaml` +3. `app-config.local.yaml` +4. `app-config..local.yaml` + +Other sets of files can by loaded by passing `--config ` flags. +Read more about the configuration loading order in the +[Configuration Files](./writing.md#configuration-files) section. + +The configuration files themselves contain plain YAML, but with support for +loading in data and secrets from various sources using for example +`$env` and `$file` keys. It is also possible to supply configuration through environment variables, for example `APP_CONFIG_app_baseUrl=https://staging.example.com`. However these diff --git a/docs/contribute/project-structure.md b/docs/contribute/project-structure.md index 5b86b655b2..f58afedee8 100644 --- a/docs/contribute/project-structure.md +++ b/docs/contribute/project-structure.md @@ -221,7 +221,3 @@ future. - [`catalog-info.yaml`](https://github.com/backstage/backstage/tree/master/catalog-info.yaml) - Description of Backstage in the Backstage Entity format. - -- [`lerna.json`](https://github.com/backstage/backstage/tree/master/lerna.json) - - [Lerna](https://github.com/lerna/lerna) monorepo config. We are using - `yarn workspaces`, so this will only be used for executing scripts. diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index cdd93b50ac..d8a2e1d232 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -126,7 +126,7 @@ metadata: The value of this annotation informs of the path to this component's TechDocs within an external entity that owns the TechDocs. In conjunction with [backstage.io/techdocs-entity](#backstageiotechdocs-entity) this allows for deep linking into the TechDocs of -another entity, not just linking to the root of another entities TechDocs. +another entity, not just linking to the root of another entity's TechDocs. ### backstage.io/view-url, backstage.io/edit-url diff --git a/docs/features/techdocs/FAQ.md b/docs/features/techdocs/FAQ.md index 117471f749..9c78e3e123 100644 --- a/docs/features/techdocs/FAQ.md +++ b/docs/features/techdocs/FAQ.md @@ -12,6 +12,8 @@ This page answers frequently asked questions about [TechDocs](README.md). - [What static site generator is TechDocs using?](#what-static-site-generator-is-techdocs-using) - [What is the mkdocs-techdocs-core plugin?](#what-is-the-mkdocs-techdocs-core-plugin) - [Does TechDocs support file formats other than Markdown (e.g. RST, AsciiDoc)?](#does-techdocs-support-file-formats-other-than-markdown-eg-rst-asciidoc-) +- [What should be the value of `backstage.io/techdocs-ref` when using external build and storage?](#what-should-be-the-value-of-backstageiotechdocs-ref-when-using-external-build-and-storage) +- [Is it possible for users to suggest changes or provide feedback on a TechDocs page?](#is-it-possible-for-users-to-suggest-changes-or-provide-feedback-on-a-techdocs-page) #### What static site generator is TechDocs using? @@ -46,6 +48,10 @@ annotation should still be present in entity descriptor file (e.g. `catalog-info.yaml`) for Backstage to know that TechDocs is enabled for the entity. +#### What happens when you navigate to a TechDocs URL for an entity uses the `backstage.io/techdocs-entity` annotation? + +If you navigate to a TechDocs URL in the format `docs/{namespace}/{kind}/{name}` for an entity that has the `backstage.io/techdocs-entity` annotation (instead of the `backstage.io/techdocs-ref` annotation), then Backstage will redirect to the TechDocs page of the entity referenced in the value of that annotation. + #### Is it possible for users to suggest changes or provide feedback on a TechDocs page? This is supported for TechDocs sites whose source code is hosted in either diff --git a/docs/frontend-system/building-plugins/01-index.md b/docs/frontend-system/building-plugins/01-index.md index 283c162fa7..03da20ea73 100644 --- a/docs/frontend-system/building-plugins/01-index.md +++ b/docs/frontend-system/building-plugins/01-index.md @@ -185,7 +185,7 @@ export const examplePlugin = createFrontendPlugin({ ## Plugin specific extensions -There are many different plugins that you can extend with additional functionality through extensions. One such plugin is [the catalog plugin](../../features/software-catalog/), one of the core features of Backstage. It lets you catalog the software in your organization, where each item in the catalog has its own page that can be populated with tools and information relating to that catalog entity. In this example we will explore how our plugin can provide such a tool to display on an entity page. +There are many different plugins that you can extend with additional functionality through extensions. One such plugin is [the catalog plugin](https://backstage.io/docs/features/software-catalog/), one of the core features of Backstage. It lets you catalog the software in your organization, where each item in the catalog has its own page that can be populated with tools and information relating to that catalog entity. In this example we will explore how our plugin can provide such a tool to display on an entity page. ```tsx title="in src/plugin.ts - An example entity content extension" import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha'; diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index cd86aa3343..252884dc54 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -31,7 +31,7 @@ This guide also assumes a basic understanding of working on a Linux based operat [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/) - A GNU-like build environment available at the command line. For example, on Debian/Ubuntu you will want to have the `make` and `build-essential` packages installed. - On macOS, you will want to have run `xcode-select --install` to get the XCode command line build tooling in place. + On macOS, you will want to run `xcode-select --install` to get the XCode command line build tooling in place. - An account with elevated rights to install the dependencies - `curl` or `wget` installed - Node.js [Active LTS Release](../overview/versioning-policy.md#nodejs-releases) installed using one of these @@ -58,9 +58,9 @@ The Backstage app we'll be creating will only have demo data until we set up int ::: -To install the Backstage Standalone app, we will make use of `npx`. `npx` is a tool that comes preinstalled with Node.js and lets you run commands straight from `npm` or other registries. Before we jump in to running the command, let's chat about what it does. +To install the Backstage Standalone app, we will make use of `npx`. `npx` is a tool that comes preinstalled with Node.js and lets you run commands straight from `npm` or other registries. Before we run the command, let's discuss what it does. -This command will create a new directory with a Backstage app inside. The wizard will ask you for the name of the app. This name will be created as sub directory in your current working directory. +This command will create a new directory with a Backstage app inside. The wizard will ask you for the name of the app. This name will be created as subdirectory in your current working directory. ![create app](../assets/getting-started/create-app-output.png) @@ -89,8 +89,8 @@ app - **package.json**: Root package.json for the project. _Note: Be sure that you don't add any npm dependencies here as they probably should be installed in the intended workspace rather than in the root._ -- **packages/**: Lerna leaf packages or "workspaces". Everything here is going - to be a separate package, managed by lerna. +- **packages/**: Yarn workspaces, everything here is going + to be a separate package, managed by Yarn. - **packages/app/**: A fully functioning Backstage frontend app that acts as a good starting point for you to get to know Backstage. - **packages/backend/**: We include a backend that helps power features such as diff --git a/docs/references/glossary.md b/docs/references/glossary.md index c955d9a5ae..ad9e2bbee5 100644 --- a/docs/references/glossary.md +++ b/docs/references/glossary.md @@ -208,7 +208,7 @@ One of the [packages](#package) within a [monorepo](#monorepo). A package may or 1. A single repository for a collection of related software projects, such as all projects belonging to an organization. -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/) +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 [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/) ## Name diff --git a/docs/releases/v1.43.0-next.2-changelog.md b/docs/releases/v1.43.0-next.2-changelog.md new file mode 100644 index 0000000000..ef7ddb1c1f --- /dev/null +++ b/docs/releases/v1.43.0-next.2-changelog.md @@ -0,0 +1,695 @@ +# Release v1.43.0-next.2 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.43.0-next.2](https://backstage.github.io/upgrade-helper/?to=1.43.0-next.2) + +## @backstage/catalog-client@1.12.0-next.0 + +### Minor Changes + +- 0e9ec44: Introduced new `streamEntities` async generator method for the catalog. + + Catalog API and Catalog Service now includes a `streamEntities` method that allows for streaming entities from the catalog. + This method is designed to handle large datasets efficiently by processing entities in a stream rather than loading them + all into memory at once. This is useful when you need to fetch a large number of entities but do not want to use pagination + or fetch all entities at once. + + Example usage: + + ```ts + const pageStream = catalogClient.streamEntities({ pageSize: 100 }, { token }); + for await (const page of pageStream) { + // Handle page of entities + for (const entity of page) { + console.log(entity); + } + } + ``` + +## @backstage/plugin-auth@0.1.0-next.0 + +### Minor Changes + +- 54ddfef: Initial publish of the `auth` frontend package + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.6-next.1 + +## @backstage/plugin-catalog-node@1.19.0-next.1 + +### Minor Changes + +- 0e9ec44: Introduced new `streamEntities` async generator method for the catalog. + + Catalog API and Catalog Service now includes a `streamEntities` method that allows for streaming entities from the catalog. + This method is designed to handle large datasets efficiently by processing entities in a stream rather than loading them + all into memory at once. This is useful when you need to fetch a large number of entities but do not want to use pagination + or fetch all entities at once. + + Example usage: + + ```ts + const pageStream = catalogClient.streamEntities({ pageSize: 100 }, { token }); + for await (const page of pageStream) { + // Handle page of entities + for (const entity of page) { + console.log(entity); + } + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + +## @backstage/plugin-catalog-react@1.21.0-next.2 + +### Minor Changes + +- 0e9ec44: Introduced new `streamEntities` async generator method for the catalog. + + Catalog API and Catalog Service now includes a `streamEntities` method that allows for streaming entities from the catalog. + This method is designed to handle large datasets efficiently by processing entities in a stream rather than loading them + all into memory at once. This is useful when you need to fetch a large number of entities but do not want to use pagination + or fetch all entities at once. + + Example usage: + + ```ts + const pageStream = catalogClient.streamEntities({ pageSize: 100 }, { token }); + for await (const page of pageStream) { + // Handle page of entities + for (const entity of page) { + console.log(entity); + } + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/backend-defaults@0.12.1-next.1 + +### Patch Changes + +- 4eda590: Fixed cache namespace and key prefix separator configuration to properly use configured values instead of hardcoded plugin ID. The cache manager now correctly combines the configured namespace with plugin IDs using the configured separator for Redis and Valkey. Memcache and memory store continue to use plugin ID as namespace. +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/integration-aws-node@0.1.17 + +## @backstage/backend-dynamic-feature-service@0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-backend@3.0.2-next.1 + - @backstage/plugin-app-node@0.1.37-next.1 + +## @backstage/cli@0.34.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/integration@1.18.0-next.0 + +## @backstage/config-loader@1.10.3-next.0 + +### Patch Changes + +- a73f495: Allow using `BACKSTAGE_ENV` for loading environment specific config files + +## @backstage/core-compat-api@0.5.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + +## @backstage/core-components@0.17.6-next.1 + +### Patch Changes + +- 1ad3d94: Dependency graph can now be opened in full screen mode +- ae7d426: update about card links style for pretty display with other language + +## @backstage/create-app@0.7.4-next.2 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/dev-utils@1.1.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + +## @backstage/repo-tools@0.15.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + +## @backstage/ui@0.7.1-next.0 + +### Patch Changes + +- 7307930: Add missing class for flex: baseline +- 89da341: Fix Select component to properly attach aria-label and aria-labelledby props to the rendered element for improved accessibility. + +## @backstage/plugin-api-docs@0.12.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-app-backend@0.5.6-next.1 + +### Patch Changes + +- afd368e: Internal update to not expose the old `createRouter`. +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-app-node@0.1.37-next.1 + +## @backstage/plugin-app-node@0.1.37-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + +## @backstage/plugin-auth-backend@0.25.4-next.1 + +### Patch Changes + +- 1d47bf3: Implementing Dynamic Client Registration with the OIDC server. You can enable this by setting `auth.experimentalDynamicClientRegistration.enabled` in `app-config.yaml`. This is highly experimental, but feedback welcome. +- 54ddfef: Updating plugin metadata +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-auth-node@0.6.7-next.1 + +### Patch Changes + +- 54ddfef: Updating plugin metadata +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + +## @backstage/plugin-auth-react@0.1.19-next.1 + +### Patch Changes + +- 54ddfef: Updating plugin metadata +- Updated dependencies + - @backstage/core-components@0.17.6-next.1 + +## @backstage/plugin-catalog@1.31.3-next.2 + +### Patch Changes + +- 85c5e04: Fix incorrect `defaultTarget` on `createComponentRouteRef`. +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-catalog-backend@3.0.2-next.1 + +### Patch Changes + +- 2204f5b: Prevent deadlock in catalog deferred stitching +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-aws@0.4.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/integration-aws-node@0.1.17 + +## @backstage/plugin-catalog-backend-module-azure@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-gitea@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-github@0.11.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.0.2-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-catalog-backend-module-github@0.11.0-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.7.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.7.3-next.1 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-catalog-backend@3.0.2-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-backend-module-ldap@0.11.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.8.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.14-next.1 + +### Patch Changes + +- afd368e: **BREAKING ALPHA**: The module has been moved from the `/alpha` export to the root of the package. +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.6.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-catalog-graph@0.4.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-catalog-import@0.13.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-devtools-backend@0.5.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/backend-defaults@0.12.1-next.1 + +## @backstage/plugin-home@0.8.12-next.2 + +### Patch Changes + +- 929c55a: Fixed race condition in CustomHomepageGrid by waiting for storage to load before rendering custom layout to prevent + rendering of the default content. +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-kubernetes@0.12.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-kubernetes-backend@0.20.2-next.2 + +### Patch Changes + +- dd7b6d2: Fix a bug where `getDefault` in the `kubernetesFetcherExtensionPoint` had the wrong `this` value +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration-aws-node@0.1.17 + +## @backstage/plugin-kubernetes-cluster@0.0.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + +## @backstage/plugin-mcp-actions-backend@0.1.3-next.1 + +### Patch Changes + +- 1d47bf3: Proxy `/.well-known/oauth-authorization-server` to `/.well-known/openid-configuration` on `auth-backend` when `auth.experimentalDynamicClientRegistration.enabled` is enabled. +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-notifications-backend@0.5.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-notifications-node@0.2.19-next.1 + +## @backstage/plugin-notifications-backend-module-email@0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-notifications-node@0.2.19-next.1 + +## @backstage/plugin-notifications-backend-module-slack@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-notifications-node@0.2.19-next.1 + +## @backstage/plugin-notifications-node@0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + +## @backstage/plugin-org@0.6.44-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-org-react@0.1.42-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + +## @backstage/plugin-scaffolder@1.34.1-next.2 + +### Patch Changes + +- 0d415ae: Render a TechDocs link on the Scaffolder Template List page when templates include either `backstage.io/techdocs-ref` or `backstage.io/techdocs-entity` annotations, using the shared `buildTechDocsURL` helper. Also adds tests to verify both annotations and optional `backstage.io/techdocs-entity-path` are respected. +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-scaffolder-react@1.19.1-next.2 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-scaffolder-backend@2.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.12-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.8.3-next.1 + +## @backstage/plugin-scaffolder-backend-module-github@0.8.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + +## @backstage/plugin-scaffolder-react@1.19.1-next.2 + +### Patch Changes + +- 58fc108: Fix scaffolder task log stream not having a minimum height +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + +## @backstage/plugin-search@1.4.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-search-backend-module-catalog@0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-search-backend-module-techdocs@0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + +## @backstage/plugin-techdocs@1.14.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-react@0.1.19-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-compat-api@0.5.2-next.2 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.53-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/plugin-techdocs@1.14.2-next.2 + +## @backstage/plugin-techdocs-backend@2.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.6-next.1 + +## @backstage/plugin-user-settings@0.8.26-next.2 + +### Patch Changes + +- b713b54: Tool-tip text correction for the Theme selection in settings page +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + +## example-app@0.2.113-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.1-next.0 + - @backstage/plugin-auth-react@0.1.19-next.1 + - @backstage/plugin-home@0.8.12-next.2 + - @backstage/plugin-scaffolder@1.34.1-next.2 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-user-settings@0.8.26-next.2 + - @backstage/plugin-scaffolder-react@1.19.1-next.2 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/cli@0.34.2-next.2 + - @backstage/plugin-catalog-graph@0.4.23-next.2 + - @backstage/plugin-catalog-import@0.13.5-next.2 + - @backstage/plugin-org@0.6.44-next.2 + - @backstage/plugin-techdocs@1.14.2-next.2 + - @backstage/plugin-api-docs@0.12.11-next.2 + - @backstage/plugin-kubernetes@0.12.11-next.2 + - @backstage/plugin-kubernetes-cluster@0.0.29-next.2 + - @backstage/plugin-search@1.4.30-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.28-next.0 + +## example-app-next@0.0.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.1-next.0 + - @backstage/plugin-auth-react@0.1.19-next.1 + - @backstage/plugin-auth@0.1.0-next.0 + - @backstage/plugin-home@0.8.12-next.2 + - @backstage/plugin-scaffolder@1.34.1-next.2 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-user-settings@0.8.26-next.2 + - @backstage/plugin-scaffolder-react@1.19.1-next.2 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/cli@0.34.2-next.2 + - @backstage/plugin-catalog-graph@0.4.23-next.2 + - @backstage/plugin-catalog-import@0.13.5-next.2 + - @backstage/plugin-org@0.6.44-next.2 + - @backstage/plugin-techdocs@1.14.2-next.2 + - @backstage/core-compat-api@0.5.2-next.2 + - @backstage/plugin-api-docs@0.12.11-next.2 + - @backstage/plugin-kubernetes@0.12.11-next.2 + - @backstage/plugin-kubernetes-cluster@0.0.29-next.2 + - @backstage/plugin-search@1.4.30-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.28-next.0 diff --git a/lerna.json b/lerna.json deleted file mode 100644 index 4621689664..0000000000 --- a/lerna.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "packages": ["packages/*", "plugins/*"], - "npmClient": "yarn", - "useWorkspaces": true, - "version": "0.0.0" -} diff --git a/microsite/data/plugins/datacontract.yaml b/microsite/data/plugins/datacontract.yaml new file mode 100644 index 0000000000..a267730496 --- /dev/null +++ b/microsite/data/plugins/datacontract.yaml @@ -0,0 +1,10 @@ +--- +title: Data Contract +author: Jan Remunda +authorUrl: https://www.remunda.cz/#backstage +category: Discovery +description: Ingest and visualize Data Contracts to your catalog via API Entities. +documentation: https://www.npmjs.com/package/@remunda/backstage-plugin-datacontract +iconUrl: https://www.remunda.cz/logos/datacontract.png +npmPackageName: '@remunda/backstage-plugin-datacontract' +addedDate: '2025-08-19' diff --git a/microsite/src/components/pluginsSearch/pluginsSearch.tsx b/microsite/src/components/pluginsSearch/pluginsSearch.tsx new file mode 100644 index 0000000000..68f9a2831a --- /dev/null +++ b/microsite/src/components/pluginsSearch/pluginsSearch.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +type Props = { + searchTerm: string; + onSearchTermChange: (newTerm: string) => void; +}; + +export const PluginsSearch = (props: Props) => { + const { searchTerm, onSearchTermChange } = props; + return ( + onSearchTermChange((e.target as HTMLInputElement).value)} + value={searchTerm} + placeholder="Search plugins..." + className="DocSearch-Input search" + /> + ); +}; diff --git a/microsite/src/pages/plugins/index.tsx b/microsite/src/pages/plugins/index.tsx index 2abdbabf4b..1a87f9c0af 100644 --- a/microsite/src/pages/plugins/index.tsx +++ b/microsite/src/pages/plugins/index.tsx @@ -5,10 +5,11 @@ import { truncateDescription } from '@site/src/util/truncateDescription'; import { ChipCategory } from '@site/src/util/types'; import Layout from '@theme/Layout'; import clsx from 'clsx'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { IPluginData, PluginCard } from './_pluginCard'; import pluginsStyles from './plugins.module.scss'; +import { PluginsSearch } from '@site/src/components/pluginsSearch/pluginsSearch'; interface IPluginsList { corePlugins: IPluginData[]; @@ -55,6 +56,7 @@ const Plugins = () => { const [selectedCategories, setSelectedCategories] = useState([]); const [showCoreFeatures, setShowCoreFeatures] = useState(true); const [showOtherPlugins, setShowOtherPlugins] = useState(true); + const [searchTerm, setSearchTerm] = useState(''); const handleChipClick = (categoryName: string) => { const isSelected = @@ -97,6 +99,34 @@ const Plugins = () => { } }; + const matchesSearch = (pluginData: IPluginData, term: string) => { + if (!term) return true; + const lowerTerm = term.toLowerCase(); + return ( + pluginData.title.toLowerCase().includes(lowerTerm) || + pluginData.description.toLowerCase().includes(lowerTerm) || + pluginData.category.toLowerCase().includes(lowerTerm) || + (pluginData.author && pluginData.author.toLowerCase().includes(lowerTerm)) + ); + }; + + const matchesCategory = (pluginData: IPluginData, categories: string[]) => { + if (categories.length === 0) return true; + return categories.includes(pluginData.category); + }; + + const corePlugins = useMemo(() => { + return plugins.corePlugins + .filter(pluginData => matchesCategory(pluginData, selectedCategories)) + .filter(pluginData => matchesSearch(pluginData, searchTerm)); + }, [selectedCategories, searchTerm]); + + const otherPlugins = useMemo(() => { + return plugins.otherPlugins + .filter(pluginData => matchesCategory(pluginData, selectedCategories)) + .filter(pluginData => matchesSearch(pluginData, searchTerm)); + }, [selectedCategories, searchTerm]); + return (
{ categories={categories} handleChipClick={handleChipClick} /> +
- {showCoreFeatures && ( + {corePlugins.length === 0 && otherPlugins.length === 0 && ( +
+

No plugins found

+

+ We couldn't find any plugins matching your criteria. Please try + adjusting your search or filter settings. +

+
+ )} + + {showCoreFeatures && corePlugins.length > 0 && (
-

Core Features

+

Core Features ({corePlugins.length})

- {plugins.corePlugins - .filter( - pluginData => - !selectedCategories.length || - selectedCategories.includes(pluginData.category), - ) - .map(pluginData => ( - - ))} + {corePlugins.map(pluginData => ( + + ))}
)} - {showOtherPlugins && ( + {showOtherPlugins && otherPlugins.length > 0 && (
-

All Plugins

+

All Plugins ({otherPlugins.length})

Friendly reminder: While we love the variety and contributions of our open source plugins, they haven't been fully vetted by the @@ -158,18 +193,9 @@ const Plugins = () => { your due diligence before installing. Happy exploring!

- {plugins.otherPlugins - .filter( - pluginData => - !selectedCategories.length || - selectedCategories.includes(pluginData.category), - ) - .map(pluginData => ( - - ))} + {otherPlugins.map(pluginData => ( + + ))}
)} diff --git a/microsite/src/pages/plugins/plugins.module.scss b/microsite/src/pages/plugins/plugins.module.scss index fb17e90625..a17e4bc5c0 100644 --- a/microsite/src/pages/plugins/plugins.module.scss +++ b/microsite/src/pages/plugins/plugins.module.scss @@ -76,6 +76,20 @@ height: 1rem; } + :global(.search) { + float: right; + margin-bottom: 1rem; + margin-right: 1rem; + font-size: calc(0.875rem * var(--ifm-button-size-multiplier)); + font-weight: var(--ifm-button-font-weight); + background-color: transparent; + border: var(--ifm-button-border-width) solid var(--ifm-color-primary); + border-radius: var(--ifm-button-border-radius); + color: var(--ifm-color-primary); + width: 220px; + height: 2.2rem; + } + :global(.dropdown) { float: right; margin-bottom: 1rem; diff --git a/package.json b/package.json index d4ebc22bc1..7aed18ab74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.43.0-next.1", + "version": "1.43.0-next.2", "backstage": { "cli": { "new": { diff --git a/packages/app-defaults/src/defaults/apis.ts b/packages/app-defaults/src/defaults/apis.ts index 63ace7b4d5..97989dff91 100644 --- a/packages/app-defaults/src/defaults/apis.ts +++ b/packages/app-defaults/src/defaults/apis.ts @@ -35,6 +35,7 @@ import { FetchMiddlewares, VMwareCloudAuth, FrontendHostDiscovery, + OpenShiftAuth, } from '@backstage/core-app-api'; import { @@ -58,6 +59,7 @@ import { bitbucketServerAuthApiRef, atlassianAuthApiRef, vmwareCloudAuthApiRef, + openshiftAuthApiRef, } from '@backstage/core-plugin-api'; import { permissionApiRef, @@ -275,6 +277,22 @@ export const apis = [ }); }, }), + createApiFactory({ + api: openshiftAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return OpenShiftAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), createApiFactory({ api: permissionApiRef, deps: { diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 5fc353c09d..a4e2386525 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,32 @@ # example-app-next +## 0.0.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.1-next.0 + - @backstage/plugin-auth-react@0.1.19-next.1 + - @backstage/plugin-auth@0.1.0-next.0 + - @backstage/plugin-home@0.8.12-next.2 + - @backstage/plugin-scaffolder@1.34.1-next.2 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-user-settings@0.8.26-next.2 + - @backstage/plugin-scaffolder-react@1.19.1-next.2 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/cli@0.34.2-next.2 + - @backstage/plugin-catalog-graph@0.4.23-next.2 + - @backstage/plugin-catalog-import@0.13.5-next.2 + - @backstage/plugin-org@0.6.44-next.2 + - @backstage/plugin-techdocs@1.14.2-next.2 + - @backstage/core-compat-api@0.5.2-next.2 + - @backstage/plugin-api-docs@0.12.11-next.2 + - @backstage/plugin-kubernetes@0.12.11-next.2 + - @backstage/plugin-kubernetes-cluster@0.0.29-next.2 + - @backstage/plugin-search@1.4.30-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.28-next.0 + ## 0.0.27-next.1 ### Patch Changes diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index f83e4d4ba0..35380c961f 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -4,7 +4,6 @@ app: routes: bindings: catalog.viewTechDoc: techdocs.docRoot - catalog.createComponent: catalog-import.importPage org.catalogIndex: catalog.catalogIndex pluginOverrides: diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 44602b2438..49ce264f06 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.27-next.1", + "version": "0.0.27-next.2", "backstage": { "role": "frontend" }, @@ -49,6 +49,7 @@ "@backstage/plugin-api-docs": "workspace:^", "@backstage/plugin-app": "workspace:^", "@backstage/plugin-app-visualizer": "workspace:^", + "@backstage/plugin-auth": "workspace:^", "@backstage/plugin-auth-react": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 93dd1db16a..f3b911accf 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,30 @@ # example-app +## 0.2.113-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.1-next.0 + - @backstage/plugin-auth-react@0.1.19-next.1 + - @backstage/plugin-home@0.8.12-next.2 + - @backstage/plugin-scaffolder@1.34.1-next.2 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-user-settings@0.8.26-next.2 + - @backstage/plugin-scaffolder-react@1.19.1-next.2 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/cli@0.34.2-next.2 + - @backstage/plugin-catalog-graph@0.4.23-next.2 + - @backstage/plugin-catalog-import@0.13.5-next.2 + - @backstage/plugin-org@0.6.44-next.2 + - @backstage/plugin-techdocs@1.14.2-next.2 + - @backstage/plugin-api-docs@0.12.11-next.2 + - @backstage/plugin-kubernetes@0.12.11-next.2 + - @backstage/plugin-kubernetes-cluster@0.0.29-next.2 + - @backstage/plugin-search@1.4.30-next.2 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.28-next.0 + ## 0.2.113-next.1 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 580c401a1b..5e38aa6256 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.113-next.1", + "version": "0.2.113-next.2", "backstage": { "role": "frontend" }, diff --git a/packages/app/src/identityProviders.ts b/packages/app/src/identityProviders.ts index 66f1460210..372477cd63 100644 --- a/packages/app/src/identityProviders.ts +++ b/packages/app/src/identityProviders.ts @@ -23,6 +23,7 @@ import { oneloginAuthApiRef, bitbucketAuthApiRef, bitbucketServerAuthApiRef, + openshiftAuthApiRef, } from '@backstage/core-plugin-api'; export const providers = [ @@ -74,4 +75,10 @@ export const providers = [ message: 'Sign In using Bitbucket Server', apiRef: bitbucketServerAuthApiRef, }, + { + id: 'openshift-auth-provider', + title: 'OpenShift', + message: 'Sign In using OpenShift', + apiRef: openshiftAuthApiRef, + }, ]; diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 0375e54232..b4c7393455 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/backend-defaults +## 0.12.1-next.1 + +### Patch Changes + +- 4eda590: Fixed cache namespace and key prefix separator configuration to properly use configured values instead of hardcoded plugin ID. The cache manager now correctly combines the configured namespace with plugin IDs using the configured separator for Redis and Valkey. Memcache and memory store continue to use plugin ID as namespace. +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/integration-aws-node@0.1.17 + ## 0.12.1-next.0 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 09bb4dc85d..1b1f0101c7 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.12.1-next.0", + "version": "0.12.1-next.1", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts index 4e9475b66e..a308caa222 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.test.ts @@ -333,4 +333,147 @@ describe('CacheManager store options', () => { keyPrefixSeparator: '!', }); }); + + it('correctly applies namespace configuration to redis and valkey stores', () => { + const testCases = [ + { store: 'redis', namespace: 'test1', separator: ':' }, + { store: 'valkey', namespace: 'test2', separator: '!' }, + ]; + + testCases.forEach(({ store, namespace, separator }) => { + const manager = CacheManager.fromConfig( + mockServices.rootConfig({ + data: { + backend: { + cache: { + store, + connection: 'redis://localhost:6379', + [store]: { + client: { + namespace, + keyPrefixSeparator: separator, + }, + }, + }, + }, + }, + }), + ); + + manager.forPlugin('testPlugin'); + + if (store === 'redis') { + // eslint-disable-next-line jest/no-conditional-expect + expect(KeyvRedis).toHaveBeenCalledWith('redis://localhost:6379', { + namespace, + keyPrefixSeparator: separator, + }); + } else if (store === 'valkey') { + // eslint-disable-next-line jest/no-conditional-expect + expect(KeyvValkey).toHaveBeenCalledWith('redis://localhost:6379', { + namespace, + keyPrefixSeparator: separator, + }); + } + }); + }); + + it('falls back to pluginId when no namespace is configured', () => { + const manager = CacheManager.fromConfig( + mockServices.rootConfig({ + data: { + backend: { + cache: { + store: 'redis', + connection: 'redis://localhost:6379', + }, + }, + }, + }), + ); + + manager.forPlugin('testPlugin'); + + expect(KeyvRedis).toHaveBeenCalledWith('redis://localhost:6379', { + keyPrefixSeparator: ':', + }); + }); + + describe('Namespace construction', () => { + it('returns pluginId when no store options are provided', () => { + const result = (CacheManager as any).constructNamespace( + 'testPlugin', + undefined, + ); + expect(result).toBe('testPlugin'); + }); + + it('returns pluginId when store options have no namespace', () => { + const storeOptions = { + client: { + keyPrefixSeparator: ':', + }, + }; + const result = (CacheManager as any).constructNamespace( + 'testPlugin', + storeOptions, + ); + expect(result).toBe('testPlugin'); + }); + + it('combines namespace and pluginId with default separator', () => { + const storeOptions = { + client: { + namespace: 'my-app', + keyPrefixSeparator: ':', + }, + }; + const result = (CacheManager as any).constructNamespace( + 'testPlugin', + storeOptions, + ); + expect(result).toBe('my-app:testPlugin'); + }); + + it('combines namespace and pluginId with custom separator', () => { + const storeOptions = { + client: { + namespace: 'my-app', + keyPrefixSeparator: '-', + }, + }; + const result = (CacheManager as any).constructNamespace( + 'testPlugin', + storeOptions, + ); + expect(result).toBe('my-app-testPlugin'); + }); + + it('uses default separator when keyPrefixSeparator is not provided', () => { + const storeOptions = { + client: { + namespace: 'my-app', + }, + }; + const result = (CacheManager as any).constructNamespace( + 'testPlugin', + storeOptions, + ); + expect(result).toBe('my-app:testPlugin'); + }); + + it('handles empty namespace by falling back to pluginId', () => { + const storeOptions = { + client: { + namespace: '', + keyPrefixSeparator: ':', + }, + }; + const result = (CacheManager as any).constructNamespace( + 'testPlugin', + storeOptions, + ); + expect(result).toBe('testPlugin'); + }); + }); }); diff --git a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts index 1c26083525..06b2b0c57b 100644 --- a/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts +++ b/packages/backend-defaults/src/entrypoints/cache/CacheManager.ts @@ -213,6 +213,26 @@ export class CacheManager { return redisOptions; } + /** + * Construct the full namespace based on the options and pluginId. + * + * @param pluginId - The plugin ID to namespace + * @param storeOptions - Optional cache store configuration options + * @returns The constructed namespace string combining the configured namespace with pluginId + */ + private static constructNamespace( + pluginId: string, + storeOptions: RedisCacheStoreOptions | undefined, + ): string { + const prefix = storeOptions?.client?.namespace + ? `${storeOptions.client.namespace}${ + storeOptions.client.keyPrefixSeparator ?? ':' + }` + : ''; + + return `${prefix}${pluginId}`; + } + /** @internal */ constructor( store: string, @@ -286,7 +306,7 @@ export class CacheManager { }); } return new Keyv({ - namespace: pluginId, + namespace: CacheManager.constructNamespace(pluginId, this.storeOptions), ttl: defaultTtl, store: stores[pluginId], emitErrors: false, @@ -326,7 +346,7 @@ export class CacheManager { }); } return new Keyv({ - namespace: pluginId, + namespace: CacheManager.constructNamespace(pluginId, this.storeOptions), ttl: defaultTtl, store: stores[pluginId], emitErrors: false, diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index 6bd6a7a781..d2e46404ef 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/backend-dynamic-feature-service +## 0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-backend@3.0.2-next.1 + - @backstage/plugin-app-node@0.1.37-next.1 + ## 0.7.4-next.0 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index ff8fc29639..6a7cc2315f 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.7.4-next.0", + "version": "0.7.4-next.1", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend/package.json b/packages/backend/package.json index a60b6d3940..64c2f1dd47 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -37,6 +37,7 @@ "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-backend-module-github-provider": "workspace:^", "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^", + "@backstage/plugin-auth-backend-module-openshift-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", "@backstage/plugin-catalog-backend-module-backstage-openapi": "workspace:^", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index f46e947b0d..e32d3148b3 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -33,6 +33,7 @@ const searchLoader = createBackendFeatureLoader({ backend.add(import('@backstage/plugin-auth-backend')); backend.add(import('./authModuleGithubProvider')); backend.add(import('@backstage/plugin-auth-backend-module-guest-provider')); +backend.add(import('@backstage/plugin-auth-backend-module-openshift-provider')); backend.add(import('@backstage/plugin-app-backend')); backend.add(import('@backstage/plugin-catalog-backend-module-unprocessed')); backend.add( diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 52e2ed04a4..254f37bd69 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/catalog-client +## 1.12.0-next.0 + +### Minor Changes + +- 0e9ec44: Introduced new `streamEntities` async generator method for the catalog. + + Catalog API and Catalog Service now includes a `streamEntities` method that allows for streaming entities from the catalog. + This method is designed to handle large datasets efficiently by processing entities in a stream rather than loading them + all into memory at once. This is useful when you need to fetch a large number of entities but do not want to use pagination + or fetch all entities at once. + + Example usage: + + ```ts + const pageStream = catalogClient.streamEntities({ pageSize: 100 }, { token }); + for await (const page of pageStream) { + // Handle page of entities + for (const entity of page) { + console.log(entity); + } + } + ``` + ## 1.11.0 ### Minor Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index a87ed29302..3b9d1c603a 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "1.11.0", + "version": "1.12.0-next.0", "description": "An isomorphic client for the catalog backend", "backstage": { "role": "common-library" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 87be49efce..bdaf3a05f6 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/cli +## 0.34.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/integration@1.18.0-next.0 + ## 0.34.2-next.1 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 51090239d8..8956b50713 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.34.2-next.1", + "version": "0.34.2-next.2", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" diff --git a/packages/cli/src/modules/build/lib/bundler/config.ts b/packages/cli/src/modules/build/lib/bundler/config.ts index c4011ede48..bf78d04b4a 100644 --- a/packages/cli/src/modules/build/lib/bundler/config.ts +++ b/packages/cli/src/modules/build/lib/bundler/config.ts @@ -264,24 +264,30 @@ export async function createConfig( singleton: true, requiredVersion: '*', eager: !isRemote, + import: false, }, 'react-dom': { singleton: true, requiredVersion: '*', eager: !isRemote, + import: false, }, // React Router 'react-router': { singleton: true, requiredVersion: '*', eager: !isRemote, + import: false, }, 'react-router-dom': { singleton: true, requiredVersion: '*', eager: !isRemote, + import: false, }, // MUI v4 + // not setting import: false for MUI packages as this + // will break once Backstage moves to BUI '@material-ui/core/styles': { singleton: true, requiredVersion: '*', @@ -293,6 +299,8 @@ export async function createConfig( eager: !isRemote, }, // MUI v5 + // not setting import: false for MUI packages as this + // will break once Backstage moves to BUI '@mui/material/styles/': { singleton: true, requiredVersion: '*', diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 6fe939f3e6..a7b38c24b9 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/config-loader +## 1.10.3-next.0 + +### Patch Changes + +- a73f495: Allow using `BACKSTAGE_ENV` for loading environment specific config files + ## 1.10.2 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index f347af8034..b0e7af3b5e 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config-loader", - "version": "1.10.2", + "version": "1.10.3-next.0", "description": "Config loading functionality used by Backstage backend, and CLI", "backstage": { "role": "node-library" diff --git a/packages/config-loader/src/schema/collect.test.ts b/packages/config-loader/src/schema/collect.test.ts index 53db5c6da7..d48c98fbe4 100644 --- a/packages/config-loader/src/schema/collect.test.ts +++ b/packages/config-loader/src/schema/collect.test.ts @@ -41,16 +41,6 @@ describe('collectConfigSchemas', () => { mockDir.clear(); }); - it('should not find any schemas without packages', async () => { - mockDir.setContent({ - 'lerna.json': JSON.stringify({ - packages: ['packages/*'], - }), - }); - - await expect(collectConfigSchemas([], [])).resolves.toEqual([]); - }); - it('should find schema in a local package', async () => { mockDir.setContent({ node_modules: { diff --git a/packages/config-loader/src/sources/ConfigSources.test.ts b/packages/config-loader/src/sources/ConfigSources.test.ts index e19ac1d51f..a6b0648889 100644 --- a/packages/config-loader/src/sources/ConfigSources.test.ts +++ b/packages/config-loader/src/sources/ConfigSources.test.ts @@ -89,6 +89,19 @@ describe('ConfigSources', () => { { name: 'FileConfigSource', path: `${root}app-config.yaml` }, { name: 'FileConfigSource', path: `${root}app-config.local.yaml` }, ]); + + process.env = Object.assign(process.env, { BACKSTAGE_ENV: 'test' }); + expect( + mergeSources( + ConfigSources.defaultForTargets({ rootDir: '/', targets: [] }), + ), + ).toEqual([ + { name: 'FileConfigSource', path: `${root}app-config.yaml` }, + { name: 'FileConfigSource', path: `${root}app-config.test.yaml` }, + { name: 'FileConfigSource', path: `${root}app-config.local.yaml` }, + { name: 'FileConfigSource', path: `${root}app-config.test.local.yaml` }, + ]); + fsSpy.mockRestore(); expect( diff --git a/packages/config-loader/src/sources/ConfigSources.ts b/packages/config-loader/src/sources/ConfigSources.ts index 37c1c6b087..18d0443986 100644 --- a/packages/config-loader/src/sources/ConfigSources.ts +++ b/packages/config-loader/src/sources/ConfigSources.ts @@ -182,6 +182,14 @@ export class ConfigSources { if (argSources.length === 0) { const defaultPath = resolvePath(rootDir, 'app-config.yaml'); const localPath = resolvePath(rootDir, 'app-config.local.yaml'); + const envPath = resolvePath( + rootDir, + `app-config.${process.env.BACKSTAGE_ENV}.yaml`, + ); + const envLocalPath = resolvePath( + rootDir, + `app-config.${process.env.BACKSTAGE_ENV}.local.yaml`, + ); const alwaysIncludeDefaultConfigSource = !options.allowMissingDefaultConfig; @@ -195,6 +203,16 @@ export class ConfigSources { ); } + if (process.env.BACKSTAGE_ENV && fs.pathExistsSync(envPath)) { + argSources.push( + FileConfigSource.create({ + watch: options.watch, + path: envPath, + substitutionFunc: options.substitutionFunc, + }), + ); + } + if (fs.pathExistsSync(localPath)) { argSources.push( FileConfigSource.create({ @@ -204,6 +222,16 @@ export class ConfigSources { }), ); } + + if (process.env.BACKSTAGE_ENV && fs.pathExistsSync(envLocalPath)) { + argSources.push( + FileConfigSource.create({ + watch: options.watch, + path: envLocalPath, + substitutionFunc: options.substitutionFunc, + }), + ); + } } return this.merge(argSources); diff --git a/packages/core-app-api/report.api.md b/packages/core-app-api/report.api.md index 445d73392d..0cecc22f4a 100644 --- a/packages/core-app-api/report.api.md +++ b/packages/core-app-api/report.api.md @@ -52,6 +52,7 @@ import { Observable } from '@backstage/types'; import { oktaAuthApiRef } from '@backstage/core-plugin-api'; import { oneloginAuthApiRef } from '@backstage/core-plugin-api'; import { OpenIdConnectApi } from '@backstage/core-plugin-api'; +import { openshiftAuthApiRef } from '@backstage/core-plugin-api'; import { PendingOAuthRequest } from '@backstage/core-plugin-api'; import { ProfileInfo } from '@backstage/core-plugin-api'; import { ProfileInfoApi } from '@backstage/core-plugin-api'; @@ -651,6 +652,12 @@ export type OpenLoginPopupOptions = { height?: number; }; +// @public +export class OpenShiftAuth { + // (undocumented) + static create(options: OAuthApiCreateOptions): typeof openshiftAuthApiRef.T; +} + // @public export type PopupOptions = { size?: 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 e02e07961a..00effb9384 100644 --- a/packages/core-app-api/src/apis/implementations/auth/index.ts +++ b/packages/core-app-api/src/apis/implementations/auth/index.ts @@ -26,4 +26,5 @@ export * from './bitbucket'; export * from './bitbucketServer'; export * from './atlassian'; export * from './vmwareCloud'; +export * from './openshift'; export type { OAuthApiCreateOptions, AuthApiCreateOptions } from './types'; diff --git a/packages/core-app-api/src/apis/implementations/auth/openshift/OpenShiftAuth.ts b/packages/core-app-api/src/apis/implementations/auth/openshift/OpenShiftAuth.ts new file mode 100644 index 0000000000..1d820189d2 --- /dev/null +++ b/packages/core-app-api/src/apis/implementations/auth/openshift/OpenShiftAuth.ts @@ -0,0 +1,52 @@ +/* + * Copyright 2025 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 { openshiftAuthApiRef } from '@backstage/core-plugin-api'; +import { OAuth2 } from '../oauth2'; +import { OAuthApiCreateOptions } from '../types'; + +const DEFAULT_PROVIDER = { + id: 'openshift', + title: 'OpenShift', + icon: () => null, +}; + +/** + * Implements the OAuth flow to OpenShift + * + * @public + */ +export default class OpenShiftAuth { + static create(options: OAuthApiCreateOptions): typeof openshiftAuthApiRef.T { + const { + configApi, + discoveryApi, + environment = 'development', + provider = DEFAULT_PROVIDER, + oauthRequestApi, + defaultScopes = ['user:info'], + } = options; + + return OAuth2.create({ + configApi, + discoveryApi, + oauthRequestApi, + provider, + environment, + defaultScopes, + }); + } +} diff --git a/plugins/catalog-backend-module-puppetdb/src/alpha.ts b/packages/core-app-api/src/apis/implementations/auth/openshift/index.ts similarity index 84% rename from plugins/catalog-backend-module-puppetdb/src/alpha.ts rename to packages/core-app-api/src/apis/implementations/auth/openshift/index.ts index 01a9b25f7c..65452114ae 100644 --- a/plugins/catalog-backend-module-puppetdb/src/alpha.ts +++ b/packages/core-app-api/src/apis/implementations/auth/openshift/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2025 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,5 +14,4 @@ * limitations under the License. */ -export * from './module'; -export { default } from './module'; +export { default as OpenShiftAuth } from './OpenShiftAuth'; diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index a966469313..ea40b301bb 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/core-compat-api +## 0.5.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + ## 0.5.2-next.1 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 1430973106..db51762180 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.5.2-next.1", + "version": "0.5.2-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 515e6eac5b..10e04ae0c6 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/core-components +## 0.17.6-next.1 + +### Patch Changes + +- 1ad3d94: Dependency graph can now be opened in full screen mode +- ae7d426: update about card links style for pretty display with other language + ## 0.17.6-next.0 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index be91c5dbd5..5cd6b9f270 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-components", - "version": "0.17.6-next.0", + "version": "0.17.6-next.1", "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" @@ -80,6 +80,7 @@ "pluralize": "^8.0.0", "qs": "^6.9.4", "rc-progress": "3.5.1", + "react-full-screen": "^1.1.1", "react-helmet": "6.1.0", "react-hook-form": "^7.12.2", "react-idle-timer": "5.7.2", @@ -106,6 +107,7 @@ "@types/d3-selection": "^3.0.1", "@types/d3-shape": "^3.0.1", "@types/d3-zoom": "^3.0.1", + "@types/fscreen": "^1", "@types/google-protobuf": "^3.7.2", "@types/react": "^18.0.0", "@types/react-helmet": "^6.1.0", diff --git a/packages/core-components/report-alpha.api.md b/packages/core-components/report-alpha.api.md index 32bd2f4939..96ddebcd3a 100644 --- a/packages/core-components/report-alpha.api.md +++ b/packages/core-components/report-alpha.api.md @@ -61,6 +61,7 @@ export const coreComponentsTranslationRef: TranslationRef< readonly 'alertDisplay.message_other': '({{ count }} newer messages)'; readonly 'autoLogout.stillTherePrompt.title': 'Logging out due to inactivity'; readonly 'autoLogout.stillTherePrompt.buttonText': "Yes! Don't log me out"; + readonly 'dependencyGraph.fullscreenTooltip': 'Toggle fullscreen'; readonly 'proxiedSignInPage.title': 'You do not appear to be signed in. Please try reloading the browser page.'; } >; diff --git a/packages/core-components/report.api.md b/packages/core-components/report.api.md index 4998accf01..c1c9d4ad5b 100644 --- a/packages/core-components/report.api.md +++ b/packages/core-components/report.api.md @@ -258,6 +258,7 @@ export interface DependencyGraphProps extends SVGProps { acyclicer?: 'greedy'; align?: DependencyGraphTypes.Alignment; + allowFullscreen?: boolean; curve?: 'curveStepBefore' | 'curveMonotoneX'; defs?: JSX.Element | JSX.Element[]; direction?: DependencyGraphTypes.Direction; diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.test.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.test.tsx index 6535808059..0dbf3a36b9 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.test.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.test.tsx @@ -14,10 +14,10 @@ * limitations under the License. */ -import { render } from '@testing-library/react'; import { DependencyGraph } from './DependencyGraph'; import { DependencyGraphTypes as Types } from './types'; import { EDGE_TEST_ID, LABEL_TEST_ID, NODE_TEST_ID } from './constants'; +import { renderInTestApp } from '@backstage/test-utils'; describe('', () => { beforeAll(() => { @@ -36,9 +36,8 @@ describe('', () => { const CUSTOM_TEST_ID = 'custom-test-id'; it('renders each node and edge supplied', async () => { - const { getByText, queryAllByTestId, findAllByTestId } = render( - , - ); + const { getByText, queryAllByTestId, findAllByTestId } = + await renderInTestApp(); const renderedNodes = await findAllByTestId(NODE_TEST_ID); expect(renderedNodes).toHaveLength(3); expect(getByText(nodes[0].id)).toBeInTheDocument(); @@ -49,9 +48,10 @@ describe('', () => { }); it('update render if already referenced nodes are added later', async () => { - const { getByText, queryAllByTestId, findAllByTestId, rerender } = render( - , - ); + const { getByText, queryAllByTestId, findAllByTestId, rerender } = + await renderInTestApp( + , + ); let renderedNodes = await findAllByTestId(NODE_TEST_ID); expect(renderedNodes).toHaveLength(2); @@ -75,9 +75,10 @@ describe('', () => { { ...edges[0], label: 'first' }, { ...edges[1], label: 'second' }, ]; - const { getByText, getAllByTestId, findAllByTestId } = render( - , - ); + const { getByText, getAllByTestId, findAllByTestId } = + await renderInTestApp( + , + ); const renderedEdges = await findAllByTestId(EDGE_TEST_ID); expect(renderedEdges).toHaveLength(2); expect(getAllByTestId(LABEL_TEST_ID)).toHaveLength(2); @@ -94,7 +95,7 @@ describe('', () => { ); - const { getByText, findByTestId, container } = render( + const { getByText, findByTestId, container } = await renderInTestApp( , ); const node = await findByTestId(CUSTOM_TEST_ID); @@ -112,7 +113,7 @@ describe('', () => { ); - const { getByText, findByTestId, container } = render( + const { getByText, findByTestId, container } = await renderInTestApp( ({ + root: { + overflow: 'hidden', + minHeight: '100%', + minWidth: '100%', + }, + fullscreen: { + backgroundColor: theme.palette.background.paper, + }, +})); /** * Properties of {@link DependencyGraph} @@ -142,7 +161,6 @@ export interface DependencyGraphProps * Custom edge rendering component */ renderEdge?: Types.RenderEdgeFunction; - /** * Custom node rendering component */ @@ -186,9 +204,18 @@ export interface DependencyGraphProps * Default: 'grow' */ fit?: 'grow' | 'contain'; + /** + * Controls if user can toggle fullscreen mode + * + * @remarks + * + * Default: true + */ + allowFullscreen?: boolean; } const WORKSPACE_ID = 'workspace'; +const DEPENDENCY_GRAPH_SVG = 'dependency-graph'; /** * Graph component used to visualize relations between entities @@ -222,11 +249,15 @@ export function DependencyGraph( curve = 'curveMonotoneX', showArrowHeads = false, fit = 'grow', + allowFullscreen = true, ...svgProps } = props; const theme = useTheme(); const [containerWidth, setContainerWidth] = useState(100); const [containerHeight, setContainerHeight] = useState(100); + const fullScreenHandle = useFullScreenHandle(); + const styles = useStyles(); + const { t } = useTranslationRef(coreComponentsTranslationRef); const graph = useRef>>( new dagre.graphlib.Graph(), @@ -248,11 +279,17 @@ export function DependencyGraph( const containerRef = useMemo( () => - debounce((node: SVGSVGElement) => { - if (!node) { + debounce((root: HTMLDivElement) => { + if (!root) { return; } // Set up zooming + panning + const node: SVGSVGElement = root.querySelector( + `svg#${DEPENDENCY_GRAPH_SVG}`, + ) as SVGSVGElement; + if (!node) { + return; + } const container = d3Selection.select(node); const workspace = d3Selection.select(node.getElementById(WORKSPACE_ID)); @@ -288,7 +325,7 @@ export function DependencyGraph( } const { width: newContainerWidth, height: newContainerHeight } = - node.getBoundingClientRect(); + root.getBoundingClientRect(); if (containerWidth !== newContainerWidth) { setContainerWidth(newContainerWidth); } @@ -412,70 +449,97 @@ export function DependencyGraph( } return ( - - - - - - {defs} - - - - {graphEdges.map(e => { - const edge = graph.current.edge(e) as GraphEdge; - if (!edge) return null; - if (renderEdge) return renderEdge({ edge, id: e }); +
+ + {allowFullscreen && ( + + + {fullScreenHandle.active ? ( + + ) : ( + + )} + + + )} - return ( - + + + - ); - })} - {graphNodes.map((id: string) => { - const node = graph.current.node(id); - if (!node) return null; - return ( - - ); - })} + + {defs} + + + + {graphEdges.map(e => { + const edge = graph.current.edge(e) as GraphEdge; + if (!edge) return null; + if (renderEdge) return renderEdge({ edge, id: e }); + + return ( + + ); + })} + {graphNodes.map((id: string) => { + const node = graph.current.node(id); + + if (!node) return null; + return ( + + ); + })} + + - - + +
); } diff --git a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx index 1fcf664bab..0b8fe8cd8c 100644 --- a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx +++ b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx @@ -27,6 +27,7 @@ const useStyles = makeStyles( gridAutoFlow: 'column', gridAutoColumns: 'min-content', gridGap: theme.spacing(3), + wordBreak: 'keep-all', }, }), { name: 'BackstageHeaderIconLinkRow' }, diff --git a/packages/core-components/src/translation.ts b/packages/core-components/src/translation.ts index b884f0af92..aa644fa909 100644 --- a/packages/core-components/src/translation.ts +++ b/packages/core-components/src/translation.ts @@ -120,6 +120,9 @@ export const coreComponentsTranslationRef = createTranslationRef({ buttonText: "Yes! Don't log me out", }, }, + dependencyGraph: { + fullscreenTooltip: 'Toggle fullscreen', + }, proxiedSignInPage: { title: 'You do not appear to be signed in. Please try reloading the browser page.', diff --git a/packages/core-plugin-api/report.api.md b/packages/core-plugin-api/report.api.md index 54bbfb94cf..b40088dc78 100644 --- a/packages/core-plugin-api/report.api.md +++ b/packages/core-plugin-api/report.api.md @@ -604,6 +604,11 @@ export type OpenIdConnectApi = { getIdToken(options?: AuthRequestOptions): Promise; }; +// @public +export const openshiftAuthApiRef: ApiRef< + OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +>; + // @public @deprecated export type OptionalParams< Params extends { diff --git a/packages/core-plugin-api/src/apis/definitions/auth.ts b/packages/core-plugin-api/src/apis/definitions/auth.ts index 9339a3e18a..f8686cb128 100644 --- a/packages/core-plugin-api/src/apis/definitions/auth.ts +++ b/packages/core-plugin-api/src/apis/definitions/auth.ts @@ -474,3 +474,20 @@ export const vmwareCloudAuthApiRef: ApiRef< > = createApiRef({ id: 'core.auth.vmware-cloud', }); + +/** + * Provides authentication towards OpenShift APIs and identities. + * + * @public + * @remarks + * + * See {@link https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/authentication_and_authorization/configuring-oauth-clients} + * on how to configure the OAuth clients and + * {@link https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html-single/authentication_and_authorization/index#tokens-scoping-about_configuring-internal-oauth} + * for available scopes. + */ +export const openshiftAuthApiRef: ApiRef< + OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +> = createApiRef({ + id: 'core.auth.openshift', +}); diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 261e595b30..0e622c2490 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/create-app +## 0.7.4-next.2 + +### Patch Changes + +- Bumped create-app version. + ## 0.7.4-next.1 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 7f2f9e4cd5..59f8498c54 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.7.4-next.1", + "version": "0.7.4-next.2", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 503ae406ae..45aaf0a814 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/dev-utils +## 1.1.14-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + ## 1.1.14-next.1 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 55b4c3fdfe..fd5881af44 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.14-next.1", + "version": "1.1.14-next.2", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/frontend-defaults/src/createApp.test.tsx b/packages/frontend-defaults/src/createApp.test.tsx index 351bc8f1b5..7b5e345e12 100644 --- a/packages/frontend-defaults/src/createApp.test.tsx +++ b/packages/frontend-defaults/src/createApp.test.tsx @@ -372,6 +372,7 @@ describe('createApp', () => { + diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index e6259c70d7..f9e2336408 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -69,6 +69,7 @@ import { OAuthScope } from '@backstage/core-plugin-api'; import { oktaAuthApiRef } from '@backstage/core-plugin-api'; import { oneloginAuthApiRef } from '@backstage/core-plugin-api'; import { OpenIdConnectApi } from '@backstage/core-plugin-api'; +import { openshiftAuthApiRef } from '@backstage/core-plugin-api'; import { PendingOAuthRequest } from '@backstage/core-plugin-api'; import { ProfileInfo } from '@backstage/core-plugin-api'; import { ProfileInfoApi } from '@backstage/core-plugin-api'; @@ -1518,6 +1519,8 @@ export { oneloginAuthApiRef }; export { OpenIdConnectApi }; +export { openshiftAuthApiRef }; + // @public export interface OverridableFrontendPlugin< TRoutes extends { diff --git a/packages/frontend-plugin-api/src/apis/definitions/auth.ts b/packages/frontend-plugin-api/src/apis/definitions/auth.ts index 89509082f0..5a31c1603d 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/auth.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/auth.ts @@ -37,4 +37,5 @@ export { microsoftAuthApiRef, oneloginAuthApiRef, vmwareCloudAuthApiRef, + openshiftAuthApiRef, } from '@backstage/core-plugin-api'; diff --git a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts index ec07d1b465..0468e96e14 100644 --- a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts +++ b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.test.ts @@ -231,6 +231,42 @@ describe('SingleInstanceGithubCredentialsProvider tests', () => { expect(token).toEqual(undefined); }); + it('should not fail to issue tokens for an organization when there is a case mismatch in the organization name', async () => { + octokit.apps.listInstallations.mockResolvedValue({ + headers: { + etag: '123', + }, + data: [ + { + id: 1, + repository_selection: 'selected', + account: { + login: 'backstage', + }, + }, + ], + } as RestEndpointMethodTypes['apps']['listInstallations']['response']); + + octokit.apps.createInstallationAccessToken.mockResolvedValueOnce({ + data: { + expires_at: DateTime.local().plus({ hours: 1 }).toString(), + token: 'secret_token', + repository_selection: 'selected', + }, + } as RestEndpointMethodTypes['apps']['createInstallationAccessToken']['response']); + + octokit.apps.listReposAccessibleToInstallation.mockReturnValue({ + data: [{ name: 'some-repo' }], + } as unknown as RestEndpointMethodTypes['apps']['listReposAccessibleToInstallation']['response']); + + const { token, headers } = await github.getCredentials({ + url: 'https://github.com/Backstage', + }); + const expectedToken = 'secret_token'; + expect(headers).toEqual({ Authorization: `Bearer ${expectedToken}` }); + expect(token).toEqual('secret_token'); + }); + it('should not fail to issue tokens for an organization when the app is installed for a single repo', async () => { octokit.apps.listInstallations.mockResolvedValue({ headers: { diff --git a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts index fd3c02486f..eaaabe8b0f 100644 --- a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts +++ b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts @@ -102,7 +102,9 @@ class GithubAppManager { private readonly allowedInstallationOwners: string[] | undefined; // undefined allows all installations constructor(config: GithubAppConfig, baseUrl?: string) { - this.allowedInstallationOwners = config.allowedInstallationOwners; + this.allowedInstallationOwners = config.allowedInstallationOwners?.map( + owner => owner.toLocaleLowerCase('en-US'), + ); this.baseUrl = baseUrl; this.baseAuthConfig = { appId: config.appId, @@ -121,7 +123,11 @@ class GithubAppManager { repo?: string, ): Promise<{ accessToken: string | undefined }> { if (this.allowedInstallationOwners) { - if (!this.allowedInstallationOwners?.includes(owner)) { + if ( + !this.allowedInstallationOwners?.includes( + owner.toLocaleLowerCase('en-US'), + ) + ) { return { accessToken: undefined }; // An empty token allows anonymous access to public repos } } diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 569444b4b8..a5a9bb0505 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/repo-tools +## 0.15.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + ## 0.15.2-next.0 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 9b7c875279..d93785f25e 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.15.2-next.0", + "version": "0.15.2-next.1", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index a88c4593e1..d251516dcd 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/ui +## 0.7.1-next.0 + +### Patch Changes + +- 7307930: Add missing class for flex: baseline +- 89da341: Fix Select component to properly attach aria-label and aria-labelledby props to the rendered element for improved accessibility. + ## 0.7.0 ### Minor Changes diff --git a/packages/ui/package.json b/packages/ui/package.json index 2ac399fcee..36b51f0bc8 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/ui", - "version": "0.7.0", + "version": "0.7.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/yarn-plugin/package.json b/packages/yarn-plugin/package.json index c534c50c76..4bd43fce40 100644 --- a/packages/yarn-plugin/package.json +++ b/packages/yarn-plugin/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@backstage/cli-common": "workspace:^", + "@backstage/errors": "workspace:^", "@backstage/release-manifests": "workspace:^", "@yarnpkg/core": "^4.4.1", "@yarnpkg/fslib": "^3.1.2", diff --git a/packages/yarn-plugin/src/util/getCurrentBackstageVersion.test.ts b/packages/yarn-plugin/src/util/getCurrentBackstageVersion.test.ts index 4b1f008fce..a0ce45c18f 100644 --- a/packages/yarn-plugin/src/util/getCurrentBackstageVersion.test.ts +++ b/packages/yarn-plugin/src/util/getCurrentBackstageVersion.test.ts @@ -73,17 +73,15 @@ describe('getCurrentBackstageVersion', () => { }); it.each` - description | content - ${'is missing'} | ${{}} - ${'is invalid'} | ${{ 'backstage.json': '}{' }} - ${'has missing version'} | ${{ 'backstage.json': '{"a":"b"}' }} - ${'has invalid version'} | ${{ 'backstage.json': '{"version":"foobar"}' }} - `('throws if backstage.json $description', ({ content }) => { + description | content | message + ${'is missing'} | ${{}} | ${/valid version string not found.*no such file/i} + ${'is invalid'} | ${{ 'backstage.json': '}{' }} | ${/valid version string not found.*not valid json/i} + ${'has missing version'} | ${{ 'backstage.json': '{"a":"b"}' }} | ${/valid version string not found.*version field is missing/i} + ${'has invalid version'} | ${{ 'backstage.json': '{"version":"foobar"}' }} | ${/valid version string not found.*exists but is not valid semver/i} + `('throws if backstage.json $description', ({ content, message }) => { mockDir.addContent(content); - expect(() => getCurrentBackstageVersion()).toThrow( - /valid version string not found/i, - ); + expect(() => getCurrentBackstageVersion()).toThrow(message); }); it('caches repeated calls', () => { diff --git a/packages/yarn-plugin/src/util/getCurrentBackstageVersion.ts b/packages/yarn-plugin/src/util/getCurrentBackstageVersion.ts index 5280854fb3..be80d29376 100644 --- a/packages/yarn-plugin/src/util/getCurrentBackstageVersion.ts +++ b/packages/yarn-plugin/src/util/getCurrentBackstageVersion.ts @@ -18,6 +18,7 @@ import assert from 'assert'; import { valid as semverValid } from 'semver'; import { ppath, xfs } from '@yarnpkg/fslib'; import { BACKSTAGE_JSON } from '@backstage/cli-common'; +import { ForwardedError } from '@backstage/errors'; import { memoize } from './memoize'; import { getWorkspaceRoot } from './getWorkspaceRoot'; @@ -26,11 +27,16 @@ export const getCurrentBackstageVersion = memoize(() => { let backstageVersion: string | null = null; try { - backstageVersion = semverValid(xfs.readJsonSync(backstageJsonPath).version); + const backstageVersionRaw = xfs.readJsonSync(backstageJsonPath).version; + assert(backstageVersionRaw !== undefined, 'Version field is missing'); + backstageVersion = semverValid(backstageVersionRaw); - assert(backstageVersion !== null); - } catch { - throw new Error('Valid version string not found in backstage.json'); + assert(backstageVersion !== null, 'Version exists but is not valid semver'); + } catch (err) { + throw new ForwardedError( + 'Valid version string not found in backstage.json', + err, + ); } return backstageVersion; diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 25671d8050..c57a56754c 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-api-docs +## 0.12.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/core-compat-api@0.5.2-next.2 + ## 0.12.11-next.1 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 4fcae79fba..17cf78a69a 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.12.11-next.1", + "version": "0.12.11-next.2", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index bd097b0005..ffe2bd1b71 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-app-backend +## 0.5.6-next.1 + +### Patch Changes + +- afd368e: Internal update to not expose the old `createRouter`. +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-app-node@0.1.37-next.1 + ## 0.5.6-next.0 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index b81e709926..4640a6459b 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.5.6-next.0", + "version": "0.5.6-next.1", "description": "A Backstage backend plugin that serves the Backstage frontend app", "backstage": { "role": "backend-plugin", diff --git a/plugins/app-backend/src/index.ts b/plugins/app-backend/src/index.ts index 9f83f6e30a..3f8a0002a7 100644 --- a/plugins/app-backend/src/index.ts +++ b/plugins/app-backend/src/index.ts @@ -21,4 +21,3 @@ */ export { appPlugin as default } from './service/appPlugin'; -export * from './service/router'; diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index 2063c2db48..14b3a215ee 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-app-node +## 0.1.37-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + ## 0.1.37-next.0 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 07340379e9..f28b28f461 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-node", - "version": "0.1.37-next.0", + "version": "0.1.37-next.1", "description": "Node.js library for the app plugin", "backstage": { "role": "node-library", diff --git a/plugins/app/report.api.md b/plugins/app/report.api.md index 24885bed4b..d12ada15b4 100644 --- a/plugins/app/report.api.md +++ b/plugins/app/report.api.md @@ -539,6 +539,21 @@ const appPlugin: OverridableFrontendPlugin< params: ApiFactory, ) => ExtensionBlueprintParams; }>; + 'api:app/openshift-auth': ExtensionDefinition<{ + kind: 'api'; + name: 'openshift-auth'; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: < + TApi, + TImpl extends TApi, + TDeps extends { [name in string]: unknown }, + >( + params: ApiFactory, + ) => ExtensionBlueprintParams; + }>; 'api:app/permission': ExtensionDefinition<{ kind: 'api'; name: 'permission'; diff --git a/plugins/app/src/defaultApis.ts b/plugins/app/src/defaultApis.ts index 0337f6c4f4..0b4eb5c825 100644 --- a/plugins/app/src/defaultApis.ts +++ b/plugins/app/src/defaultApis.ts @@ -35,6 +35,7 @@ import { createFetchApi, FetchMiddlewares, VMwareCloudAuth, + OpenShiftAuth, } from '../../../packages/core-app-api/src/apis/implementations'; import { @@ -56,6 +57,7 @@ import { bitbucketServerAuthApiRef, atlassianAuthApiRef, vmwareCloudAuthApiRef, + openshiftAuthApiRef, } from '@backstage/core-plugin-api'; import { ApiBlueprint, dialogApiRef } from '@backstage/frontend-plugin-api'; import { @@ -353,6 +355,26 @@ export const apis = [ }, }), }), + ApiBlueprint.make({ + name: 'openshift-auth', + params: defineParams => + defineParams({ + api: openshiftAuthApiRef, + deps: { + discoveryApi: discoveryApiRef, + oauthRequestApi: oauthRequestApiRef, + configApi: configApiRef, + }, + factory: ({ discoveryApi, oauthRequestApi, configApi }) => { + return OpenShiftAuth.create({ + configApi, + discoveryApi, + oauthRequestApi, + environment: configApi.getOptionalString('auth.environment'), + }); + }, + }), + }), ApiBlueprint.make({ name: 'permission', params: defineParams => diff --git a/plugins/auth-backend-module-openshift-provider/.eslintrc.js b/plugins/auth-backend-module-openshift-provider/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth-backend-module-openshift-provider/README.md b/plugins/auth-backend-module-openshift-provider/README.md new file mode 100644 index 0000000000..ae4700d561 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-auth-backend-module-openshift-provider + +The openshift-provider backend module for the auth plugin. + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/auth-backend-module-openshift-provider/catalog-info.yaml b/plugins/auth-backend-module-openshift-provider/catalog-info.yaml new file mode 100644 index 0000000000..3db615244a --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-auth-backend-module-openshift-provider + title: '@backstage/plugin-auth-backend-module-openshift-provider' + description: The OpenShift backend module for the auth plugin. +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: auth-maintainers diff --git a/plugins/auth-backend-module-openshift-provider/config.d.ts b/plugins/auth-backend-module-openshift-provider/config.d.ts new file mode 100644 index 0000000000..1fdaeb1532 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/config.d.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2025 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 { + auth?: { + providers?: { + /** @visibility frontend */ + openshift?: { + [authEnv: string]: { + clientId: string; + /** + * @visibility secret + */ + clientSecret: string; + authorizationUrl: string; + tokenUrl: string; + callbackUrl?: string; + openshiftApiServerUrl: string; + signIn?: { + resolvers: Array<{ + resolver: 'displayNameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + }>; + }; + sessionDuration?: HumanDuration | string; + }; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-openshift-provider/dev/index.ts b/plugins/auth-backend-module-openshift-provider/dev/index.ts new file mode 100644 index 0000000000..99f6828292 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/dev/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2025 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 { createBackend } from '@backstage/backend-defaults'; +import authPlugin from '@backstage/plugin-auth-backend'; +import authModuleOpenShiftProvider from '../src'; + +const backend = createBackend(); + +backend.add(authPlugin); +backend.add(authModuleOpenShiftProvider); + +backend.start(); diff --git a/plugins/auth-backend-module-openshift-provider/package.json b/plugins/auth-backend-module-openshift-provider/package.json new file mode 100644 index 0000000000..9fad0b542e --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/package.json @@ -0,0 +1,55 @@ +{ + "name": "@backstage/plugin-auth-backend-module-openshift-provider", + "version": "0.0.0", + "description": "The OpenShift backend module for the auth plugin.", + "backstage": { + "role": "backend-plugin-module", + "pluginId": "auth", + "pluginPackage": "@backstage/plugin-auth-backend" + }, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/auth-backend-module-openshift-provider" + }, + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist", + "config.d.ts" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/types": "workspace:^", + "passport-oauth2": "^1.8.0", + "zod": "^3.24.2" + }, + "devDependencies": { + "@backstage/backend-defaults": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-auth-backend": "workspace:^", + "express": "^4.18.2", + "msw": "^2.7.3", + "supertest": "^7.1.0" + }, + "configSchema": "config.d.ts" +} diff --git a/plugins/auth-backend-module-openshift-provider/report.api.md b/plugins/auth-backend-module-openshift-provider/report.api.md new file mode 100644 index 0000000000..aa429e1d47 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/report.api.md @@ -0,0 +1,28 @@ +## API Report File for "@backstage/plugin-auth-backend-module-openshift-provider" + +> 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 { OAuthAuthenticator } from '@backstage/plugin-auth-node'; +import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; +import { PassportProfile } from '@backstage/plugin-auth-node'; + +// @public (undocumented) +const authModuleOpenshiftProvider: BackendFeature; +export default authModuleOpenshiftProvider; + +// @public (undocumented) +export const openshiftAuthenticator: OAuthAuthenticator< + OpenShiftAuthenticatorContext, + PassportProfile +>; + +// @public (undocumented) +export interface OpenShiftAuthenticatorContext { + // (undocumented) + helper: PassportOAuthAuthenticatorHelper; + // (undocumented) + openshiftApiServerUrl: string; +} +``` diff --git a/plugins/auth-backend-module-openshift-provider/src/authenticator.test.ts b/plugins/auth-backend-module-openshift-provider/src/authenticator.test.ts new file mode 100644 index 0000000000..28a7738d21 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/src/authenticator.test.ts @@ -0,0 +1,226 @@ +/* + * Copyright 2025 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 { setupServer } from 'msw/node'; +import { + decodeOAuthState, + encodeOAuthState, +} from '@backstage/plugin-auth-node'; +import { registerMswTestHooks } from '@backstage/backend-test-utils'; +import { http, HttpResponse } from 'msw'; +import { openshiftAuthenticator } from './authenticator'; +import { ConfigReader } from '@backstage/config'; +import { + OAuthState, + OAuthAuthenticatorStartInput, + OAuthAuthenticatorAuthenticateInput, +} from '@backstage/plugin-auth-node'; +import express from 'express'; + +describe('openshiftAuthenticator', () => { + let implementation: any; + let oauthState: OAuthState; + + const mswServer = setupServer(); + registerMswTestHooks(mswServer); + + beforeEach(() => { + mswServer.use( + http.post('https://openshift.test/oauth/token', () => { + return HttpResponse.json({ + access_token: 'accessToken', + scope: 'user:full', + expires_in: 60 * 60 * 24, + }); + }), + http.get( + 'https://api.openshift.test/apis/user.openshift.io/v1/users/~', + async () => { + return HttpResponse.json({ + kind: 'User', + apiVersion: 'user.openshift.io/v1', + metadata: { + name: 'alice', + uid: 'ca993628-8817-4a3b-9811-be4a34c60bf4', + resourceVersion: '1', + creationTimestamp: '2022-01-11T13:10:45Z', + managedFields: [], + }, + fullName: 'Alice Adams', + identities: ['SSO:id'], + groups: ['system:authenticated', 'system:authenticated:oauth'], + }); + }, + ), + ); + + implementation = openshiftAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + clientId: 'clientId', + clientSecret: 'clientSecret', + authorizationUrl: 'https://openshift.test/oauth/authorize', + tokenUrl: 'https://openshift.test/oauth/token', + openshiftApiServerUrl: 'https://api.openshift.test', + }), + }); + + oauthState = { + nonce: 'nonce', + env: 'env', + }; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('#start', () => { + let fakeSession: Record; + let startRequest: OAuthAuthenticatorStartInput; + + beforeEach(() => { + fakeSession = {}; + startRequest = { + state: encodeOAuthState(oauthState), + req: { + method: 'GET', + url: 'test', + session: fakeSession, + }, + } as unknown as OAuthAuthenticatorStartInput; + }); + + it('initiates authorization code grant', async () => { + const startResponse = await openshiftAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + + expect(searchParams.get('response_type')).toBe('code'); + }); + + it('passes client ID from config', async () => { + const startResponse = await openshiftAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + + expect(searchParams.get('client_id')).toBe('clientId'); + }); + + it('passes callback URL from config', async () => { + const startResponse = await openshiftAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + + expect(searchParams.get('redirect_uri')).toBe( + 'https://backstage.test/callback', + ); + }); + + it('encodes OAuth state in query param', async () => { + const startResponse = await openshiftAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + const stateParam = searchParams.get('state'); + const decodedState = decodeOAuthState(stateParam!); + + expect(decodedState).toMatchObject(oauthState); + }); + }); + + describe('#authenticate', () => { + let handlerRequest: OAuthAuthenticatorAuthenticateInput; + + beforeEach(() => { + handlerRequest = { + req: { + method: 'GET', + query: { + code: 'authorization_code', + state: encodeOAuthState(oauthState), + }, + session: { + 'oauth2:openshift': { + state: encodeOAuthState(oauthState), + }, + }, + } as unknown as express.Request, + }; + }); + + it('exchanges authorization code for access token', async () => { + const authenticatorResult = await openshiftAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const accessToken = authenticatorResult.session.accessToken; + + expect(accessToken).toEqual('accessToken'); + }); + + it('returns granted scope', async () => { + const authenticatorResult = await openshiftAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const responseScope = authenticatorResult.session.scope; + + expect(responseScope).toEqual('user:full'); + }); + + it('returns a default session.tokentype field', async () => { + const authenticatorResult = await openshiftAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const tokenType = authenticatorResult.session.tokenType; + + expect(tokenType).toEqual('bearer'); + }); + + it('returns displayName', async () => { + const authenticatorResult = await openshiftAuthenticator.authenticate( + handlerRequest, + implementation, + ); + + expect(authenticatorResult).toMatchObject({ + fullProfile: { + displayName: 'alice', + }, + }); + }); + + it('should store access token as refresh token', async () => { + const authenticatorResult = await openshiftAuthenticator.authenticate( + handlerRequest, + implementation, + ); + + expect(authenticatorResult.session.refreshToken).toBe( + authenticatorResult.session.accessToken, + ); + }); + }); +}); diff --git a/plugins/auth-backend-module-openshift-provider/src/authenticator.ts b/plugins/auth-backend-module-openshift-provider/src/authenticator.ts new file mode 100644 index 0000000000..0c9acb6287 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/src/authenticator.ts @@ -0,0 +1,184 @@ +/* + * Copyright 2025 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 { + createOAuthAuthenticator, + PassportOAuthAuthenticatorHelper, + PassportOAuthDoneCallback, + PassportProfile, +} from '@backstage/plugin-auth-node'; +import { createHash } from 'node:crypto'; +import OAuth2Strategy from 'passport-oauth2'; +import { z } from 'zod'; + +/** @public */ +export interface OpenShiftAuthenticatorContext { + openshiftApiServerUrl: string; + helper: PassportOAuthAuthenticatorHelper; +} + +/** @private + * Schema for user.openshift.io/v1, + * see https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/user_and_group_apis/user-user-openshift-io-v1#user-user-openshift-io-v1 + */ +const OpenShiftUser = z.object({ + metadata: z.object({ + name: z.string(), + }), +}); + +/** @public */ +export const openshiftAuthenticator = createOAuthAuthenticator< + OpenShiftAuthenticatorContext, + PassportProfile +>({ + defaultProfileTransform: + PassportOAuthAuthenticatorHelper.defaultProfileTransform, + scopes: { + required: ['user:full'], + }, + initialize({ callbackUrl, config }) { + const clientId = config.getString('clientId'); + const clientSecret = config.getString('clientSecret'); + const authorizationUrl = config.getString('authorizationUrl'); + const tokenUrl = config.getString('tokenUrl'); + const openshiftApiServerUrl = config.getString('openshiftApiServerUrl'); + + // userUrl: `${openshiftApiServerUrl}/apis/user.openshift.io/v1/users/~`, + const strategy = new OAuth2Strategy( + { + clientID: clientId, + clientSecret: clientSecret, + callbackURL: callbackUrl, + authorizationURL: authorizationUrl, + tokenURL: tokenUrl, + passReqToCallback: false, + }, + ( + accessToken: any, + refreshToken: string, + params: any, + fullProfile: PassportProfile, + done: PassportOAuthDoneCallback, + ) => { + done(undefined, { fullProfile, params, accessToken }, { refreshToken }); + }, + ); + + strategy.userProfile = function userProfile( + accessToken: string, + done: (err?: unknown, profile?: any) => void, + ): void { + this._oauth2.useAuthorizationHeaderforGET(true); + + this._oauth2.get( + `${openshiftApiServerUrl}/apis/user.openshift.io/v1/users/~`, + accessToken, + (error, data, _) => { + if (error !== null && error.statusCode !== 200) { + done(new Error(`HTTP error! Status: ${error.statusCode}`)); + return; + } + + if (!data) { + done(new Error('No data provided!')); + return; + } + + if (typeof data !== 'string') { + done(new Error('Data of type Buffer is not supported!')); + return; + } + + const user = OpenShiftUser.parse(JSON.parse(data)); + done(null, { displayName: user.metadata.name }); + }, + ); + }; + + return { + openshiftApiServerUrl, + helper: PassportOAuthAuthenticatorHelper.from(strategy), + }; + }, + async start(input, { helper }) { + return helper.start(input, { + accessType: 'offline', + prompt: 'consent', + }); + }, + async authenticate(input, { helper }) { + // Same workaround as the GitHub provider; see https://github.com/backstage/backstage/issues/25383 + const { fullProfile, session } = await helper.authenticate(input); + session.refreshToken = session.accessToken; + session.refreshTokenExpiresInSeconds = session.expiresInSeconds; + return { fullProfile, session }; + }, + async refresh(input, { helper }) { + // Because the session is refreshed on login, this override is crucial, + // see https://github.com/backstage/backstage/issues/25383 + const accessToken = input.refreshToken; + + const fullProfile = await helper.fetchProfile(accessToken).catch(error => { + if (error.oauthError?.statusCode === 401) { + throw new Error('Invalid access token'); + } + throw error; + }); + + return { + fullProfile, + session: { + accessToken, + tokenType: 'bearer', + scope: input.scope, + refreshToken: input.refreshToken, + }, + }; + }, + async logout(input, { openshiftApiServerUrl, helper }) { + // Due to the implementation of createOAuthRouteHandlers, only the refresh token is set. + // In this provider, the refresh token actually IS the access token. + const accessToken = input.refreshToken; + if (!accessToken) { + throw new Error('access token/refresh token needs to be set for logout'); + } + + // Check if access token is still valid. + try { + await helper.fetchProfile(accessToken); + } catch { + // Invalid token, no need to delete OAuthAccessToken. + return; + } + + // Calculate token name, see: + // https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/oauth_apis/oauthaccesstoken-oauth-openshift-io-v1#apis-oauth-openshift-io-v1-oauthaccesstokens + const tokenName = createHash('sha256') + .update(accessToken.slice('sha256~'.length)) + .digest() + .toString('base64url'); + + const response = await fetch( + `${openshiftApiServerUrl}/apis/oauth.openshift.io/v1/oauthaccesstokens/sha256~${tokenName}`, + { method: 'DELETE', headers: { Authorization: `Bearer ${accessToken}` } }, + ); + + if (response.status === 401) { + throw new Error('unauthorized'); + } + }, +}); diff --git a/plugins/auth-backend-module-openshift-provider/src/index.ts b/plugins/auth-backend-module-openshift-provider/src/index.ts new file mode 100644 index 0000000000..4c8dd6cb22 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/src/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2025 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. + */ +/** + * The openshift-provider backend module for the auth plugin. + * + * @packageDocumentation + */ +export { + openshiftAuthenticator, + type OpenShiftAuthenticatorContext, +} from './authenticator'; +export { authModuleOpenshiftProvider as default } from './module'; diff --git a/plugins/auth-backend-module-openshift-provider/src/module.ts b/plugins/auth-backend-module-openshift-provider/src/module.ts new file mode 100644 index 0000000000..18d96c4778 --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/src/module.ts @@ -0,0 +1,45 @@ +/* + * Copyright 2025 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 { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { openshiftAuthenticator } from './authenticator'; +import { openshiftSignInResolvers } from './resolvers'; + +/** @public */ +export const authModuleOpenshiftProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'openshift-provider', + register(reg) { + reg.registerInit({ + deps: { providers: authProvidersExtensionPoint }, + async init({ providers }) { + providers.registerProvider({ + providerId: 'openshift', + factory: createOAuthProviderFactory({ + authenticator: openshiftAuthenticator, + signInResolverFactories: { + ...openshiftSignInResolvers, + }, + }), + }); + }, + }); + }, +}); diff --git a/plugins/auth-backend-module-openshift-provider/src/resolvers.ts b/plugins/auth-backend-module-openshift-provider/src/resolvers.ts new file mode 100644 index 0000000000..dee55ec4ca --- /dev/null +++ b/plugins/auth-backend-module-openshift-provider/src/resolvers.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2025 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 { + createSignInResolverFactory, + OAuthAuthenticatorResult, + PassportProfile, + SignInInfo, +} from '@backstage/plugin-auth-node'; + +import { + DEFAULT_NAMESPACE, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { z } from 'zod'; + +export namespace openshiftSignInResolvers { + export const displayNameMatchingUserEntityName = createSignInResolverFactory({ + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { + return async ( + info: SignInInfo>, + ctx, + ) => { + const { displayName } = info.profile; + + if (!displayName) { + throw new Error( + `OpenShift user profile does not contain a displayName`, + ); + } + + const userRef = stringifyEntityRef({ + kind: 'User', + name: displayName, + namespace: DEFAULT_NAMESPACE, + }); + + return await ctx.signInWithCatalogUser( + { entityRef: userRef }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: displayName } } + : undefined, + }, + ); + }; + }, + }); +} diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 54dee8262d..7aa33c315a 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend +## 0.25.4-next.1 + +### Patch Changes + +- 1d47bf3: Implementing Dynamic Client Registration with the OIDC server. You can enable this by setting `auth.experimentalDynamicClientRegistration.enabled` in `app-config.yaml`. This is highly experimental, but feedback welcome. +- 54ddfef: Updating plugin metadata +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.25.4-next.0 ### Patch Changes diff --git a/plugins/auth-backend/migrations/20250909120000_oidc_client_registration.js b/plugins/auth-backend/migrations/20250909120000_oidc_client_registration.js new file mode 100644 index 0000000000..87391c3467 --- /dev/null +++ b/plugins/auth-backend/migrations/20250909120000_oidc_client_registration.js @@ -0,0 +1,159 @@ +/* + * Copyright 2025 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. + */ + +// @ts-check + +/** + * @param {import('knex').Knex} knex + */ +exports.up = async function up(knex) { + // These tables make up the OIDC client registration flow. + // Clients are the top of the tree, that are created by the client registration flow. + await knex.schema.createTable('oidc_clients', table => { + table.comment( + 'OIDC clients that are registered via dynamic client registration', + ); + + table + .string('client_id') + .primary() + .notNullable() + .comment('The unique client ID of the client'); + + table + .string('client_secret') + .notNullable() + .comment('The client secret of the client'); + + table + .string('client_name') + .notNullable() + .comment('The name of the client, should be human readable'); + + table + .text('response_types', 'longtext') + .notNullable() + .comment('JSON array of supported response types'); + + table + .text('grant_types', 'longtext') + .notNullable() + .comment('JSON array of supported grant types'); + + table + .text('redirect_uris', 'longtext') + .notNullable() + .comment('Allowed redirect URIs as JSON array'); + + table.text('scope').nullable().comment('Default scopes for the client'); + + table + .text('metadata', 'longtext') + .nullable() + .comment('Additional client metadata as JSON'); + }); + + await knex.schema.createTable('oauth_authorization_sessions', table => { + table.comment('Core OAuth authorization sessions with shared context'); + + table + .string('id') + .primary() + .notNullable() + .comment('Unique session identifier'); + + table.string('client_id').notNullable().comment('OIDC client identifier'); + + table + .string('user_entity_ref') + .nullable() + .comment('Backstage user entity reference'); + + table + .text('redirect_uri', 'longtext') + .notNullable() + .comment('Client redirect URI'); + + table.text('scope').nullable().comment('Requested scopes space-separated'); + + table.string('state').nullable().comment('Client state parameter'); + + table.string('response_type').notNullable().comment('OAuth2 response type'); + + table.string('code_challenge').nullable().comment('PKCE code challenge'); + + table + .string('code_challenge_method') + .nullable() + .comment('PKCE code challenge method'); + + table.string('nonce').nullable().comment('OIDC nonce parameter'); + + table + .enum('status', ['pending', 'approved', 'rejected', 'expired']) + .defaultTo('pending') + .comment('Authorization session status'); + + table + .timestamp('expires_at', { useTz: true, precision: 0 }) + .notNullable() + .comment('Session expiration timestamp'); + + table.foreign('client_id').references('client_id').inTable('oidc_clients'); + table.index(['client_id', 'user_entity_ref']); + table.index(['status', 'expires_at']); + }); + + await knex.schema.createTable('oidc_authorization_codes', table => { + table.comment('OAuth authorization codes for code exchange flow'); + + table + .string('code') + .primary() + .notNullable() + .comment('Unique authorization code'); + + table + .string('session_id') + .notNullable() + .comment('Authorization session identifier'); + + table + .timestamp('expires_at', { useTz: true, precision: 0 }) + .notNullable() + .comment('Authorization code expiration timestamp'); + + table + .boolean('used') + .defaultTo(false) + .comment('Whether the authorization code has been used'); + + table + .foreign('session_id') + .references('id') + .inTable('oauth_authorization_sessions') + .onDelete('CASCADE'); + }); +}; + +/** + * @param {import('knex').Knex} knex + */ +exports.down = async function down(knex) { + await knex.schema.dropTable('oidc_authorization_codes'); + await knex.schema.dropTable('oauth_authorization_sessions'); + await knex.schema.dropTable('oidc_clients'); +}; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 0dddd935e6..e5f048ccae 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,11 +1,12 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.25.4-next.0", + "version": "0.25.4-next.1", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", "pluginId": "auth", "pluginPackages": [ + "@backstage/plugin-auth", "@backstage/plugin-auth-backend", "@backstage/plugin-auth-node", "@backstage/plugin-auth-react" @@ -60,6 +61,7 @@ "knex": "^3.0.0", "lodash": "^4.17.21", "luxon": "^3.0.0", + "matcher": "^4.0.0", "minimatch": "^9.0.0", "passport": "^0.7.0", "uuid": "^11.0.0" diff --git a/plugins/auth-backend/report.sql.md b/plugins/auth-backend/report.sql.md index b135414af6..7622a5750e 100644 --- a/plugins/auth-backend/report.sql.md +++ b/plugins/auth-backend/report.sql.md @@ -5,6 +5,59 @@ > [!WARNING] > Failed to migrate down from '20220321100910_timestamptz_again.js' +## Table `oauth_authorization_sessions` + +| Column | Type | Nullable | Max Length | Default | +| ----------------------- | -------------------------- | -------- | ---------- | ----------------- | +| `client_id` | `character varying` | false | 255 | - | +| `code_challenge` | `character varying` | true | 255 | - | +| `code_challenge_method` | `character varying` | true | 255 | - | +| `expires_at` | `timestamp with time zone` | false | - | - | +| `id` | `character varying` | false | 255 | - | +| `nonce` | `character varying` | true | 255 | - | +| `redirect_uri` | `text` | false | - | - | +| `response_type` | `character varying` | false | 255 | - | +| `scope` | `text` | true | - | - | +| `state` | `character varying` | true | 255 | - | +| `status` | `text` | true | - | `'pending'::text` | +| `user_entity_ref` | `character varying` | true | 255 | - | + +### Indices + +- `oauth_authorization_sessions_client_id_user_entity_ref_index` (`client_id`, `user_entity_ref`) +- `oauth_authorization_sessions_pkey` (`id`) unique primary +- `oauth_authorization_sessions_status_expires_at_index` (`status`, `expires_at`) + +## Table `oidc_authorization_codes` + +| Column | Type | Nullable | Max Length | Default | +| ------------ | -------------------------- | -------- | ---------- | ------- | +| `code` | `character varying` | false | 255 | - | +| `expires_at` | `timestamp with time zone` | false | - | - | +| `session_id` | `character varying` | false | 255 | - | +| `used` | `boolean` | true | - | `false` | + +### Indices + +- `oidc_authorization_codes_pkey` (`code`) unique primary + +## Table `oidc_clients` + +| Column | Type | Nullable | Max Length | Default | +| ---------------- | ------------------- | -------- | ---------- | ------- | +| `client_id` | `character varying` | false | 255 | - | +| `client_name` | `character varying` | false | 255 | - | +| `client_secret` | `character varying` | false | 255 | - | +| `grant_types` | `text` | false | - | - | +| `metadata` | `text` | true | - | - | +| `redirect_uris` | `text` | false | - | - | +| `response_types` | `text` | false | - | - | +| `scope` | `text` | true | - | - | + +### Indices + +- `oidc_clients_pkey` (`client_id`) unique primary + ## Table `sessions` | Column | Type | Nullable | Max Length | Default | diff --git a/plugins/auth-backend/src/authPlugin.ts b/plugins/auth-backend/src/authPlugin.ts index f3877d48cd..025d72fcb7 100644 --- a/plugins/auth-backend/src/authPlugin.ts +++ b/plugins/auth-backend/src/authPlugin.ts @@ -66,6 +66,7 @@ export const authPlugin = createBackendPlugin({ database: coreServices.database, discovery: coreServices.discovery, auth: coreServices.auth, + httpAuth: coreServices.httpAuth, catalog: catalogServiceRef, }, async init({ @@ -75,6 +76,7 @@ export const authPlugin = createBackendPlugin({ database, discovery, auth, + httpAuth, catalog, }) { const router = await createRouter({ @@ -86,6 +88,7 @@ export const authPlugin = createBackendPlugin({ catalog, providerFactories: Object.fromEntries(providers), ownershipResolver, + httpAuth, }); httpRouter.addAuthPolicy({ path: '/', diff --git a/plugins/auth-backend/src/database/OidcDatabase.test.ts b/plugins/auth-backend/src/database/OidcDatabase.test.ts new file mode 100644 index 0000000000..19285aec0a --- /dev/null +++ b/plugins/auth-backend/src/database/OidcDatabase.test.ts @@ -0,0 +1,293 @@ +/* + * Copyright 2025 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 { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; +import { AuthDatabase } from './AuthDatabase'; +import { OidcDatabase } from './OidcDatabase'; +import { resolvePackagePath } from '@backstage/backend-plugin-api'; + +jest.setTimeout(60_000); + +describe('Oidc Database', () => { + const databases = TestDatabases.create(); + + async function createOidcDatabase(databaseId: TestDatabaseId) { + const knex = await databases.init(databaseId); + + await knex.migrate.latest({ + directory: resolvePackagePath( + '@backstage/plugin-auth-backend', + 'migrations', + ), + }); + + return { + oidc: await OidcDatabase.create({ + database: AuthDatabase.create({ + getClient: async () => knex, + }), + }), + }; + } + + describe.each(databases.eachSupportedId())('%p', databaseId => { + describe('Clients', () => { + it('should create and return a client', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + await expect( + oidc.createClient({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }), + ).resolves.toEqual({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: undefined, + expiresAt: undefined, + metadata: undefined, + }); + }); + + it('should return the client thats created in a list', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + const client = await oidc.createClient({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }); + + await expect( + oidc.getClient({ clientId: 'test-client' }), + ).resolves.toEqual(client); + }); + + it('should return null if the client does not exist', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + await expect( + oidc.getClient({ clientId: 'test-client' }), + ).resolves.toBeNull(); + }); + }); + + describe('Authorization Sessions', () => { + it('should create and return an authorization session', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + const client = await oidc.createClient({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }); + + const session = await oidc.createAuthorizationSession({ + id: 'test-session', + clientId: client.clientId, + userEntityRef: 'user:default/blam', + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + codeChallenge: 'test-challenge', + codeChallengeMethod: 'S256', + nonce: 'test-nonce', + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + expect(session).toEqual( + expect.objectContaining({ + id: 'test-session', + clientId: client.clientId, + userEntityRef: 'user:default/blam', + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + codeChallenge: 'test-challenge', + codeChallengeMethod: 'S256', + nonce: 'test-nonce', + expiresAt: new Date('2025-01-01T00:00:00Z'), + status: 'pending', + }), + ); + }); + + it('should allow updating the authorization session', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + const client = await oidc.createClient({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }); + + const session = await oidc.createAuthorizationSession({ + id: 'test-session', + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + await expect( + oidc.updateAuthorizationSession({ + id: 'test-session', + userEntityRef: 'user:default/blam', + status: 'approved', + }), + ).resolves.toEqual({ + ...session, + userEntityRef: 'user:default/blam', + status: 'approved', + }); + }); + }); + + describe('Authorization Codes', () => { + it('should create and return an authorization code', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + const client = await oidc.createClient({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }); + + const session = await oidc.createAuthorizationSession({ + id: 'test-session', + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + const authCode = await oidc.createAuthorizationCode({ + code: 'test-code', + sessionId: session.id, + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + expect(authCode).toEqual( + expect.objectContaining({ + code: 'test-code', + sessionId: session.id, + expiresAt: new Date('2025-01-01T00:00:00Z'), + }), + ); + }); + + it('should return authorization code with session data', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + const client = await oidc.createClient({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }); + + const session = await oidc.createAuthorizationSession({ + id: 'test-session', + clientId: client.clientId, + userEntityRef: 'user:default/blam', + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + codeChallenge: 'test-challenge', + codeChallengeMethod: 'S256', + nonce: 'test-nonce', + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + const authCode = await oidc.createAuthorizationCode({ + code: 'test-code', + sessionId: session.id, + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + const authCodeFromDb = await oidc.getAuthorizationCode({ + code: 'test-code', + }); + const sessionFromDb = await oidc.getAuthorizationSession({ + id: authCodeFromDb!.sessionId, + }); + + expect(authCodeFromDb).toEqual(authCode); + expect(sessionFromDb).toEqual(session); + }); + + it('should allow updating the authorization code', async () => { + const { oidc } = await createOidcDatabase(databaseId); + + const client = await oidc.createClient({ + clientId: 'test-client', + clientName: 'Test Client', + clientSecret: 'test-secret', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }); + + const session = await oidc.createAuthorizationSession({ + id: 'test-session', + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + const authCode = await oidc.createAuthorizationCode({ + code: 'test-code', + sessionId: session.id, + expiresAt: new Date('2025-01-01T00:00:00Z'), + }); + + const updatedAuthCode = await oidc.updateAuthorizationCode({ + code: 'test-code', + used: true, + }); + + expect(updatedAuthCode).toEqual({ + ...authCode, + used: true, + }); + }); + }); + }); +}); diff --git a/plugins/auth-backend/src/database/OidcDatabase.ts b/plugins/auth-backend/src/database/OidcDatabase.ts new file mode 100644 index 0000000000..ebb6619400 --- /dev/null +++ b/plugins/auth-backend/src/database/OidcDatabase.ts @@ -0,0 +1,397 @@ +/* + * Copyright 2025 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 { Knex } from 'knex'; +import { AuthDatabase } from './AuthDatabase'; + +function toDate(value?: Date | string | number): Date | undefined { + if (!value) { + return undefined; + } + + return typeof value === 'string' || typeof value === 'number' + ? new Date(value) + : value; +} +type OidcClientRow = { + client_id: string; + client_secret: string; + client_name: string; + response_types: string; + grant_types: string; + redirect_uris: string; + scope: string | null; + metadata: string | null; +}; + +type OAuthAuthorizationSessionRow = { + id: string; + client_id: string; + user_entity_ref: string | null; + redirect_uri: string; + scope: string | null; + state: string | null; + response_type: string; + code_challenge: string | null; + code_challenge_method: string | null; + nonce: string | null; + status: 'pending' | 'approved' | 'rejected' | 'expired'; + expires_at: Date | string; +}; + +type OidcAuthorizationCodeRow = { + code: string; + session_id: string; + expires_at: Date | string; + used: boolean; +}; + +export type Client = { + clientId: string; + clientName: string; + clientSecret: string; + redirectUris: string[]; + responseTypes: string[]; + grantTypes: string[]; + scope?: string; + metadata?: Record; +}; + +export type AuthorizationSession = { + id: string; + clientId: string; + userEntityRef?: string; + redirectUri: string; + scope?: string; + state?: string; + responseType: string; + codeChallenge?: string; + codeChallengeMethod?: string; + nonce?: string; + status: 'pending' | 'approved' | 'rejected' | 'expired'; + expiresAt: Date; +}; + +export type ConsentRequest = { + id: string; + sessionId: string; + expiresAt: Date; +}; + +export type AuthorizationCode = { + code: string; + sessionId: string; + expiresAt: Date; + used: boolean; +}; + +export type AccessToken = { + tokenId: string; + sessionId: string; + expiresAt: Date; +}; + +/** + * This class provides database operations for OpenID Connect (OIDC) authentication flows. + * It manages OIDC clients, authorization codes, and access tokens in the database. + */ +export class OidcDatabase { + private constructor(private readonly db: Knex) {} + + static async create(options: { database: AuthDatabase }) { + const client = await options.database.get(); + return new OidcDatabase(client); + } + + async createClient(client: Client) { + await this.db('oidc_clients').insert({ + client_id: client.clientId, + client_secret: client.clientSecret, + client_name: client.clientName, + response_types: JSON.stringify(client.responseTypes), + grant_types: JSON.stringify(client.grantTypes), + redirect_uris: JSON.stringify(client.redirectUris), + scope: client.scope, + metadata: JSON.stringify(client.metadata), + }); + + return client; + } + + async getClient({ clientId }: { clientId: string }) { + const client = await this.db('oidc_clients') + .where('client_id', clientId) + .first(); + + if (!client) { + return null; + } + + return this.rowToClient(client) as Client; + } + + async createAuthorizationSession( + session: Omit, + ) { + await this.db( + 'oauth_authorization_sessions', + ).insert({ + id: session.id, + client_id: session.clientId, + user_entity_ref: session.userEntityRef, + redirect_uri: session.redirectUri, + scope: session.scope, + state: session.state, + response_type: session.responseType, + code_challenge: session.codeChallenge, + code_challenge_method: session.codeChallengeMethod, + nonce: session.nonce, + status: 'pending', + expires_at: session.expiresAt, + }); + + return { + ...session, + status: 'pending', + }; + } + + async updateAuthorizationSession( + session: Partial & { id: string }, + ) { + const row = this.authorizationSessionToRow(session); + const updatedFields = Object.fromEntries( + Object.entries(row).filter(([_, value]) => value !== undefined), + ); + + // MySQL and SQLite3 don't support RETURNING + if ( + this.db.client.config.client.includes('sqlite3') || + this.db.client.config.client.includes('mysql') + ) { + return await this.db.transaction(async trx => { + await trx('oauth_authorization_sessions') + .where('id', session.id) + .update(updatedFields); + + const updated = await trx( + 'oauth_authorization_sessions', + ) + .where('id', session.id) + .first(); + + if (!updated) { + throw new Error( + `Failed to retrieve updated authorization session with id ${session.id}`, + ); + } + + return this.rowToAuthorizationSession(updated) as AuthorizationSession; + }); + } + + const returnedRows = await this.db( + 'oauth_authorization_sessions', + ) + .where('id', session.id) + .update(updatedFields) + .returning('*'); + + if (returnedRows.length !== 1) { + throw new Error( + `Failed to retrieve updated authorization session with id ${session.id}`, + ); + } + + const [returnedSession] = returnedRows; + + return this.rowToAuthorizationSession( + returnedSession, + ) as AuthorizationSession; + } + + async getAuthorizationSession({ id }: { id: string }) { + const session = await this.db( + 'oauth_authorization_sessions', + ) + .where('id', id) + .first(); + + if (!session) { + return null; + } + + return this.rowToAuthorizationSession(session) as AuthorizationSession; + } + + async createAuthorizationCode( + authorizationCode: Omit, + ) { + await this.db('oidc_authorization_codes').insert({ + code: authorizationCode.code, + session_id: authorizationCode.sessionId, + expires_at: authorizationCode.expiresAt, + used: false, + }); + + return { + ...authorizationCode, + used: false, + }; + } + + async getAuthorizationCode({ code }: { code: string }) { + const authCode = await this.db( + 'oidc_authorization_codes', + ) + .where('code', code) + .first(); + + if (!authCode) { + return null; + } + + return this.rowToAuthorizationCode(authCode) as AuthorizationCode; + } + + async updateAuthorizationCode( + authorizationCode: Partial & { code: string }, + ) { + const row = this.authorizationCodeToRow(authorizationCode); + const updatedFields = Object.fromEntries( + Object.entries(row).filter(([_, value]) => value !== undefined), + ); + + // MySQL and SQLite3 don't support RETURNING + if ( + this.db.client.config.client.includes('sqlite3') || + this.db.client.config.client.includes('mysql') + ) { + return await this.db.transaction(async trx => { + await trx('oidc_authorization_codes') + .where('code', authorizationCode.code) + .update(updatedFields); + + const updated = await trx( + 'oidc_authorization_codes', + ) + .where('code', authorizationCode.code) + .first(); + + if (!updated) { + throw new Error( + `Failed to retrieve updated authorization code with code ${authorizationCode.code}`, + ); + } + + return this.rowToAuthorizationCode(updated) as AuthorizationCode; + }); + } + + const returnedRows = await this.db( + 'oidc_authorization_codes', + ) + .where('code', authorizationCode.code) + .update(updatedFields) + .returning('*'); + + if (returnedRows.length !== 1) { + throw new Error( + `Failed to retrieve updated authorization code with code ${authorizationCode.code}`, + ); + } + + const [returnedCode] = returnedRows; + + return this.rowToAuthorizationCode(returnedCode) as AuthorizationCode; + } + + private rowToClient(row: OidcClientRow): Client { + return { + clientId: row.client_id, + clientName: row.client_name, + clientSecret: row.client_secret, + redirectUris: row.redirect_uris + ? JSON.parse(row.redirect_uris) + : undefined, + responseTypes: row.response_types + ? JSON.parse(row.response_types) + : undefined, + grantTypes: row.grant_types ? JSON.parse(row.grant_types) : undefined, + scope: row.scope ?? undefined, + metadata: row.metadata ? JSON.parse(row.metadata) : undefined, + }; + } + + private authorizationSessionToRow( + session: Partial, + ): Partial { + return { + id: session.id, + client_id: session.clientId, + user_entity_ref: session.userEntityRef, + redirect_uri: session.redirectUri, + scope: session.scope, + state: session.state, + response_type: session.responseType, + code_challenge: session.codeChallenge, + code_challenge_method: session.codeChallengeMethod, + nonce: session.nonce, + status: session.status, + expires_at: toDate(session.expiresAt), + }; + } + + private rowToAuthorizationSession( + row: OAuthAuthorizationSessionRow, + ): Partial { + return { + id: row.id, + clientId: row.client_id, + userEntityRef: row.user_entity_ref ?? undefined, + redirectUri: row.redirect_uri, + scope: row.scope ?? undefined, + state: row.state ?? undefined, + responseType: row.response_type, + codeChallenge: row.code_challenge ?? undefined, + codeChallengeMethod: row.code_challenge_method ?? undefined, + nonce: row.nonce ?? undefined, + status: row.status, + expiresAt: toDate(row.expires_at), + }; + } + + private authorizationCodeToRow( + authorizationCode: Partial, + ): Partial { + return { + code: authorizationCode.code, + session_id: authorizationCode.sessionId, + expires_at: toDate(authorizationCode.expiresAt), + used: authorizationCode.used, + }; + } + + private rowToAuthorizationCode( + row: OidcAuthorizationCodeRow, + ): Partial { + return { + code: row.code, + sessionId: row.session_id, + expiresAt: toDate(row.expires_at), + used: Boolean(row.used), + }; + } +} diff --git a/plugins/auth-backend/src/migrations.test.ts b/plugins/auth-backend/src/migrations.test.ts index f9575c70fd..df7063351a 100644 --- a/plugins/auth-backend/src/migrations.test.ts +++ b/plugins/auth-backend/src/migrations.test.ts @@ -186,4 +186,122 @@ describe('migrations', () => { await knex.destroy(); }, ); + + it.each(databases.eachSupportedId())( + '20250909120000_oidc_client_registration.js, %p', + async databaseId => { + const knex = await databases.init(databaseId); + + await migrateUntilBefore( + knex, + '20250909120000_oidc_client_registration.js', + ); + await migrateUpOnce(knex); + + await knex + .insert({ + client_id: 'test-client-id', + client_secret: 'test-client-secret', + client_name: 'Test Client', + response_types: JSON.stringify(['code']), + grant_types: JSON.stringify(['authorization_code']), + redirect_uris: JSON.stringify(['https://example.com/callback']), + scope: 'openid profile', + metadata: JSON.stringify({ description: 'Test client' }), + }) + .into('oidc_clients'); + + await expect( + knex('oidc_clients').where('client_id', 'test-client-id').first(), + ).resolves.toEqual({ + client_id: 'test-client-id', + client_secret: 'test-client-secret', + client_name: 'Test Client', + response_types: JSON.stringify(['code']), + grant_types: JSON.stringify(['authorization_code']), + redirect_uris: JSON.stringify(['https://example.com/callback']), + scope: 'openid profile', + metadata: JSON.stringify({ description: 'Test client' }), + }); + + await knex + .insert({ + id: 'test-session-id', + client_id: 'test-client-id', + user_entity_ref: 'user:default/test-user', + redirect_uri: 'https://example.com/callback', + scope: 'openid', + state: 'test-state', + response_type: 'code', + code_challenge: 'test-challenge', + code_challenge_method: 'S256', + nonce: 'test-nonce', + status: 'pending', + expires_at: new Date(Date.now() + 3600000), + }) + .into('oauth_authorization_sessions'); + + await expect( + knex('oauth_authorization_sessions') + .where('id', 'test-session-id') + .first(), + ).resolves.toEqual( + expect.objectContaining({ + id: 'test-session-id', + client_id: 'test-client-id', + user_entity_ref: 'user:default/test-user', + redirect_uri: 'https://example.com/callback', + scope: 'openid', + state: 'test-state', + response_type: 'code', + code_challenge: 'test-challenge', + code_challenge_method: 'S256', + nonce: 'test-nonce', + status: 'pending', + }), + ); + + await knex + .insert({ + code: 'test-auth-code', + session_id: 'test-session-id', + expires_at: new Date(Date.now() + 600000), + used: false, + }) + .into('oidc_authorization_codes'); + + await expect( + knex('oidc_authorization_codes') + .where('code', 'test-auth-code') + .first(), + ).resolves.toEqual( + expect.objectContaining({ + code: 'test-auth-code', + session_id: 'test-session-id', + }), + ); + + await knex('oauth_authorization_sessions') + .where('id', 'test-session-id') + .del(); + + await expect( + knex('oidc_authorization_codes').where('session_id', 'test-session-id'), + ).resolves.toHaveLength(0); + + await migrateDownOnce(knex); + + const tables = [ + 'oidc_clients', + 'oauth_authorization_sessions', + 'oidc_authorization_codes', + ]; + + for (const table of tables) { + await expect(knex.schema.hasTable(table)).resolves.toBe(false); + } + + await knex.destroy(); + }, + ); }); diff --git a/plugins/auth-backend/src/service/OidcRouter.test.ts b/plugins/auth-backend/src/service/OidcRouter.test.ts index dbc6c88f93..de865a9c43 100644 --- a/plugins/auth-backend/src/service/OidcRouter.test.ts +++ b/plugins/auth-backend/src/service/OidcRouter.test.ts @@ -17,135 +17,794 @@ import { coreServices, createBackendPlugin, + resolvePackagePath, } from '@backstage/backend-plugin-api'; -import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; -import Router from 'express-promise-router'; +import { + mockServices, + startTestBackend, + TestDatabases, + TestDatabaseId, + mockCredentials, +} from '@backstage/backend-test-utils'; import request from 'supertest'; +import crypto from 'crypto'; import { OidcRouter } from './OidcRouter'; import { UserInfoDatabase } from '../database/UserInfoDatabase'; +import { OidcDatabase } from '../database/OidcDatabase'; +import { AuthDatabase } from '../database/AuthDatabase'; +import { OidcService } from '../service/OidcService'; +import { TokenIssuer } from '../identity/types'; + +jest.setTimeout(60_000); describe('OidcRouter', () => { - describe('/v1/userinfo', () => { - it('should return user info for full tokens', async () => { - const auth = mockServices.auth.mock(); - const mockUserInfo = { - getUserInfo: jest.fn().mockResolvedValue({ - claims: { - sub: 'k/ns:n', - ent: ['k/ns:a', 'k/ns:b'], - }, - }), - } as unknown as UserInfoDatabase; + const MOCK_USER_TOKEN = 'mock-user-token'; + const MOCK_USER_ENTITY_REF = 'user:default/test-user'; + const databases = TestDatabases.create(); - const { server } = await startTestBackend({ - features: [ - createBackendPlugin({ - pluginId: 'auth', - register(reg) { - reg.registerInit({ - deps: { httpRouter: coreServices.httpRouter }, - async init({ httpRouter }) { - const router = Router(); + async function createRouter(databaseId: TestDatabaseId) { + const knex = await databases.init(databaseId); - router.use( - OidcRouter.create({ - auth, - tokenIssuer: {} as any, - baseUrl: 'http://localhost:7000', - userInfo: mockUserInfo, - }).getRouter(), - ); - httpRouter.use(router); - httpRouter.addAuthPolicy({ - path: '/', - allow: 'unauthenticated', - }); - }, - }); - }, - }), - ], - }); - - auth.authenticate.mockResolvedValueOnce({} as any); - auth.isPrincipal.mockReturnValueOnce(true); - - await request(server) - .get('/api/auth/v1/userinfo') - .set( - 'Authorization', - `Bearer h.${btoa( - JSON.stringify({ sub: 'k/ns:n', ent: ['k/ns:a', 'k/ns:b'] }), - )}.s`, - ) - .expect(200, { - claims: { - sub: 'k/ns:n', - ent: ['k/ns:a', 'k/ns:b'], - }, - }); - - expect(mockUserInfo.getUserInfo).toHaveBeenCalledWith('k/ns:n'); + await knex.migrate.latest({ + directory: resolvePackagePath( + '@backstage/plugin-auth-backend', + 'migrations', + ), }); - it('should return user info for limited tokens', async () => { - const auth = mockServices.auth.mock(); - const mockUserInfo = { - getUserInfo: jest.fn().mockResolvedValue({ - claims: { - sub: 'k/ns:n', - ent: ['k/ns:a', 'k/ns:b'], + const authDatabase = AuthDatabase.create({ + getClient: async () => knex, + }); + + const oidcDatabase = await OidcDatabase.create({ + database: authDatabase, + }); + + const userInfoDatabase = await UserInfoDatabase.create({ + database: authDatabase, + }); + + const mockTokenIssuer = { + issueToken: jest.fn(), + listPublicKeys: jest.fn(), + } as unknown as jest.Mocked; + + const mockAuth = mockServices.auth.mock(); + const mockHttpAuth = mockServices.httpAuth.mock(); + const mockConfig = mockServices.rootConfig({ + data: { + auth: { + experimentalDynamicClientRegistration: { + enabled: true, }, - }), - } as unknown as UserInfoDatabase; + }, + }, + }); - const { server } = await startTestBackend({ - features: [ - createBackendPlugin({ - pluginId: 'auth', - register(reg) { - reg.registerInit({ - deps: { httpRouter: coreServices.httpRouter }, - async init({ httpRouter }) { - const router = Router(); + const oidcService = OidcService.create({ + auth: mockAuth, + tokenIssuer: mockTokenIssuer, + baseUrl: 'http://localhost:7000', + userInfo: userInfoDatabase, + oidc: oidcDatabase, + config: mockConfig, + }); - router.use( - OidcRouter.create({ - auth, - tokenIssuer: {} as any, - baseUrl: 'http://localhost:7000', - userInfo: mockUserInfo, - }).getRouter(), - ); - httpRouter.use(router); - httpRouter.addAuthPolicy({ - path: '/', - allow: 'unauthenticated', - }); - }, - }); - }, - }), - ], - }); + const oidcRouter = OidcRouter.create({ + auth: mockAuth, + tokenIssuer: mockTokenIssuer, + baseUrl: 'http://localhost:7000', + appUrl: 'http://localhost:3000', + logger: mockServices.logger.mock(), + userInfo: userInfoDatabase, + oidc: oidcDatabase, + httpAuth: mockHttpAuth, + config: mockConfig, + }); - auth.authenticate.mockResolvedValueOnce({} as any); - auth.isPrincipal.mockReturnValueOnce(true); + return { + router: oidcRouter, + mocks: { + httpAuth: mockHttpAuth, + auth: mockAuth, + oidc: oidcDatabase, + userInfo: userInfoDatabase, + service: oidcService, + tokenIssuer: mockTokenIssuer, + }, + }; + } - await request(server) - .get('/api/auth/v1/userinfo') - .set( - 'Authorization', - `Bearer h.${btoa(JSON.stringify({ sub: 'k/ns:n' }))}.s`, - ) - .expect(200, { + describe.each(databases.eachSupportedId())('%p', databaseId => { + describe('/v1/userinfo', () => { + it('should return user info for full tokens', async () => { + const { + mocks: { auth, userInfo }, + router, + } = await createRouter(databaseId); + + await userInfo.addUserInfo({ claims: { sub: 'k/ns:n', ent: ['k/ns:a', 'k/ns:b'], + exp: Math.floor(Date.now() / 1000) + 3600, }, }); - expect(mockUserInfo.getUserInfo).toHaveBeenCalledWith('k/ns:n'); + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + auth.isPrincipal.mockReturnValueOnce(true); + + const response = await request(server) + .get('/api/auth/v1/userinfo') + .set( + 'Authorization', + `Bearer h.${btoa( + JSON.stringify({ sub: 'k/ns:n', ent: ['k/ns:a', 'k/ns:b'] }), + )}.s`, + ) + .expect(200); + + expect(response.body).toEqual({ + claims: { + sub: 'k/ns:n', + ent: ['k/ns:a', 'k/ns:b'], + exp: expect.any(Number), + }, + }); + }); + + it('should return user info for limited tokens', async () => { + const { + mocks: { auth, userInfo }, + router, + } = await createRouter(databaseId); + + await userInfo.addUserInfo({ + claims: { + sub: 'k/ns:n', + ent: ['k/ns:a', 'k/ns:b'], + exp: Math.floor(Date.now() / 1000) + 3600, + }, + }); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + auth.isPrincipal.mockReturnValueOnce(true); + + const response = await request(server) + .get('/api/auth/v1/userinfo') + .set( + 'Authorization', + `Bearer h.${btoa(JSON.stringify({ sub: 'k/ns:n' }))}.s`, + ) + .expect(200); + + expect(response.body).toEqual({ + claims: { + sub: 'k/ns:n', + ent: ['k/ns:a', 'k/ns:b'], + exp: expect.any(Number), + }, + }); + }); + }); + + describe('auth flow', () => { + it('should register a client', async () => { + const { router } = await createRouter(databaseId); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const response = await request(server) + .post('/api/auth/v1/register') + .send({ + client_name: 'Test Client', + redirect_uris: ['https://example.com/callback'], + response_types: ['code'], + grant_types: ['authorization_code'], + scope: 'openid', + }) + .expect(201); + + expect(response.body).toEqual({ + client_id: expect.any(String), + client_secret: expect.any(String), + redirect_uris: ['https://example.com/callback'], + }); + }); + + it('should create an authorization session via authorization endpoint', async () => { + const { + mocks: { service }, + router, + } = await createRouter(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const response = await request(server) + .get('/api/auth/v1/authorize') + .query({ + client_id: client.clientId, + redirect_uri: 'https://example.com/callback', + response_type: 'code', + scope: 'openid', + state: 'test-state', + }) + .expect(302); + + expect(response.header.location).toMatch( + /^http:\/\/localhost:3000\/oauth2\/authorize\/[a-f0-9-]+$/, + ); + }); + + it('should get auth session details', async () => { + const { + mocks: { service }, + router, + } = await createRouter(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + }); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const response = await request(server) + .get(`/api/auth/v1/sessions/${authSession.id}`) + .expect(200); + + expect(response.body).toEqual({ + id: authSession.id, + clientName: 'Test Client', + scope: 'openid', + redirectUri: 'https://example.com/callback', + }); + }); + + it('should approve authorization session', async () => { + const { + mocks: { auth, service, httpAuth }, + router, + } = await createRouter(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + }); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + httpAuth.credentials.mockResolvedValueOnce( + mockCredentials.user('user:default/test-user'), + ); + + auth.isPrincipal.mockReturnValueOnce(true); + + const response = await request(server) + .post(`/api/auth/v1/sessions/${authSession.id}/approve`) + .set('Authorization', `Bearer ${MOCK_USER_TOKEN}`) + .expect(200); + + expect(response.body).toEqual({ + redirectUrl: expect.stringMatching( + /^https:\/\/example\.com\/callback\?code=[\w-]+&state=test-state$/, + ), + }); + }); + + it('should reject auth session', async () => { + const { + mocks: { service, httpAuth, auth }, + router, + } = await createRouter(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + }); + + httpAuth.credentials.mockResolvedValueOnce( + mockCredentials.user('user:default/test-user'), + ); + + auth.isPrincipal.mockReturnValueOnce(true); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const response = await request(server) + .post(`/api/auth/v1/sessions/${authSession.id}/reject`) + .expect(200); + + expect(response.body).toEqual({ + redirectUrl: expect.stringMatching( + /^https:\/\/example\.com\/callback\?error=access_denied&error_description=User\+denied\+the\+request&state=test-state$/, + ), + }); + }); + }); + + describe('token exchange', () => { + it('should exchange authorization code for tokens', async () => { + const { + mocks: { auth, service, tokenIssuer, httpAuth }, + router, + } = await createRouter(databaseId); + + httpAuth.credentials.mockResolvedValueOnce( + mockCredentials.user('user:default/test-user'), + ); + + auth.isPrincipal.mockReturnValueOnce(true); + + tokenIssuer.issueToken.mockResolvedValue({ + token: 'mock-access-token', + }); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + }); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const approvalResponse = await request(server) + .post(`/api/auth/v1/sessions/${authSession.id}/approve`) + .set('Authorization', `Bearer ${MOCK_USER_TOKEN}`) + .expect(200); + + const redirectUrl = new URL(approvalResponse.body.redirectUrl); + const authorizationCode = redirectUrl.searchParams.get('code'); + + expect(authorizationCode).toBeDefined(); + + const tokenResponse = await request(server) + .post('/api/auth/v1/token') + .send({ + grant_type: 'authorization_code', + code: authorizationCode, + redirect_uri: 'https://example.com/callback', + }) + .expect(200); + + expect(tokenResponse.body).toEqual({ + access_token: 'mock-access-token', + token_type: 'Bearer', + expires_in: 3600, + id_token: 'mock-access-token', + scope: 'openid', + }); + + expect(tokenIssuer.issueToken).toHaveBeenCalledWith({ + claims: { + sub: MOCK_USER_ENTITY_REF, + }, + }); + }); + + it('should exchange authorization code for tokens with PKCE', async () => { + const { + mocks: { auth, service, tokenIssuer, httpAuth }, + router, + } = await createRouter(databaseId); + + tokenIssuer.issueToken.mockResolvedValue({ + token: 'mock-access-token-pkce', + }); + + httpAuth.credentials.mockResolvedValueOnce( + mockCredentials.user('user:default/test-user-pkce'), + ); + + auth.isPrincipal.mockReturnValueOnce(true); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + const codeVerifier = + 'test-code-verifier-123456789012345678901234567890123456789012345'; + const codeChallenge = codeVerifier; + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + codeChallenge, + codeChallengeMethod: 'plain', + }); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const approvalResponse = await request(server) + .post(`/api/auth/v1/sessions/${authSession.id}/approve`) + .set('Authorization', `Bearer ${MOCK_USER_TOKEN}`) + .expect(200); + + const redirectUrl = new URL(approvalResponse.body.redirectUrl); + const authorizationCode = redirectUrl.searchParams.get('code'); + + expect(authorizationCode).toBeDefined(); + + const tokenResponse = await request(server) + .post('/api/auth/v1/token') + .send({ + grant_type: 'authorization_code', + code: authorizationCode, + redirect_uri: 'https://example.com/callback', + code_verifier: codeVerifier, + }) + .expect(200); + + expect(tokenResponse.body).toEqual({ + access_token: 'mock-access-token-pkce', + token_type: 'Bearer', + expires_in: 3600, + id_token: 'mock-access-token-pkce', + scope: 'openid', + }); + + expect(tokenIssuer.issueToken).toHaveBeenCalledWith({ + claims: { + sub: 'user:default/test-user-pkce', + }, + }); + }); + + it('should reject token exchange with invalid authorization code', async () => { + const { router } = await createRouter(databaseId); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const tokenResponse = await request(server) + .post('/api/auth/v1/token') + .send({ + grant_type: 'authorization_code', + code: 'invalid-code', + redirect_uri: 'https://example.com/callback', + }) + .expect(401); + + expect(tokenResponse.body).toEqual({ + error: 'invalid_client', + error_description: 'Invalid authorization code', + }); + }); + + it('should exchange authorization code for tokens with PKCE S256', async () => { + const { + mocks: { auth, service, tokenIssuer, httpAuth }, + router, + } = await createRouter(databaseId); + + tokenIssuer.issueToken.mockResolvedValue({ + token: 'mock-access-token-s256', + }); + + httpAuth.credentials.mockResolvedValueOnce( + mockCredentials.user('user:default/test-user-s256'), + ); + + auth.isPrincipal.mockReturnValueOnce(true); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + const codeVerifier = + 'test-code-verifier-s256-123456789012345678901234567890123456789'; + const codeChallenge = crypto + .createHash('sha256') + .update(codeVerifier) + .digest('base64url'); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + codeChallenge, + codeChallengeMethod: 'S256', + }); + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + httpRouter.use(router.getRouter()); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + const approvalResponse = await request(server) + .post(`/api/auth/v1/sessions/${authSession.id}/approve`) + .set('Authorization', `Bearer ${MOCK_USER_TOKEN}`) + .expect(200); + + const redirectUrl = new URL(approvalResponse.body.redirectUrl); + const authorizationCode = redirectUrl.searchParams.get('code'); + + expect(authorizationCode).toBeDefined(); + + const tokenResponse = await request(server) + .post('/api/auth/v1/token') + .send({ + grant_type: 'authorization_code', + code: authorizationCode, + redirect_uri: 'https://example.com/callback', + code_verifier: codeVerifier, + }) + .expect(200); + + expect(tokenResponse.body).toEqual({ + access_token: 'mock-access-token-s256', + token_type: 'Bearer', + expires_in: 3600, + id_token: 'mock-access-token-s256', + scope: 'openid', + }); + + expect(tokenIssuer.issueToken).toHaveBeenCalledWith({ + claims: { + sub: 'user:default/test-user-s256', + }, + }); + }); }); }); }); diff --git a/plugins/auth-backend/src/service/OidcRouter.ts b/plugins/auth-backend/src/service/OidcRouter.ts index 6ada071c44..9a3308b4eb 100644 --- a/plugins/auth-backend/src/service/OidcRouter.ts +++ b/plugins/auth-backend/src/service/OidcRouter.ts @@ -15,42 +15,72 @@ */ import Router from 'express-promise-router'; import { OidcService } from './OidcService'; -import { AuthenticationError } from '@backstage/errors'; -import { AuthService } from '@backstage/backend-plugin-api'; +import { AuthenticationError, isError } from '@backstage/errors'; +import { + AuthService, + HttpAuthService, + LoggerService, + RootConfigService, +} from '@backstage/backend-plugin-api'; import { TokenIssuer } from '../identity/types'; import { UserInfoDatabase } from '../database/UserInfoDatabase'; +import { OidcDatabase } from '../database/OidcDatabase'; +import { json } from 'express'; export class OidcRouter { - private constructor(private readonly oidc: OidcService) {} + private constructor( + private readonly oidc: OidcService, + private readonly logger: LoggerService, + private readonly auth: AuthService, + private readonly appUrl: string, + private readonly httpAuth: HttpAuthService, + private readonly config: RootConfigService, + ) {} static create(options: { auth: AuthService; tokenIssuer: TokenIssuer; baseUrl: string; + appUrl: string; + logger: LoggerService; userInfo: UserInfoDatabase; + oidc: OidcDatabase; + httpAuth: HttpAuthService; + config: RootConfigService; }) { - return new OidcRouter(OidcService.create(options)); + return new OidcRouter( + OidcService.create(options), + options.logger, + options.auth, + options.appUrl, + options.httpAuth, + options.config, + ); } public getRouter() { const router = Router(); + router.use(json()); + + // OpenID Provider Configuration endpoint + // https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig + // Returns the OpenID Provider Configuration document containing metadata about the provider router.get('/.well-known/openid-configuration', (_req, res) => { res.json(this.oidc.getConfiguration()); }); + // JSON Web Key Set endpoint + // https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.10.1.1 + // Returns the public keys used to verify JWTs issued by this provider router.get('/.well-known/jwks.json', async (_req, res) => { const { keys } = await this.oidc.listPublicKeys(); res.json({ keys }); }); - router.get('/v1/token', (_req, res) => { - res.status(501).send('Not Implemented'); - }); - - // This endpoint doesn't use the regular HttpAuthoidc, since the contract - // is specifically for the header to be communicated in the Authorization - // header, regardless of token type + // UserInfo endpoint + // https://openid.net/specs/openid-connect-core-1_0.html#UserInfo + // Returns claims about the authenticated user using an access token router.get('/v1/userinfo', async (req, res) => { const matches = req.headers.authorization?.match(/^Bearer[ ]+(\S+)$/i); const token = matches?.[1]; @@ -68,6 +98,337 @@ export class OidcRouter { res.json(userInfo); }); + if ( + this.config.getOptionalBoolean( + 'auth.experimentalDynamicClientRegistration.enabled', + ) + ) { + // Authorization endpoint + // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest + // Handles the initial authorization request from the client, validates parameters, + // and redirects to the Authorization Session page for user approval + router.get('/v1/authorize', async (req, res) => { + // todo(blam): maybe add zod types for validating input + const { + client_id: clientId, + redirect_uri: redirectUri, + response_type: responseType, + scope, + state, + nonce, + code_challenge: codeChallenge, + code_challenge_method: codeChallengeMethod, + } = req.query; + + if (!clientId || !redirectUri || !responseType) { + this.logger.error(`Failed to authorize: Missing required parameters`); + return res.status(400).json({ + error: 'invalid_request', + error_description: + 'Missing required parameters: client_id, redirect_uri, response_type', + }); + } + + try { + const result = await this.oidc.createAuthorizationSession({ + clientId: clientId as string, + redirectUri: redirectUri as string, + responseType: responseType as string, + scope: scope as string | undefined, + state: state as string | undefined, + nonce: nonce as string | undefined, + codeChallenge: codeChallenge as string | undefined, + codeChallengeMethod: codeChallengeMethod as string | undefined, + }); + + // todo(blam): maybe this URL could be overridable by config if + // the plugin is mounted somewhere else? + // support slashes in baseUrl? + const authSessionRedirectUrl = new URL( + `./oauth2/authorize/${result.id}`, + ensureTrailingSlash(this.appUrl), + ); + + return res.redirect(authSessionRedirectUrl.toString()); + } catch (error) { + const errorParams = new URLSearchParams(); + errorParams.append( + 'error', + isError(error) ? error.name : 'server_error', + ); + errorParams.append( + 'error_description', + isError(error) ? error.message : 'Unknown error', + ); + if (state) { + errorParams.append('state', state as string); + } + + const redirectUrl = new URL(redirectUri as string); + redirectUrl.search = errorParams.toString(); + return res.redirect(redirectUrl.toString()); + } + }); + + // Authorization Session request details endpoint + // Returns Authorization Session request details for the frontend + router.get('/v1/sessions/:sessionId', async (req, res) => { + const { sessionId } = req.params; + + if (!sessionId) { + return res.status(400).json({ + error: 'invalid_request', + error_description: 'Missing Authorization Session ID', + }); + } + + try { + const session = await this.oidc.getAuthorizationSession({ + sessionId, + }); + + return res.json({ + id: session.id, + clientName: session.clientName, + scope: session.scope, + redirectUri: session.redirectUri, + }); + } catch (error) { + const description = isError(error) ? error.message : 'Unknown error'; + this.logger.error( + `Failed to get authorization session: ${description}`, + error, + ); + return res.status(404).json({ + error: 'not_found', + error_description: description, + }); + } + }); + + // Authorization Session approval endpoint + // Handles user approval of Authorization Session requests and generates authorization codes + router.post('/v1/sessions/:sessionId/approve', async (req, res) => { + const { sessionId } = req.params; + + if (!sessionId) { + return res.status(400).json({ + error: 'invalid_request', + error_description: 'Missing authorization session ID', + }); + } + + try { + const httpCredentials = await this.httpAuth.credentials(req); + + if (!this.auth.isPrincipal(httpCredentials, 'user')) { + return res.status(401).json({ + error: 'unauthorized', + error_description: 'Authentication required', + }); + } + + const { userEntityRef } = httpCredentials.principal; + + const result = await this.oidc.approveAuthorizationSession({ + sessionId, + userEntityRef, + }); + + return res.json({ + redirectUrl: result.redirectUrl, + }); + } catch (error) { + const description = isError(error) ? error.message : 'Unknown error'; + this.logger.error( + `Failed to approve authorization session: ${description}`, + error, + ); + return res.status(400).json({ + error: 'invalid_request', + error_description: description, + }); + } + }); + + // Authorization Session rejection endpoint + // Handles user rejection of Authorization Session requests and redirects with error + router.post('/v1/sessions/:sessionId/reject', async (req, res) => { + const { sessionId } = req.params; + + if (!sessionId) { + return res.status(400).json({ + error: 'invalid_request', + error_description: 'Missing authorization session ID', + }); + } + + const httpCredentials = await this.httpAuth.credentials(req); + + if (!this.auth.isPrincipal(httpCredentials, 'user')) { + return res.status(401).json({ + error: 'unauthorized', + error_description: 'Authentication required', + }); + } + + const { userEntityRef } = httpCredentials.principal; + try { + const session = await this.oidc.getAuthorizationSession({ + sessionId, + }); + + await this.oidc.rejectAuthorizationSession({ + sessionId, + userEntityRef, + }); + + const errorParams = new URLSearchParams(); + errorParams.append('error', 'access_denied'); + errorParams.append('error_description', 'User denied the request'); + if (session.state) { + errorParams.append('state', session.state); + } + + const redirectUrl = new URL(session.redirectUri); + redirectUrl.search = errorParams.toString(); + + return res.json({ + redirectUrl: redirectUrl.toString(), + }); + } catch (error) { + const description = isError(error) ? error.message : 'Unknown error'; + this.logger.error( + `Failed to reject authorization session: ${description}`, + error, + ); + + return res.status(400).json({ + error: 'invalid_request', + error_description: description, + }); + } + }); + + // Token endpoint + // https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest + // Exchanges authorization codes for access tokens and ID tokens + router.post('/v1/token', async (req, res) => { + // todo(blam): maybe add zod types for validating input + const { + grant_type: grantType, + code, + redirect_uri: redirectUri, + code_verifier: codeVerifier, + } = req.body; + + if (!grantType || !code || !redirectUri) { + this.logger.error( + `Failed to exchange code for token: Missing required parameters`, + ); + return res.status(400).json({ + error: 'invalid_request', + error_description: 'Missing required parameters', + }); + } + + try { + const result = await this.oidc.exchangeCodeForToken({ + code, + redirectUri, + codeVerifier, + grantType, + }); + + return res.json({ + access_token: result.accessToken, + token_type: result.tokenType, + expires_in: result.expiresIn, + id_token: result.idToken, + scope: result.scope, + }); + } catch (error) { + const description = isError(error) ? error.message : 'Unknown error'; + this.logger.error( + `Failed to exchange code for token: ${description}`, + error, + ); + + if (isError(error)) { + if (error.name === 'AuthenticationError') { + return res.status(401).json({ + error: 'invalid_client', + error_description: error.message, + }); + } + if (error.name === 'InputError') { + return res.status(400).json({ + error: 'invalid_request', + error_description: error.message, + }); + } + } + + return res.status(500).json({ + error: 'server_error', + error_description: description, + }); + } + }); + + // Dynamic Client Registration endpoint + // https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration + // Allows clients to register themselves dynamically with the provider + router.post('/v1/register', async (req, res) => { + // todo(blam): maybe add zod types for validating input + const { + client_name: clientName, + redirect_uris: redirectUris, + response_types: responseTypes, + grant_types: grantTypes, + scope, + } = req.body; + + if (!redirectUris?.length) { + res.status(400).json({ + error: 'invalid_request', + error_description: 'redirect_uris is required', + }); + return; + } + + try { + const client = await this.oidc.registerClient({ + clientName, + redirectUris, + responseTypes, + grantTypes, + scope, + }); + + res.status(201).json({ + client_id: client.clientId, + redirect_uris: client.redirectUris, + client_secret: client.clientSecret, + }); + } catch (e) { + const description = isError(e) ? e.message : 'Unknown error'; + this.logger.error(`Failed to register client: ${description}`, e); + + res.status(500).json({ + error: 'server_error', + error_description: `Failed to register client: ${description}`, + }); + } + }); + } + return router; } } +function ensureTrailingSlash(appUrl: string): string | URL | undefined { + if (appUrl.endsWith('/')) { + return appUrl; + } + return `${appUrl}/`; +} diff --git a/plugins/auth-backend/src/service/OidcService.test.ts b/plugins/auth-backend/src/service/OidcService.test.ts new file mode 100644 index 0000000000..e4e1673397 --- /dev/null +++ b/plugins/auth-backend/src/service/OidcService.test.ts @@ -0,0 +1,789 @@ +/* + * Copyright 2025 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, + TestDatabaseId, + TestDatabases, +} from '@backstage/backend-test-utils'; +import { OidcService } from './OidcService'; +import { + BackstageCredentials, + BackstageServicePrincipal, + BackstageUserPrincipal, + resolvePackagePath, +} from '@backstage/backend-plugin-api'; +import { AuthDatabase } from '../database/AuthDatabase'; +import { OidcDatabase } from '../database/OidcDatabase'; +import { UserInfoDatabase } from '../database/UserInfoDatabase'; +import crypto from 'crypto'; +import { AnyJWK, TokenIssuer } from '../identity/types'; + +jest.setTimeout(60_000); + +describe('OidcService', () => { + const databases = TestDatabases.create(); + + async function createOidcService(databaseId: TestDatabaseId) { + const knex = await databases.init(databaseId); + + await knex.migrate.latest({ + directory: resolvePackagePath( + '@backstage/plugin-auth-backend', + 'migrations', + ), + }); + + const oidcDatabase = await OidcDatabase.create({ + database: AuthDatabase.create({ + getClient: async () => knex, + }), + }); + + const mockAuth = mockServices.auth.mock(); + const mockTokenIssuer = { + issueToken: jest.fn(), + listPublicKeys: jest.fn(), + } as jest.Mocked; + + const mockUserInfo = { + addUserInfo: jest.fn(), + getUserInfo: jest.fn(), + } as unknown as jest.Mocked; + + const mockConfig = mockServices.rootConfig.mock(); + + return { + service: OidcService.create({ + auth: mockAuth, + tokenIssuer: mockTokenIssuer, + baseUrl: 'http://mock-base-url', + userInfo: mockUserInfo, + oidc: oidcDatabase, + config: mockConfig, + }), + mocks: { + auth: mockAuth, + tokenIssuer: mockTokenIssuer, + userInfo: mockUserInfo, + config: mockConfig, + }, + }; + } + + describe.each(databases.eachSupportedId())('%p', databaseId => { + describe('getConfiguration', () => { + it('should return OIDC configuration', async () => { + const { service } = await createOidcService(databaseId); + + const config = service.getConfiguration(); + + expect(config).toEqual({ + issuer: 'http://mock-base-url', + token_endpoint: 'http://mock-base-url/v1/token', + userinfo_endpoint: 'http://mock-base-url/v1/userinfo', + jwks_uri: 'http://mock-base-url/.well-known/jwks.json', + response_types_supported: ['code', 'id_token'], + subject_types_supported: ['public'], + id_token_signing_alg_values_supported: [ + 'RS256', + 'RS384', + 'RS512', + 'ES256', + 'ES384', + 'ES512', + 'PS256', + 'PS384', + 'PS512', + 'EdDSA', + ], + scopes_supported: ['openid'], + token_endpoint_auth_methods_supported: [ + 'client_secret_basic', + 'client_secret_post', + ], + claims_supported: ['sub', 'ent'], + grant_types_supported: ['authorization_code'], + authorization_endpoint: 'http://mock-base-url/v1/authorize', + registration_endpoint: 'http://mock-base-url/v1/register', + code_challenge_methods_supported: ['S256', 'plain'], + }); + }); + }); + + describe('listPublicKeys', () => { + it('should return public keys from token issuer', async () => { + const { service, mocks } = await createOidcService(databaseId); + const mockKeys = [{ kid: 'key-1', use: 'sig' }] as AnyJWK[]; + mocks.tokenIssuer.listPublicKeys.mockResolvedValue({ keys: mockKeys }); + + const { keys } = await service.listPublicKeys(); + + expect(keys).toEqual(mockKeys); + expect(mocks.tokenIssuer.listPublicKeys).toHaveBeenCalledTimes(1); + }); + }); + + describe('getUserInfo', () => { + it('should return user info for valid token', async () => { + const { service, mocks } = await createOidcService(databaseId); + const mockCredentials: BackstageCredentials = { + principal: { + type: 'user', + userEntityRef: 'user:default/test', + }, + $$type: '@backstage/BackstageCredentials', + }; + const mockUserInfo = { sub: 'user:default/test', name: 'Test User' }; + + mocks.auth.authenticate.mockResolvedValue(mockCredentials); + mocks.auth.isPrincipal.mockReturnValue(true); + mocks.userInfo.getUserInfo.mockResolvedValue({ + claims: mockUserInfo, + }); + + const mockToken = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvdGVzdCJ9.signature'; + + const userInfo = await service.getUserInfo({ token: mockToken }); + + expect(userInfo).toEqual({ + claims: mockUserInfo, + }); + + expect(mocks.auth.authenticate).toHaveBeenCalledWith(mockToken, { + allowLimitedAccess: true, + }); + + expect(mocks.userInfo.getUserInfo).toHaveBeenCalledWith( + 'user:default/test', + ); + }); + + it('should throw error for non-user principal', async () => { + const { service, mocks } = await createOidcService(databaseId); + const mockCredentials: BackstageCredentials = + { + principal: { + type: 'service', + subject: 'test-service', + }, + $$type: '@backstage/BackstageCredentials', + }; + + mocks.auth.authenticate.mockResolvedValue(mockCredentials); + mocks.auth.isPrincipal.mockReturnValue(false); + + const mockToken = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyOmRlZmF1bHQvdGVzdCJ9.signature'; + + await expect(service.getUserInfo({ token: mockToken })).rejects.toThrow( + 'Userinfo endpoint must be called with a token that represents a user principal', + ); + }); + }); + + describe('registerClient', () => { + it('should create a new client with generated credentials', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }); + + expect(client).toEqual( + expect.objectContaining({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + scope: 'openid', + }), + ); + expect(client.clientId).toBeDefined(); + expect(client.clientSecret).toBeDefined(); + }); + + it('should throw an error for invalid redirect URI', async () => { + const { + service, + mocks: { config }, + } = await createOidcService(databaseId); + + config.getOptionalStringArray.mockReturnValue([ + 'https://example.com/*', + ]); + + await expect( + service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://invalid.com/callback'], + }), + ).rejects.toThrow('Invalid redirect_uri'); + }); + + it('should create a new client with valid redirect URI', async () => { + const { + service, + mocks: { config }, + } = await createOidcService(databaseId); + + config.getOptionalStringArray.mockReturnValue(['cursor:*']); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['cursor://callback/asd?asd=asd'], + }); + + expect(client).toEqual( + expect.objectContaining({ + redirectUris: ['cursor://callback/asd?asd=asd'], + }), + ); + }); + + it('should create a client with default values', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + }); + + expect(client).toEqual( + expect.objectContaining({ + clientName: 'Test Client', + redirectUris: [], + responseTypes: ['code'], + grantTypes: ['authorization_code'], + }), + ); + }); + }); + + describe('createAuthorizationSession', () => { + it('should create a authorization session for valid client', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + }); + + expect(authSession).toEqual({ + id: expect.any(String), + clientName: 'Test Client', + scope: 'openid', + redirectUri: 'https://example.com/callback', + }); + }); + + it('should throw error for invalid client', async () => { + const { service } = await createOidcService(databaseId); + + await expect( + service.createAuthorizationSession({ + clientId: 'invalid-client', + redirectUri: 'https://example.com/callback', + responseType: 'code', + }), + ).rejects.toThrow('Invalid client_id'); + }); + + it('should throw error for invalid redirect URI', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + await expect( + service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://invalid.com/callback', + responseType: 'code', + }), + ).rejects.toThrow('Invalid redirect_uri'); + }); + + it('should throw error for unsupported response type', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + await expect( + service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'token', + }), + ).rejects.toThrow('Only authorization code flow is supported'); + }); + + it('should handle PKCE parameters', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + codeChallenge: 'test-challenge', + codeChallengeMethod: 'S256', + }); + + expect(authSession.id).toBeDefined(); + }); + + it('should throw error for invalid PKCE method', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + await expect( + service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + codeChallenge: 'test-challenge', + codeChallengeMethod: 'invalid', + }), + ).rejects.toThrow('Invalid code_challenge_method'); + }); + }); + + describe('approveAuthorizationSession', () => { + it('should approve a valid authorization session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + state: 'test-state', + }); + + const result = await service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + expect(result.redirectUrl).toMatch( + /^https:\/\/example\.com\/callback\?code=.+&state=test-state$/, + ); + }); + + it('should throw error for invalid authorization session', async () => { + const { service } = await createOidcService(databaseId); + + await expect( + service.approveAuthorizationSession({ + sessionId: 'invalid-session', + userEntityRef: 'user:default/test', + }), + ).rejects.toThrow('Invalid authorization session'); + }); + + it('should throw error when trying to approve an already approved session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + }); + + await service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + await expect( + service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }), + ).rejects.toThrow('Authorization session not found or expired'); + }); + + it('should throw error when trying to approve an already rejected session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + }); + + await service.rejectAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + await expect( + service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }), + ).rejects.toThrow('Authorization session not found or expired'); + }); + }); + + describe('getAuthorizationSession', () => { + it('should return authorization session details', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + state: 'test-state', + }); + + const details = await service.getAuthorizationSession({ + sessionId: authSession.id, + }); + + expect(details).toEqual( + expect.objectContaining({ + id: authSession.id, + clientId: client.clientId, + clientName: 'Test Client', + redirectUri: 'https://example.com/callback', + scope: 'openid', + state: 'test-state', + responseType: 'code', + }), + ); + }); + + it('should throw error when trying to get an already approved session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + }); + + await service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + await expect( + service.getAuthorizationSession({ + sessionId: authSession.id, + }), + ).rejects.toThrow('Authorization session not found or expired'); + }); + + it('should throw error when trying to get an already rejected session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + }); + + await service.rejectAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + await expect( + service.getAuthorizationSession({ + sessionId: authSession.id, + }), + ).rejects.toThrow('Authorization session not found or expired'); + }); + }); + + describe('rejectAuthorizationSession', () => { + it('should reject a authorization session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + }); + + await service.rejectAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + await expect( + service.getAuthorizationSession({ + sessionId: authSession.id, + }), + ).rejects.toThrow('Authorization session not found or expired'); + }); + + it('should throw error for invalid authorization session', async () => { + const { service } = await createOidcService(databaseId); + + await expect( + service.rejectAuthorizationSession({ + sessionId: 'invalid-session', + userEntityRef: 'user:default/test', + }), + ).rejects.toThrow('Invalid authorization session'); + }); + + it('should throw error when trying to reject an already approved session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + }); + + await service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + await expect( + service.rejectAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }), + ).rejects.toThrow('Authorization session not found or expired'); + }); + + it('should throw error when trying to reject an already rejected session', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + }); + + await service.rejectAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + await expect( + service.rejectAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }), + ).rejects.toThrow('Authorization session not found or expired'); + }); + }); + + describe('exchangeCodeForToken', () => { + it('should exchange valid code for tokens', async () => { + const { service, mocks } = await createOidcService(databaseId); + const mockToken = 'mock-jwt-token'; + mocks.tokenIssuer.issueToken.mockResolvedValue({ token: mockToken }); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + scope: 'openid', + }); + + const authResult = await service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + const code = new URL(authResult.redirectUrl).searchParams.get('code')!; + + const tokenResult = await service.exchangeCodeForToken({ + code, + redirectUri: 'https://example.com/callback', + grantType: 'authorization_code', + }); + + expect(tokenResult).toEqual({ + accessToken: mockToken, + tokenType: 'Bearer', + expiresIn: 3600, + idToken: mockToken, + scope: 'openid', + }); + }); + + it('should throw error for invalid grant type', async () => { + const { service } = await createOidcService(databaseId); + + await expect( + service.exchangeCodeForToken({ + code: 'test-code', + redirectUri: 'https://example.com/callback', + grantType: 'client_credentials', + }), + ).rejects.toThrow('Unsupported grant type'); + }); + + it('should handle PKCE verification', async () => { + const { service, mocks } = await createOidcService(databaseId); + const mockToken = 'mock-jwt-token'; + mocks.tokenIssuer.issueToken.mockResolvedValue({ token: mockToken }); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const codeVerifier = 'test-code-verifier'; + const codeChallenge = crypto + .createHash('sha256') + .update(codeVerifier) + .digest('base64url'); + + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + codeChallenge, + codeChallengeMethod: 'S256', + }); + + const authResult = await service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + const code = new URL(authResult.redirectUrl).searchParams.get('code')!; + + const tokenResult = await service.exchangeCodeForToken({ + code, + redirectUri: 'https://example.com/callback', + grantType: 'authorization_code', + codeVerifier, + }); + + expect(tokenResult.accessToken).toBe(mockToken); + }); + + it('should throw error for invalid PKCE verifier', async () => { + const { service } = await createOidcService(databaseId); + + const client = await service.registerClient({ + clientName: 'Test Client', + redirectUris: ['https://example.com/callback'], + }); + + const codeChallenge = 'test-challenge'; + const authSession = await service.createAuthorizationSession({ + clientId: client.clientId, + redirectUri: 'https://example.com/callback', + responseType: 'code', + codeChallenge, + codeChallengeMethod: 'S256', + }); + + const authResult = await service.approveAuthorizationSession({ + sessionId: authSession.id, + userEntityRef: 'user:default/test', + }); + + const code = new URL(authResult.redirectUrl).searchParams.get('code')!; + + await expect( + service.exchangeCodeForToken({ + code, + redirectUri: 'https://example.com/callback', + grantType: 'authorization_code', + codeVerifier: 'invalid-verifier', + }), + ).rejects.toThrow('Invalid code verifier'); + }); + }); + }); +}); diff --git a/plugins/auth-backend/src/service/OidcService.ts b/plugins/auth-backend/src/service/OidcService.ts index 5024b2cc8c..b4c6bb122b 100644 --- a/plugins/auth-backend/src/service/OidcService.ts +++ b/plugins/auth-backend/src/service/OidcService.ts @@ -13,11 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AuthService } from '@backstage/backend-plugin-api'; +import { AuthService, RootConfigService } from '@backstage/backend-plugin-api'; import { TokenIssuer } from '../identity/types'; import { UserInfoDatabase } from '../database/UserInfoDatabase'; -import { InputError } from '@backstage/errors'; +import { + InputError, + AuthenticationError, + NotFoundError, +} from '@backstage/errors'; import { decodeJwt } from 'jose'; +import crypto from 'crypto'; +import { OidcDatabase } from '../database/OidcDatabase'; +import { DateTime } from 'luxon'; +import matcher from 'matcher'; export class OidcService { private constructor( @@ -25,6 +33,8 @@ export class OidcService { private readonly tokenIssuer: TokenIssuer, private readonly baseUrl: string, private readonly userInfo: UserInfoDatabase, + private readonly oidc: OidcDatabase, + private readonly config: RootConfigService, ) {} static create(options: { @@ -32,12 +42,16 @@ export class OidcService { tokenIssuer: TokenIssuer; baseUrl: string; userInfo: UserInfoDatabase; + oidc: OidcDatabase; + config: RootConfigService; }) { return new OidcService( options.auth, options.tokenIssuer, options.baseUrl, options.userInfo, + options.oidc, + options.config, ); } @@ -47,7 +61,7 @@ export class OidcService { token_endpoint: `${this.baseUrl}/v1/token`, userinfo_endpoint: `${this.baseUrl}/v1/userinfo`, jwks_uri: `${this.baseUrl}/.well-known/jwks.json`, - response_types_supported: ['id_token'], + response_types_supported: ['code', 'id_token'], subject_types_supported: ['public'], id_token_signing_alg_values_supported: [ 'RS256', @@ -62,9 +76,15 @@ export class OidcService { 'EdDSA', ], scopes_supported: ['openid'], - token_endpoint_auth_methods_supported: [], + token_endpoint_auth_methods_supported: [ + 'client_secret_basic', + 'client_secret_post', + ], claims_supported: ['sub', 'ent'], - grant_types_supported: [], + grant_types_supported: ['authorization_code'], + authorization_endpoint: `${this.baseUrl}/v1/authorize`, + registration_endpoint: `${this.baseUrl}/v1/register`, + code_challenge_methods_supported: ['S256', 'plain'], }; } @@ -89,4 +109,321 @@ export class OidcService { } return await this.userInfo.getUserInfo(userEntityRef); } + + public async registerClient(opts: { + responseTypes?: string[]; + grantTypes?: string[]; + clientName: string; + redirectUris?: string[]; + scope?: string; + }) { + const generatedClientId = crypto.randomUUID(); + const generatedClientSecret = crypto.randomUUID(); + + const allowedRedirectUriPatterns = this.config.getOptionalStringArray( + 'auth.experimentalDynamicClientRegistration.allowedRedirectUriPatterns', + ) ?? ['*']; + + for (const redirectUri of opts.redirectUris ?? []) { + if ( + !allowedRedirectUriPatterns.some(pattern => + matcher.isMatch(redirectUri, pattern), + ) + ) { + throw new InputError('Invalid redirect_uri'); + } + } + + return await this.oidc.createClient({ + clientId: generatedClientId, + clientName: opts.clientName, + clientSecret: generatedClientSecret, + redirectUris: opts.redirectUris ?? [], + responseTypes: opts.responseTypes ?? ['code'], + grantTypes: opts.grantTypes ?? ['authorization_code'], + scope: opts.scope, + }); + } + + public async createAuthorizationSession(opts: { + clientId: string; + redirectUri: string; + responseType: string; + scope?: string; + state?: string; + nonce?: string; + codeChallenge?: string; + codeChallengeMethod?: string; + }) { + const { + clientId, + redirectUri, + responseType, + scope, + state, + nonce, + codeChallenge, + codeChallengeMethod, + } = opts; + + if (responseType !== 'code') { + throw new InputError('Only authorization code flow is supported'); + } + + const client = await this.oidc.getClient({ clientId }); + if (!client) { + throw new InputError('Invalid client_id'); + } + + if (!client.redirectUris.includes(redirectUri)) { + throw new InputError('Invalid redirect_uri'); + } + + if (codeChallenge) { + if ( + !codeChallengeMethod || + !['S256', 'plain'].includes(codeChallengeMethod) + ) { + throw new InputError('Invalid code_challenge_method'); + } + } + + const sessionId = crypto.randomUUID(); + const sessionExpiresAt = DateTime.now().plus({ hours: 1 }).toJSDate(); + + await this.oidc.createAuthorizationSession({ + id: sessionId, + clientId, + redirectUri, + responseType, + scope, + state, + codeChallenge, + codeChallengeMethod, + nonce, + expiresAt: sessionExpiresAt, + }); + + return { + id: sessionId, + clientName: client.clientName, + scope, + redirectUri, + }; + } + + public async approveAuthorizationSession(opts: { + sessionId: string; + userEntityRef: string; + }) { + const { sessionId, userEntityRef } = opts; + + const session = await this.oidc.getAuthorizationSession({ + id: sessionId, + }); + + if (!session) { + throw new NotFoundError('Invalid authorization session'); + } + + if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) { + throw new InputError('Authorization session expired'); + } + + if (session.status !== 'pending') { + throw new NotFoundError('Authorization session not found or expired'); + } + + await this.oidc.updateAuthorizationSession({ + id: session.id, + userEntityRef, + status: 'approved', + }); + + const authorizationCode = crypto.randomBytes(32).toString('base64url'); + const codeExpiresAt = DateTime.now().plus({ minutes: 10 }).toJSDate(); + + await this.oidc.createAuthorizationCode({ + code: authorizationCode, + sessionId: session.id, + expiresAt: codeExpiresAt, + }); + + const redirectUrl = new URL(session.redirectUri); + + redirectUrl.searchParams.append('code', authorizationCode); + if (session.state) { + redirectUrl.searchParams.append('state', session.state); + } + + return { + redirectUrl: redirectUrl.toString(), + }; + } + + public async getAuthorizationSession(opts: { sessionId: string }) { + const session = await this.oidc.getAuthorizationSession({ + id: opts.sessionId, + }); + + if (!session) { + throw new NotFoundError('Invalid authorization session'); + } + + if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) { + throw new InputError('Authorization session expired'); + } + + if (session.status !== 'pending') { + throw new NotFoundError('Authorization session not found or expired'); + } + + const client = await this.oidc.getClient({ clientId: session.clientId }); + if (!client) { + throw new InputError('Invalid client_id'); + } + + return { + id: session.id, + clientId: session.clientId, + clientName: client.clientName, + redirectUri: session.redirectUri, + scope: session.scope, + state: session.state, + responseType: session.responseType, + codeChallenge: session.codeChallenge, + codeChallengeMethod: session.codeChallengeMethod, + nonce: session.nonce, + expiresAt: session.expiresAt, + status: session.status, + }; + } + + public async rejectAuthorizationSession(opts: { + sessionId: string; + userEntityRef: string; + }) { + const { sessionId, userEntityRef } = opts; + + const session = await this.oidc.getAuthorizationSession({ + id: sessionId, + }); + + if (!session) { + throw new NotFoundError('Invalid authorization session'); + } + + if (DateTime.fromJSDate(session.expiresAt) < DateTime.now()) { + throw new InputError('Authorization session expired'); + } + + if (session.status !== 'pending') { + throw new NotFoundError('Authorization session not found or expired'); + } + + await this.oidc.updateAuthorizationSession({ + id: session.id, + status: 'rejected', + userEntityRef, + }); + } + + public async exchangeCodeForToken(params: { + code: string; + redirectUri: string; + codeVerifier?: string; + grantType: string; + }) { + const { code, redirectUri, codeVerifier, grantType } = params; + + if (grantType !== 'authorization_code') { + throw new InputError('Unsupported grant type'); + } + + const authCode = await this.oidc.getAuthorizationCode({ code }); + if (!authCode) { + throw new AuthenticationError('Invalid authorization code'); + } + + if (DateTime.fromJSDate(authCode.expiresAt) < DateTime.now()) { + throw new AuthenticationError('Authorization code expired'); + } + + if (authCode.used) { + throw new AuthenticationError('Authorization code already used'); + } + + const session = await this.oidc.getAuthorizationSession({ + id: authCode.sessionId, + }); + + if (!session) { + throw new NotFoundError('Invalid authorization session'); + } + + if (session.redirectUri !== redirectUri) { + throw new AuthenticationError('Redirect URI mismatch'); + } + + if (session.status !== 'approved') { + throw new AuthenticationError('Authorization not approved'); + } + + if (!session.userEntityRef) { + throw new AuthenticationError('No user associated with authorization'); + } + + if (session.codeChallenge) { + if (!codeVerifier) { + throw new AuthenticationError('Code verifier required for PKCE'); + } + + if ( + !this.verifyPkce( + session.codeChallenge, + codeVerifier, + session.codeChallengeMethod, + ) + ) { + throw new AuthenticationError('Invalid code verifier'); + } + } + + await this.oidc.updateAuthorizationCode({ + code, + used: true, + }); + + const { token } = await this.tokenIssuer.issueToken({ + claims: { + sub: session.userEntityRef, + }, + }); + + return { + accessToken: token, + tokenType: 'Bearer', + expiresIn: 3600, + idToken: token, + scope: session.scope || 'openid', + }; + } + + private verifyPkce( + codeChallenge: string, + codeVerifier: string, + method?: string, + ): boolean { + if (!method || method === 'plain') { + return codeChallenge === codeVerifier; + } + + if (method === 'S256') { + const hash = crypto.createHash('sha256').update(codeVerifier).digest(); + const base64urlHash = hash.toString('base64url'); + return codeChallenge === base64urlHash; + } + + return false; + } } diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 5012c90106..0f0d5b2830 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -21,6 +21,7 @@ import { AuthService, DatabaseService, DiscoveryService, + HttpAuthService, LoggerService, RootConfigService, } from '@backstage/backend-plugin-api'; @@ -40,6 +41,7 @@ import { StaticTokenIssuer } from '../identity/StaticTokenIssuer'; import { StaticKeyStore } from '../identity/StaticKeyStore'; import { bindProviderRouters, ProviderFactories } from '../providers/router'; import { OidcRouter } from './OidcRouter'; +import { OidcDatabase } from '../database/OidcDatabase'; interface RouterOptions { logger: LoggerService; @@ -51,6 +53,7 @@ interface RouterOptions { providerFactories?: ProviderFactories; catalog: CatalogService; ownershipResolver?: AuthOwnershipResolver; + httpAuth: HttpAuthService; } export async function createRouter( @@ -63,6 +66,7 @@ export async function createRouter( database: db, tokenFactoryAlgorithm, providerFactories = {}, + httpAuth, } = options; const router = Router(); @@ -147,11 +151,18 @@ export async function createRouter( userInfo, }); + const oidc = await OidcDatabase.create({ database }); + const oidcRouter = OidcRouter.create({ auth: options.auth, tokenIssuer, baseUrl: authUrl, + appUrl, userInfo, + oidc, + logger, + httpAuth, + config, }); router.use(oidcRouter.getRouter()); diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 48554f6932..3c2ceea7c0 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-node +## 0.6.7-next.1 + +### Patch Changes + +- 54ddfef: Updating plugin metadata +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + ## 0.6.7-next.0 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 23cff9720c..64c12ae977 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,10 +1,11 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.6.7-next.0", + "version": "0.6.7-next.1", "backstage": { "role": "node-library", "pluginId": "auth", "pluginPackages": [ + "@backstage/plugin-auth", "@backstage/plugin-auth-backend", "@backstage/plugin-auth-node", "@backstage/plugin-auth-react" diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index 1dc720ab67..9b314d3e4f 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-react +## 0.1.19-next.1 + +### Patch Changes + +- 54ddfef: Updating plugin metadata +- Updated dependencies + - @backstage/core-components@0.17.6-next.1 + ## 0.1.19-next.0 ### Patch Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index 4335bbc03a..2bfee0dd44 100644 --- a/plugins/auth-react/package.json +++ b/plugins/auth-react/package.json @@ -1,11 +1,12 @@ { "name": "@backstage/plugin-auth-react", - "version": "0.1.19-next.0", + "version": "0.1.19-next.1", "description": "Web library for the auth plugin", "backstage": { "role": "web-library", "pluginId": "auth", "pluginPackages": [ + "@backstage/plugin-auth", "@backstage/plugin-auth-backend", "@backstage/plugin-auth-node", "@backstage/plugin-auth-react" diff --git a/plugins/auth/.eslintrc.js b/plugins/auth/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth/CHANGELOG.md b/plugins/auth/CHANGELOG.md new file mode 100644 index 0000000000..d28fef1c9e --- /dev/null +++ b/plugins/auth/CHANGELOG.md @@ -0,0 +1,12 @@ +# @backstage/plugin-auth + +## 0.1.0-next.0 + +### Minor Changes + +- 54ddfef: Initial publish of the `auth` frontend package + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.6-next.1 diff --git a/plugins/auth/README.md b/plugins/auth/README.md new file mode 100644 index 0000000000..440d7d294a --- /dev/null +++ b/plugins/auth/README.md @@ -0,0 +1,16 @@ +# @backstage/plugin-auth + +A Backstage frontend plugin that provides user interface components for authentication flows, specifically for OpenID Connect (OIDC) consent management. + +## Installation + +This plugin is designed to work with the `@backstage/plugin-auth-backend` package that provides OIDC provider functionality. + +```bash +# From your Backstage app directory +yarn --cwd packages/app add @backstage/plugin-auth +``` + +## Usage + +The plugin provides the route `/oauth2/authorize/:sessionId` for approving of oauth2 sessions for clients. You should see an approval flow for any sessions created through the `auth-backend`. diff --git a/plugins/auth/catalog-info.yaml b/plugins/auth/catalog-info.yaml new file mode 100644 index 0000000000..66d835a198 --- /dev/null +++ b/plugins/auth/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-auth + title: '@backstage/plugin-auth' +spec: + lifecycle: experimental + type: backstage-frontend-plugin + owner: auth-maintainers diff --git a/plugins/auth/dev/index.tsx b/plugins/auth/dev/index.tsx new file mode 100644 index 0000000000..b0762416c6 --- /dev/null +++ b/plugins/auth/dev/index.tsx @@ -0,0 +1,28 @@ +/* + * Copyright 2025 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 { createApp } from '@backstage/frontend-defaults'; +import { createRoot } from 'react-dom/client'; + +import plugin from '../src'; + +const app = createApp({ + features: [plugin], +}); + +const container = document.getElementById('root'); +const root = createRoot(container!); +root.render(app.createRoot()); diff --git a/plugins/auth/package.json b/plugins/auth/package.json new file mode 100644 index 0000000000..6c8184db35 --- /dev/null +++ b/plugins/auth/package.json @@ -0,0 +1,84 @@ +{ + "name": "@backstage/plugin-auth", + "version": "0.1.0-next.0", + "backstage": { + "role": "frontend-plugin", + "pluginId": "auth", + "pluginPackages": [ + "@backstage/plugin-auth", + "@backstage/plugin-auth-backend", + "@backstage/plugin-auth-node", + "@backstage/plugin-auth-react" + ] + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/auth" + }, + "license": "Apache-2.0", + "sideEffects": false, + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + }, + "main": "src/index.ts", + "types": "src/index.ts", + "typesVersions": { + "*": { + "package.json": [ + "package.json" + ] + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" + }, + "dependencies": { + "@backstage/core-components": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.61", + "react-use": "^17.2.4" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/frontend-defaults": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^16.0.0", + "@testing-library/user-event": "^14.0.0", + "@types/react": "^18.0.0", + "msw": "^1.0.0", + "react": "^18.0.2", + "react-dom": "^18.0.2", + "react-router-dom": "^6.3.0" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0", + "react-router-dom": "^6.3.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } +} diff --git a/plugins/auth/report.api.md b/plugins/auth/report.api.md new file mode 100644 index 0000000000..4c7d2d4a11 --- /dev/null +++ b/plugins/auth/report.api.md @@ -0,0 +1,52 @@ +## API Report File for "@backstage/plugin-auth" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { JSX as JSX_2 } from 'react'; +import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { RouteRef } from '@backstage/frontend-plugin-api'; + +// @public (undocumented) +const _default: OverridableFrontendPlugin< + { + root: RouteRef; + }, + {}, + { + 'page:auth': ExtensionDefinition<{ + kind: 'page'; + name: undefined; + config: { + path: string | undefined; + }; + configInput: { + path?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + >; + inputs: {}; + params: { + defaultPath?: [Error: `Use the 'path' param instead`]; + path: string; + loader: () => Promise; + routeRef?: RouteRef; + }; + }>; + } +>; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/auth/src/components/ConsentPage/ConsentPage.tsx b/plugins/auth/src/components/ConsentPage/ConsentPage.tsx new file mode 100644 index 0000000000..0f071025a2 --- /dev/null +++ b/plugins/auth/src/components/ConsentPage/ConsentPage.tsx @@ -0,0 +1,246 @@ +/* + * Copyright 2025 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 { useParams } from 'react-router-dom'; + +import { + Box, + Button, + Card, + CardContent, + CardActions, + Typography, + makeStyles, + Divider, +} from '@material-ui/core'; +import { Alert } from '@material-ui/lab'; +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import CancelIcon from '@material-ui/icons/Cancel'; +import AppsIcon from '@material-ui/icons/Apps'; +import WarningIcon from '@material-ui/icons/Warning'; +import { + Header, + Page, + Content, + Progress, + EmptyState, + ResponseErrorPanel, +} from '@backstage/core-components'; +import { useConsentSession } from './useConsentSession'; + +const useStyles = makeStyles(theme => ({ + authCard: { + maxWidth: 600, + margin: '0 auto', + marginTop: theme.spacing(4), + }, + appHeader: { + display: 'flex', + alignItems: 'center', + marginBottom: theme.spacing(2), + }, + appIcon: { + marginRight: theme.spacing(2), + fontSize: 40, + }, + appName: { + fontSize: '1.5rem', + fontWeight: 'bold', + }, + securityWarning: { + margin: theme.spacing(2, 0), + }, + buttonContainer: { + display: 'flex', + justifyContent: 'space-between', + gap: theme.spacing(2), + padding: theme.spacing(2), + }, + callbackUrl: { + fontFamily: 'monospace', + backgroundColor: theme.palette.background.default, + padding: theme.spacing(1), + borderRadius: theme.shape.borderRadius, + wordBreak: 'break-all', + fontSize: '0.875rem', + }, + scopeList: { + backgroundColor: theme.palette.background.default, + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(1), + }, +})); + +const ConsentPageLayout = ({ + title, + children, +}: { + title: string; + children: React.ReactNode; +}) => ( + +
+ {children} + +); + +export const ConsentPage = () => { + const classes = useStyles(); + const { sessionId } = useParams<{ sessionId: string }>(); + const { state, handleAction } = useConsentSession({ sessionId }); + + if (!sessionId) { + return ( + + + + ); + } + + if (state.status === 'loading') { + return ( + + + + + + ); + } + + if (state.status === 'error') { + return ( + + + + ); + } + + if (state.status === 'completed') { + return ( + + + + + {state.action === 'approve' ? ( + + ) : ( + + )} + + {state.action === 'approve' + ? 'Authorization Approved' + : 'Authorization Denied'} + + + {state.action === 'approve' + ? 'You have successfully authorized the application to access your Backstage account.' + : 'You have denied the application access to your Backstage account.'} + + + Redirecting to the application... + + + + + + ); + } + + const session = state.session; + const isSubmitting = state.status === 'submitting'; + const appName = session.clientName ?? session.clientId; + + return ( + + + + + + + {appName} + + wants to access your Backstage account + + + + + + + } + className={classes.securityWarning} + > + + Security Notice: By authorizing this application, + you are granting it access to your Backstage account. The + application will receive an access token that allows it to act on + your behalf. + + + + Callback URL: + + {session.redirectUri} + + + + + + Make sure you trust this application and recognize the callback + URL above. Only authorize applications you trust. + + + + + + + + + + + ); +}; diff --git a/plugins/auth/src/components/ConsentPage/index.ts b/plugins/auth/src/components/ConsentPage/index.ts new file mode 100644 index 0000000000..9fdd44c96d --- /dev/null +++ b/plugins/auth/src/components/ConsentPage/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 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 { ConsentPage } from './ConsentPage'; diff --git a/plugins/auth/src/components/ConsentPage/useConsentSession.ts b/plugins/auth/src/components/ConsentPage/useConsentSession.ts new file mode 100644 index 0000000000..a19faae6fe --- /dev/null +++ b/plugins/auth/src/components/ConsentPage/useConsentSession.ts @@ -0,0 +1,144 @@ +/* + * Copyright 2025 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 { + useApi, + alertApiRef, + fetchApiRef, + discoveryApiRef, +} from '@backstage/frontend-plugin-api'; +import { useCallback } from 'react'; +import useAsync from 'react-use/esm/useAsync'; +import useAsyncFn from 'react-use/esm/useAsyncFn'; +import { isError } from '@backstage/errors'; + +interface Session { + id: string; + clientName?: string; + clientId: string; + redirectUri: string; + scopes?: string[]; + responseType?: string; + state?: string; + nonce?: string; + codeChallenge?: string; + codeChallengeMethod?: string; + expiresAt?: string; +} + +type ConsentState = + | { status: 'loading' } + | { status: 'error'; error: string } + | { status: 'loaded'; session: Session } + | { status: 'submitting'; session: Session; action: 'approve' | 'reject' } + | { status: 'completed'; action: 'approve' | 'reject' }; + +export const useConsentSession = (opts: { sessionId?: string }) => { + const alertApi = useApi(alertApiRef); + const fetchApi = useApi(fetchApiRef); + const discoveryApi = useApi(discoveryApiRef); + const { sessionId } = opts; + + const sessionState = useAsync(async () => { + if (!sessionId) { + throw new Error('Session ID is missing'); + } + + const baseUrl = await discoveryApi.getBaseUrl('auth'); + const response = await fetchApi.fetch( + `${baseUrl}/v1/sessions/${sessionId}`, + ); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return (await response.json()) as Session; + }, [sessionId]); + + const [actionState, handleActionInternal] = useAsyncFn( + async (action: 'approve' | 'reject', session: Session) => { + const baseUrl = await discoveryApi.getBaseUrl('auth'); + const response = await fetchApi.fetch( + `${baseUrl}/v1/sessions/${session.id}/${action}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const result = await response.json(); + + if (result.redirectUrl) { + window.location.href = result.redirectUrl; + } + + return { action, redirectUrl: result.redirectUrl }; + }, + [discoveryApi, fetchApi], + ); + + const getConsentState = (): ConsentState => { + if (actionState.value) { + return { status: 'completed', action: actionState.value.action }; + } + if (actionState.loading && sessionState.value) { + return { + status: 'submitting', + session: sessionState.value, + action: 'approve', // This will be set properly when called + }; + } + if (sessionState.error) { + return { + status: 'error', + error: isError(sessionState.error) + ? sessionState.error.message + : 'Failed to load consent request', + }; + } + if (sessionState.value) { + return { status: 'loaded', session: sessionState.value }; + } + return { status: 'loading' }; + }; + + const state = getConsentState(); + return { + state, + handleAction: useCallback( + async (action: 'approve' | 'reject') => { + if (state.status !== 'loaded') return; + + try { + await handleActionInternal(action, state.session); + } catch (err) { + alertApi.post({ + message: isError(err) ? err.message : `Failed to ${action} consent`, + severity: 'error', + }); + } + }, + [state, handleActionInternal, alertApi], + ), + }; +}; diff --git a/plugins/auth/src/components/Router.tsx b/plugins/auth/src/components/Router.tsx new file mode 100644 index 0000000000..fbb5f9aaa9 --- /dev/null +++ b/plugins/auth/src/components/Router.tsx @@ -0,0 +1,29 @@ +/* + * Copyright 2025 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 { Routes, Route } from 'react-router-dom'; +import { ConsentPage } from './ConsentPage'; + +/** + * Router component for the auth plugin + * @public + */ +export const Router = () => { + return ( + + } /> + + ); +}; diff --git a/plugins/auth/src/index.ts b/plugins/auth/src/index.ts new file mode 100644 index 0000000000..717cdd4672 --- /dev/null +++ b/plugins/auth/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 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/auth/src/plugin.test.ts b/plugins/auth/src/plugin.test.ts new file mode 100644 index 0000000000..a50b718e5d --- /dev/null +++ b/plugins/auth/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2025 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 { default as authPlugin } from './plugin'; + +describe('auth', () => { + it('should export plugin', () => { + expect(authPlugin).toBeDefined(); + }); +}); diff --git a/plugins/auth/src/plugin.tsx b/plugins/auth/src/plugin.tsx new file mode 100644 index 0000000000..4ed5d57223 --- /dev/null +++ b/plugins/auth/src/plugin.tsx @@ -0,0 +1,36 @@ +/* + * Copyright 2025 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 { + createFrontendPlugin, + PageBlueprint, +} from '@backstage/frontend-plugin-api'; +import { rootRouteRef } from './routes'; + +export const AuthPage = PageBlueprint.make({ + params: { + path: '/oauth2', + routeRef: rootRouteRef, + loader: () => import('./components/Router').then(m => ), + }, +}); + +export default createFrontendPlugin({ + pluginId: 'auth', + extensions: [AuthPage], + routes: { + root: rootRouteRef, + }, +}); diff --git a/plugins/auth/src/routes.ts b/plugins/auth/src/routes.ts new file mode 100644 index 0000000000..09e6a48da4 --- /dev/null +++ b/plugins/auth/src/routes.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2025 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 { createRouteRef } from '@backstage/frontend-plugin-api'; + +export const rootRouteRef = createRouteRef(); diff --git a/plugins/auth/src/setupTests.ts b/plugins/auth/src/setupTests.ts new file mode 100644 index 0000000000..b57590b525 --- /dev/null +++ b/plugins/auth/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 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 '@testing-library/jest-dom'; diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 3ba5538fc5..7ceff47a80 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/integration-aws-node@0.1.17 + ## 0.4.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 9f98b788a7..4dfe5b990d 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.4.15-next.0", + "version": "0.4.15-next.1", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 31364b56b3..0962c2aa28 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.3.9-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 51956ca1a5..dad34fec2a 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", - "version": "0.3.9-next.0", + "version": "0.3.9-next.1", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index 7f31323ddd..d40c4e03e4 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.5.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.5.6-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 db55d563de..f004e1bb16 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.5.6-next.0", + "version": "0.5.6-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index edb0a14cf3..c462210097 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.5.3-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 56ab813052..97eea0897f 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", - "version": "0.5.3-next.0", + "version": "0.5.3-next.1", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 42964bad70..66965f3009 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.5.3-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 a3cf31ee9a..98d28131a9 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.5.3-next.0", + "version": "0.5.3-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 9cf3bcb98f..fa30682d37 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.3.12-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index ac095bfc4a..5459221795 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.3.12-next.0", + "version": "0.3.12-next.1", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index eaf94c852a..ea0a8a0bbb 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.3.6-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 62f17f1b26..9dac6c9526 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.3.6-next.0", + "version": "0.3.6-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gitea/CHANGELOG.md b/plugins/catalog-backend-module-gitea/CHANGELOG.md index 036dd6b477..f8b03e496c 100644 --- a/plugins/catalog-backend-module-gitea/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-gitea +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitea/package.json b/plugins/catalog-backend-module-gitea/package.json index 3aaf419c82..8389ee70ef 100644 --- a/plugins/catalog-backend-module-gitea/package.json +++ b/plugins/catalog-backend-module-gitea/package.json @@ -1,10 +1,12 @@ { "name": "@backstage/plugin-catalog-backend-module-gitea", - "version": "0.1.4-next.0", - "license": "Apache-2.0", + "version": "0.1.4-next.1", "description": "The gitea backend module for the catalog plugin.", - "main": "src/index.ts", - "types": "src/index.ts", + "backstage": { + "role": "backend-plugin-module", + "pluginId": "catalog", + "pluginPackage": "@backstage/plugin-catalog-backend" + }, "publishConfig": { "access": "public", "main": "dist/index.cjs.js", @@ -15,19 +17,20 @@ "url": "https://github.com/backstage/backstage", "directory": "plugins/catalog-backend-module-gitea" }, - "backstage": { - "role": "backend-plugin-module", - "pluginId": "catalog", - "pluginPackage": "@backstage/plugin-catalog-backend" - }, + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist" + ], "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", + "lint": "backstage-cli package lint", "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", @@ -42,8 +45,5 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^" - }, - "files": [ - "dist" - ] + } } diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index d6110b1f5a..9007c5ab97 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-catalog-backend-module-github@0.11.0-next.1 + ## 0.3.14-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 04029f57b9..ff2b78abc1 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.3.14-next.0", + "version": "0.3.14-next.1", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index b412ef8780..444d602783 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-github +## 0.11.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.0.2-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.11.0-next.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index a61deeb214..42c8446a75 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.11.0-next.0", + "version": "0.11.0-next.1", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index afd3317620..88d11fb806 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.7.3-next.1 + ## 0.2.13-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 05b9e75a0c..98d45ebe61 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.2.13-next.0", + "version": "0.2.13-next.1", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 958c3b364a..adbc95ee65 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.7.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.7.3-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 7d8fe59327..14d62a2ad8 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.7.3-next.0", + "version": "0.7.3-next.1", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index 9139c3e3f1..1d93406c49 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-catalog-backend@3.0.2-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.7.4-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 5a2acc53fc..7a83dd50af 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", - "version": "0.7.4-next.0", + "version": "0.7.4-next.1", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 83b25b4f4c..343a87c514 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.11.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.11.9-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 9e32b21bb3..02e2216d09 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.11.9-next.0", + "version": "0.11.9-next.1", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index b48d58fbd2..0ecb111c7a 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.8.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.8.0-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index facfd228a3..12fcfa0585 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.8.0-next.1", + "version": "0.8.0-next.2", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 6c82466060..4a1c9eca0a 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 3902aafcb7..fec08415cd 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 141e2d52ad..dee1e885fc 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.2.14-next.1 + +### Patch Changes + +- afd368e: **BREAKING ALPHA**: The module has been moved from the `/alpha` export to the root of the package. +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index bcbdcbef48..51c43d475f 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", @@ -24,16 +24,12 @@ "license": "Apache-2.0", "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, "main": "src/index.ts", "types": "src/index.ts", "typesVersions": { "*": { - "alpha": [ - "src/alpha.ts" - ], "package.json": [ "package.json" ] diff --git a/plugins/catalog-backend-module-puppetdb/report-alpha.api.md b/plugins/catalog-backend-module-puppetdb/report-alpha.api.md deleted file mode 100644 index 211add3895..0000000000 --- a/plugins/catalog-backend-module-puppetdb/report-alpha.api.md +++ /dev/null @@ -1,13 +0,0 @@ -## API Report File for "@backstage/plugin-catalog-backend-module-puppetdb" - -> 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'; - -// @alpha -const catalogModulePuppetDbEntityProvider: BackendFeature; -export default catalogModulePuppetDbEntityProvider; - -// (No @packageDocumentation comment for this package) -``` diff --git a/plugins/catalog-backend-module-puppetdb/report.api.md b/plugins/catalog-backend-module-puppetdb/report.api.md index f6fa5bfc83..6b846b29dd 100644 --- a/plugins/catalog-backend-module-puppetdb/report.api.md +++ b/plugins/catalog-backend-module-puppetdb/report.api.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 { Config } from '@backstage/config'; import { EntityProvider } from '@backstage/plugin-catalog-node'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; @@ -16,6 +17,10 @@ import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugi // @public export const ANNOTATION_PUPPET_CERTNAME = 'puppet.com/certname'; +// @public +const catalogModulePuppetDbEntityProvider: BackendFeature; +export default catalogModulePuppetDbEntityProvider; + // @public export const DEFAULT_PROVIDER_ID = 'default'; diff --git a/plugins/catalog-backend-module-puppetdb/src/index.ts b/plugins/catalog-backend-module-puppetdb/src/index.ts index b5c5f9d209..9b10bd630f 100644 --- a/plugins/catalog-backend-module-puppetdb/src/index.ts +++ b/plugins/catalog-backend-module-puppetdb/src/index.ts @@ -22,3 +22,4 @@ export * from './providers'; export * from './puppet'; +export { default } from './module'; diff --git a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts index aa524f2409..1ab95c90d8 100644 --- a/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts +++ b/plugins/catalog-backend-module-puppetdb/src/module/catalogModulePuppetDbEntityProvider.ts @@ -24,7 +24,7 @@ import { PuppetDbEntityProvider } from '../providers/PuppetDbEntityProvider'; /** * Registers the `PuppetDbEntityProvider` with the catalog processing extension point. * - * @alpha + * @public */ export const catalogModulePuppetDbEntityProvider = createBackendModule({ pluginId: 'catalog', diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index 4a6c195039..9c17f17eaa 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.2.12-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 6e040f8078..7019619a6c 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.2.12-next.0", + "version": "0.2.12-next.1", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 28436537b0..ede26e78d1 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.6.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.6.4-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index a3de0c75a4..e949240c64 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", - "version": "0.6.4-next.0", + "version": "0.6.4-next.1", "description": "Backstage Catalog module to view unprocessed entities", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index ad535f16fd..52ebffab31 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend +## 3.0.2-next.1 + +### Patch Changes + +- 2204f5b: Prevent deadlock in catalog deferred stitching +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 3.0.2-next.0 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index d825f29356..7a2df294fd 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "3.0.2-next.0", + "version": "3.0.2-next.1", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts index 1afa0b9c30..ff450e9ccf 100644 --- a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts +++ b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts @@ -435,4 +435,139 @@ describe('markForStitching', () => { } }, ); + + it.each(databases.eachSupportedId())( + 'reproduces deadlock scenario when concurrent transactions update overlapping entity sets %p', + async databaseId => { + const knex = await databases.init(databaseId); + await applyDatabaseMigrations(knex); + + // Setup test data with multiple entities + const entityRefs = [ + 'k:ns/entity-a', + 'k:ns/entity-b', + 'k:ns/entity-c', + 'k:ns/entity-d', + 'k:ns/entity-e', + 'k:ns/entity-f', + ]; + + await knex('refresh_state').insert( + entityRefs.map((ref, i) => ({ + entity_id: `${i + 1}`, + entity_ref: ref, + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: null, + next_stitch_ticket: null, + })), + ); + + // This test attempts to reproduce the deadlock by running concurrent transactions + // that update overlapping sets of entities in different orders + const errorResults = []; + + for (let attempt = 0; attempt < 10; attempt++) { + // Transaction 1: Update entities A, B, C, D, E + const transaction1 = knex.transaction(async trx => { + await markForStitching({ + knex: trx, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityRefs: [ + 'k:ns/entity-a', + 'k:ns/entity-b', + 'k:ns/entity-c', + 'k:ns/entity-d', + 'k:ns/entity-e', + ], + }); + + // Add a small delay to increase chance of collision + await new Promise(resolve => setTimeout(resolve, 10)); + + await markForStitching({ + knex: trx, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityRefs: ['k:ns/entity-f'], + }); + }); + + // Transaction 2: Update entities F, E, D, C, B (reverse order) + const transaction2 = knex.transaction(async trx => { + await markForStitching({ + knex: trx, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityRefs: [ + 'k:ns/entity-f', + 'k:ns/entity-e', + 'k:ns/entity-d', + 'k:ns/entity-c', + 'k:ns/entity-b', + ], + }); + + // Add a small delay to increase chance of collision + await new Promise(resolve => setTimeout(resolve, 10)); + + await markForStitching({ + knex: trx, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityRefs: ['k:ns/entity-a'], + }); + }); + + // Run both transactions concurrently to create potential deadlock + errorResults.push( + Promise.allSettled([transaction1, transaction2]).then(results => + results + .filter(r => r.status === 'rejected') + .map(r => (r as PromiseRejectedResult).reason), + ), + ); + } + + const allResults = await Promise.all(errorResults); + + const deadlockErrors = allResults + .flat() + .filter( + error => + error?.code === '40P01' || + error?.message?.includes('deadlock detected') || + error?.message?.includes('deadlock'), + ); + expect(deadlockErrors).toEqual([]); + + // Verify final state - all entities should have been marked for stitching + const finalState = await knex('refresh_state') + .select('entity_ref', 'next_stitch_at', 'next_stitch_ticket') + .whereNotNull('next_stitch_at') + .orderBy('entity_ref'); + + expect(finalState.length).toBeGreaterThan(0); + finalState.forEach(row => { + expect(row.next_stitch_at).not.toBeNull(); + expect(row.next_stitch_ticket).not.toBeNull(); + }); + }, + ); }); diff --git a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts index ecc364a9cc..484d6e4ed6 100644 --- a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts +++ b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts @@ -17,9 +17,34 @@ import { Knex } from 'knex'; import splitToChunks from 'lodash/chunk'; import { v4 as uuid } from 'uuid'; +import { ErrorLike, isError } from '@backstage/errors'; import { StitchingStrategy } from '../../../stitching/types'; +import { setTimeout as sleep } from 'timers/promises'; import { DbFinalEntitiesRow, DbRefreshStateRow } from '../../tables'; +const UPDATE_CHUNK_SIZE = 100; // Smaller chunks reduce contention +const DEADLOCK_RETRY_ATTEMPTS = 3; +const DEADLOCK_BASE_DELAY_MS = 25; + +// PostgreSQL deadlock error code +const POSTGRES_DEADLOCK_SQLSTATE = '40P01'; + +/** + * Checks if the given error is a deadlock error for the database engine in use. + */ +function isDeadlockError( + knex: Knex | Knex.Transaction, + e: unknown, +): e is ErrorLike { + if (knex.client.config.client.includes('pg')) { + // PostgreSQL deadlock detection + return isError(e) && e.code === POSTGRES_DEADLOCK_SQLSTATE; + } + + // Add more database engine checks here as needed + return false; +} + /** * Marks a number of entities for stitching some time in the near * future. @@ -32,9 +57,8 @@ export async function markForStitching(options: { entityRefs?: Iterable; entityIds?: Iterable; }): Promise { - // Splitting to chunks just to cover pathological cases that upset the db - const entityRefs = split(options.entityRefs); - const entityIds = split(options.entityIds); + const entityRefs = sortSplit(options.entityRefs); + const entityIds = sortSplit(options.entityIds); const knex = options.knex; const mode = options.strategy.mode; @@ -51,13 +75,15 @@ export async function markForStitching(options: { .select('entity_id') .whereIn('entity_ref', chunk), ); - await knex - .table('refresh_state') - .update({ - result_hash: 'force-stitching', - next_update_at: knex.fn.now(), - }) - .whereIn('entity_ref', chunk); + await retryOnDeadlock(async () => { + await knex + .table('refresh_state') + .update({ + result_hash: 'force-stitching', + next_update_at: knex.fn.now(), + }) + .whereIn('entity_ref', chunk); + }, knex); } for (const chunk of entityIds) { @@ -67,44 +93,74 @@ export async function markForStitching(options: { hash: 'force-stitching', }) .whereIn('entity_id', chunk); - await knex - .table('refresh_state') - .update({ - result_hash: 'force-stitching', - next_update_at: knex.fn.now(), - }) - .whereIn('entity_id', chunk); + await retryOnDeadlock(async () => { + await knex + .table('refresh_state') + .update({ + result_hash: 'force-stitching', + next_update_at: knex.fn.now(), + }) + .whereIn('entity_id', chunk); + }, knex); } } else if (mode === 'deferred') { // It's OK that this is shared across refresh state rows; it just needs to // be uniquely generated for every new stitch request. const ticket = uuid(); + // Update by primary key in deterministic order to avoid deadlocks for (const chunk of entityRefs) { - await knex('refresh_state') - .update({ - next_stitch_at: knex.fn.now(), - next_stitch_ticket: ticket, - }) - .whereIn('entity_ref', chunk); + await retryOnDeadlock(async () => { + await knex('refresh_state') + .update({ + next_stitch_at: knex.fn.now(), + next_stitch_ticket: ticket, + }) + .whereIn('entity_ref', chunk); + }, knex); } for (const chunk of entityIds) { - await knex('refresh_state') - .update({ - next_stitch_at: knex.fn.now(), - next_stitch_ticket: ticket, - }) - .whereIn('entity_id', chunk); + await retryOnDeadlock(async () => { + await knex('refresh_state') + .update({ + next_stitch_at: knex.fn.now(), + next_stitch_ticket: ticket, + }) + .whereIn('entity_id', chunk); + }, knex); } } else { throw new Error(`Unknown stitching strategy mode ${mode}`); } } -function split(input: Iterable | undefined): string[][] { +function sortSplit(input: Iterable | undefined): string[][] { if (!input) { return []; } - return splitToChunks(Array.isArray(input) ? input : [...input], 200); + const array = Array.isArray(input) ? input.slice() : [...input]; + array.sort(); + return splitToChunks(array, UPDATE_CHUNK_SIZE); +} + +async function retryOnDeadlock( + fn: () => Promise, + knex: Knex | Knex.Transaction, + retries = DEADLOCK_RETRY_ATTEMPTS, + baseMs = DEADLOCK_BASE_DELAY_MS, +): Promise { + let attempt = 0; + for (;;) { + try { + return await fn(); + } catch (e: unknown) { + if (isDeadlockError(knex, e) && attempt < retries) { + await sleep(baseMs * Math.pow(2, attempt)); + attempt++; + continue; + } + throw e; + } + } } diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index a756916c38..5fa578c1ad 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-graph +## 0.4.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 0.4.23-next.1 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 7cea775bbe..1167fe111b 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.4.23-next.1", + "version": "0.4.23-next.2", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 77ad27ced9..ecd0d8bc46 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-import +## 0.13.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-compat-api@0.5.2-next.2 + ## 0.13.5-next.1 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 050e8be908..39ed5bcef0 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.13.5-next.1", + "version": "0.13.5-next.2", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 18f44ba88f..14ac349b6d 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/plugin-catalog-node +## 1.19.0-next.1 + +### Minor Changes + +- 0e9ec44: Introduced new `streamEntities` async generator method for the catalog. + + Catalog API and Catalog Service now includes a `streamEntities` method that allows for streaming entities from the catalog. + This method is designed to handle large datasets efficiently by processing entities in a stream rather than loading them + all into memory at once. This is useful when you need to fetch a large number of entities but do not want to use pagination + or fetch all entities at once. + + Example usage: + + ```ts + const pageStream = catalogClient.streamEntities({ pageSize: 100 }, { token }); + for await (const page of pageStream) { + // Handle page of entities + for (const entity of page) { + console.log(entity); + } + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + ## 1.18.1-next.0 ### Patch Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index a61c4b801b..3111765897 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "1.18.1-next.0", + "version": "1.19.0-next.1", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index d6eb0dc5fe..7ab8dcd967 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/plugin-catalog-react +## 1.21.0-next.2 + +### Minor Changes + +- 0e9ec44: Introduced new `streamEntities` async generator method for the catalog. + + Catalog API and Catalog Service now includes a `streamEntities` method that allows for streaming entities from the catalog. + This method is designed to handle large datasets efficiently by processing entities in a stream rather than loading them + all into memory at once. This is useful when you need to fetch a large number of entities but do not want to use pagination + or fetch all entities at once. + + Example usage: + + ```ts + const pageStream = catalogClient.streamEntities({ pageSize: 100 }, { token }); + for await (const page of pageStream) { + // Handle page of entities + for (const entity of page) { + console.log(entity); + } + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 1.20.2-next.1 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 3912d9ce9d..0b072f51ce 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.20.2-next.1", + "version": "1.21.0-next.2", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 19c35e2137..cddfdf18bc 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog +## 1.31.3-next.2 + +### Patch Changes + +- 85c5e04: Fix incorrect `defaultTarget` on `createComponentRouteRef`. +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 1.31.3-next.1 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 9e1b0fcc05..fd0c1c4375 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.31.3-next.1", + "version": "1.31.3-next.2", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog/src/routes.ts b/plugins/catalog/src/routes.ts index 4f7f0427b1..6043ecd1a1 100644 --- a/plugins/catalog/src/routes.ts +++ b/plugins/catalog/src/routes.ts @@ -22,7 +22,7 @@ import { export const createComponentRouteRef = createExternalRouteRef({ id: 'create-component', optional: true, - defaultTarget: 'scaffolder.createComponent', + defaultTarget: 'scaffolder.root', }); export const viewTechDocRouteRef = createExternalRouteRef({ diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index 4da746e10a..dbd27d37d9 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-devtools-backend +## 0.5.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.3-next.0 + - @backstage/backend-defaults@0.12.1-next.1 + ## 0.5.9-next.0 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 038fcbce82..5bd90bdd41 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.5.9-next.0", + "version": "0.5.9-next.1", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index ea7326d9b3..1d2709648f 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-home +## 0.8.12-next.2 + +### Patch Changes + +- 929c55a: Fixed race condition in CustomHomepageGrid by waiting for storage to load before rendering custom layout to prevent + rendering of the default content. +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 0.8.12-next.1 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 21469df7cd..bc8d71d23b 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.8.12-next.1", + "version": "0.8.12-next.2", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx index 5d85e834e8..fec7895170 100644 --- a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx +++ b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx @@ -34,7 +34,11 @@ import { } from '@material-ui/core/styles'; import { compact } from 'lodash'; import useObservable from 'react-use/esm/useObservable'; -import { ContentHeader, ErrorBoundary } from '@backstage/core-components'; +import { + ContentHeader, + ErrorBoundary, + Progress, +} from '@backstage/core-components'; import Typography from '@material-ui/core/Typography'; import { WidgetSettingsOverlay } from './WidgetSettingsOverlay'; import { AddWidgetDialog } from './AddWidgetDialog'; @@ -90,7 +94,7 @@ const useStyles = makeStyles((theme: Theme) => function useHomeStorage( defaultWidgets: GridWidget[], -): [GridWidget[], (value: GridWidget[]) => void] { +): [GridWidget[], (value: GridWidget[]) => void, boolean] { const key = 'home'; const storageApi = useApi(storageApiRef).forBucket('home.customHomepage'); // TODO: Support multiple home pages @@ -110,6 +114,9 @@ function useHomeStorage( storageApi.observe$(key), storageApi.snapshot(key), ); + + const isStorageLoading = homeSnapshot.presence === 'unknown' || !homeSnapshot; + const widgets: GridWidget[] = useMemo(() => { if (homeSnapshot.presence === 'absent') { return defaultWidgets; @@ -122,7 +129,7 @@ function useHomeStorage( } }, [homeSnapshot, defaultWidgets]); - return [widgets, setWidgets]; + return [widgets, setWidgets, isStorageLoading]; } const convertConfigToDefaultWidgets = ( @@ -213,7 +220,7 @@ export const CustomHomepageGrid = (props: CustomHomepageGridProps) => { ? convertConfigToDefaultWidgets(props.config, availableWidgets) : []; }, [props.config, availableWidgets]); - const [widgets, setWidgets] = useHomeStorage(defaultLayout); + const [widgets, setWidgets, isStorageLoading] = useHomeStorage(defaultLayout); const [addWidgetDialogOpen, setAddWidgetDialogOpen] = useState(false); const editModeOn = widgets.find(w => w.layout.isResizable) !== undefined; const [editMode, setEditMode] = useState(editModeOn); @@ -322,6 +329,10 @@ export const CustomHomepageGrid = (props: CustomHomepageGridProps) => { ); }; + if (isStorageLoading) { + return ; + } + return ( <> diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 761e87e5e0..0986e613f8 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes-backend +## 0.20.2-next.2 + +### Patch Changes + +- dd7b6d2: Fix a bug where `getDefault` in the `kubernetesFetcherExtensionPoint` had the wrong `this` value +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration-aws-node@0.1.17 + ## 0.20.2-next.1 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index bae94407b5..15722c373d 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.20.2-next.1", + "version": "0.20.2-next.2", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-backend/src/service/KubernetesInitializer.ts b/plugins/kubernetes-backend/src/service/KubernetesInitializer.ts index c32023235f..1b0b9a31e1 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesInitializer.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesInitializer.ts @@ -183,8 +183,9 @@ export class KubernetesInitializer { async init() { const fetcher = - (await this.opts.fetcher?.({ getDefault: this.defaultFetcher })) ?? - (await this.defaultFetcher()); + (await this.opts.fetcher?.({ + getDefault: () => this.defaultFetcher(), + })) ?? (await this.defaultFetcher()); const authStrategyMap = this.opts.authStrategyMap ?? (await this.defaultAuthStrategy()); diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index 63ee864a46..af3a23232d 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + ## 0.0.29-next.1 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 6488b959f6..0f9ab60401 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-cluster", - "version": "0.0.29-next.1", + "version": "0.0.29-next.2", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 6f76f6bc2a..5a1643287e 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kubernetes +## 0.12.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 0.12.11-next.1 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 3fd141319c..d0b553a4eb 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.11-next.1", + "version": "0.12.11-next.2", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", diff --git a/plugins/mcp-actions-backend/CHANGELOG.md b/plugins/mcp-actions-backend/CHANGELOG.md index 0ca964fbd1..e7578dff07 100644 --- a/plugins/mcp-actions-backend/CHANGELOG.md +++ b/plugins/mcp-actions-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-mcp-actions-backend +## 0.1.3-next.1 + +### Patch Changes + +- 1d47bf3: Proxy `/.well-known/oauth-authorization-server` to `/.well-known/openid-configuration` on `auth-backend` when `auth.experimentalDynamicClientRegistration.enabled` is enabled. +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.1.3-next.0 ### Patch Changes diff --git a/plugins/mcp-actions-backend/package.json b/plugins/mcp-actions-backend/package.json index 2a7fbdf8a3..4ed2250925 100644 --- a/plugins/mcp-actions-backend/package.json +++ b/plugins/mcp-actions-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-mcp-actions-backend", - "version": "0.1.3-next.0", + "version": "0.1.3-next.1", "backstage": { "role": "backend-plugin", "pluginId": "mcp-actions", diff --git a/plugins/mcp-actions-backend/src/plugin.ts b/plugins/mcp-actions-backend/src/plugin.ts index f1e89417e9..d04df6cdf5 100644 --- a/plugins/mcp-actions-backend/src/plugin.ts +++ b/plugins/mcp-actions-backend/src/plugin.ts @@ -17,7 +17,8 @@ import { coreServices, createBackendPlugin, } from '@backstage/backend-plugin-api'; -import { json, Router } from 'express'; +import { json } from 'express'; +import Router from 'express-promise-router'; import { McpService } from './services/McpService'; import { createStreamableRouter } from './routers/createStreamableRouter'; import { createSseRouter } from './routers/createSseRouter'; @@ -42,8 +43,19 @@ export const mcpPlugin = createBackendPlugin({ httpRouter: coreServices.httpRouter, actions: actionsServiceRef, registry: actionsRegistryServiceRef, + rootRouter: coreServices.rootHttpRouter, + discovery: coreServices.discovery, + config: coreServices.rootConfig, }, - async init({ actions, logger, httpRouter, httpAuth }) { + async init({ + actions, + logger, + httpRouter, + httpAuth, + rootRouter, + discovery, + config, + }) { const mcpService = await McpService.create({ actions, }); @@ -66,6 +78,26 @@ export const mcpPlugin = createBackendPlugin({ router.use('/v1', streamableRouter); httpRouter.use(router); + + if ( + config.getOptionalBoolean( + 'auth.experimentalDynamicClientRegistration.enabled', + ) + ) { + // This should be replaced with throwing a WWW-Authenticate header, but that doesn't seem to be supported by + // many of the MCP client as of yet. So this seems to be the oldest version of the spec thats implemented. + rootRouter.use( + '/.well-known/oauth-authorization-server', + async (_, res) => { + const authBaseUrl = await discovery.getBaseUrl('auth'); + const oidcResponse = await fetch( + `${authBaseUrl}/.well-known/openid-configuration`, + ); + + res.json(await oidcResponse.json()); + }, + ); + } }, }); }, diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md index 9a46dad0bc..8619edd6f4 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-notifications-node@0.2.19-next.1 + ## 0.3.13-next.0 ### Patch Changes diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index 602edf0e66..6844a77d3c 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-email", - "version": "0.3.13-next.0", + "version": "0.3.13-next.1", "description": "The email backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend-module-slack/CHANGELOG.md b/plugins/notifications-backend-module-slack/CHANGELOG.md index e38e8f9d07..a9d8074e5f 100644 --- a/plugins/notifications-backend-module-slack/CHANGELOG.md +++ b/plugins/notifications-backend-module-slack/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-notifications-backend-module-slack +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-notifications-node@0.2.19-next.1 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/notifications-backend-module-slack/package.json b/plugins/notifications-backend-module-slack/package.json index 227201984b..6b32e1b31d 100644 --- a/plugins/notifications-backend-module-slack/package.json +++ b/plugins/notifications-backend-module-slack/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-slack", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "description": "The slack backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend/CHANGELOG.md b/plugins/notifications-backend/CHANGELOG.md index 41905ec918..3391980f44 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-notifications-backend +## 0.5.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/plugin-notifications-node@0.2.19-next.1 + ## 0.5.10-next.0 ### Patch Changes diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index 94c42005c0..a7933b0fc8 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.5.10-next.0", + "version": "0.5.10-next.1", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index af3c020fd3..77e35d0996 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-notifications-node +## 0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + ## 0.2.19-next.0 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index c20cf4c820..234d9d342f 100644 --- a/plugins/notifications-node/package.json +++ b/plugins/notifications-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-node", - "version": "0.2.19-next.0", + "version": "0.2.19-next.1", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/src/alpha.tsx b/plugins/notifications/src/alpha.tsx index 6d32150c35..ca4be58209 100644 --- a/plugins/notifications/src/alpha.tsx +++ b/plugins/notifications/src/alpha.tsx @@ -23,6 +23,7 @@ import { } from '@backstage/frontend-plugin-api'; import { rootRouteRef } from './routes'; import { + compatWrapper, convertLegacyRouteRef, convertLegacyRouteRefs, } from '@backstage/core-compat-api'; @@ -33,9 +34,9 @@ const page = PageBlueprint.make({ path: '/notifications', routeRef: convertLegacyRouteRef(rootRouteRef), loader: () => - import('./components/NotificationsPage').then(m => ( - - )), + import('./components/NotificationsPage').then(m => + compatWrapper(), + ), }, }); diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 410b9da982..6cf5609da0 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-org-react +## 0.1.42-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + ## 0.1.42-next.1 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index f8410a3f77..06c4135396 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.42-next.1", + "version": "0.1.42-next.2", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index ee5def4fcf..9df2d654e3 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-org +## 0.6.44-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 0.6.44-next.1 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index df5b58446c..96a4ab9e62 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.44-next.1", + "version": "0.6.44-next.2", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 9e99278912..2c396607d5 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.8.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + ## 0.8.3-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 15dedf5ac8..2d28caf5c6 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", - "version": "0.8.3-next.0", + "version": "0.8.3-next.1", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 5fbb8edcc2..305afa351c 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend +## 2.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/plugin-auth-node@0.6.7-next.1 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.12-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.8.3-next.1 + ## 2.2.1-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 6b2f9ef0cb..2e77b90177 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "2.2.1-next.0", + "version": "2.2.1-next.1", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-backend/sample-templates/notifications-demo/template.yaml b/plugins/scaffolder-backend/sample-templates/notifications-demo/template.yaml index abd18b1d42..56a7e7f5c6 100644 --- a/plugins/scaffolder-backend/sample-templates/notifications-demo/template.yaml +++ b/plugins/scaffolder-backend/sample-templates/notifications-demo/template.yaml @@ -32,7 +32,7 @@ spec: title: Title type: string description: Notification title - description: + info: title: Description type: string description: Notification longer description @@ -67,7 +67,7 @@ spec: recipients: ${{ parameters.recipients }} entityRefs: ${{ parameters.entityRefs }} title: ${{ parameters.title }} - description: ${{ parameters.description }} + info: ${{ parameters.info }} link: ${{ parameters.link }} severity: ${{ parameters.severity }} topic: ${{ parameters.topic }} diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index 467b590bed..4144c552fb 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -1385,34 +1385,24 @@ data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{ expect.anything(), ); }); - it('disallows users from seeing tasks they do not own', async () => { - const { permissions, router, taskBroker } = await createTestRouter(); - jest - .spyOn(permissions, 'authorizeConditional') - .mockImplementationOnce(async () => [ - { - conditions: { - resourceType: 'scaffolder-task', - rule: 'IS_TASK_OWNER', - params: { createdBy: ['user'] }, - }, - pluginId: 'scaffolder', - resourceType: 'scaffolder-task', - result: AuthorizeResult.CONDITIONAL, + it('allows payloads up to 10MB', async () => { + const { unwrappedRouter } = await createTestRouter(); + const mockToken = mockCredentials.user.token(); + const mockTemplate = generateMockTemplate(); + + const response = await request(unwrappedRouter) + .post('/v2/dry-run') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + template: mockTemplate, + values: { + requiredParameter1: 'A'.repeat(9 * 1024 * 1024), // ~9MB + requiredParameter2: 'required-value-2', }, - ]); - const response = await request(router).get( - `/v2/tasks?createdBy=not-user`, - ); - expect(taskBroker.list).toHaveBeenCalledWith({ - filters: { createdBy: ['not-user'], status: undefined }, - order: undefined, - pagination: { limit: undefined, offset: undefined }, - permissionFilters: { key: 'created_by', values: ['user'] }, - }); + directoryContents: [], + }); + expect(response.status).toBe(200); - expect(response.body.totalTasks).toBe(0); - expect(response.body.tasks).toEqual([]); }); }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 9315e8ccb2..8296a6003f 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -186,9 +186,12 @@ const readDuration = ( export async function createRouter( options: RouterOptions, ): Promise { - const router = await createOpenApiRouter(); - // Be generous in upload size to support a wide range of templates in dry-run mode. - router.use(express.json({ limit: '10MB' })); + const router = await createOpenApiRouter({ + middleware: [ + // Be generous in upload size to support a wide range of templates in dry-run mode. + express.json({ limit: '10MB' }), + ], + }); const { logger: parentLogger, diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 78d40108f4..00c948a51b 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-react +## 1.19.1-next.2 + +### Patch Changes + +- 58fc108: Fix scaffolder task log stream not having a minimum height +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + ## 1.19.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 1d8737db72..25c8ea0ec6 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.19.1-next.1", + "version": "1.19.1-next.2", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.tsx b/plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.tsx index a0c4ce94cd..b88d96510e 100644 --- a/plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.tsx @@ -21,6 +21,7 @@ const useStyles = makeStyles({ width: '100%', height: '100%', position: 'relative', + minHeight: 240, }, }); diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index bbb826965c..420938d6fd 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder +## 1.34.1-next.2 + +### Patch Changes + +- 0d415ae: Render a TechDocs link on the Scaffolder Template List page when templates include either `backstage.io/techdocs-ref` or `backstage.io/techdocs-entity` annotations, using the shared `buildTechDocsURL` helper. Also adds tests to verify both annotations and optional `backstage.io/techdocs-entity-path` are respected. +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/plugin-scaffolder-react@1.19.1-next.2 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-compat-api@0.5.2-next.2 + ## 1.34.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index bad13ef8d2..695afd2244 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.34.1-next.1", + "version": "1.34.1-next.2", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", @@ -72,6 +72,8 @@ "@backstage/plugin-permission-react": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/plugin-scaffolder-react": "workspace:^", + "@backstage/plugin-techdocs-common": "workspace:^", + "@backstage/plugin-techdocs-react": "workspace:^", "@backstage/types": "workspace:^", "@codemirror/language": "^6.0.0", "@codemirror/legacy-modes": "^6.1.0", @@ -109,6 +111,7 @@ "@backstage/dev-utils": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-techdocs": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/dom": "^10.0.0", "@testing-library/jest-dom": "^6.0.0", diff --git a/plugins/scaffolder/src/alpha/components/TemplateListPage/TemplateListPage.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateListPage/TemplateListPage.test.tsx index a16474a4cf..da544cb2c0 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateListPage/TemplateListPage.test.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateListPage/TemplateListPage.test.tsx @@ -27,13 +27,19 @@ import { TestApiProvider, mockApis, } from '@backstage/test-utils'; -import { rootRouteRef } from '../../../routes'; +import { rootRouteRef, viewTechDocRouteRef } from '../../../routes'; import { TemplateListPage } from './TemplateListPage'; +import { + TECHDOCS_ANNOTATION, + TECHDOCS_EXTERNAL_ANNOTATION, + TECHDOCS_EXTERNAL_PATH_ANNOTATION, +} from '@backstage/plugin-techdocs-common'; const mountedRoutes = { mountedRoutes: { '/': rootRouteRef, '/catalog/:namespace/:kind/:name': entityRouteRef, + '/docs/:namespace/:kind/:name': viewTechDocRouteRef, }, }; @@ -51,6 +57,127 @@ describe('TemplateListPage', () => { ], }); + describe('TechDocs link rendering', () => { + it('shows TechDocs link when template has backstage.io/techdocs-ref', async () => { + const mockCatalogApiWithDocs = catalogApiMock({ + entities: [ + { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + name: 'tmpl-a', + annotations: { [TECHDOCS_ANNOTATION]: 'dir:.' }, + }, + spec: { type: 'service' }, + }, + ], + }); + + const { findByText } = await renderInTestApp( + + + , + mountedRoutes, + ); + + expect(await findByText('View TechDocs')).toBeInTheDocument(); + }); + + it('shows TechDocs link when template has backstage.io/techdocs-entity', async () => { + const mockCatalogApiWithExternal = catalogApiMock({ + entities: [ + { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + name: 'tmpl-b', + annotations: { + [TECHDOCS_EXTERNAL_ANNOTATION]: 'component:default/other', + }, + }, + spec: { type: 'service' }, + }, + ], + }); + + const { findByText } = await renderInTestApp( + + + , + mountedRoutes, + ); + + expect(await findByText('View TechDocs')).toBeInTheDocument(); + }); + + it('appends path when backstage.io/techdocs-entity-path is set', async () => { + const mockCatalogApiWithPath = catalogApiMock({ + entities: [ + { + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + name: 'tmpl-c', + annotations: { + [TECHDOCS_EXTERNAL_ANNOTATION]: 'component:default/other', + [TECHDOCS_EXTERNAL_PATH_ANNOTATION]: '/guides/start', + }, + }, + spec: { type: 'service' }, + }, + ], + }); + + const { findByText } = await renderInTestApp( + + + , + mountedRoutes, + ); + + const link = (await findByText('View TechDocs')).closest('a')!; + expect(link).toHaveAttribute( + 'href', + expect.stringMatching( + /\/docs\/default\/component\/other\/?(index\.html)?#?\/guides\/start|\/docs\/default\/component\/other\/guides\/start/, + ), + ); + }); + }); + it('should render the search bar for templates', async () => { const { getByPlaceholderText } = await renderInTestApp( { const additionalLinksForEntity = useCallback( (template: TemplateEntityV1beta3) => { - const { kind, namespace, name } = parseEntityRef( - stringifyEntityRef(template), - ); - return template.metadata.annotations?.['backstage.io/techdocs-ref'] && - viewTechDocsLink + if ( + !( + template.metadata.annotations?.[TECHDOCS_ANNOTATION] || + template.metadata.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION] + ) || + !viewTechDocsLink + ) { + return []; + } + + const url = buildTechDocsURL(template, viewTechDocsLink); + return url ? [ { icon: app.getSystemIcon('docs') ?? DocsIcon, text: t( 'templateListPage.additionalLinksForEntity.viewTechDocsTitle', ), - url: viewTechDocsLink({ kind, namespace, name }), + url, }, ] : []; diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index e9a56fa5f9..8f60690872 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-search-backend-module-catalog +## 0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.3.8-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 9bcc2287ff..0ebd03caa6 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-catalog", - "version": "0.3.8-next.0", + "version": "0.3.8-next.1", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 1938883b9e..ead9c04a7b 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + ## 0.4.6-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 1faffa2b90..d1b784a4d8 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.4.6-next.0", + "version": "0.4.6-next.1", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 8449247680..8e9ca88787 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-search +## 1.4.30-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 1.4.30-next.1 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 7600d61b86..b58993b65c 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.30-next.1", + "version": "1.4.30-next.2", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index a6b33f9837..714fd56c8f 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.53-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/plugin-catalog@1.31.3-next.2 + - @backstage/plugin-techdocs@1.14.2-next.2 + ## 1.0.53-next.1 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index eebe45a88a..94fd865e02 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.53-next.1", + "version": "1.0.53-next.2", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-addons-test-utils/src/test-utils.tsx b/plugins/techdocs-addons-test-utils/src/test-utils.tsx index 91b85e7885..a7cb74cb93 100644 --- a/plugins/techdocs-addons-test-utils/src/test-utils.tsx +++ b/plugins/techdocs-addons-test-utils/src/test-utils.tsx @@ -39,10 +39,12 @@ import { } from '@backstage/plugin-techdocs-react'; import { TechDocsReaderPage, techdocsPlugin } from '@backstage/plugin-techdocs'; import { + catalogApiRef, EntityPresentationApi, entityPresentationApiRef, entityRouteRef, } from '@backstage/plugin-catalog-react'; +import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; import { searchApiRef } from '@backstage/plugin-search-react'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; @@ -235,8 +237,19 @@ export class TechDocsAddonTester { }), }; + const catalogApi = catalogApiMock({ + entities: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { namespace: 'default', name: 'docs' }, + }, + ], + }); + const apis: TechdocsAddonTesterApis = [ [fetchApiRef, fetchApi], + [catalogApiRef, catalogApi], [entityPresentationApiRef, entityPresentationApi], [discoveryApiRef, discoveryApi], [techdocsApiRef, techdocsApi], diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 036ba4387c..98b42c0b84 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-backend +## 2.1.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.6-next.1 + ## 2.1.0-next.0 ### Minor Changes diff --git a/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml index 6b30213a60..5b90693e23 100644 --- a/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml +++ b/plugins/techdocs-backend/examples/documented-component/catalog-info.yaml @@ -10,3 +10,17 @@ spec: type: service lifecycle: experimental owner: user:guest +--- +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: techdocs-entity-documented-component + title: Example Entity Documented By TechDocs Entity Annotation + description: A Service with TechDocs documentation via the `backstage.io/techdocs-entity` annotation. + annotations: + backstage.io/techdocs-entity: component:default/documented-component + backstage.io/techdocs-entity-path: /inner-component-docs +spec: + type: service + lifecycle: experimental + owner: user:guest diff --git a/plugins/techdocs-backend/examples/documented-component/docs/inner-component-docs/index.md b/plugins/techdocs-backend/examples/documented-component/docs/inner-component-docs/index.md new file mode 100644 index 0000000000..8cb6498c55 --- /dev/null +++ b/plugins/techdocs-backend/examples/documented-component/docs/inner-component-docs/index.md @@ -0,0 +1,5 @@ +# Inner Component Docs + +This is a basic example of documentation within a larger suite of TechDocs that can be referenced by whatever entities necessary. It is intended as a showcase of the `backstage.io/techdocs-entity-path` annotation for linking to a "subpage" in TechDocs that are declared by another entity. + +Please review the [How-To Guides - Deep Linking Into TechDocs](../../../../../../docs/features/techdocs/how-to-guides.md#deep-linking-into-techdocs) section for more information and you can view the example usage on the "Example Entity Documented By TechDocs Entity Annotation" component in this [catalog-info.yaml](../../catalog-info.yaml) file. diff --git a/plugins/techdocs-backend/examples/documented-component/mkdocs.yml b/plugins/techdocs-backend/examples/documented-component/mkdocs.yml index 1a159a4d12..fd954225a3 100644 --- a/plugins/techdocs-backend/examples/documented-component/mkdocs.yml +++ b/plugins/techdocs-backend/examples/documented-component/mkdocs.yml @@ -7,6 +7,7 @@ nav: - Subpage: sub-page.md - 'Code Sample': code/code-sample.md - Extensions: extensions.md + - 'Inner Component Docs': inner-component-docs/index.md plugins: - techdocs-core diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index bc6bfd6175..fd1ed4cadf 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "2.1.0-next.0", + "version": "2.1.0-next.1", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-react/src/helpers.ts b/plugins/techdocs-react/src/helpers.ts index e6daf99b26..aebb0b3e52 100644 --- a/plugins/techdocs-react/src/helpers.ts +++ b/plugins/techdocs-react/src/helpers.ts @@ -64,7 +64,7 @@ export function getEntityRootTechDocsPath(entity: Entity): string { /** * Build the TechDocs URL for the given entity. This helper should be used anywhere there - * is a link to an entities TechDocs. + * is a link to an entity's TechDocs. * * @public */ @@ -104,7 +104,7 @@ export const buildTechDocsURL = ( }); // Add on the external entity path to the url if one exists. This allows deep linking into another - // entities TechDocs. + // entity's TechDocs. const path = getEntityRootTechDocsPath(entity); return `${url}${path}`; diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index c4f6ed3cef..02593351ee 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs +## 1.14.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-react@0.1.19-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-compat-api@0.5.2-next.2 + ## 1.14.2-next.1 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 7ecec3cd0a..7777133862 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.14.2-next.1", + "version": "1.14.2-next.2", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", @@ -81,7 +81,7 @@ "@material-ui/lab": "4.0.0-alpha.61", "@material-ui/styles": "^4.10.0", "@microsoft/fetch-event-source": "^2.0.1", - "dompurify": "^3.0.0", + "dompurify": "^3.2.4", "git-url-parse": "^15.0.0", "jss": "~10.10.0", "lodash": "^4.17.21", diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx index 2af4915ef5..aea45a60fa 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx @@ -18,6 +18,7 @@ import { ReactNode } from 'react'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { + catalogApiRef, entityPresentationApiRef, entityRouteRef, } from '@backstage/plugin-catalog-react'; @@ -26,13 +27,15 @@ import { renderInTestApp, TestApiProvider, } from '@backstage/test-utils'; +import { catalogApiMock as catalogApiMockFactory } from '@backstage/plugin-catalog-react/testUtils'; import { techdocsApiRef, techdocsStorageApiRef } from '../../../api'; import { rootRouteRef, rootDocsRouteRef } from '../../../routes'; +import { TECHDOCS_EXTERNAL_ANNOTATION } from '@backstage/plugin-techdocs-common'; import { TechDocsReaderPage } from './TechDocsReaderPage'; -import { Route, useParams } from 'react-router-dom'; +import { Route, useNavigate, useParams } from 'react-router-dom'; import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib'; import { FlatRoutes } from '@backstage/core-app-api'; @@ -94,6 +97,8 @@ const entityPresentationApiMock: jest.Mocked< }), }; +const catalogApiMock = catalogApiMockFactory.mock(); + const fetchApiMock = { fetch: jest.fn().mockResolvedValue({ ok: true, @@ -114,6 +119,11 @@ jest.mock('@backstage/core-components', () => ({ Page: jest.fn(), })); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn(), +})); + const configApi = mockApis.config({ data: { app: { baseUrl: 'http://localhost:3000' } }, }); @@ -129,6 +139,7 @@ const Wrapper = ({ children }: { children: ReactNode }) => { [techdocsApiRef, techdocsApiMock], [techdocsStorageApiRef, techdocsStorageApiMock], [entityPresentationApiRef, entityPresentationApiMock], + [catalogApiRef, catalogApiMock], ]} > {children} @@ -143,6 +154,8 @@ const mountedRoutes = { }; describe('', () => { + const mockNavigate = jest.fn(); + beforeEach(() => { getEntityMetadata.mockResolvedValue(mockEntityMetadata); getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata); @@ -150,6 +163,8 @@ describe('', () => { // Expires in 10 minutes expiresAt: new Date(Date.now() + 10 * 60 * 1000).toISOString(), }); + + (useNavigate as jest.Mock).mockReturnValue(mockNavigate); }); afterEach(() => { @@ -285,4 +300,142 @@ describe('', () => { expect(text).toHaveStyle('fontFamily: Comic Sans MS'); }); + + describe('external TechDocs redirect', () => { + beforeEach(() => { + mockNavigate.mockClear(); + catalogApiMock.getEntityByRef.mockReset(); + catalogApiMock.getEntityByRef.mockResolvedValue(mockEntityMetadata); + }); + + it('should navigate to external URL when entity has external techdocs annotation', async () => { + const mockEntityWithExternalAnnotation = { + ...mockEntityMetadata, + metadata: { + ...mockEntityMetadata.metadata, + annotations: { + [TECHDOCS_EXTERNAL_ANNOTATION]: + 'component:external-namespace/external-docs', + }, + }, + }; + + catalogApiMock.getEntityByRef.mockResolvedValue( + mockEntityWithExternalAnnotation, + ); + + await renderInTestApp( + + + , + { + mountedRoutes, + }, + ); + + expect(mockNavigate).toHaveBeenCalledWith( + '/docs/external-namespace/component/external-docs', + { replace: true }, + ); + }); + + it('should render normally when entity has no external techdocs annotation', async () => { + const mockEntityWithoutExternalAnnotation = { + ...mockEntityMetadata, + metadata: { + ...mockEntityMetadata.metadata, + annotations: undefined, + }, + }; + + catalogApiMock.getEntityByRef.mockResolvedValue( + mockEntityWithoutExternalAnnotation, + ); + + const rendered = await renderInTestApp( + + + , + { + mountedRoutes, + }, + ); + + expect(rendered.container.querySelector('header')).toBeInTheDocument(); + expect(rendered.container.querySelector('article')).toBeInTheDocument(); + expect(mockNavigate).not.toHaveBeenCalled(); + }); + + it('should render normally when entity has external annotation but no value', async () => { + const mockEntityWithEmptyExternalAnnotation = { + ...mockEntityMetadata, + metadata: { + ...mockEntityMetadata.metadata, + annotations: { + [TECHDOCS_EXTERNAL_ANNOTATION]: '', + }, + }, + }; + + catalogApiMock.getEntityByRef.mockResolvedValue( + mockEntityWithEmptyExternalAnnotation, + ); + + const rendered = await renderInTestApp( + + + , + { + mountedRoutes, + }, + ); + + expect(rendered.container.querySelector('header')).toBeInTheDocument(); + expect(rendered.container.querySelector('article')).toBeInTheDocument(); + expect(mockNavigate).not.toHaveBeenCalled(); + }); + + it('should render normally when catalog API throws an error', async () => { + catalogApiMock.getEntityByRef.mockRejectedValue( + new Error('Catalog API error'), + ); + + const rendered = await renderInTestApp( + + + , + { + mountedRoutes, + }, + ); + + expect(rendered.container.querySelector('header')).toBeInTheDocument(); + expect(rendered.container.querySelector('article')).toBeInTheDocument(); + expect(mockNavigate).not.toHaveBeenCalled(); + }); + }); }); diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx index 80545151d9..c67660fd79 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx @@ -14,19 +14,26 @@ * limitations under the License. */ -import { Children, ReactElement, ReactNode } from 'react'; -import { useOutlet } from 'react-router-dom'; - +import { + Children, + ReactElement, + ReactNode, + useEffect, + useMemo, + useCallback, +} from 'react'; +import { useOutlet, useNavigate } from 'react-router-dom'; import { Page } from '@backstage/core-components'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { TECHDOCS_ADDONS_KEY, TECHDOCS_ADDONS_WRAPPER_KEY, TechDocsReaderPageProvider, + buildTechDocsURL, } from '@backstage/plugin-techdocs-react'; - +import { TECHDOCS_EXTERNAL_ANNOTATION } from '@backstage/plugin-techdocs-common'; +import useAsync from 'react-use/esm/useAsync'; import { TechDocsReaderPageRenderFunction } from '../../../types'; - import { TechDocsReaderPageContent } from '../TechDocsReaderPageContent'; import { TechDocsReaderPageHeader } from '../TechDocsReaderPageHeader'; import { TechDocsReaderPageSubheader } from '../TechDocsReaderPageSubheader'; @@ -34,9 +41,11 @@ import { rootDocsRouteRef } from '../../../routes'; import { getComponentData, useRouteRefParams, + useApi, + useRouteRef, } from '@backstage/core-plugin-api'; - import { CookieAuthRefreshProvider } from '@backstage/plugin-auth-react'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { createTheme, styled, @@ -44,6 +53,7 @@ import { ThemeProvider, useTheme, } from '@material-ui/core/styles'; +import { Progress } from '@backstage/core-components'; /* An explanation for the multiple ways of customizing the TechDocs reader page @@ -177,44 +187,106 @@ const StyledPage = styled(Page)({ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { const currentTheme = useTheme(); - const readerPageTheme = createTheme({ - ...currentTheme, - ...(props.overrideThemeOptions || {}), - }); + const readerPageTheme = useMemo( + () => + createTheme({ + ...currentTheme, + ...(props.overrideThemeOptions || {}), + }), + [currentTheme, props.overrideThemeOptions], + ); + const { kind, name, namespace } = useRouteRefParams(rootDocsRouteRef); const { children, entityRef = { kind, name, namespace } } = props; const outlet = useOutlet(); - if (!children) { + const catalogApi = useApi(catalogApiRef); + const navigate = useNavigate(); + const viewTechdocLink = useRouteRef(rootDocsRouteRef); + + const memoizedEntityRef = useMemo( + () => ({ + kind: entityRef.kind, + name: entityRef.name, + namespace: entityRef.namespace, + }), + [entityRef.kind, entityRef.name, entityRef.namespace], + ); + + const externalEntityTechDocsUrl = useAsync(async () => { + try { + const catalogEntity = await catalogApi.getEntityByRef(memoizedEntityRef); + + if ( + catalogEntity?.metadata?.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION] + ) { + return buildTechDocsURL(catalogEntity, viewTechdocLink); + } + } catch (error) { + // Ignore error and allow an attempt at loading the current entity's TechDocs when unable to fetch an external entity from the catalog. + } + + return undefined; + }, [memoizedEntityRef, catalogApi, viewTechdocLink]); + + const handleNavigation = useCallback( + (url: string) => { + navigate(url, { replace: true }); + }, + [navigate], + ); + + useEffect(() => { + if (!externalEntityTechDocsUrl.loading && externalEntityTechDocsUrl.value) { + handleNavigation(externalEntityTechDocsUrl.value); + } + }, [ + externalEntityTechDocsUrl.loading, + externalEntityTechDocsUrl.value, + handleNavigation, + ]); + + const page: ReactNode = useMemo(() => { + if (children) { + return null; + } + const childrenList = outlet ? Children.toArray(outlet.props.children) : []; const grandChildren = childrenList.flatMap( child => (child as ReactElement)?.props?.children ?? [], ); - const page: ReactNode = grandChildren.find( + return grandChildren.find( grandChild => !getComponentData(grandChild, TECHDOCS_ADDONS_WRAPPER_KEY) && !getComponentData(grandChild, TECHDOCS_ADDONS_KEY), ); + }, [children, outlet]); - // As explained above, "page" is configuration 4 and is 1 + if (externalEntityTechDocsUrl.loading || externalEntityTechDocsUrl.value) { + return ; + } + + // As explained above, "page" is configuration 4 and is 1 + if (!children) { return ( - + {(page as JSX.Element) || } ); } + // As explained above, a render function is configuration 3 and React element is 2 return ( - + {({ metadata, entityMetadata, onReady }) => ( { > {children instanceof Function ? children({ - entityRef, + entityRef: memoizedEntityRef, techdocsMetadataValue: metadata.value, entityMetadataValue: entityMetadata.value, onReady, diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx index 6f6ee81c8b..3c10b26f7f 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx @@ -52,13 +52,11 @@ const mockTechDocsMetadata = { site_description: 'test-site-desc', }; -const mockUseParams = jest.fn(); -mockUseParams.mockReturnValue({ '*': 'foo/bar/baz/' }); - +let useParamsPath = '/'; jest.mock('react-router-dom', () => { return { ...(jest.requireActual('react-router-dom') as any), - useParams: () => mockUseParams(), + useParams: () => ({ '*': useParamsPath }), }; }); @@ -189,6 +187,7 @@ describe('', () => { getEntityMetadata.mockResolvedValue(mockEntityMetadata); getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata); + useParamsPath = 'foo/bar/baz/'; await renderInTestApp( @@ -203,7 +202,31 @@ describe('', () => { await waitFor(() => { expect(document.title).toEqual( - 'Backstage | Test Entity | Foo | Bar | Baz', + 'Test Entity | Foo | Bar | Baz | Backstage', + ); + }); + }); + + it('The header title is abbreviated if path is too long', async () => { + getEntityMetadata.mockResolvedValue(mockEntityMetadata); + getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata); + + useParamsPath = 'foo/bar/baz/qux/quux/'; + await renderInTestApp( + + + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name/*': entityRouteRef, + '/docs': rootRouteRef, + }, + }, + ); + + await waitFor(() => { + expect(document.title).toEqual( + 'Test Entity | Foo | Bar | Baz | Qux | Quux | Backstage', ); }); }); diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx index bafcdd270c..6328becc73 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx @@ -172,17 +172,16 @@ export const TechDocsReaderPageHeader = ( const removeTrailingSlash = (str: string) => str.replace(/\/$/, ''); const normalizeAndSpace = (str: string) => - str.replace(/-/g, ' ').split(' ').map(capitalize).join(' '); + str.replace(/[-_]/g, ' ').split(' ').map(capitalize).join(' '); let techdocsTabTitleItems: string[] = []; if (path !== '') techdocsTabTitleItems = removeTrailingSlash(path) .split('/') - .slice(0, 3) .map(normalizeAndSpace); - const tabTitleItems = [appTitle, entityDisplayName, ...techdocsTabTitleItems]; + const tabTitleItems = [entityDisplayName, ...techdocsTabTitleItems, appTitle]; const tabTitle = tabTitleItems.join(' | '); return ( diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/attributes.ts b/plugins/techdocs/src/reader/transformers/html/hooks/attributes.ts new file mode 100644 index 0000000000..ae0b4e2ce9 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/hooks/attributes.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2025 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 { UponSanitizeAttributeHook } from 'dompurify'; + +/** + * Removes attributes that should only be present on meta tags from other elements. + * This ensures that http-equiv and content attributes are only allowed on meta tags + * where they are required for the redirect feature. + */ +export const removeRestrictedAttributes: UponSanitizeAttributeHook = ( + node, + data, +) => { + if (node.tagName !== 'META') { + if (data.attrName === 'http-equiv' || data.attrName === 'content') { + node.removeAttribute(data.attrName); + } + } +}; diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/iframes.ts b/plugins/techdocs/src/reader/transformers/html/hooks/iframes.ts index 25259dbc43..23711b52b7 100644 --- a/plugins/techdocs/src/reader/transformers/html/hooks/iframes.ts +++ b/plugins/techdocs/src/reader/transformers/html/hooks/iframes.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { isElement } from '../utils'; + /** * Checks whether a node is iframe or not. * @param node - can be any element. @@ -42,9 +44,10 @@ const isSafe = (node: Element, hosts: string[]) => { * @param node - can be any element. * @param hosts - list of allowed hosts. */ -export const removeUnsafeIframes = (hosts: string[]) => (node: Element) => { +export const removeUnsafeIframes = (hosts: string[]) => (node: Node) => { + if (!isElement(node)) return; + if (isIframe(node) && !isSafe(node, hosts)) { node.remove(); } - return node; }; diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/index.ts b/plugins/techdocs/src/reader/transformers/html/hooks/index.ts index a4356db2e9..ce237ec6d2 100644 --- a/plugins/techdocs/src/reader/transformers/html/hooks/index.ts +++ b/plugins/techdocs/src/reader/transformers/html/hooks/index.ts @@ -16,3 +16,5 @@ export { removeUnsafeLinks } from './links'; export { removeUnsafeIframes } from './iframes'; +export { removeUnsafeMetaTags } from './metatags'; +export { removeRestrictedAttributes } from './attributes'; diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/links.ts b/plugins/techdocs/src/reader/transformers/html/hooks/links.ts index 38f775cfbe..eb2cbfc6de 100644 --- a/plugins/techdocs/src/reader/transformers/html/hooks/links.ts +++ b/plugins/techdocs/src/reader/transformers/html/hooks/links.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { isElement } from '../utils'; + const MKDOCS_CSS = /main\.[A-Fa-f0-9]{8}\.min\.css$/; const GOOGLE_FONTS = /^https:\/\/fonts\.googleapis\.com/; const GSTATIC_FONTS = /^https:\/\/fonts\.gstatic\.com/; @@ -41,11 +43,11 @@ const isSafe = (node: Element) => { /** * Function that removes unsafe link nodes. * @param node - can be any element. - * @param hosts - list of allowed hosts. */ -export const removeUnsafeLinks = (node: Element) => { +export const removeUnsafeLinks = (node: Node) => { + if (!isElement(node)) return; + if (isLink(node) && !isSafe(node)) { node.remove(); } - return node; }; diff --git a/plugins/techdocs/src/reader/transformers/html/hooks/metatags.ts b/plugins/techdocs/src/reader/transformers/html/hooks/metatags.ts new file mode 100644 index 0000000000..a207a5bca2 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/hooks/metatags.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2025 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 { UponSanitizeElementHook } from 'dompurify'; +import { isElement } from '../utils'; + +/** + * Checks if a meta tag is a refresh redirect tag that should be allowed. + * These tags are required for the TechDocs redirect feature. + */ +const isAllowedMetaRefreshTag = (element: Element): boolean => { + const httpEquiv = element.getAttribute('http-equiv'); + const content = element.getAttribute('content'); + + return httpEquiv === 'refresh' && content?.includes('url=') === true; +}; + +/** + * Removes unsafe meta tags from the DOM while preserving allowed refresh redirect tags. + * Only meta tags used for page refreshing/redirects are allowed as they are required + * for the TechDocs redirect feature. + */ +export const removeUnsafeMetaTags: UponSanitizeElementHook = (node, data) => { + if (!isElement(node)) return; + + if (data.tagName === 'meta' && !isAllowedMetaRefreshTag(node)) { + node.parentNode?.removeChild(node); + } +}; diff --git a/plugins/techdocs/src/reader/transformers/html/transformer.ts b/plugins/techdocs/src/reader/transformers/html/transformer.ts index 7c341c783e..6ebf53f26e 100644 --- a/plugins/techdocs/src/reader/transformers/html/transformer.ts +++ b/plugins/techdocs/src/reader/transformers/html/transformer.ts @@ -20,7 +20,12 @@ import { useCallback, useMemo } from 'react'; import { configApiRef, useApi } from '@backstage/core-plugin-api'; import { Transformer } from '../transformer'; -import { removeUnsafeIframes, removeUnsafeLinks } from './hooks'; +import { + removeRestrictedAttributes, + removeUnsafeIframes, + removeUnsafeLinks, + removeUnsafeMetaTags, +} from './hooks'; /** * Returns html sanitizer configuration @@ -51,26 +56,9 @@ export const useSanitizerTransformer = (): Transformer => { DOMPurify.addHook('beforeSanitizeElements', removeUnsafeIframes(hosts)); } - // Only allow meta tags if they are used for refreshing the page. They are required for the redirect feature. - DOMPurify.addHook('uponSanitizeElement', (currNode, data) => { - if (data.tagName === 'meta') { - const isMetaRefreshTag = - currNode.getAttribute('http-equiv') === 'refresh' && - currNode.getAttribute('content')?.includes('url='); - if (!isMetaRefreshTag) { - currNode.parentNode?.removeChild(currNode); - } - } - }); + DOMPurify.addHook('uponSanitizeElement', removeUnsafeMetaTags); - // Only allow http-equiv and content attributes on meta tags. They are required for the redirect feature. - DOMPurify.addHook('uponSanitizeAttribute', (currNode, data) => { - if (currNode.tagName !== 'meta') { - if (data.attrName === 'http-equiv' || data.attrName === 'content') { - currNode.removeAttribute(data.attrName); - } - } - }); + DOMPurify.addHook('uponSanitizeAttribute', removeRestrictedAttributes); const tagNameCheck = config?.getOptionalString( 'allowedCustomElementTagNameRegExp', @@ -122,7 +110,7 @@ export const useSanitizerTransformer = (): Transformer => { ? new RegExp(attributeNameCheck) : undefined, }, - }); + }) as Element; }, [config], ); diff --git a/plugins/techdocs/src/reader/transformers/html/utils.ts b/plugins/techdocs/src/reader/transformers/html/utils.ts new file mode 100644 index 0000000000..b97b63743c --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/html/utils.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 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 isElement = (node: Node): node is Element => { + return node.nodeType === Node.ELEMENT_NODE; +}; diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 5d4f80079f..cfb4c0b712 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-user-settings +## 0.8.26-next.2 + +### Patch Changes + +- b713b54: Tool-tip text correction for the Theme selection in settings page +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/core-compat-api@0.5.2-next.2 + ## 0.8.26-next.1 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index d5224ac8b6..3ce96f9146 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.8.26-next.1", + "version": "0.8.26-next.2", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings/report-alpha.api.md b/plugins/user-settings/report-alpha.api.md index 5a28aacc02..1c8e844d05 100644 --- a/plugins/user-settings/report-alpha.api.md +++ b/plugins/user-settings/report-alpha.api.md @@ -123,7 +123,7 @@ export const userSettingsTranslationRef: TranslationRef< readonly 'languageToggle.select': 'Select language {{language}}'; readonly 'languageToggle.title': 'Language'; readonly 'languageToggle.description': 'Change the language'; - readonly 'themeToggle.select': 'Select theme {{theme}}'; + readonly 'themeToggle.select': 'Select {{theme}}'; readonly 'themeToggle.title': 'Theme'; readonly 'themeToggle.description': 'Change the theme mode'; readonly 'themeToggle.names.auto': 'Auto'; diff --git a/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx b/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx index b0ddbf31cf..ce9c1093ca 100644 --- a/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx +++ b/plugins/user-settings/src/components/AuthProviders/DefaultProviderSettings.tsx @@ -26,6 +26,7 @@ import { bitbucketServerAuthApiRef, atlassianAuthApiRef, oneloginAuthApiRef, + openshiftAuthApiRef, } from '@backstage/core-plugin-api'; import { userSettingsTranslationRef } from '../../translation'; import { useTranslationRef } from '@backstage/frontend-plugin-api'; @@ -128,6 +129,14 @@ export const DefaultProviderSettings = (props: { icon={Star} /> )} + {configuredProviders.includes('openshift') && ( + + )} ); }; diff --git a/plugins/user-settings/src/translation.ts b/plugins/user-settings/src/translation.ts index 0161508d14..7445a415f2 100644 --- a/plugins/user-settings/src/translation.ts +++ b/plugins/user-settings/src/translation.ts @@ -28,7 +28,7 @@ export const userSettingsTranslationRef = createTranslationRef({ themeToggle: { title: 'Theme', description: 'Change the theme mode', - select: 'Select theme {{theme}}', + select: 'Select {{theme}}', selectAuto: 'Select Auto Theme', names: { light: 'Light', diff --git a/yarn.lock b/yarn.lock index fde37ae4e3..462290b101 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3182,6 +3182,7 @@ __metadata: "@types/d3-selection": "npm:^3.0.1" "@types/d3-shape": "npm:^3.0.1" "@types/d3-zoom": "npm:^3.0.1" + "@types/fscreen": "npm:^1" "@types/google-protobuf": "npm:^3.7.2" "@types/react": "npm:^18.0.0" "@types/react-helmet": "npm:^6.1.0" @@ -3207,6 +3208,7 @@ __metadata: rc-progress: "npm:3.5.1" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" + react-full-screen: "npm:^1.1.1" react-helmet: "npm:6.1.0" react-hook-form: "npm:^7.12.2" react-idle-timer: "npm:5.7.2" @@ -4120,6 +4122,27 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-auth-backend-module-openshift-provider@workspace:^, @backstage/plugin-auth-backend-module-openshift-provider@workspace:plugins/auth-backend-module-openshift-provider": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth-backend-module-openshift-provider@workspace:plugins/auth-backend-module-openshift-provider" + dependencies: + "@backstage/backend-defaults": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/plugin-auth-backend": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + "@backstage/types": "workspace:^" + express: "npm:^4.18.2" + msw: "npm:^2.7.3" + passport-oauth2: "npm:^1.8.0" + supertest: "npm:^7.1.0" + zod: "npm:^3.24.2" + languageName: unknown + linkType: soft + "@backstage/plugin-auth-backend-module-pinniped-provider@workspace:plugins/auth-backend-module-pinniped-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-pinniped-provider@workspace:plugins/auth-backend-module-pinniped-provider" @@ -4196,6 +4219,7 @@ __metadata: knex: "npm:^3.0.0" lodash: "npm:^4.17.21" luxon: "npm:^3.0.0" + matcher: "npm:^4.0.0" minimatch: "npm:^9.0.0" passport: "npm:^0.7.0" supertest: "npm:^7.0.0" @@ -4263,6 +4287,41 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-auth@workspace:^, @backstage/plugin-auth@workspace:plugins/auth": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth@workspace:plugins/auth" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/frontend-defaults": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@testing-library/jest-dom": "npm:^6.0.0" + "@testing-library/react": "npm:^16.0.0" + "@testing-library/user-event": "npm:^14.0.0" + "@types/react": "npm:^18.0.0" + msw: "npm:^1.0.0" + react: "npm:^18.0.2" + react-dom: "npm:^18.0.2" + react-router-dom: "npm:^6.3.0" + react-use: "npm:^17.2.4" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + languageName: unknown + linkType: soft + "@backstage/plugin-bitbucket-cloud-common@workspace:^, @backstage/plugin-bitbucket-cloud-common@workspace:plugins/bitbucket-cloud-common": version: 0.0.0-use.local resolution: "@backstage/plugin-bitbucket-cloud-common@workspace:plugins/bitbucket-cloud-common" @@ -6576,6 +6635,9 @@ __metadata: "@backstage/plugin-permission-react": "workspace:^" "@backstage/plugin-scaffolder-common": "workspace:^" "@backstage/plugin-scaffolder-react": "workspace:^" + "@backstage/plugin-techdocs": "workspace:^" + "@backstage/plugin-techdocs-common": "workspace:^" + "@backstage/plugin-techdocs-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" "@codemirror/language": "npm:^6.0.0" @@ -7229,7 +7291,7 @@ __metadata: "@testing-library/user-event": "npm:^14.0.0" "@types/dompurify": "npm:^3.0.0" "@types/react": "npm:^18.0.0" - dompurify: "npm:^3.0.0" + dompurify: "npm:^3.2.4" git-url-parse: "npm:^15.0.0" jss: "npm:~10.10.0" lodash: "npm:^4.17.21" @@ -11083,9 +11145,9 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.39.1": - version: 0.39.2 - resolution: "@mswjs/interceptors@npm:0.39.2" +"@mswjs/interceptors@npm:^0.37.0": + version: 0.37.1 + resolution: "@mswjs/interceptors@npm:0.37.1" dependencies: "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/logger": "npm:^0.3.0" @@ -11093,7 +11155,7 @@ __metadata: is-node-process: "npm:^1.2.0" outvariant: "npm:^1.4.3" strict-event-emitter: "npm:^0.5.1" - checksum: 10/faaa95d636363a197f125c32066457fa74d5063d8ccae4c9c0e0510179060d92b1faf8640df45a0623e0bf42a30d610c83364a58e0eb0ca412c87b2e835936c1 + checksum: 10/332d8aa50beb4834ccbda6a800ca00b1204adc0eba23e1c1f7bb9f4e564a92707e563f7a2424d4a8607404ec91424e5d8c34a87c250b191ca7b24dff12eba2c5 languageName: node linkType: hard @@ -20270,6 +20332,13 @@ __metadata: languageName: node linkType: hard +"@types/fscreen@npm:^1": + version: 1.0.4 + resolution: "@types/fscreen@npm:1.0.4" + checksum: 10/78459a457ce7a6b7d72a5f17fdb54bbeb93c58ab77fd2858aac610fed2435bc4be9e5d2fb9883b6669b7f3a1204115cc2be59a027ab937ee8b5186225d2ea53d + languageName: node + linkType: hard + "@types/git-url-parse@npm:^9.0.0": version: 9.0.3 resolution: "@types/git-url-parse@npm:9.0.3" @@ -21470,10 +21539,10 @@ __metadata: languageName: node linkType: hard -"@types/trusted-types@npm:*": - version: 2.0.3 - resolution: "@types/trusted-types@npm:2.0.3" - checksum: 10/4794804bc4a4a173d589841b6d26cf455ff5dc4f3e704e847de7d65d215f2e7043d8757e4741ce3a823af3f08260a8d04a1a6e9c5ec9b20b7b04586956a6b005 +"@types/trusted-types@npm:*, @types/trusted-types@npm:^2.0.7": + version: 2.0.7 + resolution: "@types/trusted-types@npm:2.0.7" + checksum: 10/8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3 languageName: node linkType: hard @@ -26267,7 +26336,7 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8": +"combined-stream@npm:^1.0.8": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -26395,10 +26464,10 @@ __metadata: languageName: node linkType: hard -"component-emitter@npm:^1.3.1": - version: 1.3.1 - resolution: "component-emitter@npm:1.3.1" - checksum: 10/94550aa462c7bd5a61c1bc480e28554aa306066930152d1b1844a0dd3845d4e5db7e261ddec62ae184913b3e59b55a2ad84093b9d3596a8f17c341514d6c483d +"component-emitter@npm:^1.3.0": + version: 1.3.0 + resolution: "component-emitter@npm:1.3.0" + checksum: 10/dfc1ec2e7aa2486346c068f8d764e3eefe2e1ca0b24f57506cd93b2ae3d67829a7ebd7cc16e2bf51368fac2f45f78fcff231718e40b1975647e4a86be65e1d05 languageName: node linkType: hard @@ -27622,15 +27691,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0": - version: 4.4.1 - resolution: "debug@npm:4.4.1" +"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.4.0": + version: 4.4.0 + resolution: "debug@npm:4.4.0" dependencies: ms: "npm:^2.1.3" peerDependenciesMeta: supports-color: optional: true - checksum: 10/8e2709b2144f03c7950f8804d01ccb3786373df01e406a0f66928e47001cf2d336cbed9ee137261d4f90d68d8679468c755e3548ed83ddacdc82b194d2468afe + checksum: 10/1847944c2e3c2c732514b93d11886575625686056cd765336212dc15de2d2b29612b6cd80e1afba767bb8e1803b778caf9973e98169ef1a24a7a7009e1820367 languageName: node linkType: hard @@ -28386,10 +28455,15 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:^3.0.0, dompurify@npm:^3.1.7": - version: 3.1.7 - resolution: "dompurify@npm:3.1.7" - checksum: 10/dc637a064306f83cf911caa267ffe1f973552047602020e3b6723c90f67962813edf8a65a0b62e8c9bc13fcd173a2691212a3719bc116226967f46bcd6181277 +"dompurify@npm:^3.1.7, dompurify@npm:^3.2.4": + version: 3.2.6 + resolution: "dompurify@npm:3.2.6" + dependencies: + "@types/trusted-types": "npm:^2.0.7" + dependenciesMeta: + "@types/trusted-types": + optional: true + checksum: 10/b91631ed0e4d17fae950ef53613cc009ed7e73adc43ac94a41dd52f35483f7538d13caebdafa7626e0da145fc8184e7ac7935f14f25b7e841b32fda777e40447 languageName: node linkType: hard @@ -29917,6 +29991,7 @@ __metadata: "@backstage/plugin-api-docs": "workspace:^" "@backstage/plugin-app": "workspace:^" "@backstage/plugin-app-visualizer": "workspace:^" + "@backstage/plugin-auth": "workspace:^" "@backstage/plugin-auth-react": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" @@ -30051,6 +30126,7 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-backend-module-github-provider": "workspace:^" "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^" + "@backstage/plugin-auth-backend-module-openshift-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-backend": "workspace:^" "@backstage/plugin-catalog-backend-module-backstage-openapi": "workspace:^" @@ -31056,13 +31132,16 @@ __metadata: linkType: hard "form-data@npm:^2.3.2, form-data@npm:^2.5.0": - version: 2.5.1 - resolution: "form-data@npm:2.5.1" + version: 2.5.5 + resolution: "form-data@npm:2.5.5" dependencies: asynckit: "npm:^0.4.0" - combined-stream: "npm:^1.0.6" - mime-types: "npm:^2.1.12" - checksum: 10/2e2e5e927979ba3623f9b4c4bcc939275fae3f2dea9dafc8db3ca656a3d75476605de2c80f0e6f1487987398e056f0b4c738972d6e1edd83392d5686d0952eed + combined-stream: "npm:^1.0.8" + es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" + mime-types: "npm:^2.1.35" + safe-buffer: "npm:^5.2.1" + checksum: 10/4b6a8d07bb67089da41048e734215f68317a8e29dd5385a972bf5c458a023313c69d3b5d6b8baafbb7f808fa9881e0e2e030ffe61e096b3ddc894c516401271d languageName: node linkType: hard @@ -31105,7 +31184,7 @@ __metadata: languageName: node linkType: hard -"formidable@npm:^3.5.4": +"formidable@npm:^3.5.1": version: 3.5.4 resolution: "formidable@npm:3.5.4" dependencies: @@ -31309,6 +31388,13 @@ __metadata: languageName: node linkType: hard +"fscreen@npm:^1.0.2": + version: 1.2.0 + resolution: "fscreen@npm:1.2.0" + checksum: 10/ac50f9ac52a157b8fe6aaecdf9efa7c1cfa90b42a76c3bc6b85372fab05c5a9cd72c1b7f4c2e273eba1a0e630e381fd72ae135fcc57acd05a0943d5d0c21b451 + languageName: node + linkType: hard + "fsevents@npm:2.3.2": version: 2.3.2 resolution: "fsevents@npm:2.3.2" @@ -37210,6 +37296,15 @@ __metadata: languageName: node linkType: hard +"matcher@npm:^4.0.0": + version: 4.0.0 + resolution: "matcher@npm:4.0.0" + dependencies: + escape-string-regexp: "npm:^4.0.0" + checksum: 10/d338aff31d8dfd3626873e43777f46b123579734d53bb8d18d64b08a822ba5e8d39f5fe2e23403258e6143aa0cbe20a15662720d825cd0d3af961d5a44230328 + languageName: node + linkType: hard + "material-ui-confirm@npm:^3.0.12": version: 3.0.18 resolution: "material-ui-confirm@npm:3.0.18" @@ -38635,15 +38730,15 @@ __metadata: languageName: node linkType: hard -"msw@npm:^2.0.0, msw@npm:^2.0.8": - version: 2.10.4 - resolution: "msw@npm:2.10.4" +"msw@npm:^2.0.0, msw@npm:^2.0.8, msw@npm:^2.7.3": + version: 2.7.3 + resolution: "msw@npm:2.7.3" dependencies: "@bundled-es-modules/cookie": "npm:^2.0.1" "@bundled-es-modules/statuses": "npm:^1.0.1" "@bundled-es-modules/tough-cookie": "npm:^0.1.6" "@inquirer/confirm": "npm:^5.0.0" - "@mswjs/interceptors": "npm:^0.39.1" + "@mswjs/interceptors": "npm:^0.37.0" "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/until": "npm:^2.1.0" "@types/cookie": "npm:^0.6.0" @@ -38664,7 +38759,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 10/e2f25dda1aba66c7444c29c41d3157cb15c0332055ab7ebfb74ef4b506e7b90098cf37c577768edb5b2b2dbf0d6ed6a7a3ca8ee6da3d72df5a25823d82f33316 + checksum: 10/f193329a68fc22e477a6f8504aa44a92bd12847f2eeac1dfbd8ec1cc43ff293112ec067de1c7fe312ba02beecb313fb00aeeebf5817432b57af2d796b2dff2fa languageName: node linkType: hard @@ -40614,7 +40709,7 @@ __metadata: languageName: node linkType: hard -"passport-oauth2@npm:1.8.0, passport-oauth2@npm:1.x.x, passport-oauth2@npm:^1.1.2, passport-oauth2@npm:^1.4.0, passport-oauth2@npm:^1.6.0, passport-oauth2@npm:^1.6.1, passport-oauth2@npm:^1.7.0": +"passport-oauth2@npm:1.8.0, passport-oauth2@npm:1.x.x, passport-oauth2@npm:^1.1.2, passport-oauth2@npm:^1.4.0, passport-oauth2@npm:^1.6.0, passport-oauth2@npm:^1.6.1, passport-oauth2@npm:^1.7.0, passport-oauth2@npm:^1.8.0": version: 1.8.0 resolution: "passport-oauth2@npm:1.8.0" dependencies: @@ -42270,7 +42365,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.2, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.7.0, qs@npm:^6.9.4": +"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.11.2, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.7.0, qs@npm:^6.9.4": version: 6.14.0 resolution: "qs@npm:6.14.0" dependencies: @@ -42849,6 +42944,17 @@ __metadata: languageName: node linkType: hard +"react-full-screen@npm:^1.1.1": + version: 1.1.1 + resolution: "react-full-screen@npm:1.1.1" + dependencies: + fscreen: "npm:^1.0.2" + peerDependencies: + react: ">= 16.8.0" + checksum: 10/70ad927b9d6c485ac46b5bb4b1639ef9a860da28290b3a1c419c42b9c427d78b80e8dba403eb6451458af56838012c81d5e12ef05097395f154defc32fe06c34 + languageName: node + linkType: hard + "react-grid-layout@npm:1.3.4": version: 1.3.4 resolution: "react-grid-layout@npm:1.3.4" @@ -46509,30 +46615,30 @@ __metadata: languageName: node linkType: hard -"superagent@npm:^10.2.3": - version: 10.2.3 - resolution: "superagent@npm:10.2.3" +"superagent@npm:^9.0.1": + version: 9.0.2 + resolution: "superagent@npm:9.0.2" dependencies: - component-emitter: "npm:^1.3.1" + component-emitter: "npm:^1.3.0" cookiejar: "npm:^2.1.4" - debug: "npm:^4.3.7" + debug: "npm:^4.3.4" fast-safe-stringify: "npm:^2.1.1" - form-data: "npm:^4.0.4" - formidable: "npm:^3.5.4" + form-data: "npm:^4.0.0" + formidable: "npm:^3.5.1" methods: "npm:^1.1.2" mime: "npm:2.6.0" - qs: "npm:^6.11.2" - checksum: 10/377bf938e68927dd772169c5285be27872bf6e84fac01c52bcd9396bc5b348c9ded8f8be54649510ec09a67bc5096055847b37cb01b3bca0eb06ff1856170e35 + qs: "npm:^6.11.0" + checksum: 10/d3c0c9051ceec84d5b431eaa410ad81bcd53255cea57af1fc66d683a24c34f3ba4761b411072a9bf489a70e3d5b586a78a0e6f2eac6a561067e7d196ddab0907 languageName: node linkType: hard -"supertest@npm:^7.0.0": - version: 7.1.4 - resolution: "supertest@npm:7.1.4" +"supertest@npm:^7.0.0, supertest@npm:^7.1.0": + version: 7.1.0 + resolution: "supertest@npm:7.1.0" dependencies: methods: "npm:^1.1.2" - superagent: "npm:^10.2.3" - checksum: 10/ecb5d41f2b62b257dbdcabac245c32b8e8fb264fe2636dd85c2c883569d23dc14adc0a471abb84187cbdb49bc36ad870ad355b4a0b85973f510fd57fc229e6cc + superagent: "npm:^9.0.1" + checksum: 10/20069f739a44821dfa4f7f397b9086ef31a358366331138f97945eedb2e231796e7c55b032125d3bd12f9839f089fbb809893dbc0f98edc57e12333b9f42b726 languageName: node linkType: hard @@ -49862,6 +49968,7 @@ __metadata: "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" + "@backstage/errors": "workspace:^" "@backstage/release-manifests": "workspace:^" "@yarnpkg/builder": "npm:^4.2.1" "@yarnpkg/core": "npm:^4.4.1" @@ -50031,10 +50138,10 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.22.4, zod@npm:^3.23.8": - version: 3.25.76 - resolution: "zod@npm:3.25.76" - checksum: 10/f0c963ec40cd96858451d1690404d603d36507c1fc9682f2dae59ab38b578687d542708a7fdbf645f77926f78c9ed558f57c3d3aa226c285f798df0c4da16995 +"zod@npm:^3.22.4, zod@npm:^3.23.8, zod@npm:^3.24.2": + version: 3.25.67 + resolution: "zod@npm:3.25.67" + checksum: 10/0e35432dcca7f053e63f5dd491a87c78abe0d981817547252c3b6d05f0f58788695d1a69724759c6501dff3fd62929be24c9f314a3625179bee889150f7a61fa languageName: node linkType: hard From d0baa442e873bfe028a7f0353f3336205f369596 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 12 Sep 2025 11:53:27 +0100 Subject: [PATCH 020/211] Add CSS variables + class name structure Signed-off-by: Charles de Dreuille --- .../css-classname-structure.png | Bin 0 -> 42743 bytes docs/conf/user-interface/index.md | 154 +++++++++++++++++- microsite/src/theme/customTheme.scss | 5 + 3 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 docs/assets/user-interface/css-classname-structure.png diff --git a/docs/assets/user-interface/css-classname-structure.png b/docs/assets/user-interface/css-classname-structure.png new file mode 100644 index 0000000000000000000000000000000000000000..c241b182cc49d9044901d7e4c0dcbb30479bf006 GIT binary patch literal 42743 zcmeFY^;aA1w>}(7k>YK!;uMNo3&q{trD#hj5+q1)Zz)nJ?pEBjIKc{(;_ezeSa1js ziY)kX((jc5Df!=l=vODA!OMSQpeE$ zO6!xV&Pyq+RgA}DkH_#=51=595Afjj17q|yN~ngO%H#RuW#mlcgX6>O9N#c z&G=tjq$UtkBq|q-V&R^4VBwxWLI0;vASjz>_nNNvT55rZ)j2SK)yC8FDwVGPCeCo5 zXYc4pASftkjT1XTwb?{}o#bBZpK^dxvQKZVyu9Wgo5va&85wo#9#IF7Y`%S<2gW3@ zNT#Kw-FSeU@_*SN4wubsWZymhr(pFRDJiLHf%cOJM+~8n4npbt2O@;fM+`+NCsz*a zsC%E|Ucz12+L}4<>(}PW1#xKq6qH10c}yG}*po9=G*dlR%KAPTImIuD)0GD$?8>Xc z2$;%HusT|4GlxC>rvw1chk7&UhZw;UXmoZDJwD?ZGU7hLRH3 z5l;{6pAwoJX=F;vif3nMYoqXxIy(xw=C$#wD^JgV$4kRZJfEF1QwHNK9udjdK@^BOjP&%W*K%4lfUGj;T z?%$!w7GRatW+J{Cn=UhWXhS;g%gj~an<7;1u2(yQtUi4B^>4%39-LfUY3Txvd(j?A zNl7cTO?k94A~9c%cc8lu3=9natB)eT#>Qe^(9q1#BJcd=^=68bb%F4=m&zT=F{!?3EW?5C$=)Lo(x2C z(f)TG0Az{&XMX6u0G|9)9Psq9;=jMc=p-Ngb0YZ|?SG!v*K{-;0@NheP41 zExjIm07>irn9=Ntt!X_SH6l7a6~F)7QD0+!6@>}dAxD`*Nn>!mC9kpZYh#Cp)j(Fn z87@KK0b4|wm6iX7DF5pPYxxlJ;TIz5mG9oW+eU44`xRz&f9y?fZ<(kjZwv6~Pbi@~ zZ*2}H;WY-*$S2(F!@kAlv>(r~vUe(6ws<5NuM~-nS%+W+ZILgnfDOtl3nsAuny$Wg z=zzL=wFCpTc~n=Ii%q5Sr!3`mY&B<7opC-gbY~a`8iU!*n`{#a6NHV3 zQ>f%u% zP2QuH>fWkAwpW3ZLPlz(;@paAK3+yTOQQqm2>;6$SEFlvkYfN<3$|2KkirU7?F6ZC z94|3pF_a$sU($Ox31T=nLn-Mvxg6jo5(W2>&Gco1R&BfyTps|E-2b?6dP3>%n` zSHKE(;_IqTP9Gi;Ig2Ey`2qm+^cKDN4+aRU4nDqj^5jy+k`&qdWppHt6ZHae2>`SO zM)LwBHA<)ShfJKxII76;gW7A19xN-DW~~SA{Hz3MYPY;>ay&9H>W!jU4k&}fmx$#w<9^i>tRzldqw zcm`b>WqwWdN7A4#AQEMymWUf&@LlmmCSb`WH!h~G*aMt++?UMW^r;laTE^*w`pHHYY4oC+fD%a#Rx zu7x#TS<`2P1m~%r<6dvHxX*~&0VV1eoqRp*x*6IIm)i7d>sqf(v}uY*M-wh?OV2Fb z=68vme<)N*_<2~~EtQ?kY92$79s(-G9BlN1$!xEoEFDKWf*$uI6?_Zd}+Ld_5ZKD*2hDNAIn_mbkhYDrr*3h~p6*YXqZ z-r+=*rEVp`p0|vaI(0n1G=v^>%@DpXg6EtqOGg|VuYH4`j$P|X4X?G`eM6TU*2z0d zay$Ke6EY2L4&YuObJY=aa($5eM2j8Wyt_~cfQrlSpNz>mnM4+fG&UH~-uoNT%*|>1 z6x(LIc(9|L-&-XC1gft+#|Ml(?^-R;ek(v~r`DZk6`gKW9NfqlT}wcK|1~GJpz?> z#jxmi4Xn}EWsVgI`*_Jqg=Z20R}8D8;O3@N4z1xcqN?7 zXxf3J$<>iQdaZ|)FN6K;tVV)c&QiqLS27Ki&fCvZN*4Q3i$G>D;QG%SSd7D)j+xKD zmlObjX3|*C0NKa(p1KZ1fD6P$YOHn^>xILT0onA8`M9dvJO@`*N*=@3@oS}}jxu|H z!+C6f5LQQhbn*qH-`^{PpsNbQTCv&NgP3-DZl1yvpKEPQtn;&&Zsh|NRlAile86>( z6ye(AslWGb4Ez=+iZ5R~<`tz+pIumveD}yaVw~L|lzE=NzTR!!rBS2G)oloamWS<@ zKbPQ1i#j7!xUH9)PEI4)tV&~_MV8*X*UbW^3QDna(pUPfS}da4o*Q;`rsr=sNl0%F4lL4xh=5Gbb>^btpanEU@X>0oToOrQunq%M(#S#-<`B8-kX6(Fk@xtWXOck8M^&ufw(d`(o zJ&+5uYQYe_&hq?Vv`5rTp?#sxF&_?;-*CZpL(TYm4+XIpf!|*omsIo@>Pz9rq zHVZ%Vz^O3mm;LzI=e0PIN|ieStL@r?9_k|AP|(xM`@1_UPrA<6KR!g&$}j#Q=q&sK z&U47SSS6&iY}Cl;vTdC7X?ACyzh`5UA04Wa51+d51I<&3q}aFFP*O=yJ-n{PWwZCN zDsE;R91kxn(P9qqxy8q4dXThj7(K*q82%BvjV!0x08wfwd`qH__DX@oNvXUME`#ots34oKE?#ABx=6hPeOD0+el1XulaOBsTX9v)aem#?jj{wi-ZS+SD} zJO5SbX`Ud>GwbhY)2cu4`UnP9ThnUJdhaxzX)iTYkh^||H1e>t5&z!rCnd>il7((# z@gw!ToAO#tnFK(xSBLwVk1V*0IDq`6*!#F&vQ@LR8}Hyp(IKh@?b~bmq{9u7!d6Bk z3J$;c_?N6JRUIEcG7r=??B_#h2posOyLd^Y-qh~ zW91g5pgh!|BAWApn6O>hfzsTO<-$gF_)9qhb)p82TmvYxjtMd#(%%!#%3lX{BcTGWw}l;j~w!Rtl*#NVp~I21ewj z8+o+17)Ot7-k696$cFs|k~k{eBfHz$2E$HYG=CF3g@q{MBwi*UpA%Yod}^}y7wV{^ zv0+sWe=ZN)5f*q{MrhjGsxUXdoo7`w*E~);eJb`#h1iWYtO4LK5FuXR%FT?)$vvx9i%~YuZxW zz@qQr8Egm3O**%^Kd3`S3bGl2zjDlf)`1+x8_g8W;EKId`kLl*oL|d=7=|yW{d4`g zE6GTLj&${L$nZ9$dR!56Y81Lg?lx}_UIc~YGwcVabH+x51+PHAikfY$*fRGo<6^gN z#(;9`Suke#xX=Q~!yO9fq5MeS*`F6&BbF@yQ7Fj2cwFiZ`A599ZpoL{hPbdG zm0KfvT&UZuI$MJF6S)Wg5Q7FqQ98l-t38>c%yzR;UCgr;tBqHQ)0;gs_>nMoD&W(% zwAu{b{tr-Mqu1qB?hyGLHK8p_4Yoqd-IeNYVnZPFpOuYZ_4_l@V5nuht2lFVrqfEA z>5l#9w)mvB(TFuXsky`P9Saf`I3JGH66kyNB%@py$Ne>L~gn089!FM_ZNPXatN-1rUxh>ix)Fd9CZva3chE@ zSlO-JesVc}MU*z7?vn#CT-#*w~B!2YDkH(F$aEr| zsvdK%$2eTj->P!muG^KZWkNC^%t&o_p%LLDy#(-~h$qBXCT%2Y&)(k7%sD|uKXzDv zda#mRTHf(J83e5T)YEm&Az*L zHZZYm{?*-9YjmVP7IE3fUachZX~~Aj9n#IMzHCO&Vtu;fJ^y6NYcoXQdEQ-W%bQyv z>r$QO5|KBGZPl9*CAlbf$# zldlYwQz_f0?^kcXS;UX1Q?CP+){hYLR<$JrV|+v0}Ho zwviGj2~-OG!bC#2igz)EM|Hn|_u|CCU)&nr!Izpz0DuGgTO4!%3spdV%D}0z6EAhn z?7$o8Z!{v!cE3zYbSZC*vxFdJ_5k~LW^h%s^5(|k z=Mt2kVgzL6fGv%9H~*~{-yEfF%&GrpL=6o&XSjzcet>dKjA#c&77PxpR~WJ*K`o~y zoswEtvD^<}@^42Fov-;Hq3~^iZd%!E~v4H(I=FTQp!3BAr6AM?$;D z8Hd$@s4Zc5GED{0^xWIr-IiMALyX%yoH?HIc0%ElL;SKdffYF)GeQh)iE*xUTXCxr z2thJIY@nI$xDw+Nn)k$PwzfaA1iL+U@9e~E{O~H>R&B)EPSpJ^0;t7e?&9g9Ro@36 z^!r_0`{)u?aa7cqDm177Z$Nh^-sK_3dzRiE0*A8ESZ*|KZ?Jb{nR9s}&D|HEZaN>{ zlr45C4-&~f6e4G23KDsIr=2Hp(aQ1lH@-?9Hc$H>vJ8z{C~Ba(PyaU-kAl|cssFf> zSw_YduI*E}sj94P0DSQI4+OJrBIcaaZ*i4n8TR69 ztJC7qhE3gf-Y6V#XIAF#7NotP{UFXLd7G1+1Qu>5OIBs`&oQQcmPun(mJY-gmk`xH zc`v~D98L^! z3rD}o?8S_{&WVd63pivH;WmyPQCSmp8G$8q<;pz2Gyki)RcBuDqoSM)m-puzb~(7# zIQq%s=O52)H~QB;^Ds`p`;O%=3>oZ9+h#peL_QR~p!iPeamEJWnu^O}=3|UAPSCYU zcqO_QtTb%BowvxK;`y8LNDC){Z`0Iot_9!SKi|v=u3tA+MYs9^tHtV;K+3H=yU}JP zev#B<;Pm%>$8oL^zX;m0DvpHFbc2*=S74nDEsYq|6&a+;i!5XFe}z!qroW~ zS~*Fjc6f^7cS5^omW}o`jj&gK`$VvBkMYRNn_E)?iRdh;-IbRW?vFn>U&;C4k0%wJ z6+=u8)-RNUxNt}uCvD$mDaG2f4RMu+EOYV`4^CWXyBQgVktWnvhKrlz3Uh3e@~^~4 z6<{6iA6TyxN(#@ZKWDHqcJ)RA{WD%_HAZe@)XLzMwt(#g@0{~24%MB8<}5THX2E3U z>cW`@cM&9ljgJ6#H~nztY+IwfD*OLp4CK*z*f6B@3XI>z#lqiGY{u`sehI9-7igLe%g1rAasGY6n zT)iVFCh=8AN>n9#+k=-RQ5~Vv$S)pE>~2LZZ1<^&8#{*I-;Bc(_PvW?nFB76wpcO= zhS;p6u)?e{{6gPgPPo78bWxH4UG^-jtRdmC=GbSsULAZ{X>VUokUdspdh3AYr!E^3 zXu=;%4yxNzi{5@GjGM$vYs(!n?YO+_ST9?55s`YorqCJsL9aTAAER}adc4s#j1 za`sz$X_oasp6}hX)eghkqdgAwcsmHQcXSn9mw%`Qs20!d6XMrVFRgoNal!>3DVaGvwb2gqSEHdqnSv3ar+95}D^}AsoL3yt7 zSa}85!9I9)sivMF%PiC?7JB*Dpv6)D8PVR(HQ$)fo{bUN%z%W%xj$mPYSu21zgL%; zO*B@WtC*dGxiVY&(N-iNTLQ?*ByXEJbn#S-&xfBaCEVopsxHv)G_d_XZDBj5c{BQ+ z)=;=q_iL5=wr(Tm)b9EK1^LZ>19rF5+gyiouV@p6%L_-KK^Tte_a++!K#;aS*3vF%6&AoAH;k_K_-n(EL4#YMXFp=U3bFK42| z;c^xTzs53IbM+)I(1a#d95gt8OPS*3TW<+NSn2%k?OE#xE-+|UK2tz0mCV-j16VMt zkWvMfVTQAf>c7Y8A{QXLdP)myBSKOp*%Njp7Hr126a$^+SRk~W4h(hes<7QE=L8>#~wx$ zuuuL-9;3{k-FB=m@2{YybDlrS5VqTQ4C5bIaVw%-j!iDb*hL_G9mFA)GM)R$Hy=fR zm>*Zm>0arK8}!HyV#=h^nRLsS6w`3xI1Z_-V?JeL+~ap+H`JPN=W3$=9`2gh^9L>2 zR31a7(JE-mWX?0A3j;&@5c$)Tsi1yCxjWmE)pOz6Y-wwHf7k5-QNZ7Oq)D`m*SPB{ zBslKK1)cLGY#^$-7%zV=voG82e~mJ%9ge@2@TyYYn|5S{7rGlJZXk2s35YZmNthr& zJjIA|p+I>|WVfF_+EQT-W)PyI`0(3@fN}3rnL5Uth|6KdZcjZ@{kE(NsAn_+#s{oVrPgb07DvS`tWVp-?JYijYKq?liOspWmR;=QUKbEfs*rhZEJFOgN*fz7NJ*J# zYEJG_MiWw8s{sp?&y*Dgv8gcOUEF1xAbZ?{6M;alGxM)HT!8>s1C;^!w3@cSg%q^~WqKwo^RH5&$q- zwRnbtaq-<3(Yyk(+Qq!JHRB}tMDk*C)1)$tKysmu=PU%nK6UG&j%fr;TIBr3_&D@- z9ZmaUt&L4JC4+1c{if*Y+rYGMUcl0sGuZ&b;e)8kqDQlv?ZchX;pXO2L*-wCrGxSP zpAKr%x!7pjNPi^%=#_~D5jJaF%ih_vdV^z`Y?~fRlSU3F?8X>BT?m*3vk}z}U@51f z$IB0vN#}#$#eqVibKa)AVs?J*`y7pCDvK-}ngrKp@(x@)w$#N}l@7f8ABPU4H!P>Y z)MA?D;HV^nOc#)pNCtXYp`X=;f5TbnyuV#N&NhYCYq3FJ;|o8L6+dSA^#`^RgpeCM zls_!Cdyg$VptVjU>#>5>Rc*uYsqz{9?}PiY4T|{^r-%C>^z;$q`|I%^`^FBGJ{=W5 ztl0^LN63pHsa2THIOWQ$9pT@XeAcP!jlytb zVM{M(-!*BH{%pjkIjmp1F1u(}$0@(8vx381e`aeq0`llwoT5U*!*6HXk+Q|N;i?jD zoX?f#-eo@gg{Z_75j3MxcHDO;BDbHrgM!vkv>-oUUAtj)oPY;8@tIk>i0UcSKm;pX zdi}xS-^`mnNNGG9ev2%R<#F;DxxkmJ&i6D(qXc`z3+D=hpdfQ$j z8dLUO*6d|Wf_hux(72WKnW`CujI!x^d_>g6TMi;So7WYDVj5Md07sF~I)65m6KL`! zHHp6)`@k)yT>QBwDZIm?+wZYAH8DY3LzVcHMXfn{`nb^hXqAbI*@}Z^nX|#3>+fSe zvlh2>Ds}Fa6at2nONh?c9q%W+313o1W$1R zbKbL+rnnCpe>D>8$glKIJ6@r;$p=TnsV?mWd%w7FKgMK}kAm;vI(ms31OyzB>^J&k z$$|HK&%&h0#+yGB(F*JhOjtjL>Hme-#edhre;ssRP&^6%z*v~0X*5129FHWYK~Y=T z@hG|GH5g$unmTR6f=ZNH|Bt*X`4Nuubo!ah7OI5n!oVA}r+{oQc^AQd*M&-fas8WZ zm5s8TuWDN<9`^LN_fUBPJFR{ecUB+y?#Z~@^Uwfs|5cZ8>WaN!1O1{7geA(h+F|P`Zq@{yHmo-qq1)y4HXs9Yh zU`2_Pj#?}m(2puhUIYY4aup`qZr@0AUOm*Hw%klNg9`nGY%}rF%`j4a!0KbR0L~!J zfR*iVfR?rs{{&Pc5YxWa*5PgIn;GrU1Q4KF&x)-Rk+<+?^f`l{iPw9;JuFm(Ol1>z zb2xC5rKg)kK0jYe(6rxObfLpVa#p(}LAKdUdp+vl+>QpwmqYceZ&TT_|JI_w)~d9k ziXX+a!~nhfQ&i4WtWb$6LiN^j-xI>`B7Sz|(Jq(QwPmfqP>Qpcr*HA`(TkY77GpAc z-Qpc4okoM9<_JoX#K~T)otQ_E-;|ECFAXZPe6v5Pw^t$j_53c9>lK+{j#(6&1IS03 zp67@v1A)7?FA-32F`H9u!|&}QHCgR``e*6mm1xoB!5fj! zA*~_-upckRNxXx6@&2ZbdUL;y%e^18FtBw)tuv z3K8&j*lxiVk7ge3-^|dt^63ouODGc}X$X4PJ>eauTk(R|%`TV=Wg=aqr*Qy8#(p$e zHi(!mx?lw*7TxPPKZbPad;u6~H$kDhlJoxPyBX=J)=|{&Q}<{AbgxiZKY*X6Y45{t%QYE+o}1$O^b>?D z>F={BmAW_XERHD%ql6j_MvD}Q-@e!yn>H-$J)R4e))XBXK;I| z$LI={heiIf&RF%kY@?hd5QeFUT2TIOX~I9vZgxLj!M=^(tL|+j z3jjbA@r61|IIL#ESD|I_F7~i+AGe+^W8nVjTODHpnJQ(6Y2Bp#K?&_W-sheBk$C6n z-#%x)?f-%gtK&N4tV$^$vA_&cYhs^^GGaEOHGcb$u-%T6+_T8G?vq;-kX7=0o zbz<=Jg;K3vUphG)HxxKOkozM^2F$5zm!53-smZYy|3%&f5hz{mIVj84$v0W$Vy(YU zosT#nguVOn1Yzibw7)p1U8aCnc|R;DAT;cuCw7goK6t^f_<9TMMVZAOVeb#fb{X4_ z%#w2X%h-w~N6NN)b+KqZW3pwZR`un|qbeiJE_Ia7r)!Ox**R29XztQP^(1CHd2$s0 z6HIHuR}G#tKeFcPns4u&`Xx=kI{BlUJJ9&{#-`ZDh=|?0A>+@)h-%ZqiFf26a zTGwJX3W+C77#a`Sjfj-+&!ZqMUN}jU9gc>UV+3 z*3z?izbfXo-73V}@eHJ*vUna(OQm{FrF1#}EwA@*k|u- zrwQ;x>8J_r|iZ;bu9o?WohJ zTbm8?cJs1LR_2j927Ne4!6Q*8SYWvLx$2Rm$G2H#W#?y(3b5$E1 zR&HJ}b;#1NZt-~jmUGvWC*9vy-A_M%{w+7-uIuW*k$Kgt+jNj@+i8(B+)K+^gripX ze&MiCHoB~WDGe*vRH`~Qe^Fkz*{>nIvbw6ZR_l9Xw~?Fl9QB6%L%u;BJKxwnnFo@s zd9Jb?}ZAyQL=6I-W)#Zq_TG z5-4VK_b0a3BaqKkDzuor+(1SY@M~sG`!PV$-ajQAE7wVmltGFx-1Y`8pa- z!>?4bA=}88sf|PF?^^pTM59gmXcueptOORX?Ud)H-9LukaBq@m+r$CP+K;4LMfSs$ zyUmHmk-A@RS^|oha9B7<3`wpwr7-F`0@~78L|sb4&OIKgiCeP{dUYH7Y8+(uwK2wD zPK?P`s)G#N=;X|N82vwFkGvugo(u_Eu&!F_gOM+QE)Y6u)4_RuCcOa*lI4lVf;ZO_ zg)sdbL#g1)dU;buNJkRuaD$vnY*Z8{SKmIlYVE`By)f3NYpOb=VcNzY@p~VbH%v5l zw7h98cCfNnJwt}#XV&1}_G17Y`(JYR=d!@NH;F!!!Q}zv93kD`9j04_hFJu)5wR+n zsz;RLn)a`FwYVxm->+pTO2yY-lQa@VJ>6-NX$on@Zm*IiTNu4&>;ScqDt&O} zFHY;*8K1+6?X}8tNG~&T7DiTWKx!w%NA^YdaUsY-RNmm^;VLridQ*axnL=N&XW*`7 zkt}lx<9TKt!+a~264Sgo#DPsEZOrEthcpC(uYzG&!Jwb)+w#g~o6Vway4^}dD-eWl zumsQdbKldivng})a}B7DJY;uRuqM&ny>f0TId6BpXk9Uw7-7bLPa?21b{ql3opR=5 zUxD;ie-ZLAS-8mQLdq9u-3htgZa0{yl*JP1^+tzCyC(Nc{Dfyg?Ip8ELfq@18GP`P z7UkZpKW{FW_nLO6oAvD#6vXBhA-S#a56(cJDG9QhMgPOFJ1SLSS>VFPABpHNKSH^~ z5G|)o8p?^ojD=mWB7?UnGR>714R9)n!hK5RKhiCrj3 zZgb|lHJ$*ELk#+fVkPD1i*5#oc&ly<5XYcolf#r-zTc3x!PMc0#d_SU1OBL5Ur=D3 zPxd`zhoE7jms?W8-9Fp=DMyB=uSZ+8$En}NnveZcwE%UI#%P;%$_}Ec+#!n&eOvO! zCE11P(!?a`W^Jvnec$KQgvTy#q=lKqcT|@9JYJsfBtF1eAg&b<{TEaS9Zi) zlW5Ki$o4n^qJV~4u%8=!a~BaSHpkT4nrULdRrd%h;L39->cukbIZ~l}gkI^}I+^43;%Mu7Vm#$Wu##u;P4zeRwZo-ay1j05)yC^VWJ!=de9& zkysqzr4MZk40ktUugjy*TEw|btAeR@ysFJVE`if+4tY8lio<%NFDG|?#yoJ2o|Q-8 z!f~BLV}`n?;E`Tsbq~8u-=AaiiG;krv%I~_A4e%YOoz^SVBZM{&%L zd*Y6hC%>rjLdoUM;hK}r98B9YzcMFumv|32jI*i~LC@w2WJz|HyV2K`1mlXak?wlM z7n9K-8Ke?~2~h!4N)NS!W~~h9mGh0~yzkNBg!kuJQlEG{c;hnva1il`-jr{;t;A_W z2*u1O;pEHF-Qf!2BIM3ekIky12xeS$kL~b_O;Da-CfD*GLW8=FLPJ^{uf0riksKWw((q()uwFJfXki;mgf~JYsLs87|H0sF70CntZR?g!+mV?5P9t zC4NPr+geEMgDN=9_RV zaj7BOfc2A`h9C(%ot7;G>(+=2B3S5fkxZm$j5OYfQzxjvwP4|yWhRkY|3h=$_*k=sTu0ducX9B zG~C~+44Pl*F+S`JV_T-wmE^V1Bo?ruewI7@s@?} zZ}ps`%-yi%er$ZU?*!(ZDQa=$ra;=7ku0Ndlyda+cozepsFo7}GWhagZNhhY$>B9? zpfm<$pDICe&@XGc+W;ejL%HtE4U3cQE1;arQ8WGbZI6YlJ7DA2q-lHyV#uc_QwLxc z-P-QQY`Qw0d6n7vx9Y|HYX|QI>No^~W@k@#F6y}*dU(Fgd9!u31dmngyxPzd@b*n( z|Dnfna~_JNX4$`~%?FiOAsYeGLrR-#s%_;%BHp&fg^XyEg@|w~82JmRxix!YIJJeQ zUPq3$4@{J%+C1aoV@p{WiM5h4u^gxDsvOq*d~bJk#`lUO+aqC)F7 zf=a5H-g{?~L^ZDUA(Afb$290>P!&i zN*^jhmLm>B9PyhvdNV<|gny#mINovBH3I-r=joKt{uIy!YK@Jtsiqkfhcw1cd=#|S zN^1@Q2`@|GC^KQ!kkt|^Hlf8y<8G)e-epgzLq5v0FtubJh;g$S4Q7bJex_f|ux(`UV8M>jAPaazu3p4B|(uZSkF zCqzZE)PHm3T=M7?&WQY69*X}WF2>BSj@%Zio(CB25KBK}yMZjYnfxv|%!2D*?8)uv&(DWH+}{P(8j^(> zf5n4UIalsD@+VAgTXfdd0h?MfqNqDO4?Heh*(GAA`gbY5w$7B*?e92T&wYkV6y?qyGwwl5>3X z>!)RUcKuk$Q&NrbLs=#(6v4flYDZ!wglA>uJh>wj0mjxBZQzq_pq-p~e=qGfC`lQz zW!YgEr}pqx9p?*Q>P=bl?0tn&;3gIw9CC7dkvc1WU;IWDDzmA4t|b#iw!zc?*4#E`2*b@?SIYmxbIQ{x!!_QX@)C0QT_9X<)VYgdcV- z_!*NOm9mfbwh1+ouLw(>JtU|h$RZBC_D@k3%Sz;Gv2LlCyI=7SH`~_R17l`b7ad3V zLU?s?ESyad0)BiACN%^pehU3s(=%mtW`5l{@%!Dr-y9TX)@FjD)7hoh3~n#9zJi~u z4%lT~WED-nKA;uw56l1xDUYi!fwHj1Xd_!PCc}LEQ!oLNjSMJm#-3GVX!&HExbM{V?66|P$gpWMSW@Ac%06IwN=KirPBK=bc>*7ZO!T&?>+XK3vj1^! zTUoG5L+epU>rUzUZp($TMJuvb45p9_exAodPud(%;(41a|1%^PxDuK zM}%_YVNdZH41YHtmWy^MT$xh<0L$M|FFaRs5));v)Usz-D4^a1q2CyRHd~9kLyOS@ zK@`&3a`yr&W+36s6b%D%c1CRG2-3cwSccWXmH1W~`@2Mo^oNY&|cfaQE=RwlS zEuI|fB{c+uQIb4BJ*|bFJ-fQC?!#oPyb%>_)AH4aO-DnE8#oI0@7SlbLZhS^D$>%1NCK2obGXDO z74Sq(*EGtb;|4~G^ki_JrANKc6@nT!qKIuy?P812XdiooCMXjPC-z=zv;%U2yFVp8 zOhpY8CDWewnIG2<5bT{$Ck5fc$QJK{&^bfV4(41u!8EU^PYI4A44U2WIr7=VO2Qsc zbT9*bVDUSN=0yJ>1c@;h;W1f@LyE#@6+vrkBqBA;`;p0@2-gpiTzu-u_K6I z8hY3%%b;3iKp&T0c=jUr#gmz}?`O3w6%DRJ$}2Ha_LPj9WXMytn*27GtLeeP^sc=4 zBlgsdP!$m-I#hs`(L!vRilc!dH>>kP4Rt)-x7u#}Ynsu48?rmTPv7z$)?=ry||C^rl82y0CM;d>G}CYeT`tMsx!B#ez%A=EXG(%kBQ> z(>sA9ACsu`z5t2k`xg)|@}d7W3qe41z{e9AlrHNVu$!ewgrkNh8&sCAG9=wn*1gEg z75w>-$0Knou^P-t=p{}DNq*ltP=@`Z@I&6m*+fKcZ`_x-ctX5`zGFo`tOR4($;%s5 zuzYkS(e|#-blHcnTI}z9tzbO@3!vy&v@ASpr}>&f9R_v0djnYO72a8 z6EB8}?GY`-HG)z3YE9ajk+-#x6%xocKe_IwRt2!D`$!@1<+9BX~hvXJtV#?Z=R4a&lc3B9>{f4lB<8^rkb8p`}N zcYYPYGh1>}H^n$S+pj-^d~7dfLU?5NgN5;b?+gXmMS9qik zel{b&ULGg&?h7$i!BBaF?0*G1x+GK*ULO&75{3Dk;%7xg^o|e~0bJBjb4RbrrLqt( ztL1AQQ#0BdwIh{GKCj`kDWJa%*zYIBSKE15NgB?VuIe(IidvSD%XUYbU#gs-ukwyi zV_V@YJrn;ALR#AtDP{#;=DLp?gO9wgYU4_}Upe#!J_vS@XX=`PtL@1|9;XyYG7c_j zv`wK*nKk2{@Ir@(5zCT9yO(J*vYwK^e$U93iRzi7EYjf)R*md`LCVdE5X7$P<92%( z(@A>t9zWg+VceFiqBFRC_36~Y5cbk-m7aD1Q-RRkJ*v`$P!+B4&due0!;$E{wPBgi zwEb-4qFm|)@4H*jN_ke1vD}f*&EcP#vWVCF{wib)fNY!p%xQ7A9NHI=#u1eIFr;v~ z4SupV++&j*E|DBHN?3OeD@U6s^B`mBUdvpl9i&=L75V1S!)cp zmOH{cfpzs6WxxwQ-WFbe?>a2i&L1q*8lT~unL*E5yf@7lB*bs$WS8#CK8+L?m1JJM+Ny)z>U#@O%%Xsi8p)aT2hk$wCM=Qh$fiu z_hd0E=nXNfJo7kAt*g{6ja{99Uhar7L&=tBb)A%adMd*|c#+tZzL2+AimJ{}^E>au zmf#tvG4uHuMha&Nzkh#gemFx5b7cb3Fm~{=qk{6{jX!XL3Fr7NTZDWMtbCcsSPk~P zajtA*{pfuah$6Sk-#F%AlxD52C;kzZ?DW|JZxasHVCuTr{YNhNc3d zR0|5C6b0!WX(Aw?fK(Br_ui5q3Q>?Eyn>Vv=_n=i4iOMh>Ai#ydT0qHw2%gA%DUGR zILuGaJhK77!*A`KVpBy!_91sH{AZh_k+Ih8$f1Hy@Xnk!qv^ge4*AJcPKihZ6gD>M z)&}OesHJw6|I%4$a6HsU9{c5mXSb*an8D!ovO|d5zSn}I|Mh0s^sv#F%y$b3tJ}4q z=Cr-$`bVFOEhL4<3qhZ&-U{;gtRi+gxQ@Yg``}f9XKsKmV^3PZlUwqa#=Y|pPn%5P zr+iOm|9#aw<1!Y%m*-$w+{mlBY8K68Bb`>xh(;m`D9O7YF-JQaG~1gBgHk)EiPn`XO=IfndY3=*hTOjcq2-+qPluc>kd1HJ z+FR=6AY9o_F;!1mItdYkz^4FKwA<=E*=j*9CZXJefu6_Ty?!W-vVNUoX$Z~rI2(6Pa<}^4o-nB5 zt#-Er@6El7@vf)Dj}Ay)HyYbyl&WF8=CP%rVSvS5-W>8{E)3de@KX$?0}AXa)r_Z2 z_b3SAVAk|8r|~QxDIM^<0nLleh7K^g0z0Cn1T#BWO!<8*ttYv`5-OcIm$Q6eKOdJa zur{U)UF{ki+mdya)950?wX^LWZIJSiqN!*K+33LW$VM5qF=Zq(g=l0^3aQ9&o<`j$ zYZo@3-L&Iaa+_fBxH7d`nfpm|e{738@L2KaW$+MLb|v`H>?hboIPRj^nVyD(3b*)d zf1gvJ04b$v5a>f;5Kd^uP-gRp+jrK>j!^g%bxn1DpK)*1=kbG${uRZ>8?8~ypCs+g zi~1k!4?UpHG?VrADZi(!B6)Aa zlT)Be^7WT*A7BN98<5C)&go}ga2)JZt;f4I2`;J^p1!bVh6}iwWS~*12Lo|x^z=&5 z@vpOEe#U|yiIt&WVeVH=dzxoCxFOBDe4uusn&0IA?| zc~ADXmF9X(rw2E^LWAq;3OhRCT&Fb94l=FLml>q^Y@qx6=#g+RPVKyh4E6k27!=0db=&O?89j!=lBq zD-R!-A>UiUY+gvFw_V~08w_Ax4mRNDKL-c-+So(}aqRiim!G=OtO=2+oxyRM^b8X| zYpedbjO=P9_lp#Mds>38@Iy+zd9$`~Iz7cSvMDHDq?&n>{X39=H~wXm5+x^FwGJ-x zGxj#b=mQ+gqXUg#dPCdm-p_fZxz`@i%gUqQWulz2I)1+2Yds6YWuMleq|&e~R`}8a zlX%x=p4~Z=^8WjpB#*;D#acztwiy>_^{m%x=eX<5nv=m6tMNAHJ+|abl6a&;SkaIW zaZ4ro*WRZrBkyRer~Ou&ZLWEP)Eejy7s z7F<7|R01*W^fHpeQPkraC~85y{NG?_*DP(UM)5ZDS$h8HcqJa#Rkw4WllhLZ$70j6 zkB}rn!(I3H#6X8hLY9Bl#_0x|B7a+DiXR=2{?|C6$ZKl=kp8z5T=&rZd{r?0inHHSe+D?<(Yw{Y(K#dtN+aU zT$ygV_>O&bPFvkdOR$qcf2^Uk@dZ9Jl&G{B&u z?;!>jst55%vR|7(U5J6x~8Pt|c=TdsRv5TnfQ#Te64`vCN49#PX8c2|BE;<=AWyxI!m9qxVJ}RRa>FQFE<$% zH&wLcpz9S+M26vAjTW)A3SJiH+8QRX*041t(%Y# zVteb}^01V*p2)NI;8X{f^x9apo9^5qTFLH7ku~3QWadDWwlaFL?A=+o-B!^wLM( zn%yq88YrQR!hd!JB5Qpcf1Y=2ll|vXfa?fcuVPI=j+!=|6ty#aOQ*)nq>b) zuFJ*gLIQWVp|alox-Z*nR4)q;y5TPvVU7;5@5M`GLLe4;ABR!YS_EAHv7!obam^P} zGEk1ny84DN$c$9YAW4^r0szH(o4^xp*Oy<%o_X)vF9B=LzGJI-C zNW2`v!B>xclKA?Z9BBC=m$~=qk`IOg0!2sutAh8To_<2JP4)^9ytq})C!L=4(g~!)> z!s3Azz%f~9X%XKHkpHmW^uV~*q%9q>KAnfRuM-C5@5ljeAqZ6UupY(Eu50=V;2gf! zS)}u0pnt>?Tky))2{xUBkk;Bi0LO?8pkYQ%k(yg$i* zRv#QcMg9a#3rD>Bk91(!ywJ5lcOQPz)su_IZtDF<83=g&|I5Dt%k;lw5l`rWC&bbJ zj{j3W?|ZtFsa5y=U2hE>bq4IFZq1x1i-9WlO?oW7bYJ=KH?#f-I002bD!M?-v|lg( zfseF~*X$`!RsaZgAkd5}uH;LlveicjEK&=0w9a?>W$4BE6C&EbCzEG3x5*VV?mXm$U)jw0jwZN`Sb$N(nw3R~kCs*BpFvn^eilFR7C0*D-Hl>bj;3 zJBrD$0X=G_P9~CPi$->l{IFuT%pkvh5O$`dsH@d+f|`$3_C5|L4?1cxl|83!Jd1S_ znwN-YP8;g%Vp1J_-Ey$o$b6i+@e7m&&@%(R(l0Fiiqh`zr@M-Ktp{t5bbX4&)fpuvI2dp5^&9;C)T?0NjgCFv(XZ5#vcJ>4}rdd-u4 zLzjRmh`$IVg=@xE7`kSemo-^|;_^P4_0e4Q$)Z^mY%jp!O#QKvj!X0LENPXl*mqo$ z`UsuwociYV;76Fb$PFyxaoFu~eNaD>qx-*`G+Z?Q6}3S50sHh4v84D|F-x!^b;;gu zA}gRAvL0+7W^aed2>yq3E_~?(YdRTV6)PUR=t!Hg>lcj%+5VniO`!AG_JAloz_&Sl z3*;1aeH~Qwti+^ea4<0yiDZR)+V%3Y12y3t-So8+=8!4EpV^A5{Ns~0HQ3iz_=Qr- zXLf5tuG+g~)$AR+)bZVhlZ9shVBe#l#9A46I%#BY?L2=}Ilo#IF4dx^0Sl8d_e2KO-ty6?R7sbn_3x72VZPR+@0!J zJ^Z|-tKnV$QLj+^>VHEpjN zwF4Zuec+gZX*RvDF;$bSVR9x_25!)}wBR-K`FY(r>-z-H9D%`7Qr)CGcL_JIbg&0^ zAljvhrMlGbo@?rK15gx%bxy59DwAhtUt$GtW4le9r6RS>xQ?Q(biNe(w5wQC%ySb|u)E_^mE5mUlb59Xn?`G!>h?0V1t;n5r?+U%G-UT&9q zT0kV}vwiAxFrJrGWOMX4H!JSmsa#@q*>aI*3wPA15&(FiEbq0UJW>l_?lvBDtrOQS z1kncl+Qr*!&PH-KIA5i$zhzyR^v>ST_a6{aA#FI0satn_6@S#1l3b%_ zPmP_yO%m=R%#<%VDsSx@@9*w#U$VOe1Z{y$6iTjZT1aL$y zbqo$Z(+8^^thrVKLmBv3KNLHok&QZTB|f9Sr`X4M{_V9?-7Oi83%}S(E9x+s>wAW( z$NLf4WB9j(q?*kU5h@{PH`CNxt8eXcjuP+`OnS#hnW|lS#S!7pABt9p<5e`BIx^^t zva$fcg(v|qTGM}`#t!Yyw~w=coxJ)O=1IS zhdkiXcz_$Ax+g`2dbm3hyEhLTYHXb{VtDu_dn#SJJH-a+!hKkkgC7uCAvs;8 zO~i|(r%X4yZhw6_%L;>5y9W*bdA+O|0CBPzIMk^?EmT9RJaZ=u*4G-XwB+A3#6gb@ z9ADA1i&{-kv~XXTp;!AnD;zlHk(AM}Ighg(4^EsDTl_OIoksTs4C>z5_-2cPKUGJs zTH4|j_GGOd5=ztIasOQ6SgGcjl=6;wQL4`)UsAVWj6y73xqFG@Jo*41$mW?Ia!m#) z{4`D-UyJHEN3@jN+qI5{@^F5LU~G(S$a4LGweh3g#=;ZJkg~WB6Q9=xD^`4E;vvsB z?yo>ml-2tPIjA8{)Q0xUa?ZVp7xRnnW{5js2zK$NUr&;JJeMy&-TSv+uBwa&?`(I8 zd2*HNtB(*SDg~Rq*=g@rVzorMl^}C$NNvSjYycMz&lAa7;~d+gvLp5{Oy!O@?NBR5@D?Zja!-BgvKV;vOqzL}3vfjC3s#|q*S?ls!l~z|FR24I_Gasv zEAeI{Q*6{2WtCgHd*P|9v7kQ!H)gXM(<;?y9>f0H|Mj=%^ESt? z_K0d6rrh+mgx6t0W^p%LeVk;jH{Mfr6Biiq#bvUpg&WP?Rovy9-?R}U&JQ|OyZb#S zylvepM=5i7q%Zz%;+@4YM6%Iy0FFbDt^6nU)xx1xV&Ejr)5_}nsA|oAj%~E1!NV&z zzF&>h0&|5;N^zCq%a)eRQ7lLaH#Y|`gF@6Zg_-#L}j7qV*(4trqD z@}u5U9=I~9dsb)ctuM9q#aeh7S;|mI(BPlaHv*j~Ub&F~KL5v4eHkYBhmELw!ntGz z9R1E@JVR$0#Y#oJ$60o2UZZovEXnpb!I}>@Q030zc;#@rtT#oZtw<9oW=oj7TGk%+!<7J_)Too= zx>4YHcsS`4TciddD$m%^I$nOw@1Mdllr4_7@JT1>of@~Yf}CP`R~XHVWZTiQGeg_F zA5zSHp^$AA4cRgKexb-PDA#}MGn}=WDUZ7NR6Eg+x2|`cpV`w}k@KC2W9?UxvrATk z(t7&n2L3H%p^{aS@ABg{UEllUHBGu1)3;@t>F6z`q<^BiN$oiSln;`VN?^Ir7gt27 zi|hHXK1GWfz}1nv4$)d&qRAn_*{Pt+2=x411ZzV0wHJJ zdrPI?XCf{xpeqMG_Vgw6LjLQIp**maW3Ka_-pO6q6y$lnb_L$QYLvDeHpj$)wyp9iUtDddaQQx`aaeuTyCEwva=UMIQvOLUp15lp;|+c zT;M+JhLa5?9pJ8*rNx-j4tz^s8|3&_ayKz%%u@u8m!V)=iG2oVD3NjL`i%j;w=A_- zrm-|J=n!BXIHL;Bp|CS2J~>7bRhhUv|4Ne+cfV#3z@c3ReXwSp{k=y&mA z8jW+q8r!C5?T$$osr( zzs9)|@LmHl^wz$m%qXNBUO}pj1@ng>ZJaKyf2h&R2^LyauVoKBSA59B&b?s~NxQev zxiS1bFyberx`$P{Y6C*TJ*jHWZW!>sX;JNNZOvUFHkCqq8CZ8uuH{!~ZcOfgrGv17 zE2c_5-U+h|H3*`2~t&Q95k8XyB>P@nKmi(?LLAzT)Iw{|c>&S&dH z@$*fP%rp8jynq5g;_suXVw`r6txgWe%s2cfi*~W>Ep}m{FBJq8w!cI&}^Ebi7FT9rz*pxvi5p}~jsU(c6}j)1)of#DyZ;V_Jhlh4!}Q;p}pPw|VD`JG-Zv4;`J-$SY;3 z$GklOhQIQTwDEH=T7kqGtYE{*0C9D7xxZnuz@PZUmk4NgsxVv%Pl(826V(5b%YLJt z#6W&?sb2>yL$&Q2)<|+#t}ST3{&OyX?xA@UDnr3OanO~5LM2=)jq1Ai&d9^jmf{`&m7EYelw^=y`v^C+m-!nSf|B{+%Y*?Gh}otN8;E z^`4S@c94*%BzS=5pNvPCR{jcc(MVTwY3G`O7*j_n5%tLn+LV-r6lz?)^(<%qt*)nv z!cjn=!k@*B6a5lVgHbV^U(25>8MnpB1$TG~PRyoF z5fi+$3*J#;(0#X3@e7~Fvg7qarxdEBJU>9hQ18@NJ2q;b09tbkf0OpSYY&RAY5Th6 zW_=wLDMU7!^-R?lkjy8ES485ME|d=jgk&Gc4Fu;iicHxF66hI|d+)EZSL16^-uf;{ z+*b;umOhOAR44A4R|1e-1-G_x?f2w0ExdA!u>Qi}JTXZVv$f z$aa6=xAx(dCo7N4g|j-VN|%moeDAJo3}A+D<@81qww0$5vD6HyD`o6w3JX0OAB&!u z4fkNJUN*VPEfk3=A|}>P`e1@n%mHRW2u};3#`AcZBr$MTvhtqR(bl};)oZWMA1wS_ zek!2yodH=A6F4a?+N}}88oyxxjm*`>KIKvq^I&-PY2oVRyLg*5nvR6!$0a+%EPTk* zAA=m<(4^U&_8^23pU4%=OskB}}wBrNCM@a)3OOkE~>u*&Uy#cF)1D`qdS**`O{D#+}*d@<8eWm^8 zKtO+(1n~j_JZ-@o&mF)NisP?{dJS&PJWhXXZ!IVwU9-pAlWD;ZlRUa`NDGvN$&Bm& z_5sd(iDvkjYXePMC7J37tx*D|_IyIWA=J?Nx^ z^f6}bo42kml?ib%w}1W&)iGeZw&Lx`CsDr{<2vTaA))fOQumP+t0SyotFrXA=(fUj zu2Y@N;nt6@Tq7fd!O!PPSvfRGQ&P}6Y0jpSEf%SBywhWir1pho_bD&2_#5~SHz!); zzgmXLwmHN@@0anQMcVS#*E)}vnoEf$^(z&rlO)$dDHUw%w_#*EFx=}nnY2j4{b|;^c z!J$+7QC>2?&iGn2A3T=40R4ViU9DrW?@#|q_*YZB(&`y2{|%!+v&%1HX6@*C11^zQ ze%4O~RKAd_T^gWY;Ur*sZZ5GV?Y-2ueQ^<9M(`)rdj$Y;@wUy=spr1dZhoU*_kwOF zCG)HGba!9L4J2>BmQY4Q+}8Ke$49AAf^J#}b{pB?-wUdwtBNpZxxJ%;CfTIzWu?US z_JO+-qP_lmt18T@`QfT4-|Mf<78%%IG`ihWezHa%>_-z=47{^gjdFm9VO@qsT4dRi zk8F`Of8As*l6=g-O*`_a6(9*6 zm}2n$ul+4|e-v@8qq^!nxa|u_a~7@MIqviDz88({znofBIk(Y_3nPnuKN+83BIjxQ zQB6^+=~KE+Em(&!wa1a~J%f3FSlDKO&a1oup+-SaCP2&>8Vr zSu(KltviJaV4gFfi5!ctAYY{vkLx{l3uz^braQas*`z?>@+cKo$KG@XL6m!9KIUw? z)xO-HZF3ZKe1dUF?pfidaux<{s9CNWRe$4w&*}3$DY^ZH#+Vtkvgsr8TO;yt>66yj zD78VK@%?YM>n%H}d&qX0{nl#Q+JeFZVoiDL8IrdjgY>;YDI-${!P*`f-bc%7#41we#tuL*u$`r%cKBumYN5hQG73Xw1m3h#5vkq4hj!Cmk`eD;U|f_oo;0JHH1fTmR(PuS%A3{IG7GZwDO>Or(OfPo_Mpz`? zK(!UYiTuA`zpwVl@mc@?B>|ROk{9c1P|9u=Vh8J~v%4UjDyY74UE|8pb0Nq|0r&nG zZF?o&oW%J$FP87<*Q zPUouFR8=0-qwMt+Wy+L#k34Bb7Wt-y2fF~QHhKAn`?amCmk1$AqfbjeC!0*TrbSR7 z9rc!@-O34N()KwK1C+aOBg*m|Bkisp987xKVSE*@6wS>up`w=5=8ADbSGNlvX_TSS z{vyYv(+GXcA$h}O@^XB&dzB&!J*FKeWkWs0W-?jRr6?MkH36b8mO}I4l7@ zx*hMtV0JaOttJ}NK#D5LcHl$xOMAg5(Zx3oOwkW6R)dHv+J-dxRIn*IH4-%3hH){V)ri16%Bgh{DFW1%spOi}Iy1Xz9aF!-JOTSPA_ z2}%8(KyE7tNG)07j|ryNPK#lf*y3dd)tsdRf{c?eaKRR;B)4v2?R%H5$IsYldqf(C zW#>a=j=xrdxglkn3rOhoLxgECJtI?fGAqocSqAO2?FG%>Z#Yd=)5|oC$QL;bt5!{k z1#cO^TyhCbQq9_HTV1#>F;qmfq=Djnp0oVyj;q5L7o+I8N{bD}b9YyWHoHYRL(Kf& z<3s_H8EM7;dDv@=8xqiM?Y#-+;#ewoujzv^wRth$8P~Bnev6ggCf$hfSfR>#|D>=4 zK16Per=>7VC>~>j0r8_BmuEhL|261iq<+P>`lOg`%t);F-bp^e&+qv7Z#zRzL2Vhg zi;ql5)8fOb39>ws%|CDnO6SL5(yzll&ZW!kKO}So)Asg#6&oPzHzw3R$w>qBV!#^= z9(-pg{2NU*-w6uY^UYZr;_p>b)-!}`^=)WEM?W{8geHDZg4j=ed;glz;t-Z&yDo{3 zSX*H-9jDNB0O?dGAQEx=DR*o*(p$7^^f~2?Zy z_?g&=-+czhlP$xuhLdsp%rSVml%a+o*Op4t1tRZ+;Z>i|fFB>S8V+NfWUajYFF3BxE* z>dS`_BF44bsYT4Yw)9p8Ko6mNvjbD7X8A&pN~Tk!v1-g+Td&Fy$$v7Bn{x;170x42 zq}8(CZ3W}n7)WLxNj^;+%>k0s0`#_g=YKK#&itTD>Zo=dyP;eg+S1zUWcb>_;a6_n zrVuLY;3?0liV*{xw?Q<%G)|H#noz)}eL1sXhi`CmEb%6KD=!0RsQGnqX z2wrpXw0ToRCu&`_6_=~tR1a&qm75)KtMnBhAUf`ffI0-jbkRsP1Hk&d#+JMW2+ zzBwP%RiLx`obBOr4$j&Ilygdw_hWxM%Zja`3_@ttaMSPbvrxw6qlA&$&+Yt3;@C zoic#wOYB(j@jesE7i1+!;p!X8H<}N6c}Y9vklQiJRPxAI6&ox5%R~}-G>q>iY+QkR zA=iKJuvn+dhI94f-%HmJ5NQiIQ}*!Z{QloWiNj;*hzrZ9^AtU!KAR9vOqAOcm{#`i z1w3YeBae1L(3>Cs-G&)4dq7)>xFd)5LcE63mEcUR*9b)0(8UH3pGXofy@qh(v~}1m z^G6CKr^8v;(b0%2|fwmWO3jrqlV-ueLZ*oy`T zX7Bi>jfu;!u2G9w1bbM=M2AOYF}#V-Y)5@@X{_>INrMXhFM3hnm|mLSykq2%^FEH0 z5>2@}wJdL*?CUBPrgb`8f6nL%$kxkuwv8U>S0pr6ZrAyWUNt8t=M!=$;G$J?$jD2_ zkT94Zzft%D3*BSgRJlifm00Npa(lafw6%n;(>#ii`BSo#{kfDyIXa_E$&Xlx&KyK4 z?)<5~T*RY3n>1^rZx@oG*mPViHczcb`hjIhQN&PyF88b5iWF#rMAYx$&ugWo8K55` zA6wF^zm#WB6{%K zE_3r+Vi6R~brI}_tj@vpKlL#yYS+-G0|Xhxh%fbpK~5h)=a+8)$ykpdOf(q+-HNgK z^7vM8>krYKyB^X>%H7yC6xt)v2%mU-I4(PEJKrZlIzB_cC&{h=+3g9u2}|*Ne_v5+ z60{cCH4!C18jC#G5d2fI;{)#skY#%phE!s2_AyyI>_Qn-Tj9VGBfJIuI#o6)k16_rU=E!h@zHx%O>2+}Aq4bp`+1ay)nd_wOQ99%S9j z8%I<%;2fJ@58NjMoB-!g%hj;s4iqjnj7Wa-%(5U}OZhH)Snm0V7Q@ZHnXR+3O@mgZTK^~IySwLrE&f1w>iN4UjL(?G4pQ_afuh*QO!I2~w{ zOEBrmgI*g>FmI;8mBRY2?0sq3pH8*GAxo`COV?3Fdb}FP>zyg>;o5H!8_??nSI&ZV z^jHr9vu$Kl~vJXlWCzsRL~tgB&l5Nk>KQ2)253B7ay|opHY1Yx2gq2)td; z)Rwpu^LyyLXJ+K#qnSU}>X^kC*z`eIierJMv^V3|ka*$ShwY2@2>ja5qXA$4v|#kJ zug9ykOWZWflw!22Gr9A1T2`W8F`hn-g!a& z+!vcF0JzY*>fT z8cDa$moQr06IYsYjDEbTS-y<*{RgxWNQJ zoh2iqpvA!Jcf@1ZUQKLjACIkN%6PZEbTk_4aooVMksIr*c(+HVUYTX=!`+O7*d5=O z6*P^FiidD6%^jV}nR(ph+!((T;!*c?(O~d=uK#RcU7_8r-iH1Ph5e;iZNX<^fpu=2 z%&+$y`J0q8D*qOQYtv=cylr-#7Lb;?l{o$-*2?u8F^60W)=7<^D68HAveoH-atXF?#{DNC82~4 zk%dT8TLw(OXcEpjIY21W18;M`vF(m zcz)#oquKEnNGD^|HJ0(C5sDmPeFvC)t+yO7{iIxqWXRiy2uGNh+>^>)E%b3gpdF-z zjO<8O<2ja)V1Y<)$$wgZu%d7{Rf`|p3I_WOg`xXKiFWFEwoqI_ufVbO zu%1+4ksu{*sWb*amaojQf~34&+%Idtqi{IhsdPC?HCtr~M(aDidRAU5j^}>p-ueq< zk@j&uOP1?c>BIHte{)BTl;4o4y1fRD>5_KF~=e28DRD9z?DIFxJcgS$89HqaMMQ^EQ&vnEi%g==RGpPbT;q!>NR) zbn~WsN>$Ft)#t*6$|P9a7yPk<9j;$&M>rRXUyLl%)iEu^oXVl;v8mmC=fx-`Tn9ah zX?%;;<h&Vc@cIS`-F&w~?-|qB zp^54f)ekU@vSigYb>92L07E+c$f`>HYlTlWOkFXrM;4Zx@gJ6N)6T3zRqwmrH>{ej z)+bQ8jL;P!_rqus)&~U;W6TnKHo-sUlUi|2*L>@dO~8`tpzk+v$Nl&MjHN27leRgs z)R}>E&xb68e0;SzbRIS3S<4eFk|HS&r4qz;XP1-PN$=$-fX7OD-|IM;S=swQ@yH-B zvNKCQWt9JD(XtHEwT8kHWkeGT`1s>8JlB=n`_fAQdU?$Z{Wf7xq!Z=8RNlH-?x8&g z1zLtww`KLGD z#OL%qWU_^=aT+=45#7^d*?-toIv`DL4^QRxk{>@!FtxSjRT1UR{F%48ykfT{@41r( z+x~4o-m!32JmB1ea>Nr~y(R5I^qTC>ap&8aUl3Lg&O}ypf{E{MJ^@#L5S&NWw2emHh+SeNy{ND?~Fu8HUBeK8%Gv z`?@cG9>1_kFYM7RQtZ)CVPbmtJ$xWeWc-~*p6>1Y94^O0_|BV%tJ(cOS%P1|Cx?7` zrGyHXal1U`XF2?*PN6Z8xv)NSfpe%~+SGXd^|`h-|BvUhp9gVy8XLP->A95&3I}{F zVU8IpF3X?mO&OgovM~!x+5IP4S@FWP58#ZR+bc>(FO}L2T@ab9r-m8zYx{rIqyho; zRMGsA`+R`j%HIu>$eBZ`-4>i}@Myl?DXKNOm}t^z5M+6F7Vs3Xr1yXVP4CTY zLeVRiadK_ZrFvwe`y5ZFMivP$6Oew9e>w@%3J}+D(49OFS*VqbnYvw~ zod1?kw_!q3Zo1ZN(y42;#zJ@GsTpe50<{&22DY8*Ir#4T@mj977zV~h9X)=qlWxC4 zd^+=Q?$p-lW=XQf>%QlC+{CT7%k zyrgDyE1xJ?dsh$PD-W@F*WS%@ClWADcOe~Tr3Hu(PWgM6o*i;Nmp*$Cva!Y z%lsnSqoP7m{@8(7>o#pb+JUVEC6LCk{fIuzf!>S5Wmt9g@;fe$(a=u~?Nu#Qf8!07 zu8k|lOX$>d@f@}19I7X!7m!!R(hWn1E>kce3i0ic9;?7jqFR72p z(x~)}1|q-Z_4;Zd00sM62w-!fbA1f<6A1-uD7(hcN7(ypY-~%W=)>pZ&xeGy2Ym}k zTD1y7=LXk~0ZQ39{7V;hfe)jR*2ccl_3eu*5J?Ldpps^3L~Ia!K+iY&)^6hVQGj#Z zgZ=vXZsppV0?; zNj)nojy-jkUIv`EV>$MAaV#Np)|IJ4nX`~h=S`Zmy6Pa1b9%TAt*GKt;%_QNE3n3$ z#RL)d`{0`Mtc_WVwd!XBj2a>RI24PcFuRSxli+3Y|Q&GA;I847jdvz((+sDo*Hu=^@~*_*QW#n+47 z-gH{uN@w6mt8QxeYidfg`6w&+!z_YoxuKr*9z3@i;`}_{H5j#ipjy0qaPP6dNbO#{rn>dFmnFRq-R<1DYoAVe$m&la zHx4()w#*nBkn2h%q@S!dMZeMl39Xbn{<$L}ot`&|`*aY8D)~5b2yLMUSzGkEKJv3( zydEh;PRrG*id)|RW9%zP#_3KGc=kizNBMV=o%Epm8unHA_n2=7Zu(Amozl_h-VhP3qYodoM3>Cd+M>>M$-P z1C;Aqf##Zqj0!(FTgUILbfynE5xp!NM;!Muz-@{8{(+LARvduADSAXwv|>&E6XjCV zo|m*n=bwgojC~n!MV(pWi~q7BbNX`c?5-)mUm-n(s|m=Dh3DBiJv)aLHRogIWIHRa z>TO9+$C3Z+DK@&uh4B1WnqBK<{2H4$YgN-+UCKyp=ift128(9B{)~u6ymxEr3L(!) zqC(44A1E%hWhC!1_wXGqUado_;jERaDj!lA%w`S90pn_SxPqD97W7`1?@kLlVhxaH zl1wba#ZL9vmcS7KO0)9jTPuMqfd@a(&}6O)Wmb|5&0a59!t>o;R`SwSs(mk6Wm5Ar zcFM6x%UX#(Xeb#@$cxd}ilR0e(PXFu&#!$ay${|S;R-6n*kSQL%* znXj+h5*}U&SSlw>+;%E$K~|sw(znE4zQ%xFxOI3-*4HldlvjSaqhe$`#T>uz^JwC& zCpq=qv(w8F2 zyCMZZ6zbER_fHvOm_Gqb>?mKKJcPjtc|#Na!PK-?j`a;f#pqs}t2^(6V?B zVux&lam++ZHq!zRNoMu9u*0WRaxJ^c5#}18t)^_WKQPql>zM~|!_U^ri)@hACoTB_ z0=LHA4uEvxTbeIY!w8L^H;;AH1UPy*_?oHaex)`&ji=Yy-gjfz-S9@a!Ak-@}MIcjbFjqqKBfWYm zNyDjDOIZ|MJ^rE@ZF*mS5i%A7j{hQf0T84plmb5F_6emB_M1A}hx^bC_uD(fZsuQD z{yCl?tMH5k-k5G)jMKtudUOK1i33US`9N`?_~n|czli|^GGSR)(?knQ>(Z8vR5_tK zXDzcZz%nhk^Vifah&L}z;Q&X(xgj0|qVGQ`;PKl0b5kqs3hV9s5t9 zTgr(#B&eTWQgHC@5NRnp>U+A)$8PUj{hV?mG$06mk}Gh63Z9GK2?TC*+!`-B+7|pt z)TOWiXgD5lee%Wq`dXZV-*LDs#~CdHsQvq1H;_u!Nn5S|Ij*&x)wXN{|M$=Tqb>$` z=h6Q@FXBICMgLB`2qZH8JKq1dojLG^%9;N=g*5-G^al9E{|CMzyoOerA{HeL!Y;1w zHqMrl0fz6BX(-7C3-&x&#InyPY~0z68$c}@Q#b*TfnQih)5eauro#z8q32Gf0DG7b zASN8cLiaDcOVMhLpqSOP69H^N{InfuG`avq^DpBc6Ezt<`Y(aOB~X8}079 z+l_0bjf3Bx7j0h-2;C>@b;j|s3EU^l^+b7=%XBG2WTySm5ZUY{#EC1DqvKLzY^gO5Q#;KICmz~JBQYAln{WWPP-+? z{}ZivC}ydjzTs|wQbY^TN%w8dcaoYx=kpEEXqWHq?k;9sdXjQ3=2cIQQ`VK|UPF23 zK82@?jouC=w=51BL7tVIJE=s>$BZ&86(IWV?R7k?`vYtk%cR1PAiOlhT zTg%?w7`P`H;?pyz@TlS9-u}mh=HHDHPG%(-Q%&y#7af*A>;&)~zF=azsRanj)Zr(h*QV1Of;| zlwJ}*0t7_qMNTM&7V1F*0hNQGKEziYg14+|fz>W1bT}1zfqHBBr7V1%(LU<;{rjEWZW2?3^O?Se(a#mVyQAZ= z4hDTN^jBm@94w~n&+GCDk}>ksDsY_3VUX-DpvC`a&*F1{!pjo4vP9 z+Vw%aIo!P9omPEDl7I_1oM3m`U)b8{yi1xX8NEBNOY=`j#A6Nv8UM*X?j zw^o&sLmWnU+rEmm>~$ulj%@K7h*iSl)*NR;181EO-Cc<lB4$svO z-;A{bBMJ_B4R%7O!xXyjhl~c&GUED?L)%|<8| zt4m>w5Y?a%#ZAP!bc)Ns=-QXajc=5MKL$qkmt*>Z48*(QsZ7zK!SAoh-*=O+rPbg% zmySRwg$eK&e_zC6Xmg0@-QajTXYZW^7SkTejlQDrt z55Mzuax<(Ko_23mF9FUO0-7;<3N<>sx?;fLH!*g#`T0&X`}ZYJVRv_yHL zyc=`9WG~29rldlg{e|j?4W0Ha0Won?lhaCnMY67|1ov?<{h)rv-uBHo3vwpvMXhT!vl)!sGBL+6JGxLIm8KY6WB5Q8uHE2#N ziclWm8M1c0SWJt>L0NSezVr5TE=NL;q)e-aDHAf3pTMO1Zq%SOZwrm1~?c09uqw3I% zuop2}n~_<|O+@88EF*YhJI3!aKpr#TQU}}?f5>t>3jF2&{A%#3F??*Xepo7GW)k?PYC zGb9=rTh@51*(lrm-1Rm7GhEs{w+x{@>Q&Be_P2Nj#^^f(sNnj?;HCupWrC8t>g)0S z1I@2R4-Pg~G}=EUSihM?AN2doATf_zO?(i$lSRmUP~n-k@muzt-xjJ+Qd6IP? z?e$;3C2%S<;_cs5ir79?_9Kg!VjS>;+R+kR#kzy$(2dW0AN8vi zRwW407)*wWTuTcd=H(+YVdY-5jhPTMv_8lo1-%Br^K|PJ zpKD$;hc@|DBSsKtuApfAYn0nq#3DV?Zvd+KqMjrOVl)4AjxK|wp{qnd`I-ly)B}p$z+YrRODc#pP5E=ZLQ=oJRWqp;jzP=EbiRU*SPgO)0~C{xPmc`3Eps1he+{X_r_^?su(KJXJ~!V~$Kv z0W;RngMnM++u-{G^39qJ%_O;D@|!k=#Kmo~4>CcdqczXo}5lp$Z?T z6oJ`{CcvwdoVs>4&sXk^Cr)PHBB_RriQ%pe4i_iHxG$OKRz3Cp%e96drf2qw_Yz!zl;e(h zpX-X|U+NKnmHJV6<>}^!gQ{Q%zJG4C$DfyMDy=GD)k8$oU*z;kz@2yBO+OFwR8qBP z)m{s#oKx$~${|=Y3dPmXIvLfG(qgZ!gC2zfPTU?X{QiC(;KR*3OGjp~RV^H|&qfy4 z7Lo!LHA!qSZL^AJN2ZX+Kw6@JUl+Ad^{Q^EdZcfuW@3C}Rg4gz4Hans68RRm3r9K) zxg&;0d`KDHvXN!d9D}JS%!eGanC=J1OUm}gYGuAW1&Abvt5bNX|TW{pm)hqQg1=wPmf=BO}nZ%Pbz+1-t+b;eBQ2iWBYiF|(T78D#z2g!VHEG;!* z*4`sj5dU>iiSR^HH_JC`CB%Ns0{*=Jrq4RU{#5+v9-`8gIR6?MZ3ACP4KiEW%-}0N z9xl!M(WKH(&DVJyREQrPDs%S^xilSgROy!$?I+ZBjscEqpC>o$SNNx*UE{jXNSoLb z+n+q4c}ApDJMHCFeep&WxI5jld)ettzuFM0cNc(xlP4?itMlhlL&h>9dZmk?wS4>c zdMe(2M2u}p<#|)miw69McgE_tK|She%NZ%h?nGHsr;JQk>@*qXwq>wo3&1XM#KYDU z-I)QR{#cmK=7w?OZ8L@0?;#*iNv1RDL#33>3}mLO(Bh2%jvuB`PBAr`zizQOQX|Lt zTtpoM^8EA{$Bv+=?)|NyAeWhLCq~Lq)D?5l6OAR6_M?qC9(&eiSGbAe0$6NYzI7~q zch7luvb{xCxQ;|9mb0vHUAQ?qce7N$ql!P<(swDSwt~T3%1GR9kq6lpaui8Nm@@Qq z3Q4!fM$tNB7hoDzKA~A*(j!MR6%R&*25_E>SZFcX@5lCB-pUP%Cqd%^D4Zs)#ZmyP zERm+bR718OjUfRpgcllntDi!!d9Qdb0zS?L=8Wfb3wEcRxkA!J3%5FrcU1LTLu5DfM`RNC*h}Iu?@5>cHNfRo7Ak@{bC*O&bFL(F!^lVR`aWsK) z!0MRGPnQTef6g{A#@lsLe(qg7s9h8pXo*-fZ3G?9i^F#=;wFpcg| zm!Zeo=16^uC+`^%JkNC*t-$HdWGmy57_x{{!PrA&baqP;0yMT=CHC*j^gi(gI?@}GA4y6@ z*>^@36FEENNcP-67FS+vUr9}Rqu0u)Dz!?S*fDg`7`Rb;!UCjQC1TB`gu1n>Zm)cYEQYd4uN&>pfp7Tx_SIi1#n?~QM)3})& zdxyyk5>M}+n;q@A$+NQGDWerf9F85KTmg(FZk+P`_*MI^rW|(E6X+i=+QMEO(@_(4 z8^vuE>Z$eNIIk7};aoXnIYE}*Z(tFvz%{A6&4YYG$R8x^A0FBtsA;fd-40vxj`vd6 zXv+I;zd7z>rDqVdjJRs}GxP?5Tmhb~j!Q+B#I{fA_w8~#&IJ>5^j$`(z2@h7b5W~M zC=28)H;tYAmts=ql>RK&Km(rwDb>Uit4&m~w|nk?h3-Oq(sQ4Qxq{;DIDvpsbZp*d zfe}aNzA71r6AcT6N0Lfq6<^25d(BUIc35~USoQ0^ft=5iT>v1ox&$C>T?w`Mg+u-)KvF{mB>!oteq}jiVrV(|0_el$(2n0j zaBo)(fK-Y-w#Zw?^d zAZNRpKRFdyl{UWFt)dJk!Qt@3%Ojf*Qr*G9!EY@PuUyM2U&YI{|8)RaqH(k2Nto0E z5|X5kv;V~Qf#xkl-suK}B^7$xS-BE#JnDR{gcfR8c9;9r*@s0~22+=BW61~2BnHh^ z83sJp+?(Rm^zic9H(67m=wf7palby53VvILul2GM77nGsCwA}U(%}WQlP;N|Te@)g zh+IXiK}D!dwdcLpH+<}WPZ{;RfPlVBSNhWc|FufFm)9`feo5F@*R)QG8>d^R*)1ME z92tJx=-Qi8FHqRf0RF8AdVG9*UWH3{K^(svU+n4X>gt2L4N8gbc4pyFytf;_s;cU` zyyVueQH|R65=z(-a@Ji95dk3s4`gZXcCc`s904~!zYP~zS=qx{6+W6sXQn1PByP6( z-&}woEjq;7Z)bf*5diCBZ*!e(=4);J@zHE+xQH=|G?{?e?k*h7YqEZ}Va<2BAr%~| z@@r&|`X3H;j>{xp4_T8g$*F5sXp2)jBdi2Nwn1X80U + All available CSS variables + +#### Base colors + +These colors are used for special purposes like ring, scrollbar, ... + +| Token Name | Description | +| -------------- | ----------------------------------------------------------------------- | +| `--bui-black` | Pure black color. This one should be the same in light and dark themes. | +| `--bui-white` | Pure white color. This one should be the same in light and dark themes. | +| `--bui-gray-1` | You can use these mostly for backgrounds colors. | +| `--bui-gray-2` | You can use these mostly for backgrounds colors. | +| `--bui-gray-3` | You can use these mostly for backgrounds colors. | +| `--bui-gray-4` | You can use these mostly for backgrounds colors. | +| `--bui-gray-5` | You can use these mostly for backgrounds colors. | +| `--bui-gray-6` | You can use these mostly for backgrounds colors. | +| `--bui-gray-7` | You can use these mostly for backgrounds colors. | +| `--bui-gray-8` | You can use these mostly for backgrounds colors. | + +#### Core background colors + +These colors are used for the background of your application. We are mostly using for now a single elevated background for panels. `--bui-bg` should mostly use as the main background color of your app. + +| Token Name | Description | +| ------------------------- | ------------------------------------------------ | +| `--bui-bg` | The background color of your Backstage instance. | +| `--bui-bg-surface-1` | Use for any panels or elevated surfaces. | +| `--bui-bg-surface-2` | Use for any panels or elevated surfaces. | +| `--bui-bg-solid` | Used for solid background colors. | +| `--bui-bg-solid-hover` | Used for solid background colors when hovered. | +| `--bui-bg-solid-pressed` | Used for solid background colors when pressed. | +| `--bui-bg-solid-disabled` | Used for solid background colors when disabled. | +| `--bui-bg-tint` | Used for tint background colors. | +| `--bui-bg-tint-hover` | Used for tint background colors when hovered. | +| `--bui-bg-tint-focus` | Used for tint background colors when active. | +| `--bui-bg-tint-disabled` | Used for tint background colors when disabled. | +| `--bui-bg-danger` | Used to show errors information. | +| `--bui-bg-warning` | Used to show warnings information. | +| `--bui-bg-success` | Used to show success information. | + +#### Foreground colors + +Foreground colours are meant to work in pair with a background colours. Typeically this would work for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors are prefixed with `fg` to make it easier to identify. + +| Token Name | Description | +| ------------------------ | ----------------------------------------------------------------- | +| `--bui-fg-primary` | It should be used on top of main background surfaces. | +| `--bui-fg-secondary` | It should be used on top of main background surfaces. | +| `--bui-fg-link` | It should be used on top of main background surfaces. | +| `--bui-fg-link-hover` | It should be used on top of main background surfaces. | +| `--bui-fg-disabled` | It should be used on top of main background surfaces. | +| `--bui-fg-solid` | It should be used on top of solid background colors. | +| `--bui-fg-tint` | It should be used on top of tint background colors. | +| `--bui-fg-tint-disabled` | It should be used on top of tint background colors when disabled. | +| `--bui-fg-danger` | It should be used on top of danger background colors. | +| `--bui-fg-warning` | It should be used on top of warning background colors. | +| `--bui-fg-success` | It should be used on top of success background colors. | + +#### Border colors + +These border colors are mostly meant to be used as borders on top of any components with low contrast to help as a separator with the different background colors. + +| Token Name | Description | +| ----------------------- | --------------------------------------------------- | +| `--bui-border` | It should be used on top of `--bui-bg-surface-1`. | +| `--bui-border-hover` | Used when the component is interactive and hovered. | +| `--bui-border-pressed` | Used when the component is interactive and hovered. | +| `--bui-border-disabled` | Used when the component is disabled. | +| `--bui-border-danger` | It should be used on top of `--bui-bg-danger`. | +| `--bui-border-warning` | It should be used on top of `--bui-bg-warning`. | +| `--bui-border-success` | It should be used on top of `--bui-bg-success`. | + +#### Special colors + +These colors are used for special purposes like ring, scrollbar, ... + +| Token Name | Description | +| ----------------------- | --------------------------------- | +| `--bui-ring` | The color of the ring. | +| `--bui-scrollbar` | The color of the scrollbar. | +| `--bui-scrollbar-thumb` | The color of the scrollbar thumb. | + +#### Font families + +We have two fonts that we use across Backstage UI. The first one is the sans-serif font that we use for the body of the application. The second one is the monospace font that we use for code blocks and tables. + +| Token Name | Description | +| -------------------- | ---------------------------------- | +| `--bui-font-regular` | The sans-serif font for the theme. | +| `--bui-font-mono` | The monospace font for the theme. | + +#### Font weights + +We have two font weights that we use across Backstage UI. Regular or Bold. + +| Token Name | Description | +| --------------------------- | -------------------------------------- | +| `--bui-font-weight-regular` | The regular font weight for the theme. | +| `--bui-font-weight-bold` | The bold font weight for the theme. | + +#### Spacing + +We built a spacing system based on a single value `--bui-space`. This value is used to calculate the spacing for all the components. By default if you would like to increase or decrease the spacing between your components you can do it simply by updating `--bui-space` and it will apply to all spacing values. + +`--bui-space` is not used directly in any components but serve as an easy way to calculate the other values. + +| Token Name | Description | +| ------------- | ----------------------------------------------------------------- | +| `--bui-space` | The base unit for the spacing system. Default value is `0.25rem.` | + +#### Radius + +We use a radius system to make sure that the components have a consistent look and feel. + +| Token Name | Description | +| ------------------- | --------------------------------------------------------- | +| `--bui-radius-1` | The radius of the component. Default value is `0.125rem`. | +| `--bui-radius-2` | The radius of the component. Default value is `0.25rem`. | +| `--bui-radius-3` | The radius of the component. Default value is `0.5rem`. | +| `--bui-radius-4` | The radius of the component. Default value is `0.75rem`. | +| `--bui-radius-5` | The radius of the component. Default value is `1rem`. | +| `--bui-radius-6` | The radius of the component. Default value is `1.25rem`. | +| `--bui-radius-full` | The radius of the component. Default value is `9999px`. | + + ### Component class names -### Custom font +All Backstage UI components come with a set of CSS classes that you can use to style them. To make it easier to identify the class name you can use, we use a specific structure for the class names. + +![classname-structure](../../assets/user-interface/css-classname-structure.png) + +Every component has a unique prefix `.bui-` followed by the component name. Component props are represented using the `data-` attribute. That way, class names are easily identifiable. ## Create a theme for MUI (Legacy) diff --git a/microsite/src/theme/customTheme.scss b/microsite/src/theme/customTheme.scss index 707acb4073..823327b28f 100644 --- a/microsite/src/theme/customTheme.scss +++ b/microsite/src/theme/customTheme.scss @@ -171,6 +171,11 @@ html[data-theme='light'] { border-left: 3px solid rgb(255, 255, 255, 0.5); } +table { + width: 100%; + display: table; +} + /* #endregion */ /* For the docusaurus-pushfeedback plugin */ From 1dfaeeebdf23e744c4b494831102a04fc6c6eb31 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Fri, 12 Sep 2025 11:58:41 +0100 Subject: [PATCH 021/211] Update mkdocs.yml Signed-off-by: Charles de Dreuille --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index eea5fc9c40..45fdd2c9d6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,7 +28,7 @@ nav: - Database: 'getting-started/config/database.md' - Authentication: 'getting-started/config/authentication.md' - Configuring App with plugins: 'getting-started/configure-app-with-plugins.md' - - Customize the look-and-feel of your App: 'getting-started/app-custom-theme.md' + - Customize the look-and-feel of your App: 'conf/user-interface/index.md' - Customizing your Homepage: 'getting-started/homepage.md' - Deployment: - Deploying Backstage: 'deployment/index.md' From 7a40a90c6488a8ed04c046cc39069ba827e071fd Mon Sep 17 00:00:00 2001 From: Aditya Kumar <136452216+AdityaK60@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:10:38 +0530 Subject: [PATCH 022/211] Update addons.md Signed-off-by: Aditya Kumar <136452216+AdityaK60@users.noreply.github.com> --- docs/features/techdocs/addons.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/addons.md b/docs/features/techdocs/addons.md index f67f1c5660..435239794b 100644 --- a/docs/features/techdocs/addons.md +++ b/docs/features/techdocs/addons.md @@ -1,5 +1,5 @@ --- -id: addons--old +id: addons title: TechDocs Addons description: How to find, use, or create TechDocs Addons. --- From a4d3260567cb6a1aa54b5c379159e768f5ff0efd Mon Sep 17 00:00:00 2001 From: Aditya Kumar <136452216+AdityaK60@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:11:43 +0530 Subject: [PATCH 023/211] Update addons--new.md Signed-off-by: Aditya Kumar <136452216+AdityaK60@users.noreply.github.com> --- docs/features/techdocs/addons--new.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/techdocs/addons--new.md b/docs/features/techdocs/addons--new.md index a552ad1ace..8c58eb892c 100644 --- a/docs/features/techdocs/addons--new.md +++ b/docs/features/techdocs/addons--new.md @@ -1,5 +1,5 @@ --- -id: addons +id: addons--new title: TechDocs Addons description: How to find, use, or create TechDocs Addons. --- From f5f1cece1712ffdf84f477405415db36354ab6ba Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 15 Sep 2025 11:37:57 +0100 Subject: [PATCH 024/211] Update docs/conf/user-interface/icons.md Co-authored-by: Patrik Oldsberg Signed-off-by: Charles de Dreuille --- docs/conf/user-interface/icons.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf/user-interface/icons.md b/docs/conf/user-interface/icons.md index 44b8782430..26a6f11096 100644 --- a/docs/conf/user-interface/icons.md +++ b/docs/conf/user-interface/icons.md @@ -20,7 +20,7 @@ You can change the following [icons](https://github.com/backstage/backstage/blob ### Create React Component -In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `CustomIcons.tsx` file. +In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `customIcons.tsx` file. ```tsx title="customIcons.tsx" import { SvgIcon, SvgIconProps } from '@material-ui/core'; From 95c442a9bdc17e52f273eb6de03604abee6882dc Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Mon, 15 Sep 2025 12:39:09 +0100 Subject: [PATCH 025/211] Docs fixes Signed-off-by: Charles de Dreuille --- docs/conf/user-interface/index.md | 2 +- microsite/docusaurus.config.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/conf/user-interface/index.md b/docs/conf/user-interface/index.md index d4ecbc6940..aeae086b59 100644 --- a/docs/conf/user-interface/index.md +++ b/docs/conf/user-interface/index.md @@ -38,7 +38,7 @@ We recognize that maintaining two separate theming systems is not ideal. Because ## Creating custom themes -During the transition to Backstage UI, you will need to maintain themes in two places: some components and plugins still rely on MUI, while others use Backstage UI. At this time, there is no automated solution to generate MUI themes from your Backstage UI theme, so you will need to manage both separately until the migration is complete. +During the transition to Backstage UI, you will need to maintain themes in two places: some components and plugins still rely on MUI, while others use Backstage UI. We are working on a plugin that will make help you convert your existing MUI theme into a Backstage UI CSS file you can add to your application. We'll update this page when the plugin is available but for now you can follow progress on this PR [#31140](https://github.com/backstage/backstage/pull/31140). ```tsx title="packages/app/src/App.tsx" /* highlight-add-start */ diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index d7538cd12d..83f2f75689 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -269,6 +269,10 @@ const config: Config = { from: '/docs/plugins/url-reader/', to: '/docs/backend-system/core-services/url-reader', }, + { + from: '/docs/getting-started/app-custom-theme', + to: '/docs/conf/user-interface', + } ], }), [ From 8495b18507b3dca141abacd543db6ca6ce7b25fe Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Thu, 20 Mar 2025 12:00:23 +0100 Subject: [PATCH 026/211] feat: add external token decorator service Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/thirty-rules-press.md | 5 + docs/auth/service-to-service-auth.md | 31 +++- packages/backend-defaults/report-auth.api.md | 20 +++ .../auth/authServiceFactory.test.ts | 63 +++++++- .../entrypoints/auth/authServiceFactory.ts | 26 +++- .../external/ExternalTokenHandler.test.ts | 144 ++++++++++++++++++ .../auth/external/ExternalTokenHandler.ts | 26 +++- .../src/entrypoints/auth/index.ts | 3 + .../auth/plugin/PluginTokenHandler.ts | 2 +- 9 files changed, 309 insertions(+), 11 deletions(-) create mode 100644 .changeset/thirty-rules-press.md diff --git a/.changeset/thirty-rules-press.md b/.changeset/thirty-rules-press.md new file mode 100644 index 0000000000..f4758da67b --- /dev/null +++ b/.changeset/thirty-rules-press.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': minor +--- + +Add a new `externalTokenHandlerDecoratorServiceRef` to allow custom external token validations diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index d35aed364d..e821aeb0c7 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -414,9 +414,13 @@ Each entry has one or more of the following fields: ## Adding custom or logic for validation and issuing of tokens -The `pluginTokenHandlerDecoratorServiceRef` can be used to decorate the existing token handler without having to re-implement the entire `AuthService` implementation. +The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenHandlerDecoratorServiceRef` can be used to decorate the existing token handler without having to re-implement the entire `AuthService` implementation. This is particularly useful when you want to add additional logic to the handler, such as logging or metrics or custom token validation. +### PluginTokenHandler decoration + +The `pluginTokenHandlerDecoratorServiceRef` can be used to decorate the default PluginTokenHandler used for create and verify tokens from plugins. + The `PluginTokenHandler` interface has two methods: - `issueToken`: This method is used to issue a token for a plugin. It takes in the `pluginId` and `targetPluginId` as arguments, and an optional `limitedUserToken` object which can be used to issue a token on behalf of another user. The method returns a promise that resolves to an object containing the issued token. @@ -439,3 +443,28 @@ const decoratedPluginTokenHandler = createServiceFactory({ }, }); ``` + +### ExternalTokenHandler decoration + +The `externalTokenHandlerDecoratorServiceRef` can be used to decorate the default ExternalTokenHandler used for verify tokens from external callers. + +The `ExternalTokenHandler` interface has one methods: + +- `verifyToken`: This method is used to verify a token. It takes in the token as an argument and returns a promise that resolves to an object containing the subject of the token and an optional limited user token. + +```ts +import { + ExternalTokenHandler, + externalTokenHandlerDecoratorServiceRef, +} from '@backstage/backend-defaults/auth'; +import { createServiceFactory } from '@backstage/backend-plugin-api'; + +const decoratedPluginTokenHandler = createServiceFactory({ + service: externalTokenHandlerDecoratorServiceRef, + deps: {}, + async factory() { + return (defaultImplementation: ExternalTokenHandler) => + new CustomTokenHandler(defaultImplementation); + }, +}); +``` diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index 1883e65a6a..56a973813d 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -4,6 +4,7 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; +import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; @@ -14,6 +15,25 @@ export const authServiceFactory: ServiceFactory< 'singleton' >; +// @public +export interface ExternalTokenHandler { + // (undocumented) + verifyToken(token: string): Promise< + | { + subject: string; + accessRestrictions?: BackstagePrincipalAccessRestrictions; + } + | undefined + >; +} + +// @public +export const externalTokenHandlerDecoratorServiceRef: ServiceRef< + (defaultImplementation: ExternalTokenHandler) => ExternalTokenHandler, + 'plugin', + 'singleton' +>; + // @public export interface PluginTokenHandler { // (undocumented) diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index a244614135..990668083f 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -21,6 +21,8 @@ import { } from '@backstage/backend-test-utils'; import { authServiceFactory, + externalTokenHandlersServiceRef, + // externalTokenHandlersServiceRef, pluginTokenHandlerDecoratorServiceRef, } from './authServiceFactory'; import { base64url, decodeJwt } from 'jose'; @@ -30,6 +32,11 @@ import { setupServer } from 'msw/node'; import { toInternalBackstageCredentials } from './helpers'; import { PluginTokenHandler } from './plugin/PluginTokenHandler'; import { createServiceFactory } from '@backstage/backend-plugin-api'; +import { AccessRestriptionsMap, TokenHandler } from './external/types'; +import { Config } from '@backstage/config'; +import { x } from 'tar'; +// import { ExternalTokenHandler } from './external/ExternalTokenHandler'; +// import { TokenHandler } from './external/types'; const server = setupServer(); @@ -58,6 +65,13 @@ const mockDeps = [ subject: 'unlimited-static-subject', }, }, + { + type: 'custom', + options: { + [`custom-config`]: 'custom-config', + foo: 'bar', + }, + }, ], }, }, @@ -450,8 +464,55 @@ describe('authServiceFactory', () => { dependencies: [...mockDeps, customPluginTokenHandler], }); const searchAuth = await tester.getSubject('search'); - searchAuth.authenticate('unlimited-static-token'); + await searchAuth.authenticate('unlimited-static-token'); expect(customLogic).toHaveBeenCalledWith('unlimited-static-token'); }); }); + describe('add custom ExternalTokenHandler', () => { + it('should allow custom logic to be injected into the plugin token handler', async () => { + const customLogic = jest.fn(); + const customAddEntry = jest.fn(); + const customPluginTokenHandler = createServiceFactory({ + service: externalTokenHandlersServiceRef, + deps: {}, + async factory() { + return { + custom: new (class CustomHandler implements TokenHandler { + add(options: Config): void { + customAddEntry(options); + } + async verifyToken(token: string): Promise< + | { + subject: string; + allAccessRestrictions?: AccessRestriptionsMap; + } + | undefined + > { + customLogic(token); + return { + subject: 'foo', + }; + } + })(), + }; + }, + }); + const tester = ServiceFactoryTester.from(authServiceFactory, { + dependencies: [...mockDeps, customPluginTokenHandler], + }); + const searchAuth = await tester.getSubject('search'); + await searchAuth.authenticate('custom-token'); + expect(customAddEntry).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + options: expect.objectContaining({ + [`custom-config`]: 'custom-config', + foo: 'bar', + }), + }), + }), + ); + expect(customLogic).toHaveBeenCalledWith('custom-token'); + }); + }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index 91910292e6..48f0941b12 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -20,13 +20,18 @@ import { createServiceRef, } from '@backstage/backend-plugin-api'; import { DefaultAuthService } from './DefaultAuthService'; -import { ExternalTokenHandler } from './external/ExternalTokenHandler'; +import { + // DefaultExternalTokenHandler, + ExternalTokenHandler, +} from './external/ExternalTokenHandler'; import { DefaultPluginTokenHandler, PluginTokenHandler, } from './plugin/PluginTokenHandler'; import { createPluginKeySource } from './plugin/keys/createPluginKeySource'; import { UserTokenHandler } from './user/UserTokenHandler'; +import { TokenHandler } from './external/types'; +import { Config } from '@backstage/config'; /** * @public @@ -45,6 +50,22 @@ export const pluginTokenHandlerDecoratorServiceRef = createServiceRef< }, }), }); +/** + * @public + * This service is used to decorate the default plugin token handler with custom logic. + */ +export const externalTokenHandlersServiceRef = createServiceRef<{ + [configKey: string]: (config: Config) => TokenHandler; +}>({ + id: 'core.auth.externalTokenHandlers', + multiton: true, + // defaultFactory: async service => + // createServiceFactory({ + // service, + // deps: {}, + // factory: async () => {}, + // }), +}); /** * Handles token authentication and credentials management. @@ -64,6 +85,7 @@ export const authServiceFactory = createServiceFactory({ plugin: coreServices.pluginMetadata, database: coreServices.database, pluginTokenHandlerDecorator: pluginTokenHandlerDecoratorServiceRef, + externalTokenHandlers: externalTokenHandlersServiceRef, }, async factory({ config, @@ -72,6 +94,7 @@ export const authServiceFactory = createServiceFactory({ logger, database, pluginTokenHandlerDecorator, + externalTokenHandlers, }) { const disableDefaultAuthPolicy = config.getOptionalBoolean( @@ -106,6 +129,7 @@ export const authServiceFactory = createServiceFactory({ ownPluginId: plugin.getId(), config, logger, + externalTokenHandlers, }); return new DefaultAuthService( diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts index 39b01c46b6..de9c9e162b 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts @@ -208,4 +208,148 @@ describe('ExternalTokenHandler', () => { accessRestrictions: { permissionNames: ['catalog.entity.read'] }, }); }); + it('successfully uses custom token handlers', async () => { + const factory = new FakeTokenFactory({ + issuer: 'my-company', + keyDurationSeconds: 100, + }); + + server.use( + rest.get( + 'https://example.com/.well-known/jwks.json', + async (_, res, ctx) => { + const keys = await factory.listPublicKeys(); + return res(ctx.json(keys)); + }, + ), + ); + + const customHandler: TokenHandler = { + add: jest.fn(), + verifyToken: jest.fn(), + }; + + const handler = ExternalTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'internal-custom', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [{ ['internal-custom']: customHandler }], + }); + + expect(customHandler.add).toHaveBeenCalledWith( + expect.objectContaining({ + data: { + type: 'internal-custom', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + }), + ); + + const customToken = await factory.issueToken({ + claims: { sub: 'internal-subject' }, + }); + + await handler.verifyToken(customToken); + + expect(customHandler.verifyToken).toHaveBeenCalled(); + }); + it('should fail if config contains types not declared', async () => { + const createHandler = () => + ExternalTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'internal-custom', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Unknown type 'internal-custom' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks'"`, + ); + }); + it('should show valid custom types in errors', async () => { + const createHandler = () => + ExternalTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'internal-custom-invalid', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [ + { + ['internal-custom']: { + add: jest.fn(), + verifyToken: jest.fn(), + }, + }, + ], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Unknown type 'internal-custom-invalid' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, + ); + }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts index 72eeae6fcf..a4c55810c3 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts @@ -24,6 +24,7 @@ import { LegacyTokenHandler } from './legacy'; import { StaticTokenHandler } from './static'; import { JWKSHandler } from './jwks'; import { TokenHandler } from './types'; +import { Config } from '@backstage/config'; const NEW_CONFIG_KEY = 'backend.auth.externalAccess'; const OLD_CONFIG_KEY = 'backend.auth.keys'; @@ -40,17 +41,27 @@ export class ExternalTokenHandler { ownPluginId: string; config: RootConfigService; logger: LoggerService; + externalTokenHandlers?: { + [key: string]: (config: Config) => TokenHandler; + }[]; }): ExternalTokenHandler { - const { ownPluginId, config, logger } = options; + const { + ownPluginId, + config, + logger, + externalTokenHandlers: customHandlers, + } = options; const staticHandler = new StaticTokenHandler(); const legacyHandler = new LegacyTokenHandler(); const jwksHandler = new JWKSHandler(); - const handlers: Record = { - static: staticHandler, - legacy: legacyHandler, - jwks: jwksHandler, + const handlers: Record TokenHandler> = { + static: (handlerConfig: Config) => staticHandler.add(handlerConfig), + legacy: (handlerConfig: Config) => legacyHandler.add(handlerConfig), + jwks: (handlerConfig: Config) => jwksHandler.add(handlerConfig), + ...Object.assign({}, ...(customHandlers ?? [])), }; + const configuredHandlers = []; // Load the new-style handlers const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? []; @@ -65,7 +76,8 @@ export class ExternalTokenHandler { `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, ); } - handler.add(handlerConfig); + configuredHandlers.push(handler(handlerConfig)); + // handler.add(handlerConfig); } // Load the old keys too @@ -80,7 +92,7 @@ export class ExternalTokenHandler { legacyHandler.addOld(handlerConfig); } - return new ExternalTokenHandler(ownPluginId, Object.values(handlers)); + return new ExternalTokenHandler(ownPluginId, configuredHandlers); } constructor( diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index e48926f0ce..51fe982891 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -17,6 +17,9 @@ export { authServiceFactory, pluginTokenHandlerDecoratorServiceRef, + externalTokenHandlerDecoratorServiceRef, } from './authServiceFactory'; +export type { ExternalTokenHandler } from './external/ExternalTokenHandler'; + export type { PluginTokenHandler } from './plugin/PluginTokenHandler'; diff --git a/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts index 9aec52bc08..b692cd2b87 100644 --- a/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts @@ -46,7 +46,7 @@ type Options = { /** * @public - * Issues and verifies {@link https://backstage.iceio/docs/auth/service-to-service-auth | service-to-service tokens}. + * Issues and verifies {@link https://backstage.io/docs/auth/service-to-service-auth | service-to-service tokens}. */ export interface PluginTokenHandler { verifyToken( From 7d96f52c41cf9bca8d4ea2ab32468368c0645d40 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 24 Mar 2025 09:08:19 +0100 Subject: [PATCH 027/211] fix some test and types Signed-off-by: Juan Pablo Garcia Ripa --- .../auth/authServiceFactory.test.ts | 103 +++++++++++++----- .../external/ExternalTokenHandler.test.ts | 9 +- .../src/entrypoints/auth/external/jwks.ts | 1 + .../src/entrypoints/auth/external/legacy.ts | 1 + .../src/entrypoints/auth/external/static.ts | 1 + .../src/entrypoints/auth/external/types.ts | 2 +- 6 files changed, 85 insertions(+), 32 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index 990668083f..51df123555 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -22,7 +22,6 @@ import { import { authServiceFactory, externalTokenHandlersServiceRef, - // externalTokenHandlersServiceRef, pluginTokenHandlerDecoratorServiceRef, } from './authServiceFactory'; import { base64url, decodeJwt } from 'jose'; @@ -34,7 +33,6 @@ import { PluginTokenHandler } from './plugin/PluginTokenHandler'; import { createServiceFactory } from '@backstage/backend-plugin-api'; import { AccessRestriptionsMap, TokenHandler } from './external/types'; import { Config } from '@backstage/config'; -import { x } from 'tar'; // import { ExternalTokenHandler } from './external/ExternalTokenHandler'; // import { TokenHandler } from './external/types'; @@ -65,13 +63,6 @@ const mockDeps = [ subject: 'unlimited-static-subject', }, }, - { - type: 'custom', - options: { - [`custom-config`]: 'custom-config', - foo: 'bar', - }, - }, ], }, }, @@ -472,36 +463,84 @@ describe('authServiceFactory', () => { it('should allow custom logic to be injected into the plugin token handler', async () => { const customLogic = jest.fn(); const customAddEntry = jest.fn(); + const customConfig = jest.fn(); + const deps = [ + discoveryServiceFactory, + mockServices.rootConfig.factory({ + data: { + backend: { + baseUrl: 'http://localhost', + auth: { + keys: [{ secret: 'abc' }], + externalAccess: [ + { + type: 'static', + options: { + token: 'limited-static-token', + subject: 'limited-static-subject', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'do.it' }, + ], + }, + { + type: 'static', + options: { + token: 'unlimited-static-token', + subject: 'unlimited-static-subject', + }, + }, + { + type: 'custom', + options: { + [`custom-config`]: 'custom-config', + foo: 'bar', + }, + }, + ], + }, + }, + }, + }), + ]; + + const customHandler = new (class CustomHandler implements TokenHandler { + add(options: Config): TokenHandler { + customAddEntry(options); + return this; + } + async verifyToken(token: string): Promise< + | { + subject: string; + allAccessRestrictions?: AccessRestriptionsMap; + } + | undefined + > { + customLogic(token); + return { + subject: 'foo', + }; + } + })(); + const customPluginTokenHandler = createServiceFactory({ service: externalTokenHandlersServiceRef, deps: {}, async factory() { return { - custom: new (class CustomHandler implements TokenHandler { - add(options: Config): void { - customAddEntry(options); - } - async verifyToken(token: string): Promise< - | { - subject: string; - allAccessRestrictions?: AccessRestriptionsMap; - } - | undefined - > { - customLogic(token); - return { - subject: 'foo', - }; - } - })(), + custom: (options: Config) => { + customConfig(options); + return customHandler.add(options); + }, }; }, }); const tester = ServiceFactoryTester.from(authServiceFactory, { - dependencies: [...mockDeps, customPluginTokenHandler], + dependencies: [...deps, customPluginTokenHandler], }); const searchAuth = await tester.getSubject('search'); await searchAuth.authenticate('custom-token'); + expect(customAddEntry).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ @@ -513,6 +552,16 @@ describe('authServiceFactory', () => { }), ); expect(customLogic).toHaveBeenCalledWith('custom-token'); + expect(customConfig).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + options: expect.objectContaining({ + [`custom-config`]: 'custom-config', + foo: 'bar', + }), + }), + }), + ); }); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts index de9c9e162b..3567061f03 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts @@ -228,6 +228,7 @@ describe('ExternalTokenHandler', () => { add: jest.fn(), verifyToken: jest.fn(), }; + const customHandlerCreator = jest.fn(() => customHandler); const handler = ExternalTokenHandler.create({ ownPluginId: 'catalog', @@ -253,10 +254,10 @@ describe('ExternalTokenHandler', () => { }, }, }), - externalTokenHandlers: [{ ['internal-custom']: customHandler }], + externalTokenHandlers: [{ ['internal-custom']: customHandlerCreator }], }); - expect(customHandler.add).toHaveBeenCalledWith( + expect(customHandlerCreator).toHaveBeenCalledWith( expect.objectContaining({ data: { type: 'internal-custom', @@ -340,10 +341,10 @@ describe('ExternalTokenHandler', () => { }), externalTokenHandlers: [ { - ['internal-custom']: { + ['internal-custom']: () => ({ add: jest.fn(), verifyToken: jest.fn(), - }, + }), }, ], }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts index d88dc62a47..32271972fb 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts @@ -68,6 +68,7 @@ export class JWKSHandler implements TokenHandler { url, allAccessRestrictions, }); + return this; } async verifyToken(token: string) { diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts index 9c60e70707..fbd96aafd7 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts @@ -40,6 +40,7 @@ export class LegacyTokenHandler implements TokenHandler { config.getString('options.subject'), allAccessRestrictions, ); + return this; } // used only for the old backend.auth.keys array diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.ts index 1e89d8c6a1..b335b89e58 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.ts @@ -54,6 +54,7 @@ export class StaticTokenHandler implements TokenHandler { } this.#entries.set(token, { subject, allAccessRestrictions }); + return this; } async verifyToken(token: string) { diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index 6a1fd084ed..e3cad90e17 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -23,7 +23,7 @@ export type AccessRestriptionsMap = Map< >; export interface TokenHandler { - add(options: Config): void; + add?(options: Config): TokenHandler; verifyToken(token: string): Promise< | { subject: string; From 9377fbfb0f2449e03289f566a5ad0d3b18874a8b Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 24 Mar 2025 10:22:43 +0100 Subject: [PATCH 028/211] feat(docs): add multiton docs and fix the custom handler service Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/thirty-rules-press.md | 2 +- docs/auth/service-to-service-auth.md | 22 +++++----- .../architecture/03-services.md | 29 +++++++++++++ packages/backend-defaults/report-auth.api.md | 41 ++++++++++++------- .../auth/authServiceFactory.test.ts | 4 +- .../entrypoints/auth/authServiceFactory.ts | 6 --- .../src/entrypoints/auth/external/helpers.ts | 6 +-- .../src/entrypoints/auth/external/jwks.ts | 4 +- .../src/entrypoints/auth/external/legacy.ts | 6 +-- .../src/entrypoints/auth/external/static.ts | 4 +- .../src/entrypoints/auth/external/types.ts | 12 +++++- .../src/entrypoints/auth/index.ts | 5 ++- 12 files changed, 93 insertions(+), 48 deletions(-) diff --git a/.changeset/thirty-rules-press.md b/.changeset/thirty-rules-press.md index f4758da67b..d9968317cb 100644 --- a/.changeset/thirty-rules-press.md +++ b/.changeset/thirty-rules-press.md @@ -2,4 +2,4 @@ '@backstage/backend-defaults': minor --- -Add a new `externalTokenHandlerDecoratorServiceRef` to allow custom external token validations +Add a new `externalTokenHandlersServiceRef` to allow custom external token validations diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index e821aeb0c7..8e8ef6a183 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -414,7 +414,7 @@ Each entry has one or more of the following fields: ## Adding custom or logic for validation and issuing of tokens -The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenHandlerDecoratorServiceRef` can be used to decorate the existing token handler without having to re-implement the entire `AuthService` implementation. +The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenHandlersServiceRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. This is particularly useful when you want to add additional logic to the handler, such as logging or metrics or custom token validation. ### PluginTokenHandler decoration @@ -446,25 +446,27 @@ const decoratedPluginTokenHandler = createServiceFactory({ ### ExternalTokenHandler decoration -The `externalTokenHandlerDecoratorServiceRef` can be used to decorate the default ExternalTokenHandler used for verify tokens from external callers. +The `externalTokenHandlersServiceRef` can be used to add custom external token handlers to the default implementation. -The `ExternalTokenHandler` interface has one methods: +The returned object should be a map of token custom types and their handler factories. The object keys will be matched to the configured `externalAccess` type in the app-config, calling the factory function with the config object for that type. Custom token handlers should implement the `TokenHandler` interface, which provides methods for verifying tokens. -- `verifyToken`: This method is used to verify a token. It takes in the token as an argument and returns a promise that resolves to an object containing the subject of the token and an optional limited user token. +For example, to add a custom token handler for a type called 'custom': ```ts import { - ExternalTokenHandler, - externalTokenHandlerDecoratorServiceRef, + TokenHandler, + externalTokenHandlersServiceRef, } from '@backstage/backend-defaults/auth'; +import { Config } from '@backstage/config'; import { createServiceFactory } from '@backstage/backend-plugin-api'; -const decoratedPluginTokenHandler = createServiceFactory({ - service: externalTokenHandlerDecoratorServiceRef, +const customExternalTokenHandlers = createServiceFactory({ + service: externalTokenHandlersServiceRef, deps: {}, async factory() { - return (defaultImplementation: ExternalTokenHandler) => - new CustomTokenHandler(defaultImplementation); + return { + custom: (config: Config) => new CustomTokenHandler(config); + }; }, }); ``` diff --git a/docs/backend-system/architecture/03-services.md b/docs/backend-system/architecture/03-services.md index cfcd606d92..3b168b81e3 100644 --- a/docs/backend-system/architecture/03-services.md +++ b/docs/backend-system/architecture/03-services.md @@ -223,6 +223,35 @@ export const customFooServiceFactory = createServiceFactory({ This allows you to provide more advanced options for the service implementation that couldn't be expressed through static configuration. It also gives users of the service implementation access to other services through dependency injection, which can be useful for their customizations. +## Multiton + +By default the service reference will point to a singleton instance of the service. This meand if a new service factory uses this reference will override the previous one. This is the most common use-case, but in some cases you may want to have multiple instances of the same service. +For some services, is desirable to extend the functionality instead of overriding it. For example, some services could have many handler to address a specific event, and you may want to add a new handler instead of overriding the previous one. In this case, you can use the `multiton` option when creating the service reference: + +```ts +// example-service-ref.ts +import { createServiceRef } from '@backstage/backend-plugin-api'; + +export interface FooService { + foo(options: FooOptions): Promise; +} + +export const fooServiceRef = createServiceRef({ + id: 'example.foo', + multiton: true, // this service ref will be an array of instances +}); +``` + +When adding this serviceRef ad dependency to a factory, the factory will receive an array of instances instead of a single instance: + +```ts +deps: {fooServices: fooServiceRef}, + factory(fooServices) { + // fooServices is an array of instances + return new Bar(fooServices); + }, +``` + ## Service Factory Options Pattern :::note Note diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index 56a973813d..9b7a56cff5 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -5,9 +5,16 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; +// @public (undocumented) +export type AccessRestrictionsMap = Map< + string, // plugin ID + BackstagePrincipalAccessRestrictions +>; + // @public export const authServiceFactory: ServiceFactory< AuthService, @@ -16,22 +23,12 @@ export const authServiceFactory: ServiceFactory< >; // @public -export interface ExternalTokenHandler { - // (undocumented) - verifyToken(token: string): Promise< - | { - subject: string; - accessRestrictions?: BackstagePrincipalAccessRestrictions; - } - | undefined - >; -} - -// @public -export const externalTokenHandlerDecoratorServiceRef: ServiceRef< - (defaultImplementation: ExternalTokenHandler) => ExternalTokenHandler, +export const externalTokenHandlersServiceRef: ServiceRef< + { + [configKey: string]: (config: Config) => TokenHandler; + }, 'plugin', - 'singleton' + 'multiton' >; // @public @@ -64,5 +61,19 @@ export const pluginTokenHandlerDecoratorServiceRef: ServiceRef< 'singleton' >; +// @public +export interface TokenHandler { + // (undocumented) + add?(options: Config): TokenHandler; + // (undocumented) + verifyToken(token: string): Promise< + | { + subject: string; + allAccessRestrictions?: AccessRestrictionsMap; + } + | undefined + >; +} + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index 51df123555..d6cc0023a8 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -31,7 +31,7 @@ import { setupServer } from 'msw/node'; import { toInternalBackstageCredentials } from './helpers'; import { PluginTokenHandler } from './plugin/PluginTokenHandler'; import { createServiceFactory } from '@backstage/backend-plugin-api'; -import { AccessRestriptionsMap, TokenHandler } from './external/types'; +import { AccessRestrictionsMap, TokenHandler } from './external/types'; import { Config } from '@backstage/config'; // import { ExternalTokenHandler } from './external/ExternalTokenHandler'; // import { TokenHandler } from './external/types'; @@ -512,7 +512,7 @@ describe('authServiceFactory', () => { async verifyToken(token: string): Promise< | { subject: string; - allAccessRestrictions?: AccessRestriptionsMap; + allAccessRestrictions?: AccessRestrictionsMap; } | undefined > { diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index 48f0941b12..ae8ad205b7 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -59,12 +59,6 @@ export const externalTokenHandlersServiceRef = createServiceRef<{ }>({ id: 'core.auth.externalTokenHandlers', multiton: true, - // defaultFactory: async service => - // createServiceFactory({ - // service, - // deps: {}, - // factory: async () => {}, - // }), }); /** diff --git a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts index d6aa0a01ff..4a01117e28 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { AccessRestriptionsMap } from './types'; +import { AccessRestrictionsMap } from './types'; /** * Parses and returns the `accessRestrictions` configuration from an @@ -25,12 +25,12 @@ import { AccessRestriptionsMap } from './types'; */ export function readAccessRestrictionsFromConfig( externalAccessEntryConfig: Config, -): AccessRestriptionsMap | undefined { +): AccessRestrictionsMap | undefined { const configs = externalAccessEntryConfig.getOptionalConfigArray('accessRestrictions') ?? []; - const result: AccessRestriptionsMap = new Map(); + const result: AccessRestrictionsMap = new Map(); for (const config of configs) { const validKeys = ['plugin', 'permission', 'permissionAttribute']; for (const key of config.keys()) { diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts index 32271972fb..49167bcd35 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts @@ -20,7 +20,7 @@ import { readAccessRestrictionsFromConfig, readStringOrStringArrayFromConfig, } from './helpers'; -import { AccessRestriptionsMap, TokenHandler } from './types'; +import { AccessRestrictionsMap, TokenHandler } from './types'; /** * Handles `type: jwks` access. @@ -35,7 +35,7 @@ export class JWKSHandler implements TokenHandler { subjectPrefix?: string; url: URL; jwks: JWTVerifyGetKey; - allAccessRestrictions?: AccessRestriptionsMap; + allAccessRestrictions?: AccessRestrictionsMap; }> = []; add(config: Config) { diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts index fbd96aafd7..09d759a09e 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts @@ -17,7 +17,7 @@ import { Config } from '@backstage/config'; import { base64url, decodeJwt, decodeProtectedHeader, jwtVerify } from 'jose'; import { readAccessRestrictionsFromConfig } from './helpers'; -import { AccessRestriptionsMap, TokenHandler } from './types'; +import { AccessRestrictionsMap, TokenHandler } from './types'; /** * Handles `type: legacy` access. @@ -29,7 +29,7 @@ export class LegacyTokenHandler implements TokenHandler { key: Uint8Array; result: { subject: string; - allAccessRestrictions?: AccessRestriptionsMap; + allAccessRestrictions?: AccessRestrictionsMap; }; }>(); @@ -52,7 +52,7 @@ export class LegacyTokenHandler implements TokenHandler { #doAdd( secret: string, subject: string, - allAccessRestrictions?: AccessRestriptionsMap, + allAccessRestrictions?: AccessRestrictionsMap, ) { if (!secret.match(/^\S+$/)) { throw new Error('Illegal secret, must be a valid base64 string'); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.ts index b335b89e58..e003f0530b 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.ts @@ -16,7 +16,7 @@ import { Config } from '@backstage/config'; import { readAccessRestrictionsFromConfig } from './helpers'; -import { AccessRestriptionsMap, TokenHandler } from './types'; +import { AccessRestrictionsMap, TokenHandler } from './types'; const MIN_TOKEN_LENGTH = 8; @@ -30,7 +30,7 @@ export class StaticTokenHandler implements TokenHandler { string, { subject: string; - allAccessRestrictions?: AccessRestriptionsMap; + allAccessRestrictions?: AccessRestrictionsMap; } >(); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index e3cad90e17..6b882ef16b 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -17,17 +17,25 @@ import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; -export type AccessRestriptionsMap = Map< +/** + * @public + */ +export type AccessRestrictionsMap = Map< string, // plugin ID BackstagePrincipalAccessRestrictions >; +/** + * @public + * This interface is used to handle external tokens. + * It is used by the auth service to verify tokens and extract the subject. + */ export interface TokenHandler { add?(options: Config): TokenHandler; verifyToken(token: string): Promise< | { subject: string; - allAccessRestrictions?: AccessRestriptionsMap; + allAccessRestrictions?: AccessRestrictionsMap; } | undefined >; diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index 51fe982891..96db6c0535 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -17,9 +17,10 @@ export { authServiceFactory, pluginTokenHandlerDecoratorServiceRef, - externalTokenHandlerDecoratorServiceRef, + externalTokenHandlersServiceRef, } from './authServiceFactory'; -export type { ExternalTokenHandler } from './external/ExternalTokenHandler'; +export type { TokenHandler } from './external/types'; +export type { AccessRestrictionsMap } from './external/types'; export type { PluginTokenHandler } from './plugin/PluginTokenHandler'; From 410b81afd70627923e795a43269a2daec265a62d Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Fri, 25 Apr 2025 08:46:55 +0200 Subject: [PATCH 029/211] feat: use multiple Config factory Signed-off-by: Juan Pablo Garcia Ripa --- packages/backend-defaults/report-auth.api.md | 7 +- .../auth/authServiceFactory.test.ts | 38 ++--- .../entrypoints/auth/authServiceFactory.ts | 16 +- .../external/ExternalTokenHandler.test.ts | 100 +++++++++--- .../auth/external/ExternalTokenHandler.ts | 117 ++++++++++---- .../entrypoints/auth/external/jwks.test.ts | 45 +++--- .../src/entrypoints/auth/external/jwks.ts | 7 +- .../entrypoints/auth/external/legacy.test.ts | 143 ++++++++++-------- .../src/entrypoints/auth/external/legacy.ts | 20 +++ .../entrypoints/auth/external/static.test.ts | 129 ++++++++-------- .../src/entrypoints/auth/external/static.ts | 7 +- .../src/entrypoints/auth/external/types.ts | 2 - .../src/entrypoints/auth/index.ts | 3 +- 13 files changed, 392 insertions(+), 242 deletions(-) diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index 9b7a56cff5..bcc5c4a073 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -23,9 +23,10 @@ export const authServiceFactory: ServiceFactory< >; // @public -export const externalTokenHandlersServiceRef: ServiceRef< +export const externalTokenTypeHandlersRef: ServiceRef< { - [configKey: string]: (config: Config) => TokenHandler; + type: string; + factory: (config: Config[]) => TokenHandler; }, 'plugin', 'multiton' @@ -63,8 +64,6 @@ export const pluginTokenHandlerDecoratorServiceRef: ServiceRef< // @public export interface TokenHandler { - // (undocumented) - add?(options: Config): TokenHandler; // (undocumented) verifyToken(token: string): Promise< | { diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index d6cc0023a8..b808973016 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -21,7 +21,6 @@ import { } from '@backstage/backend-test-utils'; import { authServiceFactory, - externalTokenHandlersServiceRef, pluginTokenHandlerDecoratorServiceRef, } from './authServiceFactory'; import { base64url, decodeJwt } from 'jose'; @@ -33,8 +32,7 @@ import { PluginTokenHandler } from './plugin/PluginTokenHandler'; import { createServiceFactory } from '@backstage/backend-plugin-api'; import { AccessRestrictionsMap, TokenHandler } from './external/types'; import { Config } from '@backstage/config'; -// import { ExternalTokenHandler } from './external/ExternalTokenHandler'; -// import { TokenHandler } from './external/types'; +import { externalTokenTypeHandlersRef } from './external/ExternalTokenHandler'; const server = setupServer(); @@ -504,10 +502,11 @@ describe('authServiceFactory', () => { }), ]; - const customHandler = new (class CustomHandler implements TokenHandler { - add(options: Config): TokenHandler { - customAddEntry(options); - return this; + class CustomHandler implements TokenHandler { + constructor(options: Config[]) { + for (const option of options) { + customAddEntry(option); + } } async verifyToken(token: string): Promise< | { @@ -521,16 +520,17 @@ describe('authServiceFactory', () => { subject: 'foo', }; } - })(); + } const customPluginTokenHandler = createServiceFactory({ - service: externalTokenHandlersServiceRef, + service: externalTokenTypeHandlersRef, deps: {}, async factory() { return { - custom: (options: Config) => { - customConfig(options); - return customHandler.add(options); + type: 'custom', + factory: (configs: Config[]) => { + customConfig(configs); + return new CustomHandler(configs); }, }; }, @@ -553,14 +553,16 @@ describe('authServiceFactory', () => { ); expect(customLogic).toHaveBeenCalledWith('custom-token'); expect(customConfig).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - options: expect.objectContaining({ - [`custom-config`]: 'custom-config', - foo: 'bar', + expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + options: expect.objectContaining({ + [`custom-config`]: 'custom-config', + foo: 'bar', + }), }), }), - }), + ]), ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index ae8ad205b7..43a3c9da50 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -21,8 +21,8 @@ import { } from '@backstage/backend-plugin-api'; import { DefaultAuthService } from './DefaultAuthService'; import { - // DefaultExternalTokenHandler, ExternalTokenHandler, + externalTokenTypeHandlersRef, } from './external/ExternalTokenHandler'; import { DefaultPluginTokenHandler, @@ -30,8 +30,6 @@ import { } from './plugin/PluginTokenHandler'; import { createPluginKeySource } from './plugin/keys/createPluginKeySource'; import { UserTokenHandler } from './user/UserTokenHandler'; -import { TokenHandler } from './external/types'; -import { Config } from '@backstage/config'; /** * @public @@ -50,16 +48,6 @@ export const pluginTokenHandlerDecoratorServiceRef = createServiceRef< }, }), }); -/** - * @public - * This service is used to decorate the default plugin token handler with custom logic. - */ -export const externalTokenHandlersServiceRef = createServiceRef<{ - [configKey: string]: (config: Config) => TokenHandler; -}>({ - id: 'core.auth.externalTokenHandlers', - multiton: true, -}); /** * Handles token authentication and credentials management. @@ -79,7 +67,7 @@ export const authServiceFactory = createServiceFactory({ plugin: coreServices.pluginMetadata, database: coreServices.database, pluginTokenHandlerDecorator: pluginTokenHandlerDecoratorServiceRef, - externalTokenHandlers: externalTokenHandlersServiceRef, + externalTokenHandlers: externalTokenTypeHandlersRef, }, async factory({ config, diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts index 3567061f03..3348d84e93 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts @@ -85,12 +85,10 @@ describe('ExternalTokenHandler', () => { it('skips over inner handlers that do not match, and applies plugin restrictions', async () => { const handler1: TokenHandler = { - add: jest.fn(), verifyToken: jest.fn().mockResolvedValue(undefined), }; const handler2: TokenHandler = { - add: jest.fn(), verifyToken: jest.fn().mockResolvedValue({ subject: 'sub', allAccessRestrictions: new Map( @@ -225,10 +223,9 @@ describe('ExternalTokenHandler', () => { ); const customHandler: TokenHandler = { - add: jest.fn(), verifyToken: jest.fn(), }; - const customHandlerCreator = jest.fn(() => customHandler); + const customHandlerFactory = jest.fn(() => customHandler); const handler = ExternalTokenHandler.create({ ownPluginId: 'catalog', @@ -254,23 +251,30 @@ describe('ExternalTokenHandler', () => { }, }, }), - externalTokenHandlers: [{ ['internal-custom']: customHandlerCreator }], + externalTokenHandlers: [ + { + type: 'internal-custom', + factory: customHandlerFactory, + }, + ], }); - expect(customHandlerCreator).toHaveBeenCalledWith( - expect.objectContaining({ - data: { - type: 'internal-custom', - options: { - issuer: 'my-company', - subject: 'internal-subject', - audience: 'backstage', + expect(customHandlerFactory).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + data: { + type: 'internal-custom', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], - }, - }), + }), + ]), ); const customToken = await factory.issueToken({ @@ -310,9 +314,10 @@ describe('ExternalTokenHandler', () => { }); expect(createHandler).toThrowErrorMatchingInlineSnapshot( - `"Unknown type 'internal-custom' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks'"`, + `"Unknown type(s) 'internal-custom' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks'"`, ); }); + it('should show valid custom types in errors', async () => { const createHandler = () => ExternalTokenHandler.create({ @@ -341,8 +346,8 @@ describe('ExternalTokenHandler', () => { }), externalTokenHandlers: [ { - ['internal-custom']: () => ({ - add: jest.fn(), + type: 'internal-custom', + factory: () => ({ verifyToken: jest.fn(), }), }, @@ -350,7 +355,58 @@ describe('ExternalTokenHandler', () => { }); expect(createHandler).toThrowErrorMatchingInlineSnapshot( - `"Unknown type 'internal-custom-invalid' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, + `"Unknown type(s) 'internal-custom-invalid' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, + ); + }); + it('should show all invalid config types', async () => { + const createHandler = () => + ExternalTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'internal-custom-invalid', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + { + type: 'internal-custom-invalid-2', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [ + { + type: 'internal-custom', + factory: () => ({ + verifyToken: jest.fn(), + }), + }, + ], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Unknown type(s) 'internal-custom-invalid, internal-custom-invalid-2' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts index a4c55810c3..cc93b7b182 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts @@ -16,20 +16,70 @@ import { BackstagePrincipalAccessRestrictions, + createServiceRef, LoggerService, RootConfigService, } from '@backstage/backend-plugin-api'; import { NotAllowedError } from '@backstage/errors'; -import { LegacyTokenHandler } from './legacy'; +import { LegacyConfigWrapper, LegacyTokenHandler } from './legacy'; import { StaticTokenHandler } from './static'; import { JWKSHandler } from './jwks'; import { TokenHandler } from './types'; import { Config } from '@backstage/config'; +import { groupBy } from 'lodash'; const NEW_CONFIG_KEY = 'backend.auth.externalAccess'; const OLD_CONFIG_KEY = 'backend.auth.keys'; let loggedDeprecationWarning = false; +/** + * @public + * This service is used to decorate the default plugin token handler with custom logic. + */ +export const externalTokenTypeHandlersRef = createServiceRef<{ + type: string; + factory: (config: Config[]) => TokenHandler; +}>({ + id: 'core.auth.externalTokenHandlers', + multiton: true, + // defaultFactory // :pepe-think: seems like is not possible to use defaultFactory with multiton +}); + +type TokenTypeHandler = { + type: string; + /** + * A factory function that takes all token configuration for a given type + * and returns a TokenHandler or an array of TokenHandlers. + */ + factory: (config: Config[]) => TokenHandler | TokenHandler[]; +}; +type LegacyTokenTypeHandler = { + type: 'legacy'; + /** + * A factory function that takes all token configuration for a given type + * and returns a TokenHandler or an array of TokenHandlers. + */ + factory: ( + config: (Config | LegacyConfigWrapper)[], + ) => TokenHandler | TokenHandler[]; +}; + +const defaultHandlers: (TokenTypeHandler | LegacyTokenTypeHandler)[] = [ + { + type: 'static', + factory: configs => new StaticTokenHandler(configs), + }, + { + type: 'legacy', + factory: (configs: (Config | { legacy: true; config: Config })[]) => + new LegacyTokenHandler(configs), + }, + { + type: 'jwks', + factory: configs => new JWKSHandler(configs), + }, +]; + /** * Handles all types of external caller token types (i.e. not Backstage user * tokens, nor Backstage backend plugin tokens). @@ -41,9 +91,7 @@ export class ExternalTokenHandler { ownPluginId: string; config: RootConfigService; logger: LoggerService; - externalTokenHandlers?: { - [key: string]: (config: Config) => TokenHandler; - }[]; + externalTokenHandlers?: TokenTypeHandler[]; }): ExternalTokenHandler { const { ownPluginId, @@ -52,36 +100,18 @@ export class ExternalTokenHandler { externalTokenHandlers: customHandlers, } = options; - const staticHandler = new StaticTokenHandler(); - const legacyHandler = new LegacyTokenHandler(); - const jwksHandler = new JWKSHandler(); - const handlers: Record TokenHandler> = { - static: (handlerConfig: Config) => staticHandler.add(handlerConfig), - legacy: (handlerConfig: Config) => legacyHandler.add(handlerConfig), - jwks: (handlerConfig: Config) => jwksHandler.add(handlerConfig), - ...Object.assign({}, ...(customHandlers ?? [])), - }; - const configuredHandlers = []; + const handlersTypes = [...defaultHandlers, ...(customHandlers ?? [])]; - // Load the new-style handlers const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? []; - for (const handlerConfig of handlerConfigs) { - const type = handlerConfig.getString('type'); - const handler = handlers[type]; - if (!handler) { - const valid = Object.keys(handlers) - .map(k => `'${k}'`) - .join(', '); - throw new Error( - `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, - ); - } - configuredHandlers.push(handler(handlerConfig)); - // handler.add(handlerConfig); - } + const handlerConfigByType: Record & { + legacy?: (Config | LegacyConfigWrapper)[]; + } = groupBy(handlerConfigs, (handlerConfig: Config) => + handlerConfig.getString('type'), + ); // Load the old keys too const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? []; + if (legacyConfigs.length && !loggedDeprecationWarning) { loggedDeprecationWarning = true; logger.warn( @@ -89,10 +119,35 @@ export class ExternalTokenHandler { ); } for (const handlerConfig of legacyConfigs) { - legacyHandler.addOld(handlerConfig); + handlerConfigByType.legacy ??= []; + handlerConfigByType.legacy.push({ legacy: true, config: handlerConfig }); } - return new ExternalTokenHandler(ownPluginId, configuredHandlers); + const invalidTypes = Object.keys(handlerConfigByType).filter( + type => !handlersTypes.some(handler => handler.type === type), + ); + + if (invalidTypes.length > 0) { + const valid = handlersTypes + .map(handler => `'${handler.type}'`) + .join(', '); + throw new Error( + `Unknown type(s) '${invalidTypes.join( + ', ', + )}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, + ); + } + + const handlers = handlersTypes.flatMap(handler => { + const configs = handlerConfigByType[handler.type] ?? []; + const handlerInstances = handler.factory(configs); + if (Array.isArray(handlerInstances)) { + return handlerInstances; + } + return [handlerInstances]; + }); + + return new ExternalTokenHandler(ownPluginId, handlers); } constructor( diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts index 321e1823aa..eefbce5093 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts @@ -108,9 +108,7 @@ describe('JWKSHandler', () => { audience: 'backstage', }, }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(validEntry)); + const jwksHandler = new JWKSHandler([new ConfigReader(validEntry)]); const token = await factory.issueToken({ claims: { sub: mockSubject }, @@ -139,10 +137,10 @@ describe('JWKSHandler', () => { audience: ['multiple-audiences', 'backstage'], }, }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(invalidEntry)); - jwksHandler.add(new ConfigReader(validEntry)); + const jwksHandler = new JWKSHandler([ + new ConfigReader(invalidEntry), + new ConfigReader(validEntry), + ]); const token = await factory.issueToken({ claims: { sub: mockSubject }, @@ -169,10 +167,10 @@ describe('JWKSHandler', () => { audience: 'wrong', }, }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(invalidEntry1)); - jwksHandler.add(new ConfigReader(invalidEntry2)); + const jwksHandler = new JWKSHandler([ + new ConfigReader(invalidEntry1), + new ConfigReader(invalidEntry2), + ]); const token = await factory.issueToken({ claims: { sub: mockSubject }, @@ -184,30 +182,26 @@ describe('JWKSHandler', () => { }); it('rejects bad config', () => { - const jwksHandler = new JWKSHandler(); - expect(() => { - jwksHandler.add( + return new JWKSHandler([ new ConfigReader({ - options: { - url: 'https://exampl e.com/jwks', - }, + options: { url: 'https://exampl e.com/jwks' }, }), - ); + ]); }).toThrow('Illegal JWKS URL, must be a set of non-space characters'); expect(() => { - jwksHandler.add( + return new JWKSHandler([ new ConfigReader({ options: { url: 'https://example.com/jwks\n', }, }), - ); + ]); }).toThrow('Illegal JWKS URL, must be a set of non-space characters'); }); it('gracefully handles no added tokens', async () => { - const handler = new JWKSHandler(); + const handler = new JWKSHandler([]); await expect(handler.verifyToken('ghi')).resolves.toBeUndefined(); }); @@ -221,9 +215,7 @@ describe('JWKSHandler', () => { subjectPrefix: 'custom-prefix', }, }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(validEntry)); + const jwksHandler = new JWKSHandler([new ConfigReader(validEntry)]); const token = await factory.issueToken({ claims: { sub: mockSubject }, @@ -237,15 +229,14 @@ describe('JWKSHandler', () => { }); it('carries over access restrictions', async () => { - const jwksHandler = new JWKSHandler(); - jwksHandler.add( + const jwksHandler = new JWKSHandler([ new ConfigReader({ options: { url: `${mockBaseUrl}/.well-known/jwks.json`, }, accessRestrictions: [{ plugin: 'scaffolder', permission: 'do.it' }], }), - ); + ]); const token = await factory.issueToken({ claims: { sub: mockSubject } }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts index 49167bcd35..5f7fcb4127 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts @@ -38,7 +38,12 @@ export class JWKSHandler implements TokenHandler { allAccessRestrictions?: AccessRestrictionsMap; }> = []; - add(config: Config) { + constructor(configs: Config[]) { + for (const config of configs) { + this.add(config); + } + } + private add(config: Config) { if (!config.getString('options.url').match(/^\S+$/)) { throw new Error( 'Illegal JWKS URL, must be a set of non-space characters', diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts index c674e46ec9..4892d0a21a 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts @@ -21,7 +21,6 @@ import { DateTime } from 'luxon'; import { LegacyTokenHandler } from './legacy'; describe('LegacyTokenHandler', () => { - const tokenHandler = new LegacyTokenHandler(); const key1 = randomBytes(24); const key2 = randomBytes(24); const key3 = randomBytes(24); @@ -36,7 +35,7 @@ describe('LegacyTokenHandler', () => { }), ); - tokenHandler.add( + const configs = [ new ConfigReader({ options: { secret: key1.toString('base64'), @@ -44,8 +43,7 @@ describe('LegacyTokenHandler', () => { }, accessRestrictions: [{ plugin: 'scaffolder' }], }), - ); - tokenHandler.add( + new ConfigReader({ options: { secret: key2.toString('base64'), @@ -55,12 +53,14 @@ describe('LegacyTokenHandler', () => { { plugin: 'catalog', permission: 'catalog.entity.read' }, ], }), - ); - tokenHandler.addOld( - new ConfigReader({ - secret: key3.toString('base64'), - }), - ); + { + legacy: true, + config: new ConfigReader({ + secret: key3.toString('base64'), + }), + }, + ]; + const tokenHandler = new LegacyTokenHandler(configs); it('should verify valid tokens', async () => { const token1 = await new SignJWT({ @@ -163,94 +163,113 @@ describe('LegacyTokenHandler', () => { }); it('rejects bad config', () => { - const handler = new LegacyTokenHandler(); + const handler = new LegacyTokenHandler([]); // new style add, bad secrets - expect(() => - handler.add( - new ConfigReader({ options: { _missingsecret: true, subject: 'ok' } }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ + options: { _missingsecret: true, subject: 'ok' }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Missing required config value at 'options.secret' in 'mock-config'"`, ); - expect(() => - handler.add(new ConfigReader({ options: { secret: '', subject: 'ok' } })), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ options: { secret: '', subject: 'ok' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.secret' in 'mock-config', got empty-string, wanted string"`, ); - expect(() => - handler.add( - new ConfigReader({ options: { secret: 'has spaces', subject: 'ok' } }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ + options: { secret: 'has spaces', subject: 'ok' }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal secret, must be a valid base64 string"`, ); - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'hasnewline\n', subject: 'ok' }, - }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ + options: { secret: 'hasnewline\n', subject: 'ok' }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal secret, must be a valid base64 string"`, ); - expect(() => - handler.add(new ConfigReader({ options: { secret: 3, subject: 'ok' } })), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ options: { secret: 3, subject: 'ok' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.secret' in 'mock-config', got number, wanted string"`, ); // new style add, bad subjects - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', _missingsubject: true }, - }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ + options: { secret: 'b2s=', _missingsubject: true }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Missing required config value at 'options.subject' in 'mock-config'"`, ); - expect(() => - handler.add( - new ConfigReader({ options: { secret: 'b2s=', subject: '' } }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ options: { secret: 'b2s=', subject: '' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.subject' in 'mock-config', got empty-string, wanted string"`, ); - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', subject: 'has spaces' }, - }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ + options: { secret: 'b2s=', subject: 'has spaces' }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', subject: 'hasnewline\n' }, - }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ + options: { secret: 'b2s=', subject: 'hasnewline\n' }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); - expect(() => - handler.add( - new ConfigReader({ options: { secret: 'b2s=', subject: 3 } }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ options: { secret: 'b2s=', subject: 3 } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.subject' in 'mock-config', got number, wanted string"`, ); // new style add, bad access restrictions - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', subject: 'subject' }, - accessRestrictions: [{ plugin: ['a'] }], - }), - ), + expect( + () => + new LegacyTokenHandler([ + new ConfigReader({ + options: { secret: 'b2s=', subject: 'subject' }, + accessRestrictions: [{ plugin: ['a'] }], + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'accessRestrictions[0].plugin' in 'mock-config', got array, wanted string"`, ); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts index 09d759a09e..8be0de19e4 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts @@ -19,6 +19,10 @@ import { base64url, decodeJwt, decodeProtectedHeader, jwtVerify } from 'jose'; import { readAccessRestrictionsFromConfig } from './helpers'; import { AccessRestrictionsMap, TokenHandler } from './types'; +export type LegacyConfigWrapper = { + legacy: boolean; + config: Config; +}; /** * Handles `type: legacy` access. * @@ -33,6 +37,16 @@ export class LegacyTokenHandler implements TokenHandler { }; }>(); + constructor(configs: (Config | LegacyConfigWrapper)[]) { + for (const config of configs) { + if (isLegacy(config)) { + this.addOld(config.config); + continue; + } + this.add(config); + } + } + add(config: Config) { const allAccessRestrictions = readAccessRestrictionsFromConfig(config); this.#doAdd( @@ -119,3 +133,9 @@ export class LegacyTokenHandler implements TokenHandler { return undefined; } } + +function isLegacy( + config: Config | LegacyConfigWrapper, +): config is LegacyConfigWrapper { + return (config as LegacyConfigWrapper).legacy === true; +} diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts index a5945daba9..cf1c1a46ac 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts @@ -19,21 +19,19 @@ import { StaticTokenHandler } from './static'; describe('StaticTokenHandler', () => { it('accepts any of the added list of tokens', async () => { - const handler = new StaticTokenHandler(); - handler.add( + const configs = [ new ConfigReader({ options: { token: 'abcabcabc', subject: 'one' }, accessRestrictions: [{ plugin: 'scaffolder' }], }), - ); - handler.add( new ConfigReader({ options: { token: 'defdefdef', subject: 'two' }, accessRestrictions: [ { plugin: 'catalog', permission: 'catalog.entity.read' }, ], }), - ); + ]; + const handler = new StaticTokenHandler(configs); const accessRestrictionsOne = new Map(Object.entries({ scaffolder: {} })); const accessRestrictionsTwo = new Map( Object.entries({ @@ -55,95 +53,108 @@ describe('StaticTokenHandler', () => { }); it('gracefully handles no added tokens', async () => { - const handler = new StaticTokenHandler(); + const handler = new StaticTokenHandler([]); await expect(handler.verifyToken('ghi')).resolves.toBeUndefined(); }); it('rejects bad config', () => { - const handler = new StaticTokenHandler(); - - expect(() => - handler.add( - new ConfigReader({ options: { _missingtoken: true, subject: 'ok' } }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ options: { _missingtoken: true, subject: 'ok' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Missing required config value at 'options.token' in 'mock-config'"`, ); - expect(() => - handler.add(new ConfigReader({ options: { token: '', subject: 'ok' } })), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ options: { token: '', subject: 'ok' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.token' in 'mock-config', got empty-string, wanted string"`, ); - expect(() => - handler.add( - new ConfigReader({ options: { token: 'has spaces', subject: 'ok' } }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ options: { token: 'has spaces', subject: 'ok' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be a set of non-space characters"`, ); - expect(() => - handler.add( - new ConfigReader({ - options: { - token: 'hasnewlinebutislongenough\n', - subject: 'ok', - }, - }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ + options: { + token: 'hasnewlinebutislongenough\n', + subject: 'ok', + }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be a set of non-space characters"`, ); - expect(() => - handler.add( - new ConfigReader({ options: { token: 'short', subject: 'ok' } }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ options: { token: 'short', subject: 'ok' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be at least 8 characters length"`, ); - expect(() => - handler.add(new ConfigReader({ options: { token: 3, subject: 'ok' } })), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ options: { token: 3, subject: 'ok' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.token' in 'mock-config', got number, wanted string"`, ); - expect(() => - handler.add( - new ConfigReader({ - options: { token: 'validtoken', _missingsubject: true }, - }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ + options: { token: 'validtoken', _missingsubject: true }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Missing required config value at 'options.subject' in 'mock-config'"`, ); - expect(() => - handler.add( - new ConfigReader({ options: { token: 'validtoken', subject: '' } }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ options: { token: 'validtoken', subject: '' } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.subject' in 'mock-config', got empty-string, wanted string"`, ); - expect(() => - handler.add( - new ConfigReader({ - options: { token: 'validtoken', subject: 'has spaces' }, - }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ + options: { token: 'validtoken', subject: 'has spaces' }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); - expect(() => - handler.add( - new ConfigReader({ - options: { token: 'validtoken', subject: 'hasnewline\n' }, - }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ + options: { token: 'validtoken', subject: 'hasnewline\n' }, + }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); - expect(() => - handler.add( - new ConfigReader({ options: { token: 'validtoken', subject: 3 } }), - ), + expect( + () => + new StaticTokenHandler([ + new ConfigReader({ options: { token: 'validtoken', subject: 3 } }), + ]), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'options.subject' in 'mock-config', got number, wanted string"`, ); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.ts index e003f0530b..7764569aca 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.ts @@ -34,7 +34,12 @@ export class StaticTokenHandler implements TokenHandler { } >(); - add(config: Config) { + constructor(configs: Config[]) { + for (const config of configs) { + this.add(config); + } + } + private add(config: Config) { const token = config.getString('options.token'); const subject = config.getString('options.subject'); const allAccessRestrictions = readAccessRestrictionsFromConfig(config); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index 6b882ef16b..c3d45fd0d7 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -15,7 +15,6 @@ */ import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; /** * @public @@ -31,7 +30,6 @@ export type AccessRestrictionsMap = Map< * It is used by the auth service to verify tokens and extract the subject. */ export interface TokenHandler { - add?(options: Config): TokenHandler; verifyToken(token: string): Promise< | { subject: string; diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index 96db6c0535..1b1a29a247 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -17,9 +17,10 @@ export { authServiceFactory, pluginTokenHandlerDecoratorServiceRef, - externalTokenHandlersServiceRef, } from './authServiceFactory'; +export { externalTokenTypeHandlersRef } from './external/ExternalTokenHandler'; + export type { TokenHandler } from './external/types'; export type { AccessRestrictionsMap } from './external/types'; From 65d382e272f66896623a5d611caf77d9fe772b52 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Thu, 15 May 2025 22:58:35 +0200 Subject: [PATCH 030/211] fix: update docs with new approach and export types Signed-off-by: Juan Pablo Garcia Ripa --- docs/auth/service-to-service-auth.md | 35 ++++++++++++++---- packages/backend-defaults/report-auth.api.md | 12 ++++-- .../auth/external/ExternalTokenHandler.ts | 37 ++++++++----------- .../src/entrypoints/auth/external/types.ts | 15 ++++++++ .../src/entrypoints/auth/index.ts | 2 +- 5 files changed, 67 insertions(+), 34 deletions(-) diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index 8e8ef6a183..4d8829d91d 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -414,7 +414,7 @@ Each entry has one or more of the following fields: ## Adding custom or logic for validation and issuing of tokens -The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenHandlersServiceRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. +The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenTypeHandlersRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. This is particularly useful when you want to add additional logic to the handler, such as logging or metrics or custom token validation. ### PluginTokenHandler decoration @@ -446,26 +446,47 @@ const decoratedPluginTokenHandler = createServiceFactory({ ### ExternalTokenHandler decoration -The `externalTokenHandlersServiceRef` can be used to add custom external token handlers to the default implementation. +The `externalTokenTypeHandlersRef` can be used to add custom external token handlers to the default implementation. -The returned object should be a map of token custom types and their handler factories. The object keys will be matched to the configured `externalAccess` type in the app-config, calling the factory function with the config object for that type. Custom token handlers should implement the `TokenHandler` interface, which provides methods for verifying tokens. +The returned object from the factory function must have a `type` property which is used to identify the handler. The `factory` method is called with an array of `Config` objects, for the given type. The factory method can return a single handler or an array of handlers. The handlers are then used to handle the token for the given type. -For example, to add a custom token handler for a type called 'custom': +For example, if we whant to add a custom external token handler for the `custom` type: + +our config would look like this: + +```yaml title="in e.g. app-config.production.yaml" +backend: + auth: + externalAccess: + - type: custom + options: + customOptions: additional-value + accessRestrictions: + - plugin: events + - type: custom + options: + customOptions: another-value + accessRestrictions: + - plugin: events +``` + +And we can implement the custom token handler like this: ```ts import { TokenHandler, - externalTokenHandlersServiceRef, + externalTokenTypeHandlersRef, } from '@backstage/backend-defaults/auth'; import { Config } from '@backstage/config'; import { createServiceFactory } from '@backstage/backend-plugin-api'; const customExternalTokenHandlers = createServiceFactory({ - service: externalTokenHandlersServiceRef, + service: externalTokenTypeHandlersRef, deps: {}, async factory() { return { - custom: (config: Config) => new CustomTokenHandler(config); + type: 'custom', + factory: (configs: Config[]) => new CustomTokenHandler(configs), // can return a simple handler or an array of handlers }; }, }); diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index bcc5c4a073..2a80a59d2d 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -24,10 +24,7 @@ export const authServiceFactory: ServiceFactory< // @public export const externalTokenTypeHandlersRef: ServiceRef< - { - type: string; - factory: (config: Config[]) => TokenHandler; - }, + TokenTypeHandler, 'plugin', 'multiton' >; @@ -74,5 +71,12 @@ export interface TokenHandler { >; } +// @public +export interface TokenTypeHandler { + factory: (configs: Config[]) => TokenHandler | TokenHandler[]; + // (undocumented) + type: string; +} + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts index cc93b7b182..c3fab70026 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts @@ -27,32 +27,12 @@ import { JWKSHandler } from './jwks'; import { TokenHandler } from './types'; import { Config } from '@backstage/config'; import { groupBy } from 'lodash'; +import { TokenTypeHandler } from './types'; const NEW_CONFIG_KEY = 'backend.auth.externalAccess'; const OLD_CONFIG_KEY = 'backend.auth.keys'; let loggedDeprecationWarning = false; -/** - * @public - * This service is used to decorate the default plugin token handler with custom logic. - */ -export const externalTokenTypeHandlersRef = createServiceRef<{ - type: string; - factory: (config: Config[]) => TokenHandler; -}>({ - id: 'core.auth.externalTokenHandlers', - multiton: true, - // defaultFactory // :pepe-think: seems like is not possible to use defaultFactory with multiton -}); - -type TokenTypeHandler = { - type: string; - /** - * A factory function that takes all token configuration for a given type - * and returns a TokenHandler or an array of TokenHandlers. - */ - factory: (config: Config[]) => TokenHandler | TokenHandler[]; -}; type LegacyTokenTypeHandler = { type: 'legacy'; /** @@ -60,10 +40,23 @@ type LegacyTokenTypeHandler = { * and returns a TokenHandler or an array of TokenHandlers. */ factory: ( - config: (Config | LegacyConfigWrapper)[], + configs: ( + | import('@backstage/config').Config + | { legacy: true; config: import('@backstage/config').Config } + )[], ) => TokenHandler | TokenHandler[]; }; +/** + * @public + * This service is used to decorate the default plugin token handler with custom logic. + */ +export const externalTokenTypeHandlersRef = createServiceRef({ + id: 'core.auth.externalTokenHandlers', + multiton: true, + // defaultFactory // :pepe-think: seems like is not possible to use defaultFactory with multiton +}); + const defaultHandlers: (TokenTypeHandler | LegacyTokenTypeHandler)[] = [ { type: 'static', diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index c3d45fd0d7..5e26b66da5 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -38,3 +38,18 @@ export interface TokenHandler { | undefined >; } + +/** + * @public + * This interface is used to handle external tokens. + */ +export interface TokenTypeHandler { + type: string; + /** + * A factory function that takes all token configuration for a given type + * and returns a TokenHandler or an array of TokenHandlers. + */ + factory: ( + configs: import('@backstage/config').Config[], + ) => TokenHandler | TokenHandler[]; +} diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index 1b1a29a247..335fc2f62c 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -21,7 +21,7 @@ export { export { externalTokenTypeHandlersRef } from './external/ExternalTokenHandler'; -export type { TokenHandler } from './external/types'; +export type { TokenHandler, TokenTypeHandler } from './external/types'; export type { AccessRestrictionsMap } from './external/types'; export type { PluginTokenHandler } from './plugin/PluginTokenHandler'; From 79336700729da563bbdf6798a602a3c42e1609ad Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Wed, 9 Jul 2025 22:41:36 +0200 Subject: [PATCH 031/211] fix typos in docs Signed-off-by: Juan Pablo Garcia Ripa --- docs/auth/service-to-service-auth.md | 4 ++-- docs/backend-system/architecture/03-services.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index 4d8829d91d..322edbe6b6 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -444,13 +444,13 @@ const decoratedPluginTokenHandler = createServiceFactory({ }); ``` -### ExternalTokenHandler decoration +### Custom ExternalTokenHandler The `externalTokenTypeHandlersRef` can be used to add custom external token handlers to the default implementation. The returned object from the factory function must have a `type` property which is used to identify the handler. The `factory` method is called with an array of `Config` objects, for the given type. The factory method can return a single handler or an array of handlers. The handlers are then used to handle the token for the given type. -For example, if we whant to add a custom external token handler for the `custom` type: +For example, if we want to add a custom external token handler for the `custom` type: our config would look like this: diff --git a/docs/backend-system/architecture/03-services.md b/docs/backend-system/architecture/03-services.md index 3b168b81e3..f6722496e5 100644 --- a/docs/backend-system/architecture/03-services.md +++ b/docs/backend-system/architecture/03-services.md @@ -225,7 +225,7 @@ This allows you to provide more advanced options for the service implementation ## Multiton -By default the service reference will point to a singleton instance of the service. This meand if a new service factory uses this reference will override the previous one. This is the most common use-case, but in some cases you may want to have multiple instances of the same service. +By default the service reference will point to a singleton instance of the service. This mean if a new service factory uses this reference will override the previous one. This is the most common use-case, but in some cases you may want to have multiple instances of the same service. For some services, is desirable to extend the functionality instead of overriding it. For example, some services could have many handler to address a specific event, and you may want to add a new handler instead of overriding the previous one. In this case, you can use the `multiton` option when creating the service reference: ```ts @@ -242,7 +242,7 @@ export const fooServiceRef = createServiceRef({ }); ``` -When adding this serviceRef ad dependency to a factory, the factory will receive an array of instances instead of a single instance: +When adding this `serviceRef` as a dependency to a factory, the factory will receive an array of instances instead of a single instance: ```ts deps: {fooServices: fooServiceRef}, From 445baebdedc78a8983853c13f55f56766e09df23 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sat, 6 Sep 2025 19:02:05 +0200 Subject: [PATCH 032/211] refactor: rename ExternalTokenHandler to ExternalAuthTokenHandler Signed-off-by: Juan Pablo Garcia Ripa --- ...ernalTokenHandler.test.ts => ExternalAuthTokenHandler.test.ts} | 0 .../{ExternalTokenHandler.ts => ExternalAuthTokenHandler.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename packages/backend-defaults/src/entrypoints/auth/external/{ExternalTokenHandler.test.ts => ExternalAuthTokenHandler.test.ts} (100%) rename packages/backend-defaults/src/entrypoints/auth/external/{ExternalTokenHandler.ts => ExternalAuthTokenHandler.ts} (100%) diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts similarity index 100% rename from packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts rename to packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts similarity index 100% rename from packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts rename to packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts From 95396a44f9b6ae7c12f2886751a4ec1049993e3b Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sat, 6 Sep 2025 19:03:36 +0200 Subject: [PATCH 033/211] feat: simplify external token handler API - Remove accessRestrictions from individual handlers (now managed by framework) - Change API to evaluate configs individually for better performance Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/thirty-rules-press.md | 21 ++ docs/auth/service-to-service-auth.md | 100 +++++- packages/backend-defaults/report-auth.api.md | 2 +- .../entrypoints/auth/DefaultAuthService.ts | 4 +- .../auth/authServiceFactory.test.ts | 73 ++-- .../entrypoints/auth/authServiceFactory.ts | 6 +- .../external/ExternalAuthTokenHandler.test.ts | 174 ++++------ .../auth/external/ExternalAuthTokenHandler.ts | 133 +++----- .../src/entrypoints/auth/external/helpers.ts | 8 +- .../entrypoints/auth/external/jwks.test.ts | 131 ++------ .../src/entrypoints/auth/external/jwks.ts | 109 +++--- .../entrypoints/auth/external/legacy.test.ts | 314 ++++++++---------- .../src/entrypoints/auth/external/legacy.ts | 174 ++++------ .../entrypoints/auth/external/static.test.ts | 196 ++++++----- .../src/entrypoints/auth/external/static.ts | 103 ++++-- .../src/entrypoints/auth/external/types.ts | 42 ++- .../src/entrypoints/auth/index.ts | 6 +- 17 files changed, 728 insertions(+), 868 deletions(-) diff --git a/.changeset/thirty-rules-press.md b/.changeset/thirty-rules-press.md index d9968317cb..760780eb84 100644 --- a/.changeset/thirty-rules-press.md +++ b/.changeset/thirty-rules-press.md @@ -3,3 +3,24 @@ --- Add a new `externalTokenHandlersServiceRef` to allow custom external token validations + +BREAKING CHANGE: The `backend.auth.keys` config has been removed. Please migrate to the new `backend.auth.externalAccess` config as described in the documentation: https://backstage.io/docs/auth/service-to-service-auth + +**Migration Example:** + +```yaml +# ❌ Old format (no longer supported) +backend: + auth: + keys: + - secret: your-secret-key + +# ✅ New format +backend: + auth: + externalAccess: + - type: static + options: + token: your-secret-key + subject: external:backstage-plugin # this is the current default for old keys +``` diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index 322edbe6b6..4ba094068e 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -255,10 +255,16 @@ backend: subject: legacy-scaffolder ``` -The old style keys config is also supported as an alternative, but please -consider using the new style above instead: +:::warning +The old style `backend.auth.keys` config is **no longer supported** and has been removed. +If you are still using this configuration, you must migrate to the new `backend.auth.externalAccess` format above. -```yaml title="in e.g. app-config.production.yaml" +You'll see an error like `"Unknown key 'backend.auth.keys'"` during Backstage startup if you haven't migrated yet. +::: + +To migrate from the old config format: + +```yaml title="❌ Old format (no longer supported)" backend: auth: keys: @@ -266,6 +272,22 @@ backend: - secret: my-secret-key-scaffolder ``` +Convert to: + +```yaml title="✅ New format" +backend: + auth: + externalAccess: + - type: legacy + options: + secret: my-secret-key-catalog + subject: external:backstage-plugin + - type: legacy + options: + secret: my-secret-key-scaffolder + subject: external:backstage-plugin +``` + The secrets must be any base64-encoded random data, but for security reasons should be sufficiently long so as not to be easy to guess by brute force. You can for example generate them on the command line: @@ -444,11 +466,17 @@ const decoratedPluginTokenHandler = createServiceFactory({ }); ``` -### Custom ExternalTokenHandler +### Adding custom ExternalTokenHandler The `externalTokenTypeHandlersRef` can be used to add custom external token handlers to the default implementation. -The returned object from the factory function must have a `type` property which is used to identify the handler. The `factory` method is called with an array of `Config` objects, for the given type. The factory method can return a single handler or an array of handlers. The handlers are then used to handle the token for the given type. +Your service factory must return an object with a `type` property that matches the token type in your configuration (e.g., 'custom', 'api-key'). When Backstage encounters tokens of this type, it calls your `initialize` method with all the configuration entries that match this type. Your factory can return either a single token handler or an array of handlers to process and validate these tokens. + +:::note Note + +During token verification, all the token handlers are tested. Consider this when adding many token handlers, as it may impact performance. + +::: For example, if we want to add a custom external token handler for the `custom` type: @@ -474,20 +502,72 @@ And we can implement the custom token handler like this: ```ts import { - TokenHandler, + ExternalTokenHandler, externalTokenTypeHandlersRef, + createExternalTokenHandler, } from '@backstage/backend-defaults/auth'; -import { Config } from '@backstage/config'; import { createServiceFactory } from '@backstage/backend-plugin-api'; const customExternalTokenHandlers = createServiceFactory({ service: externalTokenTypeHandlersRef, deps: {}, async factory() { - return { + return createExternalTokenHandler({ type: 'custom', - factory: (configs: Config[]) => new CustomTokenHandler(configs), // can return a simple handler or an array of handlers - }; + initialize({ options }) { + // Initialize your handler context from config + const customOptions = options.getString('customOptions'); + return { customOptions }; + }, + async verifyToken(token, context) { + // Your custom token validation logic here + // Return undefined if token is invalid + // Return { subject: 'your-subject' } if token is valid + + if (token === 'valid-token') { + return { subject: `custom:${context.customOptions}` }; + } + return undefined; + }, + }); + }, +}); +``` + +The `createExternalTokenHandler` helper simplifies creating external token handlers with the new API: + +- **`type`**: A string identifier for your token handler type that matches the config +- **`initialize`**: Called once for each config entry of this type, receives the config options and returns a context object that will be passed to `verifyToken` +- **`verifyToken`**: Called for each token verification with the token and context, returns the subject if valid or `undefined` if not + +```ts +// Example of a more complex handler with external API call +const apiTokenHandler = createExternalTokenHandler({ + type: 'api-validation', + initialize({ options }) { + const apiBaseUrl = options.getString('apiBaseUrl'); + const apiKey = options.getString('apiKey'); + return { apiBaseUrl, apiKey }; + }, + async verifyToken(token, { apiBaseUrl, apiKey }) { + try { + const response = await fetch(`${apiBaseUrl}/validate-token`, { + method: 'POST', + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ token }), + }); + + if (response.ok) { + const { userId } = await response.json(); + return { subject: `api:${userId}` }; + } + } catch (error) { + // Log error but don't throw - return undefined for invalid tokens + } + return undefined; }, }); ``` diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index 2a80a59d2d..176bdd16de 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -60,7 +60,7 @@ export const pluginTokenHandlerDecoratorServiceRef: ServiceRef< >; // @public -export interface TokenHandler { +export interface ExternalTokenHandler { // (undocumented) verifyToken(token: string): Promise< | { diff --git a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts index d8cb60e8b5..c2c4a064a3 100644 --- a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts +++ b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts @@ -25,7 +25,7 @@ import { import { AuthenticationError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; import { decodeJwt } from 'jose'; -import { ExternalTokenHandler } from './external/ExternalTokenHandler'; +import { ExternalAuthTokenManager } from './external/export class ExternalAuthTokenManager {'; import { createCredentialsWithNonePrincipal, createCredentialsWithServicePrincipal, @@ -41,7 +41,7 @@ export class DefaultAuthService implements AuthService { constructor( private readonly userTokenHandler: UserTokenHandler, private readonly pluginTokenHandler: PluginTokenHandler, - private readonly externalTokenHandler: ExternalTokenHandler, + private readonly externalTokenHandler: ExternalAuthTokenManager, private readonly pluginId: string, private readonly disableDefaultAuthPolicy: boolean, private readonly pluginKeySource: PluginKeySource, diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index b808973016..b6d5beefa2 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -30,9 +30,9 @@ import { setupServer } from 'msw/node'; import { toInternalBackstageCredentials } from './helpers'; import { PluginTokenHandler } from './plugin/PluginTokenHandler'; import { createServiceFactory } from '@backstage/backend-plugin-api'; -import { AccessRestrictionsMap, TokenHandler } from './external/types'; -import { Config } from '@backstage/config'; -import { externalTokenTypeHandlersRef } from './external/ExternalTokenHandler'; + +import { externalTokenTypeHandlersRef } from './external/ExternalAuthTokenHandler'; +import { createExternalTokenHandler } from './external/helpers'; const server = setupServer(); @@ -44,7 +44,7 @@ const mockDeps = [ backend: { baseUrl: 'http://localhost', auth: { - keys: [{ secret: 'abc' }], + // keys: [{ secret: 'abc' }], externalAccess: [ { type: 'static', @@ -460,7 +460,6 @@ describe('authServiceFactory', () => { describe('add custom ExternalTokenHandler', () => { it('should allow custom logic to be injected into the plugin token handler', async () => { const customLogic = jest.fn(); - const customAddEntry = jest.fn(); const customConfig = jest.fn(); const deps = [ discoveryServiceFactory, @@ -469,7 +468,7 @@ describe('authServiceFactory', () => { backend: { baseUrl: 'http://localhost', auth: { - keys: [{ secret: 'abc' }], + // keys: [{ secret: 'abc' }w], externalAccess: [ { type: 'static', @@ -502,37 +501,30 @@ describe('authServiceFactory', () => { }), ]; - class CustomHandler implements TokenHandler { - constructor(options: Config[]) { - for (const option of options) { - customAddEntry(option); - } - } - async verifyToken(token: string): Promise< - | { - subject: string; - allAccessRestrictions?: AccessRestrictionsMap; - } - | undefined - > { + const customExternalTokenHandler = createExternalTokenHandler<{ + [`custom-config`]: string; + foo: string; + }>({ + type: 'custom', + initialize({ options }) { + const customConfigValue = options.getString('custom-config'); + const foo = options.getString('foo'); + customConfig(customConfigValue); + return { [`custom-config`]: customConfigValue, foo }; + }, + async verifyToken(token, ctx) { customLogic(token); return { - subject: 'foo', + subject: ctx.foo, }; - } - } + }, + }); const customPluginTokenHandler = createServiceFactory({ service: externalTokenTypeHandlersRef, deps: {}, async factory() { - return { - type: 'custom', - factory: (configs: Config[]) => { - customConfig(configs); - return new CustomHandler(configs); - }, - }; + return customExternalTokenHandler; }, }); const tester = ServiceFactoryTester.from(authServiceFactory, { @@ -541,29 +533,8 @@ describe('authServiceFactory', () => { const searchAuth = await tester.getSubject('search'); await searchAuth.authenticate('custom-token'); - expect(customAddEntry).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - options: expect.objectContaining({ - [`custom-config`]: 'custom-config', - foo: 'bar', - }), - }), - }), - ); expect(customLogic).toHaveBeenCalledWith('custom-token'); - expect(customConfig).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - data: expect.objectContaining({ - options: expect.objectContaining({ - [`custom-config`]: 'custom-config', - foo: 'bar', - }), - }), - }), - ]), - ); + expect(customConfig).toHaveBeenCalledWith('custom-config'); }); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index 43a3c9da50..34fc3b51ef 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -21,9 +21,9 @@ import { } from '@backstage/backend-plugin-api'; import { DefaultAuthService } from './DefaultAuthService'; import { - ExternalTokenHandler, + ExternalAuthTokenManager, externalTokenTypeHandlersRef, -} from './external/ExternalTokenHandler'; +} from './external/ExternalAuthTokenHandler'; import { DefaultPluginTokenHandler, PluginTokenHandler, @@ -107,7 +107,7 @@ export const authServiceFactory = createServiceFactory({ }), ); - const externalTokens = ExternalTokenHandler.create({ + const externalTokens = ExternalAuthTokenManager.create({ ownPluginId: plugin.getId(), config, logger, diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts index 3348d84e93..d97875d228 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts @@ -15,8 +15,9 @@ */ import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; -import { ExternalTokenHandler } from './ExternalTokenHandler'; -import { TokenHandler } from './types'; +import { ExternalAuthTokenManager } from './ExternalAuthTokenHandler'; +import { createExternalTokenHandler } from './helpers'; +import { AccessRestrictionsMap, ExternalTokenHandler } from './types'; import { mockServices, registerMswTestHooks, @@ -84,25 +85,51 @@ describe('ExternalTokenHandler', () => { registerMswTestHooks(server); it('skips over inner handlers that do not match, and applies plugin restrictions', async () => { - const handler1: TokenHandler = { + const handler1: ExternalTokenHandler = createExternalTokenHandler({ + type: 'type1', + initialize: jest.fn().mockResolvedValue(undefined), verifyToken: jest.fn().mockResolvedValue(undefined), - }; + }); - const handler2: TokenHandler = { - verifyToken: jest.fn().mockResolvedValue({ - subject: 'sub', - allAccessRestrictions: new Map( - Object.entries({ - plugin1: { - permissionNames: ['do.it'], - } satisfies BackstagePrincipalAccessRestrictions, - }), - ), + const handler2: ExternalTokenHandler = + createExternalTokenHandler({ + type: 'type2', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }); + + const accessRestrictions: AccessRestrictionsMap = new Map( + Object.entries({ + plugin1: { + permissionNames: ['do.it'], + } satisfies BackstagePrincipalAccessRestrictions, }), - }; + ); - const plugin1 = new ExternalTokenHandler('plugin1', [handler1, handler2]); - const plugin2 = new ExternalTokenHandler('plugin2', [handler1, handler2]); + const plugin1 = new ExternalAuthTokenManager('plugin1', [ + { + context: undefined, + handler: handler1, + }, + { + context: undefined, + handler: handler2, + allAccessRestrictions: accessRestrictions, + }, + ]); + const plugin2 = new ExternalAuthTokenManager('plugin2', [ + { + context: undefined, + handler: handler1, + }, + { + context: undefined, + handler: handler2, + allAccessRestrictions: accessRestrictions, + }, + ]); await expect(plugin1.verifyToken('token')).resolves.toEqual({ subject: 'sub', @@ -133,7 +160,7 @@ describe('ExternalTokenHandler', () => { ), ); - const handler = ExternalTokenHandler.create({ + const handler = ExternalAuthTokenManager.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ @@ -222,12 +249,10 @@ describe('ExternalTokenHandler', () => { ), ); - const customHandler: TokenHandler = { - verifyToken: jest.fn(), - }; - const customHandlerFactory = jest.fn(() => customHandler); + const verifyMock = jest.fn().mockResolvedValue({}); + const initializeMock = jest.fn().mockReturnValue({ context: 'a' }); - const handler = ExternalTokenHandler.create({ + const handler = ExternalAuthTokenManager.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ @@ -252,30 +277,23 @@ describe('ExternalTokenHandler', () => { }, }), externalTokenHandlers: [ - { + createExternalTokenHandler({ type: 'internal-custom', - factory: customHandlerFactory, - }, + initialize: initializeMock, + verifyToken: verifyMock, + }), ], }); - expect(customHandlerFactory).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - data: { - type: 'internal-custom', - options: { - issuer: 'my-company', - subject: 'internal-subject', - audience: 'backstage', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], - }, - }), - ]), - ); + expect(initializeMock).toHaveBeenCalledWith({ + options: expect.objectContaining({ + data: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + }), + }); const customToken = await factory.issueToken({ claims: { sub: 'internal-subject' }, @@ -283,11 +301,11 @@ describe('ExternalTokenHandler', () => { await handler.verifyToken(customToken); - expect(customHandler.verifyToken).toHaveBeenCalled(); + expect(verifyMock).toHaveBeenCalledWith(customToken, { context: 'a' }); }); it('should fail if config contains types not declared', async () => { const createHandler = () => - ExternalTokenHandler.create({ + ExternalAuthTokenManager.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ @@ -314,13 +332,13 @@ describe('ExternalTokenHandler', () => { }); expect(createHandler).toThrowErrorMatchingInlineSnapshot( - `"Unknown type(s) 'internal-custom' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks'"`, + `"Unknown type 'internal-custom' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks'"`, ); }); it('should show valid custom types in errors', async () => { const createHandler = () => - ExternalTokenHandler.create({ + ExternalAuthTokenManager.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ @@ -345,68 +363,18 @@ describe('ExternalTokenHandler', () => { }, }), externalTokenHandlers: [ - { + createExternalTokenHandler({ type: 'internal-custom', - factory: () => ({ - verifyToken: jest.fn(), + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', }), - }, + }), ], }); expect(createHandler).toThrowErrorMatchingInlineSnapshot( - `"Unknown type(s) 'internal-custom-invalid' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, - ); - }); - it('should show all invalid config types', async () => { - const createHandler = () => - ExternalTokenHandler.create({ - ownPluginId: 'catalog', - logger: mockServices.logger.mock(), - config: mockServices.rootConfig({ - data: { - backend: { - auth: { - externalAccess: [ - { - type: 'internal-custom-invalid', - options: { - issuer: 'my-company', - subject: 'internal-subject', - audience: 'backstage', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], - }, - { - type: 'internal-custom-invalid-2', - options: { - issuer: 'my-company', - subject: 'internal-subject', - audience: 'backstage', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], - }, - ], - }, - }, - }, - }), - externalTokenHandlers: [ - { - type: 'internal-custom', - factory: () => ({ - verifyToken: jest.fn(), - }), - }, - ], - }); - - expect(createHandler).toThrowErrorMatchingInlineSnapshot( - `"Unknown type(s) 'internal-custom-invalid, internal-custom-invalid-2' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, + `"Unknown type 'internal-custom-invalid' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts index c3fab70026..bf57ad42f4 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts @@ -21,85 +21,82 @@ import { RootConfigService, } from '@backstage/backend-plugin-api'; import { NotAllowedError } from '@backstage/errors'; -import { LegacyConfigWrapper, LegacyTokenHandler } from './legacy'; -import { StaticTokenHandler } from './static'; -import { JWKSHandler } from './jwks'; -import { TokenHandler } from './types'; -import { Config } from '@backstage/config'; -import { groupBy } from 'lodash'; -import { TokenTypeHandler } from './types'; +import { legacyTokenHandler } from './legacy'; +import { staticTokenHandler } from './static'; +import { jwksTokenHandler } from './jwks'; +import { AccessRestrictionsMap, ExternalTokenHandler } from './types'; +import { readAccessRestrictionsFromConfig } from './helpers'; const NEW_CONFIG_KEY = 'backend.auth.externalAccess'; const OLD_CONFIG_KEY = 'backend.auth.keys'; let loggedDeprecationWarning = false; -type LegacyTokenTypeHandler = { - type: 'legacy'; - /** - * A factory function that takes all token configuration for a given type - * and returns a TokenHandler or an array of TokenHandlers. - */ - factory: ( - configs: ( - | import('@backstage/config').Config - | { legacy: true; config: import('@backstage/config').Config } - )[], - ) => TokenHandler | TokenHandler[]; -}; - /** * @public * This service is used to decorate the default plugin token handler with custom logic. */ -export const externalTokenTypeHandlersRef = createServiceRef({ +export const externalTokenTypeHandlersRef = createServiceRef< + ExternalTokenHandler +>({ id: 'core.auth.externalTokenHandlers', multiton: true, // defaultFactory // :pepe-think: seems like is not possible to use defaultFactory with multiton }); -const defaultHandlers: (TokenTypeHandler | LegacyTokenTypeHandler)[] = [ - { - type: 'static', - factory: configs => new StaticTokenHandler(configs), - }, - { - type: 'legacy', - factory: (configs: (Config | { legacy: true; config: Config })[]) => - new LegacyTokenHandler(configs), - }, - { - type: 'jwks', - factory: configs => new JWKSHandler(configs), - }, +const defaultHandlers: ExternalTokenHandler[] = [ + staticTokenHandler, + legacyTokenHandler, + jwksTokenHandler, ]; +type ContextMapEntry = { + context: T; + handler: ExternalTokenHandler; + allAccessRestrictions?: AccessRestrictionsMap; +}; /** * Handles all types of external caller token types (i.e. not Backstage user * tokens, nor Backstage backend plugin tokens). * * @internal */ -export class ExternalTokenHandler { +export class ExternalAuthTokenManager { static create(options: { ownPluginId: string; config: RootConfigService; logger: LoggerService; - externalTokenHandlers?: TokenTypeHandler[]; - }): ExternalTokenHandler { + externalTokenHandlers?: ExternalTokenHandler[]; + }): ExternalAuthTokenManager { const { ownPluginId, config, - logger, externalTokenHandlers: customHandlers, } = options; const handlersTypes = [...defaultHandlers, ...(customHandlers ?? [])]; const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? []; - const handlerConfigByType: Record & { - legacy?: (Config | LegacyConfigWrapper)[]; - } = groupBy(handlerConfigs, (handlerConfig: Config) => - handlerConfig.getString('type'), + const contexts: ContextMapEntry[] = handlerConfigs.map( + handlerConfig => { + const type = handlerConfig.getString('type'); + + const handler = handlersTypes.find(h => h.type === type); + if (!handler) { + const valid = handlersTypes.map(h => `'${h.type}'`).join(', '); + throw new Error( + `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, + ); + } + return { + context: handler.initialize({ + options: handlerConfig.getConfig('options'), + }), + handler, + allAccessRestrictions: handlerConfig + ? readAccessRestrictionsFromConfig(handlerConfig) + : undefined, + }; + }, ); // Load the old keys too @@ -107,45 +104,22 @@ export class ExternalTokenHandler { if (legacyConfigs.length && !loggedDeprecationWarning) { loggedDeprecationWarning = true; - logger.warn( - `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`, - ); - } - for (const handlerConfig of legacyConfigs) { - handlerConfigByType.legacy ??= []; - handlerConfigByType.legacy.push({ legacy: true, config: handlerConfig }); - } - - const invalidTypes = Object.keys(handlerConfigByType).filter( - type => !handlersTypes.some(handler => handler.type === type), - ); - - if (invalidTypes.length > 0) { - const valid = handlersTypes - .map(handler => `'${handler.type}'`) - .join(', '); + // :pepe-think: this message was here for more than a year, and replacing this simplifies things a lot throw new Error( - `Unknown type(s) '${invalidTypes.join( - ', ', - )}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, + `The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`, ); } - const handlers = handlersTypes.flatMap(handler => { - const configs = handlerConfigByType[handler.type] ?? []; - const handlerInstances = handler.factory(configs); - if (Array.isArray(handlerInstances)) { - return handlerInstances; - } - return [handlerInstances]; - }); - - return new ExternalTokenHandler(ownPluginId, handlers); + return new ExternalAuthTokenManager(ownPluginId, contexts); } constructor( private readonly ownPluginId: string, - private readonly handlers: TokenHandler[], + private readonly contexts: { + context: unknown; + handler: ExternalTokenHandler; + allAccessRestrictions?: AccessRestrictionsMap; + }[], ) {} async verifyToken(token: string): Promise< @@ -155,10 +129,9 @@ export class ExternalTokenHandler { } | undefined > { - for (const handler of this.handlers) { - const result = await handler.verifyToken(token); + for (const { handler, allAccessRestrictions, context } of this.contexts) { + const result = await handler.verifyToken(token, context); if (result) { - const { allAccessRestrictions, ...rest } = result; if (allAccessRestrictions) { const accessRestrictions = allAccessRestrictions.get( this.ownPluginId, @@ -173,12 +146,12 @@ export class ExternalTokenHandler { } return { - ...rest, + ...result, accessRestrictions, }; } - return rest; + return result; } } diff --git a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts index 4a01117e28..296ec2e72b 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { AccessRestrictionsMap } from './types'; +import { AccessRestrictionsMap, ExternalTokenHandler } from './types'; /** * Parses and returns the `accessRestrictions` configuration from an @@ -147,3 +147,9 @@ function readPermissionAttributes(externalAccessEntryConfig: Config) { return Object.keys(result).length ? result : undefined; } + +export function createExternalTokenHandler( + handler: ExternalTokenHandler, +): ExternalTokenHandler { + return handler; +} diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts index eefbce5093..778e5090bf 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts @@ -20,7 +20,7 @@ import { SignJWT, exportJWK, generateKeyPair } from 'jose'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { v4 as uuid } from 'uuid'; -import { JWKSHandler } from './jwks'; +import { jwksTokenHandler } from './jwks'; // Simplified copy of TokenFactory in @backstage/plugin-auth-backend interface AnyJWK extends Record { @@ -101,108 +101,45 @@ describe('JWKSHandler', () => { it('verifies token with valid entry', async () => { const validEntry = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: mockBaseUrl, - audience: 'backstage', - }, + url: `${mockBaseUrl}/.well-known/jwks.json`, + algorithm: 'RS256', + issuer: mockBaseUrl, + audience: 'backstage', }; - const jwksHandler = new JWKSHandler([new ConfigReader(validEntry)]); + const context = jwksTokenHandler.initialize({ + options: new ConfigReader(validEntry), + }); const token = await factory.issueToken({ claims: { sub: mockSubject }, }); - const result = await jwksHandler.verifyToken(token); + const result = await jwksTokenHandler.verifyToken(token, context); expect(result).toEqual({ subject: `external:${mockSubject}` }); }); - it('skips invalid entry and continues verification', async () => { - const invalidEntry = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: ['fakeIssuer'], - audience: ['fakeAud'], - }, - }; - - const validEntry = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: ['multiple-issuers', mockBaseUrl], - audience: ['multiple-audiences', 'backstage'], - }, - }; - const jwksHandler = new JWKSHandler([ - new ConfigReader(invalidEntry), - new ConfigReader(validEntry), - ]); - - const token = await factory.issueToken({ - claims: { sub: mockSubject }, - }); - - const result = await jwksHandler.verifyToken(token); - - expect(result).toEqual({ subject: `external:${mockSubject}` }); - }); - - it('returns undefined if no valid entry found', async () => { - const invalidEntry1 = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: 'wrong', - }, - }; - - const invalidEntry2 = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: ['HS256'], - audience: 'wrong', - }, - }; - const jwksHandler = new JWKSHandler([ - new ConfigReader(invalidEntry1), - new ConfigReader(invalidEntry2), - ]); - - const token = await factory.issueToken({ - claims: { sub: mockSubject }, - }); - - const result = await jwksHandler.verifyToken(token); - - expect(result).toBeUndefined(); - }); - it('rejects bad config', () => { expect(() => { - return new JWKSHandler([ - new ConfigReader({ - options: { url: 'https://exampl e.com/jwks' }, + return jwksTokenHandler.initialize({ + options: new ConfigReader({ + url: 'https://exampl e.com/jwks', }), - ]); + }); }).toThrow('Illegal JWKS URL, must be a set of non-space characters'); expect(() => { - return new JWKSHandler([ - new ConfigReader({ - options: { - url: 'https://example.com/jwks\n', - }, + return jwksTokenHandler.initialize({ + options: new ConfigReader({ + url: 'https://example.com/jwks\n', }), - ]); + }); }).toThrow('Illegal JWKS URL, must be a set of non-space characters'); }); it('gracefully handles no added tokens', async () => { - const handler = new JWKSHandler([]); - await expect(handler.verifyToken('ghi')).resolves.toBeUndefined(); + await expect( + jwksTokenHandler.verifyToken('ghi', {} as any), + ).resolves.toBeUndefined(); }); it('uses custom subject prefix if provided', async () => { @@ -215,38 +152,18 @@ describe('JWKSHandler', () => { subjectPrefix: 'custom-prefix', }, }; - const jwksHandler = new JWKSHandler([new ConfigReader(validEntry)]); + const context = jwksTokenHandler.initialize({ + options: new ConfigReader(validEntry.options), + }); const token = await factory.issueToken({ claims: { sub: mockSubject }, }); - const result = await jwksHandler.verifyToken(token); + const result = await jwksTokenHandler.verifyToken(token, context); expect(result).toEqual({ subject: `external:${validEntry.options.subjectPrefix}:${mockSubject}`, }); }); - - it('carries over access restrictions', async () => { - const jwksHandler = new JWKSHandler([ - new ConfigReader({ - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - }, - accessRestrictions: [{ plugin: 'scaffolder', permission: 'do.it' }], - }), - ]); - - const token = await factory.issueToken({ claims: { sub: mockSubject } }); - - await expect(jwksHandler.verifyToken(token)).resolves.toEqual({ - subject: `external:${mockSubject}`, - allAccessRestrictions: new Map( - Object.entries({ - scaffolder: { permissionNames: ['do.it'] }, - }), - ), - }); - }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts index 5f7fcb4127..892fcf2181 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts @@ -15,56 +15,40 @@ */ import { jwtVerify, createRemoteJWKSet, JWTVerifyGetKey } from 'jose'; -import { Config } from '@backstage/config'; import { + createExternalTokenHandler, readAccessRestrictionsFromConfig, readStringOrStringArrayFromConfig, } from './helpers'; -import { AccessRestrictionsMap, TokenHandler } from './types'; +import { AccessRestrictionsMap } from './types'; -/** - * Handles `type: jwks` access. - * - * @internal - */ -export class JWKSHandler implements TokenHandler { - #entries: Array<{ - algorithms?: string[]; - audiences?: string[]; - issuers?: string[]; - subjectPrefix?: string; - url: URL; - jwks: JWTVerifyGetKey; - allAccessRestrictions?: AccessRestrictionsMap; - }> = []; +type JWKSTokenContext = { + algorithms?: string[]; + audiences?: string[]; + issuers?: string[]; + subjectPrefix?: string; + url: URL; + jwks: JWTVerifyGetKey; + allAccessRestrictions?: AccessRestrictionsMap; +}; - constructor(configs: Config[]) { - for (const config of configs) { - this.add(config); - } - } - private add(config: Config) { - if (!config.getString('options.url').match(/^\S+$/)) { +export const jwksTokenHandler = createExternalTokenHandler({ + type: 'jwks', + initialize({ options }): JWKSTokenContext { + if (!options.getString('url').match(/^\S+$/)) { throw new Error( 'Illegal JWKS URL, must be a set of non-space characters', ); } - const algorithms = readStringOrStringArrayFromConfig( - config, - 'options.algorithm', - ); - const issuers = readStringOrStringArrayFromConfig(config, 'options.issuer'); - const audiences = readStringOrStringArrayFromConfig( - config, - 'options.audience', - ); - const subjectPrefix = config.getOptionalString('options.subjectPrefix'); - const url = new URL(config.getString('options.url')); + const algorithms = readStringOrStringArrayFromConfig(options, 'algorithm'); + const issuers = readStringOrStringArrayFromConfig(options, 'issuer'); + const audiences = readStringOrStringArrayFromConfig(options, 'audience'); + const subjectPrefix = options.getOptionalString('subjectPrefix'); + const url = new URL(options.getString('url')); const jwks = createRemoteJWKSet(url); - const allAccessRestrictions = readAccessRestrictionsFromConfig(config); - - this.#entries.push({ + const allAccessRestrictions = readAccessRestrictionsFromConfig(options); + return { algorithms, audiences, issuers, @@ -72,34 +56,31 @@ export class JWKSHandler implements TokenHandler { subjectPrefix, url, allAccessRestrictions, - }); - return this; - } + }; + }, - async verifyToken(token: string) { - for (const entry of this.#entries) { - try { - const { - payload: { sub }, - } = await jwtVerify(token, entry.jwks, { - algorithms: entry.algorithms, - issuer: entry.issuers, - audience: entry.audiences, - }); + async verifyToken(token: string, context: JWKSTokenContext) { + try { + const { + payload: { sub }, + } = await jwtVerify(token, context.jwks, { + algorithms: context.algorithms, + issuer: context.issuers, + audience: context.audiences, + }); - if (sub) { - const prefix = entry.subjectPrefix - ? `external:${entry.subjectPrefix}:` - : 'external:'; - return { - subject: `${prefix}${sub}`, - allAccessRestrictions: entry.allAccessRestrictions, - }; - } - } catch { - continue; + if (sub) { + const prefix = context.subjectPrefix + ? `external:${context.subjectPrefix}:` + : 'external:'; + return { + subject: `${prefix}${sub}`, + allAccessRestrictions: context.allAccessRestrictions, + }; } + } catch { + return undefined; } return undefined; - } -} + }, +}); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts index 4892d0a21a..43160b46da 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts @@ -18,49 +18,27 @@ import { ConfigReader } from '@backstage/config'; import { randomBytes } from 'crypto'; import { SignJWT, importJWK } from 'jose'; import { DateTime } from 'luxon'; -import { LegacyTokenHandler } from './legacy'; +import { legacyTokenHandler } from './legacy'; describe('LegacyTokenHandler', () => { const key1 = randomBytes(24); const key2 = randomBytes(24); - const key3 = randomBytes(24); - const accessRestrictions1 = new Map( - Object.entries({ - scaffolder: {}, - }), - ); - const accessRestrictions2 = new Map( - Object.entries({ - catalog: { permissionNames: ['catalog.entity.read'] }, - }), - ); - const configs = [ - new ConfigReader({ - options: { - secret: key1.toString('base64'), - subject: 'key1', - }, - accessRestrictions: [{ plugin: 'scaffolder' }], - }), + const tokenHandler = legacyTokenHandler; - new ConfigReader({ - options: { - secret: key2.toString('base64'), - subject: 'key2', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], + const context1 = tokenHandler.initialize({ + options: new ConfigReader({ + secret: key1.toString('base64'), + subject: 'key1', }), - { - legacy: true, - config: new ConfigReader({ - secret: key3.toString('base64'), - }), - }, - ]; - const tokenHandler = new LegacyTokenHandler(configs); + }); + + const context2 = tokenHandler.initialize({ + options: new ConfigReader({ + secret: key2.toString('base64'), + subject: 'key2', + }), + }); it('should verify valid tokens', async () => { const token1 = await new SignJWT({ @@ -70,9 +48,8 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key1); - await expect(tokenHandler.verifyToken(token1)).resolves.toEqual({ + await expect(tokenHandler.verifyToken(token1, context1)).resolves.toEqual({ subject: 'key1', - allAccessRestrictions: accessRestrictions1, }); const token2 = await new SignJWT({ @@ -82,20 +59,8 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key2); - await expect(tokenHandler.verifyToken(token2)).resolves.toEqual({ + await expect(tokenHandler.verifyToken(token2, context2)).resolves.toEqual({ subject: 'key2', - allAccessRestrictions: accessRestrictions2, - }); - - const token3 = await new SignJWT({ - sub: 'backstage-server', - exp: DateTime.now().plus({ minutes: 1 }).toUnixInteger(), - }) - .setProtectedHeader({ alg: 'HS256' }) - .sign(key3); - - await expect(tokenHandler.verifyToken(token3)).resolves.toEqual({ - subject: 'external:backstage-plugin', }); }); @@ -107,10 +72,18 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key1); - await expect(tokenHandler.verifyToken(validToken)).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(validToken, context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(validToken, context2), + ).resolves.toBeUndefined(); await expect( - tokenHandler.verifyToken('statickeyblaaa'), + tokenHandler.verifyToken('statickeyblaaa', context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken('statickeyblaaa', context2), ).resolves.toBeUndefined(); const randomToken = await new SignJWT({ @@ -120,7 +93,10 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(randomBytes(24)); await expect( - tokenHandler.verifyToken(randomToken), + tokenHandler.verifyToken(randomToken, context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(randomToken, context2), ).resolves.toBeUndefined(); const mockPublicKey = { @@ -144,7 +120,10 @@ describe('LegacyTokenHandler', () => { .sign(await importJWK(mockPrivateKey)); await expect( - tokenHandler.verifyToken(keyWithWrongAlg), + tokenHandler.verifyToken(keyWithWrongAlg, context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(keyWithWrongAlg, context2), ).resolves.toBeUndefined(); }); @@ -157,151 +136,134 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key1); - await expect(tokenHandler.verifyToken(keyWithWrongExp)).rejects.toThrow( - /\"exp\" claim must be a number/, - ); + await expect( + tokenHandler.verifyToken(keyWithWrongExp, context1), + ).rejects.toThrow(/\"exp\" claim must be a number/); }); it('rejects bad config', () => { - const handler = new LegacyTokenHandler([]); + const handler = legacyTokenHandler; // new style add, bad secrets - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ - options: { _missingsecret: true, subject: 'ok' }, - }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.secret' in 'mock-config'"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ options: { secret: '', subject: 'ok' } }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.secret' in 'mock-config', got empty-string, wanted string"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ - options: { secret: 'has spaces', subject: 'ok' }, - }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal secret, must be a valid base64 string"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ - options: { secret: 'hasnewline\n', subject: 'ok' }, - }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal secret, must be a valid base64 string"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ options: { secret: 3, subject: 'ok' } }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.secret' in 'mock-config', got number, wanted string"`, - ); - - // new style add, bad subjects - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ - options: { secret: 'b2s=', _missingsubject: true }, - }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.subject' in 'mock-config'"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ options: { secret: 'b2s=', subject: '' } }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got empty-string, wanted string"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ - options: { secret: 'b2s=', subject: 'has spaces' }, - }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal subject, must be a set of non-space characters"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ - options: { secret: 'b2s=', subject: 'hasnewline\n' }, - }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal subject, must be a set of non-space characters"`, - ); - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ options: { secret: 'b2s=', subject: 3 } }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got number, wanted string"`, - ); - - // new style add, bad access restrictions - expect( - () => - new LegacyTokenHandler([ - new ConfigReader({ - options: { secret: 'b2s=', subject: 'subject' }, - accessRestrictions: [{ plugin: ['a'] }], - }), - ]), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'accessRestrictions[0].plugin' in 'mock-config', got array, wanted string"`, - ); - - // old style add expect(() => - handler.addOld(new ConfigReader({ secret: 'b2s=' })), - ).not.toThrow(); - expect(() => - handler.addOld(new ConfigReader({ _missingsecret: true })), + handler.initialize({ + options: new ConfigReader({ + _missingsecret: true, + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Missing required config value at 'secret' in 'mock-config'"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: '' })), + handler.initialize({ + options: new ConfigReader({ secret: '', subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'secret' in 'mock-config', got empty-string, wanted string"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: 'has spaces' })), + handler.initialize({ + options: new ConfigReader({ + secret: 'has spaces', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal secret, must be a valid base64 string"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: 'hasnewline\n' })), + handler.initialize({ + options: new ConfigReader({ + secret: 'hasnewline\n', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal secret, must be a valid base64 string"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: 3 })), + handler.initialize({ + options: new ConfigReader({ secret: 3, subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'secret' in 'mock-config', got number, wanted string"`, ); + + // new style add, bad subjects + expect(() => + handler.initialize({ + options: new ConfigReader({ + secret: 'b2s=', + _missingsubject: true, + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Missing required config value at 'subject' in 'mock-config'"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'b2s=', subject: '' }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'subject' in 'mock-config', got empty-string, wanted string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ + secret: 'b2s=', + subject: 'has spaces', + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal subject, must be a set of non-space characters"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ + secret: 'b2s=', + subject: 'hasnewline\n', + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal subject, must be a set of non-space characters"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'b2s=', subject: 3 }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'subject' in 'mock-config', got number, wanted string"`, + ); + + // // old style add + // expect(() => + // handler.addOld(new ConfigReader({ secret: 'b2s=' })), + // ).not.toThrow(); + // expect(() => + // handler.addOld(new ConfigReader({ _missingsecret: true })), + // ).toThrowErrorMatchingInlineSnapshot( + // `"Missing required config value at 'secret' in 'mock-config'"`, + // ); + // expect(() => + // handler.addOld(new ConfigReader({ secret: '' })), + // ).toThrowErrorMatchingInlineSnapshot( + // `"Invalid type in config for key 'secret' in 'mock-config', got empty-string, wanted string"`, + // ); + // expect(() => + // handler.addOld(new ConfigReader({ secret: 'has spaces' })), + // ).toThrowErrorMatchingInlineSnapshot( + // `"Illegal secret, must be a valid base64 string"`, + // ); + // expect(() => + // handler.addOld(new ConfigReader({ secret: 'hasnewline\n' })), + // ).toThrowErrorMatchingInlineSnapshot( + // `"Illegal secret, must be a valid base64 string"`, + // ); + // expect(() => + // handler.addOld(new ConfigReader({ secret: 3 })), + // ).toThrowErrorMatchingInlineSnapshot( + // `"Invalid type in config for key 'secret' in 'mock-config', got number, wanted string"`, + // ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts index 8be0de19e4..f9932a8735 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts @@ -16,126 +16,78 @@ import { Config } from '@backstage/config'; import { base64url, decodeJwt, decodeProtectedHeader, jwtVerify } from 'jose'; -import { readAccessRestrictionsFromConfig } from './helpers'; -import { AccessRestrictionsMap, TokenHandler } from './types'; + +import { createExternalTokenHandler } from './helpers'; export type LegacyConfigWrapper = { legacy: boolean; config: Config; }; -/** - * Handles `type: legacy` access. - * - * @internal - */ -export class LegacyTokenHandler implements TokenHandler { - #entries = new Array<{ - key: Uint8Array; - result: { - subject: string; - allAccessRestrictions?: AccessRestrictionsMap; - }; - }>(); - constructor(configs: (Config | LegacyConfigWrapper)[]) { - for (const config of configs) { - if (isLegacy(config)) { - this.addOld(config.config); - continue; +type LegacyTokenHandlerContext = { + key: Uint8Array; + + subject: string; +}; + +export const legacyTokenHandler = + createExternalTokenHandler({ + type: 'legacy', + initialize(ctx: { options: Config }): LegacyTokenHandlerContext { + const secret = ctx.options.getString('secret'); + const subject = ctx.options.getString('subject'); + + if (!secret.match(/^\S+$/)) { + throw new Error('Illegal secret, must be a valid base64 string'); + } else if (!subject.match(/^\S+$/)) { + throw new Error( + 'Illegal subject, must be a set of non-space characters', + ); } - this.add(config); - } - } - add(config: Config) { - const allAccessRestrictions = readAccessRestrictionsFromConfig(config); - this.#doAdd( - config.getString('options.secret'), - config.getString('options.subject'), - allAccessRestrictions, - ); - return this; - } - - // used only for the old backend.auth.keys array - addOld(config: Config) { - // This choice of subject is for compatibility reasons - this.#doAdd(config.getString('secret'), 'external:backstage-plugin'); - } - - #doAdd( - secret: string, - subject: string, - allAccessRestrictions?: AccessRestrictionsMap, - ) { - if (!secret.match(/^\S+$/)) { - throw new Error('Illegal secret, must be a valid base64 string'); - } else if (!subject.match(/^\S+$/)) { - throw new Error('Illegal subject, must be a set of non-space characters'); - } - - let key: Uint8Array; - try { - key = base64url.decode(secret); - } catch { - throw new Error('Illegal secret, must be a valid base64 string'); - } - - if (this.#entries.some(e => e.key === key)) { - throw new Error( - 'Legacy externalAccess token was declared more than once', - ); - } - - this.#entries.push({ - key, - result: { - subject, - allAccessRestrictions, - }, - }); - } - - async verifyToken(token: string) { - // First do a duck typing check to see if it remotely looks like a legacy token - try { - // We do a fair amount of checking upfront here. Since we aren't certain - // that it's even the right type of key that we're looking at, we can't - // defer eg the alg check to jwtVerify, because it won't be possible to - // discern different reasons for key verification failures from each other - // easily - const { alg } = decodeProtectedHeader(token); - if (alg !== 'HS256') { - return undefined; - } - const { sub, aud } = decodeJwt(token); - if (sub !== 'backstage-server' || aud) { - return undefined; - } - } catch (e) { - // Doesn't look like a jwt at all - return undefined; - } - - for (const { key, result } of this.#entries) { try { - await jwtVerify(token, key); - return result; - } catch (e) { - if (e.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { - throw e; - } - // Otherwise continue to try the next key + return { + key: base64url.decode(secret), + subject, + }; + } catch { + throw new Error('Illegal secret, must be a valid base64 string'); } - } + }, - // None of the signing keys matched - return undefined; - } -} + async verifyToken(token: string, context: LegacyTokenHandlerContext) { + // First do a duck typing check to see if it remotely looks like a legacy token + try { + // We do a fair amount of checking upfront here. Since we aren't certain + // that it's even the right type of key that we're looking at, we can't + // defer eg the alg check to jwtVerify, because it won't be possible to + // discern different reasons for key verification failures from each other + // easily + const { alg } = decodeProtectedHeader(token); + if (alg !== 'HS256') { + return undefined; + } + const { sub, aud } = decodeJwt(token); + if (sub !== 'backstage-server' || aud) { + return undefined; + } + } catch (e) { + // Doesn't look like a jwt at all + return undefined; + } -function isLegacy( - config: Config | LegacyConfigWrapper, -): config is LegacyConfigWrapper { - return (config as LegacyConfigWrapper).legacy === true; -} + try { + await jwtVerify(token, context.key); + return { + subject: context.subject, + }; + } catch (error) { + if (error.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { + throw error; + } + } + + // None of the signing keys matched + return undefined; + }, + }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts index cf1c1a46ac..10e78d2dbf 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts @@ -15,148 +15,146 @@ */ import { ConfigReader } from '@backstage/config'; -import { StaticTokenHandler } from './static'; +import { staticTokenHandler } from './static'; describe('StaticTokenHandler', () => { it('accepts any of the added list of tokens', async () => { - const configs = [ - new ConfigReader({ - options: { token: 'abcabcabc', subject: 'one' }, - accessRestrictions: [{ plugin: 'scaffolder' }], + const context1 = staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'abcabcabc', + subject: 'one', }), - new ConfigReader({ - options: { token: 'defdefdef', subject: 'two' }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], + }); + const context2 = staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'defdefdef', + subject: 'two', }), - ]; - const handler = new StaticTokenHandler(configs); - const accessRestrictionsOne = new Map(Object.entries({ scaffolder: {} })); - const accessRestrictionsTwo = new Map( - Object.entries({ - catalog: { - permissionNames: ['catalog.entity.read'], - }, - }), - ); + }); - await expect(handler.verifyToken('abcabcabc')).resolves.toEqual({ + await expect( + staticTokenHandler.verifyToken('abcabcabc', context1), + ).resolves.toEqual({ subject: 'one', - allAccessRestrictions: accessRestrictionsOne, }); - await expect(handler.verifyToken('defdefdef')).resolves.toEqual({ + await expect( + staticTokenHandler.verifyToken('defdefdef', context2), + ).resolves.toEqual({ subject: 'two', - allAccessRestrictions: accessRestrictionsTwo, }); - await expect(handler.verifyToken('ghighighi')).resolves.toBeUndefined(); + await expect( + staticTokenHandler.verifyToken('ghighighi', context1), + ).resolves.toBeUndefined(); }); it('gracefully handles no added tokens', async () => { - const handler = new StaticTokenHandler([]); - await expect(handler.verifyToken('ghi')).resolves.toBeUndefined(); + await expect( + staticTokenHandler.verifyToken('ghi', {} as any), + ).resolves.toBeUndefined(); }); it('rejects bad config', () => { - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ options: { _missingtoken: true, subject: 'ok' } }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ _missingtoken: true, subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.token' in 'mock-config'"`, + `"Missing required config value at 'token' in 'mock-config'"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ options: { token: '', subject: 'ok' } }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ token: '', subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.token' in 'mock-config', got empty-string, wanted string"`, + `"Invalid type in config for key 'token' in 'mock-config', got empty-string, wanted string"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ options: { token: 'has spaces', subject: 'ok' } }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'has spaces', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be a set of non-space characters"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ - options: { - token: 'hasnewlinebutislongenough\n', - subject: 'ok', - }, - }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'hasnewlinebutislongenough\n', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be a set of non-space characters"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ options: { token: 'short', subject: 'ok' } }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'short', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be at least 8 characters length"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ options: { token: 3, subject: 'ok' } }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ token: 3, subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.token' in 'mock-config', got number, wanted string"`, + `"Invalid type in config for key 'token' in 'mock-config', got number, wanted string"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ - options: { token: 'validtoken', _missingsubject: true }, - }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + _missingsubject: true, + }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.subject' in 'mock-config'"`, + `"Missing required config value at 'subject' in 'mock-config'"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ options: { token: 'validtoken', subject: '' } }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: '', + }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got empty-string, wanted string"`, + `"Invalid type in config for key 'subject' in 'mock-config', got empty-string, wanted string"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ - options: { token: 'validtoken', subject: 'has spaces' }, - }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: 'has spaces', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ - options: { token: 'validtoken', subject: 'hasnewline\n' }, - }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: 'hasnewline\n', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); - expect( - () => - new StaticTokenHandler([ - new ConfigReader({ options: { token: 'validtoken', subject: 3 } }), - ]), + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: 3, + }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got number, wanted string"`, + `"Invalid type in config for key 'subject' in 'mock-config', got number, wanted string"`, ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.ts index 7764569aca..fe7b9995a4 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.ts @@ -15,34 +15,18 @@ */ import { Config } from '@backstage/config'; -import { readAccessRestrictionsFromConfig } from './helpers'; -import { AccessRestrictionsMap, TokenHandler } from './types'; +import { createExternalTokenHandler } from './helpers'; const MIN_TOKEN_LENGTH = 8; -/** - * Handles `type: static` access. - * - * @internal - */ -export class StaticTokenHandler implements TokenHandler { - #entries = new Map< - string, - { - subject: string; - allAccessRestrictions?: AccessRestrictionsMap; - } - >(); - - constructor(configs: Config[]) { - for (const config of configs) { - this.add(config); - } - } - private add(config: Config) { - const token = config.getString('options.token'); - const subject = config.getString('options.subject'); - const allAccessRestrictions = readAccessRestrictionsFromConfig(config); +export const staticTokenHandler = createExternalTokenHandler<{ + token: string; + subject: string; +}>({ + type: 'static', + initialize(ctx: { options: Config }): { token: string; subject: string } { + const token = ctx.options.getString('token'); + const subject = ctx.options.getString('subject'); if (!token.match(/^\S+$/)) { throw new Error('Illegal token, must be a set of non-space characters'); @@ -52,17 +36,64 @@ export class StaticTokenHandler implements TokenHandler { ); } else if (!subject.match(/^\S+$/)) { throw new Error('Illegal subject, must be a set of non-space characters'); - } else if (this.#entries.has(token)) { - throw new Error( - 'Static externalAccess token was declared more than once', - ); } - this.#entries.set(token, { subject, allAccessRestrictions }); - return this; - } + return { token, subject }; + }, + async verifyToken( + token: string, + context: { token: string; subject: string }, + ) { + if (token === context.token) { + return { subject: context.subject }; + } + return undefined; + }, +}); - async verifyToken(token: string) { - return this.#entries.get(token); - } -} +/** + * Handles `type: static` access. + * + * @internal + */ +// export class StaticTokenHandler implements TokenHandler { +// #entries = new Map< +// string, +// { +// subject: string; +// allAccessRestrictions?: AccessRestrictionsMap; +// } +// >(); + +// constructor(configs: Config[]) { +// for (const config of configs) { +// this.add(config); +// } +// } +// private add(config: Config) { +// const token = config.getString('options.token'); +// const subject = config.getString('options.subject'); +// const allAccessRestrictions = readAccessRestrictionsFromConfig(config); + +// if (!token.match(/^\S+$/)) { +// throw new Error('Illegal token, must be a set of non-space characters'); +// } else if (token.length < MIN_TOKEN_LENGTH) { +// throw new Error( +// `Illegal token, must be at least ${MIN_TOKEN_LENGTH} characters length`, +// ); +// } else if (!subject.match(/^\S+$/)) { +// throw new Error('Illegal subject, must be a set of non-space characters'); +// } else if (this.#entries.has(token)) { +// throw new Error( +// 'Static externalAccess token was declared more than once', +// ); +// } + +// this.#entries.set(token, { subject, allAccessRestrictions }); +// return this; +// } + +// async verifyToken(token: string) { +// return this.#entries.get(token); +// } +// } diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index 5e26b66da5..6ce1ca13b6 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -15,6 +15,7 @@ */ import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; +import type { Config } from '@backstage/config'; /** * @public @@ -29,27 +30,24 @@ export type AccessRestrictionsMap = Map< * This interface is used to handle external tokens. * It is used by the auth service to verify tokens and extract the subject. */ -export interface TokenHandler { - verifyToken(token: string): Promise< - | { - subject: string; - allAccessRestrictions?: AccessRestrictionsMap; - } - | undefined - >; +export interface ExternalTokenHandler { + type: string; + initialize(ctx: { options: Config }): TContext; + verifyToken( + token: string, + ctx: TContext, + ): Promise<{ subject: string } | undefined>; } -/** - * @public - * This interface is used to handle external tokens. - */ -export interface TokenTypeHandler { - type: string; - /** - * A factory function that takes all token configuration for a given type - * and returns a TokenHandler or an array of TokenHandlers. - */ - factory: ( - configs: import('@backstage/config').Config[], - ) => TokenHandler | TokenHandler[]; -} +// /** +// * @public +// * This interface is used to handle external tokens. +// */ +// export interface TokenTypeHandler { +// type: string; +// /** +// * A factory function that takes all token configuration for a given type +// * and returns a TokenHandler or an array of TokenHandlers. +// */ +// factory: (configs: Config[]) => TokenHandler | TokenHandler[]; +// } diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index 335fc2f62c..e205c8b566 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -19,9 +19,11 @@ export { pluginTokenHandlerDecoratorServiceRef, } from './authServiceFactory'; -export { externalTokenTypeHandlersRef } from './external/ExternalTokenHandler'; +export { createExternalTokenHandler } from './external/helpers'; -export type { TokenHandler, TokenTypeHandler } from './external/types'; +export { externalTokenTypeHandlersRef } from './external/ExternalAuthTokenHandler'; + +export type { ExternalTokenHandler } from './external/types'; export type { AccessRestrictionsMap } from './external/types'; export type { PluginTokenHandler } from './plugin/PluginTokenHandler'; From 6361c0c00fe8716c5b9c2635dbd366aa49f8fb86 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sat, 6 Sep 2025 19:11:23 +0200 Subject: [PATCH 034/211] fix: rename the Internal token handler Signed-off-by: Juan Pablo Garcia Ripa --- .../src/entrypoints/auth/DefaultAuthService.ts | 4 ++-- .../src/entrypoints/auth/authServiceFactory.ts | 4 ++-- .../auth/external/ExternalAuthTokenHandler.test.ts | 14 +++++++------- .../auth/external/ExternalAuthTokenHandler.ts | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts index c2c4a064a3..dba4f70dec 100644 --- a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts +++ b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts @@ -25,7 +25,7 @@ import { import { AuthenticationError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; import { decodeJwt } from 'jose'; -import { ExternalAuthTokenManager } from './external/export class ExternalAuthTokenManager {'; +import { ExternalAuthTokenHandler } from './external/ExternalAuthTokenHandler'; import { createCredentialsWithNonePrincipal, createCredentialsWithServicePrincipal, @@ -41,7 +41,7 @@ export class DefaultAuthService implements AuthService { constructor( private readonly userTokenHandler: UserTokenHandler, private readonly pluginTokenHandler: PluginTokenHandler, - private readonly externalTokenHandler: ExternalAuthTokenManager, + private readonly externalTokenHandler: ExternalAuthTokenHandler, private readonly pluginId: string, private readonly disableDefaultAuthPolicy: boolean, private readonly pluginKeySource: PluginKeySource, diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index 34fc3b51ef..0f8c5ff75d 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -21,7 +21,7 @@ import { } from '@backstage/backend-plugin-api'; import { DefaultAuthService } from './DefaultAuthService'; import { - ExternalAuthTokenManager, + ExternalAuthTokenHandler, externalTokenTypeHandlersRef, } from './external/ExternalAuthTokenHandler'; import { @@ -107,7 +107,7 @@ export const authServiceFactory = createServiceFactory({ }), ); - const externalTokens = ExternalAuthTokenManager.create({ + const externalTokens = ExternalAuthTokenHandler.create({ ownPluginId: plugin.getId(), config, logger, diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts index d97875d228..491de30ac4 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts @@ -15,7 +15,7 @@ */ import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; -import { ExternalAuthTokenManager } from './ExternalAuthTokenHandler'; +import { ExternalAuthTokenHandler } from './ExternalAuthTokenHandler'; import { createExternalTokenHandler } from './helpers'; import { AccessRestrictionsMap, ExternalTokenHandler } from './types'; import { @@ -108,7 +108,7 @@ describe('ExternalTokenHandler', () => { }), ); - const plugin1 = new ExternalAuthTokenManager('plugin1', [ + const plugin1 = new ExternalAuthTokenHandler('plugin1', [ { context: undefined, handler: handler1, @@ -119,7 +119,7 @@ describe('ExternalTokenHandler', () => { allAccessRestrictions: accessRestrictions, }, ]); - const plugin2 = new ExternalAuthTokenManager('plugin2', [ + const plugin2 = new ExternalAuthTokenHandler('plugin2', [ { context: undefined, handler: handler1, @@ -160,7 +160,7 @@ describe('ExternalTokenHandler', () => { ), ); - const handler = ExternalAuthTokenManager.create({ + const handler = ExternalAuthTokenHandler.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ @@ -252,7 +252,7 @@ describe('ExternalTokenHandler', () => { const verifyMock = jest.fn().mockResolvedValue({}); const initializeMock = jest.fn().mockReturnValue({ context: 'a' }); - const handler = ExternalAuthTokenManager.create({ + const handler = ExternalAuthTokenHandler.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ @@ -305,7 +305,7 @@ describe('ExternalTokenHandler', () => { }); it('should fail if config contains types not declared', async () => { const createHandler = () => - ExternalAuthTokenManager.create({ + ExternalAuthTokenHandler.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ @@ -338,7 +338,7 @@ describe('ExternalTokenHandler', () => { it('should show valid custom types in errors', async () => { const createHandler = () => - ExternalAuthTokenManager.create({ + ExternalAuthTokenHandler.create({ ownPluginId: 'catalog', logger: mockServices.logger.mock(), config: mockServices.rootConfig({ diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts index bf57ad42f4..17541ff384 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts @@ -60,13 +60,13 @@ type ContextMapEntry = { * * @internal */ -export class ExternalAuthTokenManager { +export class ExternalAuthTokenHandler { static create(options: { ownPluginId: string; config: RootConfigService; logger: LoggerService; externalTokenHandlers?: ExternalTokenHandler[]; - }): ExternalAuthTokenManager { + }): ExternalAuthTokenHandler { const { ownPluginId, config, @@ -110,7 +110,7 @@ export class ExternalAuthTokenManager { ); } - return new ExternalAuthTokenManager(ownPluginId, contexts); + return new ExternalAuthTokenHandler(ownPluginId, contexts); } constructor( From 968b2f606ed3062f4bb08fd37b51c07e83019aea Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sat, 6 Sep 2025 19:48:23 +0200 Subject: [PATCH 035/211] fix: api-reports Signed-off-by: Juan Pablo Garcia Ripa --- packages/backend-defaults/report-auth.api.md | 46 ++++++++++--------- .../src/entrypoints/auth/external/helpers.ts | 28 +++++++++++ 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index 176bdd16de..0164fa13c1 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -5,7 +5,7 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; +import type { Config } from '@backstage/config'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; @@ -22,9 +22,32 @@ export const authServiceFactory: ServiceFactory< 'singleton' >; +// @public +export function createExternalTokenHandler( + handler: ExternalTokenHandler, +): ExternalTokenHandler; + +// @public +export interface ExternalTokenHandler { + // (undocumented) + initialize(ctx: { options: Config }): TContext; + // (undocumented) + type: string; + // (undocumented) + verifyToken( + token: string, + ctx: TContext, + ): Promise< + | { + subject: string; + } + | undefined + >; +} + // @public export const externalTokenTypeHandlersRef: ServiceRef< - TokenTypeHandler, + ExternalTokenHandler, 'plugin', 'multiton' >; @@ -59,24 +82,5 @@ export const pluginTokenHandlerDecoratorServiceRef: ServiceRef< 'singleton' >; -// @public -export interface ExternalTokenHandler { - // (undocumented) - verifyToken(token: string): Promise< - | { - subject: string; - allAccessRestrictions?: AccessRestrictionsMap; - } - | undefined - >; -} - -// @public -export interface TokenTypeHandler { - factory: (configs: Config[]) => TokenHandler | TokenHandler[]; - // (undocumented) - type: string; -} - // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts index 296ec2e72b..94d289bd7c 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts @@ -148,6 +148,34 @@ function readPermissionAttributes(externalAccessEntryConfig: Config) { return Object.keys(result).length ? result : undefined; } +/** + * Creates an external token handler with the provided implementation. + * + * This helper function simplifies the creation of external token handlers by + * providing type safety and a consistent API. External token handlers are used + * to validate tokens from external systems that need to authenticate with Backstage. + * + * See {@link https://backstage.io/docs/auth/service-to-service-auth#adding-custom-externaltokenhandler | the service-to-service auth docs} + * for more information about implementing custom external token handlers. + * + * @public + * @param handler - The external token handler implementation with type, initialize, and verifyToken methods + * @returns The same handler instance, typed as ExternalTokenHandler + * + * @example + * ```ts + * const customHandler = createExternalTokenHandler({ + * type: 'custom', + * initialize({ options }) { + * return { apiKey: options.getString('apiKey') }; + * }, + * async verifyToken(token, context) { + * // Custom validation logic here + * return { subject: 'custom:user' }; + * }, + * }); + * ``` + */ export function createExternalTokenHandler( handler: ExternalTokenHandler, ): ExternalTokenHandler { From 6ff5452d3c37b21a9390c06bb38a6f03707e5b41 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 8 Sep 2025 09:52:12 +0200 Subject: [PATCH 036/211] fix: correct grammar Co-authored-by: Peter Macdonald Signed-off-by: Juan Pablo Garcia Ripa --- docs/backend-system/architecture/03-services.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/backend-system/architecture/03-services.md b/docs/backend-system/architecture/03-services.md index f6722496e5..022aab202b 100644 --- a/docs/backend-system/architecture/03-services.md +++ b/docs/backend-system/architecture/03-services.md @@ -225,8 +225,8 @@ This allows you to provide more advanced options for the service implementation ## Multiton -By default the service reference will point to a singleton instance of the service. This mean if a new service factory uses this reference will override the previous one. This is the most common use-case, but in some cases you may want to have multiple instances of the same service. -For some services, is desirable to extend the functionality instead of overriding it. For example, some services could have many handler to address a specific event, and you may want to add a new handler instead of overriding the previous one. In this case, you can use the `multiton` option when creating the service reference: +By default the service reference will point to a singleton instance of the service. This mean if a new service factory uses this reference it will override the previous one. This is the most common use-case, but in some cases you may want to have multiple instances of the same service. +For some services, it is desirable to extend the functionality instead of overriding it. For example, some services could have many handlers to address specific events, and you may want to add a new handler instead of overriding the previous one. In this case, you can use the `multiton` option when creating the service reference: ```ts // example-service-ref.ts From 823ed49a43b7b6f22344f28ce729f8b16c373809 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Thu, 11 Sep 2025 16:33:45 +0200 Subject: [PATCH 037/211] clean up Signed-off-by: Juan Pablo Garcia Ripa --- .../entrypoints/auth/external/legacy.test.ts | 30 ------------ .../src/entrypoints/auth/external/static.ts | 47 ------------------- 2 files changed, 77 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts index 43160b46da..a278e5766c 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts @@ -235,35 +235,5 @@ describe('LegacyTokenHandler', () => { ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'subject' in 'mock-config', got number, wanted string"`, ); - - // // old style add - // expect(() => - // handler.addOld(new ConfigReader({ secret: 'b2s=' })), - // ).not.toThrow(); - // expect(() => - // handler.addOld(new ConfigReader({ _missingsecret: true })), - // ).toThrowErrorMatchingInlineSnapshot( - // `"Missing required config value at 'secret' in 'mock-config'"`, - // ); - // expect(() => - // handler.addOld(new ConfigReader({ secret: '' })), - // ).toThrowErrorMatchingInlineSnapshot( - // `"Invalid type in config for key 'secret' in 'mock-config', got empty-string, wanted string"`, - // ); - // expect(() => - // handler.addOld(new ConfigReader({ secret: 'has spaces' })), - // ).toThrowErrorMatchingInlineSnapshot( - // `"Illegal secret, must be a valid base64 string"`, - // ); - // expect(() => - // handler.addOld(new ConfigReader({ secret: 'hasnewline\n' })), - // ).toThrowErrorMatchingInlineSnapshot( - // `"Illegal secret, must be a valid base64 string"`, - // ); - // expect(() => - // handler.addOld(new ConfigReader({ secret: 3 })), - // ).toThrowErrorMatchingInlineSnapshot( - // `"Invalid type in config for key 'secret' in 'mock-config', got number, wanted string"`, - // ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.ts index fe7b9995a4..502150d5a8 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.ts @@ -50,50 +50,3 @@ export const staticTokenHandler = createExternalTokenHandler<{ return undefined; }, }); - -/** - * Handles `type: static` access. - * - * @internal - */ -// export class StaticTokenHandler implements TokenHandler { -// #entries = new Map< -// string, -// { -// subject: string; -// allAccessRestrictions?: AccessRestrictionsMap; -// } -// >(); - -// constructor(configs: Config[]) { -// for (const config of configs) { -// this.add(config); -// } -// } -// private add(config: Config) { -// const token = config.getString('options.token'); -// const subject = config.getString('options.subject'); -// const allAccessRestrictions = readAccessRestrictionsFromConfig(config); - -// if (!token.match(/^\S+$/)) { -// throw new Error('Illegal token, must be a set of non-space characters'); -// } else if (token.length < MIN_TOKEN_LENGTH) { -// throw new Error( -// `Illegal token, must be at least ${MIN_TOKEN_LENGTH} characters length`, -// ); -// } else if (!subject.match(/^\S+$/)) { -// throw new Error('Illegal subject, must be a set of non-space characters'); -// } else if (this.#entries.has(token)) { -// throw new Error( -// 'Static externalAccess token was declared more than once', -// ); -// } - -// this.#entries.set(token, { subject, allAccessRestrictions }); -// return this; -// } - -// async verifyToken(token: string) { -// return this.#entries.get(token); -// } -// } From 818c835a10e54e1889edcb450c9a11311367139d Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 15 Sep 2025 22:40:23 +0200 Subject: [PATCH 038/211] bring the legacy config back Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/thirty-rules-press.md | 21 --- docs/auth/service-to-service-auth.md | 36 +----- packages/backend-defaults/report-auth.api.md | 9 +- .../auth/authServiceFactory.test.ts | 8 +- .../entrypoints/auth/authServiceFactory.ts | 4 +- .../external/ExternalAuthTokenHandler.test.ts | 51 ++++++++ .../auth/external/ExternalAuthTokenHandler.ts | 24 +++- .../entrypoints/auth/external/legacy.test.ts | 70 ++++++++++ .../src/entrypoints/auth/external/legacy.ts | 120 ++++++++++-------- .../src/entrypoints/auth/external/types.ts | 16 --- .../src/entrypoints/auth/index.ts | 3 +- 11 files changed, 219 insertions(+), 143 deletions(-) diff --git a/.changeset/thirty-rules-press.md b/.changeset/thirty-rules-press.md index 760780eb84..d9968317cb 100644 --- a/.changeset/thirty-rules-press.md +++ b/.changeset/thirty-rules-press.md @@ -3,24 +3,3 @@ --- Add a new `externalTokenHandlersServiceRef` to allow custom external token validations - -BREAKING CHANGE: The `backend.auth.keys` config has been removed. Please migrate to the new `backend.auth.externalAccess` config as described in the documentation: https://backstage.io/docs/auth/service-to-service-auth - -**Migration Example:** - -```yaml -# ❌ Old format (no longer supported) -backend: - auth: - keys: - - secret: your-secret-key - -# ✅ New format -backend: - auth: - externalAccess: - - type: static - options: - token: your-secret-key - subject: external:backstage-plugin # this is the current default for old keys -``` diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index 4ba094068e..1423fa92f7 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -255,16 +255,10 @@ backend: subject: legacy-scaffolder ``` -:::warning -The old style `backend.auth.keys` config is **no longer supported** and has been removed. -If you are still using this configuration, you must migrate to the new `backend.auth.externalAccess` format above. +The old style keys config is also supported as an alternative, but please +consider using the new style above instead: -You'll see an error like `"Unknown key 'backend.auth.keys'"` during Backstage startup if you haven't migrated yet. -::: - -To migrate from the old config format: - -```yaml title="❌ Old format (no longer supported)" +```yaml title="in e.g. app-config.production.yaml" backend: auth: keys: @@ -272,22 +266,6 @@ backend: - secret: my-secret-key-scaffolder ``` -Convert to: - -```yaml title="✅ New format" -backend: - auth: - externalAccess: - - type: legacy - options: - secret: my-secret-key-catalog - subject: external:backstage-plugin - - type: legacy - options: - secret: my-secret-key-scaffolder - subject: external:backstage-plugin -``` - The secrets must be any base64-encoded random data, but for security reasons should be sufficiently long so as not to be easy to guess by brute force. You can for example generate them on the command line: @@ -436,7 +414,7 @@ Each entry has one or more of the following fields: ## Adding custom or logic for validation and issuing of tokens -The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenTypeHandlersRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. +The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenHandlersServiceRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. This is particularly useful when you want to add additional logic to the handler, such as logging or metrics or custom token validation. ### PluginTokenHandler decoration @@ -468,7 +446,7 @@ const decoratedPluginTokenHandler = createServiceFactory({ ### Adding custom ExternalTokenHandler -The `externalTokenTypeHandlersRef` can be used to add custom external token handlers to the default implementation. +The `externalTokenHandlersServiceRef` can be used to add custom external token handlers to the default implementation. Your service factory must return an object with a `type` property that matches the token type in your configuration (e.g., 'custom', 'api-key'). When Backstage encounters tokens of this type, it calls your `initialize` method with all the configuration entries that match this type. Your factory can return either a single token handler or an array of handlers to process and validate these tokens. @@ -503,13 +481,13 @@ And we can implement the custom token handler like this: ```ts import { ExternalTokenHandler, - externalTokenTypeHandlersRef, + externalTokenHandlersServiceRef, createExternalTokenHandler, } from '@backstage/backend-defaults/auth'; import { createServiceFactory } from '@backstage/backend-plugin-api'; const customExternalTokenHandlers = createServiceFactory({ - service: externalTokenTypeHandlersRef, + service: externalTokenHandlersServiceRef, deps: {}, async factory() { return createExternalTokenHandler({ diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index 0164fa13c1..2fc0f16452 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -4,17 +4,10 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; -import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; import type { Config } from '@backstage/config'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; -// @public (undocumented) -export type AccessRestrictionsMap = Map< - string, // plugin ID - BackstagePrincipalAccessRestrictions ->; - // @public export const authServiceFactory: ServiceFactory< AuthService, @@ -46,7 +39,7 @@ export interface ExternalTokenHandler { } // @public -export const externalTokenTypeHandlersRef: ServiceRef< +export const externalTokenHandlersServiceRef: ServiceRef< ExternalTokenHandler, 'plugin', 'multiton' diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index b6d5beefa2..b70aedfb9a 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -31,7 +31,7 @@ import { toInternalBackstageCredentials } from './helpers'; import { PluginTokenHandler } from './plugin/PluginTokenHandler'; import { createServiceFactory } from '@backstage/backend-plugin-api'; -import { externalTokenTypeHandlersRef } from './external/ExternalAuthTokenHandler'; +import { externalTokenHandlersServiceRef } from './external/ExternalAuthTokenHandler'; import { createExternalTokenHandler } from './external/helpers'; const server = setupServer(); @@ -44,7 +44,7 @@ const mockDeps = [ backend: { baseUrl: 'http://localhost', auth: { - // keys: [{ secret: 'abc' }], + keys: [{ secret: 'abc' }], externalAccess: [ { type: 'static', @@ -468,7 +468,7 @@ describe('authServiceFactory', () => { backend: { baseUrl: 'http://localhost', auth: { - // keys: [{ secret: 'abc' }w], + keys: [{ secret: 'abc' }], externalAccess: [ { type: 'static', @@ -521,7 +521,7 @@ describe('authServiceFactory', () => { }); const customPluginTokenHandler = createServiceFactory({ - service: externalTokenTypeHandlersRef, + service: externalTokenHandlersServiceRef, deps: {}, async factory() { return customExternalTokenHandler; diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index 0f8c5ff75d..ee171aef25 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -22,7 +22,7 @@ import { import { DefaultAuthService } from './DefaultAuthService'; import { ExternalAuthTokenHandler, - externalTokenTypeHandlersRef, + externalTokenHandlersServiceRef, } from './external/ExternalAuthTokenHandler'; import { DefaultPluginTokenHandler, @@ -67,7 +67,7 @@ export const authServiceFactory = createServiceFactory({ plugin: coreServices.pluginMetadata, database: coreServices.database, pluginTokenHandlerDecorator: pluginTokenHandlerDecoratorServiceRef, - externalTokenHandlers: externalTokenTypeHandlersRef, + externalTokenHandlers: externalTokenHandlersServiceRef, }, async factory({ config, diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts index 491de30ac4..f315b43314 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts @@ -233,6 +233,57 @@ describe('ExternalTokenHandler', () => { accessRestrictions: { permissionNames: ['catalog.entity.read'] }, }); }); + it('successfully uses legacy configs', async () => { + const legacyKey = randomBytes(24); + const factory = new FakeTokenFactory({ + issuer: 'my-company', + keyDurationSeconds: 100, + }); + + server.use( + rest.get( + 'https://example.com/.well-known/jwks.json', + async (_, res, ctx) => { + const keys = await factory.listPublicKeys(); + return res(ctx.json(keys)); + }, + ), + ); + + const logger = mockServices.logger.mock(); + const handler = ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger, + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + keys: [ + { + secret: legacyKey.toString('base64'), + }, + ], + }, + }, + }, + }), + }); + + expect(logger.warn).toHaveBeenCalledWith( + `DEPRECATION WARNING: The backend.auth.keys config has been replaced by backend.auth.externalAccess, see https://backstage.io/docs/auth/service-to-service-auth`, + ); + + const legacyToken = await new SignJWT({ + sub: 'backstage-server', + exp: DateTime.now().plus({ minutes: 1 }).toUnixInteger(), + }) + .setProtectedHeader({ alg: 'HS256' }) + .sign(legacyKey); + + await expect(handler.verifyToken(legacyToken)).resolves.toEqual({ + subject: 'external:backstage-plugin', + }); + }); it('successfully uses custom token handlers', async () => { const factory = new FakeTokenFactory({ issuer: 'my-company', diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts index 17541ff384..4b2cbb3e5a 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts @@ -33,14 +33,13 @@ let loggedDeprecationWarning = false; /** * @public - * This service is used to decorate the default plugin token handler with custom logic. + * This service is used to add custom handlers for external token. */ -export const externalTokenTypeHandlersRef = createServiceRef< +export const externalTokenHandlersServiceRef = createServiceRef< ExternalTokenHandler >({ id: 'core.auth.externalTokenHandlers', multiton: true, - // defaultFactory // :pepe-think: seems like is not possible to use defaultFactory with multiton }); const defaultHandlers: ExternalTokenHandler[] = [ @@ -54,6 +53,7 @@ type ContextMapEntry = { handler: ExternalTokenHandler; allAccessRestrictions?: AccessRestrictionsMap; }; + /** * Handles all types of external caller token types (i.e. not Backstage user * tokens, nor Backstage backend plugin tokens). @@ -71,6 +71,7 @@ export class ExternalAuthTokenHandler { ownPluginId, config, externalTokenHandlers: customHandlers, + logger, } = options; const handlersTypes = [...defaultHandlers, ...(customHandlers ?? [])]; @@ -104,12 +105,23 @@ export class ExternalAuthTokenHandler { if (legacyConfigs.length && !loggedDeprecationWarning) { loggedDeprecationWarning = true; - // :pepe-think: this message was here for more than a year, and replacing this simplifies things a lot - throw new Error( - `The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`, + + logger.warn( + `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`, ); } + for (const legacyConfig of legacyConfigs) { + contexts.push({ + context: legacyTokenHandler.initialize({ + legacy: true, + options: legacyConfig, + }), + handler: legacyTokenHandler, + allAccessRestrictions: readAccessRestrictionsFromConfig(legacyConfig), + }); + } + return new ExternalAuthTokenHandler(ownPluginId, contexts); } diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts index a278e5766c..48dbcc2418 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts @@ -23,6 +23,7 @@ import { legacyTokenHandler } from './legacy'; describe('LegacyTokenHandler', () => { const key1 = randomBytes(24); const key2 = randomBytes(24); + const key3 = randomBytes(24); const tokenHandler = legacyTokenHandler; @@ -40,6 +41,13 @@ describe('LegacyTokenHandler', () => { }), }); + const contextWithLegacy = tokenHandler.initialize({ + options: new ConfigReader({ + secret: key3.toString('base64'), + }), + legacy: true, + }); + it('should verify valid tokens', async () => { const token1 = await new SignJWT({ sub: 'backstage-server', @@ -62,6 +70,19 @@ describe('LegacyTokenHandler', () => { await expect(tokenHandler.verifyToken(token2, context2)).resolves.toEqual({ subject: 'key2', }); + + const token3 = await new SignJWT({ + sub: 'backstage-server', + exp: DateTime.now().plus({ minutes: 1 }).toUnixInteger(), + }) + .setProtectedHeader({ alg: 'HS256' }) + .sign(key3); + + await expect( + tokenHandler.verifyToken(token3, contextWithLegacy), + ).resolves.toEqual({ + subject: 'external:backstage-plugin', + }); }); it('should return undefined if the token is not a valid legacy token', async () => { @@ -235,5 +256,54 @@ describe('LegacyTokenHandler', () => { ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'subject' in 'mock-config', got number, wanted string"`, ); + + // old style add + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'b2s=' }), + legacy: true, + }), + ).not.toThrow(); + + expect(() => + handler.initialize({ + options: new ConfigReader({ _missingsecret: true }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Missing required config value at 'secret' in 'mock-config'"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: '' }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'secret' in 'mock-config', got empty-string, wanted string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'has spaces' }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal secret, must be a valid base64 string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'hasnewline\n' }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal secret, must be a valid base64 string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 3 }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'secret' in 'mock-config', got number, wanted string"`, + ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts index f9932a8735..2ab8c3a5ea 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts @@ -17,7 +17,7 @@ import { Config } from '@backstage/config'; import { base64url, decodeJwt, decodeProtectedHeader, jwtVerify } from 'jose'; -import { createExternalTokenHandler } from './helpers'; +import { ExternalTokenHandler } from './types'; export type LegacyConfigWrapper = { legacy: boolean; @@ -30,64 +30,74 @@ type LegacyTokenHandlerContext = { subject: string; }; -export const legacyTokenHandler = - createExternalTokenHandler({ - type: 'legacy', - initialize(ctx: { options: Config }): LegacyTokenHandlerContext { - const secret = ctx.options.getString('secret'); - const subject = ctx.options.getString('subject'); +type LegacyTokenHandlerOverloaded = + ExternalTokenHandler & { + initialize(ctx: { + options: Config; + legacy: true; + }): LegacyTokenHandlerContext; + }; - if (!secret.match(/^\S+$/)) { - throw new Error('Illegal secret, must be a valid base64 string'); - } else if (!subject.match(/^\S+$/)) { - throw new Error( - 'Illegal subject, must be a set of non-space characters', - ); - } +export const legacyTokenHandler: LegacyTokenHandlerOverloaded = { + type: 'legacy', + initialize(ctx: { + options: Config; + legacy?: true; + }): LegacyTokenHandlerContext { + const secret = ctx.options.getString('secret'); + const subject = ctx.legacy + ? 'external:backstage-plugin' + : ctx.options.getString('subject'); - try { - return { - key: base64url.decode(secret), - subject, - }; - } catch { - throw new Error('Illegal secret, must be a valid base64 string'); - } - }, + if (!secret.match(/^\S+$/)) { + throw new Error('Illegal secret, must be a valid base64 string'); + } else if (!subject.match(/^\S+$/)) { + throw new Error('Illegal subject, must be a set of non-space characters'); + } - async verifyToken(token: string, context: LegacyTokenHandlerContext) { - // First do a duck typing check to see if it remotely looks like a legacy token - try { - // We do a fair amount of checking upfront here. Since we aren't certain - // that it's even the right type of key that we're looking at, we can't - // defer eg the alg check to jwtVerify, because it won't be possible to - // discern different reasons for key verification failures from each other - // easily - const { alg } = decodeProtectedHeader(token); - if (alg !== 'HS256') { - return undefined; - } - const { sub, aud } = decodeJwt(token); - if (sub !== 'backstage-server' || aud) { - return undefined; - } - } catch (e) { - // Doesn't look like a jwt at all + try { + return { + key: base64url.decode(secret), + subject, + }; + } catch { + throw new Error('Illegal secret, must be a valid base64 string'); + } + }, + + async verifyToken(token: string, context: LegacyTokenHandlerContext) { + // First do a duck typing check to see if it remotely looks like a legacy token + try { + // We do a fair amount of checking upfront here. Since we aren't certain + // that it's even the right type of key that we're looking at, we can't + // defer eg the alg check to jwtVerify, because it won't be possible to + // discern different reasons for key verification failures from each other + // easily + const { alg } = decodeProtectedHeader(token); + if (alg !== 'HS256') { return undefined; } - - try { - await jwtVerify(token, context.key); - return { - subject: context.subject, - }; - } catch (error) { - if (error.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { - throw error; - } + const { sub, aud } = decodeJwt(token); + if (sub !== 'backstage-server' || aud) { + return undefined; } - - // None of the signing keys matched + } catch (e) { + // Doesn't look like a jwt at all return undefined; - }, - }); + } + + try { + await jwtVerify(token, context.key); + return { + subject: context.subject, + }; + } catch (error) { + if (error.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { + throw error; + } + } + + // None of the signing keys matched + return undefined; + }, +}; diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index 6ce1ca13b6..01d5a26149 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -17,9 +17,6 @@ import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; import type { Config } from '@backstage/config'; -/** - * @public - */ export type AccessRestrictionsMap = Map< string, // plugin ID BackstagePrincipalAccessRestrictions @@ -38,16 +35,3 @@ export interface ExternalTokenHandler { ctx: TContext, ): Promise<{ subject: string } | undefined>; } - -// /** -// * @public -// * This interface is used to handle external tokens. -// */ -// export interface TokenTypeHandler { -// type: string; -// /** -// * A factory function that takes all token configuration for a given type -// * and returns a TokenHandler or an array of TokenHandlers. -// */ -// factory: (configs: Config[]) => TokenHandler | TokenHandler[]; -// } diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index e205c8b566..69d41b0310 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -21,9 +21,8 @@ export { export { createExternalTokenHandler } from './external/helpers'; -export { externalTokenTypeHandlersRef } from './external/ExternalAuthTokenHandler'; +export { externalTokenHandlersServiceRef } from './external/ExternalAuthTokenHandler'; export type { ExternalTokenHandler } from './external/types'; -export type { AccessRestrictionsMap } from './external/types'; export type { PluginTokenHandler } from './plugin/PluginTokenHandler'; From fe17ef8115b79c300ac78b253cd78b553836fc8e Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 15 Sep 2025 23:08:47 +0200 Subject: [PATCH 039/211] failing api report Signed-off-by: Juan Pablo Garcia Ripa --- plugins/catalog-react/report-alpha.api.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index 30cdd91704..cf28d80c11 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -92,12 +92,12 @@ export const catalogReactTranslationRef: TranslationRef< readonly 'entityTableColumnTitle.title': 'Title'; readonly 'entityTableColumnTitle.description': 'Description'; readonly 'entityTableColumnTitle.domain': 'Domain'; + readonly 'entityTableColumnTitle.system': 'System'; + readonly 'entityTableColumnTitle.tags': 'Tags'; readonly 'entityTableColumnTitle.namespace': 'Namespace'; readonly 'entityTableColumnTitle.lifecycle': 'Lifecycle'; readonly 'entityTableColumnTitle.owner': 'Owner'; - readonly 'entityTableColumnTitle.system': 'System'; readonly 'entityTableColumnTitle.targets': 'Targets'; - readonly 'entityTableColumnTitle.tags': 'Tags'; } >; @@ -533,8 +533,8 @@ export const EntityTableColumnTitle: ({ translationKey, }: EntityTableColumnTitleProps) => | 'Title' - | 'Domain' | 'System' + | 'Domain' | 'Lifecycle' | 'Namespace' | 'Owner' From 8ed53eb466c328bb67f426e52f3a1452dd58d63d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 16 Sep 2025 00:05:27 +0200 Subject: [PATCH 040/211] frontend-plugin-api: add coreExtensionData.title Signed-off-by: Patrik Oldsberg --- .changeset/nasty-moose-rescue.md | 5 +++++ .../building-plugins/04-built-in-data-refs.md | 8 ++++++++ packages/frontend-plugin-api/report.api.md | 1 + .../frontend-plugin-api/src/wiring/coreExtensionData.ts | 1 + plugins/catalog-react/report-alpha.api.md | 6 +++--- 5 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .changeset/nasty-moose-rescue.md diff --git a/.changeset/nasty-moose-rescue.md b/.changeset/nasty-moose-rescue.md new file mode 100644 index 0000000000..ff8cf1b02c --- /dev/null +++ b/.changeset/nasty-moose-rescue.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Added `coreExtensionData.title`, especially useful for creating extensible layout with tabbed pages, but availble for use for other cases too. 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 b69e4cb3f4..c01520d7bd 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 @@ -34,6 +34,14 @@ const examplePage = createExtension({ }); ``` +### `title` + +| id | type | +| :----------: | :------: | +| `core.title` | `string` | + +The `title` data reference can be used for defining the extension input/output of string titles. + ### `routePath` | id | type | diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index f9e2336408..52a50659b6 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -377,6 +377,7 @@ export interface ConfigurableExtensionDataRef< // @public (undocumented) export const coreExtensionData: { + title: ConfigurableExtensionDataRef; reactElement: ConfigurableExtensionDataRef< JSX_3.Element, 'core.reactElement', diff --git a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts index 18466d8196..b778539101 100644 --- a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts +++ b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts @@ -20,6 +20,7 @@ import { createExtensionDataRef } from './createExtensionDataRef'; /** @public */ export const coreExtensionData = { + title: createExtensionDataRef().with({ id: 'core.title' }), reactElement: createExtensionDataRef().with({ id: 'core.reactElement', }), diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index 30cdd91704..cf28d80c11 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -92,12 +92,12 @@ export const catalogReactTranslationRef: TranslationRef< readonly 'entityTableColumnTitle.title': 'Title'; readonly 'entityTableColumnTitle.description': 'Description'; readonly 'entityTableColumnTitle.domain': 'Domain'; + readonly 'entityTableColumnTitle.system': 'System'; + readonly 'entityTableColumnTitle.tags': 'Tags'; readonly 'entityTableColumnTitle.namespace': 'Namespace'; readonly 'entityTableColumnTitle.lifecycle': 'Lifecycle'; readonly 'entityTableColumnTitle.owner': 'Owner'; - readonly 'entityTableColumnTitle.system': 'System'; readonly 'entityTableColumnTitle.targets': 'Targets'; - readonly 'entityTableColumnTitle.tags': 'Tags'; } >; @@ -533,8 +533,8 @@ export const EntityTableColumnTitle: ({ translationKey, }: EntityTableColumnTitleProps) => | 'Title' - | 'Domain' | 'System' + | 'Domain' | 'Lifecycle' | 'Namespace' | 'Owner' From 8b912380212386ac2b95e3f5ff906125065251d8 Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Tue, 8 Jul 2025 17:33:19 +0100 Subject: [PATCH 041/211] feat(http): support server-level timeout and socket options via app-config Adds support for configuring server-level HTTP options through the `app-config.yaml` file under the `backend.server` key. This includes support for: `headersTimeout`, `keepAliveTimeout`, `requestTimeout`, `timeout`, `maxHeadersCount`, and `maxRequestsPerSocket`. These options are passed directly to the underlying Node.js HTTP server, when omitted, the default values are used. Refs: https://github.com/backstage/backstage/issues/21808 Refs: https://github.com/backstage/backstage/issues/30449 Signed-off-by: Beth Griggs --- .changeset/salty-words-wash.md | 11 ++++ .../core-services/root-http-router.md | 8 +++ .../rootHttpRouter/http/config.test.ts | 53 +++++++++++++++++++ .../entrypoints/rootHttpRouter/http/config.ts | 30 +++++++++++ .../rootHttpRouter/http/createHttpServer.ts | 29 ++++++++-- .../entrypoints/rootHttpRouter/http/types.ts | 8 +++ 6 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 .changeset/salty-words-wash.md diff --git a/.changeset/salty-words-wash.md b/.changeset/salty-words-wash.md new file mode 100644 index 0000000000..eec2e85331 --- /dev/null +++ b/.changeset/salty-words-wash.md @@ -0,0 +1,11 @@ +--- +'@backstage/backend-defaults': minor +--- + +Adds support for configuring server-level HTTP options through the +`app-config.yaml` file under the `backend.server` key. Supported options +include `headersTimeout`, `keepAliveTimeout`, `requestTimeout`, `timeout`, +`maxHeadersCount`, and `maxRequestsPerSocket`. + +These are passed directly to the underlying Node.js HTTP server. +If omitted, Node.js defaults are used. diff --git a/docs/backend-system/core-services/root-http-router.md b/docs/backend-system/core-services/root-http-router.md index 1e76fb1871..0ced7aac66 100644 --- a/docs/backend-system/core-services/root-http-router.md +++ b/docs/backend-system/core-services/root-http-router.md @@ -65,6 +65,14 @@ backend: # - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M'. # - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }`. serverShutdownDelay: { seconds: 20 } + server: + # (Optional) HTTP server configuration, Node.js defaults apply otherwise + headersTimeout: 60000 + keepAliveTimeout: 5000 + maxHeadersCount: 2000 + maxRequestsPerSocket: 100 + requestTimeout: 30000 + timeout: 30000 ``` ### Via Code diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts index 577bae08f3..0fa9dc0420 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts @@ -61,6 +61,59 @@ describe('readHttpServerOptions', () => { expect(readHttpServerOptions(new ConfigReader(input))).toEqual(output); }); + it.each([ + [ + { + server: { + headersTimeout: 10000, + requestTimeout: 30000, + keepAliveTimeout: 5000, + timeout: 60000, + maxHeadersCount: 1000, + maxRequestsPerSocket: 100, + }, + }, + { + listen: { host: '', port: 7007 }, + https: undefined, + serverOptions: { + headersTimeout: 10000, + requestTimeout: 30000, + keepAliveTimeout: 5000, + timeout: 60000, + maxHeadersCount: 1000, + maxRequestsPerSocket: 100, + }, + }, + ], + [ + { + server: { + keepAliveTimeout: 8000, + timeout: 30000, + }, + }, + { + listen: { host: '', port: 7007 }, + https: undefined, + serverOptions: { + keepAliveTimeout: 8000, + timeout: 30000, + }, + }, + ], + [ + { server: {} }, + { + listen: { host: '', port: 7007 }, + https: undefined, + serverOptions: undefined, + }, + ], + ])('should read server options %#', (input, output) => { + expect(readHttpServerOptions(new ConfigReader(input))).toEqual(output); + }); + it.each([ [ { listen: { port: 'not-a-number' } }, diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts index ec29a8a044..229ba2d78d 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts @@ -38,6 +38,7 @@ export function readHttpServerOptions(config?: Config): HttpServerOptions { return { listen: readHttpListenOptions(config), https: readHttpsOptions(config), + serverOptions: readServerOptions(config), }; } @@ -99,3 +100,32 @@ function readHttpsOptions(config?: Config): HttpServerOptions['https'] { }, }; } + +function readServerOptions( + config?: Config, +): HttpServerOptions['serverOptions'] { + const serverConfig = config?.getOptionalConfig('server'); + if (!serverConfig) { + return undefined; + } + + const serverOptions: HttpServerOptions['serverOptions'] = {}; + + const keys = [ + 'headersTimeout', + 'requestTimeout', + 'keepAliveTimeout', + 'timeout', + 'maxHeadersCount', + 'maxRequestsPerSocket', + ] as const; + + for (const key of keys) { + const value = serverConfig.getOptionalNumber(key); + if (value !== undefined) { + serverOptions[key] = value; + } + } + + return Object.keys(serverOptions).length === 0 ? undefined : serverOptions; +} diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts index fbb6ac5805..1ae6cc4fd1 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts @@ -84,6 +84,8 @@ async function createServer( options: HttpServerOptions, deps: { logger: LoggerService }, ): Promise { + let server: http.Server; + if (options.https) { const { certificate } = options.https; if (certificate.type === 'generated') { @@ -91,10 +93,31 @@ async function createServer( certificate.hostname, deps.logger, ); - return https.createServer(credentials, listener); + server = https.createServer(credentials, listener); + } else { + server = https.createServer(certificate, listener); } - return https.createServer(certificate, listener); + } else { + server = http.createServer(listener); } - return http.createServer(listener); + // apply custom server options + if (options.serverOptions) { + const { serverOptions } = options; + + if (serverOptions.headersTimeout !== undefined) + server.headersTimeout = serverOptions.headersTimeout; + if (serverOptions.requestTimeout !== undefined) + server.requestTimeout = serverOptions.requestTimeout; + if (serverOptions.keepAliveTimeout !== undefined) + server.keepAliveTimeout = serverOptions.keepAliveTimeout; + if (serverOptions.timeout !== undefined) + server.timeout = serverOptions.timeout; + if (serverOptions.maxHeadersCount !== undefined) + server.maxHeadersCount = serverOptions.maxHeadersCount; + if (serverOptions.maxRequestsPerSocket !== undefined) + server.maxRequestsPerSocket = serverOptions.maxRequestsPerSocket; + } + + return server; } diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts index 2298ef11ff..13e7914c03 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts @@ -42,6 +42,14 @@ export type HttpServerOptions = { https?: { certificate: HttpServerCertificateOptions; }; + serverOptions?: { + headersTimeout?: number; + requestTimeout?: number; + keepAliveTimeout?: number; + timeout?: number; + maxHeadersCount?: number; + maxRequestsPerSocket?: number; + }; }; /** From ee06f7d181166a06c03659c56c9e4f0a00b23a28 Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Wed, 9 Jul 2025 16:49:38 +0100 Subject: [PATCH 042/211] fixup! add API report Signed-off-by: Beth Griggs --- packages/backend-defaults/report-rootHttpRouter.api.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/backend-defaults/report-rootHttpRouter.api.md b/packages/backend-defaults/report-rootHttpRouter.api.md index 84424be39d..acabea9640 100644 --- a/packages/backend-defaults/report-rootHttpRouter.api.md +++ b/packages/backend-defaults/report-rootHttpRouter.api.md @@ -82,6 +82,14 @@ export type HttpServerOptions = { https?: { certificate: HttpServerCertificateOptions; }; + serverOptions?: { + headersTimeout?: number; + requestTimeout?: number; + keepAliveTimeout?: number; + timeout?: number; + maxHeadersCount?: number; + maxRequestsPerSocket?: number; + }; }; // @public From d501e8c7686500428b008d869a6297cceb464bff Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Fri, 12 Sep 2025 15:55:49 +0100 Subject: [PATCH 043/211] feat(http): move HTTP server configuration application, enable flexible duration formats This commit applies HTTP server configuration in `rootHttpRouterServiceFactory` and adds support for multiple duration formats for timeouts. The previous tests for server config have been moved and refactored. The documentation and CHANGELOG have been updated to reflect the new configuration structure and supported formats. Signed-off-by: Beth Griggs --- .../core-services/root-http-router.md | 12 ++- packages/backend-defaults/config.d.ts | 25 ++++++ .../report-rootHttpRouter.api.md | 8 -- .../rootHttpRouter/http/config.test.ts | 53 ------------- .../entrypoints/rootHttpRouter/http/config.ts | 30 -------- .../rootHttpRouter/http/createHttpServer.ts | 29 +------ .../entrypoints/rootHttpRouter/http/types.ts | 8 -- .../rootHttpRouterServiceFactory.test.ts | 61 +++++++++++++++ .../rootHttpRouterServiceFactory.ts | 77 +++++++++++++++++++ 9 files changed, 175 insertions(+), 128 deletions(-) diff --git a/docs/backend-system/core-services/root-http-router.md b/docs/backend-system/core-services/root-http-router.md index 0ced7aac66..1bda95a990 100644 --- a/docs/backend-system/core-services/root-http-router.md +++ b/docs/backend-system/core-services/root-http-router.md @@ -67,12 +67,18 @@ backend: serverShutdownDelay: { seconds: 20 } server: # (Optional) HTTP server configuration, Node.js defaults apply otherwise + # Timeout values support multiple formats: + # - Numbers (milliseconds): 30000 + # - Duration strings: '30s', '1 minute', '2 hours' + # - ISO duration strings: 'PT30S', 'PT1M', 'PT2H' + # - Duration objects: { seconds: 30 }, { minutes: 1 }, { hours: 2 } headersTimeout: 60000 - keepAliveTimeout: 5000 + requestTimeout: '30s' + keepAliveTimeout: { seconds: 5 } + timeout: 'PT30S' + # Numeric-only settings maxHeadersCount: 2000 maxRequestsPerSocket: 100 - requestTimeout: 30000 - timeout: 30000 ``` ### Via Code diff --git a/packages/backend-defaults/config.d.ts b/packages/backend-defaults/config.d.ts index 2982557f32..f0632a37bb 100644 --- a/packages/backend-defaults/config.d.ts +++ b/packages/backend-defaults/config.d.ts @@ -95,6 +95,31 @@ export interface Config { }; }; + /** + * Server-level HTTP options configuration for the backend. + * These options are passed directly to the underlying Node.js HTTP server. + * + * Timeout values support multiple formats: + * - A number in milliseconds + * - A string in the format of '1d', '2 seconds' etc. as supported by the `ms` library + * - A standard ISO formatted duration string, e.g. 'P2DT6H' or 'PT1M' + * - An object with individual units (in plural) as keys, e.g. `{ days: 2, hours: 6 }` + */ + server?: { + /** Sets the timeout value for receiving the complete HTTP headers from the client. */ + headersTimeout?: number | string | HumanDuration; + /** Sets the timeout value for receiving the entire request (headers and body) from the client. */ + requestTimeout?: number | string | HumanDuration; + /** Sets the timeout value for inactivity on a socket during keep-alive. */ + keepAliveTimeout?: number | string | HumanDuration; + /** Sets the timeout value for sockets. */ + timeout?: number | string | HumanDuration; + /** Limits maximum incoming headers count. */ + maxHeadersCount?: number; + /** Sets the maximum number of requests socket can handle before closing keep alive connection. */ + maxRequestsPerSocket?: number; + }; + /** * Options used by the default auditor service. */ diff --git a/packages/backend-defaults/report-rootHttpRouter.api.md b/packages/backend-defaults/report-rootHttpRouter.api.md index acabea9640..84424be39d 100644 --- a/packages/backend-defaults/report-rootHttpRouter.api.md +++ b/packages/backend-defaults/report-rootHttpRouter.api.md @@ -82,14 +82,6 @@ export type HttpServerOptions = { https?: { certificate: HttpServerCertificateOptions; }; - serverOptions?: { - headersTimeout?: number; - requestTimeout?: number; - keepAliveTimeout?: number; - timeout?: number; - maxHeadersCount?: number; - maxRequestsPerSocket?: number; - }; }; // @public diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts index 0fa9dc0420..577bae08f3 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.test.ts @@ -61,59 +61,6 @@ describe('readHttpServerOptions', () => { expect(readHttpServerOptions(new ConfigReader(input))).toEqual(output); }); - it.each([ - [ - { - server: { - headersTimeout: 10000, - requestTimeout: 30000, - keepAliveTimeout: 5000, - timeout: 60000, - maxHeadersCount: 1000, - maxRequestsPerSocket: 100, - }, - }, - { - listen: { host: '', port: 7007 }, - https: undefined, - serverOptions: { - headersTimeout: 10000, - requestTimeout: 30000, - keepAliveTimeout: 5000, - timeout: 60000, - maxHeadersCount: 1000, - maxRequestsPerSocket: 100, - }, - }, - ], - [ - { - server: { - keepAliveTimeout: 8000, - timeout: 30000, - }, - }, - { - listen: { host: '', port: 7007 }, - https: undefined, - serverOptions: { - keepAliveTimeout: 8000, - timeout: 30000, - }, - }, - ], - [ - { server: {} }, - { - listen: { host: '', port: 7007 }, - https: undefined, - serverOptions: undefined, - }, - ], - ])('should read server options %#', (input, output) => { - expect(readHttpServerOptions(new ConfigReader(input))).toEqual(output); - }); - it.each([ [ { listen: { port: 'not-a-number' } }, diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts index 229ba2d78d..ec29a8a044 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/config.ts @@ -38,7 +38,6 @@ export function readHttpServerOptions(config?: Config): HttpServerOptions { return { listen: readHttpListenOptions(config), https: readHttpsOptions(config), - serverOptions: readServerOptions(config), }; } @@ -100,32 +99,3 @@ function readHttpsOptions(config?: Config): HttpServerOptions['https'] { }, }; } - -function readServerOptions( - config?: Config, -): HttpServerOptions['serverOptions'] { - const serverConfig = config?.getOptionalConfig('server'); - if (!serverConfig) { - return undefined; - } - - const serverOptions: HttpServerOptions['serverOptions'] = {}; - - const keys = [ - 'headersTimeout', - 'requestTimeout', - 'keepAliveTimeout', - 'timeout', - 'maxHeadersCount', - 'maxRequestsPerSocket', - ] as const; - - for (const key of keys) { - const value = serverConfig.getOptionalNumber(key); - if (value !== undefined) { - serverOptions[key] = value; - } - } - - return Object.keys(serverOptions).length === 0 ? undefined : serverOptions; -} diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts index 1ae6cc4fd1..fbb6ac5805 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/createHttpServer.ts @@ -84,8 +84,6 @@ async function createServer( options: HttpServerOptions, deps: { logger: LoggerService }, ): Promise { - let server: http.Server; - if (options.https) { const { certificate } = options.https; if (certificate.type === 'generated') { @@ -93,31 +91,10 @@ async function createServer( certificate.hostname, deps.logger, ); - server = https.createServer(credentials, listener); - } else { - server = https.createServer(certificate, listener); + return https.createServer(credentials, listener); } - } else { - server = http.createServer(listener); + return https.createServer(certificate, listener); } - // apply custom server options - if (options.serverOptions) { - const { serverOptions } = options; - - if (serverOptions.headersTimeout !== undefined) - server.headersTimeout = serverOptions.headersTimeout; - if (serverOptions.requestTimeout !== undefined) - server.requestTimeout = serverOptions.requestTimeout; - if (serverOptions.keepAliveTimeout !== undefined) - server.keepAliveTimeout = serverOptions.keepAliveTimeout; - if (serverOptions.timeout !== undefined) - server.timeout = serverOptions.timeout; - if (serverOptions.maxHeadersCount !== undefined) - server.maxHeadersCount = serverOptions.maxHeadersCount; - if (serverOptions.maxRequestsPerSocket !== undefined) - server.maxRequestsPerSocket = serverOptions.maxRequestsPerSocket; - } - - return server; + return http.createServer(listener); } diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts index 13e7914c03..2298ef11ff 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/http/types.ts @@ -42,14 +42,6 @@ export type HttpServerOptions = { https?: { certificate: HttpServerCertificateOptions; }; - serverOptions?: { - headersTimeout?: number; - requestTimeout?: number; - keepAliveTimeout?: number; - timeout?: number; - maxHeadersCount?: number; - maxRequestsPerSocket?: number; - }; }; /** diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.test.ts index 0f2b6463e4..8f8d1b7656 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.test.ts @@ -323,4 +323,65 @@ describe('rootHttpRouterServiceFactory', () => { }), ).resolves.toBeUndefined(); }); + + it('should start successfully with server configuration options', async () => { + const { app } = await createExpressApp( + mockServices.rootConfig.factory({ + data: { + backend: { + listen: { port: 0 }, + server: { + headersTimeout: 30000, + requestTimeout: '45s', + keepAliveTimeout: 'PT1M', + timeout: { seconds: 120 }, + maxHeadersCount: 2000, + maxRequestsPerSocket: 100, + }, + }, + }, + }), + ); + + // Verify the server starts and responds to health checks + await request(app) + .get('/.backstage/health/v1/liveness') + .expect(200, { status: 'ok' }); + }); + + it('should start successfully with partial server configuration', async () => { + const { app } = await createExpressApp( + mockServices.rootConfig.factory({ + data: { + backend: { + listen: { port: 0 }, + server: { + headersTimeout: 60000, + maxHeadersCount: 1500, + }, + }, + }, + }), + ); + + await request(app) + .get('/.backstage/health/v1/liveness') + .expect(200, { status: 'ok' }); + }); + + it('should start successfully with no server configuration', async () => { + const { app } = await createExpressApp( + mockServices.rootConfig.factory({ + data: { + backend: { + listen: { port: 0 }, + }, + }, + }), + ); + + await request(app) + .get('/.backstage/health/v1/liveness') + .expect(200, { status: 'ok' }); + }); }); diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts index 3b50515742..1bbf986a98 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts @@ -117,6 +117,83 @@ const rootHttpRouterServiceFactoryWithOptions = ( if (trustProxy !== undefined) { app.set('trust proxy', trustProxy); } + + // Apply server-level HTTP options from config + const backendConfig = config.getOptionalConfig('backend'); + const serverConfig = backendConfig?.getOptionalConfig('server'); + + if (serverConfig) { + // Helper function to read duration values (supporting number, string, or HumanDuration) + const readDurationValue = (key: string): number | undefined => { + if (!serverConfig.has(key)) { + return undefined; + } + + const value = serverConfig.getOptional(key); + if (typeof value === 'number') { + return value; + } + + // If it's not a number, try to read it as a duration + try { + const duration = readDurationFromConfig(serverConfig, { key }); + return durationToMilliseconds(duration); + } catch (error) { + // Log warning for parsing failures + logger.warn( + `Failed to parse backend.server.${key} as duration: ${error}. ` + + `Expected a number (milliseconds), duration string (e.g., '30s'), ` + + `ISO duration (e.g., 'PT30S'), or duration object (e.g., {seconds: 30}). ` + + `Falling back to number parsing.`, + ); + // Fallback to reading as number if duration parsing fails + const fallbackValue = serverConfig.getOptionalNumber(key); + if (fallbackValue === undefined && typeof value === 'string') { + logger.error( + `backend.server.${key} value '${value}' could not be parsed as either ` + + `a duration or a number. This setting will be ignored.`, + ); + } + return fallbackValue; + } + }; + + // Apply timeout settings + const headersTimeout = readDurationValue('headersTimeout'); + if (headersTimeout !== undefined) { + server.headersTimeout = headersTimeout; + } + + const requestTimeout = readDurationValue('requestTimeout'); + if (requestTimeout !== undefined) { + server.requestTimeout = requestTimeout; + } + + const keepAliveTimeout = readDurationValue('keepAliveTimeout'); + if (keepAliveTimeout !== undefined) { + server.keepAliveTimeout = keepAliveTimeout; + } + + const timeout = readDurationValue('timeout'); + if (timeout !== undefined) { + server.timeout = timeout; + } + + // Apply numeric settings + const maxHeadersCount = + serverConfig.getOptionalNumber('maxHeadersCount'); + if (maxHeadersCount !== undefined) { + server.maxHeadersCount = maxHeadersCount; + } + + const maxRequestsPerSocket = serverConfig.getOptionalNumber( + 'maxRequestsPerSocket', + ); + if (maxRequestsPerSocket !== undefined) { + server.maxRequestsPerSocket = maxRequestsPerSocket; + } + } + app.use(middleware.helmet()); app.use(middleware.cors()); app.use(middleware.compression()); From b8a381e4f150bd75de8f62264a2a089bfc704acd Mon Sep 17 00:00:00 2001 From: Tim Klever Date: Mon, 18 Aug 2025 17:30:26 -0700 Subject: [PATCH 044/211] chore: remove `isomorphic-form-data` from dependencies The need to explicitly specify this dependency was resolved. Issue: https://github.com/swagger-api/swagger-ui/issues/7436 Resolution: https://github.com/swagger-api/swagger-ui/issues/7436#issuecomment-889792304 Signed-off-by: Tim Klever --- .changeset/short-aliens-invite.md | 13 + plugins/api-docs/package.json | 3 +- yarn.lock | 807 +++++++++++++++--------------- 3 files changed, 419 insertions(+), 404 deletions(-) create mode 100644 .changeset/short-aliens-invite.md diff --git a/.changeset/short-aliens-invite.md b/.changeset/short-aliens-invite.md new file mode 100644 index 0000000000..ae3bbc3831 --- /dev/null +++ b/.changeset/short-aliens-invite.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-api-docs': minor +--- + +Remove explicit dependency on `isomorphic-form-data`. + +This explicit dependency was added to address [an issue](https://github.com/swagger-api/swagger-ui/issues/7436) in the +dependency `swagger-ui-react`. That [issue has since been resolved](https://github.com/swagger-api/swagger-ui/issues/7436#issuecomment-889792304), +and `isomorphic-form-data` no longer needs to be declared. + +Additionally, this changeset updates the `swagger-ui-react` dependency to version `5.19.0` or higher, which includes +[compatibility](https://github.com/swagger-api/swagger-ui?tab=readme-ov-file#compatibility) with the latest versions of +the OpenAPI specification. diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index febec4ff08..810279ed39 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -71,8 +71,7 @@ "graphql": "^16.0.0", "graphql-config": "^5.0.2", "graphql-ws": "^5.4.1", - "isomorphic-form-data": "^2.0.0", - "swagger-ui-react": "^5.0.0" + "swagger-ui-react": "^5.27.1" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/yarn.lock b/yarn.lock index e606b8246d..71e66f926b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2419,13 +2419,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.15, @babel/runtime-corejs3@npm:^7.24.7": - version: 7.27.0 - resolution: "@babel/runtime-corejs3@npm:7.27.0" +"@babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.15, @babel/runtime-corejs3@npm:^7.26.10, @babel/runtime-corejs3@npm:^7.27.1": + version: 7.28.4 + resolution: "@babel/runtime-corejs3@npm:7.28.4" dependencies: - core-js-pure: "npm:^3.30.2" - regenerator-runtime: "npm:^0.14.0" - checksum: 10/034628bf1b602729b53022f99fd82baca9347db435843c677b888cd7e71412aa4a3065806d622b8e98971577285b4bd546dfc8b57c14aea43a22934a1a3beca4 + core-js-pure: "npm:^3.43.0" + checksum: 10/99079931145c0606a9967fe002c3528ae237b759cee115fc97a5dc17101d5ccdf9a794fd4ce5d94c7e2e8ee1f9f6816fb50b4472e980b5e4dd878fbdfac02619 languageName: node linkType: hard @@ -3643,11 +3642,10 @@ __metadata: graphql: "npm:^16.0.0" graphql-config: "npm:^5.0.2" graphql-ws: "npm:^5.4.1" - isomorphic-form-data: "npm:^2.0.0" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.3.0" - swagger-ui-react: "npm:^5.0.0" + swagger-ui-react: "npm:^5.27.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 @@ -7620,13 +7618,6 @@ __metadata: languageName: node linkType: hard -"@braintree/sanitize-url@npm:=7.0.4": - version: 7.0.4 - resolution: "@braintree/sanitize-url@npm:7.0.4" - checksum: 10/80ea0080776a0305d697d12042acac287675e88a2abd9d294464f70ec57c1b00242d8d02a110c98ef8ea1731e512d67273ff5532c4bf01a78ab8b046fabb53d9 - languageName: node - linkType: hard - "@bundled-es-modules/cookie@npm:^2.0.1": version: 2.0.1 resolution: "@bundled-es-modules/cookie@npm:2.0.1" @@ -18486,491 +18477,489 @@ __metadata: languageName: node linkType: hard -"@swagger-api/apidom-ast@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ast@npm:1.0.0-beta.11" +"@swagger-api/apidom-ast@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ast@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" unraw: "npm:^3.0.0" - checksum: 10/efe7caf37735f6b1a9b56ca780ce59134540c00c50ef00606607a458905142b7794466cacbffdcd9afe11ece9f5a99651f2f62d24b05f71727d86718e2dea91c + checksum: 10/b3f2b91df2b9116db318ce44551d3846453b7d9f17eb809c19890ecbbc17166ad6a1fa54698b79e85600960e2f1dd2ea56487d9a50537ee362a1a9dd63f91840 languageName: node linkType: hard -"@swagger-api/apidom-core@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-core@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-core@npm:1.0.0-beta.11" +"@swagger-api/apidom-core@npm:>=1.0.0-beta.41 <1.0.0-rc.0, @swagger-api/apidom-core@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-core@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" minim: "npm:~0.23.8" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - short-unique-id: "npm:^5.0.2" + short-unique-id: "npm:^5.3.2" ts-mixer: "npm:^6.0.3" - checksum: 10/bb81c1ef603b80985c8e7ad8808a12eaaf2daa3de2a41ac2f9f8c041c7a7f26bcbfa9713738248db198af1a6b424127b10abd5ac7e6fcad95d852cea8fb8a526 + checksum: 10/abf7328a4d821f6083daa80fe1e719511825ff855dc4be59a4147a100c1ae34254cba4050ecc29e5147877f7e9d797681e96931da0ce432c310aaae82cd5a371 languageName: node linkType: hard -"@swagger-api/apidom-error@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-error@npm:^1.0.0-beta.11, @swagger-api/apidom-error@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-error@npm:1.0.0-beta.11" +"@swagger-api/apidom-error@npm:>=1.0.0-beta.41 <1.0.0-rc.0, @swagger-api/apidom-error@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-error@npm:1.0.0-beta.48" dependencies: "@babel/runtime-corejs3": "npm:^7.20.7" - checksum: 10/d63ec68067a17c10fa51b2929c18d18f397d91ab45961e398ad99341b45e87d60a0ec6f634ac3883733ea7f4a5155e9929207dd44572f65754fa0f5b7d004466 + checksum: 10/ce6461f5b06a6297074949d1aabd605c272c3061241e4d47f3720707ac8bc41c272deed033ae131fb5620bd6d709e0744275353ed490f7eca81f75db94abfc7e languageName: node linkType: hard -"@swagger-api/apidom-json-pointer@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.11, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-json-pointer@npm:1.0.0-beta.11" +"@swagger-api/apidom-json-pointer@npm:>=1.0.0-beta.41 <1.0.0-rc.0, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-json-pointer@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-json-pointer@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@types/ramda": "npm:~0.30.0" - ramda: "npm:~0.30.0" - ramda-adjunct: "npm:^5.0.0" - checksum: 10/dee858ee4b4a0f93ab082451a703c8b9e0250f7b486d8d9ef243b77f92acc49741cfdd1e10a9cc44d946b64e469aa4016a1380c7a28d660fe048a2897098afc7 + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swaggerexpert/json-pointer": "npm:^2.10.1" + checksum: 10/2075f4d64813128ee80da21f16fd96bc51cf232505d920fc77af86acd2ea1db342936753b824153b232e9522d4a7f389fd870afdf3518b0af91f497119b6e1c5 languageName: node linkType: hard -"@swagger-api/apidom-ns-api-design-systems@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-api-design-systems@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-api-design-systems@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-api-design-systems@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.3" - checksum: 10/db2d3aa5342eaa8fc9205116bc1364fee56815dd43e3f57e7b27725ea081dcf897e5343256a9941493cd72e5459faa242c347bb3ff523425026d890ac9776b96 + checksum: 10/48e6a4f323572715f8990f83f6a1617af9d7ca0c6cd0e08f9b05c6f178664da233498c7c2461f636a97a525aaa60748c1da2a8d949ed43a7a14c1639d382f737 languageName: node linkType: hard -"@swagger-api/apidom-ns-asyncapi-2@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-asyncapi-2@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-asyncapi-2@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-arazzo-1@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-ns-arazzo-1@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-arazzo-1@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-draft-7": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-2020-12": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.3" - checksum: 10/635618e980a276e607e7faf286bb43dc38d6419745199df952ccced7ca86840bc9b5320698ddb00238f06cba49eeddf4275e12e121b932f8601656cb388af2a8 + checksum: 10/3b9f60ec9ca9dccc5e3d46a48a6d7b30915294bdb7dc6ee481aae57de707485885f887ad9b9daec469712c087e6d74ed748ac8d748ed6d19cb1fb6219141bb12 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-2019-09@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-json-schema-2019-09@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-asyncapi-2@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-ns-asyncapi-2@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-asyncapi-2@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-draft-7": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-draft-7": "npm:^1.0.0-beta.48" + "@types/ramda": "npm:~0.30.0" + ramda: "npm:~0.30.0" + ramda-adjunct: "npm:^5.0.0" + ts-mixer: "npm:^6.0.3" + checksum: 10/99bcd30eb7c14334b2ebe3adfdb7ad8cf93d9565fc72217863a82b1ba48d4b5839b639964cf830bf9ba64c41c7a7d7eedeb53d8d28e5379bde1fafa8b424ab02 + languageName: node + linkType: hard + +"@swagger-api/apidom-ns-json-schema-2019-09@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-json-schema-2019-09@npm:1.0.0-beta.48" + dependencies: + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-draft-7": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/b91f90e7376922f1112752520bcac265c20245c1d2d682cd5ba9344a3eb7287962f18e3430f501af4d4db43397a2977267b15ef02a3bcd6e8ba384901e195672 + checksum: 10/bed0ed8b2ef35def9da2b8cebb31dba0d83fab88a38ab1a1af4760d370f5b3d0a1dfda7f1f1991204eab97531ddc90c70c153cb3ba9a7a5b93be93f02d873097 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-2020-12@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-json-schema-2020-12@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-json-schema-2020-12@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-json-schema-2020-12@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-2019-09": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-2019-09": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/847103b0923fa7a0fcff124f5359f735f56e38a88666051e5959403e0060e33bea63b30098dec99d36bcda6619b2a3d13ce61c26e6ca03c508ea8518e871c123 + checksum: 10/6bfad975a9db33224bc980edafab8e39218d6eb0acc384881ba14b35061d2c51b45f3e778529f5cf5875330501c892f433319dc4613a2d08ae22cf33f832d91e languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-4@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-json-schema-draft-4@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/9e0ba9e29849915c0067227a82d8d9a741221b3ac73592b73e23d3cfe0c57548b21cbbb66cdeba914218c5f675f4ec497232d465880013f8aec8f486e4c3d91c + checksum: 10/860c09c46b6ef274afeaf4fb5aa0445dd9d394fb81f3d251d34c484ce31ca568e8d8e63acdfd207180dc28ac1a6fe082fe7139dbe1aadac2e5b81f42f80aefcd languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-6@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-json-schema-draft-6@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/0df53880202cdb8dbe1e534314842c2c10fe4cd03f6eecd56d67174492807df03134f360e1c0724610fc4531520c49bb41d81e6fd7373118031ad883e0101338 + checksum: 10/418d8c1cf20cc12841ee1d7e17bc57cbfa296d7833d4f656c650fcb5ade4bd646088177c9cc1c682b2ebd80fb3713de02b8dc562d155e3874c482e9d0d4edcc2 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-7@npm:^1.0.0-beta.11": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-json-schema-draft-7@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-draft-6": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-draft-6": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.4" - checksum: 10/9ef5261e58b2e6798a3c22ad01940c613475a6515d2cd79d2f150b02152118036ef472bb75a271c0d1c3b617d51ccb2262680469824335f27150600f695eb6d8 + checksum: 10/6e683458465f221c490440f94a1d1328a39db87a27c257a5d700b2effc937b8568f98f5042d74f56fdf6692da4b4027074c33c59348199fc821c45de9598e473 languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-2@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-openapi-2@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-openapi-2@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-openapi-2@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-2@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-openapi-2@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.3" - checksum: 10/1f58e871e77e310943b489cb734a88cd11a9d9baf61ec7330f975c056b83ab74efea9fdbf449c1652f562c5ef9bcac9cf010969e900f9b8b67ceb0f440dddf95 + checksum: 10/768fb7ec2c8506857837975529f311fe28359dd56a836c3b3cb1685bed623c489b5327e5fe58a41e70235ba525ba6401cc32fc9bd50bc5ec1cc0baf6fe144900 languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-0@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-draft-4": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.3" - checksum: 10/44eabda02fb8ad965b7756931bba7ac2ae9f49ef720f401179efb9eafbf94ff6b3c8cb8fd260c9ddb1affe35c3876f780cef4cb090b755736847adb7eba699c8 + checksum: 10/a005d660c2e476a3d1bb74da9a2efa7fb0a68b1f52ddbde01514f4db9ef14051833f55b001c812da44fce3445c145ce5fd62a137477a71a6bcad220115c1e440 languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-1@npm:>=1.0.0-beta.11 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:1.0.0-beta.11" +"@swagger-api/apidom-ns-openapi-3-1@npm:>=1.0.0-beta.41 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-ns-openapi-3-1@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-json-pointer": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-2020-12": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-json-pointer": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-json-schema-2020-12": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" ts-mixer: "npm:^6.0.3" - checksum: 10/6ed3a3d7bc231c8f3273c60dcd459cdb117246e372ed9d0d2483601a966a3109b9617a4d8f327f494a31013d5bcf2bc372c5f3297bbf953c61ff4770c87414a5 + checksum: 10/3423b3d642487d562376a572dac31fec760d3a64fe8bb4065ee0634a340c418656b2ea9cfc484059db5105e7389c74ac32620413243990141a7bbdc2e7cb1062 languageName: node linkType: hard -"@swagger-api/apidom-ns-workflows-1@npm:^1.0.0-beta.11, @swagger-api/apidom-ns-workflows-1@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-ns-workflows-1@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-json-schema-2020-12": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-api-design-systems": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - ts-mixer: "npm:^6.0.3" - checksum: 10/e8eb8bbbd1aa9c3663f2a5af11d67dd77f32bd983fdf388d3b1a480d9c4886de7e4f2d70baf27da3d7d1c23a4e4118aa2fe27733bbe654ba58b177c9e7945d37 + checksum: 10/ac86988052a7ad9f03c389cef02b22da36d6cbaae6c670c7642615de7f6efe921b3991f14a55793f803e5b5cb9eb0bcda30857a70a0a0d1c31d8f2d8023e78c6 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-api-design-systems": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-api-design-systems": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/934c4c70f8881a8540bd40d3b062d4da4b23d13b07de558e241ac7c442bcf85ce6451c4c93b243705426298720272c43e5a67805ab159c023ff3e37e99900bd6 + checksum: 10/c0c9f35707afe05c559c8edacc0f548d3916f8f0cb0b4800b441258cbd871ba5c8ed083758b82bbe2589f022964bcd199f8d839648a78e979c94234e7ff22a08 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-arazzo-json-1@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-arazzo-json-1@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-api-design-systems": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-arazzo-1": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/0c8f2b77f60ceb100262fe91b9c85e88c6290ba0841522867aaa191bbbf09a61014294837ee14593fe36d6d37db8d4e81b24c5614cd600aaf2015ce2f9ec2b8b + checksum: 10/ec8bd9268b09d3446bb922d881ceab4f52164f995409a305dfa140b9c9790c2f75bf9d59bbc1d6859e1943bc1376ee72a6b7865eaf4b2043795c8576d489ce1b languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-arazzo-yaml-1@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-arazzo-yaml-1@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-asyncapi-2": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-arazzo-1": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/761380c063b773c16a7f2036eb8f2f5bb8a8850dbaf7cfcfb271e2bd9ba8a957049a63263ce782ced8c481c420aa0995a5833b6d20c07627c46ff26f1db4c180 + checksum: 10/285a55e288475bd20a960fcc90d61bb0b29c8fc586a77ac3ba254c7eacc11db95a964648ae61e8deec20d90bfe32ea5eb11ec8857c6c4b44061c98045be75d5c languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-asyncapi-2": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-asyncapi-2": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/4e1842525b04e8992bc1fb5729c27589af8c3fb785b6231b312e159c5aaf1b5d823f5974de6d77d1e9e8941c25ab755d64eac15b7dba5a6c750b98c0ce52648a + checksum: 10/1011a2be2fd576013ae5b725592c49f99dc42c50127d2707dc48893a33eb94a3820f62be6650f5bcbdb987034b5ef150905415eb44c7384a3376da97982bc467 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-json@npm:^1.0.0-beta.11, @swagger-api/apidom-parser-adapter-json@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-json@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-asyncapi-2": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.48" + "@types/ramda": "npm:~0.30.0" + ramda: "npm:~0.30.0" + ramda-adjunct: "npm:^5.0.0" + checksum: 10/c6f28dc27fe796ef3c8440afaa3ce78163e1290fd3c73437541eb629b028d4552537fdeaf29cfd9415774ee2909321289f5c9eb4682e6c5cbf6981ddabaf78b5 + languageName: node + linkType: hard + +"@swagger-api/apidom-parser-adapter-json@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-parser-adapter-json@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-json@npm:1.0.0-beta.48" + dependencies: + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" node-gyp: "npm:latest" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - tree-sitter: "npm:=0.22.1" + tree-sitter: "npm:=0.21.1" tree-sitter-json: "npm:=0.24.8" web-tree-sitter: "npm:=0.24.5" - checksum: 10/5a80f7e83d6b7e26330caf1aef5554f3d87350c1510a31829ee2b0c296d82eacda643247e1cf1628db4ec37c301a87628a130a3aaa4ad3f9e2ff9b71369e96de + checksum: 10/ddb74c62c2a28d359584d385027db17c9630186e6f007e28220379ac27b2875c236b5a748101ff0d46edc46a222287db3ba3e0f43345ce2cf978cda691879b04 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-2@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-2@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-openapi-json-2@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-2@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-2": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-2": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/7ab4fb92a517363d5c8711eab3bafd8e425605501ed7f175af4aae23b0e87375bc41c76ba4bd62eb9afb03d18aaa721d0a6d6e2af336eb221e2c00f5181d7cf3 + checksum: 10/c5d4ebfa13c58487626a9b2764995f690db5bbe25de80212d9f216451b20716facb67f3fccae03ed986810d5ecb832756716ba86fcd506b1a3e974aa0e3117dc languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/3d2f9b0d8792cb590afa1d6ba12166a086db78e7cacc31cf53d1ed8818bb8e55b87d957807632aabd21f2b5c3bd3c25d219730371914d2943edefaa8eea62b3d + checksum: 10/b2e2d71cbb862ec477ebdda8f0e71353c58cef40b1d709ab8cdcea364757f7ffd2643c13a8ff60d0993ffe6a9229deaba6654fea6f3a890730a0b598183de1db languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/4821a09d2305159dac40bc4f0ae7b0ea5e061cc735b8511ef1f189ce897e450f3a0f026689c11fe3c90fdab1bc820c6c40d82606397ad3a63a9b9cd96a5fdbd0 + checksum: 10/a9db188dee99f38e0a91644362e87c526559ddfb35019ed69738a5bebdad645beab925f47d1f0ae2430ca10bd83f329a4007bb2b3b6d23c6fe32d23865fdd672 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-2": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-2": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/6646978d91369d19ddfc5d9f045cf2c3865b56bf73a3ddc13194bd2ad8d3840ca75beb4c39c13777616ddcf9775a371c44b6ec934f2cf00d62d7c99e68c96803 + checksum: 10/ed29926c1b08108cae294bc100fd279b555731ebc23edba2461e4817f82273003cf650f1eac0d1f2382ad590d76255e23ebf08438c3017ddcec7149e7140761d languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/357a930386a736acfefeca478bc6b115921cdc5df3b469ebc3704d35b84a5a1f92484a124c5dbd4b808e08b01a1afe50054aee95ec4f2787d3c38c1fd6f09d02 + checksum: 10/10d28a60ec9b4197d0c75bf05832c07fc1e8f61f72875ebe1baf328671c53499b51a9c065e1f0310c7c22aa6cb8f51ab2e8ba2692eee2b52f609982e8fe316c5 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:^1.0.0-beta.40 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.11" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.48" "@types/ramda": "npm:~0.30.0" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - checksum: 10/6a81e0b09fc4b8f37f47587d63c6faed2a69773a6e5033914e411db000ff3f293077d29e83dd8c386684a61ee41f243a5f36b45cf349d98ff089c866a19e50e6 + checksum: 10/2f31d09f7aba46b94ad2891097c3e2b880f6f76fab298dbcd794dd42c49fc0d9194f5a73abeb4f3140c4b53deff769f3eddbf16e70a9d764c36cdb24b4998071 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-workflows-json-1@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-workflows-json-1@npm:1.0.0-beta.11" +"@swagger-api/apidom-parser-adapter-yaml-1-2@npm:^1.0.0-beta.40 <1.0.0-rc.0, @swagger-api/apidom-parser-adapter-yaml-1-2@npm:^1.0.0-beta.48": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-parser-adapter-yaml-1-2@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-workflows-1": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.11" - "@types/ramda": "npm:~0.30.0" - ramda: "npm:~0.30.0" - ramda-adjunct: "npm:^5.0.0" - checksum: 10/76b15d74a32428b88efa9cad007e8814e4e5e88e6019228077175cd084fcc500011097f8cc309557d925d1569c3eed51de21d93d07a645f84138ee19aaf4f3d1 - languageName: node - linkType: hard - -"@swagger-api/apidom-parser-adapter-workflows-yaml-1@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-workflows-yaml-1@npm:1.0.0-beta.11" - dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-ns-workflows-1": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.11" - "@types/ramda": "npm:~0.30.0" - ramda: "npm:~0.30.0" - ramda-adjunct: "npm:^5.0.0" - checksum: 10/5a621d7bf92a9f513a9eaa13f5f03c05825a1e4d4f3d9b4d210ae39b69728bfa73bff3b66f582eed48d80970f9068dcf53ea6f86eec92f72ac94542cf589161f - languageName: node - linkType: hard - -"@swagger-api/apidom-parser-adapter-yaml-1-2@npm:^1.0.0-beta.11, @swagger-api/apidom-parser-adapter-yaml-1-2@npm:^1.0.0-beta.3 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-parser-adapter-yaml-1-2@npm:1.0.0-beta.11" - dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-ast": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.11" - "@tree-sitter-grammars/tree-sitter-yaml": "npm:=0.7.0" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-ast": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@tree-sitter-grammars/tree-sitter-yaml": "npm:=0.7.1" "@types/ramda": "npm:~0.30.0" node-gyp: "npm:latest" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" - tree-sitter: "npm:=0.22.1" + tree-sitter: "npm:=0.22.4" web-tree-sitter: "npm:=0.24.5" - checksum: 10/dbac6b4f61b38d30234c8e4f446938d9fa547ee40a43a4abc73df1a10d58bac0ee0c6fa372a1719b285957ab2adf0d56eda97f683e2de7c1f5ce7739db11ee5b + checksum: 10/c032b088ffef50e119fb9cb60e7a6edfc17ec09e40c6d5ee62f5561c4da806f29ef5ba850f701763c755491f323754c23bb5a07da0cbbd3ed7d673caf5364559 languageName: node linkType: hard -"@swagger-api/apidom-reference@npm:>=1.0.0-beta.11 <1.0.0-rc.0": - version: 1.0.0-beta.11 - resolution: "@swagger-api/apidom-reference@npm:1.0.0-beta.11" +"@swagger-api/apidom-reference@npm:>=1.0.0-beta.41 <1.0.0-rc.0": + version: 1.0.0-beta.48 + resolution: "@swagger-api/apidom-reference@npm:1.0.0-beta.48" dependencies: - "@babel/runtime-corejs3": "npm:^7.20.7" - "@swagger-api/apidom-core": "npm:^1.0.0-beta.11" - "@swagger-api/apidom-error": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-json-pointer": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-ns-asyncapi-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-ns-openapi-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-ns-workflows-1": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-api-design-systems-json": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-openapi-json-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-workflows-json-1": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-workflows-yaml-1": "npm:^1.0.0-beta.3 <1.0.0-rc.0" - "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.3 <1.0.0-rc.0" + "@babel/runtime-corejs3": "npm:^7.26.10" + "@swagger-api/apidom-core": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-error": "npm:^1.0.0-beta.48" + "@swagger-api/apidom-json-pointer": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-ns-arazzo-1": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-ns-asyncapi-2": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-ns-openapi-2": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-ns-openapi-3-0": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-ns-openapi-3-1": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-arazzo-json-1": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-json": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-openapi-json-2": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "npm:^1.0.0-beta.40 <1.0.0-rc.0" + "@swagger-api/apidom-parser-adapter-yaml-1-2": "npm:^1.0.0-beta.40 <1.0.0-rc.0" "@types/ramda": "npm:~0.30.0" - axios: "npm:^1.7.4" + axios: "npm:^1.9.0" minimatch: "npm:^7.4.3" process: "npm:^0.11.10" ramda: "npm:~0.30.0" ramda-adjunct: "npm:^5.0.0" dependenciesMeta: - "@swagger-api/apidom-error": - optional: true "@swagger-api/apidom-json-pointer": optional: true + "@swagger-api/apidom-ns-arazzo-1": + optional: true "@swagger-api/apidom-ns-asyncapi-2": optional: true "@swagger-api/apidom-ns-openapi-2": @@ -18979,12 +18968,14 @@ __metadata: optional: true "@swagger-api/apidom-ns-openapi-3-1": optional: true - "@swagger-api/apidom-ns-workflows-1": - optional: true "@swagger-api/apidom-parser-adapter-api-design-systems-json": optional: true "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": optional: true + "@swagger-api/apidom-parser-adapter-arazzo-json-1": + optional: true + "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": + optional: true "@swagger-api/apidom-parser-adapter-asyncapi-json-2": optional: true "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": @@ -19003,22 +18994,27 @@ __metadata: optional: true "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": optional: true - "@swagger-api/apidom-parser-adapter-workflows-json-1": - optional: true - "@swagger-api/apidom-parser-adapter-workflows-yaml-1": - optional: true "@swagger-api/apidom-parser-adapter-yaml-1-2": optional: true - checksum: 10/1ae856fd2d13884f8f063421c010f166da1602075ae3b08c041c98199c724615b389110672e2c1b129b7b551a21b8738d32df87c066cc105669a03b6b21897e6 + checksum: 10/fd86401690a3992123783c1b6044cdbb4a36b75d437e2519f6ec092d1b03273be45e884fc426e27a9413484047d40f819751f4f7174d9625de8a52a28d2d9e35 languageName: node linkType: hard -"@swaggerexpert/cookie@npm:^1.4.1": - version: 1.4.1 - resolution: "@swaggerexpert/cookie@npm:1.4.1" +"@swaggerexpert/cookie@npm:^2.0.2": + version: 2.0.2 + resolution: "@swaggerexpert/cookie@npm:2.0.2" dependencies: apg-lite: "npm:^1.0.3" - checksum: 10/936590cb70fb7af4ec988e7ee7c5e965b66d09046d3fd2b52c140f9777cda1452f2f24d80eb64ff9678b79f566dd0ef51699050aea9c9a3fd5345dcc73a3fa7a + checksum: 10/cd13848c944d381007a7d15955d5bdf261e956a8d825a9b0847d5ab37e9898082f11aaa558e324e0400be1cf03c408c14935c02150d9987da9bd3ff5b2982974 + languageName: node + linkType: hard + +"@swaggerexpert/json-pointer@npm:^2.10.1": + version: 2.10.2 + resolution: "@swaggerexpert/json-pointer@npm:2.10.2" + dependencies: + apg-lite: "npm:^1.0.4" + checksum: 10/3280d803804811c13c0dab27e57d362f0d6161a0e1fae6e335a227ca8e913f332a4e6ba3af7faeab70cc3d44357f9c5322d816f31b399ee6b383d9cd5abc829d languageName: node linkType: hard @@ -19364,19 +19360,19 @@ __metadata: languageName: node linkType: hard -"@tree-sitter-grammars/tree-sitter-yaml@npm:=0.7.0": - version: 0.7.0 - resolution: "@tree-sitter-grammars/tree-sitter-yaml@npm:0.7.0" +"@tree-sitter-grammars/tree-sitter-yaml@npm:=0.7.1": + version: 0.7.1 + resolution: "@tree-sitter-grammars/tree-sitter-yaml@npm:0.7.1" dependencies: - node-addon-api: "npm:^8.3.0" + node-addon-api: "npm:^8.3.1" node-gyp: "npm:latest" node-gyp-build: "npm:^4.8.4" peerDependencies: - tree-sitter: ^0.22.1 + tree-sitter: ^0.22.4 peerDependenciesMeta: tree-sitter: optional: true - checksum: 10/43cc2d98b084ee5301752f698b1f381008dd0d7aa9ee55390d78a7edf667c503c4f96d8839fe7bde0f4ddfb39c36c56da5b00ec2161e9e2d87a81e4a08a64419 + checksum: 10/870a1a807be7756607bec8d4c399713f94076174fdf694f035f4f6b2d7178bd6cca94a25a2b45b76ef45eb64c49d22c350fdd92acd55f2d6830a2cf771ccade3 languageName: node linkType: hard @@ -21255,10 +21251,10 @@ __metadata: languageName: node linkType: hard -"@types/use-sync-external-store@npm:^0.0.3": - version: 0.0.3 - resolution: "@types/use-sync-external-store@npm:0.0.3" - checksum: 10/161ddb8eec5dbe7279ac971531217e9af6b99f7783213566d2b502e2e2378ea19cf5e5ea4595039d730aa79d3d35c6567d48599f69773a02ffcff1776ec2a44e +"@types/use-sync-external-store@npm:^0.0.6": + version: 0.0.6 + resolution: "@types/use-sync-external-store@npm:0.0.6" + checksum: 10/a95ce330668501ad9b1c5b7f2b14872ad201e552a0e567787b8f1588b22c7040c7c3d80f142cbb9f92d13c4ea41c46af57a20f2af4edf27f224d352abcfe4049 languageName: node linkType: hard @@ -24142,7 +24138,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.0.0, axios@npm:^1.12.0, axios@npm:^1.6.0, axios@npm:^1.7.4, axios@npm:^1.8.3": +"axios@npm:^1.0.0, axios@npm:^1.12.0, axios@npm:^1.6.0, axios@npm:^1.7.4, axios@npm:^1.8.3, axios@npm:^1.9.0": version: 1.12.2 resolution: "axios@npm:1.12.2" dependencies: @@ -26451,10 +26447,10 @@ __metadata: languageName: node linkType: hard -"core-js-pure@npm:^3.23.3, core-js-pure@npm:^3.30.2": - version: 3.31.0 - resolution: "core-js-pure@npm:3.31.0" - checksum: 10/8862bc3af702d61e141ebd902242f04b960a5654559692dda1c007eda274aa0158fdb895c279f98584041876055817386a3fbad1c18154113b086635da7e0881 +"core-js-pure@npm:^3.23.3, core-js-pure@npm:^3.43.0": + version: 3.45.1 + resolution: "core-js-pure@npm:3.45.1" + checksum: 10/8c5091695fc9c5d9c45774f1a6dcd1bdc5fcf96e55749d93a40c09b9cf23dbaa72e72da187833ba93d80f67080d9d6c88f8f2256d30e9a9d20a12838f024aba1 languageName: node linkType: hard @@ -28079,10 +28075,15 @@ __metadata: languageName: node linkType: hard -"dompurify@npm:=3.1.6": - version: 3.1.6 - resolution: "dompurify@npm:3.1.6" - checksum: 10/036844bc9b717b172ba27f5863b56f950289a05d8eebfb702d6953bbf80bd021e480ce4217bd084567186f2d0ada13358ce5556963492cfe402d774e8667f120 +"dompurify@npm:=3.2.4": + version: 3.2.4 + resolution: "dompurify@npm:3.2.4" + dependencies: + "@types/trusted-types": "npm:^2.0.7" + dependenciesMeta: + "@types/trusted-types": + optional: true + checksum: 10/98570c53385518a2f9b617f796926338856acfdd3369c88b5905bddf96bd7d391bf8a5433127155e0046e6faa2bfb767185fcd571b865dfabe624c099e2537f5 languageName: node linkType: hard @@ -30760,7 +30761,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^2.3.2, form-data@npm:^2.5.0": +"form-data@npm:^2.5.0": version: 2.5.5 resolution: "form-data@npm:2.5.5" dependencies: @@ -33949,15 +33950,6 @@ __metadata: languageName: node linkType: hard -"isomorphic-form-data@npm:^2.0.0": - version: 2.0.0 - resolution: "isomorphic-form-data@npm:2.0.0" - dependencies: - form-data: "npm:^2.3.2" - checksum: 10/234bfaa1ed037b1d6cf659eb7a5806889f1f60bc4c7effe5f54e52506004604a9d7229a03a8f9656a1a7ea5fcedca4342277083e38f88ff910b64eefa97dd95e - languageName: node - linkType: hard - "isomorphic-git@npm:^1.23.0": version: 1.27.2 resolution: "isomorphic-git@npm:1.27.2" @@ -38665,12 +38657,12 @@ __metadata: languageName: node linkType: hard -"node-addon-api@npm:^8.2.1, node-addon-api@npm:^8.2.2, node-addon-api@npm:^8.3.0": - version: 8.3.0 - resolution: "node-addon-api@npm:8.3.0" +"node-addon-api@npm:^8.0.0, node-addon-api@npm:^8.2.2, node-addon-api@npm:^8.3.0, node-addon-api@npm:^8.3.1": + version: 8.5.0 + resolution: "node-addon-api@npm:8.5.0" dependencies: node-gyp: "npm:latest" - checksum: 10/b1c2218e794c149011d8f14e5f14b2ffd5f260c08b2982d4163a0f881069dc390458de7703602b9940a1130c1ad87c3f9d35cd7bb116e2f2a134ac0a0c0036ca + checksum: 10/9a893f4f835fbc3908e0070f7bcacf36e37fd06be8008409b104c30df4092a0d9a29927b3a74cdbc1d34338274ba4116d597a41f573e06c29538a1a70d07413f languageName: node linkType: hard @@ -38755,7 +38747,7 @@ __metadata: languageName: node linkType: hard -"node-gyp-build@npm:^4.8.2, node-gyp-build@npm:^4.8.4": +"node-gyp-build@npm:^4.8.0, node-gyp-build@npm:^4.8.2, node-gyp-build@npm:^4.8.4": version: 4.8.4 resolution: "node-gyp-build@npm:4.8.4" bin: @@ -39627,12 +39619,12 @@ __metadata: languageName: node linkType: hard -"openapi-path-templating@npm:^2.0.1": - version: 2.1.0 - resolution: "openapi-path-templating@npm:2.1.0" +"openapi-path-templating@npm:^2.2.1": + version: 2.2.1 + resolution: "openapi-path-templating@npm:2.2.1" dependencies: apg-lite: "npm:^1.0.4" - checksum: 10/de3ba30a19cc4bed5ace5dad0314bea66e09689001bd3510224a441f7ced53d854655daea846f884ed024d90a8f2af2d4bd0f28256dbda0c49b337684a802da1 + checksum: 10/2bd900d761d80e04d19be8bab91a1a3b99902e9c2e38254da605bef5c48184f015f23a766cb16447549120a73bee4ffabf2dfc2702f3278c9ddd18f33866cd7b languageName: node linkType: hard @@ -39646,7 +39638,7 @@ __metadata: languageName: node linkType: hard -"openapi-server-url-templating@npm:^1.2.0": +"openapi-server-url-templating@npm:^1.3.0": version: 1.3.0 resolution: "openapi-server-url-templating@npm:1.3.0" dependencies: @@ -41621,7 +41613,14 @@ __metadata: languageName: node linkType: hard -"prismjs@npm:^1.27.0, prismjs@npm:~1.27.0": +"prismjs@npm:^1.30.0": + version: 1.30.0 + resolution: "prismjs@npm:1.30.0" + checksum: 10/6b48a2439a82e5c6882f48ebc1564c3890e16463ba17ac10c3ad4f62d98dea5b5c915b172b63b83023a70ad4f5d7be3e8a60304420db34a161fae69dd4e3e2da + languageName: node + linkType: hard + +"prismjs@npm:~1.27.0": version: 1.27.0 resolution: "prismjs@npm:1.27.0" checksum: 10/dc83e2e09170b53526182f5435fae056fc200b109cac39faa88eb48d992311c7f59b94990318962fa93299190a9b33a404920ed150e5b364ce48c897f2ba1e8e @@ -42071,12 +42070,12 @@ __metadata: languageName: node linkType: hard -"ramda-adjunct@npm:^5.0.0": - version: 5.0.1 - resolution: "ramda-adjunct@npm:5.0.1" +"ramda-adjunct@npm:^5.0.0, ramda-adjunct@npm:^5.1.0": + version: 5.1.0 + resolution: "ramda-adjunct@npm:5.1.0" peerDependencies: ramda: ">= 0.30.0" - checksum: 10/f59d2420945b1539706da806f5c0f9c4fdf4f68a5c985b3310f09c583feca7fa5583dd371ad8d71094e9ce441e0bc56fdf65a8c31f28887c06791f44822bf67c + checksum: 10/71abdb121ba127f9306306a85d1f1c5854d6932139d90680300cdd7b6e912996e0b24460f7227c6b2be1f7d5f8204814bc62930a11a1421922ac03be51120e7a languageName: node linkType: hard @@ -42759,22 +42758,22 @@ __metadata: languageName: node linkType: hard -"react-redux@npm:^9.1.2": - version: 9.1.2 - resolution: "react-redux@npm:9.1.2" +"react-redux@npm:^9.2.0": + version: 9.2.0 + resolution: "react-redux@npm:9.2.0" dependencies: - "@types/use-sync-external-store": "npm:^0.0.3" - use-sync-external-store: "npm:^1.0.0" + "@types/use-sync-external-store": "npm:^0.0.6" + use-sync-external-store: "npm:^1.4.0" peerDependencies: - "@types/react": ^18.2.25 - react: ^18.0 + "@types/react": ^18.2.25 || ^19 + react: ^18.0 || ^19 redux: ^5.0.0 peerDependenciesMeta: "@types/react": optional: true redux: optional: true - checksum: 10/319b3286f538da7e609ca90fc6762ffae007c5cf75e525a25237ac2feaee63d9cf76fe766817de1fc8f27e7bde825ca409c463037d26dd8e57c435d383f80c50 + checksum: 10/b3d2f89f469169475ab0a9f8914d54a336ac9bc6a31af6e8dcfe9901e6fe2cfd8c1a3f6ce7a2f7f3e0928a93fbab833b668804155715598b7f2ad89927d3ff50 languageName: node linkType: hard @@ -42999,19 +42998,19 @@ __metadata: languageName: node linkType: hard -"react-syntax-highlighter@npm:^15.4.5, react-syntax-highlighter@npm:^15.5.0": - version: 15.6.1 - resolution: "react-syntax-highlighter@npm:15.6.1" +"react-syntax-highlighter@npm:^15.4.5, react-syntax-highlighter@npm:^15.6.1": + version: 15.6.6 + resolution: "react-syntax-highlighter@npm:15.6.6" dependencies: "@babel/runtime": "npm:^7.3.1" highlight.js: "npm:^10.4.1" highlightjs-vue: "npm:^1.0.0" lowlight: "npm:^1.17.0" - prismjs: "npm:^1.27.0" + prismjs: "npm:^1.30.0" refractor: "npm:^3.6.0" peerDependencies: react: ">= 0.14.0" - checksum: 10/9a89c81f7dcc109b038dc2a73189fa1ea916e6485d8a39856ab3d01d2c753449b5ae1c0df9c9ee0ed5c8c9808a68422b19af9a168ec091a274bddc7ad092eb86 + checksum: 10/8b7e60bc7df7218248e17dfc3c44b8f45cd9e363a763477753d5c39242910ff822180db59098e1cc74d0d2ced54ed1ca79a32d67e1afa0c8227ca918a7e0c692 languageName: node linkType: hard @@ -43415,13 +43414,6 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.14.0": - version: 0.14.0 - resolution: "regenerator-runtime@npm:0.14.0" - checksum: 10/6c19495baefcf5fbb18a281b56a97f0197b5f219f42e571e80877f095320afac0bdb31dab8f8186858e6126950068c3f17a1226437881e3e70446ea66751897c - languageName: node - linkType: hard - "regexp.prototype.flags@npm:^1.5.3": version: 1.5.4 resolution: "regexp.prototype.flags@npm:1.5.4" @@ -44785,7 +44777,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8, sha.js@npm:^2.4.9": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.12, sha.js@npm:^2.4.8, sha.js@npm:^2.4.9": version: 2.4.12 resolution: "sha.js@npm:2.4.12" dependencies: @@ -44874,13 +44866,13 @@ __metadata: languageName: node linkType: hard -"short-unique-id@npm:^5.0.2": - version: 5.0.3 - resolution: "short-unique-id@npm:5.0.3" +"short-unique-id@npm:^5.3.2": + version: 5.3.2 + resolution: "short-unique-id@npm:5.3.2" bin: short-unique-id: bin/short-unique-id suid: bin/short-unique-id - checksum: 10/b2c8777953650a2ad455b04e07d1c792736bf3cedcfa1a97be613834f6c6ea9c4459e23020fff3e98470db77278fc78cbf6a35408806d28362d59e1a351ee10b + checksum: 10/cd15e46009b9e84700ae305568c00f83322e58531555c6e7031f363ff4204bc88361fe62bf480688261cf2b91794a7806ecaddd682004d33d2a0d328efa0f248 languageName: node linkType: hard @@ -46330,44 +46322,44 @@ __metadata: languageName: node linkType: hard -"swagger-client@npm:^3.34.0": - version: 3.34.0 - resolution: "swagger-client@npm:3.34.0" +"swagger-client@npm:^3.35.5": + version: 3.35.6 + resolution: "swagger-client@npm:3.35.6" dependencies: "@babel/runtime-corejs3": "npm:^7.22.15" "@scarf/scarf": "npm:=1.4.0" - "@swagger-api/apidom-core": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" - "@swagger-api/apidom-error": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" - "@swagger-api/apidom-json-pointer": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" - "@swagger-api/apidom-ns-openapi-3-1": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" - "@swagger-api/apidom-reference": "npm:>=1.0.0-beta.11 <1.0.0-rc.0" - "@swaggerexpert/cookie": "npm:^1.4.1" + "@swagger-api/apidom-core": "npm:>=1.0.0-beta.41 <1.0.0-rc.0" + "@swagger-api/apidom-error": "npm:>=1.0.0-beta.41 <1.0.0-rc.0" + "@swagger-api/apidom-json-pointer": "npm:>=1.0.0-beta.41 <1.0.0-rc.0" + "@swagger-api/apidom-ns-openapi-3-1": "npm:>=1.0.0-beta.41 <1.0.0-rc.0" + "@swagger-api/apidom-reference": "npm:>=1.0.0-beta.41 <1.0.0-rc.0" + "@swaggerexpert/cookie": "npm:^2.0.2" deepmerge: "npm:~4.3.0" fast-json-patch: "npm:^3.0.0-1" js-yaml: "npm:^4.1.0" neotraverse: "npm:=0.6.18" node-abort-controller: "npm:^3.1.1" node-fetch-commonjs: "npm:^3.3.2" - openapi-path-templating: "npm:^2.0.1" - openapi-server-url-templating: "npm:^1.2.0" + openapi-path-templating: "npm:^2.2.1" + openapi-server-url-templating: "npm:^1.3.0" ramda: "npm:^0.30.1" - ramda-adjunct: "npm:^5.0.0" - checksum: 10/ed89c44ba172abb9cd6a36a189cf2810ef9b1983cb71657772ede94ea804105b07549b90ba36bbead87e4ac71e609b51f0977d9f9c90112952756c7f4f8bde82 + ramda-adjunct: "npm:^5.1.0" + checksum: 10/fb6582dc45c2ae6a26ae4cc80f0a3d97e31f3b058676951f93670828f2f30f5d44ecd7c029465f5452b251a533be329a955c92cec11be7128baa5e2e963e6f9d languageName: node linkType: hard -"swagger-ui-react@npm:^5.0.0": - version: 5.18.3 - resolution: "swagger-ui-react@npm:5.18.3" +"swagger-ui-react@npm:^5.27.1": + version: 5.29.0 + resolution: "swagger-ui-react@npm:5.29.0" dependencies: - "@babel/runtime-corejs3": "npm:^7.24.7" - "@braintree/sanitize-url": "npm:=7.0.4" + "@babel/runtime-corejs3": "npm:^7.27.1" "@scarf/scarf": "npm:=1.4.0" base64-js: "npm:^1.5.1" + buffer: "npm:^6.0.3" classnames: "npm:^2.5.1" css.escape: "npm:1.5.1" deep-extend: "npm:0.6.0" - dompurify: "npm:=3.1.6" + dompurify: "npm:=3.2.4" ieee754: "npm:^1.2.1" immutable: "npm:^3.x.x" js-file-download: "npm:^0.4.12" @@ -46381,23 +46373,23 @@ __metadata: react-immutable-proptypes: "npm:2.2.0" react-immutable-pure-component: "npm:^2.2.0" react-inspector: "npm:^6.0.1" - react-redux: "npm:^9.1.2" - react-syntax-highlighter: "npm:^15.5.0" + react-redux: "npm:^9.2.0" + react-syntax-highlighter: "npm:^15.6.1" redux: "npm:^5.0.1" redux-immutable: "npm:^4.0.0" remarkable: "npm:^2.0.1" reselect: "npm:^5.1.1" serialize-error: "npm:^8.1.0" - sha.js: "npm:^2.4.11" - swagger-client: "npm:^3.34.0" + sha.js: "npm:^2.4.12" + swagger-client: "npm:^3.35.5" url-parse: "npm:^1.5.10" xml: "npm:=1.0.1" xml-but-prettier: "npm:^1.0.1" zenscroll: "npm:^4.0.2" peerDependencies: - react: ">=16.8.0 <19" - react-dom: ">=16.8.0 <19" - checksum: 10/4437fc3fd9a869ab3ef357c78137e20fc9d96dbc71332e9ac53b45decd41382c9467d610ebf0c15a628485c67475a8f89c37882ca548305d633e2dd4b632db5f + react: ">=16.8.0 <20" + react-dom: ">=16.8.0 <20" + checksum: 10/994a7b06b17fe12693a7a09057161723f5e2ab2c80c747879951a54f0d7a0cd4c65c139bdaf229f13d61f5dd61c2b63218881fc23081ced0cc91745b48d806d5 languageName: node linkType: hard @@ -47128,14 +47120,25 @@ __metadata: languageName: node linkType: hard -"tree-sitter@npm:=0.22.1": - version: 0.22.1 - resolution: "tree-sitter@npm:0.22.1" +"tree-sitter@npm:=0.21.1": + version: 0.21.1 + resolution: "tree-sitter@npm:0.21.1" dependencies: - node-addon-api: "npm:^8.2.1" + node-addon-api: "npm:^8.0.0" node-gyp: "npm:latest" - node-gyp-build: "npm:^4.8.2" - checksum: 10/fd3aac9bd375adc626a6990cdf538b00fffbb7a8c8f11351b012830ca8fa67fcb646659b88b6cbae01286333735f4881cf9007f326fc9613837d449fac4fccce + node-gyp-build: "npm:^4.8.0" + checksum: 10/6656208333de86542e73b14e040fbbdf2c0bce7cf0d6422db401963efb1ab1b31ff7d941c973e714c28838dd5f7257b75c6d55778c6751922c3dea123da6a334 + languageName: node + linkType: hard + +"tree-sitter@npm:=0.22.4": + version: 0.22.4 + resolution: "tree-sitter@npm:0.22.4" + dependencies: + node-addon-api: "npm:^8.3.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.8.4" + checksum: 10/f228a9e1cf0160e5737019d0ce28e561feaaed650c3168603314b72234ba871512d298a4a87de51271e28186eda730634f0e167e4b64473111821696ce35437f languageName: node linkType: hard @@ -48396,7 +48399,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.0.0, use-sync-external-store@npm:^1.2.0, use-sync-external-store@npm:^1.4.0": +"use-sync-external-store@npm:^1.2.0, use-sync-external-store@npm:^1.4.0": version: 1.4.0 resolution: "use-sync-external-store@npm:1.4.0" peerDependencies: From 6981ae6bb5c9ded15a24e6c2d8e7222960281a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustaf=20R=C3=A4ntil=C3=A4?= Date: Tue, 16 Sep 2025 23:08:07 +0200 Subject: [PATCH 045/211] Fixed DependencyGraph size issue, causing it not to adapt to the container size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gustaf Räntilä --- .changeset/public-sites-admire.md | 6 ++ .../DependencyGraph/DependencyGraph.tsx | 64 ++++++++++++++----- .../EntityRelationsGraph.tsx | 1 + 3 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 .changeset/public-sites-admire.md diff --git a/.changeset/public-sites-admire.md b/.changeset/public-sites-admire.md new file mode 100644 index 0000000000..aa6d245527 --- /dev/null +++ b/.changeset/public-sites-admire.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-catalog-graph': patch +--- + +Fixed DependencyGraph svg size not adapting to the container size diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 411a0e2c57..85e6d9ef3f 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -22,6 +22,8 @@ import { useRef, useState, } from 'react'; +import useMeasure from 'react-use/esm/useMeasure'; +import { once } from 'lodash'; import * as d3Zoom from 'd3-zoom'; import * as d3Selection from 'd3-selection'; import useTheme from '@material-ui/core/styles/useTheme'; @@ -41,11 +43,18 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { coreComponentsTranslationRef } from '../../translation'; const useStyles = makeStyles((theme: Theme) => ({ + fullscreenButton: { + position: 'absolute', + right: 0, + }, root: { overflow: 'hidden', minHeight: '100%', minWidth: '100%', }, + fixedHeight: { + height: '100%', + }, fullscreen: { backgroundColor: theme.palette.background.paper, }, @@ -268,9 +277,11 @@ export function DependencyGraph( const maxWidth = Math.max(graphWidth, containerWidth); const maxHeight = Math.max(graphHeight, containerHeight); - const minHeight = Math.min(graphHeight, containerHeight); - const scalableHeight = fit === 'grow' ? maxHeight : minHeight; + const [_measureRef] = useMeasure(); + const measureRef = once(_measureRef); + + const scalableHeight = fit === 'grow' ? maxHeight : containerHeight; const containerRef = useMemo( () => @@ -278,6 +289,8 @@ export function DependencyGraph( if (!root) { return; } + measureRef(root); + // Set up zooming + panning const node: SVGSVGElement = root.querySelector( `svg#${DEPENDENCY_GRAPH_SVG}`, @@ -328,7 +341,7 @@ export function DependencyGraph( setContainerHeight(newContainerHeight); } }, 100), - [containerHeight, containerWidth, maxWidth, maxHeight, zoom], + [measureRef, containerHeight, containerWidth, maxWidth, maxHeight, zoom], ); const setNodesAndEdges = useCallback(() => { @@ -431,28 +444,43 @@ export function DependencyGraph( updateGraph, ]); - function setNode(id: string, node: Types.DependencyNode) { - graph.current.setNode(id, node); - updateGraph(); - return graph.current; - } + const setNode = useCallback( + (id: string, node: Types.DependencyNode) => { + graph.current.setNode(id, node); + updateGraph(); + return graph.current; + }, + [updateGraph], + ); - function setEdge(id: dagre.Edge, edge: Types.DependencyEdge) { - graph.current.setEdge(id, edge); - updateGraph(); - return graph.current; - } + const setEdge = useCallback( + (id: dagre.Edge, edge: Types.DependencyEdge) => { + graph.current.setEdge(id, edge); + updateGraph(); + return graph.current; + }, + [updateGraph], + ); return ( -
+
{allowFullscreen && ( (
); } + +function combineClasses(...classes: (string | false | undefined)[]) { + return classes.filter(c => !!c).join(' '); +} diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx index f77ca4b37d..17b5861173 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/EntityRelationsGraph.tsx @@ -158,6 +158,7 @@ export const EntityRelationsGraph = (props: EntityRelationsGraphProps) => { renderLabel={renderLabel || DefaultRenderLabel} direction={direction} className={classes.graph} + fit="contain" paddingX={theme.spacing(4)} paddingY={theme.spacing(4)} labelPosition={DependencyGraphTypes.LabelPosition.RIGHT} From 024645e1ab98608413a4f10d683094fe48b6edd2 Mon Sep 17 00:00:00 2001 From: Hitoshi Kamezaki Date: Mon, 7 Jul 2025 10:26:42 +0900 Subject: [PATCH 046/211] Remove unused @octokit moudles from cli package - @octokit/graphql - @octokit/graphql-schema - @octokit/oauth-app Signed-off-by: Hitoshi Kamezaki --- .changeset/silent-mice-play.md | 9 ++++++ packages/cli/package.json | 3 -- yarn.lock | 51 ++-------------------------------- 3 files changed, 11 insertions(+), 52 deletions(-) create mode 100644 .changeset/silent-mice-play.md diff --git a/.changeset/silent-mice-play.md b/.changeset/silent-mice-play.md new file mode 100644 index 0000000000..aa14cf69e3 --- /dev/null +++ b/.changeset/silent-mice-play.md @@ -0,0 +1,9 @@ +--- +'@backstage/cli': patch +--- + +Remove unused @octokit modules from cli package + +- @octokit/graphql +- @octokit/graphql-schema +- @octokit/oauth-app diff --git a/packages/cli/package.json b/packages/cli/package.json index f50a5601eb..aad6eb410d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -59,9 +59,6 @@ "@backstage/types": "workspace:^", "@manypkg/get-packages": "^1.1.3", "@module-federation/enhanced": "^0.9.0", - "@octokit/graphql": "^5.0.0", - "@octokit/graphql-schema": "^13.7.0", - "@octokit/oauth-app": "^4.2.0", "@octokit/request": "^8.0.0", "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index e606b8246d..dac807ce19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2854,9 +2854,6 @@ __metadata: "@backstage/types": "workspace:^" "@manypkg/get-packages": "npm:^1.1.3" "@module-federation/enhanced": "npm:^0.9.0" - "@octokit/graphql": "npm:^5.0.0" - "@octokit/graphql-schema": "npm:^13.7.0" - "@octokit/oauth-app": "npm:^4.2.0" "@octokit/request": "npm:^8.0.0" "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.5.7" "@rollup/plugin-commonjs": "npm:^26.0.0" @@ -11867,16 +11864,6 @@ __metadata: languageName: node linkType: hard -"@octokit/auth-unauthenticated@npm:^3.0.0": - version: 3.0.0 - resolution: "@octokit/auth-unauthenticated@npm:3.0.0" - dependencies: - "@octokit/request-error": "npm:^2.1.0" - "@octokit/types": "npm:^6.0.3" - checksum: 10/3100d1f4a201ea4ca160c1f432121829ed77a3225aeb77c9d600e677dfdd1fa79bd14e6d24f51f6b10312ba40de31780148eb80bb096b7b33fc4b9c26c593526 - languageName: node - linkType: hard - "@octokit/auth-unauthenticated@npm:^5.0.0": version: 5.0.1 resolution: "@octokit/auth-unauthenticated@npm:5.0.1" @@ -11887,7 +11874,7 @@ __metadata: languageName: node linkType: hard -"@octokit/core@npm:^4.0.0, @octokit/core@npm:^4.2.1": +"@octokit/core@npm:^4.2.1": version: 4.2.4 resolution: "@octokit/core@npm:4.2.4" dependencies: @@ -11949,16 +11936,6 @@ __metadata: languageName: node linkType: hard -"@octokit/graphql-schema@npm:^13.7.0": - version: 13.10.0 - resolution: "@octokit/graphql-schema@npm:13.10.0" - dependencies: - graphql: "npm:^16.0.0" - graphql-tag: "npm:^2.10.3" - checksum: 10/c8714a9dc8f36d01e3db125e570a95030faeaf6c815811be2190ce288691bfcc5e939d161b33ef91b4c9fbca21663bace53d3eea2115a443de27a51b5dbfe2a4 - languageName: node - linkType: hard - "@octokit/graphql@npm:^5.0.0": version: 5.0.6 resolution: "@octokit/graphql@npm:5.0.6" @@ -11981,23 +11958,6 @@ __metadata: languageName: node linkType: hard -"@octokit/oauth-app@npm:^4.2.0": - version: 4.2.4 - resolution: "@octokit/oauth-app@npm:4.2.4" - dependencies: - "@octokit/auth-oauth-app": "npm:^5.0.0" - "@octokit/auth-oauth-user": "npm:^2.0.0" - "@octokit/auth-unauthenticated": "npm:^3.0.0" - "@octokit/core": "npm:^4.0.0" - "@octokit/oauth-authorization-url": "npm:^5.0.0" - "@octokit/oauth-methods": "npm:^2.0.0" - "@types/aws-lambda": "npm:^8.10.83" - fromentries: "npm:^1.3.1" - universal-user-agent: "npm:^6.0.0" - checksum: 10/1c9e48b56fb4cf3428b8967335b46cedf7740d27932ea394530d07deffa8c3bff5ceef8d14bf145b3bfc64ad1088f4ddf46ceca2a4c052f267c3faa12188ef14 - languageName: node - linkType: hard - "@octokit/oauth-app@npm:^6.0.0": version: 6.0.0 resolution: "@octokit/oauth-app@npm:6.0.0" @@ -30904,13 +30864,6 @@ __metadata: languageName: node linkType: hard -"fromentries@npm:^1.3.1": - version: 1.3.2 - resolution: "fromentries@npm:1.3.2" - checksum: 10/10d6e07d289db102c0c1eaf5c3e3fa55ddd6b50033d7de16d99a7cd89f1e1a302dfadb26457031f9bb5d2ed95a179aaf0396092dde5abcae06e8a2f0476826be - languageName: node - linkType: hard - "fs-constants@npm:^1.0.0": version: 1.0.0 resolution: "fs-constants@npm:1.0.0" @@ -31884,7 +31837,7 @@ __metadata: languageName: node linkType: hard -"graphql-tag@npm:^2.10.3, graphql-tag@npm:^2.12.6": +"graphql-tag@npm:^2.12.6": version: 2.12.6 resolution: "graphql-tag@npm:2.12.6" dependencies: From 3d09bb218aa4c565cfa4ab6a1b3f1500a8556d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Edeg=C3=A5rd?= Date: Wed, 17 Sep 2025 11:30:28 +0000 Subject: [PATCH 047/211] Adds username as optional config in order to send with a different username than the Slack App have. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Henrik Edegård --- .changeset/heavy-cooks-divide.md | 5 + docs/notifications/processors.md | 1 + .../lib/SlackNotificationProcessor.test.ts | 291 ++++++++++++++++++ .../src/lib/SlackNotificationProcessor.ts | 15 +- .../src/lib/util.ts | 4 +- 5 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 .changeset/heavy-cooks-divide.md diff --git a/.changeset/heavy-cooks-divide.md b/.changeset/heavy-cooks-divide.md new file mode 100644 index 0000000000..181cd01075 --- /dev/null +++ b/.changeset/heavy-cooks-divide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-slack': minor +--- + +Adds username as optional config in order to send Slack notifications with a specific username in the case when using one Slack App for more than just Backstage. diff --git a/docs/notifications/processors.md b/docs/notifications/processors.md index ad3e427364..c25b669ed6 100644 --- a/docs/notifications/processors.md +++ b/docs/notifications/processors.md @@ -148,6 +148,7 @@ notifications: - token: xoxb-XXXXXXXXX broadcastChannels: # Optional, if you wish to support broadcast notifications. - C12345678 + username: 'Backstage Bot' # Optional, defaults to the name of the Slack App. ``` Multiple instances can be added in the `slack` array, allowing you to have multiple configurations if you need to send diff --git a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts index 45a74cee70..c970aec7ba 100644 --- a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts +++ b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts @@ -542,6 +542,297 @@ describe('SlackNotificationProcessor', () => { }); }); + describe('when username is configured', () => { + it('should include username in group messages', async () => { + const slack = new WebClient(); + const usernameConfig = mockServices.rootConfig({ + data: { + app: { + baseUrl: 'https://example.org', + }, + notifications: { + processors: { + slack: [ + { + token: 'mock-token', + username: 'BackstageBot', + }, + ], + }, + }, + }, + }); + + const processor = SlackNotificationProcessor.fromConfig(usernameConfig, { + auth, + logger, + catalog: catalogServiceMock({ + entities: DEFAULT_ENTITIES_RESPONSE.items, + }), + slack, + })[0]; + + await processor.processOptions({ + recipients: { type: 'entity', entityRef: 'group:default/mock' }, + payload: { title: 'notification' }, + }); + + expect(slack.chat.postMessage).toHaveBeenCalledWith({ + channel: 'C12345678', + text: 'notification', + username: 'BackstageBot', + attachments: [ + { + color: '#00A699', + blocks: [ + { + type: 'section', + text: { + text: 'No description provided', + type: 'mrkdwn', + }, + accessory: { + type: 'button', + text: { + type: 'plain_text', + text: 'View More', + }, + action_id: 'button-action', + }, + }, + { + type: 'context', + elements: [ + { + type: 'plain_text', + text: 'Severity: normal', + emoji: true, + }, + { + type: 'plain_text', + text: 'Topic: N/A', + emoji: true, + }, + ], + }, + ], + fallback: 'notification', + }, + ], + }); + }); + + it('should include username in direct user messages', async () => { + const slack = new WebClient(); + const usernameConfig = mockServices.rootConfig({ + data: { + app: { + baseUrl: 'https://example.org', + }, + notifications: { + processors: { + slack: [ + { + token: 'mock-token', + username: 'BackstageBot', + }, + ], + }, + }, + }, + }); + + const processor = SlackNotificationProcessor.fromConfig(usernameConfig, { + auth, + logger, + catalog: catalogServiceMock({ + entities: DEFAULT_ENTITIES_RESPONSE.items, + }), + slack, + })[0]; + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { + title: 'notification', + link: '/catalog/user/default/jane.doe', + }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(slack.chat.postMessage).toHaveBeenCalledWith({ + channel: 'U12345678', + text: 'notification', + username: 'BackstageBot', + attachments: [ + { + color: '#00A699', + blocks: [ + { + type: 'section', + text: { + text: 'No description provided', + type: 'mrkdwn', + }, + accessory: { + type: 'button', + text: { + type: 'plain_text', + text: 'View More', + }, + action_id: 'button-action', + }, + }, + { + type: 'context', + elements: [ + { + type: 'plain_text', + text: 'Severity: normal', + emoji: true, + }, + { + type: 'plain_text', + text: 'Topic: N/A', + emoji: true, + }, + ], + }, + ], + fallback: 'notification', + }, + ], + }); + }); + + it('should include username in broadcast messages', async () => { + const slack = new WebClient(); + const usernameAndBroadcastConfig = mockServices.rootConfig({ + data: { + app: { + baseUrl: 'https://example.org', + }, + notifications: { + processors: { + slack: [ + { + token: 'mock-token', + username: 'BackstageBot', + broadcastChannels: ['C12345678'], + }, + ], + }, + }, + }, + }); + + const processor = SlackNotificationProcessor.fromConfig( + usernameAndBroadcastConfig, + { + auth, + logger, + catalog: catalogServiceMock({ + entities: DEFAULT_ENTITIES_RESPONSE.items, + }), + slack, + }, + )[0]; + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: null, + created: new Date(), + payload: { + title: 'notification', + link: '/catalog/user/default/jane.doe', + }, + }, + { + recipients: { type: 'broadcast' }, + payload: { title: 'notification' }, + }, + ); + + expect(slack.chat.postMessage).toHaveBeenCalledWith({ + channel: 'C12345678', + text: 'notification', + username: 'BackstageBot', + attachments: [ + { + color: '#00A699', + blocks: [ + { + type: 'section', + text: { + text: 'No description provided', + type: 'mrkdwn', + }, + accessory: { + type: 'button', + text: { + type: 'plain_text', + text: 'View More', + }, + action_id: 'button-action', + }, + }, + { + type: 'context', + elements: [ + { + type: 'plain_text', + text: 'Severity: normal', + emoji: true, + }, + { + type: 'plain_text', + text: 'Topic: N/A', + emoji: true, + }, + ], + }, + ], + fallback: 'notification', + }, + ], + }); + }); + }); + + describe('when username is not configured', () => { + it('should not include username in messages', async () => { + const slack = new WebClient(); + + const processor = SlackNotificationProcessor.fromConfig(config, { + auth, + logger, + catalog: catalogServiceMock({ + entities: DEFAULT_ENTITIES_RESPONSE.items, + }), + slack, + })[0]; + + await processor.processOptions({ + recipients: { type: 'entity', entityRef: 'group:default/mock' }, + payload: { title: 'notification' }, + }); + + const calls = (slack.chat.postMessage as jest.Mock).mock.calls; + expect(calls).toHaveLength(1); + expect(calls[0][0]).not.toHaveProperty('username'); + }); + }); + describe('when replacing user entity refs with Slack IDs', () => { const createBaseMessage = (text: string) => ({ channel: 'U12345678', diff --git a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts index f7dbc6a93e..f050b3b677 100644 --- a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts +++ b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts @@ -49,6 +49,7 @@ export class SlackNotificationProcessor implements NotificationProcessor { private readonly messagesFailed: Counter; private readonly broadcastChannels?: string[]; private readonly entityLoader: DataLoader; + private readonly username?: string; static fromConfig( config: Config, @@ -66,9 +67,11 @@ export class SlackNotificationProcessor implements NotificationProcessor { const token = c.getString('token'); const slack = options.slack ?? new WebClient(token); const broadcastChannels = c.getOptionalStringArray('broadcastChannels'); + const username = c.getOptionalString('username'); return new SlackNotificationProcessor({ slack, broadcastChannels, + username, ...options, }); }); @@ -80,13 +83,16 @@ export class SlackNotificationProcessor implements NotificationProcessor { logger: LoggerService; catalog: CatalogService; broadcastChannels?: string[]; + username?: string; }) { - const { auth, catalog, logger, slack, broadcastChannels } = options; + const { auth, catalog, logger, slack, broadcastChannels, username } = + options; this.logger = logger; this.catalog = catalog; this.auth = auth; this.slack = slack; this.broadcastChannels = broadcastChannels; + this.username = username; this.entityLoader = new DataLoader( async entityRefs => { @@ -206,6 +212,7 @@ export class SlackNotificationProcessor implements NotificationProcessor { const payload = toChatPostMessageArgs({ channel, payload: options.payload, + ...(this.username && { username: this.username }), }); this.logger.debug( @@ -261,7 +268,11 @@ export class SlackNotificationProcessor implements NotificationProcessor { options.payload, ); const outbound = destinations.map(channel => - toChatPostMessageArgs({ channel, payload: formattedPayload }), + toChatPostMessageArgs({ + channel, + payload: formattedPayload, + ...(this.username && { username: this.username }), + }), ); // Log debug info diff --git a/plugins/notifications-backend-module-slack/src/lib/util.ts b/plugins/notifications-backend-module-slack/src/lib/util.ts index 24f9f6a964..1116218b09 100644 --- a/plugins/notifications-backend-module-slack/src/lib/util.ts +++ b/plugins/notifications-backend-module-slack/src/lib/util.ts @@ -23,12 +23,14 @@ import { ChatPostMessageArguments, KnownBlock } from '@slack/web-api'; export function toChatPostMessageArgs(options: { channel: string; payload: NotificationPayload; + username?: string; }): ChatPostMessageArguments { - const { channel, payload } = options; + const { channel, payload, username } = options; const args: ChatPostMessageArguments = { channel, text: payload.title, + ...(username && { username }), attachments: [ { color: getColor(payload.severity), From c8ec8e52425c830a378acfbd4facaadc0addb2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustaf=20R=C3=A4ntil=C3=A4?= Date: Wed, 17 Sep 2025 16:11:48 +0200 Subject: [PATCH 048/211] Update .changeset/public-sites-admire.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: Gustaf Räntilä --- .changeset/public-sites-admire.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/public-sites-admire.md b/.changeset/public-sites-admire.md index aa6d245527..f5f3304f9f 100644 --- a/.changeset/public-sites-admire.md +++ b/.changeset/public-sites-admire.md @@ -3,4 +3,4 @@ '@backstage/plugin-catalog-graph': patch --- -Fixed DependencyGraph svg size not adapting to the container size +Fixed DependencyGraph `svg` size not adapting to the container size From 65f5a3a84d0e811538ea3bb85a8e026958ee9f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustaf=20R=C3=A4ntil=C3=A4?= Date: Wed, 17 Sep 2025 16:16:20 +0200 Subject: [PATCH 049/211] Using 'classnames' instead of custom implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gustaf Räntilä --- .../src/components/DependencyGraph/DependencyGraph.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 85e6d9ef3f..df26bb86fd 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -23,6 +23,7 @@ import { useState, } from 'react'; import useMeasure from 'react-use/esm/useMeasure'; +import classNames from 'classnames'; import { once } from 'lodash'; import * as d3Zoom from 'd3-zoom'; import * as d3Selection from 'd3-selection'; @@ -465,14 +466,14 @@ export function DependencyGraph( return (
(
); } - -function combineClasses(...classes: (string | false | undefined)[]) { - return classes.filter(c => !!c).join(' '); -} From 99fcf986fee0d121ae1b786219fac5e0a2642c79 Mon Sep 17 00:00:00 2001 From: Hope Hadfield Date: Mon, 15 Sep 2025 16:19:57 -0400 Subject: [PATCH 050/211] Remove all unused luxon dependencies Signed-off-by: Hope Hadfield --- .changeset/warm-items-look.md | 17 +++++++++++++++++ .../catalog-backend-module-aws/knip-report.md | 12 ------------ plugins/catalog-backend-module-aws/package.json | 5 +---- .../catalog-backend-module-azure/knip-report.md | 5 ----- .../catalog-backend-module-azure/package.json | 1 - .../knip-report.md | 11 ----------- .../package.json | 2 -- .../knip-report.md | 11 ----------- .../package.json | 2 -- .../knip-report.md | 5 ----- .../catalog-backend-module-gerrit/package.json | 1 - .../knip-report.md | 5 ----- .../package.json | 3 +-- .../knip-report.md | 12 ------------ .../catalog-backend-module-github/package.json | 3 --- .../knip-report.md | 5 ----- .../package.json | 3 +-- .../knip-report.md | 5 ----- .../catalog-backend-module-gitlab/package.json | 1 - .../knip-report.md | 5 ----- .../catalog-backend-module-msgraph/package.json | 1 - .../knip-report.md | 5 ----- .../package.json | 1 - plugins/kubernetes-cluster/knip-report.md | 15 --------------- plugins/kubernetes-cluster/package.json | 6 ------ plugins/kubernetes/knip-report.md | 15 --------------- plugins/kubernetes/package.json | 13 +------------ 27 files changed, 21 insertions(+), 149 deletions(-) create mode 100644 .changeset/warm-items-look.md diff --git a/.changeset/warm-items-look.md b/.changeset/warm-items-look.md new file mode 100644 index 0000000000..b8a2abd61d --- /dev/null +++ b/.changeset/warm-items-look.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-catalog-backend-module-bitbucket-server': patch +'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch +'@backstage/plugin-catalog-backend-module-github-org': patch +'@backstage/plugin-catalog-backend-module-gitlab-org': patch +'@backstage/plugin-catalog-backend-module-puppetdb': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-catalog-backend-module-gerrit': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-catalog-backend-module-gitlab': patch +'@backstage/plugin-catalog-backend-module-azure': patch +'@backstage/plugin-catalog-backend-module-aws': patch +'@backstage/plugin-kubernetes-cluster': patch +'@backstage/plugin-kubernetes': patch +--- + +Removed unused dependencies diff --git a/plugins/catalog-backend-module-aws/knip-report.md b/plugins/catalog-backend-module-aws/knip-report.md index ed7557334b..97d5b385fd 100644 --- a/plugins/catalog-backend-module-aws/knip-report.md +++ b/plugins/catalog-backend-module-aws/knip-report.md @@ -1,15 +1,3 @@ # Knip report -## Unused dependencies (2) - -| Name | Location | Severity | -| :---------------------------- | :----------- | :------- | -| @aws-sdk/credential-providers | plugins/catalog-backend-module-aws/package.json | error | -| winston | plugins/catalog-backend-module-aws/package.json | error | - -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-aws/package.json | error | diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 3f21de8eb1..0b29e2bda8 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -54,7 +54,6 @@ "@aws-sdk/client-eks": "^3.350.0", "@aws-sdk/client-organizations": "^3.350.0", "@aws-sdk/client-s3": "^3.350.0", - "@aws-sdk/credential-providers": "^3.350.0", "@aws-sdk/middleware-endpoint": "^3.347.0", "@aws-sdk/types": "^3.347.0", "@backstage/backend-defaults": "workspace:^", @@ -68,8 +67,7 @@ "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-kubernetes-common": "workspace:^", "p-limit": "^3.0.2", - "uuid": "^11.0.0", - "winston": "^3.2.1" + "uuid": "^11.0.0" }, "devDependencies": { "@aws-sdk/util-stream-node": "^3.350.0", @@ -77,7 +75,6 @@ "@backstage/cli": "workspace:^", "aws-sdk-client-mock": "^4.0.0", "aws-sdk-client-mock-jest": "^4.0.0", - "luxon": "^3.0.0", "yaml": "^2.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/catalog-backend-module-azure/knip-report.md b/plugins/catalog-backend-module-azure/knip-report.md index 622077aa40..97d5b385fd 100644 --- a/plugins/catalog-backend-module-azure/knip-report.md +++ b/plugins/catalog-backend-module-azure/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-azure/package.json | error | diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 17c66699a5..0aadfcecfc 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -63,7 +63,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "luxon": "^3.0.0", "msw": "^1.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/catalog-backend-module-bitbucket-cloud/knip-report.md b/plugins/catalog-backend-module-bitbucket-cloud/knip-report.md index a6400f9cd2..97d5b385fd 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/knip-report.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/knip-report.md @@ -1,14 +1,3 @@ # Knip report -## Unused dependencies (1) - -| Name | Location | Severity | -| :------------------------ | :----------- | :------- | -| @backstage/catalog-client | plugins/catalog-backend-module-bitbucket-cloud/package.json | error | - -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-bitbucket-cloud/package.json | error | diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 68dfbaa619..f5afab87d2 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -52,7 +52,6 @@ }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", - "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/integration": "workspace:^", @@ -66,7 +65,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-events-backend-test-utils": "workspace:^", - "luxon": "^3.0.0", "msw": "^1.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/catalog-backend-module-bitbucket-server/knip-report.md b/plugins/catalog-backend-module-bitbucket-server/knip-report.md index 067155bd06..97d5b385fd 100644 --- a/plugins/catalog-backend-module-bitbucket-server/knip-report.md +++ b/plugins/catalog-backend-module-bitbucket-server/knip-report.md @@ -1,14 +1,3 @@ # Knip report -## Unused dependencies (1) - -| Name | Location | Severity | -| :------------------------ | :----------- | :------- | -| @backstage/catalog-client | plugins/catalog-backend-module-bitbucket-server/package.json | error | - -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-bitbucket-server/package.json | error | diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index ce3ec49ae8..95b310eec1 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -47,7 +47,6 @@ }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", - "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", @@ -61,7 +60,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-events-backend-test-utils": "workspace:^", - "luxon": "^3.0.0", "msw": "^1.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/catalog-backend-module-gerrit/knip-report.md b/plugins/catalog-backend-module-gerrit/knip-report.md index 7a02c83e07..97d5b385fd 100644 --- a/plugins/catalog-backend-module-gerrit/knip-report.md +++ b/plugins/catalog-backend-module-gerrit/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-gerrit/package.json | error | diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 1c4d7e8aa7..ba6c981a41 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -61,7 +61,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/fs-extra": "^11.0.0", - "luxon": "^3.0.0", "msw": "^1.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/catalog-backend-module-github-org/knip-report.md b/plugins/catalog-backend-module-github-org/knip-report.md index d71302f8ae..97d5b385fd 100644 --- a/plugins/catalog-backend-module-github-org/knip-report.md +++ b/plugins/catalog-backend-module-github-org/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-github-org/package.json | error | diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index f9c1f516fb..a864ffe6e2 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -41,7 +41,6 @@ }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", - "@backstage/cli": "workspace:^", - "luxon": "^3.0.0" + "@backstage/cli": "workspace:^" } } diff --git a/plugins/catalog-backend-module-github/knip-report.md b/plugins/catalog-backend-module-github/knip-report.md index 35b39536c9..c9e8569b52 100644 --- a/plugins/catalog-backend-module-github/knip-report.md +++ b/plugins/catalog-backend-module-github/knip-report.md @@ -1,17 +1,5 @@ # Knip report -## Unused dependencies (2) - -| Name | Location | Severity | -| :-------------------------------- | :----------- | :------- | -| @backstage/plugin-catalog-backend | plugins/catalog-backend-module-github/package.json | error | -| @backstage/catalog-client | plugins/catalog-backend-module-github/package.json | error | - -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-github/package.json | error | ## Unlisted dependencies (4) diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 5187103233..0a5075c3c9 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -52,11 +52,9 @@ }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", - "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-catalog-backend": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-events-node": "workspace:^", @@ -73,7 +71,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", - "luxon": "^3.0.0", "msw": "^2.0.0", "type-fest": "^4.41.0" }, diff --git a/plugins/catalog-backend-module-gitlab-org/knip-report.md b/plugins/catalog-backend-module-gitlab-org/knip-report.md index 43328f5e48..97d5b385fd 100644 --- a/plugins/catalog-backend-module-gitlab-org/knip-report.md +++ b/plugins/catalog-backend-module-gitlab-org/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-gitlab-org/package.json | error | diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 09f6ee7811..2ebcdf5472 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -41,7 +41,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/plugin-events-backend-test-utils": "workspace:^", - "luxon": "^3.0.0" + "@backstage/plugin-events-backend-test-utils": "workspace:^" } } diff --git a/plugins/catalog-backend-module-gitlab/knip-report.md b/plugins/catalog-backend-module-gitlab/knip-report.md index 8c7b1cc0a0..97d5b385fd 100644 --- a/plugins/catalog-backend-module-gitlab/knip-report.md +++ b/plugins/catalog-backend-module-gitlab/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-gitlab/package.json | error | diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index c18be1028b..b14f6bb930 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -69,7 +69,6 @@ "@backstage/cli": "workspace:^", "@backstage/plugin-events-backend-test-utils": "workspace:^", "@types/lodash": "^4.14.151", - "luxon": "^3.0.0", "msw": "^1.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/catalog-backend-module-msgraph/knip-report.md b/plugins/catalog-backend-module-msgraph/knip-report.md index 1b4a6feaab..97d5b385fd 100644 --- a/plugins/catalog-backend-module-msgraph/knip-report.md +++ b/plugins/catalog-backend-module-msgraph/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused devDependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-msgraph/package.json | error | diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index f2f8a5d442..cb69f932f0 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -67,7 +67,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/lodash": "^4.14.151", - "luxon": "^3.0.0", "msw": "^1.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/catalog-backend-module-puppetdb/knip-report.md b/plugins/catalog-backend-module-puppetdb/knip-report.md index ac2ab908f0..97d5b385fd 100644 --- a/plugins/catalog-backend-module-puppetdb/knip-report.md +++ b/plugins/catalog-backend-module-puppetdb/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused dependencies (1) - -| Name | Location | Severity | -| :---- | :----------- | :------- | -| luxon | plugins/catalog-backend-module-puppetdb/package.json | error | diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index ced5df4773..68b5127056 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -56,7 +56,6 @@ "@backstage/plugin-catalog-node": "workspace:^", "@backstage/types": "workspace:^", "lodash": "^4.17.21", - "luxon": "^3.0.0", "uuid": "^11.0.0" }, "devDependencies": { diff --git a/plugins/kubernetes-cluster/knip-report.md b/plugins/kubernetes-cluster/knip-report.md index 55eba7a829..97d5b385fd 100644 --- a/plugins/kubernetes-cluster/knip-report.md +++ b/plugins/kubernetes-cluster/knip-report.md @@ -1,18 +1,3 @@ # Knip report -## Unused dependencies (5) - -| Name | Location | Severity | -| :---------------------- | :----------- | :------- | -| @kubernetes-models/base | plugins/kubernetes-cluster/package.json | error | -| cronstrue | plugins/kubernetes-cluster/package.json | error | -| js-yaml | plugins/kubernetes-cluster/package.json | error | -| lodash | plugins/kubernetes-cluster/package.json | error | -| luxon | plugins/kubernetes-cluster/package.json | error | - -## Unused devDependencies (1) - -| Name | Location | Severity | -| :------------------- | :----------- | :------- | -| @testing-library/dom | plugins/kubernetes-cluster/package.json | error | diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index e789c3edaf..49ec22c9c3 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -62,20 +62,14 @@ "@backstage/plugin-kubernetes-react": "workspace:^", "@backstage/plugin-permission-react": "workspace:^", "@kubernetes-models/apimachinery": "^2.0.0", - "@kubernetes-models/base": "^5.0.0", "@material-ui/core": "^4.12.2", "@material-ui/lab": "4.0.0-alpha.61", - "cronstrue": "^2.2.0", - "js-yaml": "^4.0.0", "kubernetes-models": "^4.1.0", - "lodash": "^4.17.21", - "luxon": "^3.0.0", "react-use": "^17.2.4" }, "devDependencies": { "@backstage/cli": "workspace:^", "@backstage/test-utils": "workspace:^", - "@testing-library/dom": "^10.0.0", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^16.0.0", "@types/node": "^20.16.0", diff --git a/plugins/kubernetes/knip-report.md b/plugins/kubernetes/knip-report.md index 11b66acd1b..97d5b385fd 100644 --- a/plugins/kubernetes/knip-report.md +++ b/plugins/kubernetes/knip-report.md @@ -1,18 +1,3 @@ # Knip report -## Unused dependencies (11) - -| Name | Location | Severity | -| :------------------------------ | :----------- | :------- | -| @kubernetes-models/apimachinery | plugins/kubernetes/package.json | error | -| @kubernetes-models/base | plugins/kubernetes/package.json | error | -| @kubernetes/client-node | plugins/kubernetes/package.json | error | -| @xterm/addon-attach | plugins/kubernetes/package.json | error | -| kubernetes-models | plugins/kubernetes/package.json | error | -| @xterm/addon-fit | plugins/kubernetes/package.json | error | -| @xterm/xterm | plugins/kubernetes/package.json | error | -| cronstrue | plugins/kubernetes/package.json | error | -| js-yaml | plugins/kubernetes/package.json | error | -| lodash | plugins/kubernetes/package.json | error | -| luxon | plugins/kubernetes/package.json | error | diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index e9da7b47ec..d5bc9c0b88 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -67,18 +67,7 @@ "@backstage/plugin-kubernetes-common": "workspace:^", "@backstage/plugin-kubernetes-react": "workspace:^", "@backstage/plugin-permission-react": "workspace:^", - "@kubernetes-models/apimachinery": "^2.0.0", - "@kubernetes-models/base": "^5.0.0", - "@kubernetes/client-node": "1.1.2", - "@material-ui/core": "^4.12.2", - "@xterm/addon-attach": "^0.11.0", - "@xterm/addon-fit": "^0.10.0", - "@xterm/xterm": "^5.5.0", - "cronstrue": "^2.2.0", - "js-yaml": "^4.0.0", - "kubernetes-models": "^4.1.0", - "lodash": "^4.17.21", - "luxon": "^3.0.0" + "@material-ui/core": "^4.12.2" }, "devDependencies": { "@backstage/cli": "workspace:^", From fa589daef639b24884fc306e7c504fac8f9ed5dd Mon Sep 17 00:00:00 2001 From: Hope Hadfield Date: Wed, 17 Sep 2025 16:29:54 -0400 Subject: [PATCH 051/211] add yarn.lock Signed-off-by: Hope Hadfield --- yarn.lock | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/yarn.lock b/yarn.lock index e606b8246d..ebc7061499 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4332,7 +4332,6 @@ __metadata: "@aws-sdk/client-eks": "npm:^3.350.0" "@aws-sdk/client-organizations": "npm:^3.350.0" "@aws-sdk/client-s3": "npm:^3.350.0" - "@aws-sdk/credential-providers": "npm:^3.350.0" "@aws-sdk/middleware-endpoint": "npm:^3.347.0" "@aws-sdk/types": "npm:^3.347.0" "@aws-sdk/util-stream-node": "npm:^3.350.0" @@ -4350,10 +4349,8 @@ __metadata: "@backstage/plugin-kubernetes-common": "workspace:^" aws-sdk-client-mock: "npm:^4.0.0" aws-sdk-client-mock-jest: "npm:^4.0.0" - luxon: "npm:^3.0.0" p-limit: "npm:^3.0.2" uuid: "npm:^11.0.0" - winston: "npm:^3.2.1" yaml: "npm:^2.0.0" languageName: unknown linkType: soft @@ -4371,7 +4368,6 @@ __metadata: "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" - luxon: "npm:^3.0.0" msw: "npm:^1.0.0" uuid: "npm:^11.0.0" languageName: unknown @@ -4403,7 +4399,6 @@ __metadata: dependencies: "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" - "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" @@ -4413,7 +4408,6 @@ __metadata: "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-backend-test-utils": "workspace:^" "@backstage/plugin-events-node": "workspace:^" - luxon: "npm:^3.0.0" msw: "npm:^1.0.0" uuid: "npm:^11.0.0" languageName: unknown @@ -4425,7 +4419,6 @@ __metadata: dependencies: "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" - "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" @@ -4435,7 +4428,6 @@ __metadata: "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-backend-test-utils": "workspace:^" "@backstage/plugin-events-node": "workspace:^" - luxon: "npm:^3.0.0" msw: "npm:^1.0.0" uuid: "npm:^11.0.0" languageName: unknown @@ -4470,7 +4462,6 @@ __metadata: "@backstage/plugin-catalog-node": "workspace:^" "@types/fs-extra": "npm:^11.0.0" fs-extra: "npm:^11.2.0" - luxon: "npm:^3.0.0" msw: "npm:^1.0.0" p-limit: "npm:^3.1.0" uuid: "npm:^11.0.0" @@ -4505,7 +4496,6 @@ __metadata: "@backstage/plugin-catalog-backend-module-github": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-node": "workspace:^" - luxon: "npm:^3.0.0" languageName: unknown linkType: soft @@ -4515,12 +4505,10 @@ __metadata: dependencies: "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" - "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-catalog-backend": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-node": "workspace:^" @@ -4531,7 +4519,6 @@ __metadata: "@types/lodash": "npm:^4.14.151" git-url-parse: "npm:^15.0.0" lodash: "npm:^4.17.21" - luxon: "npm:^3.0.0" minimatch: "npm:^9.0.0" msw: "npm:^2.0.0" type-fest: "npm:^4.41.0" @@ -4550,7 +4537,6 @@ __metadata: "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-backend-test-utils": "workspace:^" "@backstage/plugin-events-node": "workspace:^" - luxon: "npm:^3.0.0" languageName: unknown linkType: soft @@ -4572,7 +4558,6 @@ __metadata: "@gitbeaker/rest": "npm:^40.0.3" "@types/lodash": "npm:^4.14.151" lodash: "npm:^4.17.21" - luxon: "npm:^3.0.0" msw: "npm:^1.0.0" node-fetch: "npm:^2.7.0" uuid: "npm:^11.0.0" @@ -4654,7 +4639,6 @@ __metadata: "@microsoft/microsoft-graph-types": "npm:^2.6.0" "@types/lodash": "npm:^4.14.151" lodash: "npm:^4.17.21" - luxon: "npm:^3.0.0" msw: "npm:^1.0.0" p-limit: "npm:^3.0.2" qs: "npm:^6.9.4" @@ -4694,7 +4678,6 @@ __metadata: "@backstage/types": "workspace:^" "@types/lodash": "npm:^4.14.151" lodash: "npm:^4.17.21" - luxon: "npm:^3.0.0" msw: "npm:^1.0.0" uuid: "npm:^11.0.0" languageName: unknown @@ -5533,19 +5516,13 @@ __metadata: "@backstage/plugin-permission-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@kubernetes-models/apimachinery": "npm:^2.0.0" - "@kubernetes-models/base": "npm:^5.0.0" "@material-ui/core": "npm:^4.12.2" "@material-ui/lab": "npm:4.0.0-alpha.61" - "@testing-library/dom": "npm:^10.0.0" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" "@types/node": "npm:^20.16.0" "@types/react": "npm:^18.0.0" - cronstrue: "npm:^2.2.0" - js-yaml: "npm:^4.0.0" kubernetes-models: "npm:^4.1.0" - lodash: "npm:^4.17.21" - luxon: "npm:^3.0.0" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.3.0" @@ -5660,22 +5637,11 @@ __metadata: "@backstage/plugin-kubernetes-react": "workspace:^" "@backstage/plugin-permission-react": "workspace:^" "@backstage/test-utils": "workspace:^" - "@kubernetes-models/apimachinery": "npm:^2.0.0" - "@kubernetes-models/base": "npm:^5.0.0" - "@kubernetes/client-node": "npm:1.1.2" "@material-ui/core": "npm:^4.12.2" "@testing-library/dom": "npm:^10.0.0" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" "@types/react": "npm:^18.0.0" - "@xterm/addon-attach": "npm:^0.11.0" - "@xterm/addon-fit": "npm:^0.10.0" - "@xterm/xterm": "npm:^5.5.0" - cronstrue: "npm:^2.2.0" - js-yaml: "npm:^4.0.0" - kubernetes-models: "npm:^4.1.0" - lodash: "npm:^4.17.21" - luxon: "npm:^3.0.0" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.3.0" @@ -26680,7 +26646,7 @@ __metadata: languageName: node linkType: hard -"cronstrue@npm:^2.2.0, cronstrue@npm:^2.32.0": +"cronstrue@npm:^2.32.0": version: 2.52.0 resolution: "cronstrue@npm:2.52.0" bin: From 02c67c58cfafd662968674b67f0f5f446bc05f54 Mon Sep 17 00:00:00 2001 From: TA31OU Date: Thu, 18 Sep 2025 08:51:01 +0200 Subject: [PATCH 052/211] Feedback Signed-off-by: TA31OU --- plugins/catalog-backend-module-gitea/package.json | 2 +- .../EntityRelationsGraph/DefaultRenderEdge.tsx | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx diff --git a/plugins/catalog-backend-module-gitea/package.json b/plugins/catalog-backend-module-gitea/package.json index 8389ee70ef..c30c6a7dd2 100644 --- a/plugins/catalog-backend-module-gitea/package.json +++ b/plugins/catalog-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitea", - "version": "0.1.4-next.1", + "version": "0.1.4-next.0", "description": "The gitea backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx deleted file mode 100644 index 379ce77f52..0000000000 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/DefaultRenderEdge.tsx +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2025 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 2aded73e384cb97bc7ed4a663ef7e94571f4d1aa Mon Sep 17 00:00:00 2001 From: Joseph Roberto Date: Thu, 18 Sep 2025 12:45:13 -0500 Subject: [PATCH 053/211] feat: Allow configurable pagelength for Bitbucket Cloud provider Signed-off-by: Joseph Roberto --- .changeset/tame-hairs-smash.md | 6 ++++ plugins/bitbucket-cloud-common/report.api.md | 2 ++ .../src/BitbucketCloudClient.test.ts | 35 +++++++++++++++++++ .../src/BitbucketCloudClient.ts | 2 ++ .../src/pagination.test.ts | 16 ++++++++- .../bitbucket-cloud-common/src/pagination.ts | 7 ++-- .../config.d.ts | 10 ++++++ .../providers/BitbucketCloudEntityProvider.ts | 2 +- ...BitbucketCloudEntityProviderConfig.test.ts | 22 ++++++++++++ .../BitbucketCloudEntityProviderConfig.ts | 4 +++ 10 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 .changeset/tame-hairs-smash.md diff --git a/.changeset/tame-hairs-smash.md b/.changeset/tame-hairs-smash.md new file mode 100644 index 0000000000..d74b2ffeb7 --- /dev/null +++ b/.changeset/tame-hairs-smash.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch +'@backstage/plugin-bitbucket-cloud-common': patch +--- + +Allow for passing a pagelen parameter to configure the pagelength property of the BitbucketCloudEntityProvider searchCode pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. diff --git a/plugins/bitbucket-cloud-common/report.api.md b/plugins/bitbucket-cloud-common/report.api.md index c53863b92f..11f0c93593 100644 --- a/plugins/bitbucket-cloud-common/report.api.md +++ b/plugins/bitbucket-cloud-common/report.api.md @@ -36,6 +36,7 @@ export class BitbucketCloudClient { workspace: string, query: string, options?: FilterAndSortOptions & PartialResponseOptions, + pagelen?: number, ): WithPagination; } @@ -517,6 +518,7 @@ export class WithPagination< constructor( createUrl: (options: PaginationOptions) => URL, fetch: (url: URL) => Promise, + pagelen?: number | undefined, ); // (undocumented) getPage(options?: PaginationOptions): Promise; diff --git a/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.test.ts b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.test.ts index 7558c92138..ed696ec6fb 100644 --- a/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.test.ts +++ b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.test.ts @@ -79,6 +79,41 @@ describe('BitbucketCloudClient', () => { expect(results[0].file!.path).toEqual('path/to/file'); }); + it('searchCode with custom pagelen', async () => { + server.use( + rest.get( + `https://api.bitbucket.org/2.0/workspaces/ws/search/code`, + (req, res, ctx) => { + const pagelen = req.url.searchParams.get('pagelen'); + expect(pagelen).toBe('50'); + + const response: Models.SearchResultPage = { + values: [ + { + content_match_count: 1, + file: { + type: 'commit_file', + path: 'path/to/file', + }, + }, + ], + }; + return res(ctx.json(response)); + }, + ), + ); + + const pagination = client.searchCode('ws', 'query', undefined, 50); + + const results = []; + for await (const result of pagination.iterateResults()) { + results.push(result); + } + + expect(results).toHaveLength(1); + expect(results[0].file!.path).toEqual('path/to/file'); + }); + it('listRepositoriesByWorkspace', async () => { server.use( rest.get( diff --git a/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.ts b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.ts index 86f56dd70d..d1d4a35561 100644 --- a/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.ts +++ b/plugins/bitbucket-cloud-common/src/BitbucketCloudClient.ts @@ -40,6 +40,7 @@ export class BitbucketCloudClient { workspace: string, query: string, options?: FilterAndSortOptions & PartialResponseOptions, + pagelen?: number, ): WithPagination { const workspaceEnc = encodeURIComponent(workspace); return new WithPagination( @@ -50,6 +51,7 @@ export class BitbucketCloudClient { search_query: query, }), url => this.getTypeMapped(url), + pagelen, ); } diff --git a/plugins/bitbucket-cloud-common/src/pagination.test.ts b/plugins/bitbucket-cloud-common/src/pagination.test.ts index f263f51897..c355a798e4 100644 --- a/plugins/bitbucket-cloud-common/src/pagination.test.ts +++ b/plugins/bitbucket-cloud-common/src/pagination.test.ts @@ -24,7 +24,7 @@ interface TestResultItem { interface TestPage extends Models.Paginated {} describe('WithPagination', () => { - const createPagination = () => + const createPagination = (pagelen?: number) => new WithPagination( opts => new URL( @@ -45,6 +45,7 @@ describe('WithPagination', () => { ], }; }, + pagelen, ); it('iterateResults', async () => { @@ -121,4 +122,17 @@ describe('WithPagination', () => { ); }); }); + + it('uses custom pagelen when provided', async () => { + const pagination = createPagination(50); + + const page = await pagination.getPage(); + + expect(page.page).toEqual(1); + expect(page.next).toEqual('http://localhost/create-url?page=2&pagelen=50'); + expect(page.values).toHaveLength(1); + expect((page.values! as TestResultItem[])[0].url).toEqual( + 'http://localhost/create-url?page=1&pagelen=50', + ); + }); }); diff --git a/plugins/bitbucket-cloud-common/src/pagination.ts b/plugins/bitbucket-cloud-common/src/pagination.ts index 98e4f9f9d7..5d699a4a6f 100644 --- a/plugins/bitbucket-cloud-common/src/pagination.ts +++ b/plugins/bitbucket-cloud-common/src/pagination.ts @@ -30,10 +30,11 @@ export class WithPagination< constructor( private readonly createUrl: (options: PaginationOptions) => URL, private readonly fetch: (url: URL) => Promise, + private readonly pagelen?: number, ) {} getPage(options?: PaginationOptions): Promise { - const opts = { page: 1, pagelen: 100, ...options }; + const opts = { page: 1, pagelen: this.pagelen ?? 100, ...options }; const url = this.createUrl(opts); return this.fetch(url); } @@ -41,7 +42,7 @@ export class WithPagination< async *iteratePages( options?: PaginationOptions, ): AsyncGenerator { - const opts = { page: 1, pagelen: 100, ...options }; + const opts = { page: 1, pagelen: this.pagelen ?? 100, ...options }; let url: URL | undefined = this.createUrl(opts); let res; do { @@ -52,7 +53,7 @@ export class WithPagination< } async *iterateResults(options?: PaginationOptions) { - const opts = { page: 1, pagelen: 100, ...options }; + const opts = { page: 1, pagelen: this.pagelen ?? 100, ...options }; let url: URL | undefined = this.createUrl(opts); let res; do { diff --git a/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts b/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts index 1051976b24..bc6d618aa8 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/config.d.ts @@ -59,6 +59,11 @@ export interface Config { * (Optional) TaskScheduleDefinition for the discovery. */ schedule?: SchedulerServiceTaskScheduleDefinitionConfig; + /** + * (Optional) Number of results to fetch per page from Bitbucket API. Default to 100. + * @visibility frontend + */ + pagelen?: number; } | { [name: string]: { @@ -92,6 +97,11 @@ export interface Config { * (Optional) TaskScheduleDefinition for the discovery. */ schedule?: SchedulerServiceTaskScheduleDefinitionConfig; + /** + * (Optional) Number of results to fetch per page from Bitbucket API. Default to 100. + * @visibility frontend + */ + pagelen?: number; }; }; }; diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProvider.ts index 64a58c2924..33327e1f33 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProvider.ts @@ -409,7 +409,7 @@ export class BitbucketCloudEntityProvider implements EntityProvider { ].join(','); const searchResults = this.client - .searchCode(workspace, query, { fields }) + .searchCode(workspace, query, { fields }, this.config.pagelen) .iterateResults(); const result: IngestionTarget[] = []; diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.test.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.test.ts index 7f13616586..e039708099 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.test.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.test.ts @@ -92,6 +92,7 @@ describe('readProviderConfigs', () => { projectKey: undefined, repoSlug: undefined, }, + pagelen: undefined, }); expect(providerConfigs[1]).toEqual({ id: 'providerCustomCatalogPath', @@ -101,6 +102,7 @@ describe('readProviderConfigs', () => { projectKey: undefined, repoSlug: undefined, }, + pagelen: undefined, }); expect(providerConfigs[2]).toEqual({ id: 'providerWithProjectKeyFilter', @@ -110,6 +112,7 @@ describe('readProviderConfigs', () => { projectKey: /^projectKey.*filter$/, repoSlug: undefined, }, + pagelen: undefined, }); expect(providerConfigs[3]).toEqual({ id: 'providerWithRepoSlugFilter', @@ -119,6 +122,7 @@ describe('readProviderConfigs', () => { projectKey: undefined, repoSlug: /^repoSlug.*filter$/, }, + pagelen: undefined, }); expect(providerConfigs[4]).toEqual({ id: 'providerWithSchedule', @@ -134,6 +138,24 @@ describe('readProviderConfigs', () => { minutes: 3, }, }, + pagelen: undefined, }); }); + + it('provider config with pagelen', () => { + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketCloud: { + workspace: 'test-ws', + pagelen: 50, + }, + }, + }, + }); + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(1); + expect(providerConfigs[0].pagelen).toEqual(50); + }); }); diff --git a/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.ts b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.ts index 5a806d2e0b..0a8259e4d1 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.ts +++ b/plugins/catalog-backend-module-bitbucket-cloud/src/providers/BitbucketCloudEntityProviderConfig.ts @@ -32,6 +32,7 @@ export type BitbucketCloudEntityProviderConfig = { repoSlug?: RegExp; }; schedule?: SchedulerServiceTaskScheduleDefinition; + pagelen?: number; }; export function readProviderConfigs( @@ -72,6 +73,8 @@ function readProviderConfig( ) : undefined; + const pagelen = config.getOptionalNumber('pagelen'); + return { id, catalogPath, @@ -83,6 +86,7 @@ function readProviderConfig( repoSlug: repoSlugPattern ? compileRegExp(repoSlugPattern) : undefined, }, schedule, + pagelen, }; } From f5695351141ce9414c53185c7bd4d6b50c2dc1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=91=EC=A4=80?= Date: Sat, 20 Sep 2025 13:55:54 +0900 Subject: [PATCH 054/211] feat: add kubernetes configmap component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 김병준 --- .../src/components/Cluster/Cluster.tsx | 6 + .../ConfigmapsAccordions.tsx | 118 ++++++++++++++++++ .../ConfigmapsAccordions/ConfigmapsDrawer.tsx | 64 ++++++++++ .../components/ConfigmapsAccordions/index.ts | 16 +++ 4 files changed, 204 insertions(+) create mode 100644 plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.tsx create mode 100644 plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx create mode 100644 plugins/kubernetes-react/src/components/ConfigmapsAccordions/index.ts diff --git a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx index 642ed642a4..4e8c4a1947 100644 --- a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx +++ b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx @@ -30,6 +30,7 @@ import { DeploymentsAccordions } from '../DeploymentsAccordions'; import { StatefulSetsAccordions } from '../StatefulSetsAccordions'; import { IngressesAccordions } from '../IngressesAccordions'; import { ServicesAccordions } from '../ServicesAccordions'; +import { ConfigmapsAccordions } from '../ConfigmapsAccordions'; import { CronJobsAccordions } from '../CronJobsAccordions'; import { CustomResources } from '../CustomResources'; import { DaemonSetsAccordions } from '../DaemonSetsAccordions'; @@ -170,6 +171,11 @@ export const Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => { ) : undefined} + {groupedResponses.configMaps.length > 0 ? ( + + + + ) : undefined} {groupedResponses.cronJobs.length > 0 ? ( diff --git a/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.tsx b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.tsx new file mode 100644 index 0000000000..81ab874737 --- /dev/null +++ b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.tsx @@ -0,0 +1,118 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useContext } from 'react'; +import Accordion from '@material-ui/core/Accordion'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import type { V1ConfigMap } from '@kubernetes/client-node'; +import { ConfigmapsDrawer } from './ConfigmapsDrawer.tsx'; +import { GroupedResponsesContext } from '../../hooks'; +import { StructuredMetadataTable } from '@backstage/core-components'; + +type ConfigmapSummaryProps = { + configmap: V1ConfigMap; +}; + +const ConfigmapSummary = ({ configmap }: ConfigmapSummaryProps) => { + return ( + + + + + + + + Data Count: {configmap.data ? Object.keys(configmap.data).length : 0} + + + + ); +}; + +type ConfigmapsCardProps = { + configmap: V1ConfigMap; +}; + +const ConfigmapCard = ({ configmap }: ConfigmapsCardProps) => { + const metadata: any = {}; + + metadata.data = configmap.data; + + return ( + + ); +}; + +/** + * + * + * @public + */ +export type ConfigmapsAccordionsProps = {}; + +type ConfigmapsAccordionProps = { + configmap: V1ConfigMap; +}; + +const ConfigmapsAccordion = ({ configmap }: ConfigmapsAccordionProps) => { + return ( + + }> + + + + + + + ); +}; + +/** + * + * + * @public + */ +export const ConfigmapsAccordions = ({}: ConfigmapsAccordionsProps) => { + const groupedResponses = useContext(GroupedResponsesContext); + return ( + + {groupedResponses.configMaps.map((configmap, i) => ( + + + + ))} + + ); +}; diff --git a/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx new file mode 100644 index 0000000000..97e8aab723 --- /dev/null +++ b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.tsx @@ -0,0 +1,64 @@ +/* + * 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 { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import Chip from '@material-ui/core/Chip'; +import type { V1ConfigMap } from '@kubernetes/client-node'; + +export const ConfigmapsDrawer = ({ + configmap, + expanded, +}: { + configmap: V1ConfigMap; + expanded?: boolean; +}) => { + const namespace = configmap.metadata?.namespace; + return ( + { + return configmapObject || {}; + }} + > + + + + {configmap.metadata?.name ?? 'unknown object'} + + + + + ConfigMap + + + {namespace && ( + + + + )} + + + ); +}; diff --git a/plugins/kubernetes-react/src/components/ConfigmapsAccordions/index.ts b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/index.ts new file mode 100644 index 0000000000..25a1578a79 --- /dev/null +++ b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export * from './ConfigmapsAccordions.tsx'; From 5787cda0b9efa66bd94b9ca3f872cc6b9d68ccba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=91=EC=A4=80?= Date: Sat, 20 Sep 2025 14:07:50 +0900 Subject: [PATCH 055/211] feat: add test for configmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 김병준 --- .../src/__fixtures__/1-configmaps.json | 26 +++++++++ .../src/__fixtures__/2-configmaps.json | 47 ++++++++++++++++ .../ConfigmapsAccordions.test.tsx | 54 +++++++++++++++++++ .../ConfigmapsDrawer.test.tsx | 37 +++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 plugins/kubernetes-react/src/__fixtures__/1-configmaps.json create mode 100644 plugins/kubernetes-react/src/__fixtures__/2-configmaps.json create mode 100644 plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.test.tsx create mode 100644 plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.test.tsx diff --git a/plugins/kubernetes-react/src/__fixtures__/1-configmaps.json b/plugins/kubernetes-react/src/__fixtures__/1-configmaps.json new file mode 100644 index 0000000000..ab0518e87d --- /dev/null +++ b/plugins/kubernetes-react/src/__fixtures__/1-configmaps.json @@ -0,0 +1,26 @@ +{ + "configMaps": [ + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "name": "app-config", + "namespace": "default", + "uid": "1ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "data": { + "database.host": "localhost", + "database.port": "5432", + "app.name": "dice-roller", + "app.version": "1.0.0" + } + } + ] +} diff --git a/plugins/kubernetes-react/src/__fixtures__/2-configmaps.json b/plugins/kubernetes-react/src/__fixtures__/2-configmaps.json new file mode 100644 index 0000000000..2c0c7f8121 --- /dev/null +++ b/plugins/kubernetes-react/src/__fixtures__/2-configmaps.json @@ -0,0 +1,47 @@ +{ + "configMaps": [ + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "name": "app-config", + "namespace": "default", + "uid": "1ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "data": { + "database.host": "localhost", + "database.port": "5432", + "app.name": "dice-roller", + "app.version": "1.0.0" + } + }, + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "name": "redis-config", + "namespace": "default", + "uid": "2ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732553", + "creationTimestamp": "2021-07-16T22:40:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "redis" + }, + "annotations": {} + }, + "data": { + "redis.conf": "# Redis configuration\nmaxmemory 256mb\nmaxmemory-policy allkeys-lru", + "redis.host": "redis-service", + "redis.port": "6379" + } + } + ] +} diff --git a/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.test.tsx b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.test.tsx new file mode 100644 index 0000000000..d819a01cd9 --- /dev/null +++ b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsAccordions.test.tsx @@ -0,0 +1,54 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { screen } from '@testing-library/react'; +import { ConfigmapsAccordions } from './ConfigmapsAccordions'; +import * as oneConfigmapsFixture from '../../__fixtures__/1-configmaps.json'; +import * as twoConfigmapsFixture from '../../__fixtures__/2-configmaps.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { kubernetesProviders } from '../../hooks/test-utils'; + +describe('ConfigmapsAccordions', () => { + it('should render 1 configmap', async () => { + const wrapper = kubernetesProviders( + oneConfigmapsFixture, + new Set(), + ); + + await renderInTestApp(wrapper()); + + expect(screen.getByText('app-config')).toBeInTheDocument(); + expect(screen.getByText('ConfigMap')).toBeInTheDocument(); + expect(screen.getByText('namespace: default')).toBeInTheDocument(); + expect(screen.getByText('Data Count: 4')).toBeInTheDocument(); + }); + + it('should render 2 configmaps', async () => { + const wrapper = kubernetesProviders( + twoConfigmapsFixture, + new Set(), + ); + + await renderInTestApp(wrapper()); + + expect(screen.getByText('app-config')).toBeInTheDocument(); + expect(screen.getByText('redis-config')).toBeInTheDocument(); + expect(screen.getAllByText('ConfigMap')).toHaveLength(2); + expect(screen.getAllByText('namespace: default')).toHaveLength(2); + expect(screen.getByText('Data Count: 4')).toBeInTheDocument(); + expect(screen.getByText('Data Count: 3')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.test.tsx b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.test.tsx new file mode 100644 index 0000000000..cde7265cab --- /dev/null +++ b/plugins/kubernetes-react/src/components/ConfigmapsAccordions/ConfigmapsDrawer.test.tsx @@ -0,0 +1,37 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as oneConfigmapsFixture from '../../__fixtures__/1-configmaps.json'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { ConfigmapsDrawer } from './ConfigmapsDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; + +describe('ConfigmapsDrawer', () => { + it('should render configmap drawer', async () => { + const { getByText, getAllByText } = await renderInTestApp( + + + , + ); + + expect(getAllByText('app-config')).toHaveLength(3); + expect(getAllByText('ConfigMap')).toHaveLength(3); + expect(getByText('YAML')).toBeInTheDocument(); + expect(getByText('namespace: default')).toBeInTheDocument(); + }); +}); From ac405f2ed726015e024cb6c8d021679b86b6bbec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=91=EC=A4=80?= Date: Sat, 20 Sep 2025 22:56:04 +0900 Subject: [PATCH 056/211] chore: add changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 김병준 --- .changeset/famous-loops-tickle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/famous-loops-tickle.md diff --git a/.changeset/famous-loops-tickle.md b/.changeset/famous-loops-tickle.md new file mode 100644 index 0000000000..ae8c9d80ee --- /dev/null +++ b/.changeset/famous-loops-tickle.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-react': patch +--- + +The configmaps added to be rendered From d772b516cd40d31d3f327d8d0b42f2ee06951f79 Mon Sep 17 00:00:00 2001 From: vickstrom Date: Mon, 22 Sep 2025 10:31:01 +0200 Subject: [PATCH 057/211] remove host from azure blog storage integration type Signed-off-by: vickstrom --- .changeset/five-olives-bet.md | 5 +++++ packages/integration/config.d.ts | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 .changeset/five-olives-bet.md diff --git a/.changeset/five-olives-bet.md b/.changeset/five-olives-bet.md new file mode 100644 index 0000000000..8623e780b5 --- /dev/null +++ b/.changeset/five-olives-bet.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +remove host from azure blob storage integration type diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index c85d87144b..298d765697 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -105,11 +105,9 @@ export interface Config { endpointSuffix?: string; /** - * The host of the target that this matches on, e.g., "blob.core.windows.net". + * Optional endpoint URL for custom domain. Uses default if not provided. * @visibility frontend */ - host: string; - endpoint?: string; /** * Optional credential to use for Azure Active Directory authentication. From 37f540b71db528990ab0fab590e4e9b6a1f6dcbf Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Mon, 22 Sep 2025 10:37:52 +0100 Subject: [PATCH 058/211] update test handlers to simulate checking for both project.id and project.path_with_namespace Signed-off-by: Claire Peng --- .../src/__testUtils__/handlers.ts | 44 ++++++++++++------- .../src/lib/client.test.ts | 7 ++- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/__testUtils__/handlers.ts b/plugins/catalog-backend-module-gitlab/src/__testUtils__/handlers.ts index 9af00d2564..241bc86df6 100644 --- a/plugins/catalog-backend-module-gitlab/src/__testUtils__/handlers.ts +++ b/plugins/catalog-backend-module-gitlab/src/__testUtils__/handlers.ts @@ -266,24 +266,34 @@ const httpProjectFindByIdDynamic = all_projects_response.map(project => { return res(ctx.json(all_projects_response.find(p => p.id === project.id))); }); }); -const httpProjectCatalogDynamic = all_projects_response.map(project => { - const path: string = project.path_with_namespace - ? project.path_with_namespace!.replace(/\//g, '%2F') - : `${project.path_with_namespace}%2F${project.name}`; - return rest.head( - `${apiBaseUrl}/projects/${path}/repository/files/catalog-info.yaml`, - (req, res, ctx) => { - const branch = req.url.searchParams.get('ref'); - if ( - branch === project.default_branch || - branch === 'main' || - branch === 'develop' - ) { - return res(ctx.status(200)); - } - return res(ctx.status(404, 'Not Found')); - }, +/** + * Checks for both project id and namespaced path, as these can both be used for the :id segment in Gitlab API: + * https://docs.gitlab.com/api/repository_files/#get-file-from-repository + */ +const httpProjectCatalogDynamic = all_projects_response.flatMap(project => { + const possibleIdSegments: string[] = [ + project.id.toString(), + project.path_with_namespace ?? '', + ]; + + return possibleIdSegments.map(seg => + rest.head( + `${apiBaseUrl}/projects/${encodeURIComponent( + seg, + )}/repository/files/catalog-info.yaml`, + (req, res, ctx) => { + const branch = req.url.searchParams.get('ref'); + if ( + branch === project.default_branch || + branch === 'main' || + branch === 'develop' + ) { + return res(ctx.status(200)); + } + return res(ctx.status(404, 'Not Found')); + }, + ), ); }); diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts index 9b3bac6399..5a60d18493 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.test.ts @@ -580,7 +580,12 @@ describe('hasFile', () => { }); }); - it('should find catalog file', async () => { + it('should find catalog file by id', async () => { + const hasFile = await client.hasFile('1', 'main', 'catalog-info.yaml'); + expect(hasFile).toBe(true); + }); + + it('should find catalog file by namespace path', async () => { const hasFile = await client.hasFile( 'group1/test-repo1', 'main', From 047681317a79bd32d0a7b7be46acb5ee120f65ae Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Mon, 22 Sep 2025 10:49:28 +0100 Subject: [PATCH 059/211] update jsdoc for hasFile for the projectPath param Signed-off-by: Claire Peng --- plugins/catalog-backend-module-gitlab/src/lib/client.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index 3178e2c0d1..fcdf135199 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -342,8 +342,9 @@ export class GitLabClient { /** * General existence check. + * @see {@link https://docs.gitlab.com/api/repository_files/#get-file-from-repository | GitLab Repository Files API} * - * @param projectPath - The path to the project + * @param projectPath - The path to the project, either the numeric ID or the namespaced path. * @param branch - The branch used to search * @param filePath - The path to the file */ From 53aedd6a177f9db4622eb3a1ae285dd558bc3ab0 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Mon, 22 Sep 2025 11:09:29 +0100 Subject: [PATCH 060/211] rename projectPath param to project Signed-off-by: Claire Peng --- plugins/catalog-backend-module-gitlab/src/lib/client.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index fcdf135199..7860c66d1a 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -344,17 +344,17 @@ export class GitLabClient { * General existence check. * @see {@link https://docs.gitlab.com/api/repository_files/#get-file-from-repository | GitLab Repository Files API} * - * @param projectPath - The path to the project, either the numeric ID or the namespaced path. + * @param project - The path to the project, either the numeric ID or the namespaced path. * @param branch - The branch used to search * @param filePath - The path to the file */ async hasFile( - projectPath: string, + project: string, branch: string, filePath: string, ): Promise { const endpoint: string = `/projects/${encodeURIComponent( - projectPath, + project, )}/repository/files/${encodeURIComponent(filePath)}`; const request = new URL(`${this.config.apiBaseUrl}${endpoint}`); request.searchParams.append('ref', branch); From bcc071b2cd9c2e5b40528a489ab174573c51dd25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofia=20Sj=C3=B6blad?= Date: Mon, 22 Sep 2025 12:56:11 +0200 Subject: [PATCH 061/211] fix: remove default selection of tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sofia Sjöblad --- packages/ui/src/components/Header/Header.stories.tsx | 6 ++++++ packages/ui/src/components/Tabs/Tabs.tsx | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/Header/Header.stories.tsx b/packages/ui/src/components/Header/Header.stories.tsx index 7ae834f82e..6ccda8bb50 100644 --- a/packages/ui/src/components/Header/Header.stories.tsx +++ b/packages/ui/src/components/Header/Header.stories.tsx @@ -57,14 +57,17 @@ const tabs: HeaderTab[] = [ { id: 'overview', label: 'Overview', + href: '/overview', }, { id: 'checks', label: 'Checks', + href: '/checks', }, { id: 'tracks', label: 'Tracks', + href: '/tracks', }, { id: 'campaigns', @@ -82,14 +85,17 @@ const tabs2: HeaderTab[] = [ { id: 'Banana', label: 'Banana', + href: '/banana', }, { id: 'Apple', label: 'Apple', + href: '/apple', }, { id: 'Orange', label: 'Orange', + href: '/orange', }, ]; diff --git a/packages/ui/src/components/Tabs/Tabs.tsx b/packages/ui/src/components/Tabs/Tabs.tsx index d4c820e529..dacb355fc9 100644 --- a/packages/ui/src/components/Tabs/Tabs.tsx +++ b/packages/ui/src/components/Tabs/Tabs.tsx @@ -117,7 +117,7 @@ export const Tabs = (props: TabsProps) => { } } } - return undefined; + return null; })(); if (!children) return null; From d84bf179485dd6ebf8d804171d2a48ab88943a1c Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Mon, 22 Sep 2025 12:49:07 +0100 Subject: [PATCH 062/211] update client.hasFile to prefer project.id, then namespacedPath, then empty string fallback Signed-off-by: Claire Peng --- plugins/catalog-backend-module-gitlab/src/lib/client.ts | 6 +++--- .../src/providers/GitlabDiscoveryEntityProvider.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index 7860c66d1a..46ff54846c 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -344,17 +344,17 @@ export class GitLabClient { * General existence check. * @see {@link https://docs.gitlab.com/api/repository_files/#get-file-from-repository | GitLab Repository Files API} * - * @param project - The path to the project, either the numeric ID or the namespaced path. + * @param projectIdentifier - The identifier of the project, either the numeric ID or the namespaced path. * @param branch - The branch used to search * @param filePath - The path to the file */ async hasFile( - project: string, + projectIdentifier: string, branch: string, filePath: string, ): Promise { const endpoint: string = `/projects/${encodeURIComponent( - project, + projectIdentifier, )}/repository/files/${encodeURIComponent(filePath)}`; const request = new URL(`${this.config.apiBaseUrl}${endpoint}`); request.searchParams.append('ref', branch); diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index 11b6a0ce04..24bc5faccb 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -590,7 +590,7 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { this.config.fallbackBranch; const hasFile = await client.hasFile( - project.path_with_namespace ?? '', + project.id.toString() ?? project.path_with_namespace ?? '', project_branch, this.config.catalogFile, ); From 827340fef35da65139992bdeccbd69cab1ac42be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofia=20Sj=C3=B6blad?= Date: Mon, 22 Sep 2025 14:25:49 +0200 Subject: [PATCH 063/211] Add changeset for tab changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sofia Sjöblad --- .changeset/clever-papers-watch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/clever-papers-watch.md diff --git a/.changeset/clever-papers-watch.md b/.changeset/clever-papers-watch.md new file mode 100644 index 0000000000..831d4c3455 --- /dev/null +++ b/.changeset/clever-papers-watch.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +remove default selection of tab From bcac475df24d338433e3df8e86bcd58826d8995e Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Mon, 22 Sep 2025 13:42:16 +0100 Subject: [PATCH 064/211] update shouldProcessProject to first check by namespacedPath (as previous), then check by id if failed Signed-off-by: Claire Peng --- .../src/providers/GitlabDiscoveryEntityProvider.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index 24bc5faccb..869374de07 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -589,12 +589,22 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { project.default_branch ?? this.config.fallbackBranch; - const hasFile = await client.hasFile( - project.id.toString() ?? project.path_with_namespace ?? '', + // Find file with namespaced path (most use-cases) + let hasFile = await client.hasFile( + project.path_with_namespace ?? '', project_branch, this.config.catalogFile, ); + // Find file with project id if namespace failed + if (!hasFile) { + hasFile = await client.hasFile( + project.id.toString(), + project_branch, + this.config.catalogFile, + ); + } + return hasFile; } } From 9890488abbda4b9e35cec5cb4c11be248d34c448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 22 Sep 2025 14:58:12 +0200 Subject: [PATCH 065/211] make some inputs mandatory, that are always provided nowadays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/brave-teeth-reply.md | 5 + .../DefaultCatalogProcessingEngine.test.ts | 9 + .../DefaultCatalogProcessingEngine.ts | 30 ++- .../src/service/CatalogBuilder.ts | 4 +- .../src/service/DefaultRefreshService.test.ts | 1 + .../src/service/createRouter.test.ts | 6 +- .../src/service/createRouter.ts | 179 +++++++++--------- .../src/tests/integration.test.ts | 1 + 8 files changed, 120 insertions(+), 115 deletions(-) create mode 100644 .changeset/brave-teeth-reply.md diff --git a/.changeset/brave-teeth-reply.md b/.changeset/brave-teeth-reply.md new file mode 100644 index 0000000000..8a500692a7 --- /dev/null +++ b/.changeset/brave-teeth-reply.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Internal refactor to remove remnants of the old backend system diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts index ab54a7b885..93c22aab1e 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts @@ -70,6 +70,7 @@ describe('DefaultCatalogProcessingEngine', () => { orchestrator: orchestrator, stitcher: stitcher, createHash: () => hash, + scheduler: mockServices.scheduler(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -136,6 +137,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, }); @@ -219,6 +221,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, }); @@ -296,6 +299,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, }); @@ -355,6 +359,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, }); @@ -470,6 +475,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, }); @@ -575,6 +581,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, }); @@ -658,6 +665,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, }); @@ -746,6 +754,7 @@ describe('DefaultCatalogProcessingEngine', () => { knex: {} as any, orchestrator: orchestrator, stitcher: stitcher, + scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, }); diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts index 266b80f9a4..7061309175 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts @@ -59,7 +59,7 @@ const stableStringifyArray = (arr: any[]) => { // is just one. export class DefaultCatalogProcessingEngine { private readonly config: Config; - private readonly scheduler?: SchedulerService; + private readonly scheduler: SchedulerService; private readonly logger: LoggerService; private readonly knex: Knex; private readonly processingDatabase: ProcessingDatabase; @@ -79,7 +79,7 @@ export class DefaultCatalogProcessingEngine { constructor(options: { config: Config; - scheduler?: SchedulerService; + scheduler: SchedulerService; logger: LoggerService; knex: Knex; processingDatabase: ProcessingDatabase; @@ -370,25 +370,17 @@ export class DefaultCatalogProcessingEngine { } }; - if (this.scheduler) { - const abortController = new AbortController(); + const abortController = new AbortController(); + this.scheduler.scheduleTask({ + id: 'catalog_orphan_cleanup', + frequency: { milliseconds: this.orphanCleanupIntervalMs }, + timeout: { milliseconds: this.orphanCleanupIntervalMs * 0.8 }, + fn: runOnce, + signal: abortController.signal, + }); - this.scheduler.scheduleTask({ - id: 'catalog_orphan_cleanup', - frequency: { milliseconds: this.orphanCleanupIntervalMs }, - timeout: { milliseconds: this.orphanCleanupIntervalMs * 0.8 }, - fn: runOnce, - signal: abortController.signal, - }); - - return () => { - abortController.abort(); - }; - } - - const intervalKey = setInterval(runOnce, this.orphanCleanupIntervalMs); return () => { - clearInterval(intervalKey); + abortController.abort(); }; } } diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index dcd677152e..40fcc3a624 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -119,10 +119,10 @@ export type CatalogEnvironment = { reader: UrlReaderService; permissions: PermissionsService | PermissionAuthorizer; permissionsRegistry?: PermissionsRegistryService; - scheduler?: SchedulerService; + scheduler: SchedulerService; auth: AuthService; httpAuth: HttpAuthService; - auditor?: AuditorService; + auditor: AuditorService; }; /** diff --git a/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts b/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts index a89b21f79f..f999400b8a 100644 --- a/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts @@ -121,6 +121,7 @@ describe('DefaultRefreshService', () => { processingDatabase: db, knex: knex, stitcher: stitcher, + scheduler: mockServices.scheduler(), orchestrator: { async process(request: EntityProcessingRequest) { const entityRef = stringifyEntityRef(request.entity); diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index 0246a3d56b..299395552a 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -156,12 +156,12 @@ describe('createRouter readonly disabled', () => { logger: mockServices.logger.mock(), refreshService, config: new ConfigReader(undefined), - permissionIntegrationRouter: express.Router(), auth: mockServices.auth(), httpAuth: mockServices.httpAuth(), locationAnalyzer, permissionsService, enableRelationsCompatibility: true, // added + auditor: mockServices.auditor.mock(), }); app = await wrapServer(express().use(router)); @@ -218,12 +218,12 @@ describe('createRouter readonly disabled', () => { logger: mockServices.logger.mock(), refreshService, config: new ConfigReader(undefined), - permissionIntegrationRouter: express.Router(), auth: mockServices.auth(), httpAuth: mockServices.httpAuth(), locationAnalyzer, permissionsService, enableRelationsCompatibility: true, + auditor: mockServices.auditor.mock(), }); app = await wrapServer(express().use(router)); entitiesCatalog.entities.mockResolvedValueOnce({ @@ -951,6 +951,7 @@ describe('createRouter readonly and raw json enabled', () => { permissionIntegrationRouter: express.Router(), auth: mockServices.auth(), httpAuth: mockServices.httpAuth(), + orchestrator: { process: jest.fn() }, permissionsService, auditor: mockServices.auditor.mock(), }); @@ -1171,6 +1172,7 @@ describe('NextRouter permissioning', () => { }), auth: mockServices.auth(), httpAuth: mockServices.httpAuth(), + orchestrator: { process: jest.fn() }, permissionsService, auditor: mockServices.auditor.mock(), }); diff --git a/plugins/catalog-backend/src/service/createRouter.ts b/plugins/catalog-backend/src/service/createRouter.ts index 9c9b97eb4c..dc7678a3b9 100644 --- a/plugins/catalog-backend/src/service/createRouter.ts +++ b/plugins/catalog-backend/src/service/createRouter.ts @@ -20,7 +20,6 @@ import { HttpAuthService, LoggerService, PermissionsService, - SchedulerService, } from '@backstage/backend-plugin-api'; import { ANNOTATION_LOCATION, @@ -70,17 +69,15 @@ export interface RouterOptions { entitiesCatalog?: EntitiesCatalog; locationAnalyzer?: LocationAnalyzer; locationService: LocationService; - orchestrator?: CatalogProcessingOrchestrator; + orchestrator: CatalogProcessingOrchestrator; refreshService?: RefreshService; - scheduler?: SchedulerService; logger: LoggerService; config: Config; permissionIntegrationRouter?: express.Router; auth: AuthService; httpAuth: HttpAuthService; permissionsService: PermissionsService; - // TODO: Require AuditorService once `backend-legacy` is removed - auditor?: AuditorService; + auditor: AuditorService; enableRelationsCompatibility?: boolean; } @@ -124,7 +121,7 @@ export async function createRouter( router.post('/refresh', async (req, res) => { const { authorizationToken, ...restBody } = req.body; - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-mutate', severityLevel: 'medium', meta: { @@ -160,7 +157,7 @@ export async function createRouter( if (entitiesCatalog) { router .get('/entities', async (req, res) => { - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', request: req, meta: { @@ -258,7 +255,7 @@ export async function createRouter( } }) .get('/entities/by-query', async (req, res) => { - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', request: req, meta: { @@ -311,7 +308,7 @@ export async function createRouter( .get('/entities/by-uid/:uid', async (req, res) => { const { uid } = req.params; - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', request: req, meta: { @@ -356,7 +353,7 @@ export async function createRouter( .delete('/entities/by-uid/:uid', async (req, res) => { const { uid } = req.params; - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-mutate', severityLevel: 'medium', request: req, @@ -385,7 +382,7 @@ export async function createRouter( const { kind, namespace, name } = req.params; const entityRef = stringifyEntityRef({ kind, namespace, name }); - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', request: req, meta: { @@ -420,7 +417,7 @@ export async function createRouter( const { kind, namespace, name } = req.params; const entityRef = stringifyEntityRef({ kind, namespace, name }); - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', request: req, meta: { @@ -456,7 +453,7 @@ export async function createRouter( }, ) .post('/entities/by-refs', async (req, res) => { - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-fetch', request: req, meta: { @@ -495,7 +492,7 @@ export async function createRouter( } }) .get('/entity-facets', async (req, res) => { - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'entity-facets', request: req, }); @@ -525,7 +522,7 @@ export async function createRouter( const location = await validateRequestBody(req, locationInput); const dryRun = yn(req.query.dryRun, { default: false }); - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'location-mutate', severityLevel: dryRun ? 'low' : 'medium', request: req, @@ -570,7 +567,7 @@ export async function createRouter( } }) .get('/locations', async (req, res) => { - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'location-fetch', request: req, meta: { @@ -597,7 +594,7 @@ export async function createRouter( .get('/locations/:id', async (req, res) => { const { id } = req.params; - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'location-fetch', request: req, meta: { @@ -628,7 +625,7 @@ export async function createRouter( .delete('/locations/:id', async (req, res) => { const { id } = req.params; - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'location-mutate', severityLevel: 'medium', request: req, @@ -659,7 +656,7 @@ export async function createRouter( const { kind, namespace, name } = req.params; const locationRef = `${kind}:${namespace}/${name}`; - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'location-fetch', request: req, meta: { @@ -692,7 +689,7 @@ export async function createRouter( if (locationAnalyzer) { router.post('/analyze-location', async (req, res) => { - const auditorEvent = await auditor?.createEvent({ + const auditorEvent = await auditor.createEvent({ eventId: 'location-analyze', request: req, }); @@ -743,86 +740,84 @@ export async function createRouter( }); } - if (orchestrator) { - router.post('/validate-entity', async (req, res) => { - const auditorEvent = await auditor?.createEvent({ - eventId: 'entity-validate', - request: req, + router.post('/validate-entity', async (req, res) => { + const auditorEvent = await auditor.createEvent({ + eventId: 'entity-validate', + request: req, + }); + + try { + const bodySchema = z.object({ + entity: z.unknown(), + location: z.string(), }); + let body: z.infer; + let entity: Entity; + let location: { type: string; target: string }; try { - const bodySchema = z.object({ - entity: z.unknown(), - location: z.string(), - }); - - let body: z.infer; - let entity: Entity; - let location: { type: string; target: string }; - try { - body = await validateRequestBody(req, bodySchema); - entity = validateEntityEnvelope(body.entity); - location = parseLocationRef(body.location); - if (location.type !== 'url') - throw new TypeError( - `Invalid location ref ${body.location}, only 'url:' is supported, e.g. url:https://host/path`, - ); - } catch (err) { - await auditorEvent?.fail({ - error: err, - }); - - return res.status(400).json({ - errors: [serializeError(err)], - }); - } - - const credentials = await httpAuth.credentials(req); - const authorizedValidationService = new AuthorizedValidationService( - orchestrator, - permissionsService, - ); - const processingResult = await authorizedValidationService.process( - { - entity: { - ...entity, - metadata: { - ...entity.metadata, - annotations: { - [ANNOTATION_LOCATION]: body.location, - [ANNOTATION_ORIGIN_LOCATION]: body.location, - ...entity.metadata.annotations, - }, - }, - }, - }, - credentials, - ); - - if (!processingResult.ok) { - const errors = processingResult.errors.map(e => serializeError(e)); - - await auditorEvent?.fail({ - // TODO(Rugvip): Seems like there aren't proper types for AggregateError yet - error: (AggregateError as any)(errors, 'Could not validate entity'), - }); - - res.status(400).json({ - errors, - }); - } - - await auditorEvent?.success(); - - return res.status(200).end(); + body = await validateRequestBody(req, bodySchema); + entity = validateEntityEnvelope(body.entity); + location = parseLocationRef(body.location); + if (location.type !== 'url') + throw new TypeError( + `Invalid location ref ${body.location}, only 'url:' is supported, e.g. url:https://host/path`, + ); } catch (err) { await auditorEvent?.fail({ error: err, }); - throw err; + + return res.status(400).json({ + errors: [serializeError(err)], + }); } - }); - } + + const credentials = await httpAuth.credentials(req); + const authorizedValidationService = new AuthorizedValidationService( + orchestrator, + permissionsService, + ); + const processingResult = await authorizedValidationService.process( + { + entity: { + ...entity, + metadata: { + ...entity.metadata, + annotations: { + [ANNOTATION_LOCATION]: body.location, + [ANNOTATION_ORIGIN_LOCATION]: body.location, + ...entity.metadata.annotations, + }, + }, + }, + }, + credentials, + ); + + if (!processingResult.ok) { + const errors = processingResult.errors.map(e => serializeError(e)); + + await auditorEvent?.fail({ + // TODO(Rugvip): Seems like there aren't proper types for AggregateError yet + error: (AggregateError as any)(errors, 'Could not validate entity'), + }); + + res.status(400).json({ + errors, + }); + } + + await auditorEvent?.success(); + + return res.status(200).end(); + } catch (err) { + await auditorEvent?.fail({ + error: err, + }); + throw err; + } + }); return router; } diff --git a/plugins/catalog-backend/src/tests/integration.test.ts b/plugins/catalog-backend/src/tests/integration.test.ts index 719c5e6ccf..486d1bfb99 100644 --- a/plugins/catalog-backend/src/tests/integration.test.ts +++ b/plugins/catalog-backend/src/tests/integration.test.ts @@ -296,6 +296,7 @@ class TestHarness { knex: options.db, orchestrator, stitcher, + scheduler: mockServices.scheduler(), createHash: () => createHash('sha1'), pollingIntervalMs: 50, onProcessingError: event => { From a919ca34b50bc4213dfed70062e87448f9340809 Mon Sep 17 00:00:00 2001 From: Stanislav Cherkasov <150145013+stanislav-c@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:31:19 +0200 Subject: [PATCH 066/211] feat: truncate document text to fit PostgreSQL index size limit (#30943) Signed-off-by: Stanislav C <150145013+stanislav-c@users.noreply.github.com> --- .changeset/brown-falcons-own.md | 5 +++ .../search-backend-module-pg/report.api.md | 12 +++++- .../PgSearchEngineIndexer.test.ts | 43 ++++++++++++++++++- .../PgSearchEngine/PgSearchEngineIndexer.ts | 37 +++++++++++----- .../database/DatabaseDocumentStore.test.ts | 27 ++++++++++++ .../src/database/DatabaseDocumentStore.ts | 19 ++++++-- .../src/database/types.ts | 6 ++- 7 files changed, 131 insertions(+), 18 deletions(-) create mode 100644 .changeset/brown-falcons-own.md diff --git a/.changeset/brown-falcons-own.md b/.changeset/brown-falcons-own.md new file mode 100644 index 0000000000..e38f3bbfbc --- /dev/null +++ b/.changeset/brown-falcons-own.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-pg': patch +--- + +Truncate long docs to fit PG index size limit diff --git a/plugins/search-backend-module-pg/report.api.md b/plugins/search-backend-module-pg/report.api.md index ff2129b4bb..67e60b7822 100644 --- a/plugins/search-backend-module-pg/report.api.md +++ b/plugins/search-backend-module-pg/report.api.md @@ -24,7 +24,11 @@ export type ConcretePgSearchQuery = { export class DatabaseDocumentStore implements DatabaseStore { constructor(db: Knex); // (undocumented) - completeInsert(tx: Knex.Transaction, type: string): Promise; + completeInsert( + tx: Knex.Transaction, + type: string, + truncate?: boolean, + ): Promise; // (undocumented) static create(database: DatabaseService): Promise; // (undocumented) @@ -51,7 +55,11 @@ export class DatabaseDocumentStore implements DatabaseStore { // @public (undocumented) export interface DatabaseStore { // (undocumented) - completeInsert(tx: Knex.Transaction, type: string): Promise; + completeInsert( + tx: Knex.Transaction, + type: string, + truncate?: boolean, + ): Promise; // (undocumented) getTransaction(): Promise; // (undocumented) diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.test.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.test.ts index 37af4f00f0..7554cebfd7 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.test.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.test.ts @@ -65,11 +65,50 @@ describe('PgSearchEngineIndexer', () => { 'my-type', documents, ); - expect(database.completeInsert).toHaveBeenCalledWith(tx, 'my-type'); + expect(database.completeInsert).toHaveBeenCalledWith(tx, 'my-type', false); expect(tx.commit).toHaveBeenCalled(); expect(tx.rollback).not.toHaveBeenCalled(); }); + it('should insert documents that are too long for tsvector', async () => { + const documents = [ + { title: 'Hello World', text: 'Lorem Ipsum', location: 'location-1' }, + { + location: 'location-2', + text: 'Hello World', + title: 'Dolor sit amet', + }, + ]; + + const tsvectorError = new Error('string is too long for tsvector'); + database.completeInsert.mockRejectedValueOnce(tsvectorError); + + await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute(); + + expect(database.getTransaction).toHaveBeenCalledTimes(1); + expect(database.prepareInsert).toHaveBeenCalledTimes(1); + expect(database.insertDocuments).toHaveBeenCalledWith( + tx, + 'my-type', + documents, + ); + expect(database.completeInsert).toHaveBeenCalledTimes(2); + expect(database.completeInsert).toHaveBeenNthCalledWith( + 1, + tx, + 'my-type', + false, + ); + expect(database.completeInsert).toHaveBeenNthCalledWith( + 2, + tx, + 'my-type', + true, + ); + expect(tx.commit).toHaveBeenCalled(); + expect(tx.rollback).toHaveBeenCalledTimes(1); + }); + it('should batch insert documents', async () => { const documents = range(350).map(i => ({ title: `Hello World ${i}`, @@ -82,7 +121,7 @@ describe('PgSearchEngineIndexer', () => { expect(database.getTransaction).toHaveBeenCalledTimes(1); expect(database.prepareInsert).toHaveBeenCalledTimes(1); expect(database.insertDocuments).toHaveBeenCalledTimes(4); - expect(database.completeInsert).toHaveBeenCalledWith(tx, 'my-type'); + expect(database.completeInsert).toHaveBeenCalledWith(tx, 'my-type', false); }); it('should rollback transaction if no documents indexed', async () => { diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts index 2e1997c42c..ad8c4f093f 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngineIndexer.ts @@ -85,16 +85,33 @@ export class PgSearchEngineIndexer extends BatchSearchEngineIndexer { return; } - // Attempt to complete and commit the transaction. - try { - await this.store.completeInsert(this.tx!, this.type); - this.tx!.commit(); - } catch (e) { - // Otherwise, rollback the transaction and re-throw the error so that the - // stream can be closed and destroyed properly. - this.tx!.rollback!(e); - throw e; - } + // Do not truncate text by default + let retryTruncated = false; + + do { + // Attempt to complete and commit the transaction. + try { + await this.store.completeInsert(this.tx!, this.type, retryTruncated); + this.tx!.commit(); + retryTruncated = false; + } catch (e) { + // Otherwise, rollback the transaction and re-throw the error so that the + // stream can be closed and destroyed properly. + this.tx!.rollback!(e); + + if ( + e instanceof Error && + e.message.includes('string is too long for tsvector') && + !retryTruncated + ) { + // If the error is due to a string being too long for tsvector, we + // retry the operation with truncated text. + retryTruncated = true; + } else { + throw e; + } + } + } while (retryTruncated); } /** diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts index 0029b67bd6..f25351b3a5 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts @@ -22,6 +22,7 @@ import { import { IndexableDocument } from '@backstage/plugin-search-common'; import { PgSearchHighlightOptions } from '../PgSearchEngine'; import { DatabaseDocumentStore } from './DatabaseDocumentStore'; +import { v4 as uuidv4 } from 'uuid'; const highlightOptions: PgSearchHighlightOptions = { preTag: '', @@ -122,6 +123,32 @@ describe('DatabaseDocumentStore', () => { }, ); + it.each(databases.eachSupportedId())( + 'should insert truncated documents, %p', + async databaseId => { + const { store, knex } = await createStore(databaseId); + + await store.transaction(async tx => { + await store.prepareInsert(tx); + + await store.insertDocuments(tx, 'my-type', [ + { + title: 'TITLE 1', + text: Array.from({ length: 100000 }) + .map(() => uuidv4()) // text tokens should be unique to overflow the tsvector indexing and trigger truncation + .join(' '), + location: 'LOCATION-1', + }, + ]); + await store.completeInsert(tx, 'my-type', true); + }); + + expect( + await knex.count('*').where('type', 'my-type').from('documents'), + ).toEqual([{ count: '1' }]); + }, + ); + it.each(databases.eachSupportedId())( 'should insert documents in batches, %p', async databaseId => { diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts index 8bff8c0a60..0c0e838f0d 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts @@ -102,13 +102,23 @@ export class DatabaseDocumentStore implements DatabaseStore { ); } - async completeInsert(tx: Knex.Transaction, type: string): Promise { + async completeInsert( + tx: Knex.Transaction, + type: string, + truncate = false, + ): Promise { + const documentSelect = truncate + ? tx.raw( + `jsonb_set(document, '{text}', to_jsonb(LEFT(document->>'text', 50000))) as document`, + ) + : 'document'; + // Copy all new rows into the documents table await tx .insert( tx('documents_to_insert').select( 'type', - 'document', + documentSelect, 'hash', ), ) @@ -139,7 +149,10 @@ export class DatabaseDocumentStore implements DatabaseStore { await tx('documents_to_insert').insert( documents.map(document => ({ type, - document, + document: { + ...document, + // text: truncateDocsText(document.text), + }, })), ); } diff --git a/plugins/search-backend-module-pg/src/database/types.ts b/plugins/search-backend-module-pg/src/database/types.ts index 5093a170ac..f8529fbebe 100644 --- a/plugins/search-backend-module-pg/src/database/types.ts +++ b/plugins/search-backend-module-pg/src/database/types.ts @@ -38,7 +38,11 @@ export interface DatabaseStore { type: string, documents: IndexableDocument[], ): Promise; - completeInsert(tx: Knex.Transaction, type: string): Promise; + completeInsert( + tx: Knex.Transaction, + type: string, + truncate?: boolean, + ): Promise; query( tx: Knex.Transaction, pgQuery: PgSearchQuery, From 498332ae5348bcabdbb63f0e4dccf565466e82fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 22 Sep 2025 16:45:41 +0200 Subject: [PATCH 067/211] remove more unused things MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../DefaultProcessingDatabase.test.ts | 1 + .../src/database/DefaultProcessingDatabase.ts | 12 ++--- .../DefaultCatalogProcessingEngine.test.ts | 9 ++++ .../DefaultCatalogProcessingEngine.ts | 10 ++-- ...faultCatalogProcessingOrchestrator.test.ts | 9 +--- .../DefaultCatalogProcessingOrchestrator.ts | 4 -- .../src/service/CatalogBuilder.ts | 53 ++----------------- .../src/service/CatalogPlugin.ts | 3 +- .../src/service/DefaultRefreshService.test.ts | 2 + .../src/tests/integration.test.ts | 3 +- .../getProcessableEntitiesPerformance.test.ts | 1 + 11 files changed, 32 insertions(+), 75 deletions(-) diff --git a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts index a09e938575..790762a428 100644 --- a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts @@ -58,6 +58,7 @@ describe('DefaultProcessingDatabase', () => { minSeconds: 100, maxSeconds: 150, }), + events: mockServices.events.mock(), }), }; } diff --git a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts index 81c26c66ab..50c3cbb1c8 100644 --- a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts @@ -42,11 +42,7 @@ import { checkLocationKeyConflict } from './operations/refreshState/checkLocatio import { insertUnprocessedEntity } from './operations/refreshState/insertUnprocessedEntity'; import { updateUnprocessedEntity } from './operations/refreshState/updateUnprocessedEntity'; import { generateStableHash, generateTargetKey } from './util'; -import { - EventBroker, - EventParams, - EventsService, -} from '@backstage/plugin-events-node'; +import { EventParams, EventsService } from '@backstage/plugin-events-node'; import { DateTime } from 'luxon'; import { CATALOG_CONFLICTS_TOPIC } from '../constants'; import { CatalogConflictEventPayload } from '../catalog/types'; @@ -64,7 +60,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase { database: Knex; logger: LoggerService; refreshInterval: ProcessingIntervalFunction; - eventBroker?: EventBroker | EventsService; + events: EventsService; }, ) { initDatabaseMetrics(options.database); @@ -367,7 +363,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase { this.options.logger.warn( `Detected conflicting entityRef ${entityRef} already referenced by ${conflictingKey} and now also ${locationKey}`, ); - if (this.options.eventBroker && locationKey) { + if (locationKey) { const eventParams: EventParams = { topic: CATALOG_CONFLICTS_TOPIC, eventPayload: { @@ -378,7 +374,7 @@ export class DefaultProcessingDatabase implements ProcessingDatabase { lastConflictAt: DateTime.now().toISO()!, }, }; - await this.options.eventBroker?.publish(eventParams); + await this.options.events.publish(eventParams); } } } diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts index 93c22aab1e..5e6312ec4a 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts @@ -71,6 +71,7 @@ describe('DefaultCatalogProcessingEngine', () => { stitcher: stitcher, createHash: () => hash, scheduler: mockServices.scheduler(), + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -139,6 +140,7 @@ describe('DefaultCatalogProcessingEngine', () => { stitcher: stitcher, scheduler: mockServices.scheduler(), createHash: () => hash, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -223,6 +225,7 @@ describe('DefaultCatalogProcessingEngine', () => { stitcher: stitcher, scheduler: mockServices.scheduler(), createHash: () => hash, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -301,6 +304,7 @@ describe('DefaultCatalogProcessingEngine', () => { stitcher: stitcher, scheduler: mockServices.scheduler(), createHash: () => hash, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -362,6 +366,7 @@ describe('DefaultCatalogProcessingEngine', () => { scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -478,6 +483,7 @@ describe('DefaultCatalogProcessingEngine', () => { scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -584,6 +590,7 @@ describe('DefaultCatalogProcessingEngine', () => { scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -668,6 +675,7 @@ describe('DefaultCatalogProcessingEngine', () => { scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); @@ -757,6 +765,7 @@ describe('DefaultCatalogProcessingEngine', () => { scheduler: mockServices.scheduler(), createHash: () => hash, pollingIntervalMs: 100, + events: mockServices.events.mock(), }); db.transaction.mockImplementation(cb => cb((() => {}) as any)); diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts index 7061309175..b2a562ea6a 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts @@ -36,7 +36,7 @@ import { withActiveSpan, } from '../util/opentelemetry'; import { deleteOrphanedEntities } from '../database/operations/util/deleteOrphanedEntities'; -import { EventBroker, EventsService } from '@backstage/plugin-events-node'; +import { EventsService } from '@backstage/plugin-events-node'; import { CATALOG_ERRORS_TOPIC } from '../constants'; import { LoggerService, SchedulerService } from '@backstage/backend-plugin-api'; @@ -73,7 +73,7 @@ export class DefaultCatalogProcessingEngine { errors: Error[]; }) => Promise | void; private readonly tracker: ProgressTracker; - private readonly eventBroker?: EventBroker | EventsService; + private readonly events: EventsService; private stopFunc?: () => void; @@ -93,7 +93,7 @@ export class DefaultCatalogProcessingEngine { errors: Error[]; }) => Promise | void; tracker?: ProgressTracker; - eventBroker?: EventBroker | EventsService; + events: EventsService; }) { this.config = options.config; this.scheduler = options.scheduler; @@ -107,7 +107,7 @@ export class DefaultCatalogProcessingEngine { this.orphanCleanupIntervalMs = options.orphanCleanupIntervalMs ?? 30_000; this.onProcessingError = options.onProcessingError; this.tracker = options.tracker ?? progressTracker(); - this.eventBroker = options.eventBroker; + this.events = options.events; this.stopFunc = undefined; } @@ -201,7 +201,7 @@ export class DefaultCatalogProcessingEngine { const location = unprocessedEntity?.metadata?.annotations?.[ANNOTATION_LOCATION]; if (result.errors.length) { - this.eventBroker?.publish({ + this.events.publish({ topic: CATALOG_ERRORS_TOPIC, eventPayload: { entity: entityRef, diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts index 31d7c34b94..beb3611206 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts @@ -97,7 +97,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => { parser: defaultEntityDataParser, policy: EntityPolicies.allOf([]), rulesEnforcer: { isAllowed: () => true }, - legacySingleProcessorValidation: false, }); it('runs a minimal processing', async () => { @@ -192,7 +191,7 @@ describe('DefaultCatalogProcessingOrchestrator', () => { }); }); - it('runs all processor validations when asked to', async () => { + it('runs all processor validations', async () => { const validate = jest.fn(async () => true); const processor1: CatalogProcessor = { getProcessorName: () => 'processor1', @@ -213,7 +212,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => { parser: defaultEntityDataParser, policy: EntityPolicies.allOf([]), rulesEnforcer: { isAllowed: () => true }, - legacySingleProcessorValidation: true, }); const modern = new DefaultCatalogProcessingOrchestrator({ @@ -226,13 +224,12 @@ describe('DefaultCatalogProcessingOrchestrator', () => { parser: defaultEntityDataParser, policy: EntityPolicies.allOf([]), rulesEnforcer: { isAllowed: () => true }, - legacySingleProcessorValidation: false, }); await expect(legacy.process({ entity })).resolves.toMatchObject({ ok: true, }); - expect(validate).toHaveBeenCalledTimes(1); + expect(validate).toHaveBeenCalledTimes(2); validate.mockClear(); @@ -291,7 +288,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => { parser, policy: EntityPolicies.allOf([]), rulesEnforcer, - legacySingleProcessorValidation: false, }); rulesEnforcer.isAllowed.mockReturnValueOnce(true); @@ -333,7 +329,6 @@ describe('DefaultCatalogProcessingOrchestrator', () => { parser, policy: EntityPolicies.allOf([new FailingEntityPolicy()]), rulesEnforcer, - legacySingleProcessorValidation: false, }); await expect( diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index b1fcba5d40..79873264a3 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -95,7 +95,6 @@ export class DefaultCatalogProcessingOrchestrator parser: CatalogProcessorParser; policy: EntityPolicy; rulesEnforcer: CatalogRulesEnforcer; - legacySingleProcessorValidation: boolean; }, ) {} @@ -309,9 +308,6 @@ export class DefaultCatalogProcessingOrchestrator ); if (thisValid) { valid = true; - if (this.options.legacySingleProcessorValidation) { - break; - } } } catch (e) { throw new InputError( diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index 40fcc3a624..0b35e84102 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -55,7 +55,7 @@ import { PlaceholderResolver, ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; -import { EventBroker, EventsService } from '@backstage/plugin-events-node'; +import { EventsService } from '@backstage/plugin-events-node'; import { Permission, PermissionAuthorizer, @@ -123,6 +123,7 @@ export type CatalogEnvironment = { auth: AuthService; httpAuth: HttpAuthService; auditor: AuditorService; + events: EventsService; }; /** @@ -168,8 +169,6 @@ export class CatalogBuilder { private readonly permissions: Permission[]; private readonly permissionRules: CatalogPermissionRuleInput[]; private allowedLocationType: string[]; - private legacySingleProcessorValidation = false; - private eventBroker?: EventBroker | EventsService; /** * Creates a catalog builder. @@ -216,31 +215,6 @@ export class CatalogBuilder { return this; } - /** - * Processing interval determines how often entities should be processed. - * Seconds provided will be multiplied by 1.5 - * The default processing interval is 100-150 seconds. - * setting this too low will potentially deplete request quotas to upstream services. - */ - setProcessingIntervalSeconds(seconds: number): CatalogBuilder { - this.processingInterval = createRandomProcessingInterval({ - minSeconds: seconds, - maxSeconds: seconds * 1.5, - }); - return this; - } - - /** - * Overwrites the default processing interval function used to spread - * entity updates in the catalog. - */ - setProcessingInterval( - processingInterval: ProcessingIntervalFunction, - ): CatalogBuilder { - this.processingInterval = processingInterval; - return this; - } - /** * Overwrites the default location analyzer. */ @@ -427,23 +401,6 @@ export class CatalogBuilder { return this; } - /** - * Enables the legacy behaviour of canceling validation early whenever only a - * single processor declares an entity kind to be valid. - */ - useLegacySingleProcessorValidation(): this { - this.legacySingleProcessorValidation = true; - return this; - } - - /** - * Enables the publishing of events for conflicts in the DefaultProcessingDatabase - */ - setEventBroker(broker: EventBroker | EventsService): CatalogBuilder { - this.eventBroker = broker; - return this; - } - /** * Wires up and returns all of the component parts of the catalog */ @@ -461,6 +418,7 @@ export class CatalogBuilder { auditor, auth, httpAuth, + events, } = this.env; const enableRelationsCompatibility = Boolean( @@ -485,8 +443,8 @@ export class CatalogBuilder { const processingDatabase = new DefaultProcessingDatabase({ database: dbClient, logger, + events, refreshInterval: this.processingInterval, - eventBroker: this.eventBroker, }); const providerDatabase = new DefaultProviderDatabase({ database: dbClient, @@ -523,7 +481,6 @@ export class CatalogBuilder { logger, parser, policy, - legacySingleProcessorValidation: this.legacySingleProcessorValidation, }); const entitiesCatalog = new AuthorizedEntitiesCatalog( @@ -588,7 +545,7 @@ export class CatalogBuilder { onProcessingError: event => { this.onProcessingError?.(event); }, - eventBroker: this.eventBroker, + events, }); const locationAnalyzer = diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index a9bb2c9285..24147e3cc5 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -271,10 +271,9 @@ export const catalogPlugin = createBackendPlugin({ auth, httpAuth, auditor, + events, }); - builder.setEventBroker(events); - if (processingExtensions.onProcessingErrorHandler) { builder.subscribe({ onProcessingError: processingExtensions.onProcessingErrorHandler, diff --git a/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts b/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts index f999400b8a..29519e9e6b 100644 --- a/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts @@ -57,6 +57,7 @@ describe('DefaultRefreshService', () => { database: knex, logger, refreshInterval: () => 100, + events: mockServices.events.mock(), }), catalogDb: new DefaultCatalogDatabase({ database: knex, @@ -162,6 +163,7 @@ describe('DefaultRefreshService', () => { }, createHash: () => createHash('sha1'), pollingIntervalMs: 50, + events: mockServices.events.mock(), }); return engine; diff --git a/plugins/catalog-backend/src/tests/integration.test.ts b/plugins/catalog-backend/src/tests/integration.test.ts index 486d1bfb99..39871583af 100644 --- a/plugins/catalog-backend/src/tests/integration.test.ts +++ b/plugins/catalog-backend/src/tests/integration.test.ts @@ -244,6 +244,7 @@ class TestHarness { const processingDatabase = new DefaultProcessingDatabase({ database: options.db, logger, + events: mockServices.events.mock(), refreshInterval: () => 0.05, }); @@ -273,7 +274,6 @@ class TestHarness { logger, parser: defaultEntityDataParser, policy: EntityPolicies.allOf([]), - legacySingleProcessorValidation: false, }); const stitcher = DefaultStitcher.fromConfig(config, { knex: options.db, @@ -303,6 +303,7 @@ class TestHarness { proxyProgressTracker.reportError(event.unprocessedEntity, event.errors); }, tracker: proxyProgressTracker, + events: mockServices.events.mock(), }); const refresh = new DefaultRefreshService({ database: catalogDatabase }); diff --git a/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts index e2ed160b42..a1cc1d3238 100644 --- a/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/getProcessableEntitiesPerformance.test.ts @@ -79,6 +79,7 @@ describePerformanceTest('getProcessableEntities', () => { const sut = new DefaultProcessingDatabase({ database: knex, logger: mockServices.logger.mock(), + events: mockServices.events.mock(), refreshInterval: () => 0, }); From 35718b2c10b30eb768610a726de59174a122c461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 22 Sep 2025 20:03:47 +0200 Subject: [PATCH 068/211] Update .changeset/tame-hairs-smash.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/tame-hairs-smash.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/tame-hairs-smash.md b/.changeset/tame-hairs-smash.md index d74b2ffeb7..db05708132 100644 --- a/.changeset/tame-hairs-smash.md +++ b/.changeset/tame-hairs-smash.md @@ -3,4 +3,4 @@ '@backstage/plugin-bitbucket-cloud-common': patch --- -Allow for passing a pagelen parameter to configure the pagelength property of the BitbucketCloudEntityProvider searchCode pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. +Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. From f5e09631a43afd659c30b318e1bef9a03bc18bd8 Mon Sep 17 00:00:00 2001 From: Hope Hadfield Date: Thu, 18 Sep 2025 11:15:16 -0400 Subject: [PATCH 069/211] Remove unused dependencies in notifications and scaffolder Signed-off-by: Hope Hadfield --- .changeset/shiny-candles-hide.md | 9 +++++++++ .../knip-report.md | 5 ----- .../package.json | 1 - plugins/notifications-backend/knip-report.md | 14 -------------- plugins/notifications-backend/package.json | 7 +------ plugins/notifications/knip-report.md | 13 ------------- plugins/notifications/package.json | 4 ---- .../knip-report.md | 5 ----- .../package.json | 3 +-- .../knip-report.md | 5 ----- .../scaffolder-backend-module-gitlab/package.json | 1 - yarn.lock | 12 ------------ 12 files changed, 11 insertions(+), 68 deletions(-) create mode 100644 .changeset/shiny-candles-hide.md diff --git a/.changeset/shiny-candles-hide.md b/.changeset/shiny-candles-hide.md new file mode 100644 index 0000000000..7ec4a8062e --- /dev/null +++ b/.changeset/shiny-candles-hide.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch +'@backstage/plugin-notifications-backend-module-email': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-notifications-backend': patch +'@backstage/plugin-notifications': patch +--- + +Removed unused dependencies diff --git a/plugins/notifications-backend-module-email/knip-report.md b/plugins/notifications-backend-module-email/knip-report.md index a36d75cae4..97d5b385fd 100644 --- a/plugins/notifications-backend-module-email/knip-report.md +++ b/plugins/notifications-backend-module-email/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused dependencies (1) - -| Name | Location | Severity | -| :------------- | :----------- | :------- | -| @aws-sdk/types | plugins/notifications-backend-module-email/package.json | error | diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index ac8682fe2d..37b0f4d64d 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -35,7 +35,6 @@ }, "dependencies": { "@aws-sdk/client-ses": "^3.550.0", - "@aws-sdk/types": "^3.347.0", "@azure/communication-email": "^1.0.0", "@azure/identity": "^4.0.0", "@backstage/backend-plugin-api": "workspace:^", diff --git a/plugins/notifications-backend/knip-report.md b/plugins/notifications-backend/knip-report.md index 3514fea30a..97d5b385fd 100644 --- a/plugins/notifications-backend/knip-report.md +++ b/plugins/notifications-backend/knip-report.md @@ -1,17 +1,3 @@ # Knip report -## Unused dependencies (4) - -| Name | Location | Severity | -| :---------------------------- | :----------- | :------- | -| @backstage/plugin-events-node | plugins/notifications-backend/package.json | error | -| @backstage/plugin-auth-node | plugins/notifications-backend/package.json | error | -| winston | plugins/notifications-backend/package.json | error | -| yn | plugins/notifications-backend/package.json | error | - -## Unused devDependencies (1) - -| Name | Location | Severity | -| :-- | :----------- | :------- | -| msw | plugins/notifications-backend/package.json | error | diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index 1a2c746409..975e2fec0a 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -43,9 +43,7 @@ "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", - "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", - "@backstage/plugin-events-node": "workspace:^", "@backstage/plugin-notifications-common": "workspace:^", "@backstage/plugin-notifications-node": "workspace:^", "@backstage/plugin-signals-node": "workspace:^", @@ -54,9 +52,7 @@ "express-promise-router": "^4.1.0", "knex": "^3.0.0", "p-throttle": "^4.1.1", - "uuid": "^11.0.0", - "winston": "^3.2.1", - "yn": "^4.0.0" + "uuid": "^11.0.0" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", @@ -68,7 +64,6 @@ "@backstage/plugin-signals-backend": "workspace:^", "@types/express": "^4.17.6", "@types/supertest": "^2.0.8", - "msw": "^1.0.0", "supertest": "^7.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/notifications/knip-report.md b/plugins/notifications/knip-report.md index 6cd21cf3b8..97d5b385fd 100644 --- a/plugins/notifications/knip-report.md +++ b/plugins/notifications/knip-report.md @@ -1,16 +1,3 @@ # Knip report -## Unused dependencies (2) - -| Name | Location | Severity | -| :--------------- | :----------- | :------- | -| @backstage/types | plugins/notifications/package.json | error | -| @material-ui/lab | plugins/notifications/package.json | error | - -## Unused devDependencies (2) - -| Name | Location | Severity | -| :-------------------------- | :----------- | :------- | -| @testing-library/user-event | plugins/notifications/package.json | error | -| @backstage/core-app-api | plugins/notifications/package.json | error | diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index e6d66a5ec7..cbc06d242e 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -59,10 +59,8 @@ "@backstage/plugin-notifications-common": "workspace:^", "@backstage/plugin-signals-react": "workspace:^", "@backstage/theme": "workspace:^", - "@backstage/types": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", - "@material-ui/lab": "^4.0.0-alpha.61", "lodash": "^4.17.21", "material-ui-confirm": "^3.0.12", "notistack": "^3.0.1", @@ -71,13 +69,11 @@ }, "devDependencies": { "@backstage/cli": "workspace:^", - "@backstage/core-app-api": "workspace:^", "@backstage/dev-utils": "workspace:^", "@backstage/plugin-signals": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^16.0.0", - "@testing-library/user-event": "^14.0.0", "@types/react": "^18.0.0", "msw": "^1.0.0", "react": "^18.0.2", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/knip-report.md b/plugins/scaffolder-backend-module-bitbucket-server/knip-report.md index 0dd672d03d..97d5b385fd 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/knip-report.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused dependencies (1) - -| Name | Location | Severity | -| :-- | :----------- | :------- | -| zod | plugins/scaffolder-backend-module-bitbucket-server/package.json | error | diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 452653afb7..c980146970 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -48,8 +48,7 @@ "@backstage/integration": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "fs-extra": "^11.2.0", - "yaml": "^2.0.0", - "zod": "^3.22.4" + "yaml": "^2.0.0" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/scaffolder-backend-module-gitlab/knip-report.md b/plugins/scaffolder-backend-module-gitlab/knip-report.md index 3e1fc873d4..97d5b385fd 100644 --- a/plugins/scaffolder-backend-module-gitlab/knip-report.md +++ b/plugins/scaffolder-backend-module-gitlab/knip-report.md @@ -1,8 +1,3 @@ # Knip report -## Unused dependencies (1) - -| Name | Location | Severity | -| :------ | :----------- | :------- | -| winston | plugins/scaffolder-backend-module-gitlab/package.json | error | diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index f8194a434c..c1fb8024e9 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -53,7 +53,6 @@ "@gitbeaker/requester-utils": "^41.2.0", "@gitbeaker/rest": "^41.2.0", "luxon": "^3.0.0", - "winston": "^3.2.1", "yaml": "^2.0.0", "zod": "^3.22.4" }, diff --git a/yarn.lock b/yarn.lock index 483bd8e5b8..d0948f8474 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5715,7 +5715,6 @@ __metadata: resolution: "@backstage/plugin-notifications-backend-module-email@workspace:plugins/notifications-backend-module-email" dependencies: "@aws-sdk/client-ses": "npm:^3.550.0" - "@aws-sdk/types": "npm:^3.347.0" "@azure/communication-email": "npm:^1.0.0" "@azure/identity": "npm:^4.0.0" "@backstage/backend-plugin-api": "workspace:^" @@ -5775,10 +5774,8 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-backend-module-guest-provider": "workspace:^" - "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-backend": "workspace:^" - "@backstage/plugin-events-node": "workspace:^" "@backstage/plugin-notifications-common": "workspace:^" "@backstage/plugin-notifications-node": "workspace:^" "@backstage/plugin-signals-backend": "workspace:^" @@ -5789,12 +5786,9 @@ __metadata: express: "npm:^4.17.1" express-promise-router: "npm:^4.1.0" knex: "npm:^3.0.0" - msw: "npm:^1.0.0" p-throttle: "npm:^4.1.1" supertest: "npm:^7.0.0" uuid: "npm:^11.0.0" - winston: "npm:^3.2.1" - yn: "npm:^4.0.0" languageName: unknown linkType: soft @@ -5832,7 +5826,6 @@ __metadata: resolution: "@backstage/plugin-notifications@workspace:plugins/notifications" dependencies: "@backstage/cli": "workspace:^" - "@backstage/core-app-api": "workspace:^" "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" @@ -5844,13 +5837,10 @@ __metadata: "@backstage/plugin-signals-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" - "@backstage/types": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" - "@material-ui/lab": "npm:^4.0.0-alpha.61" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" - "@testing-library/user-event": "npm:^14.0.0" "@types/react": "npm:^18.0.0" lodash: "npm:^4.17.21" material-ui-confirm: "npm:^3.0.12" @@ -6147,7 +6137,6 @@ __metadata: fs-extra: "npm:^11.2.0" msw: "npm:^1.0.0" yaml: "npm:^2.0.0" - zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -6307,7 +6296,6 @@ __metadata: "@gitbeaker/requester-utils": "npm:^41.2.0" "@gitbeaker/rest": "npm:^41.2.0" luxon: "npm:^3.0.0" - winston: "npm:^3.2.1" yaml: "npm:^2.0.0" zod: "npm:^3.22.4" languageName: unknown From 8d15a51b3ec5a29cebdf87a609c2ef8530b37075 Mon Sep 17 00:00:00 2001 From: yu-kkurii <32046496+yu-kkurii@users.noreply.github.com> Date: Mon, 22 Sep 2025 21:37:29 +0200 Subject: [PATCH 070/211] update pg search sanitize list (#31083) Signed-off-by: Vladimir Kobzev --- .changeset/nice-readers-judge.md | 5 +++++ .../src/PgSearchEngine/PgSearchEngine.test.ts | 2 +- .../src/PgSearchEngine/PgSearchEngine.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/nice-readers-judge.md diff --git a/.changeset/nice-readers-judge.md b/.changeset/nice-readers-judge.md new file mode 100644 index 0000000000..a70ee8e501 --- /dev/null +++ b/.changeset/nice-readers-judge.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-pg': patch +--- + +Added the < character to the query filter regexp diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts index c56816b475..bc3df76692 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts @@ -133,7 +133,7 @@ describe('PgSearchEngine', () => { it('should sanitize query term', async () => { const actualTranslatedQuery = searchEngine.translator( { - term: 'H&e|l!l*o W\0o(r)l:d', + term: 'H&e|l!l*o < W\0o(r)l:d', pageCursor: '', }, { highlightOptions }, diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts index 5b24d71100..57c22fd19f 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts @@ -196,7 +196,7 @@ export class PgSearchEngine implements SearchEngine { pgQuery: { pgTerm: query.term .split(/\s/) - .map(p => p.replace(/[\0()|&:*!]/g, '').trim()) + .map(p => p.replace(/[\0()|&:*!<]/g, '').trim()) .filter(p => p !== '') .map(p => `(${JSON.stringify(p)} | ${JSON.stringify(p)}:*)`) .join('&'), From 226ef498e224ceb58c2c90b98683c0607785d644 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Tue, 23 Sep 2025 08:51:35 +0100 Subject: [PATCH 071/211] simplify client.hasFile to always use project.id in shouldProcessProject Signed-off-by: Claire Peng --- .../src/providers/GitlabDiscoveryEntityProvider.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index 869374de07..e52c231d46 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -589,22 +589,12 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { project.default_branch ?? this.config.fallbackBranch; - // Find file with namespaced path (most use-cases) - let hasFile = await client.hasFile( - project.path_with_namespace ?? '', + const hasFile = await client.hasFile( + project.id.toString(), project_branch, this.config.catalogFile, ); - // Find file with project id if namespace failed - if (!hasFile) { - hasFile = await client.hasFile( - project.id.toString(), - project_branch, - this.config.catalogFile, - ); - } - return hasFile; } } From 0443119e7a3a1dd1b086d715a0bf0ff10aa29cf5 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Tue, 23 Sep 2025 09:16:57 +0100 Subject: [PATCH 072/211] add changeset Signed-off-by: Claire Peng --- .changeset/twelve-oranges-grin.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .changeset/twelve-oranges-grin.md diff --git a/.changeset/twelve-oranges-grin.md b/.changeset/twelve-oranges-grin.md new file mode 100644 index 0000000000..b508a59b0b --- /dev/null +++ b/.changeset/twelve-oranges-grin.md @@ -0,0 +1,20 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +Update GitlabDiscoveryEntityProvider to use `project.id` rather than `project.path_with_namespace` for `hasFile` check in `shouldProcessProject` + +Solves [#30147](https://github.com/backstage/backstage/issues/30147): + +> Use of project.id avoids edgecases caused by path encoding or project structure changes +> [...] +> It also simplifies reasoning about the GitLab API usage, since IDs are immutable, while paths are not. + +```diff +const hasFile = await client.hasFile( +- project.path_with_namespace, ++ project.id.toString(), + project_branch, + this.config.catalogFile, +); +``` From baf1cab204a6b4050e015a8222a38b24e6a2ecf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Jerna=C5=9B?= Date: Tue, 23 Sep 2025 11:17:46 +0200 Subject: [PATCH 073/211] docs(scaffolder-gcp): Fix docstrings to mention GCP instead of Azure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Jernaś --- .changeset/itchy-falcons-leave.md | 5 +++++ plugins/scaffolder-backend-module-gcp/src/index.ts | 2 +- plugins/scaffolder-backend-module-gcp/src/module.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/itchy-falcons-leave.md diff --git a/.changeset/itchy-falcons-leave.md b/.changeset/itchy-falcons-leave.md new file mode 100644 index 0000000000..93618be48c --- /dev/null +++ b/.changeset/itchy-falcons-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gcp': patch +--- + +Fix docstrings to mention GCP instead of Azure diff --git a/plugins/scaffolder-backend-module-gcp/src/index.ts b/plugins/scaffolder-backend-module-gcp/src/index.ts index 05498e3edd..5e3eced7a4 100644 --- a/plugins/scaffolder-backend-module-gcp/src/index.ts +++ b/plugins/scaffolder-backend-module-gcp/src/index.ts @@ -15,7 +15,7 @@ */ /** - * A module for the scaffolder backend that lets you interact with azure + * A module for the scaffolder backend that lets you interact with GCP * * @packageDocumentation */ diff --git a/plugins/scaffolder-backend-module-gcp/src/module.ts b/plugins/scaffolder-backend-module-gcp/src/module.ts index 0f2f37f5af..ad9ebcecee 100644 --- a/plugins/scaffolder-backend-module-gcp/src/module.ts +++ b/plugins/scaffolder-backend-module-gcp/src/module.ts @@ -22,7 +22,7 @@ import { GcpBucketWorkspaceProvider } from './providers/GcpBucketWorkspaceProvid /** * @public - * The Azure Module for the Scaffolder Backend + * The GCP Module for the Scaffolder Backend */ export const gcpBucketModule = createBackendModule({ moduleId: 'gcp', From 2d3e2b279569c7313cc57ac719dd6765e0003994 Mon Sep 17 00:00:00 2001 From: vickstrom Date: Tue, 23 Sep 2025 10:12:16 +0200 Subject: [PATCH 074/211] implement support for direct url for AzureBlobStorageUrlReader search function Signed-off-by: vickstrom --- .changeset/calm-trains-tie.md | 5 +++ .../backend-defaults/report-urlReader.api.md | 5 ++- .../lib/AzureBlobStorageUrlReader.ts | 40 +++++++++++++++++-- 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 .changeset/calm-trains-tie.md diff --git a/.changeset/calm-trains-tie.md b/.changeset/calm-trains-tie.md new file mode 100644 index 0000000000..8c399ba123 --- /dev/null +++ b/.changeset/calm-trains-tie.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': minor +--- + +implement support for direct url for AzureBlobStorageUrlReader search function diff --git a/packages/backend-defaults/report-urlReader.api.md b/packages/backend-defaults/report-urlReader.api.md index 03a112abc7..039d51808d 100644 --- a/packages/backend-defaults/report-urlReader.api.md +++ b/packages/backend-defaults/report-urlReader.api.md @@ -87,7 +87,10 @@ export class AzureBlobStorageUrlReader implements UrlReaderService { options?: UrlReaderServiceReadUrlOptions, ): Promise; // (undocumented) - search(): Promise; + search( + url: string, + options?: UrlReaderServiceSearchOptions, + ): Promise; // (undocumented) toString(): string; } diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts index e04e2c5b47..1953beabe9 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts @@ -21,7 +21,11 @@ import { StorageSharedKeyCredential, } from '@azure/storage-blob'; import { ReaderFactory, ReadTreeResponseFactory } from './types'; -import { ForwardedError, NotModifiedError } from '@backstage/errors'; +import { + assertError, + ForwardedError, + NotModifiedError, +} from '@backstage/errors'; import { Readable } from 'stream'; import { relative } from 'path/posix'; import { ReadUrlResponseFactory } from './ReadUrlResponseFactory'; @@ -37,6 +41,7 @@ import { UrlReaderServiceReadTreeResponse, UrlReaderServiceReadUrlOptions, UrlReaderServiceReadUrlResponse, + UrlReaderServiceSearchOptions, UrlReaderServiceSearchResponse, } from '@backstage/backend-plugin-api'; @@ -190,13 +195,13 @@ export class AzureBlobStorageUrlReader implements UrlReaderService { const { path, container } = parseUrl(url); const containerClient = await this.createContainerClient(container); - const blobs = containerClient.listBlobsFlat({ prefix: path }); const responses = []; for await (const blob of blobs) { const blobClient = containerClient.getBlobClient(blob.name); + const downloadBlockBlobResponse = await blobClient.download( undefined, undefined, @@ -221,8 +226,35 @@ export class AzureBlobStorageUrlReader implements UrlReaderService { } } - async search(): Promise { - throw new Error('AzureBlobStorageUrlReader does not implement search'); + async search( + url: string, + options?: UrlReaderServiceSearchOptions, + ): Promise { + const { path } = parseUrl(url); + + if (path.match(/[*?]/)) { + throw new Error( + 'Glob search pattern not implemented for AzureBlobStorageUrlReader', + ); + } + + try { + const data = await this.readUrl(url, options); + + return { + files: [ + { + url: url, + content: data.buffer, + lastModifiedAt: data.lastModifiedAt, + }, + ], + etag: data.etag ?? '', + }; + } catch (error) { + assertError(error); + throw error; + } } toString() { From 06eaf14c052a556e2aa6f119f0549954db80c2bc Mon Sep 17 00:00:00 2001 From: vickstrom Date: Tue, 23 Sep 2025 10:38:07 +0200 Subject: [PATCH 075/211] add test for AzureBlobStorageUrlReader Signed-off-by: vickstrom --- .../lib/AzureBlobStorageUrlReader.test.ts | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.test.ts diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.test.ts new file mode 100644 index 0000000000..93f0d81441 --- /dev/null +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.test.ts @@ -0,0 +1,359 @@ +/* + * Copyright 2025 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 * as AzureStorage from '@azure/storage-blob'; +import { ConfigReader } from '@backstage/config'; +import { JsonObject } from '@backstage/types'; +import { DefaultReadTreeResponseFactory } from './tree'; +import { + AzureBlobStorageUrlReader, + parseUrl, +} from './AzureBlobStorageUrlReader'; +import { + DefaultAzureCredentialsManager, + ScmIntegrations, +} from '@backstage/integration'; +import { UrlReaderPredicateTuple } from './types'; +import { mockServices } from '@backstage/backend-test-utils'; +import { Readable } from 'stream'; + +// Mock Azure Blob Storage SDK +const mockBlobDownload = jest.fn(); +const mockGetBlobClient = jest.fn(() => ({ + download: mockBlobDownload, +})); +const mockListBlobsFlat = jest.fn(); + +class MockContainerClient { + getBlobClient = mockGetBlobClient; + listBlobsFlat = mockListBlobsFlat; +} + +class MockBlobServiceClient { + getContainerClient = jest.fn(() => new MockContainerClient()); +} + +jest + .spyOn(AzureStorage, 'BlobServiceClient') + .mockReturnValue(new MockBlobServiceClient() as any); +jest + .spyOn(AzureStorage, 'StorageSharedKeyCredential') + .mockReturnValue({} as any); + +const treeResponseFactory = DefaultReadTreeResponseFactory.create({ + config: new ConfigReader({}), +}); + +describe('parseUrl', () => { + it('parses Azure Blob Storage URLs correctly', () => { + expect( + parseUrl( + 'https://test-account.blob.core.windows.net/mycontainer/path/to/file.yaml', + ), + ).toEqual({ + path: 'path/to/file.yaml', + container: 'mycontainer', + }); + + expect( + parseUrl( + 'https://test-account.blob.core.windows.net/mycontainer/file.yaml', + ), + ).toEqual({ + path: 'file.yaml', + container: 'mycontainer', + }); + }); + + it('handles URLs with no path correctly', () => { + expect( + parseUrl('https://test-account.blob.core.windows.net/mycontainer/'), + ).toEqual({ + path: '', + container: 'mycontainer', + }); + }); + + it('throws error for invalid URLs', () => { + expect(() => + parseUrl('https://test-account.blob.core.windows.net/'), + ).toThrow('Invalid Azure Blob Storage URL format'); + }); +}); + +describe('AzureBlobStorageUrlReader', () => { + const createReader = (config: JsonObject): UrlReaderPredicateTuple[] => { + return AzureBlobStorageUrlReader.factory({ + config: new ConfigReader(config), + logger: mockServices.logger.mock(), + treeResponseFactory, + }); + }; + + it('creates a reader with minimal config', () => { + const entries = createReader({ + integrations: { + azureBlobStorage: [ + { + accountName: 'test-account', + }, + ], + }, + }); + + expect(entries).toHaveLength(1); + }); + + describe('predicates', () => { + const readers = createReader({ + integrations: { + azureBlobStorage: [ + { + accountName: 'test-account', + }, + ], + }, + }); + const predicate = readers[0].predicate; + + it('returns true for the correct azure blob storage host', () => { + expect( + predicate(new URL('https://test-account.blob.core.windows.net')), + ).toBe(true); + }); + + it('returns false for an incorrect host', () => { + expect( + predicate(new URL('https://wrongaccount.blob.core.windows.net')), + ).toBe(false); + }); + }); + + describe('toString', () => { + it('returns a string representation with account name and auth status', () => { + const config = new ConfigReader({ + integrations: { + azureBlobStorage: [ + { + accountName: 'test-account', + accountKey: 'test-key', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const credsManager = + DefaultAzureCredentialsManager.fromIntegrations(integrations); + const reader = new AzureBlobStorageUrlReader( + credsManager, + integrations.azureBlobStorage.list()[0], + { treeResponseFactory }, + ); + + expect(reader.toString()).toBe( + 'azureBlobStorage{accountName=test-account,authed=true}', + ); + }); + + it('shows authed=false when no account key provided', () => { + const config = new ConfigReader({ + integrations: { + azureBlobStorage: [ + { + accountName: 'test-account', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const credsManager = + DefaultAzureCredentialsManager.fromIntegrations(integrations); + const reader = new AzureBlobStorageUrlReader( + credsManager, + integrations.azureBlobStorage.list()[0], + { treeResponseFactory }, + ); + + expect(reader.toString()).toBe( + 'azureBlobStorage{accountName=test-account,authed=false}', + ); + }); + }); + + describe('readUrl', () => { + const [{ reader }] = createReader({ + integrations: { + azureBlobStorage: [ + { + accountName: 'test-account', + accountKey: 'test-key', + }, + ], + }, + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('returns contents of a blob via buffer', async () => { + mockBlobDownload.mockResolvedValue({ + readableStreamBody: Readable.from( + Buffer.from('site_name: Test Azure Blob'), + ), + etag: '"etag"', + lastModified: new Date('2025-01-01T00:00:00Z'), + }); + + const { buffer, etag, lastModifiedAt } = await reader.readUrl( + 'https://test-account.blob.core.windows.net/test-container/test-file.yaml', + ); + + expect(etag).toBe('"etag"'); + expect(lastModifiedAt).toEqual(new Date('2025-01-01T00:00:00Z')); + const response = await buffer(); + expect(response.toString()).toBe('site_name: Test Azure Blob'); + }); + + it('handles Azure SDK errors', async () => { + mockBlobDownload.mockRejectedValue(new Error('Blob not found')); + + await expect( + reader.readUrl( + 'https://test-account.blob.core.windows.net/test-container/nonexistent.yaml', + ), + ).rejects.toThrow('Could not retrieve file from Azure Blob Storage'); + }); + }); + + describe('readTree', () => { + const [{ reader }] = createReader({ + integrations: { + azureBlobStorage: [ + { + accountName: 'test-account', + accountKey: 'test-key', + }, + ], + }, + }); + + beforeEach(() => { + jest.clearAllMocks(); + const mockBlobs = [ + { + name: 'prefix/file1.yaml', + properties: { + lastModified: new Date('2025-01-01T00:00:00Z'), + }, + }, + { + name: 'prefix/subdir/file2.yaml', + properties: { + lastModified: new Date('2024-01-01T00:00:00Z'), + }, + }, + ]; + + mockBlobDownload.mockResolvedValue({ + readableStreamBody: Readable.from( + Buffer.from('site_name: Test Azure Blob'), + ), + }); + + mockListBlobsFlat.mockReturnValue({ + [Symbol.asyncIterator]: async function* generateBlobs() { + for (const blob of mockBlobs) { + yield blob; + } + }, + }); + }); + + it('returns contents of blobs in a container', async () => { + const response = await reader.readTree( + 'https://test-account.blob.core.windows.net/test-container/prefix', + ); + const files = await response.files(); + + expect(files).toHaveLength(2); + const file1Content = await files[0].content(); + expect(file1Content.toString()).toBe('site_name: Test Azure Blob'); + }); + }); + + describe('search', () => { + const [{ reader }] = createReader({ + integrations: { + azureBlobStorage: [ + { + accountName: 'test-account', + accountKey: 'test-key', + }, + ], + }, + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return a file when given an exact valid url', async () => { + mockBlobDownload.mockResolvedValue({ + readableStreamBody: Readable.from( + Buffer.from('site_name: Test Azure Blob'), + ), + etag: '"etag"', + lastModified: new Date('2025-01-01T00:00:00Z'), + }); + + const data = await reader.search( + 'https://test-account.blob.core.windows.net/test-container/test-file.yaml', + ); + + expect(data.etag).toBe('"etag"'); + expect(data.files.length).toBe(1); + expect(data.files[0].url).toBe( + 'https://test-account.blob.core.windows.net/test-container/test-file.yaml', + ); + expect((await data.files[0].content()).toString()).toEqual( + 'site_name: Test Azure Blob', + ); + }); + + it('should handle Azure SDK errors from readUrl', async () => { + mockBlobDownload.mockRejectedValue(new Error('Blob not found')); + + await expect( + reader.search( + 'https://test-account.blob.core.windows.net/test-container/missing.yaml', + ), + ).rejects.toThrow('Could not retrieve file from Azure Blob Storage'); + }); + + it('throws if given URL with wildcard', async () => { + await expect( + reader.search( + 'https://test-account.blob.core.windows.net/test-container/test-*.yaml', + ), + ).rejects.toThrow( + 'Glob search pattern not implemented for AzureBlobStorageUrlReader', + ); + }); + }); +}); From a9b88beaaae397c4146bb11999ed98d10f0ccbba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofia=20Sj=C3=B6blad?= Date: Tue, 23 Sep 2025 12:08:01 +0200 Subject: [PATCH 076/211] fix(bui): Enable tooltips on disabled buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sofia Sjöblad --- .changeset/moody-singers-deny.md | 5 +++++ .../ui/src/components/Button/Button.stories.tsx | 10 ++++++++++ packages/ui/src/components/Button/Button.tsx | 16 ++++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 .changeset/moody-singers-deny.md diff --git a/.changeset/moody-singers-deny.md b/.changeset/moody-singers-deny.md new file mode 100644 index 0000000000..28072baf4b --- /dev/null +++ b/.changeset/moody-singers-deny.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Enable tooltips on disabled buttons with automatic wrapper diff --git a/packages/ui/src/components/Button/Button.stories.tsx b/packages/ui/src/components/Button/Button.stories.tsx index db8e999f98..1604c04eb4 100644 --- a/packages/ui/src/components/Button/Button.stories.tsx +++ b/packages/ui/src/components/Button/Button.stories.tsx @@ -19,6 +19,7 @@ import { Button } from './Button'; import { Flex } from '../Flex'; import { Text } from '../Text'; import { Icon } from '../Icon'; +import { Tooltip, TooltipTrigger } from '../Tooltip'; const meta = { title: 'Backstage UI/Button', @@ -216,3 +217,12 @@ export const Playground: Story = { ), }; + +export const DisabledWithTooltips: Story = { + render: () => ( + + + Why this is disabled + + ), +}; diff --git a/packages/ui/src/components/Button/Button.tsx b/packages/ui/src/components/Button/Button.tsx index 40879e51ed..9f658fd00a 100644 --- a/packages/ui/src/components/Button/Button.tsx +++ b/packages/ui/src/components/Button/Button.tsx @@ -16,7 +16,7 @@ import clsx from 'clsx'; import { forwardRef, Ref } from 'react'; -import { Button as RAButton } from 'react-aria-components'; +import { Button as RAButton, Focusable } from 'react-aria-components'; import type { ButtonProps } from './types'; import { useStyles } from '../../hooks/useStyles'; @@ -30,6 +30,7 @@ export const Button = forwardRef( iconEnd, children, className, + isDisabled, ...rest } = props; @@ -38,18 +39,29 @@ export const Button = forwardRef( variant, }); - return ( + const btn = ( {iconStart} {children} {iconEnd} ); + + return isDisabled ? ( + + + {btn} + + + ) : ( + btn + ); }, ); From dbb8c3b26e36599c06ead57f7f7a78f0a69a9f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 23 Sep 2025 13:16:52 +0200 Subject: [PATCH 077/211] Update .changeset/itchy-falcons-leave.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/itchy-falcons-leave.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/itchy-falcons-leave.md b/.changeset/itchy-falcons-leave.md index 93618be48c..07188e08c5 100644 --- a/.changeset/itchy-falcons-leave.md +++ b/.changeset/itchy-falcons-leave.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend-module-gcp': patch --- -Fix docstrings to mention GCP instead of Azure +Fix documentation strings to mention GCP instead of Azure From 4adbb03a272089649fbfbda3d834644fb0b9e971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofia=20Sj=C3=B6blad?= Date: Tue, 23 Sep 2025 14:26:18 +0200 Subject: [PATCH 078/211] fix(bui): Fix SearchField onChange prop and add onChange story MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sofia Sjöblad --- .changeset/tired-mice-cheer.md | 5 ++++ .../SearchField/SearchField.stories.tsx | 24 ++++++++++++++++++- .../components/SearchField/SearchField.tsx | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/tired-mice-cheer.md diff --git a/.changeset/tired-mice-cheer.md b/.changeset/tired-mice-cheer.md new file mode 100644 index 0000000000..929aae0f59 --- /dev/null +++ b/.changeset/tired-mice-cheer.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Avoid overriding onChange when spreading props diff --git a/packages/ui/src/components/SearchField/SearchField.stories.tsx b/packages/ui/src/components/SearchField/SearchField.stories.tsx index a811d2a451..7cb89dfbfb 100644 --- a/packages/ui/src/components/SearchField/SearchField.stories.tsx +++ b/packages/ui/src/components/SearchField/SearchField.stories.tsx @@ -51,6 +51,7 @@ export const Default: Story = { style: { maxWidth: '300px', }, + 'aria-label': 'Search', }, }; @@ -192,7 +193,7 @@ export const InHeader: Story = { size="small" variant="secondary" /> - + } @@ -272,3 +273,24 @@ export const StartCollapsedWithButtons: Story = { ), }; + +export const StartCollapsedWithOnChange: Story = { + args: { + ...StartCollapsed.args, + }, + render: args => { + const handleChange = (value: string) => { + console.log('Search value:', value); + }; + + return ( + + + + ); + }, +}; diff --git a/packages/ui/src/components/SearchField/SearchField.tsx b/packages/ui/src/components/SearchField/SearchField.tsx index e497cecd49..1c4bb7fa18 100644 --- a/packages/ui/src/components/SearchField/SearchField.tsx +++ b/packages/ui/src/components/SearchField/SearchField.tsx @@ -39,6 +39,7 @@ export const SearchField = forwardRef( secondaryLabel, description, isRequired, + onChange, placeholder = 'Search', startCollapsed = false, 'aria-label': ariaLabel, From e489661c82a6112e8dd685b45dfbea7e9cb46489 Mon Sep 17 00:00:00 2001 From: Hellgren Heikki Date: Tue, 23 Sep 2025 15:13:58 +0300 Subject: [PATCH 079/211] fix(catalog): config for priority and enabled Signed-off-by: Hellgren Heikki --- .changeset/fast-heads-brake.md | 25 ++++++++++ .../software-catalog/configuration.md | 29 +++++++++++ plugins/catalog-backend/config.d.ts | 30 ++++++------ .../src/service/CatalogBuilder.ts | 48 ++++++------------- 4 files changed, 82 insertions(+), 50 deletions(-) create mode 100644 .changeset/fast-heads-brake.md diff --git a/.changeset/fast-heads-brake.md b/.changeset/fast-heads-brake.md new file mode 100644 index 0000000000..bc9f525520 --- /dev/null +++ b/.changeset/fast-heads-brake.md @@ -0,0 +1,25 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Moved catalog processor and provider disabling and priorities under own config objects. + +This is due to issue with some existing providers, such as GitHub, using array syntax for the provider configuration. + +The new config format is not backwards compatible, so users will need to update their config files. The new format +is as follows: + +```yaml +catalog: + providerOptions: + providerA: + disabled: false + providerB: + disabled: true + processorOptions: + processorA: + disabled: false + priority: 10 + processorB: + disabled: true +``` diff --git a/docs/features/software-catalog/configuration.md b/docs/features/software-catalog/configuration.md index f8de3034fd..5b293cce1c 100644 --- a/docs/features/software-catalog/configuration.md +++ b/docs/features/software-catalog/configuration.md @@ -90,6 +90,35 @@ also write a _custom processor_ to convert between the existing system and Backstage's descriptor format. This is documented in [External Integrations](external-integrations.md). +### Processor configuration + +You can configure processors under the `catalog.processorOptions` key. You can define +options for each processor by its name. With the processor options you can disable +a processor or set its priority. + +```yaml +catalog: + processorOptions: + processorName: + disabled: false # Defaults to false + priority: 100 +``` + +The priority is a number defining the order in which processors are executed. The lower the number, +the higher the priority. The default priority is `20`. + +## Provider configuration + +It's possible to also disable entity providers using the config. You can configure providers +under the `catalog.providerOptions` key. The key is the provider name. + +```yaml +catalog: + providerOptions: + providerName: + disabled: true +``` + ## Catalog Rules By default, the catalog will only allow the ingestion of entities with the kind diff --git a/plugins/catalog-backend/config.d.ts b/plugins/catalog-backend/config.d.ts index f1b72e5b45..95d28d1eae 100644 --- a/plugins/catalog-backend/config.d.ts +++ b/plugins/catalog-backend/config.d.ts @@ -223,38 +223,36 @@ export interface Config { processingInterval?: HumanDuration | false; /** - * Catalog provide specific configuration. - * Additional configuration for providers are specified in the catalog - * modules. + * Provider-specific additional configuration options. */ - providers?: { + providerOptions?: { /** - * Name is the provider ID, e.g. "bitbucketServer" + * Key is the provider name, value is an object with additional configuration */ [name: string]: { /** - * Whether the provider is enabled or not. Defaults to true. + * Determines whether this provider is disabled or not. If not specified, + * defaults to false. */ - enabled?: boolean; + disabled?: boolean; }; }; + /** - * Configuration for entity processors. Additional configuration for - * processors are specified in the catalog modules. + * Processor-specific additional configuration options. */ - processors?: { + processorOptions?: { /** - * Name is the processor ID, e.g. "catalog-processor" + * Key is the processor name, value is an object with additional configuration */ [name: string]: { /** - * Whether the processor is enabled or not. Defaults to true. + * Determines whether this processor is disabled or not. If not specified, + * defaults to false. */ - enabled?: boolean; + disabled?: boolean; /** - * The priority of the processor, which is used to determine the order in which - * processors are run. The default priority is 20, and lower value means - * that the processor runs earlier. + * The default priority is 20, and lower value means that the processor runs earlier. */ priority?: number; }; diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index dcd677152e..989c2b7184 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -736,13 +736,13 @@ export class CatalogBuilder { try { return ( config.getOptionalNumber( - `catalog.processors.${processor.getProcessorName()}.priority`, + `catalog.processorOptions.${processor.getProcessorName()}.priority`, ) ?? processor.getPriority?.() ?? 20 ); } catch (_) { - // In case the processor config is not an object, just return default priority + // In case the processor config throws, just return default priority return 20; } }; @@ -757,22 +757,12 @@ export class CatalogBuilder { private filterProcessors(processors: CatalogProcessor[]) { const { config } = this.env; - const processorsConfig = config.getOptionalConfig('catalog.processors'); - if (!processorsConfig) { - return processors; - } - - return processors.filter(p => { - try { - const processorConfig = processorsConfig.getOptionalConfig( - p.getProcessorName(), - ); - return processorConfig?.getOptionalBoolean('enabled') ?? true; - } catch (_) { - // In case the processor config is not an object, just include the processor - return true; - } - }); + return processors.filter( + p => + config.getOptionalBoolean( + `catalog.processorOptions.${p.getProcessorName()}.disabled`, + ) !== true, + ); } // TODO(Rugvip): These old processors are removed, for a while we'll be throwing @@ -887,22 +877,12 @@ export class CatalogBuilder { private filterProviders(providers: EntityProvider[]) { const { config } = this.env; - const providersConfig = config.getOptionalConfig('catalog.providers'); - if (!providersConfig) { - return providers; - } - - return providers.filter(p => { - try { - const providerConfig = providersConfig.getOptionalConfig( - p.getProviderName(), - ); - return providerConfig?.getOptionalBoolean('enabled') ?? true; - } catch (_) { - // In case the provider config is not an object, just include the provider - return true; - } - }); + return providers.filter( + p => + config.getOptionalBoolean( + `catalog.providerOptions.${p.getProviderName()}.disabled`, + ) !== true, + ); } private static getDefaultProcessingInterval( From 23829e022c5bd9eb7ead7efd1ef5056744db2e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofia=20Sj=C3=B6blad?= Date: Tue, 23 Sep 2025 15:13:32 +0200 Subject: [PATCH 080/211] fix(bui): Add missing Cell component props to table documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sofia Sjöblad --- docs-ui/src/content/components/table.props.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs-ui/src/content/components/table.props.ts b/docs-ui/src/content/components/table.props.ts index 1b40055849..d46c6b8ea7 100644 --- a/docs-ui/src/content/components/table.props.ts +++ b/docs-ui/src/content/components/table.props.ts @@ -212,6 +212,11 @@ export const cellPropDefs: Record = { description: "A string representation of the cell's contents, used for features like typeahead.", }, + leadingIcon: { + type: 'enum', + values: ['ReactNode'], + description: 'Optional icon to display before the cell content.', + }, ...classNamePropDefs, ...stylePropDefs, }; From 6ea23c9c6d99f0b04216329198575cae20311b73 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 23 Sep 2025 15:49:58 +0200 Subject: [PATCH 081/211] chore: remove deprecated types and making breaking changes Signed-off-by: benjdlambert --- plugins/scaffolder-backend/report.api.md | 373 ------------------ plugins/scaffolder-backend/src/index.ts | 4 +- .../actions/TemplateActionRegistry.ts | 3 +- .../src/scaffolder/tasks/DatabaseTaskStore.ts | 6 +- .../tasks/NunjucksWorkflowRunner.ts | 2 +- .../src/scaffolder/tasks/StorageTaskBroker.ts | 4 +- .../src/scaffolder/tasks/TaskWorker.ts | 8 +- .../src/scaffolder/tasks/types.ts | 50 +-- plugins/scaffolder-node/report.api.md | 13 +- plugins/scaffolder-node/src/tasks/types.ts | 16 +- 10 files changed, 24 insertions(+), 455 deletions(-) diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index 918718e495..8f9249cc5e 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -3,45 +3,21 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { AuditorService } from '@backstage/backend-plugin-api'; -import { AuthService } from '@backstage/backend-plugin-api'; import { BackendFeature } from '@backstage/backend-plugin-api'; -import { BackstageCredentials } from '@backstage/backend-plugin-api'; import { CatalogService } from '@backstage/plugin-catalog-node'; -import { Config } from '@backstage/config'; -import { DatabaseService } from '@backstage/backend-plugin-api'; import { Duration } from 'luxon'; -import { EventsService } from '@backstage/plugin-events-node'; import { HumanDuration } from '@backstage/types'; -import { JsonObject } from '@backstage/types'; -import { Knex } from 'knex'; -import { LoggerService } from '@backstage/backend-plugin-api'; -import { PermissionCriteria } from '@backstage/plugin-permission-common'; -import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; import { RESOURCE_TYPE_SCAFFOLDER_ACTION } from '@backstage/plugin-scaffolder-common/alpha'; import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE } from '@backstage/plugin-scaffolder-common/alpha'; import { ScmIntegrations } from '@backstage/integration'; -import { SerializedTask } from '@backstage/plugin-scaffolder-node'; -import { SerializedTaskEvent } from '@backstage/plugin-scaffolder-node'; -import { TaskBroker } from '@backstage/plugin-scaffolder-node'; -import { TaskCompletionState } from '@backstage/plugin-scaffolder-node'; -import { TaskContext } from '@backstage/plugin-scaffolder-node'; -import { TaskFilters } from '@backstage/plugin-scaffolder-node'; -import { TaskRecovery } from '@backstage/plugin-scaffolder-common'; -import { TaskSecrets } from '@backstage/plugin-scaffolder-node'; -import { TaskSpec } from '@backstage/plugin-scaffolder-common'; -import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { TaskStatus } from '@backstage/plugin-scaffolder-node'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateFilter } from '@backstage/plugin-scaffolder-node'; import { TemplateGlobal } from '@backstage/plugin-scaffolder-node'; import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { UpdateTaskCheckpointOptions } from '@backstage/plugin-scaffolder-node/alpha'; import { UrlReaderService } from '@backstage/backend-plugin-api'; -import { WorkspaceProvider } from '@backstage/plugin-scaffolder-node/alpha'; // @public (undocumented) export type ActionPermissionRuleInput< @@ -267,359 +243,10 @@ export function createWaitAction(options?: { 'v2' >; -// @public @deprecated -export type CreateWorkerOptions = { - taskBroker: TaskBroker; - actionRegistry: TemplateActionRegistry; - integrations: ScmIntegrations; - workingDirectory: string; - logger: LoggerService; - auditor?: AuditorService; - config?: Config; - additionalTemplateFilters?: Record; - concurrentTasksLimit?: number; - additionalTemplateGlobals?: Record; - permissions?: PermissionEvaluator; - gracefulShutdown?: boolean; -}; - -// @public -export interface CurrentClaimedTask { - createdBy?: string; - secrets?: TaskSecrets; - spec: TaskSpec; - state?: JsonObject; - taskId: string; - workspace?: Promise; -} - -// @public @deprecated -export class DatabaseTaskStore implements TaskStore { - // (undocumented) - cancelTask( - options: TaskStoreEmitOptions< - { - message: string; - } & JsonObject - >, - ): Promise; - // (undocumented) - claimTask(): Promise; - // (undocumented) - cleanWorkspace({ taskId }: { taskId: string }): Promise; - // (undocumented) - completeTask(options: { - taskId: string; - status: TaskStatus; - eventBody: JsonObject; - }): Promise; - // (undocumented) - static create(options: DatabaseTaskStoreOptions): Promise; - // (undocumented) - createTask( - options: TaskStoreCreateTaskOptions, - ): Promise; - // (undocumented) - emitLogEvent( - options: TaskStoreEmitOptions< - { - message: string; - } & JsonObject - >, - ): Promise; - // (undocumented) - getTask(taskId: string): Promise; - // (undocumented) - getTaskState({ taskId }: { taskId: string }): Promise< - | { - state: JsonObject; - } - | undefined - >; - // (undocumented) - heartbeatTask(taskId: string): Promise; - // (undocumented) - list(options: { - createdBy?: string; - status?: TaskStatus; - filters?: { - createdBy?: string | string[]; - status?: TaskStatus | TaskStatus[]; - }; - pagination?: { - limit?: number; - offset?: number; - }; - order?: { - order: 'asc' | 'desc'; - field: string; - }[]; - permissionFilters?: PermissionCriteria; - }): Promise<{ - tasks: SerializedTask[]; - totalTasks?: number; - }>; - // (undocumented) - listEvents(options: TaskStoreListEventsOptions): Promise<{ - events: SerializedTaskEvent[]; - }>; - // (undocumented) - listStaleTasks(options: { timeoutS: number }): Promise<{ - tasks: { - taskId: string; - recovery?: TaskRecovery; - }[]; - }>; - // (undocumented) - recoverTasks(options: TaskStoreRecoverTaskOptions): Promise<{ - ids: string[]; - }>; - // (undocumented) - rehydrateWorkspace(options: { - taskId: string; - targetPath: string; - }): Promise; - // (undocumented) - retryTask?(options: { secrets?: TaskSecrets; taskId: string }): Promise; - // (undocumented) - saveTaskState(options: { taskId: string; state?: JsonObject }): Promise; - // (undocumented) - serializeWorkspace(options: { path: string; taskId: string }): Promise; - // (undocumented) - shutdownTask(options: TaskStoreShutDownTaskOptions): Promise; -} - -// @public @deprecated -export type DatabaseTaskStoreOptions = { - database: DatabaseService | Knex; - events?: EventsService; -}; - // @public const scaffolderPlugin: BackendFeature; export default scaffolderPlugin; -// @public @deprecated -export class TaskManager implements TaskContext { - // (undocumented) - get cancelSignal(): AbortSignal; - // (undocumented) - cleanWorkspace?(): Promise; - // (undocumented) - complete(result: TaskCompletionState, metadata?: JsonObject): Promise; - // (undocumented) - static create( - task: CurrentClaimedTask, - storage: TaskStore, - abortSignal: AbortSignal, - logger: LoggerService, - auth?: AuthService, - config?: Config, - additionalWorkspaceProviders?: Record, - ): TaskManager; - // (undocumented) - get createdBy(): string | undefined; - // (undocumented) - get done(): boolean; - // (undocumented) - emitLog(message: string, logMetadata?: JsonObject): Promise; - // (undocumented) - getInitiatorCredentials(): Promise; - // (undocumented) - getTaskState?(): Promise< - | { - state?: JsonObject; - } - | undefined - >; - // (undocumented) - getWorkspaceName(): Promise; - // (undocumented) - rehydrateWorkspace?(options: { - taskId: string; - targetPath: string; - }): Promise; - // (undocumented) - get secrets(): TaskSecrets | undefined; - // (undocumented) - serializeWorkspace?(options: { path: string }): Promise; - // (undocumented) - get spec(): TaskSpecV1beta3; - // (undocumented) - get taskId(): string; - // (undocumented) - updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise; -} - -// @public @deprecated -export interface TaskStore { - // (undocumented) - cancelTask?(options: TaskStoreEmitOptions): Promise; - // (undocumented) - claimTask(): Promise; - // (undocumented) - cleanWorkspace?({ taskId }: { taskId: string }): Promise; - // (undocumented) - completeTask(options: { - taskId: string; - status: TaskStatus; - eventBody: JsonObject; - }): Promise; - // (undocumented) - createTask( - options: TaskStoreCreateTaskOptions, - ): Promise; - // (undocumented) - emitLogEvent(options: TaskStoreEmitOptions): Promise; - // (undocumented) - getTask(taskId: string): Promise; - // (undocumented) - getTaskState?({ taskId }: { taskId: string }): Promise< - | { - state: JsonObject; - } - | undefined - >; - // (undocumented) - heartbeatTask(taskId: string): Promise; - // (undocumented) - list?(options: { - filters?: { - createdBy?: string | string[]; - status?: TaskStatus | TaskStatus[]; - }; - pagination?: { - limit?: number; - offset?: number; - }; - permissionFilters?: PermissionCriteria; - order?: { - order: 'asc' | 'desc'; - field: string; - }[]; - }): Promise<{ - tasks: SerializedTask[]; - totalTasks?: number; - }>; - // @deprecated (undocumented) - list?(options: { - createdBy?: string; - status?: TaskStatus; - filters?: { - createdBy?: string | string[]; - status?: TaskStatus | TaskStatus[]; - }; - pagination?: { - limit?: number; - offset?: number; - }; - order?: { - order: 'asc' | 'desc'; - field: string; - }[]; - }): Promise<{ - tasks: SerializedTask[]; - totalTasks?: number; - }>; - // (undocumented) - listEvents(options: TaskStoreListEventsOptions): Promise<{ - events: SerializedTaskEvent[]; - }>; - // (undocumented) - listStaleTasks(options: { timeoutS: number }): Promise<{ - tasks: { - taskId: string; - }[]; - }>; - // (undocumented) - recoverTasks?(options: TaskStoreRecoverTaskOptions): Promise<{ - ids: string[]; - }>; - // (undocumented) - rehydrateWorkspace?(options: { - taskId: string; - targetPath: string; - }): Promise; - // (undocumented) - retryTask?(options: { secrets?: TaskSecrets; taskId: string }): Promise; - // (undocumented) - saveTaskState?(options: { - taskId: string; - state?: JsonObject; - }): Promise; - // (undocumented) - serializeWorkspace?({ - path, - taskId, - }: { - path: string; - taskId: string; - }): Promise; - // (undocumented) - shutdownTask?(options: TaskStoreShutDownTaskOptions): Promise; -} - -// @public @deprecated -export type TaskStoreCreateTaskOptions = { - spec: TaskSpec; - createdBy?: string; - secrets?: TaskSecrets; -}; - -// @public @deprecated -export type TaskStoreCreateTaskResult = { - taskId: string; -}; - -// @public @deprecated -export type TaskStoreEmitOptions = { - taskId: string; - body: TBody; -}; - -// @public @deprecated -export type TaskStoreListEventsOptions = { - isTaskRecoverable?: boolean; - taskId: string; - after?: number | undefined; -}; - -// @public @deprecated -export type TaskStoreRecoverTaskOptions = { - timeout: HumanDuration; -}; - -// @public @deprecated -export type TaskStoreShutDownTaskOptions = { - taskId: string; -}; - -// @public @deprecated -export class TaskWorker { - // (undocumented) - static create(options: CreateWorkerOptions): Promise; - // (undocumented) - protected onReadyToClaimTask(): Promise; - // (undocumented) - recoverTasks(): Promise; - // (undocumented) - runOneTask(task: TaskContext): Promise; - // (undocumented) - start(): void; - // (undocumented) - stop(): Promise; -} - -// @public @deprecated -export class TemplateActionRegistry { - // (undocumented) - get(actionId: string): TemplateAction; - // (undocumented) - list(): TemplateAction[]; - // (undocumented) - register(action: TemplateAction): void; -} - // @public (undocumented) export type TemplatePermissionRuleInput< TParams extends PermissionRuleParams = PermissionRuleParams, diff --git a/plugins/scaffolder-backend/src/index.ts b/plugins/scaffolder-backend/src/index.ts index 4273ed8197..d911d51536 100644 --- a/plugins/scaffolder-backend/src/index.ts +++ b/plugins/scaffolder-backend/src/index.ts @@ -21,7 +21,9 @@ */ export { scaffolderPlugin as default } from './ScaffolderPlugin'; -export * from './scaffolder'; + +export * from './scaffolder/actions/builtin'; + export { type TemplatePermissionRuleInput, type ActionPermissionRuleInput, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts b/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts index c819d4c21f..d6d7685a21 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/TemplateActionRegistry.ts @@ -16,10 +16,9 @@ import { ConflictError, NotFoundError } from '@backstage/errors'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; + /** * Registry of all registered template actions. - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export class TemplateActionRegistry { private readonly actions = new Map(); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index fdcaa8613e..22ce3e83b7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -85,8 +85,6 @@ export type RawDbTaskEventRow = { /** * DatabaseTaskStore - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. - * @public */ export type DatabaseTaskStoreOptions = { database: DatabaseService | Knex; @@ -118,8 +116,6 @@ const parseSqlDateToIsoString = (input: T): T | string => { /** * DatabaseTaskStore - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. - * @public */ export class DatabaseTaskStore implements TaskStore { private readonly db: Knex; @@ -723,7 +719,7 @@ export class DatabaseTaskStore implements TaskStore { }); } - async retryTask?(options: { + async retryTask(options: { secrets?: TaskSecrets; taskId: string; }): Promise { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index d069a6bc1c..bcae93fb4d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -33,7 +33,7 @@ import { SecureTemplater, SecureTemplateRenderer, } from '../../lib/templating/SecureTemplater'; -import { TemplateActionRegistry } from '../actions'; +import { TemplateActionRegistry } from '../actions/TemplateActionRegistry'; import { generateExampleOutput, isTruthy } from './helper'; import { TaskTrackType, WorkflowResponse, WorkflowRunner } from './types'; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts index 5b4d568144..2c2c176770 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts @@ -50,8 +50,6 @@ type TaskState = { }; /** * TaskManager - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. - * @public */ export class TaskManager implements TaskContext { private isDone = false; @@ -488,7 +486,7 @@ export class StorageTaskBroker implements TaskBroker { }); } - async retry?(options: { + async retry(options: { secrets?: TaskSecrets; taskId: string; }): Promise { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index 9abe5f3ba6..adfdf0b553 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -25,7 +25,7 @@ import { TemplateGlobal, } from '@backstage/plugin-scaffolder-node'; import PQueue from 'p-queue'; -import { TemplateActionRegistry } from '../actions'; +import { TemplateActionRegistry } from '../actions/TemplateActionRegistry'; import { NunjucksWorkflowRunner } from './NunjucksWorkflowRunner'; import { WorkflowRunner } from './types'; import { setTimeout } from 'timers/promises'; @@ -36,8 +36,6 @@ const DEFAULT_TASK_PARAMETER_MAX_LENGTH = 256; /** * TaskWorkerOptions - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. - * @public */ export type TaskWorkerOptions = { taskBroker: TaskBroker; @@ -54,8 +52,6 @@ export type TaskWorkerOptions = { /** * CreateWorkerOptions - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. - * @public */ export type CreateWorkerOptions = { taskBroker: TaskBroker; @@ -86,8 +82,6 @@ export type CreateWorkerOptions = { /** * TaskWorker - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. - * @public */ export class TaskWorker { private taskQueue: PQueue; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index 62ffb171ff..874e32b838 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -30,8 +30,6 @@ import { PermissionCriteria } from '@backstage/plugin-permission-common'; /** * TaskStoreEmitOptions * - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export type TaskStoreEmitOptions = { taskId: string; @@ -41,8 +39,6 @@ export type TaskStoreEmitOptions = { /** * TaskStoreListEventsOptions * - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export type TaskStoreListEventsOptions = { isTaskRecoverable?: boolean; @@ -53,8 +49,6 @@ export type TaskStoreListEventsOptions = { /** * TaskStoreShutDownTaskOptions * - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export type TaskStoreShutDownTaskOptions = { taskId: string; @@ -62,8 +56,6 @@ export type TaskStoreShutDownTaskOptions = { /** * The options passed to {@link TaskStore.createTask} - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export type TaskStoreCreateTaskOptions = { spec: TaskSpec; @@ -73,8 +65,6 @@ export type TaskStoreCreateTaskOptions = { /** * The options passed to {@link TaskStore.recoverTasks} - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export type TaskStoreRecoverTaskOptions = { timeout: HumanDuration; @@ -82,8 +72,6 @@ export type TaskStoreRecoverTaskOptions = { /** * The response from {@link TaskStore.createTask} - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export type TaskStoreCreateTaskResult = { taskId: string; @@ -92,19 +80,17 @@ export type TaskStoreCreateTaskResult = { /** * TaskStore * - * @public - * @deprecated this type is deprecated, and there will be a new way to create Workers in the next major version. */ export interface TaskStore { - cancelTask?(options: TaskStoreEmitOptions): Promise; + cancelTask(options: TaskStoreEmitOptions): Promise; createTask( options: TaskStoreCreateTaskOptions, ): Promise; - retryTask?(options: { secrets?: TaskSecrets; taskId: string }): Promise; + retryTask(options: { secrets?: TaskSecrets; taskId: string }): Promise; - recoverTasks?( + recoverTasks( options: TaskStoreRecoverTaskOptions, ): Promise<{ ids: string[] }>; @@ -137,51 +123,31 @@ export interface TaskStore { order?: { order: 'asc' | 'desc'; field: string }[]; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }>; - /** - * @deprecated Make sure to pass `createdBy` and `status` in the `filters` parameter instead - */ - list?(options: { - createdBy?: string; - status?: TaskStatus; - filters?: { - createdBy?: string | string[]; - status?: TaskStatus | TaskStatus[]; - }; - pagination?: { - limit?: number; - offset?: number; - }; - order?: { order: 'asc' | 'desc'; field: string }[]; - }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }>; - emitLogEvent(options: TaskStoreEmitOptions): Promise; - getTaskState?({ taskId }: { taskId: string }): Promise< + getTaskState({ taskId }: { taskId: string }): Promise< | { state: JsonObject; } | undefined >; - saveTaskState?(options: { - taskId: string; - state?: JsonObject; - }): Promise; + saveTaskState(options: { taskId: string; state?: JsonObject }): Promise; listEvents( options: TaskStoreListEventsOptions, ): Promise<{ events: SerializedTaskEvent[] }>; - shutdownTask?(options: TaskStoreShutDownTaskOptions): Promise; + shutdownTask(options: TaskStoreShutDownTaskOptions): Promise; rehydrateWorkspace?(options: { taskId: string; targetPath: string; }): Promise; - cleanWorkspace?({ taskId }: { taskId: string }): Promise; + cleanWorkspace({ taskId }: { taskId: string }): Promise; - serializeWorkspace?({ + serializeWorkspace({ path, taskId, }: { diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index c97344867a..fa0163b245 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -356,7 +356,7 @@ export type SerializedTaskEvent = { // @public export interface TaskBroker { // (undocumented) - cancel?(taskId: string): Promise; + cancel(taskId: string): Promise; // (undocumented) claim(): Promise; // (undocumented) @@ -370,7 +370,7 @@ export interface TaskBroker { // (undocumented) get(taskId: string): Promise; // (undocumented) - list?(options?: { + list(options?: { filters?: { createdBy?: string | string[]; status?: TaskStatus | TaskStatus[]; @@ -388,15 +388,10 @@ export interface TaskBroker { tasks: SerializedTask[]; totalTasks?: number; }>; - // @deprecated (undocumented) - list?(options: { createdBy?: string; status?: TaskStatus }): Promise<{ - tasks: SerializedTask[]; - totalTasks?: number; - }>; // (undocumented) - recoverTasks?(): Promise; + recoverTasks(): Promise; // (undocumented) - retry?(options: { secrets?: TaskSecrets; taskId: string }): Promise; + retry(options: { secrets?: TaskSecrets; taskId: string }): Promise; // (undocumented) vacuumTasks(options: { timeoutS: number }): Promise; } diff --git a/plugins/scaffolder-node/src/tasks/types.ts b/plugins/scaffolder-node/src/tasks/types.ts index aaac468fce..fd80d547c1 100644 --- a/plugins/scaffolder-node/src/tasks/types.ts +++ b/plugins/scaffolder-node/src/tasks/types.ts @@ -177,13 +177,13 @@ export interface TaskContext { * @public */ export interface TaskBroker { - cancel?(taskId: string): Promise; + cancel(taskId: string): Promise; - retry?(options: { secrets?: TaskSecrets; taskId: string }): Promise; + retry(options: { secrets?: TaskSecrets; taskId: string }): Promise; claim(): Promise; - recoverTasks?(): Promise; + recoverTasks(): Promise; dispatch( options: TaskBrokerDispatchOptions, @@ -198,7 +198,7 @@ export interface TaskBroker { get(taskId: string): Promise; - list?(options?: { + list(options?: { filters?: { createdBy?: string | string[]; status?: TaskStatus | TaskStatus[]; @@ -210,12 +210,4 @@ export interface TaskBroker { order?: { order: 'asc' | 'desc'; field: string }[]; permissionFilters?: PermissionCriteria; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }>; - - /** - * @deprecated Make sure to pass `createdBy` and `status` in the `filters` parameter instead - */ - list?(options: { - createdBy?: string; - status?: TaskStatus; - }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }>; } From 9b81a90b91d7973660952489257eb231af992b96 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 23 Sep 2025 15:51:25 +0200 Subject: [PATCH 082/211] chore: changesets Signed-off-by: benjdlambert --- .changeset/ready-poems-change.md | 5 +++++ .changeset/wet-spiders-wait.md | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/ready-poems-change.md create mode 100644 .changeset/wet-spiders-wait.md diff --git a/.changeset/ready-poems-change.md b/.changeset/ready-poems-change.md new file mode 100644 index 0000000000..e716779b95 --- /dev/null +++ b/.changeset/ready-poems-change.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-node': minor +--- + +Making fields required diff --git a/.changeset/wet-spiders-wait.md b/.changeset/wet-spiders-wait.md new file mode 100644 index 0000000000..49cb2a56ac --- /dev/null +++ b/.changeset/wet-spiders-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': major +--- + +Removing the deprecated types and interfaces From 55b0b7e48e6b111d0ea547cc9a6886b8a8acdf80 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 23 Sep 2025 15:58:42 +0200 Subject: [PATCH 083/211] chore: update changeset for scaffolder-backend Signed-off-by: benjdlambert --- .changeset/wet-spiders-wait.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.changeset/wet-spiders-wait.md b/.changeset/wet-spiders-wait.md index 49cb2a56ac..6db9dfc1a1 100644 --- a/.changeset/wet-spiders-wait.md +++ b/.changeset/wet-spiders-wait.md @@ -2,4 +2,6 @@ '@backstage/plugin-scaffolder-backend': major --- -Removing the deprecated types and interfaces +Removing the deprecated types and interfaces, there's no replacement for these types, and hopefully not currently used as they offer no value with the plugin being on the new backend system and no way to consume them. + +Affected types: `CreateWorkerOptions`, `CurrentClaimedTask`, `DatabaseTaskStore`, `DatabaseTaskStoreOptions`, `TaskManager`, `TaskStore`, `TaskStoreCreateTaskOptions`, `TaskStoreCreateTaskResult`, `TaskStoreEmitOptions`, `TaskStoreListEventsOptions`, `TaskStoreRecoverTaskOptions`, `TaskStoreShutDownTaskOptions`, `TaskWorker` and `TemplateActionRegistry`. From 62e3e83bb44c39c76e9e3fe6ecb309babba24965 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 23 Sep 2025 16:01:55 +0200 Subject: [PATCH 084/211] chore: fix Signed-off-by: benjdlambert --- .changeset/ready-poems-change.md | 6 +++++- .changeset/wet-spiders-wait.md | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.changeset/ready-poems-change.md b/.changeset/ready-poems-change.md index e716779b95..fc95ddb99a 100644 --- a/.changeset/ready-poems-change.md +++ b/.changeset/ready-poems-change.md @@ -2,4 +2,8 @@ '@backstage/plugin-scaffolder-node': minor --- -Making fields required +**BREAKING** - Marking optional fields as required in the `TaskBroker`, these can be fixed with a no-op `() => void` if you don't want to implement the functions. + +- `cancel`, `recoverTasks` and `retry` are the required methods on the `TaskBroker` interface. + +**NOTE**: If you're affected by this breaking change, please reach out to us in an issue as we're thinking about completely removing the `TaskBroker` extension point soon and would like to hear your use cases for the upcoming re-architecture of the `scaffolder-backend` plugin. diff --git a/.changeset/wet-spiders-wait.md b/.changeset/wet-spiders-wait.md index 6db9dfc1a1..aaffc92013 100644 --- a/.changeset/wet-spiders-wait.md +++ b/.changeset/wet-spiders-wait.md @@ -2,6 +2,6 @@ '@backstage/plugin-scaffolder-backend': major --- -Removing the deprecated types and interfaces, there's no replacement for these types, and hopefully not currently used as they offer no value with the plugin being on the new backend system and no way to consume them. +**BREAKING** - Removing the deprecated types and interfaces, there's no replacement for these types, and hopefully not currently used as they offer no value with the plugin being on the new backend system and no way to consume them. Affected types: `CreateWorkerOptions`, `CurrentClaimedTask`, `DatabaseTaskStore`, `DatabaseTaskStoreOptions`, `TaskManager`, `TaskStore`, `TaskStoreCreateTaskOptions`, `TaskStoreCreateTaskResult`, `TaskStoreEmitOptions`, `TaskStoreListEventsOptions`, `TaskStoreRecoverTaskOptions`, `TaskStoreShutDownTaskOptions`, `TaskWorker` and `TemplateActionRegistry`. From 6e2bda78bd711adc097e39aa51ae8514b1a0bfce Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 23 Sep 2025 16:15:36 +0200 Subject: [PATCH 085/211] chore: moar deprecations Signed-off-by: benjdlambert --- .changeset/solid-bikes-leave.md | 9 ++++++++ plugins/scaffolder-node/report-alpha.api.md | 2 +- plugins/scaffolder-node/report.api.md | 22 +++++++++--------- plugins/scaffolder-node/src/alpha/index.ts | 1 + plugins/scaffolder-node/src/tasks/types.ts | 25 ++++++++++++++++++++- 5 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 .changeset/solid-bikes-leave.md diff --git a/.changeset/solid-bikes-leave.md b/.changeset/solid-bikes-leave.md new file mode 100644 index 0000000000..6f1bfe0b2f --- /dev/null +++ b/.changeset/solid-bikes-leave.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-scaffolder-node': patch +--- + +**DEPRECATION**: We're going to be working on refactoring a lot of the internals of the Scaffolder backend plugin, and with that comes a lot of deprecations and removals for public types that are making these things hard. + +If you're using these types, please reach out to us either on Discord or a GitHub issue with your use cases. + +- `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskContext`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskEventType`, `TaskFilter`, `TaskFilters`, `TaskStatus` are the types that have now been marked as deprecated, and will be removed in a future release. diff --git a/plugins/scaffolder-node/report-alpha.api.md b/plugins/scaffolder-node/report-alpha.api.md index 09541fbc56..c5ac6ce79d 100644 --- a/plugins/scaffolder-node/report-alpha.api.md +++ b/plugins/scaffolder-node/report-alpha.api.md @@ -147,7 +147,7 @@ export interface ScaffolderAutocompleteExtensionPoint { // @alpha export const scaffolderAutocompleteExtensionPoint: ExtensionPoint; -// @alpha +// @alpha @deprecated export interface ScaffolderTaskBrokerExtensionPoint { // (undocumented) setTaskBroker(taskBroker: TaskBroker): void; diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index fa0163b245..027d57ede4 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -327,7 +327,7 @@ export function serializeDirectoryContents( }, ): Promise; -// @public +// @public @deprecated export type SerializedTask = { id: string; spec: TaskSpec; @@ -339,7 +339,7 @@ export type SerializedTask = { state?: JsonObject; }; -// @public +// @public @deprecated export type SerializedTaskEvent = { id: number; isTaskRecoverable?: boolean; @@ -353,7 +353,7 @@ export type SerializedTaskEvent = { createdAt: string; }; -// @public +// @public @deprecated export interface TaskBroker { // (undocumented) cancel(taskId: string): Promise; @@ -396,22 +396,22 @@ export interface TaskBroker { vacuumTasks(options: { timeoutS: number }): Promise; } -// @public +// @public @deprecated export type TaskBrokerDispatchOptions = { spec: TaskSpec; secrets?: TaskSecrets; createdBy?: string; }; -// @public +// @public @deprecated export type TaskBrokerDispatchResult = { taskId: string; }; -// @public +// @public @deprecated export type TaskCompletionState = 'failed' | 'completed'; -// @public +// @public @deprecated export interface TaskContext { // (undocumented) cancelSignal: AbortSignal; @@ -455,16 +455,16 @@ export interface TaskContext { updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise; } -// @public +// @public @deprecated export type TaskEventType = 'completion' | 'log' | 'cancelled' | 'recovered'; -// @public +// @public @deprecated export type TaskFilter = { key: string; values?: string[]; }; -// @public +// @public @deprecated export type TaskFilters = | { anyOf: TaskFilter[]; @@ -482,7 +482,7 @@ export type TaskSecrets = Record & { backstageToken?: string; }; -// @public +// @public @deprecated export type TaskStatus = | 'cancelled' | 'completed' diff --git a/plugins/scaffolder-node/src/alpha/index.ts b/plugins/scaffolder-node/src/alpha/index.ts index 30f95f116a..2147085983 100644 --- a/plugins/scaffolder-node/src/alpha/index.ts +++ b/plugins/scaffolder-node/src/alpha/index.ts @@ -53,6 +53,7 @@ export const scaffolderActionsExtensionPoint = * Extension point for replacing the scaffolder task broker. * * @alpha + * @deprecated this extension point is planned to be removed, please reach out to us in an issue if you're using this extension point and your use cases. */ export interface ScaffolderTaskBrokerExtensionPoint { setTaskBroker(taskBroker: TaskBroker): void; diff --git a/plugins/scaffolder-node/src/tasks/types.ts b/plugins/scaffolder-node/src/tasks/types.ts index fd80d547c1..d6e4bb2dd9 100644 --- a/plugins/scaffolder-node/src/tasks/types.ts +++ b/plugins/scaffolder-node/src/tasks/types.ts @@ -33,6 +33,8 @@ export type TaskSecrets = Record & { * The status of each step of the Task * * @public + * @deprecated this type is planned to be removed. + * Please reach out to us in an issue if you're using this type and your use cases. */ export type TaskStatus = | 'cancelled' @@ -46,6 +48,8 @@ export type TaskStatus = * The state of a completed task. * * @public + * @deprecated this interface is planned to be removed. + * Please reach out to us in an issue if you're using this interface and your use cases. */ export type TaskCompletionState = 'failed' | 'completed'; @@ -53,6 +57,8 @@ export type TaskCompletionState = 'failed' | 'completed'; * SerializedTask * * @public + * @deprecated this type is planned to be removed. + * Please reach out to us in an issue if you're using this type and your use cases. */ export type SerializedTask = { id: string; @@ -69,6 +75,8 @@ export type SerializedTask = { * TaskEventType * * @public + * @deprecated this type is planned to be removed. + * Please reach out to us in an issue if you're using this type and your use cases. */ export type TaskEventType = 'completion' | 'log' | 'cancelled' | 'recovered'; @@ -76,6 +84,8 @@ export type TaskEventType = 'completion' | 'log' | 'cancelled' | 'recovered'; * SerializedTaskEvent * * @public + * @deprecated this type is planned to be removed. + * Please reach out to us in an issue if you're using this type and your use cases. */ export type SerializedTaskEvent = { id: number; @@ -94,6 +104,8 @@ export type SerializedTaskEvent = { * The result of {@link TaskBroker.dispatch} * * @public + * @deprecated this interface is planned to be removed. + * Please reach out to us in an issue if you're using this interface and your use cases. */ export type TaskBrokerDispatchResult = { taskId: string; @@ -104,6 +116,8 @@ export type TaskBrokerDispatchResult = { * Currently a spec and optional secrets * * @public + * @deprecated this interface is planned to be removed. + * Please reach out to us in an issue if you're using this interface and your use cases. */ export type TaskBrokerDispatchOptions = { spec: TaskSpec; @@ -114,6 +128,8 @@ export type TaskBrokerDispatchOptions = { /** * TaskFilter * @public + * @deprecated this type is planned to be removed. + * Please reach out to us in an issue if you're using this type and your use cases. */ export type TaskFilter = { key: string; @@ -123,6 +139,8 @@ export type TaskFilter = { /** * TaskFilters * @public + * @deprecated this type is planned to be removed. + * Please reach out to us in an issue if you're using this type and your use cases. */ export type TaskFilters = | { anyOf: TaskFilter[] } @@ -131,9 +149,12 @@ export type TaskFilters = | TaskFilter; /** - * Task + * TaskContext * * @public + * + * @deprecated this interface is planned to be removed. + * Please reach out to us in an issue if you're using this interface and your use cases. */ export interface TaskContext { taskId?: string; @@ -175,6 +196,8 @@ export interface TaskContext { * TaskBroker * * @public + * @deprecated this interface is planned to be removed. + * Please reach out to us in an issue if you're using this interface and your use cases. */ export interface TaskBroker { cancel(taskId: string): Promise; From 236d42720d56cafe31927a244fd48e72028152c6 Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Tue, 23 Sep 2025 15:25:31 +0100 Subject: [PATCH 086/211] fixup! remove fallback number parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Beth Griggs --- .../rootHttpRouter/rootHttpRouterServiceFactory.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts index 1bbf986a98..b0cc9d160e 100644 --- a/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts @@ -146,15 +146,7 @@ const rootHttpRouterServiceFactoryWithOptions = ( `ISO duration (e.g., 'PT30S'), or duration object (e.g., {seconds: 30}). ` + `Falling back to number parsing.`, ); - // Fallback to reading as number if duration parsing fails - const fallbackValue = serverConfig.getOptionalNumber(key); - if (fallbackValue === undefined && typeof value === 'string') { - logger.error( - `backend.server.${key} value '${value}' could not be parsed as either ` + - `a duration or a number. This setting will be ignored.`, - ); - } - return fallbackValue; + return undefined; } }; From c8aa21077c765901e3a2b1e69d7cacbbf8a5fad9 Mon Sep 17 00:00:00 2001 From: benjdlambert Date: Tue, 23 Sep 2025 16:38:54 +0200 Subject: [PATCH 087/211] chore: last bits Signed-off-by: benjdlambert --- .changeset/legal-eagles-jog.md | 18 ++++++++++ .changeset/red-times-bet.md | 13 +++++++ .../src/module.ts | 2 +- .../src/module.ts | 6 ++-- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/module.ts | 6 ++-- .../src/module.ts | 6 ++-- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/module.ts | 2 +- .../src/ScaffolderPlugin.ts | 7 ++-- plugins/scaffolder-node/report-alpha.api.md | 12 +------ plugins/scaffolder-node/report.api.md | 10 ++++++ plugins/scaffolder-node/src/alpha/index.ts | 21 +---------- plugins/scaffolder-node/src/extensions.ts | 36 +++++++++++++++++++ plugins/scaffolder-node/src/index.ts | 1 + 22 files changed, 102 insertions(+), 56 deletions(-) create mode 100644 .changeset/legal-eagles-jog.md create mode 100644 .changeset/red-times-bet.md create mode 100644 plugins/scaffolder-node/src/extensions.ts diff --git a/.changeset/legal-eagles-jog.md b/.changeset/legal-eagles-jog.md new file mode 100644 index 0000000000..5b520af91f --- /dev/null +++ b/.changeset/legal-eagles-jog.md @@ -0,0 +1,18 @@ +--- +'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch +'@backstage/plugin-scaffolder-backend-module-notifications': patch +'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket': patch +'@backstage/plugin-scaffolder-backend-module-gerrit': patch +'@backstage/plugin-scaffolder-backend-module-github': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-sentry': patch +'@backstage/plugin-scaffolder-backend-module-yeoman': patch +'@backstage/plugin-scaffolder-backend-module-azure': patch +'@backstage/plugin-scaffolder-backend-module-gitea': patch +'@backstage/plugin-scaffolder-backend-module-rails': patch +--- + +Updating import for the `scaffolderActionsExtensionPoint` to be the main export diff --git a/.changeset/red-times-bet.md b/.changeset/red-times-bet.md new file mode 100644 index 0000000000..29e89fb07f --- /dev/null +++ b/.changeset/red-times-bet.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-scaffolder-node': patch +--- + +**BREAKING ALPHA**: We've moved the `scaffolderActionsExtensionPoint` from `/alpha` to the main export. + +```tsx +// before +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + +// after +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; +``` diff --git a/plugins/scaffolder-backend-module-azure/src/module.ts b/plugins/scaffolder-backend-module-azure/src/module.ts index 40ca3f0978..937d806932 100644 --- a/plugins/scaffolder-backend-module-azure/src/module.ts +++ b/plugins/scaffolder-backend-module-azure/src/module.ts @@ -18,7 +18,7 @@ import { coreServices, } from '@backstage/backend-plugin-api'; import { ScmIntegrations } from '@backstage/integration'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createPublishAzureAction } from './actions'; /** diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts index 4001c064da..04ac3b7a56 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/module.ts @@ -17,10 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { - scaffolderActionsExtensionPoint, - scaffolderAutocompleteExtensionPoint, -} from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderAutocompleteExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; import { createBitbucketCloudBranchRestrictionAction } from './actions/bitbucketCloudBranchRestriction'; import { createBitbucketPipelinesRunAction, @@ -29,6 +26,7 @@ import { } from './actions'; import { ScmIntegrations } from '@backstage/integration'; import { handleAutocompleteRequest } from './autocomplete/autocomplete'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; /** * @public diff --git a/plugins/scaffolder-backend-module-bitbucket-server/src/module.ts b/plugins/scaffolder-backend-module-bitbucket-server/src/module.ts index d3d457cb1f..21159759d4 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/src/module.ts +++ b/plugins/scaffolder-backend-module-bitbucket-server/src/module.ts @@ -17,7 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createPublishBitbucketServerAction, createPublishBitbucketServerPullRequestAction, diff --git a/plugins/scaffolder-backend-module-bitbucket/src/module.ts b/plugins/scaffolder-backend-module-bitbucket/src/module.ts index db27d0b7b9..884c8be004 100644 --- a/plugins/scaffolder-backend-module-bitbucket/src/module.ts +++ b/plugins/scaffolder-backend-module-bitbucket/src/module.ts @@ -17,7 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createBitbucketPipelinesRunAction, createPublishBitbucketCloudAction, diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/module.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/module.ts index 402f56c951..3b18ef8294 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/src/module.ts +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/module.ts @@ -17,7 +17,7 @@ import { createBackendModule, coreServices, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createConfluenceToMarkdownAction } from './actions'; import { ScmIntegrations } from '@backstage/integration'; diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/module.ts b/plugins/scaffolder-backend-module-cookiecutter/src/module.ts index df1b201518..3caa9259f2 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/src/module.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/module.ts @@ -17,7 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createFetchCookiecutterAction } from './actions'; import { ScmIntegrations } from '@backstage/integration'; diff --git a/plugins/scaffolder-backend-module-gerrit/src/module.ts b/plugins/scaffolder-backend-module-gerrit/src/module.ts index 32607431f3..f0bbed9649 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/module.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/module.ts @@ -17,7 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createPublishGerritAction, createPublishGerritReviewAction, diff --git a/plugins/scaffolder-backend-module-gitea/src/module.ts b/plugins/scaffolder-backend-module-gitea/src/module.ts index 658f240471..b087e1637e 100644 --- a/plugins/scaffolder-backend-module-gitea/src/module.ts +++ b/plugins/scaffolder-backend-module-gitea/src/module.ts @@ -17,7 +17,7 @@ import { createBackendModule, coreServices, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createPublishGiteaAction } from './actions'; import { ScmIntegrations } from '@backstage/integration'; diff --git a/plugins/scaffolder-backend-module-github/src/module.ts b/plugins/scaffolder-backend-module-github/src/module.ts index 67bda58fc4..964bc018ae 100644 --- a/plugins/scaffolder-backend-module-github/src/module.ts +++ b/plugins/scaffolder-backend-module-github/src/module.ts @@ -17,10 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { - scaffolderActionsExtensionPoint, - scaffolderAutocompleteExtensionPoint, -} from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderAutocompleteExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; import { createGithubActionsDispatchAction, createGithubAutolinksAction, @@ -42,6 +39,7 @@ import { } from '@backstage/integration'; import { createHandleAutocompleteRequest } from './autocomplete/autocomplete'; import { catalogServiceRef } from '@backstage/plugin-catalog-node'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; /** * @public diff --git a/plugins/scaffolder-backend-module-gitlab/src/module.ts b/plugins/scaffolder-backend-module-gitlab/src/module.ts index 7dbfddfc1b..3c04f57077 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/module.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/module.ts @@ -18,10 +18,7 @@ import { createBackendModule, } from '@backstage/backend-plugin-api'; import { ScmIntegrations } from '@backstage/integration'; -import { - scaffolderActionsExtensionPoint, - scaffolderAutocompleteExtensionPoint, -} from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderAutocompleteExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; import { createGitlabGroupEnsureExistsAction, createGitlabIssueAction, @@ -36,6 +33,7 @@ import { } from './actions'; import { createGitlabProjectMigrateAction } from './actions/gitlabProjectMigrate'; import { createHandleAutocompleteRequest } from './autocomplete/autocomplete'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; /** * @public diff --git a/plugins/scaffolder-backend-module-notifications/src/module.ts b/plugins/scaffolder-backend-module-notifications/src/module.ts index e8b6823e99..4639175229 100644 --- a/plugins/scaffolder-backend-module-notifications/src/module.ts +++ b/plugins/scaffolder-backend-module-notifications/src/module.ts @@ -15,7 +15,7 @@ */ import { createBackendModule } from '@backstage/backend-plugin-api'; import { notificationService } from '@backstage/plugin-notifications-node'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createSendNotificationAction } from './actions'; /** diff --git a/plugins/scaffolder-backend-module-rails/src/module.ts b/plugins/scaffolder-backend-module-rails/src/module.ts index ec70610cd3..eb14c47977 100644 --- a/plugins/scaffolder-backend-module-rails/src/module.ts +++ b/plugins/scaffolder-backend-module-rails/src/module.ts @@ -17,7 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createFetchRailsAction } from './actions'; import { ScmIntegrations } from '@backstage/integration'; diff --git a/plugins/scaffolder-backend-module-sentry/src/module.ts b/plugins/scaffolder-backend-module-sentry/src/module.ts index dcde344262..2b343edbc7 100644 --- a/plugins/scaffolder-backend-module-sentry/src/module.ts +++ b/plugins/scaffolder-backend-module-sentry/src/module.ts @@ -17,7 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createSentryCreateProjectAction } from './actions/createProject'; /** diff --git a/plugins/scaffolder-backend-module-yeoman/src/module.ts b/plugins/scaffolder-backend-module-yeoman/src/module.ts index 1fdbda28a3..0564d69369 100644 --- a/plugins/scaffolder-backend-module-yeoman/src/module.ts +++ b/plugins/scaffolder-backend-module-yeoman/src/module.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { createBackendModule } from '@backstage/backend-plugin-api'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; import { createRunYeomanAction } from './actions'; /** diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index a00da9e524..39bab85c7e 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -21,7 +21,11 @@ import { import { ScmIntegrations } from '@backstage/integration'; import { catalogServiceRef } from '@backstage/plugin-catalog-node'; import { eventsServiceRef } from '@backstage/plugin-events-node'; -import { TaskBroker, TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + scaffolderActionsExtensionPoint, + TaskBroker, + TemplateAction, +} from '@backstage/plugin-scaffolder-node'; import { AutocompleteHandler, CreatedTemplateFilter, @@ -29,7 +33,6 @@ import { createTemplateFilter, createTemplateGlobalFunction, createTemplateGlobalValue, - scaffolderActionsExtensionPoint, scaffolderAutocompleteExtensionPoint, scaffolderTaskBrokerExtensionPoint, scaffolderTemplatingExtensionPoint, diff --git a/plugins/scaffolder-node/report-alpha.api.md b/plugins/scaffolder-node/report-alpha.api.md index c5ac6ce79d..e1d49f571a 100644 --- a/plugins/scaffolder-node/report-alpha.api.md +++ b/plugins/scaffolder-node/report-alpha.api.md @@ -6,7 +6,6 @@ import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { JsonValue } from '@backstage/types'; import { TaskBroker } from '@backstage/plugin-scaffolder-node'; -import { TemplateAction } from '@backstage/plugin-scaffolder-node'; import { TemplateFilter as TemplateFilter_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateGlobal as TemplateGlobal_2 } from '@backstage/plugin-scaffolder-node'; import { z } from 'zod'; @@ -123,15 +122,6 @@ export const restoreWorkspace: (opts: { buffer?: Buffer; }) => Promise; -// @alpha -export interface ScaffolderActionsExtensionPoint { - // (undocumented) - addActions(...actions: TemplateAction[]): void; -} - -// @alpha -export const scaffolderActionsExtensionPoint: ExtensionPoint; - // @alpha export interface ScaffolderAutocompleteExtensionPoint { // (undocumented) @@ -153,7 +143,7 @@ export interface ScaffolderTaskBrokerExtensionPoint { setTaskBroker(taskBroker: TaskBroker): void; } -// @alpha +// @alpha @deprecated export const scaffolderTaskBrokerExtensionPoint: ExtensionPoint; // @alpha diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index 027d57ede4..7f70c9d786 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -6,6 +6,7 @@ import { BackstageCredentials } from '@backstage/backend-plugin-api'; import { CheckpointContext } from '@backstage/plugin-scaffolder-node/alpha'; import { Expand } from '@backstage/types'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { LoggerService } from '@backstage/backend-plugin-api'; @@ -306,6 +307,15 @@ export const parseRepoUrl: ( project?: string; }; +// @public +export interface ScaffolderActionsExtensionPoint { + // (undocumented) + addActions(...actions: TemplateAction[]): void; +} + +// @public +export const scaffolderActionsExtensionPoint: ExtensionPoint; + // @public (undocumented) export interface SerializedFile { // (undocumented) diff --git a/plugins/scaffolder-node/src/alpha/index.ts b/plugins/scaffolder-node/src/alpha/index.ts index 2147085983..9d0b9efafd 100644 --- a/plugins/scaffolder-node/src/alpha/index.ts +++ b/plugins/scaffolder-node/src/alpha/index.ts @@ -17,7 +17,6 @@ import { createExtensionPoint } from '@backstage/backend-plugin-api'; import { TaskBroker, - TemplateAction, TemplateFilter, TemplateGlobal, } from '@backstage/plugin-scaffolder-node'; @@ -30,25 +29,6 @@ export * from './globals'; export * from './types'; export * from './checkpoints'; -/** - * Extension point for managing scaffolder actions. - * - * @alpha - */ -export interface ScaffolderActionsExtensionPoint { - addActions(...actions: TemplateAction[]): void; -} - -/** - * Extension point for managing scaffolder actions. - * - * @alpha - */ -export const scaffolderActionsExtensionPoint = - createExtensionPoint({ - id: 'scaffolder.actions', - }); - /** * Extension point for replacing the scaffolder task broker. * @@ -63,6 +43,7 @@ export interface ScaffolderTaskBrokerExtensionPoint { * Extension point for replacing the scaffolder task broker. * * @alpha + * @deprecated this extension point is planned to be removed, please reach out to us in an issue if you're using this extension point and your use cases. */ export const scaffolderTaskBrokerExtensionPoint = createExtensionPoint({ diff --git a/plugins/scaffolder-node/src/extensions.ts b/plugins/scaffolder-node/src/extensions.ts new file mode 100644 index 0000000000..94b366fceb --- /dev/null +++ b/plugins/scaffolder-node/src/extensions.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2025 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 { TemplateAction } from './actions'; + +/** + * Extension point for managing scaffolder actions. + * + * @public + */ +export interface ScaffolderActionsExtensionPoint { + addActions(...actions: TemplateAction[]): void; +} + +/** + * Extension point for managing scaffolder actions. + * + * @public + */ +export const scaffolderActionsExtensionPoint = + createExtensionPoint({ + id: 'scaffolder.actions', + }); diff --git a/plugins/scaffolder-node/src/index.ts b/plugins/scaffolder-node/src/index.ts index 5d49ee874c..a9aa77ba4e 100644 --- a/plugins/scaffolder-node/src/index.ts +++ b/plugins/scaffolder-node/src/index.ts @@ -24,3 +24,4 @@ export * from './actions'; export * from './tasks'; export * from './files'; export * from './types'; +export * from './extensions'; From c73bfa46d48fe995f988e211e6dfd35f223af418 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 23 Sep 2025 15:00:29 +0000 Subject: [PATCH 088/211] Version Packages (next) --- .changeset/create-app-1758639549.md | 5 + .changeset/pre.json | 30 +- docs/releases/v1.44.0-next.0-changelog.md | 1578 +++++++++++++++++ package.json | 2 +- packages/app-defaults/CHANGELOG.md | 11 + 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 | 47 + packages/app-next/package.json | 2 +- packages/app/CHANGELOG.md | 42 + packages/app/package.json | 2 +- packages/backend-defaults/CHANGELOG.md | 30 + packages/backend-defaults/package.json | 2 +- .../CHANGELOG.md | 25 + .../package.json | 2 +- packages/backend-test-utils/CHANGELOG.md | 15 + packages/backend-test-utils/package.json | 2 +- packages/backend/CHANGELOG.md | 41 + packages/backend/package.json | 2 +- packages/cli/CHANGELOG.md | 17 + packages/cli/package.json | 2 +- packages/core-compat-api/CHANGELOG.md | 10 + packages/core-compat-api/package.json | 2 +- packages/core-components/CHANGELOG.md | 13 + packages/core-components/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 | 14 + packages/frontend-app-api/package.json | 2 +- packages/frontend-defaults/CHANGELOG.md | 12 + packages/frontend-defaults/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- packages/frontend-internal/CHANGELOG.md | 9 + packages/frontend-internal/package.json | 2 +- packages/frontend-plugin-api/CHANGELOG.md | 10 + packages/frontend-plugin-api/package.json | 2 +- packages/frontend-test-utils/CHANGELOG.md | 13 + packages/frontend-test-utils/package.json | 2 +- packages/integration-react/CHANGELOG.md | 9 + packages/integration-react/package.json | 2 +- packages/integration/CHANGELOG.md | 10 + packages/integration/package.json | 2 +- packages/scaffolder-internal/CHANGELOG.md | 8 + packages/scaffolder-internal/package.json | 2 +- .../techdocs-cli-embedded-app/CHANGELOG.md | 19 + .../techdocs-cli-embedded-app/package.json | 2 +- packages/techdocs-cli/CHANGELOG.md | 11 + packages/techdocs-cli/package.json | 2 +- packages/ui/CHANGELOG.md | 7 + packages/ui/package.json | 2 +- plugins/api-docs/CHANGELOG.md | 27 + plugins/api-docs/package.json | 2 +- plugins/app-visualizer/CHANGELOG.md | 9 + plugins/app-visualizer/package.json | 2 +- plugins/app/CHANGELOG.md | 14 + plugins/app/package.json | 2 +- plugins/auth-react/CHANGELOG.md | 9 + plugins/auth-react/package.json | 2 +- plugins/auth/CHANGELOG.md | 10 + plugins/auth/package.json | 2 +- plugins/bitbucket-cloud-common/CHANGELOG.md | 8 + plugins/bitbucket-cloud-common/package.json | 2 +- .../catalog-backend-module-aws/CHANGELOG.md | 16 + .../catalog-backend-module-aws/package.json | 2 +- .../catalog-backend-module-azure/CHANGELOG.md | 12 + .../catalog-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../catalog-backend-module-gcp/CHANGELOG.md | 11 + .../catalog-backend-module-gcp/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../catalog-backend-module-gitea/CHANGELOG.md | 12 + .../catalog-backend-module-gitea/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- .../catalog-backend-module-logs/CHANGELOG.md | 9 + .../catalog-backend-module-logs/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- plugins/catalog-backend/CHANGELOG.md | 43 + plugins/catalog-backend/package.json | 2 +- plugins/catalog-graph/CHANGELOG.md | 16 + plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/CHANGELOG.md | 18 + plugins/catalog-import/package.json | 2 +- plugins/catalog-react/CHANGELOG.md | 21 + plugins/catalog-react/package.json | 2 +- .../catalog-unprocessed-entities/CHANGELOG.md | 12 + .../catalog-unprocessed-entities/package.json | 2 +- plugins/catalog/CHANGELOG.md | 24 + plugins/catalog/package.json | 2 +- plugins/config-schema/CHANGELOG.md | 10 + plugins/config-schema/package.json | 2 +- plugins/devtools-backend/CHANGELOG.md | 16 + plugins/devtools-backend/package.json | 2 +- plugins/devtools/CHANGELOG.md | 13 + plugins/devtools/package.json | 2 +- .../events-backend-module-github/CHANGELOG.md | 11 + .../events-backend-module-github/package.json | 2 +- plugins/example-todo-list/CHANGELOG.md | 8 + plugins/example-todo-list/package.json | 2 +- plugins/home-react/CHANGELOG.md | 9 + plugins/home-react/package.json | 2 +- plugins/home/CHANGELOG.md | 17 + plugins/home/package.json | 2 +- plugins/kubernetes-backend/CHANGELOG.md | 19 + plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes-cluster/CHANGELOG.md | 13 + plugins/kubernetes-cluster/package.json | 2 +- plugins/kubernetes-common/CHANGELOG.md | 10 + plugins/kubernetes-common/package.json | 2 +- plugins/kubernetes-node/CHANGELOG.md | 10 + plugins/kubernetes-node/package.json | 2 +- plugins/kubernetes-react/CHANGELOG.md | 12 + plugins/kubernetes-react/package.json | 2 +- plugins/kubernetes/CHANGELOG.md | 15 + plugins/kubernetes/package.json | 2 +- plugins/mcp-actions-backend/CHANGELOG.md | 12 + plugins/mcp-actions-backend/package.json | 2 +- plugins/notifications/CHANGELOG.md | 15 + plugins/notifications/package.json | 2 +- plugins/org-react/CHANGELOG.md | 11 + plugins/org-react/package.json | 2 +- plugins/org/CHANGELOG.md | 13 + plugins/org/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- plugins/scaffolder-backend/CHANGELOG.md | 37 + plugins/scaffolder-backend/package.json | 2 +- plugins/scaffolder-common/CHANGELOG.md | 11 + plugins/scaffolder-common/package.json | 2 +- .../scaffolder-node-test-utils/CHANGELOG.md | 10 + .../scaffolder-node-test-utils/package.json | 2 +- plugins/scaffolder-node/CHANGELOG.md | 37 + plugins/scaffolder-node/package.json | 2 +- plugins/scaffolder-react/CHANGELOG.md | 17 + plugins/scaffolder-react/package.json | 2 +- plugins/scaffolder/CHANGELOG.md | 24 + plugins/scaffolder/package.json | 2 +- plugins/search-backend-module-pg/CHANGELOG.md | 12 + plugins/search-backend-module-pg/package.json | 2 +- .../CHANGELOG.md | 16 + .../package.json | 2 +- plugins/search-backend/CHANGELOG.md | 16 + 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/signals/CHANGELOG.md | 13 + plugins/signals/package.json | 2 +- .../techdocs-addons-test-utils/CHANGELOG.md | 15 + .../techdocs-addons-test-utils/package.json | 2 +- plugins/techdocs-backend/CHANGELOG.md | 20 + plugins/techdocs-backend/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- plugins/techdocs-node/CHANGELOG.md | 14 + plugins/techdocs-node/package.json | 2 +- plugins/techdocs-react/CHANGELOG.md | 13 + plugins/techdocs-react/package.json | 2 +- plugins/techdocs/CHANGELOG.md | 23 + plugins/techdocs/package.json | 2 +- plugins/user-settings-backend/CHANGELOG.md | 13 + plugins/user-settings-backend/package.json | 2 +- plugins/user-settings/CHANGELOG.md | 18 + plugins/user-settings/package.json | 2 +- 214 files changed, 3291 insertions(+), 107 deletions(-) create mode 100644 .changeset/create-app-1758639549.md create mode 100644 docs/releases/v1.44.0-next.0-changelog.md diff --git a/.changeset/create-app-1758639549.md b/.changeset/create-app-1758639549.md new file mode 100644 index 0000000000..b50d431d4b --- /dev/null +++ b/.changeset/create-app-1758639549.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped create-app version. diff --git a/.changeset/pre.json b/.changeset/pre.json index 12e4285e7c..7003364d39 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -205,5 +205,33 @@ "@backstage/plugin-user-settings-backend": "0.3.6", "@backstage/plugin-user-settings-common": "0.0.1" }, - "changesets": [] + "changesets": [ + "brave-teeth-reply", + "brown-falcons-own", + "calm-trains-tie", + "clever-papers-watch", + "cold-coats-show", + "cool-baboons-count", + "create-app-1758639549", + "eager-toes-start", + "fast-heads-brake", + "fast-queens-guess", + "five-olives-bet", + "hungry-crews-fetch", + "itchy-falcons-leave", + "kind-places-reply", + "legal-eagles-jog", + "nice-readers-judge", + "public-sites-admire", + "rare-states-pay", + "ready-poems-change", + "ready-pots-arrive", + "red-times-bet", + "salty-words-wash", + "short-aliens-invite", + "solid-bikes-leave", + "tame-hairs-smash", + "tender-cups-tap", + "wet-spiders-wait" + ] } diff --git a/docs/releases/v1.44.0-next.0-changelog.md b/docs/releases/v1.44.0-next.0-changelog.md new file mode 100644 index 0000000000..601ea2b234 --- /dev/null +++ b/docs/releases/v1.44.0-next.0-changelog.md @@ -0,0 +1,1578 @@ +# Release v1.44.0-next.0 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.44.0-next.0](https://backstage.github.io/upgrade-helper/?to=1.44.0-next.0) + +## @backstage/plugin-scaffolder-backend@3.0.0-next.0 + +### Major Changes + +- 9b81a90: **BREAKING** - Removing the deprecated types and interfaces, there's no replacement for these types, and hopefully not currently used as they offer no value with the plugin being on the new backend system and no way to consume them. + + Affected types: `CreateWorkerOptions`, `CurrentClaimedTask`, `DatabaseTaskStore`, `DatabaseTaskStoreOptions`, `TaskManager`, `TaskStore`, `TaskStoreCreateTaskOptions`, `TaskStoreCreateTaskResult`, `TaskStoreEmitOptions`, `TaskStoreListEventsOptions`, `TaskStoreRecoverTaskOptions`, `TaskStoreShutDownTaskOptions`, `TaskWorker` and `TemplateActionRegistry`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.14-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.3-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.0 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/backend-defaults@0.13.0-next.0 + +### Minor Changes + +- 2d3e2b2: implement support for direct url for AzureBlobStorageUrlReader search function +- 8b91238: Adds support for configuring server-level HTTP options through the + `app-config.yaml` file under the `backend.server` key. Supported options + include `headersTimeout`, `keepAliveTimeout`, `requestTimeout`, `timeout`, + `maxHeadersCount`, and `maxRequestsPerSocket`. + + These are passed directly to the underlying Node.js HTTP server. + If omitted, Node.js defaults are used. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-app-api@1.2.7 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/cli-node@0.2.14 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-api-docs@0.13.0-next.0 + +### Minor Changes + +- b8a381e: Remove explicit dependency on `isomorphic-form-data`. + + This explicit dependency was added to address [an issue](https://github.com/swagger-api/swagger-ui/issues/7436) in the + dependency `swagger-ui-react`. That [issue has since been resolved](https://github.com/swagger-api/swagger-ui/issues/7436#issuecomment-889792304), + and `isomorphic-form-data` no longer needs to be declared. + + Additionally, this changeset updates the `swagger-ui-react` dependency to version `5.19.0` or higher, which includes + [compatibility](https://github.com/swagger-api/swagger-ui?tab=readme-ov-file#compatibility) with the latest versions of + the OpenAPI specification. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/plugin-scaffolder-node@0.12.0-next.0 + +### Minor Changes + +- 9b81a90: **BREAKING** - Marking optional fields as required in the `TaskBroker`, these can be fixed with a no-op `() => void` if you don't want to implement the functions. + + - `cancel`, `recoverTasks` and `retry` are the required methods on the `TaskBroker` interface. + + **NOTE**: If you're affected by this breaking change, please reach out to us in an issue as we're thinking about completely removing the `TaskBroker` extension point soon and would like to hear your use cases for the upcoming re-architecture of the `scaffolder-backend` plugin. + +### Patch Changes + +- c8aa210: **BREAKING ALPHA**: We've moved the `scaffolderActionsExtensionPoint` from `/alpha` to the main export. + + ```tsx + // before + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + + // after + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; + ``` + +- 6e2bda7: **DEPRECATION**: We're going to be working on refactoring a lot of the internals of the Scaffolder backend plugin, and with that comes a lot of deprecations and removals for public types that are making these things hard. + + If you're using these types, please reach out to us either on Discord or a GitHub issue with your use cases. + + - `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskContext`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskEventType`, `TaskFilter`, `TaskFilters`, `TaskStatus` are the types that have now been marked as deprecated, and will be removed in a future release. + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/app-defaults@1.7.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/backend-dynamic-feature-service@0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-events-backend@0.5.6 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.14 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.37 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-common@1.2.19 + +## @backstage/backend-test-utils@1.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/backend-app-api@1.2.7 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/cli@0.34.4-next.0 + +### Patch Changes + +- 6ebc1ea: Fixed module federation config by only setting `import: false` on shared libraries for remote. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.14 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.11 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.2 + +## @backstage/core-compat-api@0.5.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/version-bridge@1.0.11 + +## @backstage/core-components@0.18.2-next.0 + +### Patch Changes + +- d493126: Swap base token for semantic token in ItemCardHeader to ensure readability in light mode. +- 6981ae6: Fixed DependencyGraph `svg` size not adapting to the container size +- Updated dependencies + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/version-bridge@1.0.11 + +## @backstage/create-app@0.7.5-next.0 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## @backstage/dev-utils@1.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + +## @backstage/frontend-app-api@0.13.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-defaults@0.3.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/frontend-defaults@0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-app@0.3.1-next.0 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-app-api@0.13.1-next.0 + +## @backstage/frontend-dynamic-feature-loader@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/config@1.3.3 + +## @backstage/frontend-plugin-api@0.12.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/frontend-test-utils@0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-app@0.3.1-next.0 + - @backstage/config@1.3.3 + - @backstage/frontend-app-api@0.13.1-next.0 + - @backstage/test-utils@1.7.11 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/integration@1.18.1-next.0 + +### Patch Changes + +- d772b51: remove host from azure blob storage integration type +- 84443f1: Adds config definitions for Azure Blob Storage. +- Updated dependencies + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/integration-react@1.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + +## @techdocs/cli@1.9.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-techdocs-node@1.13.8-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.3 + +## @backstage/ui@0.7.2-next.0 + +### Patch Changes + +- 827340f: remove default selection of tab +- 9a47125: Improved SearchField component flex layout and animations. Fixed SearchField behavior in Header components by switching from width-based transitions to flex-basis transitions for better responsive behavior. Added new Storybook stories to test SearchField integration with Header component. + +## @backstage/plugin-app@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/plugin-app-visualizer@0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + +## @backstage/plugin-auth@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + +## @backstage/plugin-auth-react@0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-bitbucket-cloud-common@0.3.3-next.0 + +### Patch Changes + +- 2aded73: Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + +## @backstage/plugin-catalog@1.31.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-catalog-backend@3.1.1-next.0 + +### Patch Changes + +- 9890488: Internal refactor to remove remnants of the old backend system + +- 2aaf01a: Fix for duplicate search results in entity-facets API call + +- e489661: Moved catalog processor and provider disabling and priorities under own config objects. + + This is due to issue with some existing providers, such as GitHub, using array syntax for the provider configuration. + + The new config format is not backwards compatible, so users will need to update their config files. The new format + is as follows: + + ```yaml + catalog: + providerOptions: + providerA: + disabled: false + providerB: + disabled: true + processorOptions: + processorA: + disabled: false + priority: 10 + processorB: + disabled: true + ``` + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/plugin-catalog-backend-module-aws@0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-catalog-backend-module-azure@0.3.10-next.0 + +### Patch Changes + +- 84443f1: Adds config definitions for Azure Blob Storage. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.4-next.0 + +### Patch Changes + +- 2aded73: Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.3-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-catalog-backend-module-gitea@0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-catalog-backend-module-github@0.11.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.11.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/plugin-catalog-backend-module-logs@0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-catalog-graph@0.5.2-next.0 + +### Patch Changes + +- 87b5e6e: Add missing API implementation for catalog graph plugin in NFS apps. +- 6981ae6: Fixed DependencyGraph `svg` size not adapting to the container size +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-catalog-import@0.13.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + +## @backstage/plugin-catalog-react@1.21.2-next.0 + +### Patch Changes + +- 2a3704d: Correct translation key from "type" to "owner" for owner column in entity table to ensure the right translation is loaded. +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-test-utils@0.3.7-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-config-schema@0.1.73-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-devtools@0.1.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.17 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/plugin-devtools-backend@0.5.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-devtools-common@0.1.17 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/plugin-events-backend-module-github@0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.15 + +## @backstage/plugin-home@0.8.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-home-react@0.1.31-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + +## @backstage/plugin-home-react@0.1.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + +## @backstage/plugin-kubernetes@0.12.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-kubernetes-react@0.5.12-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/plugin-kubernetes-backend@0.20.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-kubernetes-node@0.3.5-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/plugin-kubernetes-cluster@0.0.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/plugin-kubernetes-react@0.5.12-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/plugin-kubernetes-common@0.9.7-next.0 + +### Patch Changes + +- bdd7f95: Make SERVICEACCOUNT_CA_PATH public so it can be imported by external modules. +- Updated dependencies + - @backstage/catalog-model@1.7.5 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/plugin-kubernetes-node@0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/types@1.2.2 + +## @backstage/plugin-kubernetes-react@0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-mcp-actions-backend@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-notifications@0.5.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.1.0 + - @backstage/plugin-signals-react@0.0.15 + +## @backstage/plugin-org@0.6.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-catalog-common@1.1.5 + +## @backstage/plugin-org-react@0.1.43-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + +## @backstage/plugin-scaffolder@1.34.2-next.0 + +### Patch Changes + +- e0ffe84: Add missing `templatingExtensions` option to RouterProps.contextMenu to allow global control across scaffolder pages +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.3-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.16-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.14-next.0 + +### Patch Changes + +- baf1cab: Fix documentation strings to mention GCP instead of Azure +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.15-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/plugin-notifications-common@0.1.0 + - @backstage/plugin-notifications-node@0.2.19 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.15-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node-test-utils@0.3.4-next.0 + +## @backstage/plugin-scaffolder-common@1.7.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + +## @backstage/plugin-scaffolder-node-test-utils@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-test-utils@1.9.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/types@1.2.2 + +## @backstage/plugin-scaffolder-react@1.19.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.36 + +## @backstage/plugin-search@1.4.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.19 + +## @backstage/plugin-search-backend@2.0.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-common@1.2.19 + +## @backstage/plugin-search-backend-module-pg@0.5.49-next.0 + +### Patch Changes + +- a919ca3: Truncate long docs to fit PG index size limit +- 8d15a51: Added the < character to the query filter regexp +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/plugin-search-common@1.2.19 + +## @backstage/plugin-search-backend-module-techdocs@0.4.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/plugin-techdocs-node@1.13.8-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-common@1.2.19 + +## @backstage/plugin-search-react@1.9.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.19 + +## @backstage/plugin-signals@0.0.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.15 + +## @backstage/plugin-techdocs@1.15.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-auth-react@0.1.20-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-techdocs-addons-test-utils@1.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/test-utils@1.7.11 + +## @backstage/plugin-techdocs-backend@2.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-techdocs-node@1.13.8-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.0 + +## @backstage/plugin-techdocs-node@1.13.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-techdocs-react@1.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-user-settings@0.8.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.15 + - @backstage/plugin-user-settings-common@0.0.1 + +## @backstage/plugin-user-settings-backend@0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-node@0.1.24 + - @backstage/plugin-user-settings-common@0.0.1 + +## example-app@0.2.114-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.2-next.0 + - @backstage/plugin-catalog-graph@0.5.2-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/cli@0.34.4-next.0 + - @backstage/plugin-scaffolder@1.34.2-next.0 + - @backstage/plugin-api-docs@0.13.0-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-catalog-import@0.13.6-next.0 + - @backstage/plugin-home@0.8.13-next.0 + - @backstage/plugin-kubernetes@0.12.12-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.0 + - @backstage/plugin-org@0.6.45-next.0 + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/plugin-search@1.4.31-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.0 + - @backstage/plugin-user-settings@0.8.27-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-auth-react@0.1.20-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.0 + - @backstage/plugin-devtools@0.1.32-next.0 + - @backstage/plugin-notifications@0.5.10-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-signals@0.0.24-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/frontend-app-api@0.13.1-next.0 + - @backstage/theme@0.6.8 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-search-common@1.2.19 + +## example-app-next@0.0.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.2-next.0 + - @backstage/plugin-catalog-graph@0.5.2-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/cli@0.34.4-next.0 + - @backstage/plugin-scaffolder@1.34.2-next.0 + - @backstage/plugin-api-docs@0.13.0-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-catalog-import@0.13.6-next.0 + - @backstage/plugin-home@0.8.13-next.0 + - @backstage/plugin-kubernetes@0.12.12-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.0 + - @backstage/plugin-org@0.6.45-next.0 + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/plugin-search@1.4.31-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.0 + - @backstage/plugin-user-settings@0.8.27-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/frontend-defaults@0.3.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-app@0.3.1-next.0 + - @backstage/plugin-app-visualizer@0.1.24-next.0 + - @backstage/plugin-auth@0.1.1-next.0 + - @backstage/plugin-auth-react@0.1.20-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.0 + - @backstage/plugin-notifications@0.5.10-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-signals@0.0.24-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/frontend-app-api@0.13.1-next.0 + - @backstage/theme@0.6.8 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-search-common@1.2.19 + +## app-next-example-plugin@0.0.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + +## example-backend@0.0.43-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.15-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.0 + - @backstage/plugin-scaffolder-backend@3.0.0-next.0 + - @backstage/plugin-app-backend@0.5.6 + - @backstage/plugin-auth-backend@0.25.4 + - @backstage/plugin-auth-backend-module-github-provider@0.3.7 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-devtools-backend@0.5.10-next.0 + - @backstage/plugin-events-backend@0.5.6 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.4 + - @backstage/plugin-kubernetes-backend@0.20.3-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.4-next.0 + - @backstage/plugin-notifications-backend@0.5.10 + - @backstage/plugin-permission-backend@0.7.4 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-proxy-backend@0.6.6 + - @backstage/plugin-search-backend@2.0.7-next.0 + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/plugin-signals-backend@0.3.8 + - @backstage/plugin-techdocs-backend@2.1.1-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.15-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.12 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.6 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.4 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.12 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-backend-module-catalog@0.3.8 + - @backstage/plugin-search-backend-module-explore@0.3.7 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.0 + +## e2e-test@0.2.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.5-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## @internal/frontend@0.0.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @internal/scaffolder@0.0.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + +## techdocs-cli-embedded-app@0.2.113-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/cli@0.34.4-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/test-utils@1.7.11 + - @backstage/theme@0.6.8 + +## @internal/plugin-todo-list@1.0.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 diff --git a/package.json b/package.json index 5904f0f305..c58f5fb3d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.43.0", + "version": "1.44.0-next.0", "backstage": { "cli": { "new": { diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index 02f126bcf5..e5d901b279 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/app-defaults +## 1.7.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/plugin-permission-react@0.4.36 + ## 1.7.0 ### Minor Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 37973fbf0f..e872055314 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/app-defaults", - "version": "1.7.0", + "version": "1.7.1-next.0", "description": "Provides the default wiring of a Backstage App", "backstage": { "role": "web-library" diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 7949e120fa..4dfaaa596d 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.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + ## 0.0.27 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index 3b76202029..384911f923 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,6 +1,6 @@ { "name": "app-next-example-plugin", - "version": "0.0.27", + "version": "0.0.28-next.0", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 7e66d0d041..c1abb0f661 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,52 @@ # example-app-next +## 0.0.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.2-next.0 + - @backstage/plugin-catalog-graph@0.5.2-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/cli@0.34.4-next.0 + - @backstage/plugin-scaffolder@1.34.2-next.0 + - @backstage/plugin-api-docs@0.13.0-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-catalog-import@0.13.6-next.0 + - @backstage/plugin-home@0.8.13-next.0 + - @backstage/plugin-kubernetes@0.12.12-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.0 + - @backstage/plugin-org@0.6.45-next.0 + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/plugin-search@1.4.31-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.0 + - @backstage/plugin-user-settings@0.8.27-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/frontend-defaults@0.3.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-app@0.3.1-next.0 + - @backstage/plugin-app-visualizer@0.1.24-next.0 + - @backstage/plugin-auth@0.1.1-next.0 + - @backstage/plugin-auth-react@0.1.20-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.0 + - @backstage/plugin-notifications@0.5.10-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-signals@0.0.24-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/frontend-app-api@0.13.1-next.0 + - @backstage/theme@0.6.8 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-search-common@1.2.19 + ## 0.0.27 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 461b5673fc..99b0a23f51 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.27", + "version": "0.0.28-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index 533f4ab84c..daa56a15da 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,47 @@ # example-app +## 0.2.114-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.2-next.0 + - @backstage/plugin-catalog-graph@0.5.2-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/cli@0.34.4-next.0 + - @backstage/plugin-scaffolder@1.34.2-next.0 + - @backstage/plugin-api-docs@0.13.0-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-catalog-import@0.13.6-next.0 + - @backstage/plugin-home@0.8.13-next.0 + - @backstage/plugin-kubernetes@0.12.12-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.0 + - @backstage/plugin-org@0.6.45-next.0 + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/plugin-search@1.4.31-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.0 + - @backstage/plugin-user-settings@0.8.27-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-auth-react@0.1.20-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.0 + - @backstage/plugin-devtools@0.1.32-next.0 + - @backstage/plugin-notifications@0.5.10-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-signals@0.0.24-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/frontend-app-api@0.13.1-next.0 + - @backstage/theme@0.6.8 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-search-common@1.2.19 + ## 0.2.113 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 90ffcd0b3b..0d0ad4e1c2 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.113", + "version": "0.2.114-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 1adfd0f80e..e390e71e79 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/backend-defaults +## 0.13.0-next.0 + +### Minor Changes + +- 2d3e2b2: implement support for direct url for AzureBlobStorageUrlReader search function +- 8b91238: Adds support for configuring server-level HTTP options through the + `app-config.yaml` file under the `backend.server` key. Supported options + include `headersTimeout`, `keepAliveTimeout`, `requestTimeout`, `timeout`, + `maxHeadersCount`, and `maxRequestsPerSocket`. + + These are passed directly to the underlying Node.js HTTP server. + If omitted, Node.js defaults are used. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-app-api@1.2.7 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/cli-node@0.2.14 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.15 + ## 0.12.1 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 013e32db59..4a27d65c33 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.12.1", + "version": "0.13.0-next.0", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index e3f857785d..2e2a966438 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/backend-dynamic-feature-service +## 0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-events-backend@0.5.6 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.14 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.37 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-common@1.2.19 + ## 0.7.4 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 04577cbf43..268d54c180 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.7.4", + "version": "0.7.5-next.0", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index cc82ef5593..d81c184682 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/backend-test-utils +## 1.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/backend-app-api@1.2.7 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + ## 1.9.0 ### Minor Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 868c98a8f2..a9b160bcd8 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "1.9.0", + "version": "1.9.1-next.0", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 398465966e..a1e68bd8e7 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,46 @@ # example-backend +## 0.0.43-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.15-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.0 + - @backstage/plugin-scaffolder-backend@3.0.0-next.0 + - @backstage/plugin-app-backend@0.5.6 + - @backstage/plugin-auth-backend@0.25.4 + - @backstage/plugin-auth-backend-module-github-provider@0.3.7 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-devtools-backend@0.5.10-next.0 + - @backstage/plugin-events-backend@0.5.6 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.4 + - @backstage/plugin-kubernetes-backend@0.20.3-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.4-next.0 + - @backstage/plugin-notifications-backend@0.5.10 + - @backstage/plugin-permission-backend@0.7.4 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-proxy-backend@0.6.6 + - @backstage/plugin-search-backend@2.0.7-next.0 + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/plugin-signals-backend@0.3.8 + - @backstage/plugin-techdocs-backend@2.1.1-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.15-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.12 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.6 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.4 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.12 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-backend-module-catalog@0.3.8 + - @backstage/plugin-search-backend-module-explore@0.3.7 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.0 + ## 0.0.42 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 8bca4f3b22..15bfab297f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.42", + "version": "0.0.43-next.0", "backstage": { "role": "backend" }, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 7947c15cbb..8c35d62164 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/cli +## 0.34.4-next.0 + +### Patch Changes + +- 6ebc1ea: Fixed module federation config by only setting `import: false` on shared libraries for remote. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.14 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.11 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.2 + ## 0.34.2 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index f50a5601eb..99a4c56b58 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.34.2", + "version": "0.34.4-next.0", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index eb4c7d051f..a1d9a3d37d 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-compat-api +## 0.5.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/version-bridge@1.0.11 + ## 0.5.2 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index acab8947c8..1ccb842a9c 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.5.2", + "version": "0.5.3-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 63e6e45065..adb205392f 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/core-components +## 0.18.2-next.0 + +### Patch Changes + +- d493126: Swap base token for semantic token in ItemCardHeader to ensure readability in light mode. +- 6981ae6: Fixed DependencyGraph `svg` size not adapting to the container size +- Updated dependencies + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/version-bridge@1.0.11 + ## 0.18.0 ### Minor Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index d369052e99..4e16285cfb 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-components", - "version": "0.18.0", + "version": "0.18.2-next.0", "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 5d317d4afa..245cd34a46 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/create-app +## 0.7.5-next.0 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + ## 0.7.4 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 263d65e5c8..0330fb9663 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.7.4", + "version": "0.7.5-next.0", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 47ec659481..10177c7c7d 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + ## 1.1.14 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 40882a898b..055f4f94b7 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.14", + "version": "1.1.15-next.0", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 471c2fc53e..fae3f16eb4 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.5-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + ## 0.2.32 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 6b255f43b1..80a2ea7d3f 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.32", + "version": "0.2.33-next.0", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 1a23d4b535..80591e1bd9 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/frontend-app-api +## 0.13.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-defaults@0.3.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + ## 0.13.0 ### Minor Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 3f2dab8571..bd52912d55 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.13.0", + "version": "0.13.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index 65713fac5f..2ebae3e384 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/frontend-defaults +## 0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-app@0.3.1-next.0 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-app-api@0.13.1-next.0 + ## 0.3.1 ### Patch Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index 6a8b100c0d..06efcf0576 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.3.1", + "version": "0.3.2-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-dynamic-feature-loader/CHANGELOG.md b/packages/frontend-dynamic-feature-loader/CHANGELOG.md index 0e7886365d..fd185af296 100644 --- a/packages/frontend-dynamic-feature-loader/CHANGELOG.md +++ b/packages/frontend-dynamic-feature-loader/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/frontend-dynamic-feature-loader +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/config@1.3.3 + ## 0.1.5 ### Patch Changes diff --git a/packages/frontend-dynamic-feature-loader/package.json b/packages/frontend-dynamic-feature-loader/package.json index 7ed00c92de..bda6b0686c 100644 --- a/packages/frontend-dynamic-feature-loader/package.json +++ b/packages/frontend-dynamic-feature-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-dynamic-feature-loader", - "version": "0.1.5", + "version": "0.1.6-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-internal/CHANGELOG.md b/packages/frontend-internal/CHANGELOG.md index 7de465f8c0..c0e28a9529 100644 --- a/packages/frontend-internal/CHANGELOG.md +++ b/packages/frontend-internal/CHANGELOG.md @@ -1,5 +1,14 @@ # @internal/frontend +## 0.0.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + ## 0.0.13 ### Patch Changes diff --git a/packages/frontend-internal/package.json b/packages/frontend-internal/package.json index 2399eaa453..41943f4e89 100644 --- a/packages/frontend-internal/package.json +++ b/packages/frontend-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/frontend", - "version": "0.0.13", + "version": "0.0.14-next.0", "backstage": { "role": "web-library", "inline": true diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 647bd8f6f1..537939e8a5 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/frontend-plugin-api +## 0.12.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + ## 0.12.0 ### Minor Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 1e261787fb..714cd0471a 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.12.0", + "version": "0.12.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 4e96c27dd3..6f17abe709 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/frontend-test-utils +## 0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-app@0.3.1-next.0 + - @backstage/config@1.3.3 + - @backstage/frontend-app-api@0.13.1-next.0 + - @backstage/test-utils@1.7.11 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + ## 0.3.6 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index ba71540d7a..7aab8a9265 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.3.6", + "version": "0.3.7-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index b152cf511b..efc73ec308 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration-react +## 1.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + ## 1.2.10 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index a3d8000f94..fe6c47f736 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-react", - "version": "1.2.10", + "version": "1.2.11-next.0", "description": "Frontend package for managing integrations towards external systems", "backstage": { "role": "web-library" diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 946e392c0b..b03eff3ebc 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/integration +## 1.18.1-next.0 + +### Patch Changes + +- d772b51: remove host from azure blob storage integration type +- 84443f1: Adds config definitions for Azure Blob Storage. +- Updated dependencies + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 1.18.0 ### Minor Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 68289e2079..8d7f655e2b 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "1.18.0", + "version": "1.18.1-next.0", "description": "Helpers for managing integrations towards external systems", "backstage": { "role": "common-library" diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index de8c725e03..e636e2ccc8 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/scaffolder +## 0.0.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + ## 0.0.13 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index abbd487103..9f490a8b62 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.13", + "version": "0.0.14-next.0", "backstage": { "role": "web-library", "inline": true diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 76a8f81a63..5eda7ebcaf 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.113-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/cli@0.34.4-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/app-defaults@1.7.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/test-utils@1.7.11 + - @backstage/theme@0.6.8 + ## 0.2.112 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 09e974ed88..577426d02c 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.112", + "version": "0.2.113-next.0", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 1fb32bec51..9d47af4de6 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.9.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-techdocs-node@1.13.8-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.3 + ## 1.9.8 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 7ce62cd803..4a4675c6e1 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.8", + "version": "1.9.9-next.0", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 0fcd9d2ff6..4c9d6f833b 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/ui +## 0.7.2-next.0 + +### Patch Changes + +- 827340f: remove default selection of tab +- 9a47125: Improved SearchField component flex layout and animations. Fixed SearchField behavior in Header components by switching from width-based transitions to flex-basis transitions for better responsive behavior. Added new Storybook stories to test SearchField integration with Header component. + ## 0.7.1 ### Patch Changes diff --git a/packages/ui/package.json b/packages/ui/package.json index 80192895d9..b4432c8b6b 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/ui", - "version": "0.7.1", + "version": "0.7.2-next.0", "backstage": { "role": "web-library" }, diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 4a40848ced..817b60fd0c 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-api-docs +## 0.13.0-next.0 + +### Minor Changes + +- b8a381e: Remove explicit dependency on `isomorphic-form-data`. + + This explicit dependency was added to address [an issue](https://github.com/swagger-api/swagger-ui/issues/7436) in the + dependency `swagger-ui-react`. That [issue has since been resolved](https://github.com/swagger-api/swagger-ui/issues/7436#issuecomment-889792304), + and `isomorphic-form-data` no longer needs to be declared. + + Additionally, this changeset updates the `swagger-ui-react` dependency to version `5.19.0` or higher, which includes + [compatibility](https://github.com/swagger-api/swagger-ui?tab=readme-ov-file#compatibility) with the latest versions of + the OpenAPI specification. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + ## 0.12.11 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 810279ed39..0012ec9b54 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.12.11", + "version": "0.13.0-next.0", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index bc5ac063cc..85876e49b2 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-visualizer +## 0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + ## 0.1.23 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 4affb81b50..b0ce29d3c8 100644 --- a/plugins/app-visualizer/package.json +++ b/plugins/app-visualizer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-visualizer", - "version": "0.1.23", + "version": "0.1.24-next.0", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index c1026add86..615f644cc4 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-app +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.36 + ## 0.3.0 ### Minor Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index d17d903b0a..c0934f7c54 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.3.0", + "version": "0.3.1-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "app", diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index 88440f314b..86662ef3f3 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-react +## 0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + ## 0.1.19 ### Patch Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index 77b01bfdd1..a41ccd3432 100644 --- a/plugins/auth-react/package.json +++ b/plugins/auth-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-react", - "version": "0.1.19", + "version": "0.1.20-next.0", "description": "Web library for the auth plugin", "backstage": { "role": "web-library", diff --git a/plugins/auth/CHANGELOG.md b/plugins/auth/CHANGELOG.md index 7092c0bf3c..506006165a 100644 --- a/plugins/auth/CHANGELOG.md +++ b/plugins/auth/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth/package.json b/plugins/auth/package.json index dffb0eb8ff..578a7246ec 100644 --- a/plugins/auth/package.json +++ b/plugins/auth/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth", - "version": "0.1.0", + "version": "0.1.1-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "auth", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 31345b723c..4c7a9afb60 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.3.3-next.0 + +### Patch Changes + +- 2aded73: Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + ## 0.3.2 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 1c72ef31b9..2375f46dc2 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", - "version": "0.3.2", + "version": "0.3.3-next.0", "description": "Common functionalities for bitbucket-cloud plugins", "backstage": { "role": "common-library", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 0836bee10d..582cea0429 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.4.15 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 3f21de8eb1..48088f2529 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.4.15", + "version": "0.4.16-next.0", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 842d0498cf..7b58ca6a0b 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.3.10-next.0 + +### Patch Changes + +- 84443f1: Adds config definitions for Azure Blob Storage. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.3.9 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 17c66699a5..bc337337f0 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", - "version": "0.3.9", + "version": "0.3.10-next.0", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 45d3c5ed5b..531418dc68 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.5.4-next.0 + +### Patch Changes + +- 2aded73: Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.3-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + ## 0.5.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 68dfbaa619..562911f1bd 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", - "version": "0.5.3", + "version": "0.5.4-next.0", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 1df0cda05b..72f0f277ed 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + ## 0.5.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index ce3ec49ae8..abbd7be1aa 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.5.3", + "version": "0.5.4-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 4d06b61dbe..c02fe65bd5 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.3.12 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 8fb1acd48f..1123f00416 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.3.12", + "version": "0.3.13-next.0", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index 097e4f344f..c26520ccc5 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.3.6 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 1c4d7e8aa7..2586dda386 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.3.6", + "version": "0.3.7-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gitea/CHANGELOG.md b/plugins/catalog-backend-module-gitea/CHANGELOG.md index 3274f6c9f1..09f2a60401 100644 --- a/plugins/catalog-backend-module-gitea/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-gitea +## 0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitea/package.json b/plugins/catalog-backend-module-gitea/package.json index c9dacc7b64..3fac7944e1 100644 --- a/plugins/catalog-backend-module-gitea/package.json +++ b/plugins/catalog-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitea", - "version": "0.1.4", + "version": "0.1.5-next.0", "license": "Apache-2.0", "description": "The gitea backend module for the catalog plugin.", "main": "src/index.ts", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index 6198a1942e..ad55f9a4f5 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.11.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + ## 0.3.14 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index f9c1f516fb..a862c80768 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.3.14", + "version": "0.3.15-next.0", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 718a0035e8..c4cfc67865 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-github +## 0.11.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + ## 0.11.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 5187103233..cf564acfe1 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.11.0", + "version": "0.11.1-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 022eb1445a..654d97b7c1 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + ## 0.2.13 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 09f6ee7811..09d9796167 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.2.13", + "version": "0.2.14-next.0", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 2610055302..d4f3382f60 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.7.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + ## 0.7.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index c18be1028b..4a48f14bd6 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.7.3", + "version": "0.7.4-next.0", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index aa22c5aad7..d25514e90f 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.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + ## 0.7.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index ffc675fcff..7aa7c5e7b1 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", - "version": "0.7.4", + "version": "0.7.5-next.0", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index fbc0b1dfa7..2f62c77773 100644 --- a/plugins/catalog-backend-module-logs/CHANGELOG.md +++ b/plugins/catalog-backend-module-logs/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-logs +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/plugin-events-node@0.4.15 + ## 0.1.14 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index 3b27e9cd79..74c71fd3fe 100644 --- a/plugins/catalog-backend-module-logs/package.json +++ b/plugins/catalog-backend-module-logs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-logs", - "version": "0.1.14", + "version": "0.1.15-next.0", "description": "A module that subscribes to catalog related events and logs them.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 6cef384c74..72eecbb0df 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.2.14 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 155c24115d..e8d9a5b741 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", - "version": "0.2.14", + "version": "0.2.15-next.0", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index a654ff5c3d..6cddd6166b 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.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.2.12 ### 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 beed6b3028..5f1f4277d2 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.2.12", + "version": "0.2.13-next.0", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 54be12f4cb..0ec926a05c 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,48 @@ # @backstage/plugin-catalog-backend +## 3.1.1-next.0 + +### Patch Changes + +- 9890488: Internal refactor to remove remnants of the old backend system +- 2aaf01a: Fix for duplicate search results in entity-facets API call +- e489661: Moved catalog processor and provider disabling and priorities under own config objects. + + This is due to issue with some existing providers, such as GitHub, using array syntax for the provider configuration. + + The new config format is not backwards compatible, so users will need to update their config files. The new format + is as follows: + + ```yaml + catalog: + providerOptions: + providerA: + disabled: false + providerB: + disabled: true + processorOptions: + processorA: + disabled: false + priority: 10 + processorB: + disabled: true + ``` + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + ## 3.1.0 ### Minor Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index a880cc224f..ec09efc9a3 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "3.1.0", + "version": "3.1.1-next.0", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 8d19916dab..4293e64516 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-graph +## 0.5.2-next.0 + +### Patch Changes + +- 87b5e6e: Add missing API implementation for catalog graph plugin in NFS apps. +- 6981ae6: Fixed DependencyGraph `svg` size not adapting to the container size +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/types@1.2.2 + ## 0.5.0 ### Minor Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 86bd930095..aa66e4a234 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.5.0", + "version": "0.5.2-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 9c8778e168..10252aadbe 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.13.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.5 + ## 0.13.5 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 97b7283ab9..8065881127 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.13.5", + "version": "0.13.6-next.0", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index f5e4d26638..bfc54981fa 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-catalog-react +## 1.21.2-next.0 + +### Patch Changes + +- 2a3704d: Correct translation key from "type" to "owner" for owner column in entity table to ensure the right translation is loaded. +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-test-utils@0.3.7-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-permission-react@0.4.36 + ## 1.21.0 ### Minor Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index dabfb55d11..b645537a7e 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.21.0", + "version": "1.21.2-next.0", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index 31667429ca..3483042a69 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.2.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + ## 0.2.21 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index 9fdd8b5e29..0abe8ae2be 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.2.21", + "version": "0.2.22-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-unprocessed-entities", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 8ae3b4c8da..a10136e79b 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-catalog +## 1.31.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + ## 1.31.3 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 2a7a956c37..580b6abdb9 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.31.3", + "version": "1.31.4-next.0", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 74c7a68e58..5e94cbe988 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-config-schema +## 0.1.73-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.1.72 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 78addfd9c2..c49c1e9087 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-config-schema", - "version": "0.1.72", + "version": "0.1.73-next.0", "description": "A Backstage plugin that lets you browse the configuration schema of your app", "backstage": { "role": "frontend-plugin", diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index eeaeda6605..9213c182e9 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-devtools-backend +## 0.5.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.3 + - @backstage/config-loader@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-devtools-common@0.1.17 + - @backstage/plugin-permission-common@0.9.1 + ## 0.5.9 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index c3892d274f..3a95c1dbf9 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.5.9", + "version": "0.5.10-next.0", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index de537019e3..7f5ad6310d 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-devtools +## 0.1.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.17 + - @backstage/plugin-permission-react@0.4.36 + ## 0.1.31 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 9629d30370..ea69e09e86 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.31", + "version": "0.1.32-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "devtools", diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index 28495671fb..483afda21f 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-events-backend-module-github +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/types@1.2.2 + - @backstage/plugin-events-node@0.4.15 + ## 0.4.4 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index aebbc6eddf..d9cd60cedc 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.4.4", + "version": "0.4.5-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 621859a9ab..564ed40cb9 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.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-plugin-api@1.11.0 + ## 1.0.43 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index f0273e482b..c8f75da342 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.43", + "version": "1.0.44-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "todo-list", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index b4fff66b63..37c1ac8b57 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-home-react +## 0.1.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + ## 0.1.30 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index bce8419b08..dbc036029b 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home-react", - "version": "0.1.30", + "version": "0.1.31-next.0", "description": "A Backstage plugin that contains react components helps you build a home page", "backstage": { "role": "web-library", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 5731706ece..1ce162951b 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-home +## 0.8.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-home-react@0.1.31-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + ## 0.8.12 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index a348b8eff9..70bba26ce4 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.8.12", + "version": "0.8.13-next.0", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 1ede69fa30..c67593601c 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-kubernetes-backend +## 0.20.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-kubernetes-node@0.3.5-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-permission-common@0.9.1 + ## 0.20.2 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 6d029eae95..51ea136593 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.20.2", + "version": "0.20.3-next.0", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index 7f7cf88809..4d05386487 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/plugin-kubernetes-react@0.5.12-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-permission-react@0.4.36 + ## 0.0.29 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index e789c3edaf..8d65670f28 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-cluster", - "version": "0.0.29", + "version": "0.0.30-next.0", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index 64ea6455e4..0ec9185623 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-common +## 0.9.7-next.0 + +### Patch Changes + +- bdd7f95: Make SERVICEACCOUNT_CA_PATH public so it can be imported by external modules. +- Updated dependencies + - @backstage/catalog-model@1.7.5 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + ## 0.9.6 ### Patch Changes diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index cde58de050..f78da14fa6 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-common", - "version": "0.9.6", + "version": "0.9.7-next.0", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", "backstage": { "role": "common-library", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index d7366fa90c..8870323c75 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-node +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/types@1.2.2 + ## 0.3.4 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 2c4c043ec5..cd204be633 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.3.4", + "version": "0.3.5-next.0", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index c542907fe6..033d51737b 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-react +## 0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.5.11 ### Patch Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 82e3d59d8b..c417938b40 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-react", - "version": "0.5.11", + "version": "0.5.12-next.0", "description": "Web library for the kubernetes-react plugin", "backstage": { "role": "web-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 627226fee0..991781331c 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-kubernetes +## 0.12.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-kubernetes-react@0.5.12-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-permission-react@0.4.36 + ## 0.12.11 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index e9da7b47ec..aaf3c0f65d 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.11", + "version": "0.12.12-next.0", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", diff --git a/plugins/mcp-actions-backend/CHANGELOG.md b/plugins/mcp-actions-backend/CHANGELOG.md index 8f079b5dd6..dbc8326e1a 100644 --- a/plugins/mcp-actions-backend/CHANGELOG.md +++ b/plugins/mcp-actions-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-mcp-actions-backend +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/mcp-actions-backend/package.json b/plugins/mcp-actions-backend/package.json index 4b8bbd3b72..cf2b5e1d74 100644 --- a/plugins/mcp-actions-backend/package.json +++ b/plugins/mcp-actions-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-mcp-actions-backend", - "version": "0.1.3", + "version": "0.1.4-next.0", "backstage": { "role": "backend-plugin", "pluginId": "mcp-actions", diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index 9ce2086421..1c1e910e44 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-notifications +## 0.5.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-common@0.1.0 + - @backstage/plugin-signals-react@0.0.15 + ## 0.5.9 ### Patch Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index e6d66a5ec7..092a26c3ba 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.5.9", + "version": "0.5.10-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index aee4c9702e..dd586ca211 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.43-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + ## 0.1.42 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index c177584968..94d8e62962 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.42", + "version": "0.1.43-next.0", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 6a62f12664..b9fa9ec4d6 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-org +## 0.6.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/plugin-catalog-common@1.1.5 + ## 0.6.44 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 9d1c60072c..0868489426 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.44", + "version": "0.6.45-next.0", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index fc6d08bf48..c4c021f726 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.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index c1db900f79..837b6e4d8c 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-azure", - "version": "0.2.13", + "version": "0.2.14-next.0", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index cc61bd54fd..3648ebf879 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.3-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index a68a30da84..b2d9b1b92c 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud", - "version": "0.2.13", + "version": "0.2.14-next.0", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index ff9e8a84d1..3e573f638d 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 452653afb7..97f8abc1aa 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-server", - "version": "0.2.13", + "version": "0.2.14-next.0", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index 25ff743f3c..535f4bab5c 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.3.15-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.3.14 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index b88bfcc259..957dff0afc 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", - "version": "0.3.14", + "version": "0.3.15-next.0", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 354a075745..fb5bbbfaf5 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.3.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.3.13 ### 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 a708176e47..e4ef5be916 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", - "version": "0.3.13", + "version": "0.3.14-next.0", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 24b6ca0dd4..a9dbde5b6f 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.16-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.3.15 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 6300973618..530732acd0 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", - "version": "0.3.15", + "version": "0.3.16-next.0", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index e7c99d1075..83e72c1e3a 100644 --- a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gcp +## 0.2.14-next.0 + +### Patch Changes + +- baf1cab: Fix documentation strings to mention GCP instead of Azure +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index bc3a2cbabb..0a4609c457 100644 --- a/plugins/scaffolder-backend-module-gcp/package.json +++ b/plugins/scaffolder-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gcp", - "version": "0.2.13", + "version": "0.2.14-next.0", "description": "The GCP Bucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index c6b2802b91..58877701b2 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 3a4013a54e..928c3965f3 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gerrit", - "version": "0.2.13", + "version": "0.2.14-next.0", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index 4c76e53bf5..f6309bd70b 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index acc972343f..cc6e18fbd2 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitea", - "version": "0.2.13", + "version": "0.2.14-next.0", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 7f7ba5a597..1a2f46d3fb 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.9.1-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.0 + ## 0.9.0 ### Minor Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 50fc1646e0..49851f967a 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", - "version": "0.9.0", + "version": "0.9.1-next.0", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index df082eb7eb..1b371afb91 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.9.6-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.9.5 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index f8194a434c..64eb919a59 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.9.5", + "version": "0.9.6-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index bf28a5d88f..5937669786 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.15-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/plugin-notifications-common@0.1.0 + - @backstage/plugin-notifications-node@0.2.19 + ## 0.1.14 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index 22aaa22396..4220fc14ba 100644 --- a/plugins/scaffolder-backend-module-notifications/package.json +++ b/plugins/scaffolder-backend-module-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-notifications", - "version": "0.1.14", + "version": "0.1.15-next.0", "description": "The notifications backend module for the scaffolder plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 5970f3f8a2..f8cf3b6e3f 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.5.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.5.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index c6f57ae2a4..589b4cf729 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", - "version": "0.5.13", + "version": "0.5.14-next.0", "description": "A module for the scaffolder backend that lets you template projects using Rails", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 097b7f38f3..fca74f3951 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.2.14-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + ## 0.2.13 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index 55930738b6..f07e77413f 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.2.13", + "version": "0.2.14-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 9ac2c16a7e..449503e901 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.4.15-next.0 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/types@1.2.2 + - @backstage/plugin-scaffolder-node-test-utils@0.3.4-next.0 + ## 0.4.14 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index e5b60c405d..295b154103 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.4.14", + "version": "0.4.15-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 94b1ed5e93..1e3fbe7bf1 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-scaffolder-backend +## 3.0.0-next.0 + +### Major Changes + +- 9b81a90: **BREAKING** - Removing the deprecated types and interfaces, there's no replacement for these types, and hopefully not currently used as they offer no value with the plugin being on the new backend system and no way to consume them. + + Affected types: `CreateWorkerOptions`, `CurrentClaimedTask`, `DatabaseTaskStore`, `DatabaseTaskStoreOptions`, `TaskManager`, `TaskStore`, `TaskStoreCreateTaskOptions`, `TaskStoreCreateTaskResult`, `TaskStoreEmitOptions`, `TaskStoreListEventsOptions`, `TaskStoreRecoverTaskOptions`, `TaskStoreShutDownTaskOptions`, `TaskWorker` and `TemplateActionRegistry`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.14-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.14-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.3-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.0 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-events-node@0.4.15 + - @backstage/plugin-permission-common@0.9.1 + ## 2.2.1 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 53aab2b891..99faf1bf5b 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "2.2.1", + "version": "3.0.0-next.0", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index 2c8133b33c..67e4b5058d 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-common +## 1.7.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + ## 1.7.1 ### Patch Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 63ddd99e9e..3a4eef3e03 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-common", - "version": "1.7.1", + "version": "1.7.2-next.0", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", "backstage": { "role": "common-library", diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index d14226a463..2520b628e7 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0-next.0 + - @backstage/backend-test-utils@1.9.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/types@1.2.2 + ## 0.3.3 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 8baac6e67f..3cbae20d33 100644 --- a/plugins/scaffolder-node-test-utils/package.json +++ b/plugins/scaffolder-node-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node-test-utils", - "version": "0.3.3", + "version": "0.3.4-next.0", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index c322aa5700..750fd7ce98 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-scaffolder-node +## 0.12.0-next.0 + +### Minor Changes + +- 9b81a90: **BREAKING** - Marking optional fields as required in the `TaskBroker`, these can be fixed with a no-op `() => void` if you don't want to implement the functions. + + - `cancel`, `recoverTasks` and `retry` are the required methods on the `TaskBroker` interface. + + **NOTE**: If you're affected by this breaking change, please reach out to us in an issue as we're thinking about completely removing the `TaskBroker` extension point soon and would like to hear your use cases for the upcoming re-architecture of the `scaffolder-backend` plugin. + +### Patch Changes + +- c8aa210: **BREAKING ALPHA**: We've moved the `scaffolderActionsExtensionPoint` from `/alpha` to the main export. + + ```tsx + // before + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + + // after + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; + ``` + +- 6e2bda7: **DEPRECATION**: We're going to be working on refactoring a lot of the internals of the Scaffolder backend plugin, and with that comes a lot of deprecations and removals for public types that are making these things hard. + + If you're using these types, please reach out to us either on Discord or a GitHub issue with your use cases. + + - `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskContext`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskEventType`, `TaskFilter`, `TaskFilters`, `TaskStatus` are the types that have now been marked as deprecated, and will be removed in a future release. + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + ## 0.11.1 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index a3ae56d3d5..88a7ebf8ce 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.11.1", + "version": "0.12.0-next.0", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 12061a9561..cda6c9b6a6 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-scaffolder-react +## 1.19.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.36 + ## 1.19.1 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index f33c6e0a70..ac452d2205 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.19.1", + "version": "1.19.2-next.0", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 5b331f64d3..d4b6a5bd15 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-scaffolder +## 1.34.2-next.0 + +### Patch Changes + +- e0ffe84: Add missing `templatingExtensions` option to RouterProps.contextMenu to allow global control across scaffolder pages +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/plugin-scaffolder-react@1.19.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-permission-react@0.4.36 + - @backstage/plugin-techdocs-common@0.1.1 + ## 1.34.1 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 6190cefbac..fe2c88e3ed 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.34.1", + "version": "1.34.2-next.0", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index 14afeae46a..2b148f8542 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.49-next.0 + +### Patch Changes + +- a919ca3: Truncate long docs to fit PG index size limit +- 8d15a51: Added the < character to the query filter regexp +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/plugin-search-common@1.2.19 + ## 0.5.48 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 4a385fffeb..312783ddc3 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-pg", - "version": "0.5.48", + "version": "0.5.49-next.0", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 1a952bbb9b..a23f555d67 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.4.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/plugin-techdocs-node@1.13.8-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-common@1.2.19 + ## 0.4.6 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 89bb5f964c..4a4a13d3e6 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.4.6", + "version": "0.4.7-next.0", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 323d97127b..9bef273db2 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend +## 2.0.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-permission-node@0.10.4 + - @backstage/plugin-search-backend-node@1.3.15 + - @backstage/backend-openapi-utils@0.6.1 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-common@1.2.19 + ## 2.0.6 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 1802646e5d..4e61e05ce6 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "2.0.6", + "version": "2.0.7-next.0", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index d8133c742e..6a23e79db4 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-react +## 1.9.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.19 + ## 1.9.4 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 2fbfa9267f..7bd07a786e 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.9.4", + "version": "1.9.5-next.0", "backstage": { "role": "web-library", "pluginId": "search", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 5bd43fd7c4..d3119d0c9a 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search +## 1.4.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.19 + ## 1.4.30 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 6ecbb32437..48f462897d 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.30", + "version": "1.4.31-next.0", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin", diff --git a/plugins/signals/CHANGELOG.md b/plugins/signals/CHANGELOG.md index cb5c18da48..4d878217e4 100644 --- a/plugins/signals/CHANGELOG.md +++ b/plugins/signals/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-signals +## 0.0.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.15 + ## 0.0.23 ### Patch Changes diff --git a/plugins/signals/package.json b/plugins/signals/package.json index c3a3f9ad66..d4ecc528c5 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals", - "version": "0.0.23", + "version": "0.0.24-next.0", "backstage": { "role": "frontend-plugin", "pluginId": "signals", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 11f82bfe63..32d6473083 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.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/plugin-catalog@1.31.4-next.0 + - @backstage/plugin-techdocs@1.15.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/test-utils@1.7.11 + ## 1.1.0 ### Minor Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 2de0951a9c..0c24577409 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.1.0", + "version": "1.1.1-next.0", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 1234dd5b58..970677836e 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-techdocs-backend +## 2.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/plugin-techdocs-node@1.13.8-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.5 + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/plugin-permission-common@0.9.1 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + ## 2.1.0 ### Minor Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index c9d0218404..7bc5e52385 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "2.1.0", + "version": "2.1.1-next.0", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index d3842b48d6..841e350d23 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.0 + ## 1.1.28 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 12522a0949..92e40a664c 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", - "version": "1.1.28", + "version": "1.1.29-next.0", "description": "Plugin module for contributed TechDocs Addons", "backstage": { "role": "frontend-plugin-module", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 6cc19abea5..b3bcc36620 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-techdocs-node +## 1.13.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.0 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + ## 1.13.7 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index d7ecd3b661..5d36d96287 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.13.7", + "version": "1.13.8-next.0", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library", diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index d03925b40d..4e11d1c6f9 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-techdocs-react +## 1.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-techdocs-common@0.1.1 + ## 1.3.3 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 13e9dfc66c..6020c8fb78 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-react", - "version": "1.3.3", + "version": "1.3.4-next.0", "description": "Shared frontend utilities for TechDocs and Addons", "backstage": { "role": "web-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 7bdefe0293..67a2e51ad8 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-techdocs +## 1.15.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-auth-react@0.1.20-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + ## 1.15.0 ### Minor Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 7f03dd32f5..ffc41132fe 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.15.0", + "version": "1.15.1-next.0", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 20a635d42e..7b9763d4da 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-user-settings-backend +## 0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-auth-node@0.6.7 + - @backstage/backend-plugin-api@1.4.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-node@0.1.24 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.3.6 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index f1f8a3ba68..30c7355ba1 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings-backend", - "version": "0.3.6", + "version": "0.3.7-next.0", "description": "The Backstage backend plugin to manage user settings", "backstage": { "role": "backend-plugin", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index fba325d67f..2c3a347cd7 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-user-settings +## 0.8.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/core-app-api@1.19.0 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.15 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.8.26 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 1ec4ac7072..8b0c297f70 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.8.26", + "version": "0.8.27-next.0", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", From 3b8e156c140ca73fca70c77323ebf290fcca76cb Mon Sep 17 00:00:00 2001 From: Hellgren Heikki Date: Tue, 23 Sep 2025 20:43:05 +0300 Subject: [PATCH 089/211] fix: default notification recipient resolver exclusion Signed-off-by: Hellgren Heikki --- .changeset/slimy-signs-agree.md | 5 ++ ...faultNotificationRecipientResolver.test.ts | 10 +-- .../DefaultNotificationRecipientResolver.ts | 16 ++--- .../src/service/router.test.ts | 69 +++++++++++++++++++ 4 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 .changeset/slimy-signs-agree.md diff --git a/.changeset/slimy-signs-agree.md b/.changeset/slimy-signs-agree.md new file mode 100644 index 0000000000..c4307f0bd7 --- /dev/null +++ b/.changeset/slimy-signs-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend': patch +--- + +Fixed exclude entity reference not working in notification sending diff --git a/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.test.ts b/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.test.ts index cd17db3680..7faec0c23c 100644 --- a/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.test.ts +++ b/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.test.ts @@ -34,7 +34,7 @@ describe('getUsersForEntityRef', () => { await expect( resolver.resolveNotificationRecipients({ entityRefs: ['user:foo', 'user:ignored'], - excludeEntityRefs: ['user:ignored'], + excludedEntityRefs: ['user:ignored'], }), ).resolves.toEqual({ userEntityRefs: ['user:foo'] }); expect(catalog.getEntitiesByRefs).not.toHaveBeenCalled(); @@ -87,7 +87,7 @@ describe('getUsersForEntityRef', () => { await expect( resolver.resolveNotificationRecipients({ entityRefs: ['group:default/parent_group'], - excludeEntityRefs: ['user:default/ignored'], + excludedEntityRefs: ['user:default/ignored'], }), ).resolves.toEqual({ userEntityRefs: ['user:default/foo', 'user:default/bar'], @@ -120,7 +120,7 @@ describe('getUsersForEntityRef', () => { await expect( resolver.resolveNotificationRecipients({ entityRefs: ['component:default/test_component'], - excludeEntityRefs: [], + excludedEntityRefs: [], }), ).resolves.toEqual({ userEntityRefs: ['user:default/foo'] }); }); @@ -164,7 +164,7 @@ describe('getUsersForEntityRef', () => { await expect( resolver.resolveNotificationRecipients({ entityRefs: ['component:default/test_component'], - excludeEntityRefs: [], + excludedEntityRefs: [], }), ).resolves.toEqual({ userEntityRefs: ['user:default/foo'] }); }); @@ -208,7 +208,7 @@ describe('getUsersForEntityRef', () => { await expect( resolver.resolveNotificationRecipients({ entityRefs: ['component:default/test_component'], - excludeEntityRefs: ['user:default/foo'], + excludedEntityRefs: ['user:default/foo'], }), ).resolves.toEqual({ userEntityRefs: [] }); }); diff --git a/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.ts b/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.ts index 2401ec7c1f..200b5dc4ea 100644 --- a/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.ts +++ b/plugins/notifications-backend/src/service/DefaultNotificationRecipientResolver.ts @@ -54,16 +54,16 @@ export class DefaultNotificationRecipientResolver async resolveNotificationRecipients(options: { entityRefs: string[]; - excludeEntityRefs?: string[]; + excludedEntityRefs?: string[]; }): Promise<{ userEntityRefs: string[] }> { - const { entityRefs, excludeEntityRefs = [] } = options; + const { entityRefs, excludedEntityRefs = [] } = options; const [userEntityRefs, otherEntityRefs] = partitionEntityRefs(entityRefs); const users: string[] = userEntityRefs.filter( - ref => !excludeEntityRefs.includes(ref), + ref => !excludedEntityRefs.includes(ref), ); const filtered = otherEntityRefs.filter( - ref => !excludeEntityRefs.includes(ref), + ref => !excludedEntityRefs.includes(ref), ); const fields = ['kind', 'metadata.name', 'metadata.namespace', 'relations']; @@ -87,7 +87,7 @@ export class DefaultNotificationRecipientResolver } const currentEntityRef = stringifyEntityRef(entity); - if (excludeEntityRefs.includes(currentEntityRef)) { + if (excludedEntityRefs.includes(currentEntityRef)) { return []; } @@ -130,7 +130,7 @@ export class DefaultNotificationRecipientResolver const ret = [ ...new Set([...groupUsers, ...childGroupUsers.flat(2)]), - ].filter(ref => !excludeEntityRefs.includes(ref)); + ].filter(ref => !excludedEntityRefs.includes(ref)); cachedEntityRefs.set(currentEntityRef, ret); return ret; } @@ -145,7 +145,7 @@ export class DefaultNotificationRecipientResolver } if (isUserEntityRef(ownerRef)) { - if (excludeEntityRefs.includes(ownerRef)) { + if (excludedEntityRefs.includes(ownerRef)) { return []; } return [ownerRef]; @@ -171,7 +171,7 @@ export class DefaultNotificationRecipientResolver userEntityRefs: [...new Set(users)] .filter(Boolean) // Need to filter again after resolving users - .filter(ref => !excludeEntityRefs.includes(ref)), + .filter(ref => !excludedEntityRefs.includes(ref)), }; } } diff --git a/plugins/notifications-backend/src/service/router.test.ts b/plugins/notifications-backend/src/service/router.test.ts index 08aa021014..f42a7b4513 100644 --- a/plugins/notifications-backend/src/service/router.test.ts +++ b/plugins/notifications-backend/src/service/router.test.ts @@ -274,6 +274,31 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => { expect(notifications).toHaveLength(1); }); + it('should not send to user entity if excluded', async () => { + const response = await sendNotification({ + recipients: { + type: 'entity', + entityRef: ['user:default/mock'], + excludeEntityRef: 'user:default/mock', + }, + payload: { + title: 'test notification', + metadata: { + attr: 1, + }, + }, + }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); + + const client = await database.getClient(); + const notifications = await client('notification') + .where('user', 'user:default/mock') + .select(); + expect(notifications).toHaveLength(0); + }); + it('should send to group entity', async () => { const response = await sendNotification({ recipients: { @@ -306,6 +331,50 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => { expect(notifications).toHaveLength(1); }); + it('should send not send to group entity if excluded', async () => { + const response = await sendNotification({ + recipients: { + type: 'entity', + entityRef: ['group:default/mock'], + excludeEntityRef: 'group:default/mock', + }, + payload: { + title: 'test notification', + }, + }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); + + const client = await database.getClient(); + const notifications = await client('notification') + .where('user', 'user:default/mock') + .select(); + expect(notifications).toHaveLength(0); + }); + + it('should send not send to user entity if excluded', async () => { + const response = await sendNotification({ + recipients: { + type: 'entity', + entityRef: ['group:default/mock'], + excludeEntityRef: 'user:default/mock', + }, + payload: { + title: 'test notification', + }, + }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); + + const client = await database.getClient(); + const notifications = await client('notification') + .where('user', 'user:default/mock') + .select(); + expect(notifications).toHaveLength(0); + }); + it('should only send one notification per user', async () => { const response = await sendNotification({ recipients: { From 67a3e1a7a4285dda50c97b9a71b4a5949f400962 Mon Sep 17 00:00:00 2001 From: Sydney Achinger Date: Tue, 23 Sep 2025 11:58:54 -0400 Subject: [PATCH 090/211] Implement AbortController request cancellation for search. Signed-off-by: Sydney Achinger --- .changeset/ripe-yaks-brake.md | 6 + plugins/search-react/report.api.md | 14 +- plugins/search-react/src/api.ts | 10 +- .../SearchAutocomplete.test.tsx | 68 +++++--- .../components/SearchBar/SearchBar.test.tsx | 12 ++ .../SearchFilter/SearchFilter.test.tsx | 8 + .../SearchPagination.test.tsx | 12 ++ .../src/context/SearchContext.test.tsx | 151 +++++++++++------- .../src/context/SearchContext.tsx | 36 ++++- plugins/search/src/apis.test.ts | 17 +- plugins/search/src/apis.ts | 9 +- .../HomePageSearchBar.test.tsx | 3 + .../SearchModal/SearchModal.test.tsx | 21 ++- .../SearchType/SearchType.Accordion.test.tsx | 34 ++-- .../SearchType/SearchType.Accordion.tsx | 36 ++++- .../components/SearchType/SearchType.test.tsx | 13 +- 16 files changed, 322 insertions(+), 128 deletions(-) create mode 100644 .changeset/ripe-yaks-brake.md diff --git a/.changeset/ripe-yaks-brake.md b/.changeset/ripe-yaks-brake.md new file mode 100644 index 0000000000..953d0f3192 --- /dev/null +++ b/.changeset/ripe-yaks-brake.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-react': patch +'@backstage/plugin-search': patch +--- + +Implement AbortController request cancellation in SearchBar to prevent overlapping search requests. This change ensures that when users type quickly, previous search requests are properly canceled before new ones start. diff --git a/plugins/search-react/report.api.md b/plugins/search-react/report.api.md index f0ec6030fd..6c9a1cd09d 100644 --- a/plugins/search-react/report.api.md +++ b/plugins/search-react/report.api.md @@ -93,13 +93,23 @@ export class MockSearchApi implements SearchApi { // (undocumented) mockedResults?: SearchResultSet | undefined; // (undocumented) - query(): Promise; + query( + _query: SearchQuery, + _options?: { + signal?: AbortSignal; + }, + ): Promise; } // @public (undocumented) export interface SearchApi { // (undocumented) - query(query: SearchQuery): Promise; + query( + query: SearchQuery, + options?: { + signal?: AbortSignal; + }, + ): Promise; } // @public (undocumented) diff --git a/plugins/search-react/src/api.ts b/plugins/search-react/src/api.ts index 24a247a641..61da8a3d77 100644 --- a/plugins/search-react/src/api.ts +++ b/plugins/search-react/src/api.ts @@ -28,7 +28,10 @@ export const searchApiRef = createApiRef({ * @public */ export interface SearchApi { - query(query: SearchQuery): Promise; + query( + query: SearchQuery, + options?: { signal?: AbortSignal }, + ): Promise; } /** @@ -39,7 +42,10 @@ export interface SearchApi { export class MockSearchApi implements SearchApi { constructor(public mockedResults?: SearchResultSet) {} - query(): Promise { + query( + _query: SearchQuery, + _options?: { signal?: AbortSignal }, + ): Promise { return Promise.resolve(this.mockedResults || { results: [] }); } } diff --git a/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.test.tsx b/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.test.tsx index 173d6c3450..36757dafff 100644 --- a/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.test.tsx +++ b/plugins/search-react/src/components/SearchAutocomplete/SearchAutocomplete.test.tsx @@ -95,12 +95,17 @@ describe('SearchAutocomplete', () => { ); await waitFor(() => { - expect(query).toHaveBeenCalledWith({ - filters: {}, - pageCursor: undefined, - term: options[0], - types: [], - }); + expect(query).toHaveBeenCalledWith( + { + filters: {}, + pageCursor: undefined, + term: options[0], + types: [], + }, + { + signal: expect.any(AbortSignal), + }, + ); }); }); @@ -117,23 +122,33 @@ describe('SearchAutocomplete', () => { ); await waitFor(() => { - expect(query).toHaveBeenCalledWith({ - filters: {}, - pageCursor: undefined, - term: options[0], - types: [], - }); + expect(query).toHaveBeenCalledWith( + { + filters: {}, + pageCursor: undefined, + term: options[0], + types: [], + }, + { + signal: expect.any(AbortSignal), + }, + ); }); await userEvent.click(screen.getByLabelText('Clear')); await waitFor(() => { - expect(query).toHaveBeenCalledWith({ - filters: {}, - pageCursor: undefined, - term: '', - types: [], - }); + expect(query).toHaveBeenCalledWith( + { + filters: {}, + pageCursor: undefined, + term: '', + types: [], + }, + { + signal: expect.any(AbortSignal), + }, + ); }); }); @@ -154,12 +169,17 @@ describe('SearchAutocomplete', () => { await userEvent.click(screen.getByText(options[0])); await waitFor(() => { - expect(query).toHaveBeenCalledWith({ - filters: {}, - pageCursor: undefined, - term: options[0], - types: [], - }); + expect(query).toHaveBeenCalledWith( + { + filters: {}, + pageCursor: undefined, + term: options[0], + types: [], + }, + { + signal: expect.any(AbortSignal), + }, + ); }); }); diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx index 54889792db..977f413f97 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx @@ -171,6 +171,9 @@ describe('SearchBar', () => { expect(searchApiMock.query).toHaveBeenLastCalledWith( expect.objectContaining({ term: value }), + { + signal: expect.any(AbortSignal), + }, ); jest.runAllTimers(); @@ -203,6 +206,9 @@ describe('SearchBar', () => { expect(searchApiMock.query).toHaveBeenLastCalledWith( expect.objectContaining({ term: '' }), + { + signal: expect.any(AbortSignal), + }, ); }); @@ -254,6 +260,9 @@ describe('SearchBar', () => { await waitFor(() => expect(searchApiMock.query).not.toHaveBeenLastCalledWith( expect.objectContaining({ term: value }), + expect.objectContaining({ + signal: expect.any(AbortSignal), + }), ), ); @@ -265,6 +274,9 @@ describe('SearchBar', () => { expect(searchApiMock.query).toHaveBeenLastCalledWith( expect.objectContaining({ term: value }), + { + signal: expect.any(AbortSignal), + }, ); jest.runAllTimers(); diff --git a/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx b/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx index d8d3b75aa2..7191b842d3 100644 --- a/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx +++ b/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx @@ -178,6 +178,7 @@ describe('SearchFilter', () => { await waitFor(() => { expect(searchApiMock.query).toHaveBeenLastCalledWith( expect.objectContaining({ filters: { field: [values[0]] } }), + { signal: expect.any(Object) }, ); }); @@ -186,6 +187,7 @@ describe('SearchFilter', () => { await waitFor(() => { expect(searchApiMock.query).toHaveBeenLastCalledWith( expect.objectContaining({ filters: {} }), + { signal: expect.any(Object) }, ); }); }); @@ -217,6 +219,7 @@ describe('SearchFilter', () => { expect.objectContaining({ filters: { ...filters, field: [values[0]] }, }), + { signal: expect.any(Object) }, ); }); @@ -225,6 +228,7 @@ describe('SearchFilter', () => { await waitFor(() => { expect(searchApiMock.query).toHaveBeenLastCalledWith( expect.objectContaining({ filters }), + { signal: expect.any(Object) }, ); }); }); @@ -421,6 +425,7 @@ describe('SearchFilter', () => { expect.objectContaining({ filters: { [name]: values[0] }, }), + { signal: expect.any(AbortSignal) }, ); }); @@ -437,6 +442,7 @@ describe('SearchFilter', () => { expect.objectContaining({ filters: {}, }), + { signal: expect.any(AbortSignal) }, ); }); }); @@ -479,6 +485,7 @@ describe('SearchFilter', () => { expect.objectContaining({ filters: { ...filters, [name]: values[0] }, }), + { signal: expect.any(AbortSignal) }, ); }); @@ -493,6 +500,7 @@ describe('SearchFilter', () => { await waitFor(() => { expect(searchApiMock.query).toHaveBeenLastCalledWith( expect.objectContaining({ filters }), + { signal: expect.any(AbortSignal) }, ); }); }); diff --git a/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx b/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx index d601caadd9..e482dc3a75 100644 --- a/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx +++ b/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx @@ -197,6 +197,9 @@ describe('SearchPagination', () => { expect.objectContaining({ pageLimit: 10, }), + { + signal: expect.any(AbortSignal), + }, ); }); @@ -229,6 +232,9 @@ describe('SearchPagination', () => { expect.objectContaining({ pageCursor: 'Mg==', // page: 2 }), + { + signal: expect.any(AbortSignal), + }, ); await userEvent.click(screen.getByLabelText('Previous page')); @@ -239,6 +245,9 @@ describe('SearchPagination', () => { expect.objectContaining({ pageCursor: 'MQ==', // page: 1 }), + { + signal: expect.any(AbortSignal), + }, ); }); @@ -271,6 +280,9 @@ describe('SearchPagination', () => { pageCursor: undefined, pageLimit: 10, }), + { + signal: expect.any(AbortSignal), + }, ); }); }); diff --git a/plugins/search-react/src/context/SearchContext.test.tsx b/plugins/search-react/src/context/SearchContext.test.tsx index f604183c7f..cbdf684b70 100644 --- a/plugins/search-react/src/context/SearchContext.test.tsx +++ b/plugins/search-react/src/context/SearchContext.test.tsx @@ -264,11 +264,14 @@ describe('SearchContext', () => { result.current.setTerm(term); }); - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - term, - types: ['*'], - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + term, + types: ['*'], + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); }); it('When types is set', async () => { @@ -286,11 +289,14 @@ describe('SearchContext', () => { result.current.setTypes(types); }); - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - types, - term: '', - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + types, + term: '', + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); }); it('When filters are set', async () => { @@ -308,11 +314,14 @@ describe('SearchContext', () => { result.current.setFilters(filters); }); - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - filters, - term: '', - types: ['*'], - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + filters, + term: '', + types: ['*'], + }, + { signal: expect.any(AbortSignal) }, + ); }); it('When page limit is set', async () => { @@ -330,12 +339,15 @@ describe('SearchContext', () => { result.current.setPageLimit(pageLimit); }); - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - pageLimit, - term: '', - types: ['*'], - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + pageLimit, + term: '', + types: ['*'], + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); }); it('When page cursor is set', async () => { @@ -353,12 +365,15 @@ describe('SearchContext', () => { result.current.setPageCursor(pageCursor); }); - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - pageCursor, - term: '', - types: ['*'], - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + pageCursor, + term: '', + types: ['*'], + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); }); it('provides function for fetch the next page', async () => { @@ -382,12 +397,15 @@ describe('SearchContext', () => { result.current.fetchNextPage!(); }); - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - term: '', - types: ['*'], - filters: {}, - pageCursor: 'NEXT', - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + term: '', + types: ['*'], + filters: {}, + pageCursor: 'NEXT', + }, + { signal: expect.any(AbortSignal) }, + ); }); it('provides function for fetch the previous page', async () => { @@ -410,12 +428,15 @@ describe('SearchContext', () => { result.current.fetchPreviousPage!(); }); - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - term: '', - types: ['*'], - filters: {}, - pageCursor: 'PREVIOUS', - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + term: '', + types: ['*'], + filters: {}, + pageCursor: 'PREVIOUS', + }, + { signal: expect.any(AbortSignal) }, + ); }); }); @@ -456,11 +477,14 @@ describe('SearchContext', () => { }); await waitFor(() => { - expect(searchApiMock.query).toHaveBeenCalledWith({ - term: '', - types: ['*'], - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenCalledWith( + { + term: '', + types: ['*'], + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); expect(analyticsApiMock.captureEvent).not.toHaveBeenCalled(); }); @@ -470,11 +494,14 @@ describe('SearchContext', () => { }); await waitFor(() => { - expect(searchApiMock.query).toHaveBeenCalledWith({ - term: 'eva', - types: ['*'], - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenCalledWith( + { + term: 'eva', + types: ['*'], + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); expect(analyticsApiMock.captureEvent).toHaveBeenCalledWith({ action: 'search', subject: 'eva', @@ -493,11 +520,14 @@ describe('SearchContext', () => { }); await waitFor(() => { - expect(searchApiMock.query).toHaveBeenCalledWith({ - term: 'eva.m', - types: ['*'], - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenCalledWith( + { + term: 'eva.m', + types: ['*'], + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); expect(analyticsApiMock.captureEvent).toHaveBeenCalledWith({ action: 'search', subject: 'eva.m', @@ -531,11 +561,14 @@ describe('SearchContext', () => { }); await waitFor(() => { - expect(searchApiMock.query).toHaveBeenLastCalledWith({ - term: 'term', - types: ['*'], - filters: {}, - }); + expect(searchApiMock.query).toHaveBeenLastCalledWith( + { + term: 'term', + types: ['*'], + filters: {}, + }, + { signal: expect.any(AbortSignal) }, + ); expect(analyticsApiMock.captureEvent).toHaveBeenCalledWith({ action: 'search', subject: 'term', diff --git a/plugins/search-react/src/context/SearchContext.tsx b/plugins/search-react/src/context/SearchContext.tsx index 3bd29c881a..ba06010789 100644 --- a/plugins/search-react/src/context/SearchContext.tsx +++ b/plugins/search-react/src/context/SearchContext.tsx @@ -23,6 +23,7 @@ import { useCallback, useContext, useEffect, + useRef, useState, } from 'react'; import useAsync, { AsyncState } from 'react-use/esm/useAsync'; @@ -132,15 +133,28 @@ const useSearchContextValue = ( const prevTerm = usePrevious(term); const prevFilters = usePrevious(filters); + const abortControllerRef = useRef(null); const result = useAsync(async () => { - const resultSet = await searchApi.query({ - term, - types, - filters, - pageLimit, - pageCursor, - }); + // Here we cancel the previous request before making a new one + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + + const controller = new AbortController(); + abortControllerRef.current = controller; + + const resultSet = await searchApi.query( + { + term, + types, + filters, + pageLimit, + pageCursor, + }, + { signal: controller.signal }, + ); + if (term) { analytics.captureEvent('search', term, { value: resultSet.numberOfResults, @@ -162,6 +176,14 @@ const useSearchContextValue = ( setPageCursor(result.value?.previousPageCursor); }, [result.value?.previousPageCursor]); + useEffect(() => { + return () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + }; + }, []); + useEffect(() => { // Any time a term is reset, we want to start from page 0. // Only reset the term if it has been modified by the user at least once, the initial state must not reset the term. diff --git a/plugins/search/src/apis.test.ts b/plugins/search/src/apis.test.ts index 15745d2e68..1bb245dc9f 100644 --- a/plugins/search/src/apis.test.ts +++ b/plugins/search/src/apis.test.ts @@ -52,10 +52,19 @@ describe('apis', () => { identityApi.getCredentials.mockResolvedValue({}); await client.query(query); expect(getBaseUrl).toHaveBeenLastCalledWith('search'); - expect(mockFetch).toHaveBeenLastCalledWith( - `${baseUrl}/query?term=`, - undefined, - ); + expect(mockFetch).toHaveBeenLastCalledWith(`${baseUrl}/query?term=`, { + signal: undefined, + }); + }); + + it('Fetch is called with abort signal when provided', async () => { + identityApi.getCredentials.mockResolvedValue({}); + const abortController = new AbortController(); + await client.query(query, { signal: abortController.signal }); + expect(getBaseUrl).toHaveBeenLastCalledWith('search'); + expect(mockFetch).toHaveBeenLastCalledWith(`${baseUrl}/query?term=`, { + signal: abortController.signal, + }); }); it('Resolves JSON from fetch response', async () => { diff --git a/plugins/search/src/apis.ts b/plugins/search/src/apis.ts index b56d88a22c..03e6b6d1d4 100644 --- a/plugins/search/src/apis.ts +++ b/plugins/search/src/apis.ts @@ -30,12 +30,17 @@ export class SearchClient implements SearchApi { this.fetchApi = options.fetchApi; } - async query(query: SearchQuery): Promise { + async query( + query: SearchQuery, + options?: { signal?: AbortSignal }, + ): Promise { const queryString = qs.stringify(query); const url = `${await this.discoveryApi.getBaseUrl( 'search', )}/query?${queryString}`; - const response = await this.fetchApi.fetch(url); + const response = await this.fetchApi.fetch(url, { + signal: options?.signal, + }); if (!response.ok) { throw await ResponseError.fromResponse(response); diff --git a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.test.tsx b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.test.tsx index 37b7c13665..e7c47b7d30 100644 --- a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.test.tsx +++ b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.test.tsx @@ -48,6 +48,9 @@ describe('', () => { expect(searchApiMock.query).toHaveBeenCalledWith( expect.objectContaining({ term: '' }), + { + signal: expect.any(AbortSignal), + }, ); await userEvent.type(screen.getByLabelText('Search'), 'term{enter}'); diff --git a/plugins/search/src/components/SearchModal/SearchModal.test.tsx b/plugins/search/src/components/SearchModal/SearchModal.test.tsx index c404f54e06..8d8e1bb9b9 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.test.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.test.tsx @@ -88,7 +88,9 @@ describe('SearchModal', () => { ); expect(screen.getByRole('dialog')).toBeInTheDocument(); - expect(searchApiMock.query).toHaveBeenCalledWith(initialState); + expect(searchApiMock.query).toHaveBeenCalledWith(initialState, { + signal: expect.any(AbortSignal), + }); }); it('Should create a local search context if a parent is not defined', async () => { @@ -104,12 +106,15 @@ describe('SearchModal', () => { ); expect(screen.getByRole('dialog')).toBeInTheDocument(); - expect(searchApiMock.query).toHaveBeenCalledWith({ - term: '', - filters: {}, - types: [], - pageCursor: undefined, - }); + expect(searchApiMock.query).toHaveBeenCalledWith( + { + term: '', + filters: {}, + types: [], + pageCursor: undefined, + }, + { signal: expect.any(AbortSignal) }, + ); }); it('Should render a custom Modal correctly', async () => { @@ -200,6 +205,7 @@ describe('SearchModal', () => { expect(searchApiMock.query).toHaveBeenCalledWith( expect.objectContaining({ term: 'term' }), + { signal: expect.any(AbortSignal) }, ); const input = screen.getByLabelText('Search'); @@ -232,6 +238,7 @@ describe('SearchModal', () => { expect(searchApiMock.query).toHaveBeenCalledWith( expect.objectContaining({ term: 'term' }), + { signal: expect.any(AbortSignal) }, ); const fullResultsBtn = screen.getByRole('button', { diff --git a/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx b/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx index 1245ceed5a..a0dd2976e6 100644 --- a/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx +++ b/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx @@ -157,18 +157,28 @@ describe('SearchType.Accordion', () => { , ); - expect(searchApiMock.query).toHaveBeenCalledWith({ - term: 'abc', - types: [], - filters: { foo: 'bar' }, - pageLimit: 0, - }); - expect(searchApiMock.query).toHaveBeenCalledWith({ - term: 'abc', - types: [expectedType.value], - filters: {}, - pageLimit: 0, - }); + expect(searchApiMock.query).toHaveBeenCalledWith( + { + term: 'abc', + types: [], + filters: { foo: 'bar' }, + pageLimit: 0, + }, + { + signal: expect.any(AbortSignal), + }, + ); + expect(searchApiMock.query).toHaveBeenCalledWith( + { + term: 'abc', + types: [expectedType.value], + filters: {}, + pageLimit: 0, + }, + { + signal: expect.any(AbortSignal), + }, + ); await waitFor(() => { const countLabels = getAllByText('1234 results'); expect(countLabels.length).toEqual(2); diff --git a/plugins/search/src/components/SearchType/SearchType.Accordion.tsx b/plugins/search/src/components/SearchType/SearchType.Accordion.tsx index 65c96b1e60..8168418ed2 100644 --- a/plugins/search/src/components/SearchType/SearchType.Accordion.tsx +++ b/plugins/search/src/components/SearchType/SearchType.Accordion.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { cloneElement, Fragment, useEffect, useState } from 'react'; +import { cloneElement, Fragment, useEffect, useRef, useState } from 'react'; import { useApi } from '@backstage/core-plugin-api'; import { searchApiRef, useSearch } from '@backstage/plugin-search-react'; import Accordion from '@material-ui/core/Accordion'; @@ -86,6 +86,7 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { const [expanded, setExpanded] = useState(true); const { defaultValue, name, showCounts, types: givenTypes } = props; const { t } = useTranslationRef(searchTranslationRef); + const abortControllerRef = useRef(null); const toggleExpanded = () => setExpanded(prevState => !prevState); const handleClick = (type: string) => { @@ -117,18 +118,29 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { if (!showCounts) { return {}; } + // Here we cancel the previous requests before making a new one + // All requests are made with a new AbortController signal + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + + const controller = new AbortController(); + abortControllerRef.current = controller; const counts = await Promise.all( definedTypes .map(type => type.value) .map(async type => { - const { numberOfResults } = await searchApi.query({ - term, - types: type ? [type] : [], - filters: - types.includes(type) || (!types.length && !type) ? filters : {}, - pageLimit: 0, - }); + const { numberOfResults } = await searchApi.query( + { + term, + types: type ? [type] : [], + filters: + types.includes(type) || (!types.length && !type) ? filters : {}, + pageLimit: 0, + }, + { signal: controller.signal }, + ); return [ type, @@ -145,6 +157,14 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { return Object.fromEntries(counts); }, [filters, showCounts, term, types]); + useEffect(() => { + return () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + }; + }, []); + return ( diff --git a/plugins/search/src/components/SearchType/SearchType.test.tsx b/plugins/search/src/components/SearchType/SearchType.test.tsx index 231463c9bf..e62ac19fee 100644 --- a/plugins/search/src/components/SearchType/SearchType.test.tsx +++ b/plugins/search/src/components/SearchType/SearchType.test.tsx @@ -192,6 +192,9 @@ describe('SearchType', () => { expect.objectContaining({ types: [values[0]], }), + { + signal: expect.any(AbortSignal), + }, ); }); @@ -240,6 +243,9 @@ describe('SearchType', () => { expect.objectContaining({ types: [...typeValues, values[0]], }), + { + signal: expect.any(AbortSignal), + }, ); }); @@ -253,7 +259,12 @@ describe('SearchType', () => { await waitFor(() => { expect(searchApiMock.query).toHaveBeenLastCalledWith( - expect.objectContaining([]), + expect.objectContaining({ + types: typeValues, + }), + { + signal: expect.any(AbortSignal), + }, ); }); }); From e02473eeb39d4c48b4bbada88a9c67a6cd726232 Mon Sep 17 00:00:00 2001 From: Sydney Achinger Date: Tue, 23 Sep 2025 16:15:43 -0400 Subject: [PATCH 091/211] Update test Signed-off-by: Sydney Achinger --- .../search/components/TechDocsSearch.test.tsx | 92 +++++++++++-------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx b/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx index f7e67e12cf..c140e830ad 100644 --- a/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx +++ b/plugins/techdocs/src/search/components/TechDocsSearch.test.tsx @@ -84,16 +84,21 @@ it('should trigger query when autocomplete input changed', async () => { ); await singleResult; - expect(querySpy).toHaveBeenCalledWith({ - filters: { - kind: 'Testable', - name: 'test', - namespace: 'testspace', + expect(querySpy).toHaveBeenCalledWith( + { + filters: { + kind: 'Testable', + name: 'test', + namespace: 'testspace', + }, + pageCursor: '', + term: '', + types: ['techdocs'], }, - pageCursor: '', - term: '', - types: ['techdocs'], - }); + { + signal: expect.any(AbortSignal), + }, + ); const autocomplete = screen.getByTestId('techdocs-search-bar'); const input = within(autocomplete).getByRole('textbox'); @@ -105,16 +110,21 @@ it('should trigger query when autocomplete input changed', async () => { await singleResult; await waitFor(() => - expect(querySpy).toHaveBeenCalledWith({ - filters: { - kind: 'Testable', - name: 'test', - namespace: 'testspace', + expect(querySpy).toHaveBeenCalledWith( + { + filters: { + kind: 'Testable', + name: 'test', + namespace: 'testspace', + }, + pageCursor: '', + term: 'asdf', + types: ['techdocs'], }, - pageCursor: '', - term: 'asdf', - types: ['techdocs'], - }), + { + signal: expect.any(AbortSignal), + }, + ), ); }); @@ -144,16 +154,21 @@ it('should update filter values when a new entityName is provided', async () => await renderInTestApp(); await singleResult; - expect(querySpy).toHaveBeenCalledWith({ - filters: { - kind: 'Testable', - name: 'test', - namespace: 'testspace', + expect(querySpy).toHaveBeenCalledWith( + { + filters: { + kind: 'Testable', + name: 'test', + namespace: 'testspace', + }, + pageCursor: '', + term: '', + types: ['techdocs'], }, - pageCursor: '', - term: '', - types: ['techdocs'], - }); + { + signal: expect.any(AbortSignal), + }, + ); const button = screen.getByText('Update Entity'); act(() => { @@ -162,15 +177,20 @@ it('should update filter values when a new entityName is provided', async () => await singleResult; await waitFor(() => - expect(querySpy).toHaveBeenCalledWith({ - filters: { - kind: 'TestableDiff', - name: 'test-diff', - namespace: 'testspace-diff', + expect(querySpy).toHaveBeenCalledWith( + { + filters: { + kind: 'TestableDiff', + name: 'test-diff', + namespace: 'testspace-diff', + }, + pageCursor: '', + term: '', + types: ['techdocs'], }, - pageCursor: '', - term: '', - types: ['techdocs'], - }), + { + signal: expect.any(AbortSignal), + }, + ), ); }); From 482df90524e6c665d3e053dc07c9b67cfcdb4724 Mon Sep 17 00:00:00 2001 From: Sydney Achinger <78113809+squid-ney@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:53:07 -0400 Subject: [PATCH 092/211] Update changeset Signed-off-by: Sydney Achinger <78113809+squid-ney@users.noreply.github.com> Signed-off-by: Sydney Achinger --- .changeset/ripe-yaks-brake.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/ripe-yaks-brake.md b/.changeset/ripe-yaks-brake.md index 953d0f3192..3b3097011b 100644 --- a/.changeset/ripe-yaks-brake.md +++ b/.changeset/ripe-yaks-brake.md @@ -3,4 +3,4 @@ '@backstage/plugin-search': patch --- -Implement AbortController request cancellation in SearchBar to prevent overlapping search requests. This change ensures that when users type quickly, previous search requests are properly canceled before new ones start. +Implemented AbortController request cancellation for overlapping search requests. This change ensures that when users type quickly, previous search requests are properly canceled before new ones start. From 3947797480cd22f361dec2c7a9a37abc6dfbe428 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 23 Sep 2025 23:55:35 +0200 Subject: [PATCH 093/211] docs: fix grid usage Signed-off-by: Vincenzo Scamporlino --- docs-ui/src/content/components/grid.props.ts | 54 ++++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/docs-ui/src/content/components/grid.props.ts b/docs-ui/src/content/components/grid.props.ts index 34d89785cc..6f037cd64f 100644 --- a/docs-ui/src/content/components/grid.props.ts +++ b/docs-ui/src/content/components/grid.props.ts @@ -43,12 +43,12 @@ export const gridItemPropDefs: Record = { values: [...columnsValues, 'full'], responsive: true, }, - start: { + colStart: { type: 'enum | string', values: [...columnsValues, 'auto'], responsive: true, }, - end: { + colEnd: { type: 'enum | string', values: [...columnsValues, 'auto'], responsive: true, @@ -60,7 +60,7 @@ export const gridItemPropDefs: Record = { export const gridUsageSnippet = `import { Grid } from '@backstage/ui'; -`; +`; export const gridDefaultSnippet = ` @@ -68,44 +68,44 @@ export const gridDefaultSnippet = ` `; -export const gridSimpleSnippet = ` +export const gridSimpleSnippet = ` Hello World Hello World Hello World -`; +`; -export const gridComplexSnippet = ` - - Hello World +export const gridComplexSnippet = ` + + Hello World - - Hello World + + Hello World -`; +`; -export const gridMixingRowsSnippet = ` - - Hello World +export const gridMixingRowsSnippet = ` + + Hello World - - Hello World + + Hello World - - Hello World + + Hello World -`; +`; -export const gridResponsiveSnippet = ` +export const gridResponsiveSnippet = ` - Hello World + - Hello World + Hello World -`; +`; -export const gridStartEndSnippet = ` - - Hello World +export const gridStartEndSnippet = ` + + Hello World -`; +`; From b45b0945dd17699c31bb523856ab85c646fe4398 Mon Sep 17 00:00:00 2001 From: Hellgren Heikki Date: Wed, 24 Sep 2025 06:55:14 +0300 Subject: [PATCH 094/211] fix(config): allow colon to be in the config key Signed-off-by: Hellgren Heikki --- .changeset/six-cooks-battle.md | 6 ++++++ packages/config-loader/src/sources/EnvConfigSource.ts | 2 +- packages/config/src/reader.test.ts | 5 +++++ packages/config/src/reader.ts | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 .changeset/six-cooks-battle.md diff --git a/.changeset/six-cooks-battle.md b/.changeset/six-cooks-battle.md new file mode 100644 index 0000000000..ecf856225e --- /dev/null +++ b/.changeset/six-cooks-battle.md @@ -0,0 +1,6 @@ +--- +'@backstage/config-loader': patch +'@backstage/config': patch +--- + +Allow colon to be used as config key. diff --git a/packages/config-loader/src/sources/EnvConfigSource.ts b/packages/config-loader/src/sources/EnvConfigSource.ts index d724f00c6d..988110b370 100644 --- a/packages/config-loader/src/sources/EnvConfigSource.ts +++ b/packages/config-loader/src/sources/EnvConfigSource.ts @@ -85,7 +85,7 @@ export class EnvConfigSource implements ConfigSource { const ENV_PREFIX = 'APP_CONFIG_'; // Update the same pattern in config package if this is changed -const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i; +const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; /** * Read runtime configuration from the environment. diff --git a/packages/config/src/reader.test.ts b/packages/config/src/reader.test.ts index 3f6b9ac40a..bb16fbeaa1 100644 --- a/packages/config/src/reader.test.ts +++ b/packages/config/src/reader.test.ts @@ -43,6 +43,7 @@ const DATA = { null: null, string: 'string', strings: ['string1', 'string2'], + 'with:colon': 'yes', }, nestlings: [{ boolean: true }, { string: 'string' }, { number: 42 }] as {}[], }; @@ -57,6 +58,7 @@ function expectValidValues(config: ConfigReader) { expect(config.has('nested.one')).toBe(true); expect(config.has('nested.missing')).toBe(false); expect(config.has('nested.null')).toBe(false); + expect(config.has('nested.with:colon')).toBe(true); expect(config.getNumber('zero')).toBe(0); expect(config.getNumber('one')).toBe(1); expect(config.getNumber('zeroString')).toBe(0); @@ -84,8 +86,11 @@ function expectValidValues(config: ConfigReader) { null: undefined, string: 'string', strings: ['string1', 'string2'], + 'with:colon': 'yes', }); expect(config.getConfig('nested').getString('string')).toBe('string'); + expect(config.getString('nested.string')).toBe('string'); + expect(config.getString('nested.with:colon')).toBe('yes'); expect(config.getOptionalConfig('nested')!.getStringArray('strings')).toEqual( ['string1', 'string2'], ); diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 2e83de9bf2..7ad0cf738d 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { JsonValue, JsonObject } from '@backstage/types'; +import { JsonObject, JsonValue } from '@backstage/types'; import { AppConfig, Config } from './types'; // Update the same pattern in config-loader package if this is changed -const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i; +const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; function isObject(value: JsonValue | undefined): value is JsonObject { return typeof value === 'object' && value !== null && !Array.isArray(value); From 93e1aa0133e9229b0257c38b0a5b3e5ce23b531a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Edeg=C3=A5rd?= Date: Wed, 24 Sep 2025 07:23:01 +0000 Subject: [PATCH 095/211] Simplify username usage. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Henrik Edegård --- .../src/lib/SlackNotificationProcessor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts index f050b3b677..96d4b2ff3e 100644 --- a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts +++ b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts @@ -212,7 +212,7 @@ export class SlackNotificationProcessor implements NotificationProcessor { const payload = toChatPostMessageArgs({ channel, payload: options.payload, - ...(this.username && { username: this.username }), + username: this.username, }); this.logger.debug( @@ -271,7 +271,7 @@ export class SlackNotificationProcessor implements NotificationProcessor { toChatPostMessageArgs({ channel, payload: formattedPayload, - ...(this.username && { username: this.username }), + username: this.username, }), ); From 075e0648b8af051133ccd09742e136af69dbc904 Mon Sep 17 00:00:00 2001 From: Hellgren Heikki Date: Wed, 24 Sep 2025 10:23:35 +0300 Subject: [PATCH 096/211] feat(scaffolder): add missing form fields for the nfs Signed-off-by: Hellgren Heikki --- .changeset/wide-flies-jog.md | 5 + plugins/scaffolder/report-alpha.api.md | 126 +++++++++++++++++- plugins/scaffolder/report.api.md | 75 ++++++++++- plugins/scaffolder/src/alpha/extensions.tsx | 67 +++++++++- .../src/alpha/fields/EntityNamePicker.ts | 28 ++++ .../src/alpha/fields/EntityPicker.ts | 24 ++++ .../src/alpha/fields/EntityTagsPicker.ts | 24 ++++ .../src/alpha/fields/MultiEntityPicker.ts | 24 ++++ .../src/alpha/fields/MyGroupsPicker.ts | 24 ++++ .../src/alpha/fields/OwnedEntityPicker.ts | 24 ++++ .../src/alpha/fields/OwnerPicker.ts | 24 ++++ .../src/alpha/fields/RepoBranchPicker.ts | 24 ++++ plugins/scaffolder/src/alpha/plugin.tsx | 20 ++- .../fields/EntityNamePicker/index.ts | 1 + .../fields/EntityNamePicker/schema.ts | 5 +- .../fields/EntityNamePicker/validation.ts | 3 + .../fields/MultiEntityPicker/schema.ts | 7 + .../scaffolder/src/components/fields/index.ts | 3 + 18 files changed, 496 insertions(+), 12 deletions(-) create mode 100644 .changeset/wide-flies-jog.md create mode 100644 plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts create mode 100644 plugins/scaffolder/src/alpha/fields/EntityPicker.ts create mode 100644 plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts create mode 100644 plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts create mode 100644 plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts create mode 100644 plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts create mode 100644 plugins/scaffolder/src/alpha/fields/OwnerPicker.ts create mode 100644 plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts diff --git a/.changeset/wide-flies-jog.md b/.changeset/wide-flies-jog.md new file mode 100644 index 0000000000..4fbafe66a1 --- /dev/null +++ b/.changeset/wide-flies-jog.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Added missing form fields for the new frontend system. diff --git a/plugins/scaffolder/report-alpha.api.md b/plugins/scaffolder/report-alpha.api.md index 534d136e6f..505688247f 100644 --- a/plugins/scaffolder/report-alpha.api.md +++ b/plugins/scaffolder/report-alpha.api.md @@ -226,6 +226,126 @@ const _default: OverridableFrontendPlugin< routeRef?: RouteRef; }; }>; + 'scaffolder-form-field:scaffolder/entity-name-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'entity-name-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; + 'scaffolder-form-field:scaffolder/entity-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'entity-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; + 'scaffolder-form-field:scaffolder/entity-tags-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'entity-tags-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; + 'scaffolder-form-field:scaffolder/multi-entity-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'multi-entity-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; + 'scaffolder-form-field:scaffolder/my-groups-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'my-groups-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; + 'scaffolder-form-field:scaffolder/owned-entity-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'owned-entity-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; + 'scaffolder-form-field:scaffolder/owner-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'owner-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; + 'scaffolder-form-field:scaffolder/repo-branch-picker': ExtensionDefinition<{ + kind: 'scaffolder-form-field'; + name: 'repo-branch-picker'; + config: {}; + configInput: {}; + output: ExtensionDataRef< + () => Promise, + 'scaffolder.form-field-loader', + {} + >; + inputs: {}; + params: { + field: () => Promise; + }; + }>; 'scaffolder-form-field:scaffolder/repo-url-picker': ExtensionDefinition<{ kind: 'scaffolder-form-field'; name: 'repo-url-picker'; @@ -402,10 +522,10 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'ongoingTask.title': 'Run of'; readonly 'ongoingTask.contextMenu.cancel': 'Cancel'; readonly 'ongoingTask.contextMenu.startOver': 'Start Over'; - readonly 'ongoingTask.contextMenu.retry': 'Retry'; readonly 'ongoingTask.contextMenu.hideLogs': 'Hide Logs'; readonly 'ongoingTask.contextMenu.showLogs': 'Show Logs'; readonly 'ongoingTask.contextMenu.hideButtonBar': 'Hide Button Bar'; + readonly 'ongoingTask.contextMenu.retry': 'Retry'; readonly 'ongoingTask.contextMenu.showButtonBar': 'Show Button Bar'; readonly 'ongoingTask.subtitle': 'Task {{taskId}}'; readonly 'ongoingTask.pageTitle.hasTemplateName': 'Run of {{templateName}}'; @@ -488,8 +608,8 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'templateListPage.pageTitle': 'Create a new component'; readonly 'templateListPage.templateGroups.defaultTitle': 'Templates'; readonly 'templateListPage.templateGroups.otherTitle': 'Other Templates'; - readonly 'templateListPage.contentHeader.supportButtonTitle': 'Create new software components using standard templates. Different templates create different kinds of components (services, websites, documentation, ...).'; readonly 'templateListPage.contentHeader.registerExistingButtonTitle': 'Register Existing Component'; + readonly 'templateListPage.contentHeader.supportButtonTitle': 'Create new software components using standard templates. Different templates create different kinds of components (services, websites, documentation, ...).'; readonly 'templateListPage.additionalLinksForEntity.viewTechDocsTitle': 'View TechDocs'; readonly 'templateWizardPage.title': 'Create a new component'; readonly 'templateWizardPage.subtitle': 'Create new software components using standard templates in your organization'; @@ -502,8 +622,8 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'templateEditorToolbar.addToCatalogDialogTitle': 'Publish changes'; readonly 'templateEditorToolbar.addToCatalogDialogContent.stepsIntroduction': 'Follow the instructions below to create or update a template:'; readonly 'templateEditorToolbar.addToCatalogDialogContent.stepsListItems': 'Save the template files in a local directory\nCreate a pull request to a new or existing git repository\nIf the template already exists, the changes will be reflected in the software catalog once the pull request gets merged\nBut if you are creating a new template, follow the documentation linked below to register the new template repository in software catalog'; - readonly 'templateEditorToolbar.addToCatalogDialogActions.documentationUrl': 'https://backstage.io/docs/features/software-templates/adding-templates/'; readonly 'templateEditorToolbar.addToCatalogDialogActions.documentationButton': 'Go to the documentation'; + readonly 'templateEditorToolbar.addToCatalogDialogActions.documentationUrl': 'https://backstage.io/docs/features/software-templates/adding-templates/'; readonly 'templateEditorToolbarFileMenu.button': 'File'; readonly 'templateEditorToolbarFileMenu.options.openDirectory': 'Open template directory'; readonly 'templateEditorToolbarFileMenu.options.createDirectory': 'Create template directory'; diff --git a/plugins/scaffolder/report.api.md b/plugins/scaffolder/report.api.md index a4a339f01e..b1a6b62e9d 100644 --- a/plugins/scaffolder/report.api.md +++ b/plugins/scaffolder/report.api.md @@ -70,6 +70,15 @@ export const EntityNamePickerFieldExtension: FieldExtensionComponent_2< any >; +// @public (undocumented) +export const EntityNamePickerFieldSchema: FieldSchema_2; + +// @public (undocumented) +export const entityNamePickerValidation: ( + value: string, + validation: FieldValidation, +) => void; + // @public export const EntityPickerFieldExtension: FieldExtensionComponent_2< string, @@ -228,6 +237,39 @@ export const MultiEntityPickerFieldExtension: FieldExtensionComponent_2< } >; +// @public (undocumented) +export const MultiEntityPickerFieldSchema: FieldSchema_2< + string[], + { + defaultKind?: string | undefined; + defaultNamespace?: string | false | undefined; + catalogFilter?: + | Record< + string, + | string + | string[] + | { + exists?: boolean | undefined; + } + > + | Record< + string, + | string + | string[] + | { + exists?: boolean | undefined; + } + >[] + | undefined; + allowArbitraryValues?: boolean | undefined; + } +>; + +// @public +export type MultiEntityPickerUiOptions = NonNullable< + (typeof MultiEntityPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; + // @public export const MyGroupsPickerFieldExtension: FieldExtensionComponent_2< string, @@ -380,9 +422,9 @@ export const RepoBranchPickerFieldExtension: FieldExtensionComponent_2< | { azure?: string[] | undefined; github?: string[] | undefined; - gitlab?: string[] | undefined; bitbucket?: string[] | undefined; gerrit?: string[] | undefined; + gitlab?: string[] | undefined; gitea?: string[] | undefined; } | undefined; @@ -391,6 +433,33 @@ export const RepoBranchPickerFieldExtension: FieldExtensionComponent_2< } >; +// @public (undocumented) +export const RepoBranchPickerFieldSchema: FieldSchema_2< + string, + { + requestUserCredentials?: + | { + secretsKey: string; + additionalScopes?: + | { + azure?: string[] | undefined; + github?: string[] | undefined; + bitbucket?: string[] | undefined; + gerrit?: string[] | undefined; + gitlab?: string[] | undefined; + gitea?: string[] | undefined; + } + | undefined; + } + | undefined; + } +>; + +// @public +export type RepoBranchPickerUiOptions = NonNullable< + (typeof RepoBranchPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; + // @public export const repoPickerValidation: ( value: string, @@ -416,9 +485,9 @@ export const RepoUrlPickerFieldExtension: FieldExtensionComponent_2< | { azure?: string[] | undefined; github?: string[] | undefined; - gitlab?: string[] | undefined; bitbucket?: string[] | undefined; gerrit?: string[] | undefined; + gitlab?: string[] | undefined; gitea?: string[] | undefined; } | undefined; @@ -443,9 +512,9 @@ export const RepoUrlPickerFieldSchema: FieldSchema_2< | { azure?: string[] | undefined; github?: string[] | undefined; - gitlab?: string[] | undefined; bitbucket?: string[] | undefined; gerrit?: string[] | undefined; + gitlab?: string[] | undefined; gitea?: string[] | undefined; } | undefined; diff --git a/plugins/scaffolder/src/alpha/extensions.tsx b/plugins/scaffolder/src/alpha/extensions.tsx index 803644d95a..cb69e691b8 100644 --- a/plugins/scaffolder/src/alpha/extensions.tsx +++ b/plugins/scaffolder/src/alpha/extensions.tsx @@ -19,13 +19,13 @@ import { convertLegacyRouteRef, } from '@backstage/core-compat-api'; import { - NavItemBlueprint, - PageBlueprint, ApiBlueprint, + createExtensionInput, discoveryApiRef, fetchApiRef, identityApiRef, - createExtensionInput, + NavItemBlueprint, + PageBlueprint, } from '@backstage/frontend-plugin-api'; import { rootRouteRef } from '../routes'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; @@ -72,6 +72,67 @@ export const repoUrlPickerFormField = FormFieldBlueprint.make({ }, }); +export const entityNamePickerFormField = FormFieldBlueprint.make({ + name: 'entity-name-picker', + params: { + field: () => + import('./fields/EntityNamePicker').then(m => m.EntityNamePicker), + }, +}); + +export const entityPickerFormField = FormFieldBlueprint.make({ + name: 'entity-picker', + params: { + field: () => import('./fields/EntityPicker').then(m => m.EntityPicker), + }, +}); + +export const ownerPickerFormField = FormFieldBlueprint.make({ + name: 'owner-picker', + params: { + field: () => import('./fields/OwnerPicker').then(m => m.OwnerPicker), + }, +}); + +export const entityTagsPickerFormField = FormFieldBlueprint.make({ + name: 'entity-tags-picker', + params: { + field: () => + import('./fields/EntityTagsPicker').then(m => m.EntityTagsPicker), + }, +}); + +export const multiEntityPickerFormField = FormFieldBlueprint.make({ + name: 'multi-entity-picker', + params: { + field: () => + import('./fields/MultiEntityPicker').then(m => m.MultiEntityPicker), + }, +}); + +export const myGroupsPickerFormField = FormFieldBlueprint.make({ + name: 'my-groups-picker', + params: { + field: () => import('./fields/MyGroupsPicker').then(m => m.MyGroupsPicker), + }, +}); + +export const ownedEntityPickerFormField = FormFieldBlueprint.make({ + name: 'owned-entity-picker', + params: { + field: () => + import('./fields/OwnedEntityPicker').then(m => m.OwnedEntityPicker), + }, +}); + +export const repoBranchPickerFormField = FormFieldBlueprint.make({ + name: 'repo-branch-picker', + params: { + field: () => + import('./fields/RepoBranchPicker').then(m => m.RepoBranchPicker), + }, +}); + export const scaffolderApi = ApiBlueprint.make({ params: defineParams => defineParams({ diff --git a/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts b/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts new file mode 100644 index 0000000000..715cb068a0 --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts @@ -0,0 +1,28 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { EntityNamePicker as Component } from '../../components/fields/EntityNamePicker/EntityNamePicker'; +import { + EntityNamePickerFieldSchema, + entityNamePickerValidation, +} from '../../components'; + +export const EntityNamePicker = createFormField({ + component: Component, + name: 'EntityNamePicker', + validation: entityNamePickerValidation, + schema: EntityNamePickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/fields/EntityPicker.ts b/plugins/scaffolder/src/alpha/fields/EntityPicker.ts new file mode 100644 index 0000000000..059e87fc19 --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/EntityPicker.ts @@ -0,0 +1,24 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { EntityPicker as Component } from '../../components/fields/EntityPicker/EntityPicker'; +import { EntityPickerFieldSchema } from '../../components'; + +export const EntityPicker = createFormField({ + component: Component, + name: 'EntityPicker', + schema: EntityPickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts b/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts new file mode 100644 index 0000000000..c4d5f88abb --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts @@ -0,0 +1,24 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { EntityTagsPicker as Component } from '../../components/fields/EntityTagsPicker/EntityTagsPicker'; +import { EntityTagsPickerFieldSchema } from '../../components'; + +export const EntityTagsPicker = createFormField({ + component: Component, + name: 'EntityTagsPicker', + schema: EntityTagsPickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts b/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts new file mode 100644 index 0000000000..32bc54f86e --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts @@ -0,0 +1,24 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { MultiEntityPicker as Component } from '../../components/fields/MultiEntityPicker/MultiEntityPicker'; +import { MultiEntityPickerFieldSchema } from '../../components'; + +export const MultiEntityPicker = createFormField({ + component: Component, + name: 'MultiEntityPicker', + schema: MultiEntityPickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts b/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts new file mode 100644 index 0000000000..cc5f717c28 --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts @@ -0,0 +1,24 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { MyGroupsPicker as Component } from '../../components/fields/MyGroupsPicker/MyGroupsPicker'; +import { MyGroupsPickerFieldSchema } from '../../components'; + +export const MyGroupsPicker = createFormField({ + component: Component, + name: 'MyGroupsPicker', + schema: MyGroupsPickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts b/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts new file mode 100644 index 0000000000..6f40b23803 --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts @@ -0,0 +1,24 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { OwnedEntityPicker as Component } from '../../components/fields/OwnedEntityPicker/OwnedEntityPicker'; +import { OwnedEntityPickerFieldSchema } from '../../components'; + +export const OwnedEntityPicker = createFormField({ + component: Component, + name: 'OwnedEntityPicker', + schema: OwnedEntityPickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts b/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts new file mode 100644 index 0000000000..ea58f1f00f --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts @@ -0,0 +1,24 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { OwnerPicker as Component } from '../../components/fields/OwnerPicker/OwnerPicker'; +import { OwnerPickerFieldSchema } from '../../components'; + +export const OwnerPicker = createFormField({ + component: Component, + name: 'OwnerPicker', + schema: OwnerPickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts b/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts new file mode 100644 index 0000000000..9694b6a8c4 --- /dev/null +++ b/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts @@ -0,0 +1,24 @@ +/* + * 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; +import { RepoBranchPicker as Component } from '../../components/fields/RepoBranchPicker/RepoBranchPicker'; +import { RepoBranchPickerFieldSchema } from '../../components'; + +export const RepoBranchPicker = createFormField({ + component: Component, + name: 'RepoBranchPicker', + schema: RepoBranchPickerFieldSchema, +}); diff --git a/plugins/scaffolder/src/alpha/plugin.tsx b/plugins/scaffolder/src/alpha/plugin.tsx index a414c61da5..3b41aa3893 100644 --- a/plugins/scaffolder/src/alpha/plugin.tsx +++ b/plugins/scaffolder/src/alpha/plugin.tsx @@ -17,10 +17,10 @@ import { convertLegacyRouteRefs } from '@backstage/core-compat-api'; import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; import { - rootRouteRef, actionsRouteRef, editRouteRef, registerComponentRouteRef, + rootRouteRef, scaffolderListTaskRouteRef, scaffolderTaskRouteRef, selectedTemplateRouteRef, @@ -28,10 +28,18 @@ import { viewTechDocRouteRef, } from '../routes'; import { + entityNamePickerFormField, + entityPickerFormField, + entityTagsPickerFormField, + multiEntityPickerFormField, + myGroupsPickerFormField, + ownedEntityPickerFormField, + ownerPickerFormField, + repoBranchPickerFormField, repoUrlPickerFormField, + scaffolderApi, scaffolderNavItem, scaffolderPage, - scaffolderApi, } from './extensions'; import { isTemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { formFieldsApi } from '@backstage/plugin-scaffolder-react/alpha'; @@ -73,5 +81,13 @@ export default createFrontendPlugin({ formDecoratorsApi, formFieldsApi, repoUrlPickerFormField, + entityNamePickerFormField, + entityPickerFormField, + ownerPickerFormField, + entityTagsPickerFormField, + multiEntityPickerFormField, + myGroupsPickerFormField, + ownedEntityPickerFormField, + repoBranchPickerFormField, ], }); diff --git a/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts b/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts index 7076221480..82d9cb0b17 100644 --- a/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts +++ b/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export { entityNamePickerValidation } from './validation'; +export { EntityNamePickerFieldSchema } from './schema'; diff --git a/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts index fe4765b8b8..0230f26879 100644 --- a/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts @@ -15,7 +15,10 @@ */ import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; -const EntityNamePickerFieldSchema = makeFieldSchema({ +/** + * @public + */ +export const EntityNamePickerFieldSchema = makeFieldSchema({ output: z => z.string(), }); diff --git a/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts b/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts index 9302e9abc1..e475606c11 100644 --- a/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts +++ b/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts @@ -17,6 +17,9 @@ import { FieldValidation } from '@rjsf/utils'; import { KubernetesValidatorFunctions } from '@backstage/catalog-model'; +/** + * @public + */ export const entityNamePickerValidation = ( value: string, validation: FieldValidation, diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts index 667b4638b8..aa396229ee 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts @@ -16,6 +16,9 @@ import { z as zod } from 'zod'; import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; +/** + * @public + */ export const entityQueryFilterExpressionSchema = zod.record( zod .string() @@ -23,6 +26,9 @@ export const entityQueryFilterExpressionSchema = zod.record( .or(zod.array(zod.string())), ); +/** + * @public + */ export const MultiEntityPickerFieldSchema = makeFieldSchema({ output: z => z.array(z.string()), uiOptions: z => @@ -54,6 +60,7 @@ export const MultiEntityPickerFieldSchema = makeFieldSchema({ /** * The input props that can be specified under `ui:options` for the * `EntityPicker` field extension. + * @public */ export type MultiEntityPickerUiOptions = NonNullable< (typeof MultiEntityPickerFieldSchema.TProps.uiSchema)['ui:options'] diff --git a/plugins/scaffolder/src/components/fields/index.ts b/plugins/scaffolder/src/components/fields/index.ts index 7f119f27be..dc1de86adb 100644 --- a/plugins/scaffolder/src/components/fields/index.ts +++ b/plugins/scaffolder/src/components/fields/index.ts @@ -14,10 +14,13 @@ * limitations under the License. */ export * from './EntityPicker'; +export * from './EntityNamePicker'; export * from './OwnerPicker'; export * from './RepoUrlPicker'; export * from './OwnedEntityPicker'; export * from './EntityTagsPicker'; +export * from './RepoBranchPicker'; +export * from './MultiEntityPicker'; export * from './MyGroupsPicker'; export { type FieldSchema, makeFieldSchemaFromZod } from './utils'; From 48d409f4abe5df6d08f1731b8dc1366cbd424580 Mon Sep 17 00:00:00 2001 From: Hellgren Heikki Date: Wed, 24 Sep 2025 10:25:14 +0300 Subject: [PATCH 097/211] fix: correct year for copyright headers Signed-off-by: Hellgren Heikki --- plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/EntityPicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/OwnerPicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts | 2 +- plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts b/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts index 715cb068a0..770e055948 100644 --- a/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts +++ b/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/EntityPicker.ts b/plugins/scaffolder/src/alpha/fields/EntityPicker.ts index 059e87fc19..d4ae6a5ecb 100644 --- a/plugins/scaffolder/src/alpha/fields/EntityPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/EntityPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts b/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts index c4d5f88abb..35b45a91a6 100644 --- a/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts b/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts index 32bc54f86e..0b89e7351b 100644 --- a/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts b/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts index cc5f717c28..a8f0e74b8c 100644 --- a/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts b/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts index 6f40b23803..becd48b5ce 100644 --- a/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts b/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts index ea58f1f00f..53ec16eb8b 100644 --- a/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts b/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts index 9694b6a8c4..176b4fdaa6 100644 --- a/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts b/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts index add8408b2d..829c20d7c0 100644 --- a/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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. @@ -16,8 +16,8 @@ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; import { RepoUrlPicker as Component } from '../../components/fields/RepoUrlPicker/RepoUrlPicker'; import { - RepoUrlPickerFieldSchema, repoPickerValidation, + RepoUrlPickerFieldSchema, } from '../../components'; export const RepoUrlPicker = createFormField({ From c8cad6ac273cd1ea416330cfc137a2c2c87d7f8f Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Wed, 24 Sep 2025 09:46:02 +0200 Subject: [PATCH 098/211] prevent handlers overrides Signed-off-by: Juan Pablo Garcia Ripa --- .../external/ExternalAuthTokenHandler.test.ts | 70 +++++++++++++++++++ .../auth/external/ExternalAuthTokenHandler.ts | 33 ++++++--- .../src/entrypoints/auth/external/jwks.ts | 1 - 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts index f315b43314..e3f1acf270 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts @@ -354,6 +354,76 @@ describe('ExternalTokenHandler', () => { expect(verifyMock).toHaveBeenCalledWith(customToken, { context: 'a' }); }); + it('should fail if custom handler has same type as builtin handlers', async () => { + const logger = mockServices.logger.mock(); + const customStaticHandler: ExternalTokenHandler = + createExternalTokenHandler({ + type: 'static', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'custom-static-subject', + }), + }); + + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger, + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'static', + options: { + token: 'mytoken', + subject: 'static-subject', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [customStaticHandler], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Duplicate external token handler type 'static', each handler must have a unique type"`, + ); + }); + it('should fail if 2 custom handlers have the same type', async () => { + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig(), + externalTokenHandlers: [ + createExternalTokenHandler({ + type: 'internal-custom', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }), + createExternalTokenHandler({ + type: 'internal-custom', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }), + ], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Duplicate external token handler type 'internal-custom', each handler must have a unique type"`, + ); + }); it('should fail if config contains types not declared', async () => { const createHandler = () => ExternalAuthTokenHandler.create({ diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts index 4b2cbb3e5a..21b3aed99d 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts @@ -42,11 +42,11 @@ export const externalTokenHandlersServiceRef = createServiceRef< multiton: true, }); -const defaultHandlers: ExternalTokenHandler[] = [ - staticTokenHandler, - legacyTokenHandler, - jwksTokenHandler, -]; +const defaultHandlers: Record> = { + static: staticTokenHandler, + legacy: legacyTokenHandler, + jwks: jwksTokenHandler, +}; type ContextMapEntry = { context: T; @@ -70,20 +70,35 @@ export class ExternalAuthTokenHandler { const { ownPluginId, config, - externalTokenHandlers: customHandlers, + externalTokenHandlers: customHandlers = [], logger, } = options; - const handlersTypes = [...defaultHandlers, ...(customHandlers ?? [])]; + const handlersTypes = customHandlers.reduce< + Record> + >( + (acc, handler) => { + if (acc[handler.type]) { + throw new Error( + `Duplicate external token handler type '${handler.type}', each handler must have a unique type`, + ); + } + acc[handler.type] = handler; + return acc; + }, + { ...defaultHandlers }, + ); const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? []; const contexts: ContextMapEntry[] = handlerConfigs.map( handlerConfig => { const type = handlerConfig.getString('type'); - const handler = handlersTypes.find(h => h.type === type); + const handler = handlersTypes[type]; if (!handler) { - const valid = handlersTypes.map(h => `'${h.type}'`).join(', '); + const valid = Object.keys(handlersTypes) + .map(h => `'${h}'`) + .join(', '); throw new Error( `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, ); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts index 892fcf2181..33b55df775 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts @@ -75,7 +75,6 @@ export const jwksTokenHandler = createExternalTokenHandler({ : 'external:'; return { subject: `${prefix}${sub}`, - allAccessRestrictions: context.allAccessRestrictions, }; } } catch { From 49be37008ce3b553d5b96f72fdc71b22e664f464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Edeg=C3=A5rd?= Date: Wed, 24 Sep 2025 07:55:22 +0000 Subject: [PATCH 099/211] Simplified more. The Slack library will remove undefined values when sending the actual request. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Henrik Edegård --- .../src/lib/SlackNotificationProcessor.test.ts | 2 +- plugins/notifications-backend-module-slack/src/lib/util.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts index c970aec7ba..ad4bf01e9f 100644 --- a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts +++ b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts @@ -829,7 +829,7 @@ describe('SlackNotificationProcessor', () => { const calls = (slack.chat.postMessage as jest.Mock).mock.calls; expect(calls).toHaveLength(1); - expect(calls[0][0]).not.toHaveProperty('username'); + expect(calls[0][0].username).toBeUndefined(); }); }); diff --git a/plugins/notifications-backend-module-slack/src/lib/util.ts b/plugins/notifications-backend-module-slack/src/lib/util.ts index 1116218b09..33293ea24f 100644 --- a/plugins/notifications-backend-module-slack/src/lib/util.ts +++ b/plugins/notifications-backend-module-slack/src/lib/util.ts @@ -30,7 +30,7 @@ export function toChatPostMessageArgs(options: { const args: ChatPostMessageArguments = { channel, text: payload.title, - ...(username && { username }), + username, attachments: [ { color: getColor(payload.severity), From 4ee97cf3ab270d583b0f8bf2f7aeb8b75e50dbe9 Mon Sep 17 00:00:00 2001 From: Hellgren Heikki Date: Wed, 24 Sep 2025 10:43:15 +0300 Subject: [PATCH 100/211] chore(scaffolder): clean up api report from schemas and validators Signed-off-by: Hellgren Heikki --- plugins/scaffolder/report-alpha.api.md | 6 +- plugins/scaffolder/report.api.md | 79 ++----------------- .../src/alpha/fields/EntityNamePicker.ts | 4 +- .../src/alpha/fields/EntityPicker.ts | 6 +- .../src/alpha/fields/EntityTagsPicker.ts | 6 +- .../src/alpha/fields/MultiEntityPicker.ts | 6 +- .../src/alpha/fields/MyGroupsPicker.ts | 6 +- .../src/alpha/fields/OwnedEntityPicker.ts | 6 +- .../src/alpha/fields/OwnerPicker.ts | 6 +- .../src/alpha/fields/RepoBranchPicker.ts | 6 +- .../src/alpha/fields/RepoUrlPicker.ts | 4 +- .../fields/EntityNamePicker/index.ts | 1 + .../fields/EntityNamePicker/schema.ts | 3 - .../fields/EntityNamePicker/validation.ts | 3 - .../components/fields/EntityPicker/index.ts | 1 + .../fields/EntityTagsPicker/index.ts | 1 + .../fields/EntityTagsPicker/schema.ts | 1 - .../fields/MultiEntityPicker/index.ts | 1 + .../fields/MultiEntityPicker/schema.ts | 7 -- .../components/fields/MyGroupsPicker/index.ts | 1 + .../fields/MyGroupsPicker/schema.ts | 1 - .../fields/OwnedEntityPicker/index.ts | 1 + .../fields/OwnedEntityPicker/schema.ts | 3 +- .../components/fields/OwnerPicker/index.ts | 1 + .../components/fields/OwnerPicker/schema.ts | 1 - .../fields/RepoBranchPicker/index.ts | 2 +- .../fields/RepoBranchPicker/schema.ts | 5 -- .../components/fields/RepoUrlPicker/index.ts | 1 + .../fields/RepoUrlPicker/validation.ts | 1 - .../scaffolder/src/components/fields/index.ts | 10 --- plugins/scaffolder/src/components/index.ts | 28 ++++++- 31 files changed, 77 insertions(+), 131 deletions(-) diff --git a/plugins/scaffolder/report-alpha.api.md b/plugins/scaffolder/report-alpha.api.md index 505688247f..a5dd2a22d1 100644 --- a/plugins/scaffolder/report-alpha.api.md +++ b/plugins/scaffolder/report-alpha.api.md @@ -522,10 +522,10 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'ongoingTask.title': 'Run of'; readonly 'ongoingTask.contextMenu.cancel': 'Cancel'; readonly 'ongoingTask.contextMenu.startOver': 'Start Over'; + readonly 'ongoingTask.contextMenu.retry': 'Retry'; readonly 'ongoingTask.contextMenu.hideLogs': 'Hide Logs'; readonly 'ongoingTask.contextMenu.showLogs': 'Show Logs'; readonly 'ongoingTask.contextMenu.hideButtonBar': 'Hide Button Bar'; - readonly 'ongoingTask.contextMenu.retry': 'Retry'; readonly 'ongoingTask.contextMenu.showButtonBar': 'Show Button Bar'; readonly 'ongoingTask.subtitle': 'Task {{taskId}}'; readonly 'ongoingTask.pageTitle.hasTemplateName': 'Run of {{templateName}}'; @@ -608,8 +608,8 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'templateListPage.pageTitle': 'Create a new component'; readonly 'templateListPage.templateGroups.defaultTitle': 'Templates'; readonly 'templateListPage.templateGroups.otherTitle': 'Other Templates'; - readonly 'templateListPage.contentHeader.registerExistingButtonTitle': 'Register Existing Component'; readonly 'templateListPage.contentHeader.supportButtonTitle': 'Create new software components using standard templates. Different templates create different kinds of components (services, websites, documentation, ...).'; + readonly 'templateListPage.contentHeader.registerExistingButtonTitle': 'Register Existing Component'; readonly 'templateListPage.additionalLinksForEntity.viewTechDocsTitle': 'View TechDocs'; readonly 'templateWizardPage.title': 'Create a new component'; readonly 'templateWizardPage.subtitle': 'Create new software components using standard templates in your organization'; @@ -622,8 +622,8 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'templateEditorToolbar.addToCatalogDialogTitle': 'Publish changes'; readonly 'templateEditorToolbar.addToCatalogDialogContent.stepsIntroduction': 'Follow the instructions below to create or update a template:'; readonly 'templateEditorToolbar.addToCatalogDialogContent.stepsListItems': 'Save the template files in a local directory\nCreate a pull request to a new or existing git repository\nIf the template already exists, the changes will be reflected in the software catalog once the pull request gets merged\nBut if you are creating a new template, follow the documentation linked below to register the new template repository in software catalog'; - readonly 'templateEditorToolbar.addToCatalogDialogActions.documentationButton': 'Go to the documentation'; readonly 'templateEditorToolbar.addToCatalogDialogActions.documentationUrl': 'https://backstage.io/docs/features/software-templates/adding-templates/'; + readonly 'templateEditorToolbar.addToCatalogDialogActions.documentationButton': 'Go to the documentation'; readonly 'templateEditorToolbarFileMenu.button': 'File'; readonly 'templateEditorToolbarFileMenu.options.openDirectory': 'Open template directory'; readonly 'templateEditorToolbarFileMenu.options.createDirectory': 'Create template directory'; diff --git a/plugins/scaffolder/report.api.md b/plugins/scaffolder/report.api.md index b1a6b62e9d..27fa47a184 100644 --- a/plugins/scaffolder/report.api.md +++ b/plugins/scaffolder/report.api.md @@ -70,15 +70,6 @@ export const EntityNamePickerFieldExtension: FieldExtensionComponent_2< any >; -// @public (undocumented) -export const EntityNamePickerFieldSchema: FieldSchema_2; - -// @public (undocumented) -export const entityNamePickerValidation: ( - value: string, - validation: FieldValidation, -) => void; - // @public export const EntityPickerFieldExtension: FieldExtensionComponent_2< string, @@ -237,39 +228,6 @@ export const MultiEntityPickerFieldExtension: FieldExtensionComponent_2< } >; -// @public (undocumented) -export const MultiEntityPickerFieldSchema: FieldSchema_2< - string[], - { - defaultKind?: string | undefined; - defaultNamespace?: string | false | undefined; - catalogFilter?: - | Record< - string, - | string - | string[] - | { - exists?: boolean | undefined; - } - > - | Record< - string, - | string - | string[] - | { - exists?: boolean | undefined; - } - >[] - | undefined; - allowArbitraryValues?: boolean | undefined; - } ->; - -// @public -export type MultiEntityPickerUiOptions = NonNullable< - (typeof MultiEntityPickerFieldSchema.TProps.uiSchema)['ui:options'] ->; - // @public export const MyGroupsPickerFieldExtension: FieldExtensionComponent_2< string, @@ -422,9 +380,9 @@ export const RepoBranchPickerFieldExtension: FieldExtensionComponent_2< | { azure?: string[] | undefined; github?: string[] | undefined; + gitlab?: string[] | undefined; bitbucket?: string[] | undefined; gerrit?: string[] | undefined; - gitlab?: string[] | undefined; gitea?: string[] | undefined; } | undefined; @@ -433,33 +391,6 @@ export const RepoBranchPickerFieldExtension: FieldExtensionComponent_2< } >; -// @public (undocumented) -export const RepoBranchPickerFieldSchema: FieldSchema_2< - string, - { - requestUserCredentials?: - | { - secretsKey: string; - additionalScopes?: - | { - azure?: string[] | undefined; - github?: string[] | undefined; - bitbucket?: string[] | undefined; - gerrit?: string[] | undefined; - gitlab?: string[] | undefined; - gitea?: string[] | undefined; - } - | undefined; - } - | undefined; - } ->; - -// @public -export type RepoBranchPickerUiOptions = NonNullable< - (typeof RepoBranchPickerFieldSchema.TProps.uiSchema)['ui:options'] ->; - // @public export const repoPickerValidation: ( value: string, @@ -473,11 +404,11 @@ export const repoPickerValidation: ( export const RepoUrlPickerFieldExtension: FieldExtensionComponent_2< string, { - allowedHosts?: string[] | undefined; allowedOrganizations?: string[] | undefined; allowedOwners?: string[] | undefined; allowedProjects?: string[] | undefined; allowedRepos?: string[] | undefined; + allowedHosts?: string[] | undefined; requestUserCredentials?: | { secretsKey: string; @@ -485,9 +416,9 @@ export const RepoUrlPickerFieldExtension: FieldExtensionComponent_2< | { azure?: string[] | undefined; github?: string[] | undefined; + gitlab?: string[] | undefined; bitbucket?: string[] | undefined; gerrit?: string[] | undefined; - gitlab?: string[] | undefined; gitea?: string[] | undefined; } | undefined; @@ -500,11 +431,11 @@ export const RepoUrlPickerFieldExtension: FieldExtensionComponent_2< export const RepoUrlPickerFieldSchema: FieldSchema_2< string, { - allowedHosts?: string[] | undefined; allowedOrganizations?: string[] | undefined; allowedOwners?: string[] | undefined; allowedProjects?: string[] | undefined; allowedRepos?: string[] | undefined; + allowedHosts?: string[] | undefined; requestUserCredentials?: | { secretsKey: string; @@ -512,9 +443,9 @@ export const RepoUrlPickerFieldSchema: FieldSchema_2< | { azure?: string[] | undefined; github?: string[] | undefined; + gitlab?: string[] | undefined; bitbucket?: string[] | undefined; gerrit?: string[] | undefined; - gitlab?: string[] | undefined; gitea?: string[] | undefined; } | undefined; diff --git a/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts b/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts index 770e055948..dcb68edd15 100644 --- a/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts +++ b/plugins/scaffolder/src/alpha/fields/EntityNamePicker.ts @@ -14,11 +14,11 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { EntityNamePicker as Component } from '../../components/fields/EntityNamePicker/EntityNamePicker'; import { + EntityNamePicker as Component, EntityNamePickerFieldSchema, entityNamePickerValidation, -} from '../../components'; +} from '../../components/fields/EntityNamePicker'; export const EntityNamePicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/EntityPicker.ts b/plugins/scaffolder/src/alpha/fields/EntityPicker.ts index d4ae6a5ecb..c80b38fe43 100644 --- a/plugins/scaffolder/src/alpha/fields/EntityPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/EntityPicker.ts @@ -14,8 +14,10 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { EntityPicker as Component } from '../../components/fields/EntityPicker/EntityPicker'; -import { EntityPickerFieldSchema } from '../../components'; +import { + EntityPicker as Component, + EntityPickerFieldSchema, +} from '../../components/fields/EntityPicker'; export const EntityPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts b/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts index 35b45a91a6..9a8e86730e 100644 --- a/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/EntityTagsPicker.ts @@ -14,8 +14,10 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { EntityTagsPicker as Component } from '../../components/fields/EntityTagsPicker/EntityTagsPicker'; -import { EntityTagsPickerFieldSchema } from '../../components'; +import { + EntityTagsPicker as Component, + EntityTagsPickerFieldSchema, +} from '../../components/fields/EntityTagsPicker'; export const EntityTagsPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts b/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts index 0b89e7351b..8e8c65d67c 100644 --- a/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/MultiEntityPicker.ts @@ -14,8 +14,10 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { MultiEntityPicker as Component } from '../../components/fields/MultiEntityPicker/MultiEntityPicker'; -import { MultiEntityPickerFieldSchema } from '../../components'; +import { + MultiEntityPicker as Component, + MultiEntityPickerFieldSchema, +} from '../../components/fields/MultiEntityPicker'; export const MultiEntityPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts b/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts index a8f0e74b8c..a2cd5823b4 100644 --- a/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/MyGroupsPicker.ts @@ -14,8 +14,10 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { MyGroupsPicker as Component } from '../../components/fields/MyGroupsPicker/MyGroupsPicker'; -import { MyGroupsPickerFieldSchema } from '../../components'; +import { + MyGroupsPicker as Component, + MyGroupsPickerFieldSchema, +} from '../../components/fields/MyGroupsPicker'; export const MyGroupsPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts b/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts index becd48b5ce..7b13797ce1 100644 --- a/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/OwnedEntityPicker.ts @@ -14,8 +14,10 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { OwnedEntityPicker as Component } from '../../components/fields/OwnedEntityPicker/OwnedEntityPicker'; -import { OwnedEntityPickerFieldSchema } from '../../components'; +import { + OwnedEntityPicker as Component, + OwnedEntityPickerFieldSchema, +} from '../../components/fields/OwnedEntityPicker'; export const OwnedEntityPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts b/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts index 53ec16eb8b..00761207a0 100644 --- a/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/OwnerPicker.ts @@ -14,8 +14,10 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { OwnerPicker as Component } from '../../components/fields/OwnerPicker/OwnerPicker'; -import { OwnerPickerFieldSchema } from '../../components'; +import { + OwnerPicker as Component, + OwnerPickerFieldSchema, +} from '../../components/fields/OwnerPicker'; export const OwnerPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts b/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts index 176b4fdaa6..b1cf2d8802 100644 --- a/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/RepoBranchPicker.ts @@ -14,8 +14,10 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { RepoBranchPicker as Component } from '../../components/fields/RepoBranchPicker/RepoBranchPicker'; -import { RepoBranchPickerFieldSchema } from '../../components'; +import { + RepoBranchPicker as Component, + RepoBranchPickerFieldSchema, +} from '../../components/fields/RepoBranchPicker'; export const RepoBranchPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts b/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts index 829c20d7c0..10394f7df0 100644 --- a/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts +++ b/plugins/scaffolder/src/alpha/fields/RepoUrlPicker.ts @@ -14,11 +14,11 @@ * limitations under the License. */ import { createFormField } from '@backstage/plugin-scaffolder-react/alpha'; -import { RepoUrlPicker as Component } from '../../components/fields/RepoUrlPicker/RepoUrlPicker'; import { repoPickerValidation, + RepoUrlPicker as Component, RepoUrlPickerFieldSchema, -} from '../../components'; +} from '../../components/fields/RepoUrlPicker'; export const RepoUrlPicker = createFormField({ component: Component, diff --git a/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts b/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts index 82d9cb0b17..fc295d66c5 100644 --- a/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts +++ b/plugins/scaffolder/src/components/fields/EntityNamePicker/index.ts @@ -13,5 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export { EntityNamePicker } from './EntityNamePicker'; export { entityNamePickerValidation } from './validation'; export { EntityNamePickerFieldSchema } from './schema'; diff --git a/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts index 0230f26879..68c8d5157e 100644 --- a/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts @@ -15,9 +15,6 @@ */ import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; -/** - * @public - */ export const EntityNamePickerFieldSchema = makeFieldSchema({ output: z => z.string(), }); diff --git a/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts b/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts index e475606c11..9302e9abc1 100644 --- a/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts +++ b/plugins/scaffolder/src/components/fields/EntityNamePicker/validation.ts @@ -17,9 +17,6 @@ import { FieldValidation } from '@rjsf/utils'; import { KubernetesValidatorFunctions } from '@backstage/catalog-model'; -/** - * @public - */ export const entityNamePickerValidation = ( value: string, validation: FieldValidation, diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/index.ts b/plugins/scaffolder/src/components/fields/EntityPicker/index.ts index b8df596c98..08583dca30 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/EntityPicker/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export { EntityPicker } from './EntityPicker'; export { EntityPickerFieldSchema, type EntityPickerUiOptions } from './schema'; diff --git a/plugins/scaffolder/src/components/fields/EntityTagsPicker/index.ts b/plugins/scaffolder/src/components/fields/EntityTagsPicker/index.ts index 52bad9a80d..77786f0e1a 100644 --- a/plugins/scaffolder/src/components/fields/EntityTagsPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/EntityTagsPicker/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export { EntityTagsPicker } from './EntityTagsPicker'; export { EntityTagsPickerFieldSchema, type EntityTagsPickerUiOptions, diff --git a/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts index d1a300f5c8..0162804ce1 100644 --- a/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts @@ -46,7 +46,6 @@ export type EntityTagsPickerProps = typeof EntityTagsPickerFieldSchema.TProps; /** * The input props that can be specified under `ui:options` for the * `EntityTagsPicker` field extension. - * * @public */ export type EntityTagsPickerUiOptions = NonNullable< diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/index.ts b/plugins/scaffolder/src/components/fields/MultiEntityPicker/index.ts index 9a597b9b97..add40e4a81 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export { MultiEntityPicker } from './MultiEntityPicker'; export { MultiEntityPickerFieldSchema, type MultiEntityPickerUiOptions, diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts index aa396229ee..667b4638b8 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts @@ -16,9 +16,6 @@ import { z as zod } from 'zod'; import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; -/** - * @public - */ export const entityQueryFilterExpressionSchema = zod.record( zod .string() @@ -26,9 +23,6 @@ export const entityQueryFilterExpressionSchema = zod.record( .or(zod.array(zod.string())), ); -/** - * @public - */ export const MultiEntityPickerFieldSchema = makeFieldSchema({ output: z => z.array(z.string()), uiOptions: z => @@ -60,7 +54,6 @@ export const MultiEntityPickerFieldSchema = makeFieldSchema({ /** * The input props that can be specified under `ui:options` for the * `EntityPicker` field extension. - * @public */ export type MultiEntityPickerUiOptions = NonNullable< (typeof MultiEntityPickerFieldSchema.TProps.uiSchema)['ui:options'] diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/index.ts b/plugins/scaffolder/src/components/fields/MyGroupsPicker/index.ts index 00be19cf96..42982dcb6f 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export { MyGroupsPicker } from './MyGroupsPicker'; export { MyGroupsPickerSchema, type MyGroupsPickerUiOptions, diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts b/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts index 6ccda91afb..41e8579328 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts @@ -36,7 +36,6 @@ export type MyGroupsPickerUiOptions = NonNullable< /** * Props for the MyGroupsPicker. - * @public */ export type MyGroupsPickerProps = typeof MyGroupsPickerFieldSchema.TProps; diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/index.ts b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/index.ts index 985dae1d3c..6738a8a057 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export { OwnedEntityPicker } from './OwnedEntityPicker'; export { OwnedEntityPickerFieldSchema, type OwnedEntityPickerUiOptions, diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts index 6dd00df49d..e5435c150f 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { EntityPickerFieldSchema } from '../EntityPicker/schema'; +import { EntityPickerFieldSchema } from '../EntityPicker'; /** * @public @@ -23,7 +23,6 @@ export const OwnedEntityPickerFieldSchema = EntityPickerFieldSchema; /** * The input props that can be specified under `ui:options` for the * `OwnedEntityPicker` field extension. - * * @public */ export type OwnedEntityPickerUiOptions = NonNullable< diff --git a/plugins/scaffolder/src/components/fields/OwnerPicker/index.ts b/plugins/scaffolder/src/components/fields/OwnerPicker/index.ts index 9d94650e04..5080ee1602 100644 --- a/plugins/scaffolder/src/components/fields/OwnerPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/OwnerPicker/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export { OwnerPicker } from './OwnerPicker'; export { OwnerPickerFieldSchema, type OwnerPickerUiOptions } from './schema'; diff --git a/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts b/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts index 76389216ba..5a5b2e9cf6 100644 --- a/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts @@ -54,7 +54,6 @@ export const OwnerPickerFieldSchema = makeFieldSchema({ /** * The input props that can be specified under `ui:options` for the * `OwnerPicker` field extension. - * * @public */ export type OwnerPickerUiOptions = NonNullable< diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/index.ts b/plugins/scaffolder/src/components/fields/RepoBranchPicker/index.ts index 2b87b62ebd..e41b723aa9 100644 --- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/index.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +export { RepoBranchPicker } from './RepoBranchPicker'; export { RepoBranchPickerFieldSchema, type RepoBranchPickerUiOptions, diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts b/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts index 62a3b66e14..4e76f4d988 100644 --- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts @@ -15,9 +15,6 @@ */ import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; -/** - * @public - */ export const RepoBranchPickerFieldSchema = makeFieldSchema({ output: z => z.string(), uiOptions: z => @@ -69,8 +66,6 @@ export const RepoBranchPickerFieldSchema = makeFieldSchema({ /** * The input props that can be specified under `ui:options` for the * `RepoBranchPicker` field extension. - * - * @public */ export type RepoBranchPickerUiOptions = NonNullable< (typeof RepoBranchPickerFieldSchema.TProps.uiSchema)['ui:options'] diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/index.ts b/plugins/scaffolder/src/components/fields/RepoUrlPicker/index.ts index 33e5ef1715..d4dc219b68 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/index.ts +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +export { RepoUrlPicker } from './RepoUrlPicker'; export { RepoUrlPickerFieldSchema, type RepoUrlPickerUiOptions, diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts b/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts index 06c2fd5684..682fc6bb2a 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/validation.ts @@ -22,7 +22,6 @@ import { scmIntegrationsApiRef } from '@backstage/integration-react'; * The validation function for the `repoUrl` that is returned from the * field extension. Ensures that you have all the required fields filled for * the different providers that exist. - * * @public */ export const repoPickerValidation = ( diff --git a/plugins/scaffolder/src/components/fields/index.ts b/plugins/scaffolder/src/components/fields/index.ts index dc1de86adb..287f62022c 100644 --- a/plugins/scaffolder/src/components/fields/index.ts +++ b/plugins/scaffolder/src/components/fields/index.ts @@ -13,14 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export * from './EntityPicker'; -export * from './EntityNamePicker'; -export * from './OwnerPicker'; -export * from './RepoUrlPicker'; -export * from './OwnedEntityPicker'; -export * from './EntityTagsPicker'; -export * from './RepoBranchPicker'; -export * from './MultiEntityPicker'; -export * from './MyGroupsPicker'; - export { type FieldSchema, makeFieldSchemaFromZod } from './utils'; diff --git a/plugins/scaffolder/src/components/index.ts b/plugins/scaffolder/src/components/index.ts index 552a75c99b..abafd42ed2 100644 --- a/plugins/scaffolder/src/components/index.ts +++ b/plugins/scaffolder/src/components/index.ts @@ -14,7 +14,33 @@ * limitations under the License. */ export * from './fields'; -export type { RepoUrlPickerUiOptions } from './fields'; + +export { + EntityPickerFieldSchema, + type EntityPickerUiOptions, +} from './fields/EntityPicker'; +export { + EntityTagsPickerFieldSchema, + type EntityTagsPickerUiOptions, +} from './fields/EntityTagsPicker'; +export { + MyGroupsPickerFieldSchema, + MyGroupsPickerSchema, + type MyGroupsPickerUiOptions, +} from './fields/MyGroupsPicker'; +export { + OwnedEntityPickerFieldSchema, + type OwnedEntityPickerUiOptions, +} from './fields/OwnedEntityPicker'; +export { + OwnerPickerFieldSchema, + type OwnerPickerUiOptions, +} from './fields/OwnerPicker'; +export { + repoPickerValidation, + RepoUrlPickerFieldSchema, + type RepoUrlPickerUiOptions, +} from './fields/RepoUrlPicker'; export { TemplateTypePicker } from './TemplateTypePicker'; From 5417077b085032d72799504e769c3db902730e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 24 Sep 2025 13:05:58 +0200 Subject: [PATCH 101/211] break out provider/processor handling into a separate file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .../src/service/CatalogBuilder.ts | 51 +---- .../catalog-backend/src/service/util.test.ts | 195 ++++++++++++++++++ plugins/catalog-backend/src/service/util.ts | 84 +++++++- 3 files changed, 282 insertions(+), 48 deletions(-) create mode 100644 plugins/catalog-backend/src/service/util.test.ts diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index 35997c03c5..23178238c7 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -111,6 +111,7 @@ import { catalogEntityPermissionResourceRef, CatalogPermissionRuleInput, } from '@backstage/plugin-catalog-node/alpha'; +import { filterAndSortProcessors, filterProviders } from './util'; export type CatalogEnvironment = { logger: LoggerService; @@ -525,11 +526,12 @@ export class CatalogBuilder { const locationStore = new DefaultLocationStore(dbClient); const configLocationProvider = new ConfigLocationEntityProvider(config); - const entityProviders = this.filterProviders( + const entityProviders = filterProviders( lodash.uniqBy( [...this.entityProviders, locationStore, configLocationProvider], provider => provider.getProviderName(), ), + config, ); const processingEngine = new DefaultCatalogProcessingEngine({ @@ -684,42 +686,7 @@ export class CatalogBuilder { this.checkMissingExternalProcessors(processors); - const filteredProcessors = this.filterProcessors(processors); - - // Lastly sort the processors by priority. Config can override the - // priority of a processor to allow control of 3rd party processors. - filteredProcessors.sort((a, b) => { - const getProcessorPriority = (processor: CatalogProcessor) => { - try { - return ( - config.getOptionalNumber( - `catalog.processorOptions.${processor.getProcessorName()}.priority`, - ) ?? - processor.getPriority?.() ?? - 20 - ); - } catch (_) { - // In case the processor config throws, just return default priority - return 20; - } - }; - - const aPriority = getProcessorPriority(a); - const bPriority = getProcessorPriority(b); - return aPriority - bPriority; - }); - - return filteredProcessors; - } - - private filterProcessors(processors: CatalogProcessor[]) { - const { config } = this.env; - return processors.filter( - p => - config.getOptionalBoolean( - `catalog.processorOptions.${p.getProcessorName()}.disabled`, - ) !== true, - ); + return filterAndSortProcessors(processors, config); } // TODO(Rugvip): These old processors are removed, for a while we'll be throwing @@ -832,16 +799,6 @@ export class CatalogBuilder { ); } - private filterProviders(providers: EntityProvider[]) { - const { config } = this.env; - return providers.filter( - p => - config.getOptionalBoolean( - `catalog.providerOptions.${p.getProviderName()}.disabled`, - ) !== true, - ); - } - private static getDefaultProcessingInterval( config: Config, ): ProcessingIntervalFunction { diff --git a/plugins/catalog-backend/src/service/util.test.ts b/plugins/catalog-backend/src/service/util.test.ts new file mode 100644 index 0000000000..9a0d2cc799 --- /dev/null +++ b/plugins/catalog-backend/src/service/util.test.ts @@ -0,0 +1,195 @@ +/* + * Copyright 2025 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 } from '@backstage/backend-test-utils'; +import { filterAndSortProcessors, filterProviders } from './util'; + +describe('filterAndSortProcessors', () => { + it('should filter processors', () => { + const p1 = { getProcessorName: () => 'processor1' }; + const p2 = { getProcessorName: () => 'processor2' }; + + expect( + filterAndSortProcessors([p1, p2], mockServices.rootConfig({ data: {} })), + ).toEqual([p1, p2]); + + expect( + filterAndSortProcessors( + [p1, p2], + mockServices.rootConfig({ + data: { + catalog: { + processorOptions: { + processor1: { disabled: true }, + }, + }, + }, + }), + ), + ).toEqual([p2]); + + expect( + filterAndSortProcessors( + [p1, p2], + mockServices.rootConfig({ + data: { + catalog: { + processorOptions: { + processor2: { disabled: true }, + }, + }, + }, + }), + ), + ).toEqual([p1]); + }); + + it('should sort processors', () => { + const p1 = { getProcessorName: () => 'processor1', getPriority: () => 10 }; + const p2 = { getProcessorName: () => 'processor2' }; + const p3 = { + getProcessorName: () => 'processor3', + getPriority: () => { + throw new Error('failed'); + }, + }; + + expect( + filterAndSortProcessors( + [p1, p2, p3], + mockServices.rootConfig({ data: {} }), + ), + ).toEqual([p1, p2, p3]); // p2 and p3 got the defaults + + expect( + filterAndSortProcessors( + [p1, p2, p3], + mockServices.rootConfig({ + data: { + catalog: { + processorOptions: { + processor2: { priority: 2 }, + processor3: { priority: 1 }, + }, + }, + }, + }), + ), + ).toEqual([p3, p2, p1]); + }); + + it('rejects invalid config', () => { + const p1 = { getProcessorName: () => 'processor1' }; + const p2 = { getProcessorName: () => 'processor1' }; + + expect(() => + filterAndSortProcessors( + [p1, p2], + mockServices.rootConfig({ + data: { + catalog: { + processorOptions: { + processor1: { disabled: 'i guess so, maybe' }, + }, + }, + }, + }), + ), + ).toThrowErrorMatchingInlineSnapshot( + `"Unable to convert config value for key 'catalog.processorOptions.processor1.disabled' in 'mock-config' to a boolean"`, + ); + + expect(() => + filterAndSortProcessors( + [p1, p2], + mockServices.rootConfig({ + data: { + catalog: { + processorOptions: { + processor1: { priority: 'somewhere in the middle, roughly' }, + }, + }, + }, + }), + ), + ).toThrowErrorMatchingInlineSnapshot( + `"Unable to convert config value for key 'catalog.processorOptions.processor1.priority' in 'mock-config' to a number"`, + ); + }); +}); + +describe('filterProviders', () => { + it('should filter providers', () => { + const p1 = { getProviderName: () => 'provider1', connect: jest.fn() }; + const p2 = { getProviderName: () => 'provider2', connect: jest.fn() }; + + expect( + filterProviders([p1, p2], mockServices.rootConfig({ data: {} })), + ).toEqual([p1, p2]); + + expect( + filterProviders( + [p1, p2], + mockServices.rootConfig({ + data: { + catalog: { + providerOptions: { + provider1: { disabled: true }, + }, + }, + }, + }), + ), + ).toEqual([p2]); + + expect( + filterProviders( + [p1, p2], + mockServices.rootConfig({ + data: { + catalog: { + providerOptions: { + provider2: { disabled: true }, + }, + }, + }, + }), + ), + ).toEqual([p1]); + }); + + it('rejects invalid config', () => { + const p1 = { getProviderName: () => 'provider1', connect: jest.fn() }; + const p2 = { getProviderName: () => 'provider2', connect: jest.fn() }; + + expect(() => + filterProviders( + [p1, p2], + mockServices.rootConfig({ + data: { + catalog: { + providerOptions: { + provider1: { disabled: 'i guess so, maybe' }, + }, + }, + }, + }), + ), + ).toThrowErrorMatchingInlineSnapshot( + `"Unable to convert config value for key 'catalog.providerOptions.provider1.disabled' in 'mock-config' to a boolean"`, + ); + }); +}); diff --git a/plugins/catalog-backend/src/service/util.ts b/plugins/catalog-backend/src/service/util.ts index 9847cb7225..54f6a6afae 100644 --- a/plugins/catalog-backend/src/service/util.ts +++ b/plugins/catalog-backend/src/service/util.ts @@ -24,12 +24,17 @@ import { QueryEntitiesInitialRequest, QueryEntitiesRequest, } from '../catalog/types'; -import { EntityFilter } from '@backstage/plugin-catalog-node'; +import { + CatalogProcessor, + EntityFilter, + EntityProvider, +} from '@backstage/plugin-catalog-node'; import { Entity, parseEntityRef, stringifyEntityRef, } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; export async function requireRequestBody(req: Request): Promise { const contentType = req.header('content-type'); @@ -166,3 +171,80 @@ export function expandLegacyCompoundRelationsInEntity(entity: Entity): Entity { } return entity; } + +/** + * Given a list of catalog processors, filter out the ones that are disabled + * through the `catalog.processorOptions` config and sort them by priority. + */ +export function filterAndSortProcessors( + processors: CatalogProcessor[], + config: Config, +): CatalogProcessor[] { + function getProcessorOptions( + processor: CatalogProcessor, + ): Config | undefined { + const root = config.getOptionalConfig('catalog.processorOptions'); + try { + return root?.getOptionalConfig(processor.getProcessorName()); + } catch { + // We silence errors specifically here, to cover for cases where the + // processor name contains special characters which makes the config + // reader throw an error. + return undefined; + } + } + + function getProcessorDisabled(processor: CatalogProcessor): boolean { + return ( + getProcessorOptions(processor)?.getOptionalBoolean('disabled') === true + ); + } + + function getProcessorPriority(processor: CatalogProcessor): number { + let priority = + getProcessorOptions(processor)?.getOptionalNumber('priority'); + + if (priority === undefined) { + try { + priority = processor.getPriority?.(); + } catch { + // In case the processor method throws, just return default priority + } + } + + return priority ?? 20; + } + + return processors + .filter(p => !getProcessorDisabled(p)) + .sort((a, b) => getProcessorPriority(a) - getProcessorPriority(b)); +} + +/** + * Given a list of entity providers, filter out the ones that are disabled + * through the `catalog.providerOptions` config. + */ +export function filterProviders( + providers: EntityProvider[], + config: Config, +): EntityProvider[] { + function getProviderOptions(provider: EntityProvider): Config | undefined { + const root = config.getOptionalConfig('catalog.providerOptions'); + try { + return root?.getOptionalConfig(provider.getProviderName()); + } catch { + // We silence errors specifically here, to cover for cases where the + // provider name contains special characters which makes the config + // reader throw an error. + return undefined; + } + } + + function getProviderDisabled(provider: EntityProvider): boolean { + return ( + getProviderOptions(provider)?.getOptionalBoolean('disabled') === true + ); + } + + return providers.filter(p => !getProviderDisabled(p)); +} From e037246a3a79199acf37bf9fe0207f0d3da86972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 24 Sep 2025 13:47:11 +0200 Subject: [PATCH 102/211] do is instead of get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/catalog-backend/src/service/util.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend/src/service/util.ts b/plugins/catalog-backend/src/service/util.ts index 54f6a6afae..2d3d4879cb 100644 --- a/plugins/catalog-backend/src/service/util.ts +++ b/plugins/catalog-backend/src/service/util.ts @@ -194,7 +194,7 @@ export function filterAndSortProcessors( } } - function getProcessorDisabled(processor: CatalogProcessor): boolean { + function isProcessorDisabled(processor: CatalogProcessor): boolean { return ( getProcessorOptions(processor)?.getOptionalBoolean('disabled') === true ); @@ -216,7 +216,7 @@ export function filterAndSortProcessors( } return processors - .filter(p => !getProcessorDisabled(p)) + .filter(p => !isProcessorDisabled(p)) .sort((a, b) => getProcessorPriority(a) - getProcessorPriority(b)); } @@ -240,11 +240,11 @@ export function filterProviders( } } - function getProviderDisabled(provider: EntityProvider): boolean { + function isProviderDisabled(provider: EntityProvider): boolean { return ( getProviderOptions(provider)?.getOptionalBoolean('disabled') === true ); } - return providers.filter(p => !getProviderDisabled(p)); + return providers.filter(p => !isProviderDisabled(p)); } From faf0f07e02e4cae7017b77f4b9c831c9dc86fb7e Mon Sep 17 00:00:00 2001 From: Abhishek B Date: Wed, 24 Sep 2025 18:09:35 +0530 Subject: [PATCH 103/211] Correct the database name associated with the catalog plugin Signed-off-by: Abhishek B --- docs/tutorials/manual-knex-rollback.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/tutorials/manual-knex-rollback.md b/docs/tutorials/manual-knex-rollback.md index b0f67ba3e0..442a48e2f5 100644 --- a/docs/tutorials/manual-knex-rollback.md +++ b/docs/tutorials/manual-knex-rollback.md @@ -23,7 +23,7 @@ You can interact with Knex running the commands below in the project root: We want to check the migration status: ```sh -$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ Using environment: production Found 2 Completed Migration file/files. 20211229105307_init.js @@ -34,7 +34,7 @@ No Pending Migration files Found. Now lets rollback a specific migration called `20240113144027_assets-namespace.js`: ```sh -$ node_modules/.bin/knex migrate:down 20240113144027_assets-namespace.js --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +$ node_modules/.bin/knex migrate:down 20240113144027_assets-namespace.js --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ Using environment: production Batch 2 rolled back the following migrations: 20240113144027_assets-namespace.js @@ -43,7 +43,7 @@ Batch 2 rolled back the following migrations: Now we can check the migration status again to confirm the rollback: ```sh -$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ Using environment: production Found 1 Completed Migration file/files. 20211229105307_init.js @@ -54,7 +54,7 @@ Found 1 Pending Migration file/files. Now lets use `migrate:currentVersion` which retrieves the current migration version. If there aren't any migrations run yet, it will return "none". ```sh -$ node_modules/.bin/knex migrate:currentVersion --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg +$ node_modules/.bin/knex migrate:currentVersion --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg Using environment: production Current Version: 20240113144027 ``` From c2c60546d2d8f086f7c957602ed9c78cafe8ee46 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 24 Sep 2025 12:57:42 +0000 Subject: [PATCH 104/211] Version Packages (next) --- .changeset/create-app-1758718573.md | 5 + .changeset/pre.json | 2 + docs/releases/v1.44.0-next.1-changelog.md | 1925 +++++++++++++++++ package.json | 2 +- packages/app-defaults/CHANGELOG.md | 10 + packages/app-defaults/package.json | 2 +- packages/app-next/CHANGELOG.md | 44 + packages/app-next/package.json | 2 +- packages/app/CHANGELOG.md | 39 + packages/app/package.json | 2 +- packages/backend-app-api/CHANGELOG.md | 8 + packages/backend-app-api/package.json | 2 +- packages/backend-defaults/CHANGELOG.md | 16 + packages/backend-defaults/package.json | 2 +- .../CHANGELOG.md | 22 + .../package.json | 2 +- packages/backend-openapi-utils/CHANGELOG.md | 7 + packages/backend-openapi-utils/package.json | 2 +- packages/backend-plugin-api/CHANGELOG.md | 10 + packages/backend-plugin-api/package.json | 2 +- packages/backend-test-utils/CHANGELOG.md | 13 + packages/backend-test-utils/package.json | 2 +- packages/backend/CHANGELOG.md | 40 + packages/backend/package.json | 2 +- packages/cli/CHANGELOG.md | 11 + packages/cli/package.json | 2 +- packages/config-loader/CHANGELOG.md | 8 + packages/config-loader/package.json | 2 +- packages/config/CHANGELOG.md | 6 + packages/config/package.json | 2 +- packages/core-app-api/CHANGELOG.md | 8 + packages/core-app-api/package.json | 2 +- packages/core-compat-api/CHANGELOG.md | 9 + packages/core-compat-api/package.json | 2 +- packages/core-components/CHANGELOG.md | 8 + packages/core-components/package.json | 2 +- packages/core-plugin-api/CHANGELOG.md | 7 + packages/core-plugin-api/package.json | 2 +- packages/create-app/CHANGELOG.md | 6 + packages/create-app/package.json | 2 +- packages/dev-utils/CHANGELOG.md | 12 + packages/dev-utils/package.json | 2 +- packages/frontend-app-api/CHANGELOG.md | 11 + packages/frontend-app-api/package.json | 2 +- packages/frontend-defaults/CHANGELOG.md | 11 + packages/frontend-defaults/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- packages/frontend-plugin-api/CHANGELOG.md | 8 + packages/frontend-plugin-api/package.json | 2 +- packages/frontend-test-utils/CHANGELOG.md | 11 + packages/frontend-test-utils/package.json | 2 +- packages/integration-aws-node/CHANGELOG.md | 7 + packages/integration-aws-node/package.json | 2 +- packages/integration-react/CHANGELOG.md | 9 + packages/integration-react/package.json | 2 +- packages/integration/CHANGELOG.md | 7 + packages/integration/package.json | 2 +- packages/repo-tools/CHANGELOG.md | 9 + packages/repo-tools/package.json | 2 +- .../techdocs-cli-embedded-app/CHANGELOG.md | 17 + .../techdocs-cli-embedded-app/package.json | 2 +- packages/techdocs-cli/CHANGELOG.md | 9 + packages/techdocs-cli/package.json | 2 +- packages/test-utils/CHANGELOG.md | 11 + packages/test-utils/package.json | 2 +- plugins/api-docs/CHANGELOG.md | 14 + plugins/api-docs/package.json | 2 +- plugins/app-backend/CHANGELOG.md | 11 + plugins/app-backend/package.json | 2 +- plugins/app-node/CHANGELOG.md | 8 + plugins/app-node/package.json | 2 +- plugins/app-visualizer/CHANGELOG.md | 9 + plugins/app-visualizer/package.json | 2 +- plugins/app/CHANGELOG.md | 11 + plugins/app/package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../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 | 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 | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../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 | 9 + .../package.json | 2 +- .../CHANGELOG.md | 8 + .../package.json | 2 +- plugins/auth-backend/CHANGELOG.md | 10 + plugins/auth-backend/package.json | 2 +- plugins/auth-node/CHANGELOG.md | 9 + plugins/auth-node/package.json | 2 +- plugins/auth-react/CHANGELOG.md | 8 + plugins/auth-react/package.json | 2 +- .../catalog-backend-module-aws/CHANGELOG.md | 14 + .../catalog-backend-module-aws/package.json | 2 +- .../catalog-backend-module-azure/CHANGELOG.md | 11 + .../catalog-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../catalog-backend-module-gcp/CHANGELOG.md | 10 + .../catalog-backend-module-gcp/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../catalog-backend-module-gitea/CHANGELOG.md | 11 + .../catalog-backend-module-gitea/package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../catalog-backend-module-ldap/CHANGELOG.md | 10 + .../catalog-backend-module-ldap/package.json | 2 +- .../catalog-backend-module-logs/CHANGELOG.md | 9 + .../catalog-backend-module-logs/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- plugins/catalog-backend/CHANGELOG.md | 16 + plugins/catalog-backend/package.json | 2 +- plugins/catalog-common/CHANGELOG.md | 8 + plugins/catalog-common/package.json | 2 +- plugins/catalog-graph/CHANGELOG.md | 12 + plugins/catalog-graph/package.json | 2 +- plugins/catalog-import/CHANGELOG.md | 16 + plugins/catalog-import/package.json | 2 +- plugins/catalog-node/CHANGELOG.md | 11 + plugins/catalog-node/package.json | 2 +- plugins/catalog-react/CHANGELOG.md | 16 + plugins/catalog-react/package.json | 2 +- .../CHANGELOG.md | 7 + .../package.json | 2 +- .../catalog-unprocessed-entities/CHANGELOG.md | 10 + .../catalog-unprocessed-entities/package.json | 2 +- plugins/catalog/CHANGELOG.md | 19 + plugins/catalog/package.json | 2 +- plugins/config-schema/CHANGELOG.md | 8 + plugins/config-schema/package.json | 2 +- plugins/devtools-backend/CHANGELOG.md | 13 + plugins/devtools-backend/package.json | 2 +- plugins/devtools-common/CHANGELOG.md | 7 + plugins/devtools-common/package.json | 2 +- plugins/devtools/CHANGELOG.md | 12 + plugins/devtools/package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../events-backend-module-azure/CHANGELOG.md | 8 + .../events-backend-module-azure/package.json | 2 +- .../CHANGELOG.md | 8 + .../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 | 10 + .../events-backend-module-github/package.json | 2 +- .../events-backend-module-gitlab/CHANGELOG.md | 9 + .../events-backend-module-gitlab/package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../events-backend-module-kafka/CHANGELOG.md | 9 + .../events-backend-module-kafka/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 | 7 + .../example-todo-list-backend/package.json | 2 +- plugins/example-todo-list-common/CHANGELOG.md | 7 + plugins/example-todo-list-common/package.json | 2 +- plugins/example-todo-list/CHANGELOG.md | 8 + plugins/example-todo-list/package.json | 2 +- plugins/gateway-backend/CHANGELOG.md | 7 + plugins/gateway-backend/package.json | 2 +- plugins/home-react/CHANGELOG.md | 9 + plugins/home-react/package.json | 2 +- plugins/home/CHANGELOG.md | 15 + plugins/home/package.json | 2 +- plugins/kubernetes-backend/CHANGELOG.md | 16 + plugins/kubernetes-backend/package.json | 2 +- plugins/kubernetes-cluster/CHANGELOG.md | 12 + plugins/kubernetes-cluster/package.json | 2 +- plugins/kubernetes-common/CHANGELOG.md | 7 + plugins/kubernetes-common/package.json | 2 +- plugins/kubernetes-node/CHANGELOG.md | 8 + plugins/kubernetes-node/package.json | 2 +- plugins/kubernetes-react/CHANGELOG.md | 9 + plugins/kubernetes-react/package.json | 2 +- plugins/kubernetes/CHANGELOG.md | 14 + plugins/kubernetes/package.json | 2 +- plugins/mcp-actions-backend/CHANGELOG.md | 10 + plugins/mcp-actions-backend/package.json | 2 +- .../CHANGELOG.md | 13 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- plugins/notifications-backend/CHANGELOG.md | 14 + plugins/notifications-backend/package.json | 2 +- plugins/notifications-common/CHANGELOG.md | 7 + plugins/notifications-common/package.json | 2 +- plugins/notifications-node/CHANGELOG.md | 10 + plugins/notifications-node/package.json | 2 +- plugins/notifications/CHANGELOG.md | 12 + plugins/notifications/package.json | 2 +- plugins/org-react/CHANGELOG.md | 10 + plugins/org-react/package.json | 2 +- plugins/org/CHANGELOG.md | 12 + plugins/org/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/permission-backend/CHANGELOG.md | 11 + plugins/permission-backend/package.json | 2 +- plugins/permission-common/CHANGELOG.md | 7 + plugins/permission-common/package.json | 2 +- plugins/permission-node/CHANGELOG.md | 10 + plugins/permission-node/package.json | 2 +- plugins/permission-react/CHANGELOG.md | 9 + plugins/permission-react/package.json | 2 +- plugins/proxy-backend/CHANGELOG.md | 8 + plugins/proxy-backend/package.json | 2 +- plugins/proxy-node/CHANGELOG.md | 7 + plugins/proxy-node/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- .../CHANGELOG.md | 9 + .../package.json | 2 +- plugins/scaffolder-backend/CHANGELOG.md | 27 + plugins/scaffolder-backend/package.json | 2 +- plugins/scaffolder-common/CHANGELOG.md | 8 + plugins/scaffolder-common/package.json | 2 +- .../scaffolder-node-test-utils/CHANGELOG.md | 9 + .../scaffolder-node-test-utils/package.json | 2 +- plugins/scaffolder-node/CHANGELOG.md | 10 + plugins/scaffolder-node/package.json | 2 +- plugins/scaffolder-react/CHANGELOG.md | 13 + plugins/scaffolder-react/package.json | 2 +- plugins/scaffolder/CHANGELOG.md | 19 + plugins/scaffolder/package.json | 2 +- .../CHANGELOG.md | 14 + .../package.json | 2 +- .../CHANGELOG.md | 11 + .../package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- plugins/search-backend-module-pg/CHANGELOG.md | 10 + plugins/search-backend-module-pg/package.json | 2 +- .../CHANGELOG.md | 10 + .../package.json | 2 +- .../CHANGELOG.md | 15 + .../package.json | 2 +- plugins/search-backend-node/CHANGELOG.md | 10 + plugins/search-backend-node/package.json | 2 +- plugins/search-backend/CHANGELOG.md | 14 + plugins/search-backend/package.json | 2 +- plugins/search-common/CHANGELOG.md | 7 + plugins/search-common/package.json | 2 +- plugins/search-react/CHANGELOG.md | 10 + plugins/search-react/package.json | 2 +- plugins/search/CHANGELOG.md | 13 + plugins/search/package.json | 2 +- plugins/signals-backend/CHANGELOG.md | 11 + plugins/signals-backend/package.json | 2 +- plugins/signals-node/CHANGELOG.md | 10 + plugins/signals-node/package.json | 2 +- plugins/signals-react/CHANGELOG.md | 7 + plugins/signals-react/package.json | 2 +- plugins/signals/CHANGELOG.md | 11 + plugins/signals/package.json | 2 +- .../techdocs-addons-test-utils/CHANGELOG.md | 15 + .../techdocs-addons-test-utils/package.json | 2 +- plugins/techdocs-backend/CHANGELOG.md | 16 + plugins/techdocs-backend/package.json | 2 +- .../CHANGELOG.md | 12 + .../package.json | 2 +- plugins/techdocs-node/CHANGELOG.md | 11 + plugins/techdocs-node/package.json | 2 +- plugins/techdocs-react/CHANGELOG.md | 10 + plugins/techdocs-react/package.json | 2 +- plugins/techdocs/CHANGELOG.md | 19 + plugins/techdocs/package.json | 2 +- plugins/user-settings-backend/CHANGELOG.md | 10 + plugins/user-settings-backend/package.json | 2 +- plugins/user-settings/CHANGELOG.md | 13 + plugins/user-settings/package.json | 2 +- 358 files changed, 4032 insertions(+), 178 deletions(-) create mode 100644 .changeset/create-app-1758718573.md create mode 100644 docs/releases/v1.44.0-next.1-changelog.md diff --git a/.changeset/create-app-1758718573.md b/.changeset/create-app-1758718573.md new file mode 100644 index 0000000000..b50d431d4b --- /dev/null +++ b/.changeset/create-app-1758718573.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped create-app version. diff --git a/.changeset/pre.json b/.changeset/pre.json index 7003364d39..03b4d762e2 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -213,6 +213,7 @@ "cold-coats-show", "cool-baboons-count", "create-app-1758639549", + "create-app-1758718573", "eager-toes-start", "fast-heads-brake", "fast-queens-guess", @@ -229,6 +230,7 @@ "red-times-bet", "salty-words-wash", "short-aliens-invite", + "six-cooks-battle", "solid-bikes-leave", "tame-hairs-smash", "tender-cups-tap", diff --git a/docs/releases/v1.44.0-next.1-changelog.md b/docs/releases/v1.44.0-next.1-changelog.md new file mode 100644 index 0000000000..e9d3f16f97 --- /dev/null +++ b/docs/releases/v1.44.0-next.1-changelog.md @@ -0,0 +1,1925 @@ +# Release v1.44.0-next.1 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.44.0-next.1](https://backstage.github.io/upgrade-helper/?to=1.44.0-next.1) + +## @backstage/app-defaults@1.7.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + +## @backstage/backend-app-api@1.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + +## @backstage/backend-defaults@0.13.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-app-api@1.2.8-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/cli-node@0.2.14 + +## @backstage/backend-dynamic-feature-service@0.7.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/plugin-app-node@0.1.38-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-events-backend@0.5.7-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/cli-node@0.2.14 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/backend-openapi-utils@0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + +## @backstage/backend-plugin-api@1.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + +## @backstage/backend-test-utils@1.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-app-api@1.2.8-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/cli@0.34.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/cli-node@0.2.14 + - @backstage/release-manifests@0.0.13 + +## @backstage/config@1.3.4-next.0 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. + +## @backstage/config-loader@1.10.4-next.0 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. +- Updated dependencies + - @backstage/config@1.3.4-next.0 + +## @backstage/core-app-api@1.19.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + +## @backstage/core-compat-api@0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/core-components@0.18.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + +## @backstage/core-plugin-api@1.11.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + +## @backstage/create-app@0.7.5-next.1 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/dev-utils@1.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + +## @backstage/frontend-app-api@0.13.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-defaults@0.3.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/frontend-defaults@0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-app@0.3.1-next.1 + +## @backstage/frontend-dynamic-feature-loader@0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/frontend-plugin-api@0.12.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + +## @backstage/frontend-test-utils@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/test-utils@1.7.12-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-app@0.3.1-next.1 + +## @backstage/integration@1.18.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + +## @backstage/integration-aws-node@0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + +## @backstage/integration-react@1.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + +## @backstage/repo-tools@0.15.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/cli-node@0.2.14 + +## @techdocs/cli@1.9.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/plugin-techdocs-node@1.13.8-next.1 + +## @backstage/test-utils@1.7.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + +## @backstage/plugin-api-docs@0.13.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-app@0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/plugin-app-backend@0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/plugin-app-node@0.1.38-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-app-node@0.1.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + +## @backstage/plugin-app-visualizer@0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/plugin-auth-backend@0.25.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-auth0-provider@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-backend@0.25.5-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-github-provider@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-google-provider@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-backend@0.25.5-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-okta-provider@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-openshift-provider@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + +## @backstage/plugin-auth-node@0.6.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-auth-react@0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + +## @backstage/plugin-catalog@1.31.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-backend@3.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-backend-module-aws@0.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + +## @backstage/plugin-catalog-backend-module-azure@0.3.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-gitea@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-github@0.11.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend-module-github@0.11.1-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-catalog-backend-module-ldap@0.11.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-logs@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.10-next.0 + +## @backstage/plugin-catalog-common@1.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-catalog-graph@0.5.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-import@0.13.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-node@1.19.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-react@1.21.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-test-utils@0.3.7-next.1 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/plugin-catalog-unprocessed-entities-common@0.0.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + +## @backstage/plugin-config-schema@0.1.73-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + +## @backstage/plugin-devtools@0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-devtools-common@0.1.18-next.0 + +## @backstage/plugin-devtools-backend@0.5.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-devtools-common@0.1.18-next.0 + +## @backstage/plugin-devtools-common@0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + +## @backstage/plugin-events-backend@0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-azure@0.2.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-server@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-gerrit@0.2.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-github@0.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-gitlab@0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-google-pubsub@0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-module-kafka@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-backend-test-utils@0.1.49-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-events-node@0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + +## @backstage/plugin-gateway-backend@1.0.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + +## @backstage/plugin-home@0.8.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-home-react@0.1.31-next.1 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-home-react@0.1.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/plugin-kubernetes@0.12.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-kubernetes-react@0.5.12-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + +## @backstage/plugin-kubernetes-backend@0.20.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-kubernetes-node@0.3.5-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-kubernetes-cluster@0.0.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-kubernetes-react@0.5.12-next.1 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + +## @backstage/plugin-kubernetes-common@0.9.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + +## @backstage/plugin-kubernetes-node@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + +## @backstage/plugin-kubernetes-react@0.5.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + +## @backstage/plugin-mcp-actions-backend@0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-notifications@0.5.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-signals-react@0.0.16-next.0 + +## @backstage/plugin-notifications-backend@0.5.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-notifications-node@0.2.20-next.0 + +## @backstage/plugin-notifications-backend-module-email@0.3.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-notifications-node@0.2.20-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-notifications-backend-module-slack@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-notifications-node@0.2.20-next.0 + +## @backstage/plugin-notifications-common@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + +## @backstage/plugin-notifications-node@0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-org@0.6.45-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + +## @backstage/plugin-org-react@0.1.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-permission-backend@0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + +## @backstage/plugin-permission-common@0.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + +## @backstage/plugin-permission-node@0.10.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + +## @backstage/plugin-permission-react@0.4.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + +## @backstage/plugin-proxy-backend@0.6.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-proxy-node@0.1.9-next.0 + +## @backstage/plugin-proxy-node@0.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + +## @backstage/plugin-scaffolder@1.34.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-scaffolder-react@1.19.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-scaffolder-backend@3.0.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6-next.1 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.1 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-notifications-node@0.2.20-next.0 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.3.4-next.1 + +## @backstage/plugin-scaffolder-common@1.7.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/plugin-permission-common@0.9.2-next.0 + +## @backstage/plugin-scaffolder-node@0.12.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + +## @backstage/plugin-scaffolder-node-test-utils@0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/backend-test-utils@1.9.1-next.1 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + +## @backstage/plugin-scaffolder-react@1.19.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-search@1.4.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-search-backend@2.0.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-search-backend-module-catalog@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-search-backend-module-elasticsearch@1.7.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-search-backend-module-explore@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-search-backend-module-pg@0.5.49-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-search-backend-module-techdocs@0.4.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-techdocs-node@1.13.8-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-search-backend-node@1.3.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-search-common@1.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + +## @backstage/plugin-search-react@1.9.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-signals@0.0.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-signals-react@0.0.16-next.0 + +## @backstage/plugin-signals-backend@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-signals-node@0.1.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + +## @backstage/plugin-signals-react@0.0.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.1-next.0 + +## @backstage/plugin-techdocs@1.15.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-auth-react@0.1.20-next.1 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/test-utils@1.7.12-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + +## @backstage/plugin-techdocs-backend@2.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.1 + - @backstage/plugin-techdocs-node@1.13.8-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/plugin-techdocs-node@1.13.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## @backstage/plugin-techdocs-react@1.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + +## @backstage/plugin-user-settings@0.8.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-signals-react@0.0.16-next.0 + +## @backstage/plugin-user-settings-backend@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + +## example-app@0.2.114-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/cli@0.34.4-next.1 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-catalog-import@0.13.6-next.1 + - @backstage/plugin-home@0.8.13-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/plugin-api-docs@0.13.0-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-graph@0.5.2-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-notifications@0.5.10-next.1 + - @backstage/plugin-org@0.6.45-next.1 + - @backstage/plugin-scaffolder@1.34.2-next.1 + - @backstage/plugin-scaffolder-react@1.19.2-next.1 + - @backstage/plugin-search@1.4.31-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/plugin-signals@0.0.24-next.1 + - @backstage/plugin-user-settings@0.8.27-next.1 + - @backstage/plugin-auth-react@0.1.20-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.1 + - @backstage/plugin-devtools@0.1.32-next.1 + - @backstage/plugin-kubernetes@0.12.12-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## example-app-next@0.0.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/cli@0.34.4-next.1 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/frontend-defaults@0.3.2-next.1 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-catalog-import@0.13.6-next.1 + - @backstage/plugin-home@0.8.13-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-api-docs@0.13.0-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-graph@0.5.2-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-notifications@0.5.10-next.1 + - @backstage/plugin-org@0.6.45-next.1 + - @backstage/plugin-scaffolder@1.34.2-next.1 + - @backstage/plugin-scaffolder-react@1.19.2-next.1 + - @backstage/plugin-search@1.4.31-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/plugin-signals@0.0.24-next.1 + - @backstage/plugin-user-settings@0.8.27-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-app@0.3.1-next.1 + - @backstage/plugin-app-visualizer@0.1.24-next.1 + - @backstage/plugin-auth-react@0.1.20-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.1 + - @backstage/plugin-kubernetes@0.12.12-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.1 + - @backstage/plugin-auth@0.1.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + +## example-backend@0.0.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/plugin-app-backend@0.5.7-next.0 + - @backstage/plugin-devtools-backend@0.5.10-next.1 + - @backstage/plugin-proxy-backend@0.6.7-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-backend@0.25.5-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.13-next.0 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.1-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.7-next.0 + - @backstage/plugin-events-backend@0.5.7-next.0 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.5-next.0 + - @backstage/plugin-kubernetes-backend@0.20.3-next.1 + - @backstage/plugin-notifications-backend@0.5.11-next.0 + - @backstage/plugin-permission-backend@0.7.5-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-scaffolder-backend@3.0.0-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.1 + - @backstage/plugin-search-backend@2.0.7-next.1 + - @backstage/plugin-search-backend-module-catalog@0.3.9-next.0 + - @backstage/plugin-search-backend-module-explore@0.3.8-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.1 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-signals-backend@0.3.9-next.0 + - @backstage/plugin-techdocs-backend@2.1.1-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.8-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.15-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.5-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.4-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.13-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.15-next.1 + +## techdocs-cli-embedded-app@0.2.113-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/cli@0.34.4-next.1 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/test-utils@1.7.12-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + +## @internal/plugin-todo-list@1.0.44-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + +## @internal/plugin-todo-list-backend@1.0.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + +## @internal/plugin-todo-list-common@1.0.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 diff --git a/package.json b/package.json index c58f5fb3d3..b510a5a864 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.44.0-next.0", + "version": "1.44.0-next.1", "backstage": { "cli": { "new": { diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index e5d901b279..7e0fe1b55d 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/app-defaults +## 1.7.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + ## 1.7.1-next.0 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index e872055314..1a2cbecc12 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/app-defaults", - "version": "1.7.1-next.0", + "version": "1.7.1-next.1", "description": "Provides the default wiring of a Backstage App", "backstage": { "role": "web-library" diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index c1abb0f661..87c84cf26c 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,49 @@ # example-app-next +## 0.0.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/cli@0.34.4-next.1 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/frontend-defaults@0.3.2-next.1 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-catalog-import@0.13.6-next.1 + - @backstage/plugin-home@0.8.13-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-api-docs@0.13.0-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-graph@0.5.2-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-notifications@0.5.10-next.1 + - @backstage/plugin-org@0.6.45-next.1 + - @backstage/plugin-scaffolder@1.34.2-next.1 + - @backstage/plugin-scaffolder-react@1.19.2-next.1 + - @backstage/plugin-search@1.4.31-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/plugin-signals@0.0.24-next.1 + - @backstage/plugin-user-settings@0.8.27-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-app@0.3.1-next.1 + - @backstage/plugin-app-visualizer@0.1.24-next.1 + - @backstage/plugin-auth-react@0.1.20-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.1 + - @backstage/plugin-kubernetes@0.12.12-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.1 + - @backstage/plugin-auth@0.1.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 0.0.28-next.0 ### Patch Changes diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 99b0a23f51..ebfb59c96a 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.28-next.0", + "version": "0.0.28-next.1", "backstage": { "role": "frontend" }, diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index daa56a15da..675001c296 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,44 @@ # example-app +## 0.2.114-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/cli@0.34.4-next.1 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-catalog-import@0.13.6-next.1 + - @backstage/plugin-home@0.8.13-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/plugin-api-docs@0.13.0-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-graph@0.5.2-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-notifications@0.5.10-next.1 + - @backstage/plugin-org@0.6.45-next.1 + - @backstage/plugin-scaffolder@1.34.2-next.1 + - @backstage/plugin-scaffolder-react@1.19.2-next.1 + - @backstage/plugin-search@1.4.31-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/plugin-signals@0.0.24-next.1 + - @backstage/plugin-user-settings@0.8.27-next.1 + - @backstage/plugin-auth-react@0.1.20-next.1 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22-next.1 + - @backstage/plugin-devtools@0.1.32-next.1 + - @backstage/plugin-kubernetes@0.12.12-next.1 + - @backstage/plugin-kubernetes-cluster@0.0.30-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 0.2.114-next.0 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 0d0ad4e1c2..56b5178692 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.114-next.0", + "version": "0.2.114-next.1", "backstage": { "role": "frontend" }, diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index d965783fac..a0176a008f 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/backend-app-api +## 1.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + ## 1.2.7 ### Patch Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 9407f28707..b082de0cfa 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-app-api", - "version": "1.2.7", + "version": "1.2.8-next.0", "description": "Core API used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index e390e71e79..1201918fb5 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/backend-defaults +## 0.13.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-app-api@1.2.8-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/cli-node@0.2.14 + ## 0.13.0-next.0 ### Minor Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index 4a27d65c33..f85d34db9e 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.13.0-next.0", + "version": "0.13.0-next.1", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index 2e2a966438..a15b315a62 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/backend-dynamic-feature-service +## 0.7.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/plugin-app-node@0.1.38-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-events-backend@0.5.7-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/cli-node@0.2.14 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 0.7.5-next.0 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 268d54c180..78e881a6bc 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.7.5-next.0", + "version": "0.7.5-next.1", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 87bea0405e..61815004c3 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/backend-openapi-utils +## 0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + ## 0.6.1 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 31d7aaf486..48211fa851 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-openapi-utils", - "version": "0.6.1", + "version": "0.6.2-next.0", "description": "OpenAPI typescript support.", "backstage": { "role": "node-library" diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 914d45f68b..44931c21e1 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/backend-plugin-api +## 1.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + ## 1.4.3 ### Patch Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index baebdcd506..fcdbd15107 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-plugin-api", - "version": "1.4.3", + "version": "1.4.4-next.0", "description": "Core API used by Backstage backend plugins", "backstage": { "role": "node-library" diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index d81c184682..536d595d34 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/backend-test-utils +## 1.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-app-api@1.2.8-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 1.9.1-next.0 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index a9b160bcd8..3ee214b345 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "1.9.1-next.0", + "version": "1.9.1-next.1", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index a1e68bd8e7..f8c601b473 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,45 @@ # example-backend +## 0.0.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/plugin-app-backend@0.5.7-next.0 + - @backstage/plugin-devtools-backend@0.5.10-next.1 + - @backstage/plugin-proxy-backend@0.6.7-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-backend@0.25.5-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.13-next.0 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.1-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.7-next.0 + - @backstage/plugin-events-backend@0.5.7-next.0 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.5-next.0 + - @backstage/plugin-kubernetes-backend@0.20.3-next.1 + - @backstage/plugin-notifications-backend@0.5.11-next.0 + - @backstage/plugin-permission-backend@0.7.5-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-scaffolder-backend@3.0.0-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.1 + - @backstage/plugin-search-backend@2.0.7-next.1 + - @backstage/plugin-search-backend-module-catalog@0.3.9-next.0 + - @backstage/plugin-search-backend-module-explore@0.3.8-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.1 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-signals-backend@0.3.9-next.0 + - @backstage/plugin-techdocs-backend@2.1.1-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.8-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.15-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.5-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.4-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.13-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.15-next.1 + ## 0.0.43-next.0 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 15bfab297f..c698a424a5 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.43-next.0", + "version": "0.0.43-next.1", "backstage": { "role": "backend" }, diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 8c35d62164..28b2293982 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/cli +## 0.34.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/cli-node@0.2.14 + - @backstage/release-manifests@0.0.13 + ## 0.34.4-next.0 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 99a4c56b58..3c12b383ad 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.34.4-next.0", + "version": "0.34.4-next.1", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 872136d5a2..b29be4a927 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/config-loader +## 1.10.4-next.0 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. +- Updated dependencies + - @backstage/config@1.3.4-next.0 + ## 1.10.3 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index f8d103e97b..dda4d6e222 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config-loader", - "version": "1.10.3", + "version": "1.10.4-next.0", "description": "Config loading functionality used by Backstage backend, and CLI", "backstage": { "role": "node-library" diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index bf9455a84e..43bcc0551b 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/config +## 1.3.4-next.0 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. + ## 1.3.3 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index 4ec4038e26..ec6cd78422 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config", - "version": "1.3.3", + "version": "1.3.4-next.0", "description": "Config API used by Backstage core, backend, and CLI", "backstage": { "role": "common-library" diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 05bdf33224..334a36acb3 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/core-app-api +## 1.19.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + ## 1.19.0 ### Minor Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 8a52a71867..d4a4dce17d 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-app-api", - "version": "1.19.0", + "version": "1.19.1-next.0", "description": "Core app API used by Backstage apps", "backstage": { "role": "web-library" diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index a1d9a3d37d..f0cb3b6878 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/core-compat-api +## 0.5.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 0.5.3-next.0 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 1ccb842a9c..0bbe284db9 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.5.3-next.0", + "version": "0.5.3-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index adb205392f..b6109caf2e 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/core-components +## 0.18.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + ## 0.18.2-next.0 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 4e16285cfb..c2d09762b3 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-components", - "version": "0.18.2-next.0", + "version": "0.18.2-next.1", "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 61291f8e05..89ad055230 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/core-plugin-api +## 1.11.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + ## 1.11.0 ### Minor Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 6e62c66c3a..d019090bb4 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-plugin-api", - "version": "1.11.0", + "version": "1.11.1-next.0", "description": "Core API used by Backstage plugins", "backstage": { "role": "web-library" diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 245cd34a46..b2960b4c83 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/create-app +## 0.7.5-next.1 + +### Patch Changes + +- Bumped create-app version. + ## 0.7.5-next.0 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 0330fb9663..cd37f6eef6 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.7.5-next.0", + "version": "0.7.5-next.1", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 10177c7c7d..7f7338afeb 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/dev-utils +## 1.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + ## 1.1.15-next.0 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 055f4f94b7..707717f6a4 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.15-next.0", + "version": "1.1.15-next.1", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 80591e1bd9..780e62fbe8 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/frontend-app-api +## 0.13.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-defaults@0.3.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 0.13.1-next.0 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index bd52912d55..707067b54f 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.13.1-next.0", + "version": "0.13.1-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index 2ebae3e384..f60f948f3a 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/frontend-defaults +## 0.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-app@0.3.1-next.1 + ## 0.3.2-next.0 ### Patch Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index 06efcf0576..ab8f88b653 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.3.2-next.0", + "version": "0.3.2-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-dynamic-feature-loader/CHANGELOG.md b/packages/frontend-dynamic-feature-loader/CHANGELOG.md index fd185af296..853dc59772 100644 --- a/packages/frontend-dynamic-feature-loader/CHANGELOG.md +++ b/packages/frontend-dynamic-feature-loader/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/frontend-dynamic-feature-loader +## 0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 0.1.6-next.0 ### Patch Changes diff --git a/packages/frontend-dynamic-feature-loader/package.json b/packages/frontend-dynamic-feature-loader/package.json index bda6b0686c..7c68833418 100644 --- a/packages/frontend-dynamic-feature-loader/package.json +++ b/packages/frontend-dynamic-feature-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-dynamic-feature-loader", - "version": "0.1.6-next.0", + "version": "0.1.6-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 537939e8a5..e12aedb385 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/frontend-plugin-api +## 0.12.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + ## 0.12.1-next.0 ### Patch Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 714cd0471a..15e557101c 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.12.1-next.0", + "version": "0.12.1-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 6f17abe709..0c198ecc1b 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/frontend-test-utils +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/frontend-app-api@0.13.1-next.1 + - @backstage/test-utils@1.7.12-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-app@0.3.1-next.1 + ## 0.3.7-next.0 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 7aab8a9265..3241bc0ce5 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.3.7-next.0", + "version": "0.3.7-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/integration-aws-node/CHANGELOG.md b/packages/integration-aws-node/CHANGELOG.md index 297e6330f2..830f38632e 100644 --- a/packages/integration-aws-node/CHANGELOG.md +++ b/packages/integration-aws-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/integration-aws-node +## 0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + ## 0.1.17 ### Patch Changes diff --git a/packages/integration-aws-node/package.json b/packages/integration-aws-node/package.json index ab0f3583a4..10855b7f54 100644 --- a/packages/integration-aws-node/package.json +++ b/packages/integration-aws-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-aws-node", - "version": "0.1.17", + "version": "0.1.18-next.0", "description": "Helpers for fetching AWS account credentials", "backstage": { "role": "node-library" diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index efc73ec308..adf06f88bd 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration-react +## 1.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + ## 1.2.11-next.0 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index fe6c47f736..410500c520 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-react", - "version": "1.2.11-next.0", + "version": "1.2.11-next.1", "description": "Frontend package for managing integrations towards external systems", "backstage": { "role": "web-library" diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index b03eff3ebc..e6546099f2 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/integration +## 1.18.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + ## 1.18.1-next.0 ### Patch Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 8d7f655e2b..2dbb4a32e8 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "1.18.1-next.0", + "version": "1.18.1-next.1", "description": "Helpers for managing integrations towards external systems", "backstage": { "role": "common-library" diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index d93d88571a..d8272e383a 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/repo-tools +## 0.15.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/cli-node@0.2.14 + ## 0.15.2 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 0b0954da70..a536a95f4c 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.15.2", + "version": "0.15.3-next.0", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 5eda7ebcaf..54f526a1f2 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,22 @@ # techdocs-cli-embedded-app +## 0.2.113-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/cli@0.34.4-next.1 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/test-utils@1.7.12-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/app-defaults@1.7.1-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + ## 0.2.113-next.0 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 577426d02c..951a3df06e 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.113-next.0", + "version": "0.2.113-next.1", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 9d47af4de6..2fe1e1101a 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,14 @@ # @techdocs/cli +## 1.9.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/plugin-techdocs-node@1.13.8-next.1 + ## 1.9.9-next.0 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 4a4675c6e1..b46d0bc540 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.9-next.0", + "version": "1.9.9-next.1", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 57e6d7e237..8425798421 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/test-utils +## 1.7.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + ## 1.7.11 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index aade054088..6e914f3b50 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/test-utils", - "version": "1.7.11", + "version": "1.7.12-next.0", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 817b60fd0c..446f2ed89f 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-api-docs +## 0.13.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.13.0-next.0 ### Minor Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 0012ec9b54..1aa27b8770 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.13.0-next.0", + "version": "0.13.0-next.1", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 02039c933e..c4f1db2d8c 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-app-backend +## 0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/plugin-app-node@0.1.38-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.5.6 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 609cf1c543..4d4184e745 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.5.6", + "version": "0.5.7-next.0", "description": "A Backstage backend plugin that serves the Backstage frontend app", "backstage": { "role": "backend-plugin", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index ea9532aca5..85d63c28f0 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-node +## 0.1.38-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + ## 0.1.37 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index f11a84bc55..68f07f9a22 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-node", - "version": "0.1.37", + "version": "0.1.38-next.0", "description": "Node.js library for the app plugin", "backstage": { "role": "node-library", diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index 85876e49b2..ea0b147aff 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-app-visualizer +## 0.1.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 0.1.24-next.0 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index b0ce29d3c8..fa30acefe2 100644 --- a/plugins/app-visualizer/package.json +++ b/plugins/app-visualizer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-visualizer", - "version": "0.1.24-next.0", + "version": "0.1.24-next.1", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index 615f644cc4..f4bf0f1255 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-app +## 0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 0.3.1-next.0 ### Patch Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index c0934f7c54..5a31bcc847 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.3.1-next.0", + "version": "0.3.1-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "app", diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 16d1637cd6..842ca78bb5 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.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.4.7 ### Patch Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index 4fd74b74ca..cff00133b1 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", - "version": "0.4.7", + "version": "0.4.8-next.0", "description": "The atlassian-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md index 725b88735c..8a2ad5f168 100644 --- a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-auth0-provider +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/auth-backend-module-auth0-provider/package.json b/plugins/auth-backend-module-auth0-provider/package.json index 8e06baa4db..00dc9323b3 100644 --- a/plugins/auth-backend-module-auth0-provider/package.json +++ b/plugins/auth-backend-module-auth0-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-auth0-provider", - "version": "0.2.7", + "version": "0.2.8-next.0", "description": "The auth0-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index f2026fd2b9..723f7ad420 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-backend@0.25.5-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.4.7 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index d85b993f28..1ad538a2d9 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", - "version": "0.4.7", + "version": "0.4.8-next.0", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md index 355d9ea55f..893319b20f 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-azure-easyauth-provider +## 0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.2.12 ### Patch Changes diff --git a/plugins/auth-backend-module-azure-easyauth-provider/package.json b/plugins/auth-backend-module-azure-easyauth-provider/package.json index 534d721107..4d07dd3fc6 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/package.json +++ b/plugins/auth-backend-module-azure-easyauth-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-azure-easyauth-provider", - "version": "0.2.12", + "version": "0.2.13-next.0", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index 79a379c94c..6eed708472 100644 --- a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-bitbucket-provider +## 0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index e7826bcf08..f283fc02b4 100644 --- a/plugins/auth-backend-module-bitbucket-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-provider", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md index dad1d86780..9707eb2f61 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-bitbucket-server-provider +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-server-provider/package.json b/plugins/auth-backend-module-bitbucket-server-provider/package.json index 7f830811e7..2d5bfd2d90 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-server-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-server-provider", - "version": "0.2.7", + "version": "0.2.8-next.0", "description": "The bitbucket-server-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index 3bd2055825..f91945cc0c 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-cloudflare-access-provider +## 0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.4.7 ### Patch Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index 537526dbdc..168e8beb2a 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/package.json +++ b/plugins/auth-backend-module-cloudflare-access-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-cloudflare-access-provider", - "version": "0.4.7", + "version": "0.4.8-next.0", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index d587f6a999..bd7e41965c 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.4.7 ### 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 9bf40aaf4d..34b2a5e0b0 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", - "version": "0.4.7", + "version": "0.4.8-next.0", "description": "A GCP IAP auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index 0ea39f5b6d..70c7e89e07 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.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index d5854b1ff2..536447e70a 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "The github-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index afd0f2258f..2fb20e02da 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.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index b7cb8c5da3..a9e127e8bd 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "The gitlab-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 7c5651d120..c9bc882f02 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.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index f947b84b39..ee64a21ec7 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "A Google auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-guest-provider/CHANGELOG.md b/plugins/auth-backend-module-guest-provider/CHANGELOG.md index d57b0e967e..23207f8475 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.2.12 ### Patch Changes diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json index 7e6a657797..5ecfc2bff2 100644 --- a/plugins/auth-backend-module-guest-provider/package.json +++ b/plugins/auth-backend-module-guest-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-guest-provider", - "version": "0.2.12", + "version": "0.2.13-next.0", "description": "The guest-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index 3a21bd1bf7..41fa37111c 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.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index a5f661b839..d64f626183 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "The microsoft-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index 0caf910ee2..f9ffe99d18 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.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.4.7 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index beeba6af8b..3dd64a625b 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", - "version": "0.4.7", + "version": "0.4.8-next.0", "description": "The oauth2-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index b64d3a2481..a5adb734ed 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.2.12 ### 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 6edd907d51..1041138065 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/package.json +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", - "version": "0.2.12", + "version": "0.2.13-next.0", "description": "The oauth2-proxy-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index d21b18e15c..0b7d28f313 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.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-backend@0.25.5-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.4.7 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index d40ded83d7..2cace6cd92 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", - "version": "0.4.7", + "version": "0.4.8-next.0", "description": "The oidc-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index 4db3b0639b..dd873c0fa9 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.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.2.7 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index a1f8977142..e23e950456 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", - "version": "0.2.7", + "version": "0.2.8-next.0", "description": "The okta-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md index 95b2e88aec..2d3823b451 100644 --- a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-onelogin-provider +## 0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index a7b087aaa6..cb125303d4 100644 --- a/plugins/auth-backend-module-onelogin-provider/package.json +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-onelogin-provider", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "The onelogin-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-openshift-provider/CHANGELOG.md b/plugins/auth-backend-module-openshift-provider/CHANGELOG.md index 06f116c2ef..ee774f7634 100644 --- a/plugins/auth-backend-module-openshift-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-openshift-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-openshift-provider +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth-backend-module-openshift-provider/package.json b/plugins/auth-backend-module-openshift-provider/package.json index 648f09bf24..2a6d089f7d 100644 --- a/plugins/auth-backend-module-openshift-provider/package.json +++ b/plugins/auth-backend-module-openshift-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-openshift-provider", - "version": "0.1.0", + "version": "0.1.1-next.0", "description": "The OpenShift backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index a23d6cb733..57d2709222 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.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index ad80ff2904..0b0be4a1e7 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "The pinniped-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md index 41ac1cafdd..253020462a 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + ## 0.5.7 ### 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 0222da22a5..817dc428ab 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", - "version": "0.5.7", + "version": "0.5.8-next.0", "description": "The vmware-cloud-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index be4b8bcaad..952550248e 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend +## 0.25.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + ## 0.25.4 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 74c03204b6..f2f9e28c51 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.25.4", + "version": "0.25.5-next.0", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index d784004cd3..d69b151c1d 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-node +## 0.6.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.6.7 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 5c61000f64..2cd1d7e5b2 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.6.7", + "version": "0.6.8-next.0", "backstage": { "role": "node-library", "pluginId": "auth", diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index 86662ef3f3..afd088cad8 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-react +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index a41ccd3432..99d59b6e6d 100644 --- a/plugins/auth-react/package.json +++ b/plugins/auth-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-react", - "version": "0.1.20-next.0", + "version": "0.1.20-next.1", "description": "Web library for the auth plugin", "backstage": { "role": "web-library", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 582cea0429..a77ccf785c 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + ## 0.4.16-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 48088f2529..12d6761143 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.4.16-next.0", + "version": "0.4.16-next.1", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 7b58ca6a0b..8f04f5cae5 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.3.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.3.10-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index bc337337f0..44b8b9cfeb 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", - "version": "0.3.10-next.0", + "version": "0.3.10-next.1", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index 5021408d44..a5b83b98da 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + ## 0.5.6 ### Patch Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index a17bf50b47..3243d6e317 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.5.6", + "version": "0.5.7-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 531418dc68..bce5bc231b 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.5.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.5.4-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 562911f1bd..14bef6edaf 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", - "version": "0.5.4-next.0", + "version": "0.5.4-next.1", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 72f0f277ed..cb7767ce16 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.5.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.5.4-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 abbd7be1aa..f0bbf19128 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.5.4-next.0", + "version": "0.5.4-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index c02fe65bd5..dfba428022 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + ## 0.3.13-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 1123f00416..7a313a6486 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.3.13-next.0", + "version": "0.3.13-next.1", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index c26520ccc5..c1f9fe3867 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.3.7-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 2586dda386..eec09488a6 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.3.7-next.0", + "version": "0.3.7-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gitea/CHANGELOG.md b/plugins/catalog-backend-module-gitea/CHANGELOG.md index 09f2a60401..35bb7f8bd3 100644 --- a/plugins/catalog-backend-module-gitea/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gitea +## 0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.1.5-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitea/package.json b/plugins/catalog-backend-module-gitea/package.json index 3fac7944e1..601b973da0 100644 --- a/plugins/catalog-backend-module-gitea/package.json +++ b/plugins/catalog-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitea", - "version": "0.1.5-next.0", + "version": "0.1.5-next.1", "license": "Apache-2.0", "description": "The gitea backend module for the catalog plugin.", "main": "src/index.ts", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index ad55f9a4f5..df63d6fb8f 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend-module-github@0.11.1-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.3.15-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 a862c80768..bab1bdfe00 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.3.15-next.0", + "version": "0.3.15-next.1", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index c4cfc67865..2921c3960d 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-github +## 0.11.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.11.1-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index cf564acfe1..7ed76c0ed7 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.11.1-next.0", + "version": "0.11.1-next.1", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 654d97b7c1..b162bf1103 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 09d9796167..80a0b2627a 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index d4f3382f60..18a1915ee6 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.7.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.7.4-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 4a48f14bd6..6a676975a2 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.7.4-next.0", + "version": "0.7.4-next.1", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index d25514e90f..b15da28058 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.7.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.7.5-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 7aa7c5e7b1..b63bfaa76b 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", - "version": "0.7.5-next.0", + "version": "0.7.5-next.1", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 2a1dbf6d62..e3f5ef572d 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.11.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.11.9 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 91ac03c93a..38845929f4 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.11.9", + "version": "0.11.10-next.0", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index 2f62c77773..0ee19e94f0 100644 --- a/plugins/catalog-backend-module-logs/CHANGELOG.md +++ b/plugins/catalog-backend-module-logs/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-logs +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-backend@3.1.1-next.1 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.1.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index 74c71fd3fe..a51f0821ba 100644 --- a/plugins/catalog-backend-module-logs/package.json +++ b/plugins/catalog-backend-module-logs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-logs", - "version": "0.1.15-next.0", + "version": "0.1.15-next.1", "description": "A module that subscribes to catalog related events and logs them.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 8ab315e9dd..3db73151fe 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.8.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index f2f8a5d442..580fcc7962 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.8.0", + "version": "0.8.1-next.0", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 72eecbb0df..2c98a53a84 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.2.15-next.0 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index e8d9a5b741..96ced0dd97 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", - "version": "0.2.15-next.0", + "version": "0.2.15-next.1", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 14fa817eba..cdbee9bfb8 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + ## 0.2.14 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index ced5df4773..2501290bed 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", - "version": "0.2.14", + "version": "0.2.15-next.0", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index 6cddd6166b..b53acb9e97 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.2.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.2.13-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 5f1f4277d2..f5d9a6c1af 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.2.13-next.0", + "version": "0.2.13-next.1", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 1c4b8f8363..3df0e25d43 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.10-next.0 + ## 0.6.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 32bcfe904f..d2f10d1aa7 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", - "version": "0.6.4", + "version": "0.6.5-next.0", "description": "Backstage Catalog module to view unprocessed entities", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 0ec926a05c..0f2c5cb2ae 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend +## 3.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 3.1.1-next.0 ### Patch Changes diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index ec09efc9a3..fbb6789146 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "3.1.1-next.0", + "version": "3.1.1-next.1", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index a0cdac70e8..a536481835 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-common +## 1.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 1.1.5 ### Patch Changes diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 7ce4e23d55..9ee7c8d416 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-common", - "version": "1.1.5", + "version": "1.1.6-next.0", "description": "Common functionalities for the catalog plugin", "backstage": { "role": "common-library", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 4293e64516..b492c41a37 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-graph +## 0.5.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/catalog-client@1.12.0 + ## 0.5.2-next.0 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index aa66e4a234..f455dbce31 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.5.2-next.0", + "version": "0.5.2-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 10252aadbe..7cd77df78b 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-import +## 0.13.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.13.6-next.0 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 8065881127..80524e0fde 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.13.6-next.0", + "version": "0.13.6-next.1", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index ba27f2a2e2..c61acc0a30 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-node +## 1.19.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 1.19.0 ### Minor Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index 545656ae80..378068b773 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "1.19.0", + "version": "1.19.1-next.0", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index bfc54981fa..b4088aaaf7 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-react +## 1.21.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-test-utils@0.3.7-next.1 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 1.21.2-next.0 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index b645537a7e..cb55343b0a 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.21.2-next.0", + "version": "1.21.2-next.1", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-unprocessed-entities-common/CHANGELOG.md b/plugins/catalog-unprocessed-entities-common/CHANGELOG.md index d72fea4dd6..9e256e7b77 100644 --- a/plugins/catalog-unprocessed-entities-common/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-unprocessed-entities-common +## 0.0.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 0.0.9 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities-common/package.json b/plugins/catalog-unprocessed-entities-common/package.json index 6663c2535d..cd8aa8e4f0 100644 --- a/plugins/catalog-unprocessed-entities-common/package.json +++ b/plugins/catalog-unprocessed-entities-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities-common", - "version": "0.0.9", + "version": "0.0.10-next.0", "description": "Common functionalities for the catalog-unprocessed-entities plugin", "backstage": { "role": "common-library", diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index 3483042a69..6a6d24b25c 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.2.22-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 0.2.22-next.0 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index 0abe8ae2be..1eea850146 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.2.22-next.0", + "version": "0.2.22-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-unprocessed-entities", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index a10136e79b..e260fb05b7 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-catalog +## 1.31.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + ## 1.31.4-next.0 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 580b6abdb9..95c843e504 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.31.4-next.0", + "version": "1.31.4-next.1", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 5e94cbe988..15b30e6845 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-config-schema +## 0.1.73-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + ## 0.1.73-next.0 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index c49c1e9087..c87141b1f3 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-config-schema", - "version": "0.1.73-next.0", + "version": "0.1.73-next.1", "description": "A Backstage plugin that lets you browse the configuration schema of your app", "backstage": { "role": "frontend-plugin", diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index 9213c182e9..e48d8fe67c 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-devtools-backend +## 0.5.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.4-next.0 + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-devtools-common@0.1.18-next.0 + ## 0.5.10-next.0 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 3a95c1dbf9..a1ca86b9ba 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.5.10-next.0", + "version": "0.5.10-next.1", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools-common/CHANGELOG.md b/plugins/devtools-common/CHANGELOG.md index 55ed84de0f..b4d0c31621 100644 --- a/plugins/devtools-common/CHANGELOG.md +++ b/plugins/devtools-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-devtools-common +## 0.1.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 0.1.17 ### Patch Changes diff --git a/plugins/devtools-common/package.json b/plugins/devtools-common/package.json index a5056edf41..bae134de49 100644 --- a/plugins/devtools-common/package.json +++ b/plugins/devtools-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-common", - "version": "0.1.17", + "version": "0.1.18-next.0", "description": "Common functionalities for the devtools plugin", "backstage": { "role": "common-library", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index 7f5ad6310d..99e894a877 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-devtools +## 0.1.32-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-devtools-common@0.1.18-next.0 + ## 0.1.32-next.0 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index ea69e09e86..367a90867e 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.32-next.0", + "version": "0.1.32-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "devtools", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index baca97880c..58cca92f47 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.4.15 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 359cd768c8..36ea2e4cad 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.4.15", + "version": "0.4.16-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index 91791d0966..99e91bb475 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.2.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.2.24 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 3747c2b097..a6b93f76c3 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.2.24", + "version": "0.2.25-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index 5014807859..25ba6c3ed3 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.2.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.2.24 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index e523b668ca..b653428ae7 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.2.24", + "version": "0.2.25-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-server/CHANGELOG.md b/plugins/events-backend-module-bitbucket-server/CHANGELOG.md index 8c54c534b3..88a40b4114 100644 --- a/plugins/events-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-bitbucket-server +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.1.5 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-server/package.json b/plugins/events-backend-module-bitbucket-server/package.json index dd4429548c..b7365f0d3d 100644 --- a/plugins/events-backend-module-bitbucket-server/package.json +++ b/plugins/events-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-server", - "version": "0.1.5", + "version": "0.1.6-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index 511a31488d..8938f8ff6d 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.2.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.2.24 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 81978168a7..dfd74ab6bd 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.2.24", + "version": "0.2.25-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index 483afda21f..0eef3b4c4c 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend-module-github +## 0.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.4.5-next.0 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index d9cd60cedc..be65aa0744 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.4.5-next.0", + "version": "0.4.5-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index 0df6ece6ab..68f72cdfd1 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.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.3.5 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index 560f4fae76..63a15f990d 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.3.5", + "version": "0.3.6-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-google-pubsub/CHANGELOG.md b/plugins/events-backend-module-google-pubsub/CHANGELOG.md index 6002004dc4..4d296461b0 100644 --- a/plugins/events-backend-module-google-pubsub/CHANGELOG.md +++ b/plugins/events-backend-module-google-pubsub/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-google-pubsub +## 0.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/events-backend-module-google-pubsub/package.json b/plugins/events-backend-module-google-pubsub/package.json index 88f6fe714e..4b52f60661 100644 --- a/plugins/events-backend-module-google-pubsub/package.json +++ b/plugins/events-backend-module-google-pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-google-pubsub", - "version": "0.1.4", + "version": "0.1.5-next.0", "description": "The google-pubsub backend module for the events plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/events-backend-module-kafka/CHANGELOG.md b/plugins/events-backend-module-kafka/CHANGELOG.md index f12109da75..b3b94438cd 100644 --- a/plugins/events-backend-module-kafka/CHANGELOG.md +++ b/plugins/events-backend-module-kafka/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-kafka +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/events-backend-module-kafka/package.json b/plugins/events-backend-module-kafka/package.json index 03a1c6c1dc..d298375b55 100644 --- a/plugins/events-backend-module-kafka/package.json +++ b/plugins/events-backend-module-kafka/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-kafka", - "version": "0.1.3", + "version": "0.1.4-next.0", "description": "The kafka backend module for the events plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index 5a02e4ed51..bb2cced423 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.49-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.1.48 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index a27e4efbe6..9ef009f4cf 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-test-utils", - "version": "0.1.48", + "version": "0.1.49-next.0", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", "backstage": { "role": "node-library", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index 15b1b91730..825199efef 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend +## 0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.5.6 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index a5f530f33c..1094448ac5 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.5.6", + "version": "0.5.7-next.0", "backstage": { "role": "backend-plugin", "pluginId": "events", diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 1b706fdf22..152b45c4f0 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-node +## 0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + ## 0.4.15 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index efd879a47a..3d1cbb949c 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-node", - "version": "0.4.15", + "version": "0.4.16-next.0", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index bd1db36bb8..da8317ef8f 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,12 @@ # @internal/plugin-todo-list-backend +## 1.0.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + ## 1.0.43 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index 6775feb352..eaaa88f919 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.43", + "version": "1.0.44-next.0", "backstage": { "role": "backend-plugin", "pluginId": "todo-list", diff --git a/plugins/example-todo-list-common/CHANGELOG.md b/plugins/example-todo-list-common/CHANGELOG.md index 72a97d5d14..8f7d8cde0f 100644 --- a/plugins/example-todo-list-common/CHANGELOG.md +++ b/plugins/example-todo-list-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @internal/plugin-todo-list-common +## 1.0.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 1.0.26 ### Patch Changes diff --git a/plugins/example-todo-list-common/package.json b/plugins/example-todo-list-common/package.json index 3858b463dc..ea144ba06d 100644 --- a/plugins/example-todo-list-common/package.json +++ b/plugins/example-todo-list-common/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-common", - "version": "1.0.26", + "version": "1.0.27-next.0", "backstage": { "role": "common-library", "pluginId": "todo-list", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 564ed40cb9..67d827c298 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.44-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + ## 1.0.44-next.0 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index c8f75da342..723cab4ed1 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.44-next.0", + "version": "1.0.44-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "todo-list", diff --git a/plugins/gateway-backend/CHANGELOG.md b/plugins/gateway-backend/CHANGELOG.md index eabcf7fe66..933a55625c 100644 --- a/plugins/gateway-backend/CHANGELOG.md +++ b/plugins/gateway-backend/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-gateway-backend +## 1.0.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + ## 1.0.5 ### Patch Changes diff --git a/plugins/gateway-backend/package.json b/plugins/gateway-backend/package.json index 985f5f9380..1503fd8668 100644 --- a/plugins/gateway-backend/package.json +++ b/plugins/gateway-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gateway-backend", - "version": "1.0.5", + "version": "1.0.6-next.0", "backstage": { "role": "backend-plugin", "pluginId": "gateway", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index 37c1ac8b57..c3f58c0cb3 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-home-react +## 0.1.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 0.1.31-next.0 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index dbc036029b..3d2b97c535 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home-react", - "version": "0.1.31-next.0", + "version": "0.1.31-next.1", "description": "A Backstage plugin that contains react components helps you build a home page", "backstage": { "role": "web-library", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 1ce162951b..54dbb31ae0 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-home +## 0.8.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-home-react@0.1.31-next.1 + - @backstage/catalog-client@1.12.0 + ## 0.8.13-next.0 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index 70bba26ce4..52a4fdd395 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.8.13-next.0", + "version": "0.8.13-next.1", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index c67593601c..5221cbf95f 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-kubernetes-backend +## 0.20.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-kubernetes-node@0.3.5-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + - @backstage/catalog-client@1.12.0 + ## 0.20.3-next.0 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 51ea136593..f90c2ff4a9 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.20.3-next.0", + "version": "0.20.3-next.1", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index 4d05386487..527c13c88d 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.30-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-kubernetes-react@0.5.12-next.1 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + ## 0.0.30-next.0 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 8d65670f28..3e0544ced9 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-cluster", - "version": "0.0.30-next.0", + "version": "0.0.30-next.1", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index 0ec9185623..3c9078d364 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-kubernetes-common +## 0.9.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 0.9.7-next.0 ### Patch Changes diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index f78da14fa6..18dc397817 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-common", - "version": "0.9.7-next.0", + "version": "0.9.7-next.1", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", "backstage": { "role": "common-library", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 8870323c75..e6d163150c 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-kubernetes-node +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + ## 0.3.5-next.0 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index cd204be633..9b0297a9c2 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.3.5-next.0", + "version": "0.3.5-next.1", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index 033d51737b..ee05844a0b 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kubernetes-react +## 0.5.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + ## 0.5.12-next.0 ### Patch Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index c417938b40..583bb5ef0d 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-react", - "version": "0.5.12-next.0", + "version": "0.5.12-next.1", "description": "Web library for the kubernetes-react plugin", "backstage": { "role": "web-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 991781331c..62ffb354bf 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-kubernetes +## 0.12.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-kubernetes-react@0.5.12-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-kubernetes-common@0.9.7-next.1 + ## 0.12.12-next.0 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index aaf3c0f65d..1ae796c1c0 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.12-next.0", + "version": "0.12.12-next.1", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", diff --git a/plugins/mcp-actions-backend/CHANGELOG.md b/plugins/mcp-actions-backend/CHANGELOG.md index dbc8326e1a..b00eb4ed8d 100644 --- a/plugins/mcp-actions-backend/CHANGELOG.md +++ b/plugins/mcp-actions-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-mcp-actions-backend +## 0.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.1.4-next.0 ### Patch Changes diff --git a/plugins/mcp-actions-backend/package.json b/plugins/mcp-actions-backend/package.json index cf2b5e1d74..804bb0251a 100644 --- a/plugins/mcp-actions-backend/package.json +++ b/plugins/mcp-actions-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-mcp-actions-backend", - "version": "0.1.4-next.0", + "version": "0.1.4-next.1", "backstage": { "role": "backend-plugin", "pluginId": "mcp-actions", diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md index 1d58fa9727..99f2277dad 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-notifications-node@0.2.20-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.3.13 ### Patch Changes diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index ac8682fe2d..7c1e279c6c 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-email", - "version": "0.3.13", + "version": "0.3.14-next.0", "description": "The email backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend-module-slack/CHANGELOG.md b/plugins/notifications-backend-module-slack/CHANGELOG.md index cc69729a3a..757678ac3e 100644 --- a/plugins/notifications-backend-module-slack/CHANGELOG.md +++ b/plugins/notifications-backend-module-slack/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-notifications-backend-module-slack +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-notifications-node@0.2.20-next.0 + ## 0.1.5 ### Patch Changes diff --git a/plugins/notifications-backend-module-slack/package.json b/plugins/notifications-backend-module-slack/package.json index 4f7e59770e..118e54493b 100644 --- a/plugins/notifications-backend-module-slack/package.json +++ b/plugins/notifications-backend-module-slack/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-slack", - "version": "0.1.5", + "version": "0.1.6-next.0", "description": "The slack backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend/CHANGELOG.md b/plugins/notifications-backend/CHANGELOG.md index 9291e3e550..15e529d14f 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-notifications-backend +## 0.5.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-notifications-node@0.2.20-next.0 + ## 0.5.10 ### Patch Changes diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index 1a2c746409..c12535bd50 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.5.10", + "version": "0.5.11-next.0", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-common/CHANGELOG.md b/plugins/notifications-common/CHANGELOG.md index 77108764b7..8b8d000d02 100644 --- a/plugins/notifications-common/CHANGELOG.md +++ b/plugins/notifications-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-notifications-common +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/notifications-common/package.json b/plugins/notifications-common/package.json index a56cbc06f4..769559fcb4 100644 --- a/plugins/notifications-common/package.json +++ b/plugins/notifications-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-common", - "version": "0.1.0", + "version": "0.1.1-next.0", "description": "Common functionalities for the notifications plugin", "backstage": { "role": "common-library", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index 1db30a23ee..c10c5f7298 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-notifications-node +## 0.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.2.19 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index 2046692085..4029787583 100644 --- a/plugins/notifications-node/package.json +++ b/plugins/notifications-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-node", - "version": "0.2.19", + "version": "0.2.20-next.0", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index 1c1e910e44..6f3ae7c818 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-notifications +## 0.5.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-signals-react@0.0.16-next.0 + ## 0.5.10-next.0 ### Patch Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index 092a26c3ba..eb1a616832 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.5.10-next.0", + "version": "0.5.10-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index dd586ca211..ce7f577228 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-org-react +## 0.1.43-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/catalog-client@1.12.0 + ## 0.1.43-next.0 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 94d8e62962..7bb23cee16 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.43-next.0", + "version": "0.1.43-next.1", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index b9fa9ec4d6..74115ab64e 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-org +## 0.6.45-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + ## 0.6.45-next.0 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 0868489426..b879153b9d 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.45-next.0", + "version": "0.6.45-next.1", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 601c110e9d..a7f4ca35fc 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.2.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + ## 0.2.12 ### 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 b20d4f85a6..ee78f2a838 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", - "version": "0.2.12", + "version": "0.2.13-next.0", "description": "Allow all policy backend module for the permission plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 5bcc2ccff7..4f3d7e5587 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-permission-backend +## 0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + ## 0.7.4 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 434694dc06..2fa47a318d 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.7.4", + "version": "0.7.5-next.0", "backstage": { "role": "backend-plugin", "pluginId": "permission", diff --git a/plugins/permission-common/CHANGELOG.md b/plugins/permission-common/CHANGELOG.md index d3d9d320ca..733f271b9c 100644 --- a/plugins/permission-common/CHANGELOG.md +++ b/plugins/permission-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-permission-common +## 0.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + ## 0.9.1 ### Patch Changes diff --git a/plugins/permission-common/package.json b/plugins/permission-common/package.json index e12c557d4d..d40badc146 100644 --- a/plugins/permission-common/package.json +++ b/plugins/permission-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-common", - "version": "0.9.1", + "version": "0.9.2-next.0", "description": "Isomorphic types and client for Backstage permissions and authorization", "backstage": { "role": "common-library", diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index bcd8dc8be3..1f07bf1fd4 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-permission-node +## 0.10.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 0.10.4 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 38f9d07cd8..5fcc329924 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-node", - "version": "0.10.4", + "version": "0.10.5-next.0", "description": "Common permission and authorization utilities for backend plugins", "backstage": { "role": "node-library", diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index df0bc44dc3..55b06661f9 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-permission-react +## 0.4.37-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 0.4.36 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index 6e15c5fdeb..8a6243c505 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.36", + "version": "0.4.37-next.0", "backstage": { "role": "web-library", "pluginId": "permission", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index fd2683ed4d..31a4986b95 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-proxy-backend +## 0.6.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-proxy-node@0.1.9-next.0 + ## 0.6.6 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 76501017c5..ba6fabdff1 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.6.6", + "version": "0.6.7-next.0", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", "backstage": { "role": "backend-plugin", diff --git a/plugins/proxy-node/CHANGELOG.md b/plugins/proxy-node/CHANGELOG.md index 01cc972c66..f0efb3e344 100644 --- a/plugins/proxy-node/CHANGELOG.md +++ b/plugins/proxy-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-proxy-node +## 0.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + ## 0.1.8 ### Patch Changes diff --git a/plugins/proxy-node/package.json b/plugins/proxy-node/package.json index 79453d4c6c..c877a1f855 100644 --- a/plugins/proxy-node/package.json +++ b/plugins/proxy-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-node", - "version": "0.1.8", + "version": "0.1.9-next.0", "description": "The plugin-proxy-node module for @backstage/plugin-proxy-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index c4c021f726..09ab5c5315 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 837b6e4d8c..d88bfd3644 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-azure", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index 3648ebf879..25d549b80e 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index b2d9b1b92c..62ebc4dd81 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index 3e573f638d..9df81bc074 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 97f8abc1aa..5a94568f31 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-server", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index 535f4bab5c..70c74689d2 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.3.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.1 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.3.15-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index 957dff0afc..c1c4bf4b20 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", - "version": "0.3.15-next.0", + "version": "0.3.15-next.1", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index fb5bbbfaf5..e7679f53a2 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.3.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.3.14-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 e4ef5be916..ffa94b8898 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", - "version": "0.3.14-next.0", + "version": "0.3.14-next.1", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index a9dbde5b6f..fdd71a766f 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.3.16-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 530732acd0..2f293d1e99 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", - "version": "0.3.16-next.0", + "version": "0.3.16-next.1", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index 83e72c1e3a..9fd84eae8a 100644 --- a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-gcp +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 0a4609c457..a01afe161f 100644 --- a/plugins/scaffolder-backend-module-gcp/package.json +++ b/plugins/scaffolder-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gcp", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "The GCP Bucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index 58877701b2..1205bdb94a 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 928c3965f3..04dcd408eb 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gerrit", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index f6309bd70b..7595f6f16e 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index cc6e18fbd2..162a9863f2 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitea", - "version": "0.2.14-next.0", + "version": "0.2.14-next.1", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 1a2f46d3fb..bf4bd23777 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + ## 0.9.1-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 49851f967a..94db5bdf32 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", - "version": "0.9.1-next.0", + "version": "0.9.1-next.1", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 1b371afb91..c1312cc2a6 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.9.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.9.6-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 64eb919a59..2f3a9fad89 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.9.6-next.0", + "version": "0.9.6-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index 5937669786..ec1caf1cfd 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-notifications-node@0.2.20-next.0 + ## 0.1.15-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index 4220fc14ba..25725743c9 100644 --- a/plugins/scaffolder-backend-module-notifications/package.json +++ b/plugins/scaffolder-backend-module-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-notifications", - "version": "0.1.15-next.0", + "version": "0.1.15-next.1", "description": "The notifications backend module for the scaffolder plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index f8cf3b6e3f..cea46b79e2 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.5.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.5.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 589b4cf729..9e4da03db0 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", - "version": "0.5.14-next.0", + "version": "0.5.14-next.1", "description": "A module for the scaffolder backend that lets you template projects using Rails", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index fca74f3951..53d878fc2c 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.2.14-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index f07e77413f..32bd5d1fc1 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.2.14-next.0", + "version": "0.2.14-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 449503e901..c0846372a0 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.4.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.3.4-next.1 + ## 0.4.15-next.0 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 295b154103..20390d9fd1 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.4.15-next.0", + "version": "0.4.15-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 1e3fbe7bf1..4cff936ddc 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-scaffolder-backend +## 3.0.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6-next.1 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + ## 3.0.0-next.0 ### Major Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 99faf1bf5b..a310b26f01 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "3.0.0-next.0", + "version": "3.0.0-next.1", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index 67e4b5058d..4fd26a92bc 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-scaffolder-common +## 1.7.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 1.7.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 3a4eef3e03..097e64ab13 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-common", - "version": "1.7.2-next.0", + "version": "1.7.2-next.1", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", "backstage": { "role": "common-library", diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index 2520b628e7..dab7e41108 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/backend-test-utils@1.9.1-next.1 + - @backstage/plugin-scaffolder-node@0.12.0-next.1 + ## 0.3.4-next.0 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 3cbae20d33..fd2767b0fc 100644 --- a/plugins/scaffolder-node-test-utils/package.json +++ b/plugins/scaffolder-node-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node-test-utils", - "version": "0.3.4-next.0", + "version": "0.3.4-next.1", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 750fd7ce98..7a46a277e2 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-node +## 0.12.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + ## 0.12.0-next.0 ### Minor Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 88a7ebf8ce..1852e6909a 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.12.0-next.0", + "version": "0.12.0-next.1", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index cda6c9b6a6..ad51eccb64 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-react +## 1.19.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/catalog-client@1.12.0 + ## 1.19.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index ac452d2205..1c61387895 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.19.2-next.0", + "version": "1.19.2-next.1", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index d4b6a5bd15..60ef454a7c 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-scaffolder +## 1.34.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-permission-react@0.4.37-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-scaffolder-react@1.19.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-scaffolder-common@1.7.2-next.1 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 1.34.2-next.0 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index fe2c88e3ed..ef05bbd203 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.34.2-next.0", + "version": "1.34.2-next.1", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index 5267a5f25a..4577e13e7b 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-search-backend-module-catalog +## 0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.3.8 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 8db4d6fb96..65ad71aa61 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-catalog", - "version": "0.3.8", + "version": "0.3.9-next.0", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index f9aaad3e06..a2fcb531f9 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.7.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 1.7.6 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 7e87d35fc6..1a876032fd 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", - "version": "1.7.6", + "version": "1.7.7-next.0", "description": "A module for the search backend that implements search using ElasticSearch", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 644c75f6f7..6cd3b52e68 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-explore +## 0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 0.3.7 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 08fe70b093..576c602099 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-explore", - "version": "0.3.7", + "version": "0.3.8-next.0", "description": "A module for the search backend that exports explore modules", "backstage": { "moved": "@backstage-community/plugin-search-backend-module-explore", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index 2b148f8542..18ec68b9bf 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.49-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 0.5.49-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 312783ddc3..0eb6dfd93f 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-pg", - "version": "0.5.49-next.0", + "version": "0.5.49-next.1", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index b5b6802e05..f9509d5fef 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.3.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 0.3.13 ### 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 3885882caa..3bf8b7dfaf 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", - "version": "0.3.13", + "version": "0.3.14-next.0", "description": "A module for the search backend that exports stack overflow modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index a23f555d67..fa9fc4c851 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.4.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/plugin-techdocs-node@1.13.8-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + ## 0.4.7-next.0 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 4a4a13d3e6..f5938b6059 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.4.7-next.0", + "version": "0.4.7-next.1", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index f61d4f4318..28856ff5b8 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-node +## 1.3.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 1.3.15 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index e862163a08..c84c9b5d98 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "1.3.15", + "version": "1.3.16-next.0", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", "backstage": { "role": "node-library", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 9bef273db2..2445f7707c 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-search-backend +## 2.0.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-permission-node@0.10.5-next.0 + - @backstage/plugin-search-backend-node@1.3.16-next.0 + - @backstage/backend-openapi-utils@0.6.2-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 2.0.7-next.0 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 4e61e05ce6..99f80d5bfa 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "2.0.7-next.0", + "version": "2.0.7-next.1", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", diff --git a/plugins/search-common/CHANGELOG.md b/plugins/search-common/CHANGELOG.md index ef5deee025..0f7aa1a167 100644 --- a/plugins/search-common/CHANGELOG.md +++ b/plugins/search-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-search-common +## 1.2.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2-next.0 + ## 1.2.19 ### Patch Changes diff --git a/plugins/search-common/package.json b/plugins/search-common/package.json index 582e6b3434..dbdbccf225 100644 --- a/plugins/search-common/package.json +++ b/plugins/search-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-common", - "version": "1.2.19", + "version": "1.2.20-next.0", "description": "Common functionalities for Search, to be shared between various search-enabled plugins", "backstage": { "role": "common-library", diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index 6a23e79db4..06ef62a738 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-react +## 1.9.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 1.9.5-next.0 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 7bd07a786e..320eef5c5d 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.9.5-next.0", + "version": "1.9.5-next.1", "backstage": { "role": "web-library", "pluginId": "search", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index d3119d0c9a..daf30ccac0 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search +## 1.4.31-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 1.4.31-next.0 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 48f462897d..1c4b422c08 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.31-next.0", + "version": "1.4.31-next.1", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin", diff --git a/plugins/signals-backend/CHANGELOG.md b/plugins/signals-backend/CHANGELOG.md index 86eb851f7a..3eebcf0ddf 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-signals-backend +## 0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.3.8 ### Patch Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index b5de8611df..f9834ab228 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-backend", - "version": "0.3.8", + "version": "0.3.9-next.0", "backstage": { "role": "backend-plugin", "pluginId": "signals", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index 0bdbb32c87..0951df4c42 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-signals-node +## 0.1.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-events-node@0.4.16-next.0 + ## 0.1.24 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index 907d51c588..3f592954e5 100644 --- a/plugins/signals-node/package.json +++ b/plugins/signals-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-node", - "version": "0.1.24", + "version": "0.1.25-next.0", "description": "Node.js library for the signals plugin", "backstage": { "role": "node-library", diff --git a/plugins/signals-react/CHANGELOG.md b/plugins/signals-react/CHANGELOG.md index d5cfb09e39..1f513931bb 100644 --- a/plugins/signals-react/CHANGELOG.md +++ b/plugins/signals-react/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-signals-react +## 0.0.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.1-next.0 + ## 0.0.15 ### Patch Changes diff --git a/plugins/signals-react/package.json b/plugins/signals-react/package.json index cc92bb120c..7916ae57ce 100644 --- a/plugins/signals-react/package.json +++ b/plugins/signals-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-react", - "version": "0.0.15", + "version": "0.0.16-next.0", "description": "Web library for the signals plugin", "backstage": { "role": "web-library", diff --git a/plugins/signals/CHANGELOG.md b/plugins/signals/CHANGELOG.md index 4d878217e4..fc017d9bce 100644 --- a/plugins/signals/CHANGELOG.md +++ b/plugins/signals/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-signals +## 0.0.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-signals-react@0.0.16-next.0 + ## 0.0.24-next.0 ### Patch Changes diff --git a/plugins/signals/package.json b/plugins/signals/package.json index d4ecc528c5..3e2a2fc337 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals", - "version": "0.0.24-next.0", + "version": "0.0.24-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "signals", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index 32d6473083..7da2594325 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.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/test-utils@1.7.12-next.0 + - @backstage/plugin-techdocs@1.15.1-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/plugin-catalog@1.31.4-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + ## 1.1.1-next.0 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 0c24577409..8c71a8622a 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.1.1-next.0", + "version": "1.1.1-next.1", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 970677836e..fba425b578 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-techdocs-backend +## 2.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/integration@1.18.1-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-permission-common@0.9.2-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.7-next.1 + - @backstage/plugin-techdocs-node@1.13.8-next.1 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-catalog-common@1.1.6-next.0 + - @backstage/catalog-client@1.12.0 + ## 2.1.1-next.0 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 7bc5e52385..72bbb5a834 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "2.1.1-next.0", + "version": "2.1.1-next.1", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 841e350d23..b44f48355d 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 1.1.29-next.0 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 92e40a664c..dd7acb2fa5 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", - "version": "1.1.29-next.0", + "version": "1.1.29-next.1", "description": "Plugin module for contributed TechDocs Addons", "backstage": { "role": "frontend-plugin-module", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index b3bcc36620..8d60815de0 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs-node +## 1.13.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-search-common@1.2.20-next.0 + ## 1.13.8-next.0 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 5d36d96287..cdd1de2061 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.13.8-next.0", + "version": "1.13.8-next.1", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library", diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 4e11d1c6f9..196132fadd 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-techdocs-react +## 1.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.1 + ## 1.3.4-next.0 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 6020c8fb78..6ae10aead2 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-react", - "version": "1.3.4-next.0", + "version": "1.3.4-next.1", "description": "Shared frontend utilities for TechDocs and Addons", "backstage": { "role": "web-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 67a2e51ad8..9fe41a7e39 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-techdocs +## 1.15.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-auth-react@0.1.20-next.1 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + ## 1.15.1-next.0 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index ffc41132fe..97480471c6 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.15.1-next.0", + "version": "1.15.1-next.1", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 7b9763d4da..b2cebb44be 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-user-settings-backend +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-auth-node@0.6.8-next.0 + - @backstage/plugin-signals-node@0.1.25-next.0 + ## 0.3.7-next.0 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index 30c7355ba1..30e7c83b0f 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings-backend", - "version": "0.3.7-next.0", + "version": "0.3.7-next.1", "description": "The Backstage backend plugin to manage user settings", "backstage": { "role": "backend-plugin", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 2c3a347cd7..a5e5104c71 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-user-settings +## 0.8.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.19.1-next.0 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-signals-react@0.0.16-next.0 + ## 0.8.27-next.0 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 8b0c297f70..ca2b5f2e9e 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.8.27-next.0", + "version": "0.8.27-next.1", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", From 8ac36e8737587a95c29a6629f93c99b8b4ef16e3 Mon Sep 17 00:00:00 2001 From: Gustavo RPS <516827+gustavorps@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:27:31 -0300 Subject: [PATCH 105/211] Add PromiseRouter import to http-router.md Signed-off-by: Gustavo RPS <516827+gustavorps@users.noreply.github.com> --- docs/backend-system/core-services/http-router.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/backend-system/core-services/http-router.md b/docs/backend-system/core-services/http-router.md index 5e065152a0..23304f663f 100644 --- a/docs/backend-system/core-services/http-router.md +++ b/docs/backend-system/core-services/http-router.md @@ -134,6 +134,7 @@ import { createAuthIntegrationRouter, createRateLimitMiddleware, } from '@backstage/backend-defaults/httpRouter'; +import PromiseRouter from 'express-promise-router'; import { createServiceFactory } from '@backstage/backend-plugin-api'; const backend = createBackend(); From 02f152703d09970af742c23e72421bbb4e20e6e2 Mon Sep 17 00:00:00 2001 From: Gustavo RPS <516827+gustavorps@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:32:07 -0300 Subject: [PATCH 106/211] Add express Handler import to http-router.md Signed-off-by: Gustavo RPS <516827+gustavorps@users.noreply.github.com> --- docs/backend-system/core-services/http-router.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/backend-system/core-services/http-router.md b/docs/backend-system/core-services/http-router.md index 23304f663f..4f1e2284ad 100644 --- a/docs/backend-system/core-services/http-router.md +++ b/docs/backend-system/core-services/http-router.md @@ -135,6 +135,7 @@ import { createRateLimitMiddleware, } from '@backstage/backend-defaults/httpRouter'; import PromiseRouter from 'express-promise-router'; +import { Handler } from 'express'; import { createServiceFactory } from '@backstage/backend-plugin-api'; const backend = createBackend(); From 3dc8604ace962a93f3795547ab6a555205289c8d Mon Sep 17 00:00:00 2001 From: Gustavo RPS <516827+gustavorps@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:36:10 -0300 Subject: [PATCH 107/211] Add coreServices and HttpRouterServiceAuthPolicy imports Signed-off-by: Gustavo RPS <516827+gustavorps@users.noreply.github.com> --- docs/backend-system/core-services/http-router.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/backend-system/core-services/http-router.md b/docs/backend-system/core-services/http-router.md index 4f1e2284ad..583cd0212d 100644 --- a/docs/backend-system/core-services/http-router.md +++ b/docs/backend-system/core-services/http-router.md @@ -136,7 +136,11 @@ import { } from '@backstage/backend-defaults/httpRouter'; import PromiseRouter from 'express-promise-router'; import { Handler } from 'express'; -import { createServiceFactory } from '@backstage/backend-plugin-api'; +import { + createServiceFactory, + coreServices, + HttpRouterServiceAuthPolicy +} from '@backstage/backend-plugin-api'; const backend = createBackend(); From b58a488eff432b5e8e275dd130551bc86cae4d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sofia=20Sj=C3=B6blad?= Date: Thu, 25 Sep 2025 10:20:05 +0200 Subject: [PATCH 108/211] fix(bui): fix button link internal routing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sofia Sjöblad --- .../src/components/ButtonLink/ButtonLink.tsx | 57 +++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/packages/ui/src/components/ButtonLink/ButtonLink.tsx b/packages/ui/src/components/ButtonLink/ButtonLink.tsx index 153643a530..b5b5ab208d 100644 --- a/packages/ui/src/components/ButtonLink/ButtonLink.tsx +++ b/packages/ui/src/components/ButtonLink/ButtonLink.tsx @@ -16,13 +16,16 @@ import clsx from 'clsx'; import { forwardRef, Ref } from 'react'; -import { Link as RALink } from 'react-aria-components'; +import { Link as RALink, RouterProvider } from 'react-aria-components'; +import { useNavigate, useHref } from 'react-router-dom'; import type { ButtonLinkProps } from './types'; import { useStyles } from '../../hooks/useStyles'; +import { isExternalLink } from '../../utils/isExternalLink'; /** @public */ export const ButtonLink = forwardRef( (props: ButtonLinkProps, ref: Ref) => { + const navigate = useNavigate(); const { size = 'small', variant = 'primary', @@ -30,6 +33,7 @@ export const ButtonLink = forwardRef( iconEnd, children, className, + href, ...rest } = props; @@ -40,17 +44,48 @@ export const ButtonLink = forwardRef( const { classNames: classNamesButtonLink } = useStyles('ButtonLink'); + const isExternal = isExternalLink(href); + + // If it's an external link, render RALink without RouterProvider + if (isExternal) { + return ( + + {iconStart} + {children} + {iconEnd} + + ); + } + + // For internal links, use RouterProvider return ( - - {iconStart} - {children} - {iconEnd} - + + + {iconStart} + {children} + {iconEnd} + + ); }, ); From 61b6281e5c17445414d8a7d2ae258cf7779d2497 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Thu, 25 Sep 2025 10:12:40 +0100 Subject: [PATCH 109/211] simplify changeset & update hasFile to accept number or string Signed-off-by: Claire Peng --- .changeset/twelve-oranges-grin.md | 11 +---------- .../catalog-backend-module-gitlab/src/lib/client.ts | 2 +- .../src/providers/GitlabDiscoveryEntityProvider.ts | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/.changeset/twelve-oranges-grin.md b/.changeset/twelve-oranges-grin.md index b508a59b0b..174dbdcd66 100644 --- a/.changeset/twelve-oranges-grin.md +++ b/.changeset/twelve-oranges-grin.md @@ -6,15 +6,6 @@ Update GitlabDiscoveryEntityProvider to use `project.id` rather than `project.pa Solves [#30147](https://github.com/backstage/backstage/issues/30147): -> Use of project.id avoids edgecases caused by path encoding or project structure changes +> Use of project.id avoids edge cases caused by path encoding or project structure changes > [...] > It also simplifies reasoning about the GitLab API usage, since IDs are immutable, while paths are not. - -```diff -const hasFile = await client.hasFile( -- project.path_with_namespace, -+ project.id.toString(), - project_branch, - this.config.catalogFile, -); -``` diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index 46ff54846c..5c373dcf4a 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -349,7 +349,7 @@ export class GitLabClient { * @param filePath - The path to the file */ async hasFile( - projectIdentifier: string, + projectIdentifier: string | number, branch: string, filePath: string, ): Promise { diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts index e52c231d46..b3371dcc9e 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabDiscoveryEntityProvider.ts @@ -590,7 +590,7 @@ export class GitlabDiscoveryEntityProvider implements EntityProvider { this.config.fallbackBranch; const hasFile = await client.hasFile( - project.id.toString(), + project.id, project_branch, this.config.catalogFile, ); From 077dee1902fb0c967bbf728497011db5f29c85af Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 12 Sep 2025 18:53:05 +0200 Subject: [PATCH 110/211] plugins: scaffold bui-themer plugin Signed-off-by: Patrik Oldsberg --- packages/app/package.json | 1 + packages/app/src/App.tsx | 2 + plugins/bui-themer/.eslintrc.js | 1 + plugins/bui-themer/README.md | 13 + plugins/bui-themer/catalog-info.yaml | 9 + plugins/bui-themer/dev/index.tsx | 26 ++ plugins/bui-themer/package.json | 53 +++ .../ExampleComponent.test.tsx | 38 +++ .../ExampleComponent/ExampleComponent.tsx | 52 +++ .../src/components/ExampleComponent/index.ts | 16 + .../ExampleFetchComponent.test.tsx | 34 ++ .../ExampleFetchComponent.tsx | 322 ++++++++++++++++++ .../components/ExampleFetchComponent/index.ts | 16 + plugins/bui-themer/src/index.ts | 16 + plugins/bui-themer/src/plugin.test.ts | 22 ++ plugins/bui-themer/src/plugin.ts | 37 ++ plugins/bui-themer/src/routes.ts | 20 ++ plugins/bui-themer/src/setupTests.ts | 16 + yarn.lock | 131 +++++-- 19 files changed, 805 insertions(+), 20 deletions(-) create mode 100644 plugins/bui-themer/.eslintrc.js create mode 100644 plugins/bui-themer/README.md create mode 100644 plugins/bui-themer/catalog-info.yaml create mode 100644 plugins/bui-themer/dev/index.tsx create mode 100644 plugins/bui-themer/package.json create mode 100644 plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx create mode 100644 plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx create mode 100644 plugins/bui-themer/src/components/ExampleComponent/index.ts create mode 100644 plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx create mode 100644 plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx create mode 100644 plugins/bui-themer/src/components/ExampleFetchComponent/index.ts create mode 100644 plugins/bui-themer/src/index.ts create mode 100644 plugins/bui-themer/src/plugin.test.ts create mode 100644 plugins/bui-themer/src/plugin.ts create mode 100644 plugins/bui-themer/src/routes.ts create mode 100644 plugins/bui-themer/src/setupTests.ts diff --git a/packages/app/package.json b/packages/app/package.json index 56b5178692..56e619e65a 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -46,6 +46,7 @@ "@backstage/integration-react": "workspace:^", "@backstage/plugin-api-docs": "workspace:^", "@backstage/plugin-auth-react": "workspace:^", + "@backstage/plugin-bui-themer": "workspace:^", "@backstage/plugin-catalog": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-graph": "workspace:^", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index ad525420a7..bd64dc504f 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -73,6 +73,7 @@ import { } from '@backstage/plugin-notifications'; import { CustomizableHomePage } from './components/home/CustomizableHomePage'; import { HomePage } from './components/home/HomePage'; +import { BuiThemerPage } from '@backstage/plugin-bui-themer'; const app = createApp({ apis, @@ -208,6 +209,7 @@ const routes = ( {customDevToolsPage} } /> + } /> ); diff --git a/plugins/bui-themer/.eslintrc.js b/plugins/bui-themer/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/bui-themer/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/bui-themer/README.md b/plugins/bui-themer/README.md new file mode 100644 index 0000000000..f73dd7875e --- /dev/null +++ b/plugins/bui-themer/README.md @@ -0,0 +1,13 @@ +# bui-themer + +Welcome to the bui-themer 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 [/bui-themer](http://localhost:3000/bui-themer). + +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/bui-themer/catalog-info.yaml b/plugins/bui-themer/catalog-info.yaml new file mode 100644 index 0000000000..15e907a0ac --- /dev/null +++ b/plugins/bui-themer/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-bui-themer + title: '@backstage/plugin-bui-themer' +spec: + lifecycle: experimental + type: backstage-frontend-plugin + owner: maintainers diff --git a/plugins/bui-themer/dev/index.tsx b/plugins/bui-themer/dev/index.tsx new file mode 100644 index 0000000000..ce3986f7da --- /dev/null +++ b/plugins/bui-themer/dev/index.tsx @@ -0,0 +1,26 @@ +/* + * Copyright 2025 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 { createDevApp } from '@backstage/dev-utils'; +import { buiThemerPlugin, BuiThemerPage } from '../src/plugin'; + +createDevApp() + .registerPlugin(buiThemerPlugin) + .addPage({ + element: , + title: 'Root Page', + path: '/bui-themer', + }) + .render(); diff --git a/plugins/bui-themer/package.json b/plugins/bui-themer/package.json new file mode 100644 index 0000000000..c0d49b0879 --- /dev/null +++ b/plugins/bui-themer/package.json @@ -0,0 +1,53 @@ +{ + "name": "@backstage/plugin-bui-themer", + "version": "0.1.0", + "license": "Apache-2.0", + "private": true, + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin", + "pluginId": "bui-themer" + }, + "sideEffects": false, + "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/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.61", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^14.0.0", + "@testing-library/user-event": "^14.0.0", + "msw": "^1.0.0", + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx new file mode 100644 index 0000000000..f5f7f185c5 --- /dev/null +++ b/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2025 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 { ExampleComponent } from './ExampleComponent'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { screen } from '@testing-library/react'; +import { registerMswTestHooks, renderInTestApp } from '@backstage/test-utils'; + +describe('ExampleComponent', () => { + const server = setupServer(); + // Enable sane handlers for network requests + registerMswTestHooks(server); + + // setup mock response + beforeEach(() => { + server.use( + rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), + ); + }); + + it('should render', async () => { + await renderInTestApp(); + expect(screen.getByText('Welcome to bui-themer!')).toBeInTheDocument(); + }); +}); diff --git a/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx new file mode 100644 index 0000000000..3a04bd7562 --- /dev/null +++ b/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2025 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, Grid } from '@material-ui/core'; +import { + InfoCard, + Header, + Page, + Content, + ContentHeader, + HeaderLabel, + SupportButton, +} from '@backstage/core-components'; +import { ExampleFetchComponent } from '../ExampleFetchComponent'; + +export const ExampleComponent = () => ( + +
+ + +
+ + + A description of your plugin goes here. + + + + + + All content should be wrapped in a card like this. + + + + + + + + +
+); diff --git a/plugins/bui-themer/src/components/ExampleComponent/index.ts b/plugins/bui-themer/src/components/ExampleComponent/index.ts new file mode 100644 index 0000000000..66b3fc6c97 --- /dev/null +++ b/plugins/bui-themer/src/components/ExampleComponent/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 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 { ExampleComponent } from './ExampleComponent'; diff --git a/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx new file mode 100644 index 0000000000..a3085e3dcb --- /dev/null +++ b/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright 2025 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 { renderInTestApp } from '@backstage/test-utils'; +import { ExampleFetchComponent } from './ExampleFetchComponent'; + +describe('ExampleFetchComponent', () => { + it('renders the user table', async () => { + const { getAllByText, getByAltText, getByText, findByRole } = + await renderInTestApp(); + + // Wait for the table to render + const table = await findByRole('table'); + const nationality = getAllByText('GB'); + // Assert that the table contains the expected user data + expect(table).toBeInTheDocument(); + expect(getByAltText('Carolyn')).toBeInTheDocument(); + expect(getByText('Carolyn Moore')).toBeInTheDocument(); + expect(getByText('carolyn.moore@example.com')).toBeInTheDocument(); + expect(nationality[0]).toBeInTheDocument(); + }); +}); diff --git a/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx new file mode 100644 index 0000000000..6c9e77094d --- /dev/null +++ b/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx @@ -0,0 +1,322 @@ +/* + * Copyright 2025 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 { makeStyles } from '@material-ui/core/styles'; +import { + Table, + TableColumn, + Progress, + ResponseErrorPanel, +} from '@backstage/core-components'; +import useAsync from 'react-use/lib/useAsync'; + +export const exampleUsers = { + results: [ + { + gender: 'female', + name: { + title: 'Miss', + first: 'Carolyn', + last: 'Moore', + }, + email: 'carolyn.moore@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Carolyn', + nat: 'GB', + }, + { + gender: 'female', + name: { + title: 'Ms', + first: 'Esma', + last: 'Berberoğlu', + }, + email: 'esma.berberoglu@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Esma', + nat: 'TR', + }, + { + gender: 'female', + name: { + title: 'Ms', + first: 'Isabella', + last: 'Rhodes', + }, + email: 'isabella.rhodes@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella', + nat: 'GB', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Derrick', + last: 'Carter', + }, + email: 'derrick.carter@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Derrick', + nat: 'IE', + }, + { + gender: 'female', + name: { + title: 'Miss', + first: 'Mattie', + last: 'Lambert', + }, + email: 'mattie.lambert@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mattie', + nat: 'AU', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Mijat', + last: 'Rakić', + }, + email: 'mijat.rakic@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mijat', + nat: 'RS', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Javier', + last: 'Reid', + }, + email: 'javier.reid@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Javier', + nat: 'US', + }, + { + gender: 'female', + name: { + title: 'Ms', + first: 'Isabella', + last: 'Li', + }, + email: 'isabella.li@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella', + nat: 'CA', + }, + { + gender: 'female', + name: { + title: 'Mrs', + first: 'Stephanie', + last: 'Garrett', + }, + email: 'stephanie.garrett@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Stephanie', + nat: 'AU', + }, + { + gender: 'female', + name: { + title: 'Ms', + first: 'Antonia', + last: 'Núñez', + }, + email: 'antonia.nunez@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Antonia', + nat: 'ES', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Donald', + last: 'Young', + }, + email: 'donald.young@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Donald', + nat: 'US', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Iegor', + last: 'Holodovskiy', + }, + email: 'iegor.holodovskiy@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Iegor', + nat: 'UA', + }, + { + gender: 'female', + name: { + title: 'Madame', + first: 'Jessica', + last: 'David', + }, + email: 'jessica.david@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jessica', + nat: 'CH', + }, + { + gender: 'female', + name: { + title: 'Ms', + first: 'Eve', + last: 'Martinez', + }, + email: 'eve.martinez@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Eve', + nat: 'FR', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Caleb', + last: 'Silva', + }, + email: 'caleb.silva@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Caleb', + nat: 'US', + }, + { + gender: 'female', + name: { + title: 'Miss', + first: 'Marcia', + last: 'Jenkins', + }, + email: 'marcia.jenkins@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Marcia', + nat: 'US', + }, + { + gender: 'female', + name: { + title: 'Mrs', + first: 'Mackenzie', + last: 'Jones', + }, + email: 'mackenzie.jones@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mackenzie', + nat: 'NZ', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Jeremiah', + last: 'Gutierrez', + }, + email: 'jeremiah.gutierrez@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jeremiah', + nat: 'AU', + }, + { + gender: 'female', + name: { + title: 'Ms', + first: 'Luciara', + last: 'Souza', + }, + email: 'luciara.souza@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Luciara', + nat: 'BR', + }, + { + gender: 'male', + name: { + title: 'Mr', + first: 'Valgi', + last: 'da Cunha', + }, + email: 'valgi.dacunha@example.com', + picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Valgi', + nat: 'BR', + }, + ], +}; + +const useStyles = makeStyles({ + avatar: { + height: 32, + width: 32, + borderRadius: '50%', + }, +}); + +type User = { + gender: string; // "male" + name: { + title: string; // "Mr", + first: string; // "Duane", + last: string; // "Reed" + }; + email: string; // "duane.reed@example.com" + picture: string; // "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane" + nat: string; // "AU" +}; + +type DenseTableProps = { + users: User[]; +}; + +export const DenseTable = ({ users }: DenseTableProps) => { + const classes = useStyles(); + + const columns: TableColumn[] = [ + { title: 'Avatar', field: 'avatar' }, + { title: 'Name', field: 'name' }, + { title: 'Email', field: 'email' }, + { title: 'Nationality', field: 'nationality' }, + ]; + + const data = users.map(user => { + return { + avatar: ( + {user.name.first} + ), + name: `${user.name.first} ${user.name.last}`, + email: user.email, + nationality: user.nat, + }; + }); + + return ( + + ); +}; + +export const ExampleFetchComponent = () => { + const { value, loading, error } = useAsync(async (): Promise => { + // Would use fetch in a real world example + return exampleUsers.results; + }, []); + + if (loading) { + return ; + } else if (error) { + return ; + } + + return ; +}; diff --git a/plugins/bui-themer/src/components/ExampleFetchComponent/index.ts b/plugins/bui-themer/src/components/ExampleFetchComponent/index.ts new file mode 100644 index 0000000000..b0be0a2a71 --- /dev/null +++ b/plugins/bui-themer/src/components/ExampleFetchComponent/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 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 { ExampleFetchComponent } from './ExampleFetchComponent'; diff --git a/plugins/bui-themer/src/index.ts b/plugins/bui-themer/src/index.ts new file mode 100644 index 0000000000..26bc35ebb1 --- /dev/null +++ b/plugins/bui-themer/src/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 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 { buiThemerPlugin, BuiThemerPage } from './plugin'; diff --git a/plugins/bui-themer/src/plugin.test.ts b/plugins/bui-themer/src/plugin.test.ts new file mode 100644 index 0000000000..b19adaf007 --- /dev/null +++ b/plugins/bui-themer/src/plugin.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright 2025 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 { buiThemerPlugin } from './plugin'; + +describe('bui-themer', () => { + it('should export plugin', () => { + expect(buiThemerPlugin).toBeDefined(); + }); +}); diff --git a/plugins/bui-themer/src/plugin.ts b/plugins/bui-themer/src/plugin.ts new file mode 100644 index 0000000000..d87d035b12 --- /dev/null +++ b/plugins/bui-themer/src/plugin.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2025 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, + createRoutableExtension, +} from '@backstage/core-plugin-api'; + +import { rootRouteRef } from './routes'; + +export const buiThemerPlugin = createPlugin({ + id: 'bui-themer', + routes: { + root: rootRouteRef, + }, +}); + +export const BuiThemerPage = buiThemerPlugin.provide( + createRoutableExtension({ + name: 'BuiThemerPage', + component: () => + import('./components/ExampleComponent').then(m => m.ExampleComponent), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/bui-themer/src/routes.ts b/plugins/bui-themer/src/routes.ts new file mode 100644 index 0000000000..efd79e62be --- /dev/null +++ b/plugins/bui-themer/src/routes.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2025 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 { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'bui-themer', +}); diff --git a/plugins/bui-themer/src/setupTests.ts b/plugins/bui-themer/src/setupTests.ts new file mode 100644 index 0000000000..b57590b525 --- /dev/null +++ b/plugins/bui-themer/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 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 '@testing-library/jest-dom'; diff --git a/yarn.lock b/yarn.lock index c1a352407c..cb9a75fa61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4323,6 +4323,31 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-bui-themer@workspace:^, @backstage/plugin-bui-themer@workspace:plugins/bui-themer": + version: 0.0.0-use.local + resolution: "@backstage/plugin-bui-themer@workspace:plugins/bui-themer" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-app-api": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@material-ui/core": "npm:^4.9.13" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:^4.0.0-alpha.61" + "@testing-library/jest-dom": "npm:^6.0.0" + "@testing-library/react": "npm:^14.0.0" + "@testing-library/user-event": "npm:^14.0.0" + msw: "npm:^1.0.0" + react: "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + react-use: "npm:^17.2.4" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-catalog-backend-module-aws@workspace:plugins/catalog-backend-module-aws": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-backend-module-aws@workspace:plugins/catalog-backend-module-aws" @@ -19238,6 +19263,22 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^9.0.0": + version: 9.3.4 + resolution: "@testing-library/dom@npm:9.3.4" + dependencies: + "@babel/code-frame": "npm:^7.10.4" + "@babel/runtime": "npm:^7.12.5" + "@types/aria-query": "npm:^5.0.1" + aria-query: "npm:5.1.3" + chalk: "npm:^4.1.0" + dom-accessibility-api: "npm:^0.5.9" + lz-string: "npm:^1.5.0" + pretty-format: "npm:^27.0.2" + checksum: 10/510da752ea76f4a10a0a4e3a77917b0302cf03effe576cd3534cab7e796533ee2b0e9fb6fb11b911a1ebd7c70a0bb6f235bf4f816c9b82b95b8fe0cddfd10975 + languageName: node + linkType: hard + "@testing-library/jest-dom@npm:^6.0.0, @testing-library/jest-dom@npm:^6.6.3": version: 6.8.0 resolution: "@testing-library/jest-dom@npm:6.8.0" @@ -19274,6 +19315,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^14.0.0": + version: 14.3.1 + resolution: "@testing-library/react@npm:14.3.1" + dependencies: + "@babel/runtime": "npm:^7.12.5" + "@testing-library/dom": "npm:^9.0.0" + "@types/react-dom": "npm:^18.0.0" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10/83359dcdf9eaf067839f34604e1a181cbc14fc09f3a07672403700fcc6a900c4b8054ad1114fc24b4b9f89d84e2a09e1b7c9afce2306b1d4b4c9e30eb1cb12de + languageName: node + linkType: hard + "@testing-library/react@npm:^16.0.0": version: 16.3.0 resolution: "@testing-library/react@npm:16.3.0" @@ -23624,6 +23679,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:5.1.3": + version: 5.1.3 + resolution: "aria-query@npm:5.1.3" + dependencies: + deep-equal: "npm:^2.0.5" + checksum: 10/e5da608a7c4954bfece2d879342b6c218b6b207e2d9e5af270b5e38ef8418f02d122afdc948b68e32649b849a38377785252059090d66fa8081da95d1609c0d2 + languageName: node + linkType: hard + "aria-query@npm:5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" @@ -23640,7 +23704,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" dependencies: @@ -27393,6 +27457,32 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^2.0.5": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.0" + call-bind: "npm:^1.0.5" + es-get-iterator: "npm:^1.1.3" + get-intrinsic: "npm:^1.2.2" + is-arguments: "npm:^1.1.1" + is-array-buffer: "npm:^3.0.2" + is-date-object: "npm:^1.0.5" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.2" + isarray: "npm:^2.0.5" + object-is: "npm:^1.1.5" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.4" + regexp.prototype.flags: "npm:^1.5.1" + side-channel: "npm:^1.0.4" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.1" + which-typed-array: "npm:^1.1.13" + checksum: 10/1ce49d0b71d0f14d8ef991a742665eccd488dfc9b3cada069d4d7a86291e591c92d2589c832811dea182b4015736b210acaaebce6184be356c1060d176f5a05f + languageName: node + linkType: hard + "deep-equal@npm:~1.0.1": version: 1.0.1 resolution: "deep-equal@npm:1.0.1" @@ -28611,7 +28701,7 @@ __metadata: languageName: node linkType: hard -"es-get-iterator@npm:^1.0.2": +"es-get-iterator@npm:^1.0.2, es-get-iterator@npm:^1.1.3": version: 1.1.3 resolution: "es-get-iterator@npm:1.1.3" dependencies: @@ -29663,6 +29753,7 @@ __metadata: "@backstage/integration-react": "workspace:^" "@backstage/plugin-api-docs": "workspace:^" "@backstage/plugin-auth-react": "workspace:^" + "@backstage/plugin-bui-themer": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-graph": "workspace:^" @@ -31215,7 +31306,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": version: 1.3.0 resolution: "get-intrinsic@npm:1.3.0" dependencies: @@ -33148,7 +33239,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": version: 3.0.5 resolution: "is-array-buffer@npm:3.0.5" dependencies: @@ -33621,7 +33712,7 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.2.1": +"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1": version: 1.2.1 resolution: "is-regex@npm:1.2.1" dependencies: @@ -33670,7 +33761,7 @@ __metadata: languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.4": +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4": version: 1.0.4 resolution: "is-shared-array-buffer@npm:1.0.4" dependencies: @@ -43096,6 +43187,15 @@ __metadata: languageName: node linkType: hard +"react@npm:^16.13.1 || ^17.0.0 || ^18.0.0, react@npm:^18.0.2": + version: 18.3.1 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10/261137d3f3993eaa2368a83110466fc0e558bc2c7f7ae7ca52d94f03aac945f45146bd85e5f481044db1758a1dbb57879e2fcdd33924e2dde1bdc550ce73f7bf + languageName: node + linkType: hard + "react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0": version: 19.1.1 resolution: "react@npm:19.1.1" @@ -43113,15 +43213,6 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.0.2": - version: 18.3.1 - resolution: "react@npm:18.3.1" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10/261137d3f3993eaa2368a83110466fc0e558bc2c7f7ae7ca52d94f03aac945f45146bd85e5f481044db1758a1dbb57879e2fcdd33924e2dde1bdc550ce73f7bf - languageName: node - linkType: hard - "read-cmd-shim@npm:^2.0.0": version: 2.0.0 resolution: "read-cmd-shim@npm:2.0.0" @@ -43396,7 +43487,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.3": +"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.3": version: 1.5.4 resolution: "regexp.prototype.flags@npm:1.5.4" dependencies: @@ -44905,7 +44996,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.6, side-channel@npm:^1.1.0": +"side-channel@npm:^1.0.4, side-channel@npm:^1.0.6, side-channel@npm:^1.1.0": version: 1.1.0 resolution: "side-channel@npm:1.1.0" dependencies: @@ -49105,7 +49196,7 @@ __metadata: languageName: node linkType: hard -"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": +"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" dependencies: @@ -49139,7 +49230,7 @@ __metadata: languageName: node linkType: hard -"which-collection@npm:^1.0.2": +"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2": version: 1.0.2 resolution: "which-collection@npm:1.0.2" dependencies: @@ -49161,7 +49252,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18, which-typed-array@npm:^1.1.2": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18, which-typed-array@npm:^1.1.2": version: 1.1.19 resolution: "which-typed-array@npm:1.1.19" dependencies: From 501b159de42bde4a06769f19653dafc51fa15948 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 12 Sep 2025 19:03:54 +0200 Subject: [PATCH 111/211] bui-themer: trim example components and add NFS support Signed-off-by: Patrik Oldsberg --- plugins/bui-themer/package.json | 2 + .../BuiThemerPage.tsx} | 5 +- .../index.ts | 3 +- .../ExampleComponent.test.tsx | 38 --- .../ExampleComponent/ExampleComponent.tsx | 52 --- .../ExampleFetchComponent.test.tsx | 34 -- .../ExampleFetchComponent.tsx | 322 ------------------ plugins/bui-themer/src/index.ts | 2 + .../bui-themer/src/{plugin.ts => plugin.tsx} | 31 +- yarn.lock | 2 + 10 files changed, 41 insertions(+), 450 deletions(-) rename plugins/bui-themer/src/components/{ExampleFetchComponent/index.ts => BuiThemerPage/BuiThemerPage.tsx} (90%) rename plugins/bui-themer/src/components/{ExampleComponent => BuiThemerPage}/index.ts (91%) delete mode 100644 plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx delete mode 100644 plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx delete mode 100644 plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx delete mode 100644 plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx rename plugins/bui-themer/src/{plugin.ts => plugin.tsx} (59%) diff --git a/plugins/bui-themer/package.json b/plugins/bui-themer/package.json index c0d49b0879..f6f5faffd4 100644 --- a/plugins/bui-themer/package.json +++ b/plugins/bui-themer/package.json @@ -25,8 +25,10 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", diff --git a/plugins/bui-themer/src/components/ExampleFetchComponent/index.ts b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx similarity index 90% rename from plugins/bui-themer/src/components/ExampleFetchComponent/index.ts rename to plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index b0be0a2a71..0328a3e6d6 100644 --- a/plugins/bui-themer/src/components/ExampleFetchComponent/index.ts +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -13,4 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { ExampleFetchComponent } from './ExampleFetchComponent'; + +export function BuiThemerPage() { + return
TODO
; +} diff --git a/plugins/bui-themer/src/components/ExampleComponent/index.ts b/plugins/bui-themer/src/components/BuiThemerPage/index.ts similarity index 91% rename from plugins/bui-themer/src/components/ExampleComponent/index.ts rename to plugins/bui-themer/src/components/BuiThemerPage/index.ts index 66b3fc6c97..3d32f11259 100644 --- a/plugins/bui-themer/src/components/ExampleComponent/index.ts +++ b/plugins/bui-themer/src/components/BuiThemerPage/index.ts @@ -13,4 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { ExampleComponent } from './ExampleComponent'; + +export { BuiThemerPage } from './BuiThemerPage'; diff --git a/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx deleted file mode 100644 index f5f7f185c5..0000000000 --- a/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.test.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2025 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 { ExampleComponent } from './ExampleComponent'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { screen } from '@testing-library/react'; -import { registerMswTestHooks, renderInTestApp } from '@backstage/test-utils'; - -describe('ExampleComponent', () => { - const server = setupServer(); - // Enable sane handlers for network requests - registerMswTestHooks(server); - - // setup mock response - beforeEach(() => { - server.use( - rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), - ); - }); - - it('should render', async () => { - await renderInTestApp(); - expect(screen.getByText('Welcome to bui-themer!')).toBeInTheDocument(); - }); -}); diff --git a/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx deleted file mode 100644 index 3a04bd7562..0000000000 --- a/plugins/bui-themer/src/components/ExampleComponent/ExampleComponent.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2025 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, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - Content, - ContentHeader, - HeaderLabel, - SupportButton, -} from '@backstage/core-components'; -import { ExampleFetchComponent } from '../ExampleFetchComponent'; - -export const ExampleComponent = () => ( - -
- - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - -
-); diff --git a/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx deleted file mode 100644 index a3085e3dcb..0000000000 --- a/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2025 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 { renderInTestApp } from '@backstage/test-utils'; -import { ExampleFetchComponent } from './ExampleFetchComponent'; - -describe('ExampleFetchComponent', () => { - it('renders the user table', async () => { - const { getAllByText, getByAltText, getByText, findByRole } = - await renderInTestApp(); - - // Wait for the table to render - const table = await findByRole('table'); - const nationality = getAllByText('GB'); - // Assert that the table contains the expected user data - expect(table).toBeInTheDocument(); - expect(getByAltText('Carolyn')).toBeInTheDocument(); - expect(getByText('Carolyn Moore')).toBeInTheDocument(); - expect(getByText('carolyn.moore@example.com')).toBeInTheDocument(); - expect(nationality[0]).toBeInTheDocument(); - }); -}); diff --git a/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx deleted file mode 100644 index 6c9e77094d..0000000000 --- a/plugins/bui-themer/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright 2025 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 { makeStyles } from '@material-ui/core/styles'; -import { - Table, - TableColumn, - Progress, - ResponseErrorPanel, -} from '@backstage/core-components'; -import useAsync from 'react-use/lib/useAsync'; - -export const exampleUsers = { - results: [ - { - gender: 'female', - name: { - title: 'Miss', - first: 'Carolyn', - last: 'Moore', - }, - email: 'carolyn.moore@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Carolyn', - nat: 'GB', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Esma', - last: 'Berberoğlu', - }, - email: 'esma.berberoglu@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Esma', - nat: 'TR', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Isabella', - last: 'Rhodes', - }, - email: 'isabella.rhodes@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella', - nat: 'GB', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Derrick', - last: 'Carter', - }, - email: 'derrick.carter@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Derrick', - nat: 'IE', - }, - { - gender: 'female', - name: { - title: 'Miss', - first: 'Mattie', - last: 'Lambert', - }, - email: 'mattie.lambert@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mattie', - nat: 'AU', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Mijat', - last: 'Rakić', - }, - email: 'mijat.rakic@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mijat', - nat: 'RS', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Javier', - last: 'Reid', - }, - email: 'javier.reid@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Javier', - nat: 'US', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Isabella', - last: 'Li', - }, - email: 'isabella.li@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Isabella', - nat: 'CA', - }, - { - gender: 'female', - name: { - title: 'Mrs', - first: 'Stephanie', - last: 'Garrett', - }, - email: 'stephanie.garrett@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Stephanie', - nat: 'AU', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Antonia', - last: 'Núñez', - }, - email: 'antonia.nunez@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Antonia', - nat: 'ES', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Donald', - last: 'Young', - }, - email: 'donald.young@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Donald', - nat: 'US', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Iegor', - last: 'Holodovskiy', - }, - email: 'iegor.holodovskiy@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Iegor', - nat: 'UA', - }, - { - gender: 'female', - name: { - title: 'Madame', - first: 'Jessica', - last: 'David', - }, - email: 'jessica.david@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jessica', - nat: 'CH', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Eve', - last: 'Martinez', - }, - email: 'eve.martinez@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Eve', - nat: 'FR', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Caleb', - last: 'Silva', - }, - email: 'caleb.silva@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Caleb', - nat: 'US', - }, - { - gender: 'female', - name: { - title: 'Miss', - first: 'Marcia', - last: 'Jenkins', - }, - email: 'marcia.jenkins@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Marcia', - nat: 'US', - }, - { - gender: 'female', - name: { - title: 'Mrs', - first: 'Mackenzie', - last: 'Jones', - }, - email: 'mackenzie.jones@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Mackenzie', - nat: 'NZ', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Jeremiah', - last: 'Gutierrez', - }, - email: 'jeremiah.gutierrez@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Jeremiah', - nat: 'AU', - }, - { - gender: 'female', - name: { - title: 'Ms', - first: 'Luciara', - last: 'Souza', - }, - email: 'luciara.souza@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Luciara', - nat: 'BR', - }, - { - gender: 'male', - name: { - title: 'Mr', - first: 'Valgi', - last: 'da Cunha', - }, - email: 'valgi.dacunha@example.com', - picture: 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Valgi', - nat: 'BR', - }, - ], -}; - -const useStyles = makeStyles({ - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - email: string; // "duane.reed@example.com" - picture: string; // "https://api.dicebear.com/6.x/open-peeps/svg?seed=Duane" - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable = ({ users }: DenseTableProps) => { - const classes = useStyles(); - - const columns: TableColumn[] = [ - { title: 'Avatar', field: 'avatar' }, - { title: 'Name', field: 'name' }, - { title: 'Email', field: 'email' }, - { title: 'Nationality', field: 'nationality' }, - ]; - - const data = users.map(user => { - return { - avatar: ( - {user.name.first} - ), - name: `${user.name.first} ${user.name.last}`, - email: user.email, - nationality: user.nat, - }; - }); - - return ( -
- ); -}; - -export const ExampleFetchComponent = () => { - const { value, loading, error } = useAsync(async (): Promise => { - // Would use fetch in a real world example - return exampleUsers.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return ; - } - - return ; -}; diff --git a/plugins/bui-themer/src/index.ts b/plugins/bui-themer/src/index.ts index 26bc35ebb1..4cf05a7f23 100644 --- a/plugins/bui-themer/src/index.ts +++ b/plugins/bui-themer/src/index.ts @@ -13,4 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + export { buiThemerPlugin, BuiThemerPage } from './plugin'; +export { default } from './plugin'; diff --git a/plugins/bui-themer/src/plugin.ts b/plugins/bui-themer/src/plugin.tsx similarity index 59% rename from plugins/bui-themer/src/plugin.ts rename to plugins/bui-themer/src/plugin.tsx index d87d035b12..78d3196126 100644 --- a/plugins/bui-themer/src/plugin.ts +++ b/plugins/bui-themer/src/plugin.tsx @@ -13,13 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { + convertLegacyRouteRef, + convertLegacyRouteRefs, +} from '@backstage/core-compat-api'; import { createPlugin, createRoutableExtension, } from '@backstage/core-plugin-api'; - +import { + createFrontendPlugin, + PageBlueprint, +} from '@backstage/frontend-plugin-api'; import { rootRouteRef } from './routes'; +// Old system export const buiThemerPlugin = createPlugin({ id: 'bui-themer', routes: { @@ -31,7 +40,25 @@ export const BuiThemerPage = buiThemerPlugin.provide( createRoutableExtension({ name: 'BuiThemerPage', component: () => - import('./components/ExampleComponent').then(m => m.ExampleComponent), + import('./components/BuiThemerPage').then(m => m.BuiThemerPage), mountPoint: rootRouteRef, }), ); + +// New system +export default createFrontendPlugin({ + pluginId: 'bui-themer', + extensions: [ + PageBlueprint.make({ + params: { + path: '/bui-themer', + loader: () => + import('./components/BuiThemerPage').then(m => ), + routeRef: convertLegacyRouteRef(rootRouteRef), + }, + }), + ], + routes: convertLegacyRouteRefs({ + root: rootRouteRef, + }), +}); diff --git a/yarn.lock b/yarn.lock index cb9a75fa61..f22d88ee20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4329,9 +4329,11 @@ __metadata: dependencies: "@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/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": "npm:^4.9.13" From c56cb0460e6abdeec4e38aae8d61931d0e4b3748 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 12 Sep 2025 19:11:20 +0200 Subject: [PATCH 112/211] bui-themer: add implementation plan Signed-off-by: Patrik Oldsberg --- .../components/BuiThemerPage/BuiThemerPage.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 0328a3e6d6..8fb72eda1c 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -14,6 +14,21 @@ * limitations under the License. */ +/* + +IMPLEMENTATION PLAN: + +- create a utility that converts the MUI theme palette into printed CSS variables +populating the variables in packages/ui/src/css/core.css +- the conversion utility will be created in an adjacent `convertMuiToBuiTheme` file, which defines a `convertMuiToBuiTheme` function that accepts a MUI theme instance and returns CSS in text format +- create tests for the conversion utility +- Create a page that shows the generated CSS variables and allows the user to copy them +- The page should show coverted CSS variables for all installed themes in the app, which can be acquired via the AppThemeApi, defined at packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts +- The AppThemeApi can be accessed via the useApi(appThemeApiRef) hook +- The app themes only provide a `Provider` component that in turn provides the MUI theme. The theme can be accessed by wrapping the component in the theme's `Provider` component and then using the `useTheme` hook from MUI + +*/ + export function BuiThemerPage() { return
TODO
; } From 05ff15d1c0bee41d9e3ac4219c4c142d42e31541 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 12 Sep 2025 19:20:54 +0200 Subject: [PATCH 113/211] bui-themer: refined implementation plan Signed-off-by: Patrik Oldsberg --- .../BuiThemerPage/BuiThemerPage.tsx | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 8fb72eda1c..2da27cad36 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -18,14 +18,36 @@ IMPLEMENTATION PLAN: -- create a utility that converts the MUI theme palette into printed CSS variables -populating the variables in packages/ui/src/css/core.css -- the conversion utility will be created in an adjacent `convertMuiToBuiTheme` file, which defines a `convertMuiToBuiTheme` function that accepts a MUI theme instance and returns CSS in text format -- create tests for the conversion utility -- Create a page that shows the generated CSS variables and allows the user to copy them -- The page should show coverted CSS variables for all installed themes in the app, which can be acquired via the AppThemeApi, defined at packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts -- The AppThemeApi can be accessed via the useApi(appThemeApiRef) hook -- The app themes only provide a `Provider` component that in turn provides the MUI theme. The theme can be accessed by wrapping the component in the theme's `Provider` component and then using the `useTheme` hook from MUI +- Create a converter utility that maps a MUI v5 `Theme` to BUI CSS variables, + covering palette, typography, spacing, and shape tokens to match + `packages/ui/src/css/core.css`. +- Place it in an adjacent `convertMuiToBuiTheme.ts` file exporting + `convertMuiToBuiTheme(theme: Theme, options?): string`, returning + deterministic CSS text. +- Scope output blocks as: + - `:root { ... }` for light themes + - `[data-theme-mode='dark'] { ... }` for dark themes + Optionally prefix by theme id (e.g. `[data-app-theme='']`) so multiple + theme blocks can coexist without conflicts. +- Do not modify `packages/ui/src/css/core.css`; generate CSS for copy/download + only. +- Define a mapping from MUI fields to `--bui-*` tokens with sensible fallbacks + aligning with core defaults. Handle success/warning/error/background/text + surfaces, borders, and link/hover states. +- Add tests for light/dark and fallback behavior using MUI `createTheme` and + snapshot the generated CSS. +- Build the page to list all installed themes via `useApi(appThemeApiRef)`, + render a minimal child under each theme `Provider`, and read `useTheme()` to + get the runtime theme instance. Provide Copy and Download actions and an + optional live preview applying the generated variables. +- Performance: memoize generated CSS by theme id and regenerate when the + installed theme list changes. Observing `activeThemeId$()` is optional unless + previewing the active theme. + +REQUIREMENTS: + +- No changes can be made outside the plugins/bui-themer/src directory +- Only components from the `@backstage/ui` package at `packages/ui` can be used */ From 6262191013b65f95fb8d2d49089b880621c51ac6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 12 Sep 2025 19:46:05 +0200 Subject: [PATCH 114/211] bui-themer: initial implementation plan execution Signed-off-by: Patrik Oldsberg --- plugins/bui-themer/package.json | 2 + .../BuiThemerPage/BuiThemerPage.tsx | 297 ++++++++++++++++-- .../convertMuiToBuiTheme.test.ts | 278 ++++++++++++++++ .../BuiThemerPage/convertMuiToBuiTheme.ts | 286 +++++++++++++++++ yarn.lock | 185 ++++++++++- 5 files changed, 1015 insertions(+), 33 deletions(-) create mode 100644 plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts create mode 100644 plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts diff --git a/plugins/bui-themer/package.json b/plugins/bui-themer/package.json index f6f5faffd4..ff97c92ecb 100644 --- a/plugins/bui-themer/package.json +++ b/plugins/bui-themer/package.json @@ -30,9 +30,11 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/theme": "workspace:^", + "@backstage/ui": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.61", + "@mui/material": "^7.3.2", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 2da27cad36..b36c550081 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -14,43 +14,276 @@ * limitations under the License. */ -/* +import { useMemo, useState, useCallback, useEffect } from 'react'; +import { useApi } from '@backstage/core-plugin-api'; +import { appThemeApiRef } from '@backstage/core-plugin-api'; +import { useTheme } from '@mui/material/styles'; +import { + Box, + Button, + Card, + Container, + Flex, + Grid, + HeaderPage, + Text, + TextField, + Switch, +} from '@backstage/ui'; +import { + convertMuiToBuiTheme, + ConvertMuiToBuiThemeOptions, +} from './convertMuiToBuiTheme'; -IMPLEMENTATION PLAN: +interface ThemePreviewProps { + themeId: string; + themeTitle: string; + variant: 'light' | 'dark'; + Provider: React.ComponentType<{ children: React.ReactNode }>; +} -- Create a converter utility that maps a MUI v5 `Theme` to BUI CSS variables, - covering palette, typography, spacing, and shape tokens to match - `packages/ui/src/css/core.css`. -- Place it in an adjacent `convertMuiToBuiTheme.ts` file exporting - `convertMuiToBuiTheme(theme: Theme, options?): string`, returning - deterministic CSS text. -- Scope output blocks as: - - `:root { ... }` for light themes - - `[data-theme-mode='dark'] { ... }` for dark themes - Optionally prefix by theme id (e.g. `[data-app-theme='']`) so multiple - theme blocks can coexist without conflicts. -- Do not modify `packages/ui/src/css/core.css`; generate CSS for copy/download - only. -- Define a mapping from MUI fields to `--bui-*` tokens with sensible fallbacks - aligning with core defaults. Handle success/warning/error/background/text - surfaces, borders, and link/hover states. -- Add tests for light/dark and fallback behavior using MUI `createTheme` and - snapshot the generated CSS. -- Build the page to list all installed themes via `useApi(appThemeApiRef)`, - render a minimal child under each theme `Provider`, and read `useTheme()` to - get the runtime theme instance. Provide Copy and Download actions and an - optional live preview applying the generated variables. -- Performance: memoize generated CSS by theme id and regenerate when the - installed theme list changes. Observing `activeThemeId$()` is optional unless - previewing the active theme. +// Memoization cache for generated CSS +const cssCache = new Map(); -REQUIREMENTS: +interface ThemeContentProps { + themeId: string; + themeTitle: string; + variant: 'light' | 'dark'; +} -- No changes can be made outside the plugins/bui-themer/src directory -- Only components from the `@backstage/ui` package at `packages/ui` can be used +function ThemeContent({ themeId, themeTitle, variant }: ThemeContentProps) { + const [generatedCss, setGeneratedCss] = useState(''); + const [isPreviewMode, setIsPreviewMode] = useState(false); + const [includeThemeId, setIncludeThemeId] = useState(false); + const muiTheme = useTheme(); -*/ + const css = useMemo(() => { + // Create cache key based on theme properties and options + const cacheKey = `${themeId}-${includeThemeId}-${JSON.stringify({ + palette: muiTheme.palette, + typography: muiTheme.typography, + spacing: muiTheme.spacing, + shape: muiTheme.shape, + })}`; + + // Check cache first + if (cssCache.has(cacheKey)) { + return cssCache.get(cacheKey)!; + } + + const options: ConvertMuiToBuiThemeOptions = { + themeId, + includeThemeId, + }; + const result = convertMuiToBuiTheme(muiTheme, options); + + // Cache the result + cssCache.set(cacheKey, result); + + // Clean up old cache entries (keep only last 50) + if (cssCache.size > 50) { + const firstKey = cssCache.keys().next().value; + if (firstKey) { + cssCache.delete(firstKey); + } + } + + return result; + }, [muiTheme, themeId, includeThemeId]); + + useEffect(() => { + setGeneratedCss(css); + }, [css]); + + const handleCopy = useCallback(() => { + window.navigator.clipboard.writeText(generatedCss); + }, [generatedCss]); + + const handleDownload = useCallback(() => { + const blob = new Blob([generatedCss], { type: 'text/css' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `bui-theme-${themeId}.css`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + }, [generatedCss, themeId]); + + const handlePreviewToggle = useCallback(() => { + setIsPreviewMode(!isPreviewMode); + if (!isPreviewMode) { + // Apply the generated CSS to a style element + const styleId = `bui-theme-preview-${themeId}`; + let styleElement = document.getElementById(styleId); + if (!styleElement) { + styleElement = document.createElement('style'); + styleElement.id = styleId; + document.head.appendChild(styleElement); + } + styleElement.textContent = generatedCss; + } else { + // Remove the preview styles + const styleElement = document.getElementById( + `bui-theme-preview-${themeId}`, + ); + if (styleElement) { + styleElement.remove(); + } + } + }, [isPreviewMode, generatedCss, themeId]); + + return ( + + + + + {themeTitle} + + {variant} theme + + + + + + + + + + + + + + + + Generated CSS: + + + + + {isPreviewMode && ( + + + Live Preview: + + + Solid Button + + + Tint Button + + + Surface Card + + + )} + + + + ); +} + +function ThemePreview({ + themeId, + themeTitle, + variant, + Provider, +}: ThemePreviewProps) { + return ( + + + + ); +} export function BuiThemerPage() { - return
TODO
; + const appThemeApi = useApi(appThemeApiRef); + const installedThemes = appThemeApi.getInstalledThemes(); + + return ( + + + + + Convert MUI v5 themes to BUI CSS variables. Select a theme to generate + the corresponding BUI CSS variables that can be copied or downloaded. + + + + + {installedThemes.length === 0 ? ( + + + + No themes found. Please install some themes in your Backstage + app. + + + + ) : ( + + {installedThemes.map(theme => ( + + + + ))} + + )} + + + ); } diff --git a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts new file mode 100644 index 0000000000..e45f48f3a0 --- /dev/null +++ b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts @@ -0,0 +1,278 @@ +/* + * Copyright 2025 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 { createTheme } from '@mui/material/styles'; +import { convertMuiToBuiTheme } from './convertMuiToBuiTheme'; + +describe('convertMuiToBuiTheme', () => { + it('should generate CSS for light theme', () => { + const theme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#1976d2', + dark: '#115293', + }, + background: { + default: '#f5f5f5', + paper: '#ffffff', + }, + text: { + primary: '#000000', + secondary: '#666666', + }, + }, + typography: { + fontFamily: 'Roboto, sans-serif', + h1: { + fontSize: '2.5rem', + }, + body1: { + fontSize: '1rem', + }, + }, + spacing: 8, + shape: { + borderRadius: 4, + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain(':root {'); + expect(result).toContain('--bui-font-regular: Roboto, sans-serif;'); + expect(result).toContain('--bui-space: 8px;'); + expect(result).toContain('--bui-radius-3: 4px;'); + expect(result).toContain('--bui-bg: #f5f5f5;'); + expect(result).toContain('--bui-bg-surface-1: #ffffff;'); + expect(result).toContain('--bui-fg-primary: #000000;'); + expect(result).toContain('--bui-fg-secondary: #666666;'); + expect(result).toContain('--bui-bg-solid: #1976d2;'); + }); + + it('should generate CSS for dark theme', () => { + const theme = createTheme({ + palette: { + mode: 'dark', + primary: { + main: '#90caf9', + dark: '#42a5f5', + }, + background: { + default: '#121212', + paper: '#1e1e1e', + }, + text: { + primary: '#ffffff', + secondary: '#b3b3b3', + }, + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain("[data-theme-mode='dark'] {"); + expect(result).toContain('--bui-bg: #121212;'); + expect(result).toContain('--bui-bg-surface-1: #1e1e1e;'); + expect(result).toContain('--bui-fg-primary: #ffffff;'); + expect(result).toContain('--bui-fg-secondary: #b3b3b3;'); + }); + + it('should include theme ID scoping when requested', () => { + const theme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#1976d2', + }, + }, + }); + + const result = convertMuiToBuiTheme(theme, { + themeId: 'my-theme', + includeThemeId: true, + }); + + expect(result).toContain("[data-app-theme='my-theme'] :root {"); + }); + + it('should handle missing theme properties gracefully', () => { + const theme = createTheme({}); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain(':root {'); + expect(result).toContain('--bui-font-weight-regular: 400;'); + expect(result).toContain('--bui-font-weight-bold: 700;'); + // Should have default values even when theme properties are missing + expect(result).toContain('--bui-black: #000;'); + expect(result).toContain('--bui-white: #fff;'); + }); + + it('should generate proper gray scale for light theme', () => { + const theme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#1976d2', + }, + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-gray-1: #f8f8f8;'); + expect(result).toContain('--bui-gray-8: #595959;'); + }); + + it('should generate proper gray scale for dark theme', () => { + const theme = createTheme({ + palette: { + mode: 'dark', + primary: { + main: '#90caf9', + }, + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-gray-1: #191919;'); + expect(result).toContain('--bui-gray-8: #b4b4b4;'); + }); + + it('should generate surface colors correctly', () => { + const theme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#1976d2', + dark: '#115293', + }, + error: { + main: '#f44336', + light: '#ffcdd2', + }, + warning: { + main: '#ff9800', + light: '#ffe0b2', + }, + success: { + main: '#4caf50', + light: '#c8e6c9', + }, + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-bg-solid: #1976d2;'); + expect(result).toContain('--bui-bg-solid-hover: #115293;'); + expect(result).toContain('--bui-bg-danger: #ffcdd2;'); + expect(result).toContain('--bui-bg-warning: #ffe0b2;'); + expect(result).toContain('--bui-bg-success: #c8e6c9;'); + }); + + it('should generate foreground colors correctly', () => { + const theme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#1976d2', + dark: '#115293', + }, + error: { + main: '#f44336', + }, + warning: { + main: '#ff9800', + }, + success: { + main: '#4caf50', + }, + text: { + disabled: '#cccccc', + }, + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-fg-link: #1976d2;'); + expect(result).toContain('--bui-fg-link-hover: #115293;'); + expect(result).toContain('--bui-fg-disabled: #cccccc;'); + expect(result).toContain('--bui-fg-danger: #f44336;'); + expect(result).toContain('--bui-fg-warning: #ff9800;'); + expect(result).toContain('--bui-fg-success: #4caf50;'); + }); + + it('should generate border colors correctly', () => { + const theme = createTheme({ + palette: { + mode: 'light', + error: { + main: '#f44336', + }, + warning: { + main: '#ff9800', + }, + success: { + main: '#4caf50', + }, + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-border: rgba(0, 0, 0, 0.1);'); + expect(result).toContain('--bui-border-hover: rgba(0, 0, 0, 0.2);'); + expect(result).toContain('--bui-border-danger: #f44336;'); + expect(result).toContain('--bui-border-warning: #ff9800;'); + expect(result).toContain('--bui-border-success: #4caf50;'); + }); + + it('should handle function-based spacing', () => { + const theme = createTheme({ + spacing: (factor: number) => `${factor * 4}px`, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-space: 4px;'); + }); + + it('should handle string-based spacing', () => { + const theme = createTheme({ + spacing: '8px', + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-space: calc(1 * 8px);'); + }); + + it('should handle string-based border radius', () => { + const theme = createTheme({ + shape: { + borderRadius: '8px', + }, + }); + + const result = convertMuiToBuiTheme(theme); + + expect(result).toContain('--bui-radius-3: 8px;'); + }); +}); diff --git a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts new file mode 100644 index 0000000000..e1f1618760 --- /dev/null +++ b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts @@ -0,0 +1,286 @@ +/* + * Copyright 2025 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 { Theme as Mui5Theme } from '@mui/material/styles'; + +export interface ConvertMuiToBuiThemeOptions { + /** + * Theme ID to use for scoping CSS variables + */ + themeId?: string; + /** + * Whether to include theme ID scoping in the CSS + */ + includeThemeId?: boolean; +} + +/** + * Converts a MUI v5 Theme to BUI CSS variables + * @param theme - The MUI v5 theme to convert + * @param options - Conversion options + * @returns CSS string with BUI variables + */ +export function convertMuiToBuiTheme( + theme: Mui5Theme, + options: ConvertMuiToBuiThemeOptions = {}, +): string { + const { themeId, includeThemeId = false } = options; + const isDark = theme.palette.mode === 'dark'; + + // Generate CSS variables based on theme + const variables = generateBuiVariables(theme); + + // Create CSS selector based on theme mode and ID + let selector = isDark ? "[data-theme-mode='dark']" : ':root'; + if (includeThemeId && themeId) { + selector = `[data-app-theme='${themeId}'] ${selector}`; + } + + return `${selector} {\n${variables}\n}`; +} + +/** + * Generates BUI CSS variables from MUI theme + */ +function generateBuiVariables(theme: Mui5Theme): string { + const variables: string[] = []; + + // Font families + if (theme.typography.fontFamily) { + variables.push(` --bui-font-regular: ${theme.typography.fontFamily};`); + } + + // Font weights + variables.push( + ` --bui-font-weight-regular: ${ + theme.typography.fontWeightRegular || 400 + };`, + ); + variables.push( + ` --bui-font-weight-bold: ${theme.typography.fontWeightBold || 600};`, + ); + + // Font sizes - map MUI typography scale to BUI scale + const fontSizeMap = { + h1: '--bui-font-size-10', + h2: '--bui-font-size-8', + h3: '--bui-font-size-7', + h4: '--bui-font-size-6', + h5: '--bui-font-size-5', + h6: '--bui-font-size-4', + body1: '--bui-font-size-4', + body2: '--bui-font-size-3', + caption: '--bui-font-size-2', + overline: '--bui-font-size-1', + }; + + Object.entries(fontSizeMap).forEach(([muiKey, buiVar]) => { + const typographyVariant = + theme.typography[muiKey as keyof typeof theme.typography]; + const fontSize = + typeof typographyVariant === 'object' && typographyVariant?.fontSize + ? typographyVariant.fontSize + : undefined; + if (fontSize) { + variables.push(` ${buiVar}: ${fontSize};`); + } + }); + + // Spacing - map MUI spacing to BUI spacing scale + if (theme.spacing) { + const spacingUnit = + typeof theme.spacing === 'function' ? theme.spacing(1) : theme.spacing; + const spacingValue = + typeof spacingUnit === 'number' ? `${spacingUnit}px` : spacingUnit; + variables.push(` --bui-space: ${spacingValue};`); + } + + // Border radius + if (theme.shape?.borderRadius) { + const radius = theme.shape.borderRadius; + const radiusValue = typeof radius === 'number' ? `${radius}px` : radius; + variables.push(` --bui-radius-3: ${radiusValue};`); + } + + // Colors - map MUI palette to BUI color tokens + const palette = theme.palette; + + // Base colors + if (palette.common?.black) { + variables.push(` --bui-black: ${palette.common.black};`); + } + if (palette.common?.white) { + variables.push(` --bui-white: ${palette.common.white};`); + } + + // Gray scale - generate from primary color or use defaults + const grayScale = generateGrayScale(palette.mode === 'dark'); + Object.entries(grayScale).forEach(([key, value]) => { + variables.push(` --bui-gray-${key}: ${value};`); + }); + + // Background colors + if (palette.background?.default) { + variables.push(` --bui-bg: ${palette.background.default};`); + } + if (palette.background?.paper) { + variables.push(` --bui-bg-surface-1: ${palette.background.paper};`); + } + + // Generate surface colors + const surfaceColors = generateSurfaceColors(palette); + Object.entries(surfaceColors).forEach(([key, value]) => { + variables.push(` --bui-bg-${key}: ${value};`); + }); + + // Foreground colors + if (palette.text?.primary) { + variables.push(` --bui-fg-primary: ${palette.text.primary};`); + } + if (palette.text?.secondary) { + variables.push(` --bui-fg-secondary: ${palette.text.secondary};`); + } + + // Generate foreground colors + const foregroundColors = generateForegroundColors(palette); + Object.entries(foregroundColors).forEach(([key, value]) => { + variables.push(` --bui-fg-${key}: ${value};`); + }); + + // Border colors + const borderColors = generateBorderColors(palette); + Object.entries(borderColors).forEach(([key, value]) => { + variables.push(` --bui-border${key ? `-${key}` : ''}: ${value};`); + }); + + // Special colors + if (palette.primary?.main) { + variables.push(` --bui-ring: ${palette.primary.main};`); + } + + return variables.join('\n'); +} + +/** + * Generates gray scale colors + */ +function generateGrayScale(isDark: boolean): Record { + if (isDark) { + return { + '1': '#191919', + '2': '#242424', + '3': '#373737', + '4': '#464646', + '5': '#575757', + '6': '#7b7b7b', + '7': '#9e9e9e', + '8': '#b4b4b4', + }; + } + + return { + '1': '#f8f8f8', + '2': '#ececec', + '3': '#d9d9d9', + '4': '#c1c1c1', + '5': '#9e9e9e', + '6': '#8c8c8c', + '7': '#757575', + '8': '#595959', + }; +} + +/** + * Generates surface background colors + */ +function generateSurfaceColors( + palette: Mui5Theme['palette'], +): Record { + const isDark = palette.mode === 'dark'; + + // Helper function to get tint colors with proper fallbacks + const getTintColor = (opacity: string) => { + if (palette.primary?.main) { + return `${palette.primary.main}${opacity}`; + } + return isDark + ? `rgba(156, 201, 255, ${opacity === '40' ? '0.12' : '0.16'})` + : `rgba(31, 84, 147, ${opacity === '40' ? '0.4' : '0.6'})`; + }; + + // Helper function to get fallback colors based on theme mode + const getFallbackColor = (lightColor: string, darkColor: string) => { + return isDark ? darkColor : lightColor; + }; + + return { + 'surface-2': getFallbackColor('#ececec', '#242424'), + solid: palette.primary?.main || getFallbackColor('#1f5493', '#9cc9ff'), + 'solid-hover': + palette.primary?.dark || getFallbackColor('#163a66', '#83b9fd'), + 'solid-pressed': + palette.primary?.dark || getFallbackColor('#0f2b4e', '#83b9fd'), + 'solid-disabled': getFallbackColor('#ebebeb', '#222222'), + tint: 'transparent', + 'tint-hover': getTintColor('40'), + 'tint-pressed': getTintColor('60'), + 'tint-disabled': getFallbackColor('#ebebeb', 'transparent'), + danger: palette.error?.light || getFallbackColor('#feebe7', '#3b1219'), + warning: palette.warning?.light || getFallbackColor('#fff2b2', '#302008'), + success: palette.success?.light || getFallbackColor('#e6f6eb', '#132d21'), + }; +} + +/** + * Generates foreground colors + */ +function generateForegroundColors( + palette: Mui5Theme['palette'], +): Record { + const isDark = palette.mode === 'dark'; + + return { + link: palette.primary?.main || (isDark ? '#9cc9ff' : '#1f5493'), + 'link-hover': palette.primary?.dark || (isDark ? '#7eb5f7' : '#1f2d5c'), + disabled: palette.text?.disabled || (isDark ? '#9e9e9e' : '#9e9e9e'), + solid: isDark ? '#101821' : '#ffffff', + 'solid-disabled': isDark ? '#575757' : '#9c9c9c', + tint: palette.primary?.main || (isDark ? '#9cc9ff' : '#1f5493'), + 'tint-disabled': isDark ? '#575757' : '#9e9e9e', + danger: palette.error?.main || '#e22b2b', + warning: palette.warning?.main || '#e36d05', + success: palette.success?.main || '#1db954', + }; +} + +/** + * Generates border colors + */ +function generateBorderColors( + palette: Mui5Theme['palette'], +): Record { + const isDark = palette.mode === 'dark'; + + return { + '': isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.1)', + hover: isDark ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.2)', + pressed: isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.4)', + disabled: isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)', + danger: palette.error?.main || '#f87a7a', + warning: palette.warning?.main || '#e36d05', + success: palette.success?.main || '#53db83', + }; +} diff --git a/yarn.lock b/yarn.lock index f22d88ee20..41f7dc6d64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2435,6 +2435,13 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.28.3": + version: 7.28.4 + resolution: "@babel/runtime@npm:7.28.4" + checksum: 10/6c9a70452322ea80b3c9b2a412bcf60771819213a67576c8cec41e88a95bb7bf01fc983754cda35dc19603eef52df22203ccbf7777b9d6316932f9fb77c25163 + languageName: node + linkType: hard + "@babel/template@npm:^7.27.2, @babel/template@npm:^7.3.3": version: 7.27.2 resolution: "@babel/template@npm:7.27.2" @@ -4336,9 +4343,11 @@ __metadata: "@backstage/frontend-plugin-api": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" + "@backstage/ui": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:^4.0.0-alpha.61" + "@mui/material": "npm:^7.3.2" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^14.0.0" "@testing-library/user-event": "npm:^14.0.0" @@ -8184,6 +8193,19 @@ __metadata: languageName: node linkType: hard +"@emotion/cache@npm:^11.14.0": + version: 11.14.0 + resolution: "@emotion/cache@npm:11.14.0" + dependencies: + "@emotion/memoize": "npm:^0.9.0" + "@emotion/sheet": "npm:^1.4.0" + "@emotion/utils": "npm:^1.4.2" + "@emotion/weak-memoize": "npm:^0.4.0" + stylis: "npm:4.2.0" + checksum: 10/52336b28a27b07dde8fcdfd80851cbd1487672bbd4db1e24cca1440c95d8a6a968c57b0453c2b7c88d9b432b717f99554dbecc05b5cdef27933299827e69fd8e + languageName: node + linkType: hard + "@emotion/hash@npm:^0.8.0": version: 0.8.0 resolution: "@emotion/hash@npm:0.8.0" @@ -11170,6 +11192,13 @@ __metadata: languageName: node linkType: hard +"@mui/core-downloads-tracker@npm:^7.3.2": + version: 7.3.2 + resolution: "@mui/core-downloads-tracker@npm:7.3.2" + checksum: 10/01c0976e5fb6a8f0b59ebd58019af5452d7761e97daf0f2ef121bc3323508edf2fea1b13ea1a54b0098d6ee5eef70041c9722fe43291b237b989b6813a24b71e + languageName: node + linkType: hard + "@mui/material@npm:^5.12.2": version: 5.16.14 resolution: "@mui/material@npm:5.16.14" @@ -11203,6 +11232,42 @@ __metadata: languageName: node linkType: hard +"@mui/material@npm:^7.3.2": + version: 7.3.2 + resolution: "@mui/material@npm:7.3.2" + dependencies: + "@babel/runtime": "npm:^7.28.3" + "@mui/core-downloads-tracker": "npm:^7.3.2" + "@mui/system": "npm:^7.3.2" + "@mui/types": "npm:^7.4.6" + "@mui/utils": "npm:^7.3.2" + "@popperjs/core": "npm:^2.11.8" + "@types/react-transition-group": "npm:^4.4.12" + clsx: "npm:^2.1.1" + csstype: "npm:^3.1.3" + prop-types: "npm:^15.8.1" + react-is: "npm:^19.1.1" + react-transition-group: "npm:^4.4.5" + peerDependencies: + "@emotion/react": ^11.5.0 + "@emotion/styled": ^11.3.0 + "@mui/material-pigment-css": ^7.3.2 + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + "@mui/material-pigment-css": + optional: true + "@types/react": + optional: true + checksum: 10/e9a5348060d6e6e6cb1ce0fe299a582fa9b37b56dff46b62f72d029d5078acefd56a258e559b29bb3d12e89d0a81fd4f1194ce07cc136bbf03fdb4878fd9b3c0 + languageName: node + linkType: hard + "@mui/private-theming@npm:^5.16.14": version: 5.16.14 resolution: "@mui/private-theming@npm:5.16.14" @@ -11220,6 +11285,23 @@ __metadata: languageName: node linkType: hard +"@mui/private-theming@npm:^7.3.2": + version: 7.3.2 + resolution: "@mui/private-theming@npm:7.3.2" + dependencies: + "@babel/runtime": "npm:^7.28.3" + "@mui/utils": "npm:^7.3.2" + prop-types: "npm:^15.8.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/5b733fc9e09e5b056af3e448899c665c6d1560ec56f593811fc88b9c5499ad42d8365551b182473880de6141a206da4f1f9e5b2ebcea12461dc6d8954e2a3ed7 + languageName: node + linkType: hard + "@mui/styled-engine@npm:^5.16.14": version: 5.16.14 resolution: "@mui/styled-engine@npm:5.16.14" @@ -11241,6 +11323,29 @@ __metadata: languageName: node linkType: hard +"@mui/styled-engine@npm:^7.3.2": + version: 7.3.2 + resolution: "@mui/styled-engine@npm:7.3.2" + dependencies: + "@babel/runtime": "npm:^7.28.3" + "@emotion/cache": "npm:^11.14.0" + "@emotion/serialize": "npm:^1.3.3" + "@emotion/sheet": "npm:^1.4.0" + csstype: "npm:^3.1.3" + prop-types: "npm:^15.8.1" + peerDependencies: + "@emotion/react": ^11.4.1 + "@emotion/styled": ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + checksum: 10/f31a6080b950cc66edd19a5285d30e0557fc16ef07a5e9027d3479f18dda4a569a62976c0fa396a99b077c01000c4fced3c8ab3ea0f6bcd82d6db06b821503b2 + languageName: node + linkType: hard + "@mui/styles@npm:^5.14.18": version: 5.16.14 resolution: "@mui/styles@npm:5.16.14" @@ -11300,6 +11405,34 @@ __metadata: languageName: node linkType: hard +"@mui/system@npm:^7.3.2": + version: 7.3.2 + resolution: "@mui/system@npm:7.3.2" + dependencies: + "@babel/runtime": "npm:^7.28.3" + "@mui/private-theming": "npm:^7.3.2" + "@mui/styled-engine": "npm:^7.3.2" + "@mui/types": "npm:^7.4.6" + "@mui/utils": "npm:^7.3.2" + clsx: "npm:^2.1.1" + csstype: "npm:^3.1.3" + prop-types: "npm:^15.8.1" + peerDependencies: + "@emotion/react": ^11.5.0 + "@emotion/styled": ^11.3.0 + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + "@types/react": + optional: true + checksum: 10/1e157027a693877a246c36e5c98dd988172daedb3430f95843a508fa3f8a707c394fe216078a17868d5d25690ea2f4c5af007dffff639436c36d982fb16e920a + languageName: node + linkType: hard + "@mui/types@npm:^7.2.15": version: 7.2.19 resolution: "@mui/types@npm:7.2.19" @@ -11312,6 +11445,20 @@ __metadata: languageName: node linkType: hard +"@mui/types@npm:^7.4.6": + version: 7.4.6 + resolution: "@mui/types@npm:7.4.6" + dependencies: + "@babel/runtime": "npm:^7.28.3" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/6263ef077332b75e01c1b347339f100f8743d8135efdee4a6c167ce70c69ca29d9face54c53395cbc1c8a8f5453fe8ef3f4457842cf0c05318f6ef6cde44456e + languageName: node + linkType: hard + "@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.16.14": version: 5.16.14 resolution: "@mui/utils@npm:5.16.14" @@ -11332,6 +11479,26 @@ __metadata: languageName: node linkType: hard +"@mui/utils@npm:^7.3.2": + version: 7.3.2 + resolution: "@mui/utils@npm:7.3.2" + dependencies: + "@babel/runtime": "npm:^7.28.3" + "@mui/types": "npm:^7.4.6" + "@types/prop-types": "npm:^15.7.15" + clsx: "npm:^2.1.1" + prop-types: "npm:^15.8.1" + react-is: "npm:^19.1.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/defcebcb8e49cdbdf271e49066d78046d2d25d4c8177176a6c5d3368fe85e6a280c7be71dbf356b434863d96d69236956afe6a4a47bc394de6aeecfa8bc24fda + languageName: node + linkType: hard + "@n1ru4l/push-pull-async-iterable-iterator@npm:^3.1.0": version: 3.1.0 resolution: "@n1ru4l/push-pull-async-iterable-iterator@npm:3.1.0" @@ -20765,7 +20932,7 @@ __metadata: languageName: node linkType: hard -"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.12, @types/prop-types@npm:^15.7.3": +"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.12, @types/prop-types@npm:^15.7.15, @types/prop-types@npm:^15.7.3": version: 15.7.15 resolution: "@types/prop-types@npm:15.7.15" checksum: 10/31aa2f59b28f24da6fb4f1d70807dae2aedfce090ec63eaf9ea01727a9533ef6eaf017de5bff99fbccad7d1c9e644f52c6c2ba30869465dd22b1a7221c29f356 @@ -20879,6 +21046,15 @@ __metadata: languageName: node linkType: hard +"@types/react-transition-group@npm:^4.4.12": + version: 4.4.12 + resolution: "@types/react-transition-group@npm:4.4.12" + peerDependencies: + "@types/react": "*" + checksum: 10/ea14bc84f529a3887f9954b753843820ac8a3c49fcdfec7840657ecc6a8800aad98afdbe4b973eb96c7252286bde38476fcf64b1c09527354a9a9366e516d9a2 + languageName: node + linkType: hard + "@types/react-virtualized-auto-sizer@npm:^1.0.1": version: 1.0.4 resolution: "@types/react-virtualized-auto-sizer@npm:1.0.4" @@ -42751,6 +42927,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^19.1.1": + version: 19.1.1 + resolution: "react-is@npm:19.1.1" + checksum: 10/44e0937da1f0da1d5dbd4f01972870768ef207f8a49717f4491f5022454f34c956cb66be560aee5286387169853b0283a81e1f419b51dc62654c8710dc98065a + languageName: node + linkType: hard + "react-json-view@npm:^1.21.3": version: 1.21.3 resolution: "react-json-view@npm:1.21.3" From 0d4ded62f81e76c674bbf05ab41857aeb10df11b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 12:34:35 +0200 Subject: [PATCH 115/211] bui-themer: get rid of most hardcoded defaults Signed-off-by: Patrik Oldsberg --- .../convertMuiToBuiTheme.test.ts | 60 +++++ .../BuiThemerPage/convertMuiToBuiTheme.ts | 228 +++++++++++++++--- 2 files changed, 261 insertions(+), 27 deletions(-) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts index e45f48f3a0..ffbd4fc392 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts +++ b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts @@ -275,4 +275,64 @@ describe('convertMuiToBuiTheme', () => { expect(result).toContain('--bui-radius-3: 8px;'); }); + + describe('early validation', () => { + it('should throw error when theme is undefined', () => { + expect(() => convertMuiToBuiTheme(undefined as any)).toThrow( + 'Theme is required', + ); + }); + + it('should throw error when theme palette is missing', () => { + const theme = { typography: {}, spacing: () => 8, shape: {} } as any; + expect(() => convertMuiToBuiTheme(theme)).toThrow( + 'Theme palette is required', + ); + }); + + it('should throw error when theme palette mode is missing', () => { + const theme = { + palette: {}, + typography: {}, + spacing: () => 8, + shape: {}, + } as any; + expect(() => convertMuiToBuiTheme(theme)).toThrow( + 'Theme palette mode is required', + ); + }); + + it('should throw error when theme typography is missing', () => { + const theme = { + palette: { mode: 'light' }, + spacing: () => 8, + shape: {}, + } as any; + expect(() => convertMuiToBuiTheme(theme)).toThrow( + 'Theme typography is required', + ); + }); + + it('should throw error when theme spacing is missing', () => { + const theme = { + palette: { mode: 'light' }, + typography: {}, + shape: {}, + } as any; + expect(() => convertMuiToBuiTheme(theme)).toThrow( + 'Theme spacing is required', + ); + }); + + it('should throw error when theme shape is missing', () => { + const theme = { + palette: { mode: 'light' }, + typography: {}, + spacing: () => 8, + } as any; + expect(() => convertMuiToBuiTheme(theme)).toThrow( + 'Theme shape is required', + ); + }); + }); }); diff --git a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts index e1f1618760..dee1c1fd72 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts +++ b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts @@ -15,6 +15,7 @@ */ import { Theme as Mui5Theme } from '@mui/material/styles'; +import { themes } from '@backstage/theme'; export interface ConvertMuiToBuiThemeOptions { /** @@ -37,6 +38,31 @@ export function convertMuiToBuiTheme( theme: Mui5Theme, options: ConvertMuiToBuiThemeOptions = {}, ): string { + // Early validation of required theme properties + if (!theme) { + throw new Error('Theme is required'); + } + + if (!theme.palette) { + throw new Error('Theme palette is required'); + } + + if (!theme.palette.mode) { + throw new Error('Theme palette mode is required'); + } + + if (!theme.typography) { + throw new Error('Theme typography is required'); + } + + if (!theme.spacing) { + throw new Error('Theme spacing is required'); + } + + if (!theme.shape) { + throw new Error('Theme shape is required'); + } + const { themeId, includeThemeId = false } = options; const isDark = theme.palette.mode === 'dark'; @@ -211,36 +237,92 @@ function generateSurfaceColors( ): Record { const isDark = palette.mode === 'dark'; - // Helper function to get tint colors with proper fallbacks + // Get the default Backstage theme for fallback values + const defaultTheme = isDark ? themes.dark : themes.light; + const defaultMuiTheme = defaultTheme.getTheme('v5'); + + if (!defaultMuiTheme) { + throw new Error( + `Failed to get MUI v5 theme from Backstage ${ + isDark ? 'dark' : 'light' + } theme`, + ); + } + + const defaultPalette = defaultMuiTheme.palette; + if (!defaultPalette) { + throw new Error( + `Failed to get palette from Backstage ${isDark ? 'dark' : 'light'} theme`, + ); + } + + // Helper function to get tint colors const getTintColor = (opacity: string) => { - if (palette.primary?.main) { - return `${palette.primary.main}${opacity}`; + const primaryColor = palette.primary?.main || defaultPalette.primary?.main; + if (!primaryColor) { + throw new Error('Primary color not found in current or default theme'); } - return isDark - ? `rgba(156, 201, 255, ${opacity === '40' ? '0.12' : '0.16'})` - : `rgba(31, 84, 147, ${opacity === '40' ? '0.4' : '0.6'})`; + return `${primaryColor}${opacity}`; }; - // Helper function to get fallback colors based on theme mode - const getFallbackColor = (lightColor: string, darkColor: string) => { + // Helper function to get colors based on theme mode + const getThemeColor = (lightColor: string, darkColor: string) => { return isDark ? darkColor : lightColor; }; return { - 'surface-2': getFallbackColor('#ececec', '#242424'), - solid: palette.primary?.main || getFallbackColor('#1f5493', '#9cc9ff'), + 'surface-2': getThemeColor('#ececec', '#242424'), + solid: + palette.primary?.main || + defaultPalette.primary?.main || + (() => { + throw new Error('Primary color not found in current or default theme'); + })(), 'solid-hover': - palette.primary?.dark || getFallbackColor('#163a66', '#83b9fd'), + palette.primary?.dark || + defaultPalette.primary?.dark || + (() => { + throw new Error( + 'Primary dark color not found in current or default theme', + ); + })(), 'solid-pressed': - palette.primary?.dark || getFallbackColor('#0f2b4e', '#83b9fd'), - 'solid-disabled': getFallbackColor('#ebebeb', '#222222'), + palette.primary?.dark || + defaultPalette.primary?.dark || + (() => { + throw new Error( + 'Primary dark color not found in current or default theme', + ); + })(), + 'solid-disabled': getThemeColor('#ebebeb', '#222222'), tint: 'transparent', 'tint-hover': getTintColor('40'), 'tint-pressed': getTintColor('60'), - 'tint-disabled': getFallbackColor('#ebebeb', 'transparent'), - danger: palette.error?.light || getFallbackColor('#feebe7', '#3b1219'), - warning: palette.warning?.light || getFallbackColor('#fff2b2', '#302008'), - success: palette.success?.light || getFallbackColor('#e6f6eb', '#132d21'), + 'tint-disabled': getThemeColor('#ebebeb', 'transparent'), + danger: + palette.error?.light || + defaultPalette.error?.light || + (() => { + throw new Error( + 'Error light color not found in current or default theme', + ); + })(), + warning: + palette.warning?.light || + defaultPalette.warning?.light || + (() => { + throw new Error( + 'Warning light color not found in current or default theme', + ); + })(), + success: + palette.success?.light || + defaultPalette.success?.light || + (() => { + throw new Error( + 'Success light color not found in current or default theme', + ); + })(), }; } @@ -252,17 +334,75 @@ function generateForegroundColors( ): Record { const isDark = palette.mode === 'dark'; + // Get the default Backstage theme for fallback values + const defaultTheme = isDark ? themes.dark : themes.light; + const defaultMuiTheme = defaultTheme.getTheme('v5'); + + if (!defaultMuiTheme) { + throw new Error( + `Failed to get MUI v5 theme from Backstage ${ + isDark ? 'dark' : 'light' + } theme`, + ); + } + + const defaultPalette = defaultMuiTheme.palette; + if (!defaultPalette) { + throw new Error( + `Failed to get palette from Backstage ${isDark ? 'dark' : 'light'} theme`, + ); + } + return { - link: palette.primary?.main || (isDark ? '#9cc9ff' : '#1f5493'), - 'link-hover': palette.primary?.dark || (isDark ? '#7eb5f7' : '#1f2d5c'), - disabled: palette.text?.disabled || (isDark ? '#9e9e9e' : '#9e9e9e'), + link: + palette.primary?.main || + defaultPalette.primary?.main || + (() => { + throw new Error('Primary color not found in current or default theme'); + })(), + 'link-hover': + palette.primary?.dark || + defaultPalette.primary?.dark || + (() => { + throw new Error( + 'Primary dark color not found in current or default theme', + ); + })(), + disabled: + palette.text?.disabled || + defaultPalette.text?.disabled || + (() => { + throw new Error( + 'Text disabled color not found in current or default theme', + ); + })(), solid: isDark ? '#101821' : '#ffffff', 'solid-disabled': isDark ? '#575757' : '#9c9c9c', - tint: palette.primary?.main || (isDark ? '#9cc9ff' : '#1f5493'), + tint: + palette.primary?.main || + defaultPalette.primary?.main || + (() => { + throw new Error('Primary color not found in current or default theme'); + })(), 'tint-disabled': isDark ? '#575757' : '#9e9e9e', - danger: palette.error?.main || '#e22b2b', - warning: palette.warning?.main || '#e36d05', - success: palette.success?.main || '#1db954', + danger: + palette.error?.main || + defaultPalette.error?.main || + (() => { + throw new Error('Error color not found in current or default theme'); + })(), + warning: + palette.warning?.main || + defaultPalette.warning?.main || + (() => { + throw new Error('Warning color not found in current or default theme'); + })(), + success: + palette.success?.main || + defaultPalette.success?.main || + (() => { + throw new Error('Success color not found in current or default theme'); + })(), }; } @@ -274,13 +414,47 @@ function generateBorderColors( ): Record { const isDark = palette.mode === 'dark'; + // Get the default Backstage theme for fallback values + const defaultTheme = isDark ? themes.dark : themes.light; + const defaultMuiTheme = defaultTheme.getTheme('v5'); + + if (!defaultMuiTheme) { + throw new Error( + `Failed to get MUI v5 theme from Backstage ${ + isDark ? 'dark' : 'light' + } theme`, + ); + } + + const defaultPalette = defaultMuiTheme.palette; + if (!defaultPalette) { + throw new Error( + `Failed to get palette from Backstage ${isDark ? 'dark' : 'light'} theme`, + ); + } + return { '': isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.1)', hover: isDark ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.2)', pressed: isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.4)', disabled: isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)', - danger: palette.error?.main || '#f87a7a', - warning: palette.warning?.main || '#e36d05', - success: palette.success?.main || '#53db83', + danger: + palette.error?.main || + defaultPalette.error?.main || + (() => { + throw new Error('Error color not found in current or default theme'); + })(), + warning: + palette.warning?.main || + defaultPalette.warning?.main || + (() => { + throw new Error('Warning color not found in current or default theme'); + })(), + success: + palette.success?.main || + defaultPalette.success?.main || + (() => { + throw new Error('Success color not found in current or default theme'); + })(), }; } From 71cb5327c54aae23936e404e81bb99234df684d4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 12:53:08 +0200 Subject: [PATCH 116/211] bui-themer: skip gray scale Signed-off-by: Patrik Oldsberg --- .../BuiThemerPage/convertMuiToBuiTheme.ts | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts index dee1c1fd72..dbd60c6ea6 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts +++ b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts @@ -152,12 +152,6 @@ function generateBuiVariables(theme: Mui5Theme): string { variables.push(` --bui-white: ${palette.common.white};`); } - // Gray scale - generate from primary color or use defaults - const grayScale = generateGrayScale(palette.mode === 'dark'); - Object.entries(grayScale).forEach(([key, value]) => { - variables.push(` --bui-gray-${key}: ${value};`); - }); - // Background colors if (palette.background?.default) { variables.push(` --bui-bg: ${palette.background.default};`); @@ -200,35 +194,6 @@ function generateBuiVariables(theme: Mui5Theme): string { return variables.join('\n'); } -/** - * Generates gray scale colors - */ -function generateGrayScale(isDark: boolean): Record { - if (isDark) { - return { - '1': '#191919', - '2': '#242424', - '3': '#373737', - '4': '#464646', - '5': '#575757', - '6': '#7b7b7b', - '7': '#9e9e9e', - '8': '#b4b4b4', - }; - } - - return { - '1': '#f8f8f8', - '2': '#ececec', - '3': '#d9d9d9', - '4': '#c1c1c1', - '5': '#9e9e9e', - '6': '#8c8c8c', - '7': '#757575', - '8': '#595959', - }; -} - /** * Generates surface background colors */ From 6c0a51b3f7606d42bf1d27eb38767233132fbd97 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 15:07:43 +0200 Subject: [PATCH 117/211] bui-themer: simplify theme conversion Signed-off-by: Patrik Oldsberg --- plugins/bui-themer/package.json | 1 + .../convertMuiToBuiTheme.test.ts | 104 +----- .../BuiThemerPage/convertMuiToBuiTheme.ts | 306 +++--------------- yarn.lock | 1 + 4 files changed, 45 insertions(+), 367 deletions(-) diff --git a/plugins/bui-themer/package.json b/plugins/bui-themer/package.json index ff97c92ecb..64cad56775 100644 --- a/plugins/bui-themer/package.json +++ b/plugins/bui-themer/package.json @@ -35,6 +35,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.61", "@mui/material": "^7.3.2", + "@mui/system": "^7.3.2", "react-use": "^17.2.4" }, "peerDependencies": { diff --git a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts index ffbd4fc392..7c11dd7d25 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts +++ b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts @@ -54,7 +54,8 @@ describe('convertMuiToBuiTheme', () => { expect(result).toContain(':root {'); expect(result).toContain('--bui-font-regular: Roboto, sans-serif;'); - expect(result).toContain('--bui-space: 8px;'); + // Spacing is skipped for default 8px + expect(result).not.toContain('--bui-space:'); expect(result).toContain('--bui-radius-3: 4px;'); expect(result).toContain('--bui-bg: #f5f5f5;'); expect(result).toContain('--bui-bg-surface-1: #ffffff;'); @@ -122,38 +123,6 @@ describe('convertMuiToBuiTheme', () => { expect(result).toContain('--bui-white: #fff;'); }); - it('should generate proper gray scale for light theme', () => { - const theme = createTheme({ - palette: { - mode: 'light', - primary: { - main: '#1976d2', - }, - }, - }); - - const result = convertMuiToBuiTheme(theme); - - expect(result).toContain('--bui-gray-1: #f8f8f8;'); - expect(result).toContain('--bui-gray-8: #595959;'); - }); - - it('should generate proper gray scale for dark theme', () => { - const theme = createTheme({ - palette: { - mode: 'dark', - primary: { - main: '#90caf9', - }, - }, - }); - - const result = convertMuiToBuiTheme(theme); - - expect(result).toContain('--bui-gray-1: #191919;'); - expect(result).toContain('--bui-gray-8: #b4b4b4;'); - }); - it('should generate surface colors correctly', () => { const theme = createTheme({ palette: { @@ -180,7 +149,7 @@ describe('convertMuiToBuiTheme', () => { const result = convertMuiToBuiTheme(theme); expect(result).toContain('--bui-bg-solid: #1976d2;'); - expect(result).toContain('--bui-bg-solid-hover: #115293;'); + expect(result).toContain('--bui-bg-solid-hover: rgb(21, 100, 179);'); expect(result).toContain('--bui-bg-danger: #ffcdd2;'); expect(result).toContain('--bui-bg-warning: #ffe0b2;'); expect(result).toContain('--bui-bg-success: #c8e6c9;'); @@ -237,8 +206,7 @@ describe('convertMuiToBuiTheme', () => { const result = convertMuiToBuiTheme(theme); - expect(result).toContain('--bui-border: rgba(0, 0, 0, 0.1);'); - expect(result).toContain('--bui-border-hover: rgba(0, 0, 0, 0.2);'); + // Base border colors are no longer generated expect(result).toContain('--bui-border-danger: #f44336;'); expect(result).toContain('--bui-border-warning: #ff9800;'); expect(result).toContain('--bui-border-success: #4caf50;'); @@ -251,7 +219,7 @@ describe('convertMuiToBuiTheme', () => { const result = convertMuiToBuiTheme(theme); - expect(result).toContain('--bui-space: 4px;'); + expect(result).toContain('--bui-space: calc(4px * 0.5);'); }); it('should handle string-based spacing', () => { @@ -261,7 +229,7 @@ describe('convertMuiToBuiTheme', () => { const result = convertMuiToBuiTheme(theme); - expect(result).toContain('--bui-space: calc(1 * 8px);'); + expect(result).toContain('--bui-space: calc(calc(1 * 8px) * 0.5);'); }); it('should handle string-based border radius', () => { @@ -275,64 +243,4 @@ describe('convertMuiToBuiTheme', () => { expect(result).toContain('--bui-radius-3: 8px;'); }); - - describe('early validation', () => { - it('should throw error when theme is undefined', () => { - expect(() => convertMuiToBuiTheme(undefined as any)).toThrow( - 'Theme is required', - ); - }); - - it('should throw error when theme palette is missing', () => { - const theme = { typography: {}, spacing: () => 8, shape: {} } as any; - expect(() => convertMuiToBuiTheme(theme)).toThrow( - 'Theme palette is required', - ); - }); - - it('should throw error when theme palette mode is missing', () => { - const theme = { - palette: {}, - typography: {}, - spacing: () => 8, - shape: {}, - } as any; - expect(() => convertMuiToBuiTheme(theme)).toThrow( - 'Theme palette mode is required', - ); - }); - - it('should throw error when theme typography is missing', () => { - const theme = { - palette: { mode: 'light' }, - spacing: () => 8, - shape: {}, - } as any; - expect(() => convertMuiToBuiTheme(theme)).toThrow( - 'Theme typography is required', - ); - }); - - it('should throw error when theme spacing is missing', () => { - const theme = { - palette: { mode: 'light' }, - typography: {}, - shape: {}, - } as any; - expect(() => convertMuiToBuiTheme(theme)).toThrow( - 'Theme spacing is required', - ); - }); - - it('should throw error when theme shape is missing', () => { - const theme = { - palette: { mode: 'light' }, - typography: {}, - spacing: () => 8, - } as any; - expect(() => convertMuiToBuiTheme(theme)).toThrow( - 'Theme shape is required', - ); - }); - }); }); diff --git a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts index dbd60c6ea6..1d6e581b57 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts +++ b/plugins/bui-themer/src/components/BuiThemerPage/convertMuiToBuiTheme.ts @@ -15,7 +15,7 @@ */ import { Theme as Mui5Theme } from '@mui/material/styles'; -import { themes } from '@backstage/theme'; +import { blend, alpha } from '@mui/system/colorManipulator'; export interface ConvertMuiToBuiThemeOptions { /** @@ -38,31 +38,6 @@ export function convertMuiToBuiTheme( theme: Mui5Theme, options: ConvertMuiToBuiThemeOptions = {}, ): string { - // Early validation of required theme properties - if (!theme) { - throw new Error('Theme is required'); - } - - if (!theme.palette) { - throw new Error('Theme palette is required'); - } - - if (!theme.palette.mode) { - throw new Error('Theme palette mode is required'); - } - - if (!theme.typography) { - throw new Error('Theme typography is required'); - } - - if (!theme.spacing) { - throw new Error('Theme spacing is required'); - } - - if (!theme.shape) { - throw new Error('Theme shape is required'); - } - const { themeId, includeThemeId = false } = options; const isDark = theme.palette.mode === 'dark'; @@ -125,13 +100,10 @@ function generateBuiVariables(theme: Mui5Theme): string { } }); - // Spacing - map MUI spacing to BUI spacing scale - if (theme.spacing) { - const spacingUnit = - typeof theme.spacing === 'function' ? theme.spacing(1) : theme.spacing; - const spacingValue = - typeof spacingUnit === 'number' ? `${spacingUnit}px` : spacingUnit; - variables.push(` --bui-space: ${spacingValue};`); + const spacing = theme.spacing(1); + // Skip spacing if the theme is using the default + if (spacing !== '8px') { + variables.push(` --bui-space: calc(${spacing} * 0.5);`); } // Border radius @@ -161,8 +133,21 @@ function generateBuiVariables(theme: Mui5Theme): string { } // Generate surface colors - const surfaceColors = generateSurfaceColors(palette); - Object.entries(surfaceColors).forEach(([key, value]) => { + Object.entries({ + 'surface-1': palette.background.default, + 'surface-2': palette.background.paper, + solid: palette.primary.main, + 'solid-hover': blend(palette.primary.main, palette.primary.dark, 0.5), + 'solid-pressed': palette.primary.dark, + 'solid-disabled': palette.action.disabledBackground, + tint: 'transparent', + 'tint-hover': alpha(palette.primary.main, 0.4), + 'tint-pressed': alpha(palette.primary.main, 0.6), + 'tint-disabled': palette.action.disabledBackground, + danger: palette.error.light, + warning: palette.warning.light, + success: palette.success.light, + }).forEach(([key, value]) => { variables.push(` --bui-bg-${key}: ${value};`); }); @@ -175,14 +160,27 @@ function generateBuiVariables(theme: Mui5Theme): string { } // Generate foreground colors - const foregroundColors = generateForegroundColors(palette); - Object.entries(foregroundColors).forEach(([key, value]) => { + Object.entries({ + link: palette.primary.main, + 'link-hover': palette.primary.dark, + disabled: palette.text.disabled, + solid: palette.text.primary, + 'solid-disabled': palette.action.disabled, + tint: palette.primary.main, + 'tint-disabled': palette.action.disabled, + danger: palette.error.main, + warning: palette.warning.main, + success: palette.success.main, + }).forEach(([key, value]) => { variables.push(` --bui-fg-${key}: ${value};`); }); // Border colors - const borderColors = generateBorderColors(palette); - Object.entries(borderColors).forEach(([key, value]) => { + Object.entries({ + danger: palette.error.main, + warning: palette.warning.main, + success: palette.success.main, + }).forEach(([key, value]) => { variables.push(` --bui-border${key ? `-${key}` : ''}: ${value};`); }); @@ -193,233 +191,3 @@ function generateBuiVariables(theme: Mui5Theme): string { return variables.join('\n'); } - -/** - * Generates surface background colors - */ -function generateSurfaceColors( - palette: Mui5Theme['palette'], -): Record { - const isDark = palette.mode === 'dark'; - - // Get the default Backstage theme for fallback values - const defaultTheme = isDark ? themes.dark : themes.light; - const defaultMuiTheme = defaultTheme.getTheme('v5'); - - if (!defaultMuiTheme) { - throw new Error( - `Failed to get MUI v5 theme from Backstage ${ - isDark ? 'dark' : 'light' - } theme`, - ); - } - - const defaultPalette = defaultMuiTheme.palette; - if (!defaultPalette) { - throw new Error( - `Failed to get palette from Backstage ${isDark ? 'dark' : 'light'} theme`, - ); - } - - // Helper function to get tint colors - const getTintColor = (opacity: string) => { - const primaryColor = palette.primary?.main || defaultPalette.primary?.main; - if (!primaryColor) { - throw new Error('Primary color not found in current or default theme'); - } - return `${primaryColor}${opacity}`; - }; - - // Helper function to get colors based on theme mode - const getThemeColor = (lightColor: string, darkColor: string) => { - return isDark ? darkColor : lightColor; - }; - - return { - 'surface-2': getThemeColor('#ececec', '#242424'), - solid: - palette.primary?.main || - defaultPalette.primary?.main || - (() => { - throw new Error('Primary color not found in current or default theme'); - })(), - 'solid-hover': - palette.primary?.dark || - defaultPalette.primary?.dark || - (() => { - throw new Error( - 'Primary dark color not found in current or default theme', - ); - })(), - 'solid-pressed': - palette.primary?.dark || - defaultPalette.primary?.dark || - (() => { - throw new Error( - 'Primary dark color not found in current or default theme', - ); - })(), - 'solid-disabled': getThemeColor('#ebebeb', '#222222'), - tint: 'transparent', - 'tint-hover': getTintColor('40'), - 'tint-pressed': getTintColor('60'), - 'tint-disabled': getThemeColor('#ebebeb', 'transparent'), - danger: - palette.error?.light || - defaultPalette.error?.light || - (() => { - throw new Error( - 'Error light color not found in current or default theme', - ); - })(), - warning: - palette.warning?.light || - defaultPalette.warning?.light || - (() => { - throw new Error( - 'Warning light color not found in current or default theme', - ); - })(), - success: - palette.success?.light || - defaultPalette.success?.light || - (() => { - throw new Error( - 'Success light color not found in current or default theme', - ); - })(), - }; -} - -/** - * Generates foreground colors - */ -function generateForegroundColors( - palette: Mui5Theme['palette'], -): Record { - const isDark = palette.mode === 'dark'; - - // Get the default Backstage theme for fallback values - const defaultTheme = isDark ? themes.dark : themes.light; - const defaultMuiTheme = defaultTheme.getTheme('v5'); - - if (!defaultMuiTheme) { - throw new Error( - `Failed to get MUI v5 theme from Backstage ${ - isDark ? 'dark' : 'light' - } theme`, - ); - } - - const defaultPalette = defaultMuiTheme.palette; - if (!defaultPalette) { - throw new Error( - `Failed to get palette from Backstage ${isDark ? 'dark' : 'light'} theme`, - ); - } - - return { - link: - palette.primary?.main || - defaultPalette.primary?.main || - (() => { - throw new Error('Primary color not found in current or default theme'); - })(), - 'link-hover': - palette.primary?.dark || - defaultPalette.primary?.dark || - (() => { - throw new Error( - 'Primary dark color not found in current or default theme', - ); - })(), - disabled: - palette.text?.disabled || - defaultPalette.text?.disabled || - (() => { - throw new Error( - 'Text disabled color not found in current or default theme', - ); - })(), - solid: isDark ? '#101821' : '#ffffff', - 'solid-disabled': isDark ? '#575757' : '#9c9c9c', - tint: - palette.primary?.main || - defaultPalette.primary?.main || - (() => { - throw new Error('Primary color not found in current or default theme'); - })(), - 'tint-disabled': isDark ? '#575757' : '#9e9e9e', - danger: - palette.error?.main || - defaultPalette.error?.main || - (() => { - throw new Error('Error color not found in current or default theme'); - })(), - warning: - palette.warning?.main || - defaultPalette.warning?.main || - (() => { - throw new Error('Warning color not found in current or default theme'); - })(), - success: - palette.success?.main || - defaultPalette.success?.main || - (() => { - throw new Error('Success color not found in current or default theme'); - })(), - }; -} - -/** - * Generates border colors - */ -function generateBorderColors( - palette: Mui5Theme['palette'], -): Record { - const isDark = palette.mode === 'dark'; - - // Get the default Backstage theme for fallback values - const defaultTheme = isDark ? themes.dark : themes.light; - const defaultMuiTheme = defaultTheme.getTheme('v5'); - - if (!defaultMuiTheme) { - throw new Error( - `Failed to get MUI v5 theme from Backstage ${ - isDark ? 'dark' : 'light' - } theme`, - ); - } - - const defaultPalette = defaultMuiTheme.palette; - if (!defaultPalette) { - throw new Error( - `Failed to get palette from Backstage ${isDark ? 'dark' : 'light'} theme`, - ); - } - - return { - '': isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.1)', - hover: isDark ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.2)', - pressed: isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.4)', - disabled: isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)', - danger: - palette.error?.main || - defaultPalette.error?.main || - (() => { - throw new Error('Error color not found in current or default theme'); - })(), - warning: - palette.warning?.main || - defaultPalette.warning?.main || - (() => { - throw new Error('Warning color not found in current or default theme'); - })(), - success: - palette.success?.main || - defaultPalette.success?.main || - (() => { - throw new Error('Success color not found in current or default theme'); - })(), - }; -} diff --git a/yarn.lock b/yarn.lock index 41f7dc6d64..9dd2bd9e16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4348,6 +4348,7 @@ __metadata: "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:^4.0.0-alpha.61" "@mui/material": "npm:^7.3.2" + "@mui/system": "npm:^7.3.2" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^14.0.0" "@testing-library/user-event": "npm:^14.0.0" From 09e65c6266b4458bb40f303e919de87dbe44f01f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 15:43:15 +0200 Subject: [PATCH 118/211] bui-themer: switch to horizontal layout Signed-off-by: Patrik Oldsberg --- .../BuiThemerPage/BuiThemerPage.tsx | 116 ++++++++++-------- 1 file changed, 65 insertions(+), 51 deletions(-) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index b36c550081..4ae25b60ce 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -24,10 +24,8 @@ import { Card, Container, Flex, - Grid, HeaderPage, Text, - TextField, Switch, } from '@backstage/ui'; import { @@ -136,16 +134,16 @@ function ThemeContent({ themeId, themeTitle, variant }: ThemeContentProps) { return ( - - + + - {themeTitle} + {themeTitle} {variant} theme - + - + @@ -172,53 +170,70 @@ function ThemeContent({ themeId, themeTitle, variant }: ThemeContentProps) { Generated CSS: - - - - {isPreviewMode && ( - + {generatedCss} + + + + {isPreviewMode && ( + + Live Preview: - Solid Button - - - Tint Button - - - Surface Card + + + Solid Button + + + Tint Button + + + Surface Card + + )} @@ -270,18 +285,17 @@ export function BuiThemerPage() { ) : ( - + {installedThemes.map(theme => ( - - - + ))} - + )} From d5c170eaf48e916ec95c4ef71ce30d332ee40458 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 16:32:07 +0200 Subject: [PATCH 119/211] bui-themer: extract mui theme and then unmount theme provider Signed-off-by: Patrik Oldsberg --- .../src/unified/UnifiedThemeProvider.tsx | 14 +++- .../BuiThemerPage/BuiThemerPage.tsx | 66 +++++++++++-------- 2 files changed, 49 insertions(+), 31 deletions(-) diff --git a/packages/theme/src/unified/UnifiedThemeProvider.tsx b/packages/theme/src/unified/UnifiedThemeProvider.tsx index a80e36cf35..006c45605e 100644 --- a/packages/theme/src/unified/UnifiedThemeProvider.tsx +++ b/packages/theme/src/unified/UnifiedThemeProvider.tsx @@ -74,12 +74,22 @@ export function UnifiedThemeProvider( const themeName = 'backstage'; useEffect(() => { + const oldMode = document.body.getAttribute('data-theme-mode'); + const oldName = document.body.getAttribute('data-theme-name'); document.body.setAttribute('data-theme-mode', themeMode); document.body.setAttribute('data-theme-name', themeName); return () => { - document.body.removeAttribute('data-theme-mode'); - document.body.removeAttribute('data-theme-name'); + if (oldMode) { + document.body.setAttribute('data-theme-mode', oldMode); + } else { + document.body.removeAttribute('data-theme-mode'); + } + if (oldName) { + document.body.setAttribute('data-theme-name', oldName); + } else { + document.body.removeAttribute('data-theme-name'); + } }; }, [themeMode, themeName]); diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 4ae25b60ce..4df3aae006 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -15,9 +15,9 @@ */ import { useMemo, useState, useCallback, useEffect } from 'react'; -import { useApi } from '@backstage/core-plugin-api'; +import { AppTheme, useApi } from '@backstage/core-plugin-api'; import { appThemeApiRef } from '@backstage/core-plugin-api'; -import { useTheme } from '@mui/material/styles'; +import { Theme, useTheme } from '@mui/material/styles'; import { Box, Button, @@ -33,13 +33,6 @@ import { ConvertMuiToBuiThemeOptions, } from './convertMuiToBuiTheme'; -interface ThemePreviewProps { - themeId: string; - themeTitle: string; - variant: 'light' | 'dark'; - Provider: React.ComponentType<{ children: React.ReactNode }>; -} - // Memoization cache for generated CSS const cssCache = new Map(); @@ -47,13 +40,18 @@ interface ThemeContentProps { themeId: string; themeTitle: string; variant: 'light' | 'dark'; + muiTheme: Theme; } -function ThemeContent({ themeId, themeTitle, variant }: ThemeContentProps) { +function ThemeContent({ + themeId, + themeTitle, + variant, + muiTheme, +}: ThemeContentProps) { const [generatedCss, setGeneratedCss] = useState(''); const [isPreviewMode, setIsPreviewMode] = useState(false); const [includeThemeId, setIncludeThemeId] = useState(false); - const muiTheme = useTheme(); const css = useMemo(() => { // Create cache key based on theme properties and options @@ -243,23 +241,29 @@ function ThemeContent({ themeId, themeTitle, variant }: ThemeContentProps) { ); } -function ThemePreview({ - themeId, - themeTitle, - variant, - Provider, -}: ThemePreviewProps) { +function MuiThemeExtractor(props: { + appTheme: AppTheme; + children: (theme: Theme) => JSX.Element; +}): JSX.Element { + const { appTheme, children } = props; + const [theme, setTheme] = useState(null); + const { Provider } = appTheme; + if (theme) { + return children(theme); + } + return ( - + ); } +function MuiThemeExtractorInner(props: { setTheme: (theme: Theme) => void }) { + props.setTheme(useTheme()); + return null; +} + export function BuiThemerPage() { const appThemeApi = useApi(appThemeApiRef); const installedThemes = appThemeApi.getInstalledThemes(); @@ -287,13 +291,17 @@ export function BuiThemerPage() { ) : ( {installedThemes.map(theme => ( - + + {muiTheme => ( + + )} + ))} )} From 5161dbc757b6ef3c59f9420cef27321d74b9dab2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 16:37:44 +0200 Subject: [PATCH 120/211] bui-themer: remove unnecessary css cache Signed-off-by: Patrik Oldsberg --- .../BuiThemerPage/BuiThemerPage.tsx | 39 ++----------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 4df3aae006..061ea379cd 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -28,13 +28,7 @@ import { Text, Switch, } from '@backstage/ui'; -import { - convertMuiToBuiTheme, - ConvertMuiToBuiThemeOptions, -} from './convertMuiToBuiTheme'; - -// Memoization cache for generated CSS -const cssCache = new Map(); +import { convertMuiToBuiTheme } from './convertMuiToBuiTheme'; interface ThemeContentProps { themeId: string; @@ -54,37 +48,10 @@ function ThemeContent({ const [includeThemeId, setIncludeThemeId] = useState(false); const css = useMemo(() => { - // Create cache key based on theme properties and options - const cacheKey = `${themeId}-${includeThemeId}-${JSON.stringify({ - palette: muiTheme.palette, - typography: muiTheme.typography, - spacing: muiTheme.spacing, - shape: muiTheme.shape, - })}`; - - // Check cache first - if (cssCache.has(cacheKey)) { - return cssCache.get(cacheKey)!; - } - - const options: ConvertMuiToBuiThemeOptions = { + return convertMuiToBuiTheme(muiTheme, { themeId, includeThemeId, - }; - const result = convertMuiToBuiTheme(muiTheme, options); - - // Cache the result - cssCache.set(cacheKey, result); - - // Clean up old cache entries (keep only last 50) - if (cssCache.size > 50) { - const firstKey = cssCache.keys().next().value; - if (firstKey) { - cssCache.delete(firstKey); - } - } - - return result; + }); }, [muiTheme, themeId, includeThemeId]); useEffect(() => { From 8ceba35b280d7daac1ad66ae9fab1b954f175998 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 16:42:36 +0200 Subject: [PATCH 121/211] bui-themer: switch theme cards to use tabs Signed-off-by: Patrik Oldsberg --- .../BuiThemerPage/BuiThemerPage.tsx | 184 +++++++++++------- 1 file changed, 109 insertions(+), 75 deletions(-) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 061ea379cd..90e7d21902 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -27,6 +27,10 @@ import { HeaderPage, Text, Switch, + Tabs, + TabList, + Tab, + TabPanel, } from '@backstage/ui'; import { convertMuiToBuiTheme } from './convertMuiToBuiTheme'; @@ -46,6 +50,7 @@ function ThemeContent({ const [generatedCss, setGeneratedCss] = useState(''); const [isPreviewMode, setIsPreviewMode] = useState(false); const [includeThemeId, setIncludeThemeId] = useState(false); + const [activeTab, setActiveTab] = useState('css'); const css = useMemo(() => { return convertMuiToBuiTheme(muiTheme, { @@ -123,85 +128,114 @@ function ThemeContent({ - - - - Generated CSS: - - - {generatedCss} - - + setActiveTab(key as string)} + > + + Generated CSS + Live Preview + - {isPreviewMode && ( - - - Live Preview: - - - - - Solid Button - - - Tint Button - - - Surface Card - - + + + + Generated CSS: + + + {generatedCss} + - - )} + + + + + + + + + {isPreviewMode ? ( + + + Live Preview: + + + + + Solid Button + + + Tint Button + + + Surface Card + + + + + ) : ( + + + Click "Start Preview" to see the theme applied to sample + components. + + + )} + + + From 3cb8a3aa88f004975f75d6f5a808425860dd92c3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 13 Sep 2025 17:19:13 +0200 Subject: [PATCH 122/211] bui-themer: new and improved preview Signed-off-by: Patrik Oldsberg --- .../BuiThemerPage/BuiThemerPage.tsx | 227 ++++++++++-------- 1 file changed, 133 insertions(+), 94 deletions(-) diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 90e7d21902..a65c35d665 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -31,6 +31,13 @@ import { TabList, Tab, TabPanel, + TextField, + Select, + Checkbox, + Radio, + RadioGroup, + CardHeader, + CardBody, } from '@backstage/ui'; import { convertMuiToBuiTheme } from './convertMuiToBuiTheme'; @@ -41,6 +48,128 @@ interface ThemeContentProps { muiTheme: Theme; } +interface IsolatedPreviewProps { + mode: 'light' | 'dark'; + css: string; +} + +function BuiThemePreview({ mode, css }: IsolatedPreviewProps) { + return ( +
line.trim().startsWith('--bui-')) + .map(line => { + const [key, value] = line.trim().split(':'); + return [key?.trim(), value?.replace(';', '').trim()]; + }) + .filter(([key, value]) => key && value), + ), + width: '100%', + backgroundColor: 'var(--bui-bg-surface-2)', + padding: 'var(--bui-space-3)', + borderRadius: 'var(--bui-radius-2)', + }} + > + + + Theme Preview + + + This preview shows how your theme will look with various Backstage + UI components + + + + + + Button Variants + + + + + + + + + + + Form Inputs + + + + + + + + Option 1 + + Option 2 + + Option 3 + + + + + + + Surface Variations + + + + Surface 1 + + + Surface 2 + + + Solid + + + + + +
+ ); +} diff --git a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx index 856790161b..bb3e797bb1 100644 --- a/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx +++ b/plugins/bui-themer/src/components/BuiThemerPage/BuiThemerPage.tsx @@ -14,264 +14,11 @@ * limitations under the License. */ -import { useMemo, useState, useCallback } from 'react'; -import { AppTheme, useApi } from '@backstage/core-plugin-api'; +import { useApi } from '@backstage/core-plugin-api'; import { appThemeApiRef } from '@backstage/core-plugin-api'; -import { Theme, useTheme } from '@mui/material/styles'; -import { - Box, - Button, - Card, - Container, - Flex, - HeaderPage, - Text, - Tabs, - TabList, - Tab, - TabPanel, - TextField, - Select, - Checkbox, - Radio, - RadioGroup, - CardHeader, - CardBody, -} from '@backstage/ui'; -import { convertMuiToBuiTheme } from './convertMuiToBuiTheme'; - -interface ThemeContentProps { - themeId: string; - themeTitle: string; - variant: 'light' | 'dark'; - muiTheme: Theme; -} - -interface IsolatedPreviewProps { - mode: 'light' | 'dark'; - styleObject: Record; -} - -function BuiThemePreview({ mode, styleObject }: IsolatedPreviewProps) { - return ( -
- - - Theme Preview - - - This preview shows how your theme will look with various Backstage - UI components - - - - - - Button Variants - - - - - - - - - - - Form Inputs - - - -