Skip to content
Closed
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
5 changes: 5 additions & 0 deletions workspaces/scorecard/.changeset/puny-eggs-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@red-hat-developer-hub/backstage-plugin-scorecard': patch
---

Added support for the New Frontend System (NFS), including an alpha export for NFS apps and a new `app-next` package.
3 changes: 2 additions & 1 deletion workspaces/scorecard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
},
"scripts": {
"start": "backstage-cli repo start",
"start:next": "backstage-cli repo start packages/app-next packages/backend",
"start-backend": "yarn workspace backend start",
"tsc": "tsc",
"tsc:full": "tsc --skipLibCheck true --incremental false",
"build:backend": "yarn workspace backend build",
"build:all": "backstage-cli repo build --all",
"build:api-reports": "yarn build:api-reports:only",
"build:api-reports:only": "backstage-repo-tools api-reports -o ae-wrong-input-file-type,ae-undocumented,ae-missing-release-tag --validate-release-tags",
"build:api-reports:only": "yarn tsc && backstage-repo-tools api-reports -o ae-wrong-input-file-type,ae-undocumented,ae-missing-release-tag --validate-release-tags",
"build-image": "yarn workspace backend build-image",
"build:knip-reports": "backstage-repo-tools knip-reports",
"clean": "backstage-cli repo clean",
Expand Down
1 change: 1 addition & 0 deletions workspaces/scorecard/packages/app-next/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
36 changes: 36 additions & 0 deletions workspaces/scorecard/packages/app-next/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "app-next",
"version": "0.0.0",
"private": true,
"backstage": {
"role": "frontend"
},
"repository": {
"type": "git",
"url": "https://github.com/redhat-developer/rhdh-plugins",
"directory": "workspaces/scorecard/packages/app-next"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"clean": "backstage-cli package clean"
},
"dependencies": {
"@backstage/cli": "^0.34.5",
"@backstage/core-compat-api": "^0.5.5",
"@backstage/core-components": "^0.18.3",
"@backstage/core-plugin-api": "^1.12.0",
"@backstage/frontend-defaults": "^0.4.0",
"@backstage/plugin-catalog": "^1.32.0",
"@backstage/plugin-scaffolder": "^1.34.3",
"@backstage/plugin-user-settings": "^0.8.29",
"@backstage/ui": "^0.9.1",
"@red-hat-developer-hub/backstage-plugin-scorecard": "workspace:^",
"@red-hat-developer-hub/backstage-plugin-theme": "^0.12.0",
"react": "^18.0.2",
"react-dom": "^18.0.2"
},
Comment on lines +18 to +32
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Missing react-router-dom dep 🐞 Bug ⛯ Reliability

app-next depends on @backstage/frontend-defaults, which declares react-router-dom as a peer
dependency, but app-next does not declare it. This makes app-next fragile when built/used in
isolation (e.g., focused installs) and can result in runtime/module resolution errors or peer
dependency failures.
Agent Prompt
## Issue description
`workspaces/scorecard/packages/app-next` depends on `@backstage/frontend-defaults`, which has a `react-router-dom` peer dependency, but `app-next` does not declare `react-router-dom`.

## Issue Context
This can break `app-next` builds/starts when installed or built independently (e.g., focused installs, extracting app-next into its own repo, stricter peer-dep enforcement).

## Fix Focus Areas
- workspaces/scorecard/packages/app-next/package.json[18-35]
- workspaces/scorecard/yarn.lock[2714-2733]

## Proposed change
Add `react-router-dom` to `dependencies` for app-next (use a version range compatible with the Backstage packages in this workspace, e.g. `^6.30.2`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

"devDependencies": {
"@types/react": "^18"
}
}
60 changes: 60 additions & 0 deletions workspaces/scorecard/packages/app-next/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Backstage is an open source framework for building developer portals"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link
rel="manifest"
href="<%= publicPath %>/manifest.json"
crossorigin="use-credentials"
/>
<link rel="icon" href="<%= publicPath %>/favicon.ico" />
<link rel="shortcut icon" href="<%= publicPath %>/favicon.ico" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="<%= publicPath %>/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="<%= publicPath %>/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="<%= publicPath %>/favicon-16x16.png"
/>
<link
rel="mask-icon"
href="<%= publicPath %>/safari-pinned-tab.svg"
color="#5bbad5"
/>
<title><%= config.getOptionalString('app.title') ?? 'Backstage' %></title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `yarn start`.
To create a production bundle, use `yarn build`.
-->
</body>
</html>
97 changes: 97 additions & 0 deletions workspaces/scorecard/packages/app-next/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright Red Hat, Inc.
*
* 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 { convertLegacyAppOptions } from '@backstage/core-compat-api';
import { SignInPage } from '@backstage/core-components';
import { githubAuthApiRef, gitlabAuthApiRef } from '@backstage/core-plugin-api';
import { getThemes } from '@red-hat-developer-hub/backstage-plugin-theme';
import scorecardPlugin, {
scorecardTranslationsModule,
createScorecardCatalogModule,
} from '@red-hat-developer-hub/backstage-plugin-scorecard/alpha';

import catalogPlugin from '@backstage/plugin-catalog/alpha';
import scaffolderPlugin from '@backstage/plugin-scaffolder/alpha';
import userSettingsPlugin from '@backstage/plugin-user-settings/alpha';

/*
* Custom sign-in page (legacy component). SignInPageBlueprint and ThemeBlueprint
* are deprecated; sign-in and themes are provided via convertLegacyAppOptions.
*/
const customSignInPage = (props: React.ComponentProps<typeof SignInPage>) => (
<SignInPage
{...props}
auto
providers={[
'guest',
{
id: 'github-auth-provider',
title: 'GitHub',
message: 'Sign in using GitHub',
apiRef: githubAuthApiRef,
},
{
id: 'gitlab-auth-provider',
title: 'GitLab',
message: 'Sign in using GitLab',
apiRef: gitlabAuthApiRef,
},
]}
/>
);

/*
* Legacy options: themes (RHDH light/dark) and SignInPage.
* Replaces deprecated SignInPageBlueprint and ThemeBlueprint modules.
*/
const legacyConvertedOptions = convertLegacyAppOptions({
themes: getThemes(),
components: {
SignInPage: customSignInPage as React.ComponentType<any>,
},
});

/*
* Scorecard: entity kinds that show the Scorecard tab on the catalog entity page.
* Catalog module attaches to catalogPlugin for these kinds only.
*/
const scorecardEntityKinds = ['component', 'service', 'template'];
const scorecardCatalogModule = createScorecardCatalogModule({
entityKinds: scorecardEntityKinds,
});

const scorecard = [
scorecardCatalogModule,
scorecardTranslationsModule,
scorecardPlugin,
];

/*
* app-next: Backstage app using the New Frontend System (NFS).
* Sign-in and themes use legacy options (convertLegacyAppOptions).
*/
const app = createApp({
features: [
legacyConvertedOptions,
...scorecard,
catalogPlugin,
scaffolderPlugin,
userSettingsPlugin,
],
});

export default app.createRoot();
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import '@backstage/cli/asset-types';
import '@backstage/ui/css/styles.css';
import ReactDOM from 'react-dom/client';
import App from './App';

export * from './translations';
ReactDOM.createRoot(document.getElementById('root')!).render(App);

Check warning on line 21 in workspaces/scorecard/packages/app-next/src/index.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

This assertion is unnecessary since it does not change the type of the expression.

See more on https://sonarcloud.io/project/issues?id=redhat-developer_rhdh-plugins&issues=AZzCtoGZes0J6d_UUBGP&open=AZzCtoGZes0J6d_UUBGP&pullRequest=2477
7 changes: 5 additions & 2 deletions workspaces/scorecard/plugins/scorecard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
"types": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.ts",
"./alpha": "./src/alpha.tsx",
"./package.json": "./package.json"
},
"typesVersions": {
"*": {
"alpha": [
"src/alpha.ts"
"src/alpha.tsx"
],
"package.json": [
"package.json"
Expand Down Expand Up @@ -48,8 +48,11 @@
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/catalog-model": "^1.5.0",
"@backstage/core-components": "^0.18.3",
"@backstage/core-plugin-api": "^1.12.0",
"@backstage/frontend-plugin-api": "^0.13.4",
"@backstage/plugin-app-react": "^0.1.0",
"@backstage/plugin-catalog-react": "^1.21.3",
"@backstage/plugin-permission-react": "^0.4.38",
"@backstage/theme": "^0.7.0",
Expand Down
92 changes: 58 additions & 34 deletions workspaces/scorecard/plugins/scorecard/report-alpha.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,72 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).

```ts
import { TranslationRef } from '@backstage/frontend-plugin-api';
import { TranslationResource } from '@backstage/frontend-plugin-api';
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import type { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendModule } from '@backstage/frontend-plugin-api';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/core-plugin-api';
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
import { TranslationResource } from '@backstage/core-plugin-api/alpha';

// @public
export const scorecardTranslationRef: TranslationRef<
'plugin.scorecard',
export function createScorecardCatalogModule(
options?: ScorecardEntityContentOptions,
): FrontendModule;

// @public
export function createScorecardEntityContent(
options?: ScorecardEntityContentOptions,
): ExtensionDefinition;

// @public
const _default: OverridableFrontendPlugin<
{
readonly 'emptyState.button': string;
readonly 'emptyState.title': string;
readonly 'emptyState.description': string;
readonly 'emptyState.altText': string;
readonly 'permissionRequired.button': string;
readonly 'permissionRequired.title': string;
readonly 'permissionRequired.description': string;
readonly 'permissionRequired.altText': string;
readonly 'errors.entityMissingProperties': string;
readonly 'errors.invalidApiResponse': string;
readonly 'errors.fetchError': string;
readonly 'errors.metricDataUnavailable': string;
readonly 'errors.invalidThresholds': string;
readonly 'errors.missingPermission': string;
readonly 'errors.noDataFound': string;
readonly 'errors.authenticationError': string;
readonly 'errors.missingPermissionMessage': string;
readonly 'errors.userNotFoundInCatalogMessage': string;
readonly 'errors.noDataFoundMessage': string;
readonly 'errors.authenticationErrorMessage': string;
readonly 'metric.github.open_prs.title': string;
readonly 'metric.github.open_prs.description': string;
readonly 'metric.jira.open_issues.title': string;
readonly 'metric.jira.open_issues.description': string;
readonly 'thresholds.success': string;
readonly 'thresholds.error': string;
readonly 'thresholds.warning': string;
readonly 'thresholds.noEntities': string;
readonly 'thresholds.entities_one': string;
readonly 'thresholds.entities_other': string;
root: RouteRef<undefined>;
},
{},
{
'api:scorecard': OverridableExtensionDefinition<{
kind: 'api';
name: undefined;
config: {};
configInput: {};
output: ExtensionDataRef<AnyApiFactory, 'core.api.factory', {}>;
inputs: {};
params: <
TApi,
TImpl extends TApi,
TDeps extends {
[x: string]: unknown;
},
>(
params: ApiFactory<TApi, TImpl, TDeps>,
) => ExtensionBlueprintParams<AnyApiFactory>;
}>;
}
>;
export default _default;

// @public
export interface ScorecardEntityContentOptions {
entityKinds?: string[];
}

// @public
export const scorecardTranslationRef: TranslationRef<
'plugin.scorecard',
Record<string, string>
>;

// @public
export const scorecardTranslations: TranslationResource<'plugin.scorecard'>;

// @public
export const scorecardTranslationsModule: FrontendModule;

// (No @packageDocumentation comment for this package)
```
Loading
Loading