Skip to content

feat(x2a): RHDH 1.10 support#3321

Open
mareklibra wants to merge 1 commit into
redhat-developer:mainfrom
mareklibra:localRhdhExport
Open

feat(x2a): RHDH 1.10 support#3321
mareklibra wants to merge 1 commit into
redhat-developer:mainfrom
mareklibra:localRhdhExport

Conversation

@mareklibra

@mareklibra mareklibra commented Jun 7, 2026

Copy link
Copy Markdown
Member

Fixes: FLPATH-4033

Upgrade x2a workspace to Backstage 1.49.4 for RHDH 1.10
Bump all @backstage/* dependencies from 1.45.3 to 1.49.4 manifest
levels across all plugins (x2a, x2a-backend, x2a-common, x2a-node,
x2a-mcp-extras, scaffolder-backend-module-x2a) and app packages.
Key changes:

  • Add dual frontend architecture: rename packages/app to
    packages/app-legacy (legacy frontend) and create new packages/app
    using the New Frontend System (NFS) with PageBlueprint,
    NavContentBlueprint, and TranslationBlueprint
  • Add plugins/x2a/src/alpha.tsx with NFS plugin definition
    (createFrontendPlugin, x2aTranslationsModule)
  • Integrate RHDH theme, scaffolder, config-driven sign-in, and
    sidebar navigation into the NFS app
  • Replace deprecated scaffolder-backend-module-bitbucket with
    scaffolder-backend-module-bitbucket-cloud
  • Remove variant="gridItem" props per Backstage 1.49 breaking changes
  • Remove x2a-dcr references from the app (RHDH 1.9-only workaround)
  • Add Jest 30 + cli-defaults devDependencies (Backstage 1.46+ change)
  • Add resolutions for @backstage/frontend-plugin-api, plugin-catalog,
    plugin-scaffolder, and zod to fix schema/type conflicts
  • Update CatalogService mock with new required methods
    (queryLocations, streamLocations, updateLocation)
  • Refactor DownloadStaticPublicFile to use a mockable navigateTo
    helper for Jest 30 jsdom compatibility
  • Add app.extensions config for NFS language settings

TODO:

@mareklibra mareklibra requested review from a team, elai-shalev and eloycoto as code owners June 7, 2026 20:56
@mareklibra mareklibra marked this pull request as draft June 7, 2026 20:56
@rhdh-gh-app

rhdh-gh-app Bot commented Jun 7, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
app workspaces/x2a/packages/app none v0.0.0
backend workspaces/x2a/packages/backend none v0.0.0
@red-hat-developer-hub/backstage-plugin-scaffolder-backend-module-x2a workspaces/x2a/plugins/scaffolder-backend-module-x2a major v0.4.1
@red-hat-developer-hub/backstage-plugin-x2a-backend workspaces/x2a/plugins/x2a-backend major v1.6.0
@red-hat-developer-hub/backstage-plugin-x2a-common workspaces/x2a/plugins/x2a-common major v1.4.0
@red-hat-developer-hub/backstage-plugin-x2a-dcr workspaces/x2a/plugins/x2a-dcr major v0.2.0
@red-hat-developer-hub/backstage-plugin-x2a-mcp-extras workspaces/x2a/plugins/x2a-mcp-extras major v0.3.0
@red-hat-developer-hub/backstage-plugin-x2a-node workspaces/x2a/plugins/x2a-node major v0.4.0
@red-hat-developer-hub/backstage-plugin-x2a workspaces/x2a/plugins/x2a major v1.4.0

url: '${AAP_URL:-https://aap.example.com}',
orgName: '${AAP_ORG_NAME:-MyOrganization}',
username: '${AAP_USERNAME}',
password: '${AAP_PASSWORD}',
@mareklibra

Copy link
Copy Markdown
Member Author

The plugins and conversions can be executed in RHDH 1.10 and OCP:

image

@mareklibra

Copy link
Copy Markdown
Member Author

The DCM flow with package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-plugin-auth:bs_1.49.4__0.1.6" seems to be working well:

Screenshot From 2026-06-16 15-37-18
Screenshot From 2026-06-16 15-37-46

@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 21.42857% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.58%. Comparing base (2465411) to head (0d5d497).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3321      +/-   ##
==========================================
+ Coverage   52.43%   53.58%   +1.15%     
==========================================
  Files        2251     2253       +2     
  Lines       85725    85814      +89     
  Branches    24111    24117       +6     
==========================================
+ Hits        44948    45983    +1035     
+ Misses      39164    38387     -777     
+ Partials     1613     1444     -169     
Flag Coverage Δ *Carryforward flag
adoption-insights 83.70% <ø> (ø) Carriedforward from 2465411
ai-integrations 67.95% <ø> (ø) Carriedforward from 2465411
app-defaults 69.79% <ø> (ø) Carriedforward from 2465411
augment 46.39% <ø> (ø) Carriedforward from 2465411
boost 100.00% <ø> (ø) Carriedforward from 2465411
bulk-import 72.46% <ø> (ø) Carriedforward from 2465411
cost-management 14.10% <ø> (ø) Carriedforward from 2465411
dcm 61.79% <ø> (ø) Carriedforward from 2465411
extensions 61.53% <ø> (ø) Carriedforward from 2465411
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 2465411
global-header 59.71% <ø> (ø) Carriedforward from 2465411
homepage 49.84% <ø> (ø) Carriedforward from 2465411
install-dynamic-plugins 56.23% <ø> (ø) Carriedforward from 2465411
konflux 91.49% <ø> (ø) Carriedforward from 2465411
lightspeed 68.57% <ø> (ø) Carriedforward from 2465411
mcp-integrations 85.46% <ø> (ø) Carriedforward from 2465411
orchestrator 37.75% <ø> (ø) Carriedforward from 2465411
quickstart 63.76% <ø> (ø) Carriedforward from 2465411
sandbox 79.56% <ø> (ø) Carriedforward from 2465411
scorecard 83.83% <ø> (ø) Carriedforward from 2465411
theme 61.26% <ø> (ø) Carriedforward from 2465411
translations 6.55% <ø> (ø) Carriedforward from 2465411
x2a 78.28% <21.42%> (+21.03%) ⬆️

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2465411...0d5d497. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mareklibra mareklibra marked this pull request as ready for review June 17, 2026 09:47
@mareklibra mareklibra force-pushed the localRhdhExport branch 2 times, most recently from 5e25e9c to 7b88287 Compare June 17, 2026 13:53

@eloycoto eloycoto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As mention in private, I do not have to the instance.

I'm also a bit worried about the current NODE_TLS disabled inside the plug-in.

// Set it here so Node.js TLS layer skips server cert verification.
const cluster = kc.getCurrentCluster();
if (cluster?.skipTLSVerify && !process.env.NODE_TLS_REJECT_UNAUTHORIZED) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure about this, if you change this value, means that the whole NODE_TLS is disabled for all request that the module do.

If we add this, we're allowing that any attacker load any URL with invalid certificate (Scaffolder) without real constraints, no? We have a way to skip the CERT issues in k8s in place, this maybe should be change to be honest.

https://github.com/x2ansible/x2ansible.github.io/blob/9a4eb40698b55d03a448d49778bc575fe422f0a9/deploy/app.yaml#L268-L269

@mareklibra mareklibra Jun 19, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right, it turns off the validation for the whole node process.

I was not able to make it working just for the single session, no matter documentation states so. There might be different version library resolved at runtime.

This workaround is based on the assumption that the skipTLSVerify is used in dev clusters only. Maybe its too strong assumption.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This workaround is based on the assumption that the skipTLSVerify is used in dev clusters only. Maybe its too strong assumption.

I would like to say that this does not happen in production, but..

* limitations under the License.
*/

