From 34075b7b812ebe6f5fd97c52c8900ffec109259c Mon Sep 17 00:00:00 2001 From: debsmita1 Date: Mon, 10 Mar 2025 19:57:11 +0530 Subject: [PATCH 1/2] feat: allow overriding build info --- .../resources/config_map/app-config-rhdh.yaml | 8 ++ docs/customization.md | 32 +++-- .../plugins/user-settings-info-card.spec.ts | 34 +++++ packages/app/config.d.ts | 10 ++ packages/app/src/build-metadata.json | 10 +- .../components/UserSettings/InfoCard.test.tsx | 134 +++++++++++++++++- .../src/components/UserSettings/InfoCard.tsx | 40 ++++-- packages/app/src/types/types.ts | 6 + 8 files changed, 246 insertions(+), 28 deletions(-) create mode 100644 e2e-tests/playwright/e2e/plugins/user-settings-info-card.spec.ts diff --git a/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml b/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml index 00d29ca222..5fe2e46d35 100644 --- a/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml +++ b/.ibm/pipelines/resources/config_map/app-config-rhdh.yaml @@ -181,3 +181,11 @@ argocd: token: temp permission: enabled: false + +buildInfo: + title: 'RHDH Build info' + card: + TechDocs builder: 'local' + Authentication provider: 'Github' + RBAC: disabled + full: true diff --git a/docs/customization.md b/docs/customization.md index b88c40008d..38f681e59a 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -91,9 +91,9 @@ app: branding: theme: light: - primaryColor: '#38be8b' + primaryColor: "#38be8b" dark: - primaryColor: '#ab75cf' + primaryColor: "#ab75cf" ``` ![Example Light Mode Primary Color](images/example-light-mode-primary-color.png) @@ -110,11 +110,11 @@ app: branding: theme: light: - headerColor1: 'hsl(204 100% 71%)' - headerColor2: 'color(a98-rgb 1 0 0)' + headerColor1: "hsl(204 100% 71%)" + headerColor2: "color(a98-rgb 1 0 0)" dark: - headerColor1: '#0000d0' - headerColor2: 'rgb(255 246 140)' + headerColor1: "#0000d0" + headerColor2: "rgb(255 246 140)" ``` ![Example Light Mode Banner](images/example-light-mode-banner.png) @@ -129,9 +129,9 @@ app: branding: theme: light: - navigationIndicatorColor: '#be0000' + navigationIndicatorColor: "#be0000" dark: - navigationIndicatorColor: '#f4eea9' + navigationIndicatorColor: "#f4eea9" ``` ![Example Light Mode Sidebar Indicator](images/example-sidebar-indicator-light.png) @@ -165,3 +165,19 @@ app: If support is not configured, it would look as below. ![Example Support Not Configured](images/support-not-configured.png) + +## Customizing Build info content in the user settings page + +To customize the build info content in the user settings page, provide your content to the `buildInfo` field in the `app-config.yaml` file. + +Example configurations: + +``` +buildInfo: + title: + card: + TechDocs builder: 'local' + Authentication provider: 'Github' + RBAC: disabled + full: true # If set to true, only the information specified in this configuration will be displayed. If set to false, the provided details will be shown along with the build versions. By default it will only display the configured information. +``` diff --git a/e2e-tests/playwright/e2e/plugins/user-settings-info-card.spec.ts b/e2e-tests/playwright/e2e/plugins/user-settings-info-card.spec.ts new file mode 100644 index 0000000000..efbae8f4d0 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/user-settings-info-card.spec.ts @@ -0,0 +1,34 @@ +import { test } from "@playwright/test"; +import { Common } from "../../utils/common"; +import { UIhelper } from "../../utils/ui-helper"; +import { UI_HELPER_ELEMENTS } from "../../support/pageObjects/global-obj"; + +test.describe("Test user settings info card", () => { + let uiHelper: UIhelper; + + test.beforeEach(async ({ page }) => { + const common = new Common(page); + await common.loginAsGuest(); + + uiHelper = new UIhelper(page); + }); + + test("Check if customized build info is rendered", async ({ page }) => { + await uiHelper.openSidebar("Home"); + page.getByText("Guest").click(); + await page.getByRole("menuitem", { name: "Settings" }).click(); + await uiHelper.verifyTextInSelector( + UI_HELPER_ELEMENTS.MuiCardHeader, + "RHDH Build info", + ); + await uiHelper.verifyTextInSelector( + UI_HELPER_ELEMENTS.MuiCard("RHDH Build info"), + "TechDocs builder: local\nAuthentication provider: Github", + ); + await page.getByTitle("Show more").click(); + await uiHelper.verifyTextInSelector( + UI_HELPER_ELEMENTS.MuiCard("RHDH Build info"), + "TechDocs builder: local\nAuthentication provider: Github\nRBAC: disabled", + ); + }); +}); diff --git a/packages/app/config.d.ts b/packages/app/config.d.ts index 0ff2275be6..5bfcb1d38e 100644 --- a/packages/app/config.d.ts +++ b/packages/app/config.d.ts @@ -188,4 +188,14 @@ export interface Config { * @visibility frontend */ includeTransitiveGroupOwnership?: boolean; + + /** + * Allows you to customize RHDH Metadata card + * @deepVisibility frontend + */ + buildInfo?: { + title: string; + card: { [key: string]: string }; + full?: boolean; + }; } diff --git a/packages/app/src/build-metadata.json b/packages/app/src/build-metadata.json index d7893e4c8c..47d2628851 100644 --- a/packages/app/src/build-metadata.json +++ b/packages/app/src/build-metadata.json @@ -1,8 +1,8 @@ { "title": "RHDH Metadata", - "card": [ - "RHDH Version: 1.6.0", - "Backstage Version: 1.36.1", - "Last Commit: repo @ 2025-03-17T17:01:16Z" - ] + "card": { + "RHDH Version": "1.6.0", + "Backstage Version": "1.36.1", + "Last Commit": "repo @ 2025-03-17T17:01:16Z" + } } \ No newline at end of file diff --git a/packages/app/src/components/UserSettings/InfoCard.test.tsx b/packages/app/src/components/UserSettings/InfoCard.test.tsx index a8bc659658..d5e853d342 100644 --- a/packages/app/src/components/UserSettings/InfoCard.test.tsx +++ b/packages/app/src/components/UserSettings/InfoCard.test.tsx @@ -1,4 +1,9 @@ -import { renderInTestApp } from '@backstage/test-utils'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { + mockApis, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; import { userEvent } from '@testing-library/user-event'; @@ -6,15 +11,138 @@ import { InfoCard } from './InfoCard'; describe('InfoCard', () => { it('should render essential versions by default', async () => { - const renderResult = await renderInTestApp(); + const mockConfig = mockApis.config({ + data: { + buildInfo: {}, + }, + }); + const renderResult = await renderInTestApp( + + + , + ); expect(renderResult.getByText(/RHDH Version/)).toBeInTheDocument(); expect(renderResult.getByText(/Backstage Version/)).toBeInTheDocument(); }); it('should hide the build time by default and show it on click', async () => { - const renderResult = await renderInTestApp(); + const mockConfig = mockApis.config({ + data: { + buildInfo: {}, + }, + }); + const renderResult = await renderInTestApp( + + + , + ); expect(renderResult.queryByText(/Last Commit/)).toBeNull(); await userEvent.click(renderResult.getByText(/RHDH Version/)); expect(renderResult.getByText(/Last Commit/)).toBeInTheDocument(); }); + + it('should render the customized values when build info is configured', async () => { + const mockConfig = mockApis.config({ + data: { + buildInfo: { + title: 'RHDH Build info', + card: { + 'TechDocs builder': 'local', + 'Authentication provider': 'Github', + RBAC: 'disabled', + }, + }, + }, + }); + const renderResult = await renderInTestApp( + + + , + ); + expect(renderResult.queryByText(/TechDocs builder/)).toBeInTheDocument(); + expect( + renderResult.queryByText(/Authentication provider/), + ).toBeInTheDocument(); + await userEvent.click(renderResult.getByText(/TechDocs builder/)); + expect(renderResult.getByText(/RBAC/)).toBeInTheDocument(); + expect(renderResult.queryByText(/Last Commit/)).not.toBeInTheDocument(); + }); + + it('should append the customized values along with RHDH versions when build info is configured with `full` set to false', async () => { + const mockConfig = mockApis.config({ + data: { + buildInfo: { + title: 'RHDH Build info', + card: { + 'TechDocs builder': 'local', + 'Authentication provider': 'Github', + RBAC: 'disabled', + }, + full: false, + }, + }, + }); + const renderResult = await renderInTestApp( + + + , + ); + expect(renderResult.queryByText(/TechDocs builder/)).toBeInTheDocument(); + expect( + renderResult.queryByText(/Authentication provider/), + ).toBeInTheDocument(); + await userEvent.click(renderResult.getByText(/TechDocs builder/)); + expect(renderResult.getByText(/RBAC/)).toBeInTheDocument(); + expect(renderResult.queryByText(/Last Commit/)).toBeInTheDocument(); + expect(renderResult.queryByText(/RHDH Version/)).toBeInTheDocument(); + }); + + it('should display only the customized values when build info is configured with full set to true, without appending RHDH versions', async () => { + const mockConfig = mockApis.config({ + data: { + buildInfo: { + title: 'RHDH Build info', + card: { + 'TechDocs builder': 'local', + 'Authentication provider': 'Github', + RBAC: 'disabled', + }, + full: true, + }, + }, + }); + const renderResult = await renderInTestApp( + + + , + ); + expect(renderResult.queryByText(/TechDocs builder/)).toBeInTheDocument(); + expect( + renderResult.queryByText(/Authentication provider/), + ).toBeInTheDocument(); + await userEvent.click(renderResult.getByText(/TechDocs builder/)); + expect(renderResult.getByText(/RBAC/)).toBeInTheDocument(); + expect(renderResult.queryByText(/Last Commit/)).not.toBeInTheDocument(); + }); + + it('should fallback to default json if the customized card value is empty', async () => { + const mockConfig = mockApis.config({ + data: { + buildInfo: { + title: 'RHDH Build info', + card: undefined, + }, + }, + }); + const renderResult = await renderInTestApp( + + + , + ); + expect( + renderResult.queryByText(/TechDocs builder/), + ).not.toBeInTheDocument(); + expect(renderResult.getByText(/RHDH Version/)).toBeInTheDocument(); + expect(renderResult.getByText(/Backstage Version/)).toBeInTheDocument(); + }); }); diff --git a/packages/app/src/components/UserSettings/InfoCard.tsx b/packages/app/src/components/UserSettings/InfoCard.tsx index 5abbb1eb5c..49bf03c84a 100644 --- a/packages/app/src/components/UserSettings/InfoCard.tsx +++ b/packages/app/src/components/UserSettings/InfoCard.tsx @@ -4,6 +4,7 @@ import { InfoCard as BSInfoCard, CopyTextButton, } from '@backstage/core-components'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; @@ -11,8 +12,12 @@ import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import buildMetadata from '../../build-metadata.json'; +import { BuildInfo } from '../../types/types'; export const InfoCard = () => { + const config = useApi(configApiRef); + const buildInfo: BuildInfo | undefined = config.getOptional('buildInfo'); + const [showBuildInformation, setShowBuildInformation] = React.useState( () => @@ -32,24 +37,35 @@ export const InfoCard = () => { } }; - let clipboardText = buildMetadata.title; - if (buildMetadata.card?.length) { + const title = buildInfo?.title ?? buildMetadata.title; + + let clipboardText = title; + const buildDetails = Object.entries( + buildInfo?.full === false // append build versions to the object only when buildInfo.full === false + ? { ...buildInfo?.card, ...buildMetadata?.card } + : (buildInfo?.card ?? buildMetadata?.card), + ).map(([key, value]) => `${key}: ${value}`); + if (buildDetails?.length) { clipboardText += '\n\n'; - buildMetadata.card.forEach(text => { + buildDetails.forEach(text => { clipboardText += `${text}\n`; }); } - const filteredCards = showBuildInformation - ? buildMetadata.card - : buildMetadata.card.filter( - text => - text.startsWith('RHDH Version') || - text.startsWith('Backstage Version'), - ); + const filteredContent = () => { + if (buildInfo?.card) { + return buildDetails.slice(0, 2); + } + return buildDetails.filter( + text => + text.startsWith('RHDH Version') || text.startsWith('Backstage Version'), + ); + }; + + const filteredCards = showBuildInformation ? buildDetails : filteredContent(); // Ensure that we show always some information const versionInfo = - filteredCards.length > 0 ? filteredCards.join('\n') : buildMetadata.card[0]; + filteredCards.length > 0 ? filteredCards.join('\n') : buildDetails[0]; /** * Show all build information and automatically select them @@ -80,7 +96,7 @@ export const InfoCard = () => { return ( diff --git a/packages/app/src/types/types.ts b/packages/app/src/types/types.ts index 93bb469f92..71f64543ef 100644 --- a/packages/app/src/types/types.ts +++ b/packages/app/src/types/types.ts @@ -6,3 +6,9 @@ export type LearningPathLink = { minutes?: number; paths?: number; }; + +export type BuildInfo = { + title: string; + card: { [key: string]: string }; + full?: boolean; +}; From 3a1fe014e8b586f0d1226d9fd3879ce7667f1043 Mon Sep 17 00:00:00 2001 From: debsmita1 Date: Fri, 21 Mar 2025 21:36:23 +0530 Subject: [PATCH 2/2] update build-info metadata script --- .github/actions/get-sha/action.yml | 2 +- .rhdh/docker/Dockerfile | 2 +- docker/Dockerfile | 2 +- scripts/update-metadata.mjs | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/actions/get-sha/action.yml b/.github/actions/get-sha/action.yml index cc0297eb6e..05cc70a07c 100644 --- a/.github/actions/get-sha/action.yml +++ b/.github/actions/get-sha/action.yml @@ -42,6 +42,6 @@ runs: if [[ -f packages/app/src/build-metadata.json ]]; then now="$(date -u +%FT%TZ)" sed -i packages/app/src/build-metadata.json -r \ - -e 's|("Last Commit:.+)|"Last Commit: '$REPO' @ '$SHORT_SHA'"|' + -e 's|("Last Commit":.+)|"Last Commit": "'$REPO' @ '$SHORT_SHA'"|' fi echo "short_sha=$SHORT_SHA" >> "$GITHUB_OUTPUT" diff --git a/.rhdh/docker/Dockerfile b/.rhdh/docker/Dockerfile index 42d4649c68..9609511c39 100644 --- a/.rhdh/docker/Dockerfile +++ b/.rhdh/docker/Dockerfile @@ -214,7 +214,7 @@ COPY packages/app/src/build-metadata.json ./packages/app/src/ RUN \ # Downstream only - relabel upstream + append build time into packages/app/src/build-metadata.json now=$(date -u +%FT%TZ); sed -i packages/app/src/build-metadata.json -r \ - -e "s/\"Last Commit: (.+)\"/\"Upstream: \1\", \"Build Time: $now\"/" && \ + -e "s/\"Last Commit\": \"(.+)\"/\"Upstream\": \"\1\", \"Build Time\": \"$now\"/" && \ cat packages/app/src/build-metadata.json; echo && \ "$YARN" build --filter=backend && \ # Downstream only - replace registry refs with cachito ones diff --git a/docker/Dockerfile b/docker/Dockerfile index ec0fb9eda2..f58b90acf0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -162,7 +162,7 @@ RUN rm app-config.yaml && mv app-config.example.yaml app-config.yaml RUN \ # Upstream only - append build time into packages/app/src/build-metadata.json now=$(date -u +%FT%TZ); sed -i packages/app/src/build-metadata.json -r \ - -e "s/(\"Last Commit: .+\")/\1, \"Build Time: $now\"/" && \ + -e "s/(\"Last Commit\": \"(.+)\")/\1, \"Build Time\": \"$now\"/" && \ cat packages/app/src/build-metadata.json; echo RUN "$YARN" build --filter=backend diff --git a/scripts/update-metadata.mjs b/scripts/update-metadata.mjs index ad432364b6..d9ba8d0518 100644 --- a/scripts/update-metadata.mjs +++ b/scripts/update-metadata.mjs @@ -28,11 +28,11 @@ export function updateBuildMetadata(backstageVersion) { const commitTime = new Date().toISOString().slice(0, -5) + 'Z'; // eg., 2024-05-03T12:12:08Z - const card = [ - `RHDH Version: ${rhdhVersion}`, - `Backstage Version: ${backstageVersion}`, - `Last Commit: repo @ ${commitTime}`, - ]; + const card = { + 'RHDH Version': `${rhdhVersion}`, + 'Backstage Version': `${backstageVersion}`, + 'Last Commit': `repo @ ${commitTime}`, + }; buildMetadata.card = card; writeFileSync(