Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(user-settings): allow overriding build info in the user settings page #2564

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/get-sha/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
8 changes: 8 additions & 0 deletions .ibm/pipelines/resources/config_map/app-config-rhdh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,11 @@ argocd:
token: temp
permission:
enabled: false

buildInfo:
title: 'RHDH Build info'
card:
TechDocs builder: 'local'
Authentication provider: 'Github'
RBAC: disabled
full: true
2 changes: 1 addition & 1 deletion .rhdh/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 24 additions & 8 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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: <Specify the title you want to display in the build info card.>
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.
```
34 changes: 34 additions & 0 deletions e2e-tests/playwright/e2e/plugins/user-settings-info-card.spec.ts
Original file line number Diff line number Diff line change
@@ -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",
);
});
});
10 changes: 10 additions & 0 deletions packages/app/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
}
10 changes: 5 additions & 5 deletions packages/app/src/build-metadata.json
Original file line number Diff line number Diff line change
@@ -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": {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file is generated in the build process. Maybe also in the midstream repo on GitLab.

I recommend to check this with @nickboldt, or maybe you can support an object and an array to support the old format (and revert this)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a comment from @nickboldt here about revamping the file
I was under the impression that this file is somehow manually built

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's generated in this repo, then I apply a sed transformation downstream to spiff it up and add more build information (the up and midstream repo commits, and the build time).

      # set build-metadata.json info, using upstream info: ${ROOTPATH}/sync/upstream_SHA_rhdh-hub ==> redhat-developer/rhdh main @ 2ff35695
      sed -i packages/app/src/build-metadata.json -r \
        -e 's|"(Last Commit: )(.+)"|"Upstream: '"$upstream_repo_hub"'", "Midstream: '"$midstream_repo_and_SHA"'", "Build Time: '"$now"'"|'

-- https://gitlab.cee.redhat.com/rhidp/rhdh/-/blob/rhdh-1-rhel-9/build/ci/sync-midstream.sh#L605-607

If you change the file format, just let me know so I can adjust the downstream sed changes. Or even better, throw me a PR to update the script above to properly adopt your changes! :D

"RHDH Version": "1.6.0",
"Backstage Version": "1.36.1",
"Last Commit": "repo @ 2025-03-17T17:01:16Z"
}
}
134 changes: 131 additions & 3 deletions packages/app/src/components/UserSettings/InfoCard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,148 @@
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';

import { InfoCard } from './InfoCard';

describe('InfoCard', () => {
it('should render essential versions by default', async () => {
const renderResult = await renderInTestApp(<InfoCard />);
const mockConfig = mockApis.config({
data: {
buildInfo: {},
},
});
const renderResult = await renderInTestApp(
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
<InfoCard />
</TestApiProvider>,
);
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(<InfoCard />);
const mockConfig = mockApis.config({
data: {
buildInfo: {},
},
});
const renderResult = await renderInTestApp(
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
<InfoCard />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
<InfoCard />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
<InfoCard />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
<InfoCard />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
<InfoCard />
</TestApiProvider>,
);
expect(
renderResult.queryByText(/TechDocs builder/),
).not.toBeInTheDocument();
expect(renderResult.getByText(/RHDH Version/)).toBeInTheDocument();
expect(renderResult.getByText(/Backstage Version/)).toBeInTheDocument();
});
});
40 changes: 28 additions & 12 deletions packages/app/src/components/UserSettings/InfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ 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';
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<boolean>(
() =>
Expand All @@ -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
Expand Down Expand Up @@ -80,7 +96,7 @@ export const InfoCard = () => {

return (
<BSInfoCard
title={buildMetadata.title}
title={title}
action={
// This is a workaround to ensure that the buttons doesn't increase the header size.
<div style={{ position: 'relative' }}>
Expand Down
Loading
Loading