export function navigateTo(url: string) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Because JS is totally functional programming, shoudn't we use onNavigate function, so it passed as the argument, and it's more FP friendly? It cannot change based on external parameters and it's clear for testing.

I'm worried that the a global mutation will change the DownloadStaticPublicFile, this way seems more FP friendly.

  export const DownloadStaticPublicFile = ({
    onNavigate = (url) => { globalThis.location.href = url; }
  }: DownloadStaticPublicFileProps) => {
    // ...
    useEffect(() => {
      discoveryApi.getBaseUrl('x2a').then(baseUrl => {
        onNavigate(`${baseUrl}/static/${filePath}`);
      });
    }, [discoveryApi, filePath, onNavigate]);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The only reason for adding the function is in simplifying tests, there are changes around jsdom in Jest 30 (newly used).

Both approaches, the suggested and the one in PR, work well.
Considering how the navigateTo is implemented and called, I do not see the described risk as relevant. It's not a global mutation.

To move forward, changing it. It's a cosmetic change.

}
}

function copyDirSync(src, dest) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why not? https://nodejs.org/api/fs.html#fscpsyncsrc-dest-options we're over complicated this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks, good idea

@@ -0,0 +1,189 @@
#!/usr/bin/env node

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why this file is needed? Not sure If I get why

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Do you mean the whole export-to-rhdh-repo.js?

It's simplification for local RHDH testing. With every change, a dev needs to:

  • Build each plugin as a dynamic plugin
  • Copy the output into the correct directory in the RHDH repo
  • Configure RHDH's app-config with all x2a-specific settings (ok, maybe just once)

Doing this manually for 5 plugins every time a change occurs is tedious. The script reduces this to a single yarn enable-in-rhdh-repo command. We used that in the Orchestrator, its implementatino here is highly motivated by the one there.

I could keep it for myself but I believe others can benefit from it as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ok, maybe it is good it you can document this, or telling how to do it! because I didn't know about it!


const source = x2aDynamicPlugins.frontend[pluginKey];
if (!config.dynamicPlugins.frontend[pluginKey]) {
config.dynamicPlugins.frontend[pluginKey] = source;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are a few merge here, and I'm thinking, can we use something like this?

https://lodash.com/docs/#merge

I'm worried that a new config is added, and we miss here, (I have no clue why we need this too), and we miss adding here the conditional

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The merge would not work here since it overwrites existing leaf values in target.
We need to set defaults without affecting user customizations in app-config.local.yaml. Using lodash.merge would destroy any user-edited values.

But it brought me to the idea with deepDefaults(), it's slightly different from the lodash version around merging of arrays.

Reimplemented.

Bump all @backstage/* dependencies from 1.45.3 to 1.49.4 across all
plugins (x2a, x2a-backend, x2a-common, x2a-node, x2a-mcp-extras,
scaffolder-backend-module-x2a) and app packages.

Frontend:
- Replace legacy frontend (packages/app) with New Frontend System
  (NFS) using createApp from @backstage/frontend-defaults,
  PageBlueprint, NavContentBlueprint, TranslationBlueprint, and
  SignInPageBlueprint with GitHub/GitLab/Bitbucket auth providers
- Add plugins/x2a/src/alpha.tsx with NFS plugin definition
  (createFrontendPlugin, x2aTranslationsModule) including
  FormFieldBlueprint for RepoAuthentication and RulesAcceptance
  scaffolder field extensions
- Delete legacy app files (Root, EntityPage, SearchPage, apis.ts,
  App.test.tsx, setupTests.ts) that are no longer needed with NFS
- Add plugins/x2a/app-config.yaml for RHDH dynamic plugin wiring
  (scaffolder field extensions, translation resources, app icons,
  dynamic routes)
- Integrate RHDH theme via @red-hat-developer-hub/backstage-plugin-theme
- Add app.extensions config for NFS language settings (en/de/es/fr/it)
- Refactor DownloadStaticPublicFile to accept an onNavigate callback
  prop for Jest 30 jsdom compatibility

Backend:
- Replace deprecated scaffolder-backend-module-bitbucket with
  scaffolder-backend-module-bitbucket-cloud
- Add TLS certificate error diagnostics in makeK8sClient with
  actionable hints for NODE_EXTRA_CA_CERTS and cluster CA setup
- Remove global NODE_TLS_REJECT_UNAUTHORIZED bypass
- Update CatalogService mock with new required methods
  (queryLocations, streamLocations, updateLocation)

Testing and tooling:
- Move e2e tests to workspace-level e2e-tests/ with multi-locale
  Playwright config (per-locale ports for en/de/es/fr/it)
- Upgrade @playwright/test to 1.60.0
- Add Jest 30 + @backstage/cli-defaults devDependencies
- Add --registry flag to build-dynamic-plugins.sh
- Add enable-in-rhdh-repo scripts (export-to-rhdh-repo.js,
  update-config-in-rhdh-repo.js, config-for-rhdh-repo.yaml) for
  local RHDH development and testing

Cleanup:
- Remove variant="gridItem" props per Backstage 1.49 breaking changes
- Remove x2a-dcr references from the app (RHDH 1.9-only workaround)
- Add dist-dynamic to eslint ignorePatterns in all plugin configs
- Add resolutions for NFS packages and zod to fix schema/type conflicts
- Document TLS certificate handling in README

Signed-off-by: Marek Libra <marek.libra@gmail.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@mareklibra

Copy link
Copy Markdown
Member Author

Changes are in.
I had to squash the changes to make rebase easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants