Skip to content

feat(app): migrate portal to Backstage New Frontend System#621

Open
kaviththiranga wants to merge 44 commits into
openchoreo:mainfrom
kaviththiranga:backstage-migration-phase2
Open

feat(app): migrate portal to Backstage New Frontend System#621
kaviththiranga wants to merge 44 commits into
openchoreo:mainfrom
kaviththiranga:backstage-migration-phase2

Conversation

@kaviththiranga

@kaviththiranga kaviththiranga commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates the OpenChoreo Backstage portal from the legacy frontend system (createApp from
@backstage/app-defaults + <FlatRoutes>) to the New Frontend System (createApp from
@backstage/frontend-defaults + features: [...]). The five custom OpenChoreo frontend
plugins now ship a /alpha NFS entry point alongside their legacy default export.

This addresses the body of
openchoreo/openchoreo#3568 — adopters
can now drop --legacy from the @backstage/create-app step when installing the plugin suite.

Cut on top of #614 (8d8bd80f chore(deps): upgrade Backstage portal to v1.51.0).

Changes

Plugin packages (5 × /alpha entry point)

  • @openchoreo/backstage-plugin
  • @openchoreo/backstage-plugin-openchoreo-ci
  • @openchoreo/backstage-plugin-openchoreo-observability
  • @openchoreo/backstage-plugin-openchoreo-workflows
  • @openchoreo/backstage-plugin-platform-engineer-core

Each plugin's default export remains the legacy createPlugin instance — the dev sandboxes
(plugins/*/dev/index.tsx) and any host on the legacy frontend keep working unchanged. The new
/alpha entry exposes a createFrontendPlugin with ApiBlueprint extensions for the
plugin's APIs and PageBlueprint / EntityContentBlueprint extensions for the top-level pages
where applicable. Routable extensions that the host currently mounts with per-call props (e.g.
<ObservabilityRuntimeLogs renderRowAction={...} />) stay on the legacy export for now — a
future PR will route those through a host-injected callable registry API so they can flow
through NFS too.

App shell (packages/app)

  • Swap createApp import to @backstage/frontend-defaults.
  • Existing apis/icons/components.SignInPage/themes flow through convertLegacyAppOptions;
    legacy <FlatRoutes> JSX tree flows through convertLegacyAppRoot.
  • Register the five custom NFS plugins as features, plus the upstream NFS plugins we need for
    route ref resolution (plugin-scaffolder/alpha, plugin-catalog-import/alpha).
  • Override upstream APIs we customize, scoped under the right pluginId via withOverrides({ extensions: [getExtension(...).override({...})] }):
    • api:catalog-graph → adds OpenChoreo custom relations (deploysTo, usesPipeline,
      hostedOn, instanceOf, …).
    • api:catalog/entity-presentation → adds kind icons for Environment, DataPlane,
      DeploymentPipeline, ResourceType, etc.
    • api:scaffolder/form-decorators → injects openChoreoTokenDecorator for user-based
      authorization on scaffolder actions.
    • page:scaffolder → disabled so our legacy <ScaffolderPage> mount (with
      CustomTemplateListPage and CustomReviewStep) owns the page.
  • Migrate DynamicSignInPage to an NFS SignInPageBlueprint extension in
    apis/customOverrides.tsx.
  • Reinstate __experimentalTranslations (catalog-import header customization) as a
    TranslationBlueprint.make({...}) module.
  • Hoist ScaffolderPreselectionProvider to wrap <Root> because convertLegacyAppRoot
    requires the route element to BE the routable extension (no wrappers between).
  • Drop ScaffolderLayout (CSS-only wrapper for card width constraint) — minor visual
    regression, deferred to a future CSS-scoped rule in buiOverrides.css.

Dependencies

  • Pull @backstage/frontend-defaults@^0.5.2, frontend-app-api@^0.16.3,
    frontend-plugin-api@^0.17.0, core-compat-api@^0.5.11, plugin-app@^0.4.6,
    plugin-app-react@^0.2.3 forward as direct deps of packages/app, plus frontend-test-utils
    as a dev dep. Collapses @backstage/frontend-plugin-api from 8 nested copies → 1 hoisted (plus
    3 community-plugin-isolated copies of ^0.13.4 that don't cross NFS boundaries).
  • Each of the 5 plugins gains @backstage/frontend-plugin-api: ^0.17.0 as a dep and an
    exports./alpha map entry.

Known deferred items

Tracked for follow-up PRs after this lands:

  • Field extensions still ride through the legacy <ScaffolderFieldExtensions> JSX block
    rather than NFS FormFieldBlueprint modules. The two don't bridge while our custom
    <ScaffolderPage> mount owns /create — upstream's FormFieldBlueprint attaches via
    attachTo: { id: 'page:scaffolder', input: 'formFields' }, and we have page:scaffolder
    disabled. The proper rewiring needs CustomTemplateListPage / CustomReviewStep to be
    re-authored as NFS overrides of scaffolderTemplatesSubPage / scaffolderPage.
  • Entity tabs that take per-mount props (e.g. <ObservabilityRuntimeLogs renderRowAction={...} />) and host-side wrappers like <FeatureGatedContent feature="observability"> keep flowing through the legacy plugin exports. The NFS-native
    rewiring needs a host-injected callable registry API for the props case and the blueprint's
    if/filter slot for the gating case.
  • openchoreo.github.io install docs still recommend npx @backstage/create-app --legacy.
    Will land as a separate PR on the docs repo (per the original migration plan's Phase 7 scope),
    sequenced after this PR merges and the affected plugin versions release.

Summary by CodeRabbit

  • New Features
    • Dynamic sign-in now switches between guest/auto and OpenChoreo auth based on configuration.
    • Added NFS-compatible alpha entry points for OpenChoreo, CI, workflows, observability, and platform-engineer-core, including expanded entity pages/tabs.
    • Observability runtime logs now support host-registered log-row action renderers.
    • New OpenChoreo scaffolder create page with additional field extensions.
  • Bug Fixes
    • Cleared stale scaffolder preselection when URL query params change.
  • Refactor
    • Migrated app bootstrapping to feature-based New Frontend System.
  • Tests
    • Updated smoke tests for alpha-scoped registration.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR migrates the OpenChoreo Backstage plugin suite and host app to Backstage's New Frontend System by adding /alpha entrypoints across all packages, deferring API factory registration to plugin-level blueprints, refactoring app bootstrap to feature-based createApp, introducing app-level overrides for catalog, scaffolder, and sign-in configuration, and adding entity loading context and routable log-row action renderer registry.

Changes

Backstage NFS Migration

Layer / File(s) Summary
Release changeset documentation
.changeset/migrate-portal-to-nfs.md, .changeset/fix-nfs-migration-followups.md
Documents NFS migration, /alpha entrypoints across packages, and follow-up fixes for catalog relations, entity presentation, decorator handling, and entity-page routing contributions.
App bootstrap refactor to feature-based createApp
packages/app/src/App.tsx
Removes legacy JSX root, imports NFS createApp, builds legacyAppOptions/legacyRoot via conversion helpers, assembles features array with overrides and plugin features, and updates /create route scaffolder rendering.
Defer API factories to /alpha blueprints
packages/app/src/apis.ts
Removes app-scoped factories for scaffolder form decorators, OpenChoreo CI/workflows clients, catalog graph customizations, and entity presentation; keeps auth API ref for customAppModule wiring.
App dependencies, entrypoint, and tests
packages/app/package.json, packages/app/src/App.test.tsx, packages/app/src/index.tsx, packages/app/src/apis.test.ts
Adds NFS frontend dependencies; changes App export to default instance; updates test expectations to exclude /alpha-owned API factories.
Custom overrides for catalog, scaffolder, and app
packages/app/src/apis/customOverrides.tsx, packages/app/src/apis/customOverrides.test.tsx
Introduces catalogGraphPluginAlpha, catalogPluginAlpha, customAppModule (with sign-in, translations, observability row actions), and scaffolderPluginAlpha overrides; comprehensive test coverage.
Auth and sign-in components
packages/app/src/components/DynamicSignInPage.tsx, packages/app/src/components/settings/OpenChoreoProviderSettings.tsx
Adds config-driven sign-in: renders guest auto-sign or OpenChoreo provider based on openchoreo.features.auth.enabled setting; updates auth API ref imports.
Root layout and scaffolder page
packages/app/src/components/Root/Root.tsx, packages/app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx
Wraps Root with ScaffolderPreselectionProvider; creates OpenChoreoScaffolderPage with custom field extensions and component overrides.
Catalog entity page loading and routing
packages/app/src/components/catalog/EntityLayoutWithDelete.tsx, packages/app/src/components/catalog/OpenChoreoCatalogEntityPage.tsx
Refactors EntityLayoutWithDelete to gate rendering until entity loads; introduces OpenChoreoCatalogEntityPage providing entity context via AsyncEntityProvider for descendant per-kind pages.
App utilities and feature gating
packages/app/src/kindIcons.ts, packages/app/src/scaffolder/ScaffolderPreselectionContext.tsx, packages/app/src/components/catalog/EntityPage.tsx, plugins/openchoreo-react/src/components/FeatureGate/*, plugins/openchoreo-react/src/index.ts
Centralizes KIND_ICONS mapping; fixes scaffolder preselection URL-query sync; exports FeatureGatedContent and props interface for plugins to gate routable entity content behind feature flags.
openchoreo-ci /alpha plugin
plugins/openchoreo-ci/package.json, plugins/openchoreo-ci/src/alpha.tsx
Adds ./alpha export; defines CI client ApiBlueprint and component workflows entity tab; registers createFrontendPlugin.
openchoreo-workflows /alpha plugin
plugins/openchoreo-workflows/package.json, plugins/openchoreo-workflows/src/alpha.tsx, plugins/openchoreo-workflows/src/alpha.test.tsx
Adds ./alpha export; creates generic-workflows client API, /workflows page, and filtered workflow-runs entity tab; includes extension tests.
openchoreo-observability log row action registry
plugins/openchoreo-observability/src/api/LogRowActionRendererApi.ts, plugins/openchoreo-observability/src/api/index.ts, plugins/openchoreo-observability/src/alpha/LogRowActionBlueprint.ts, plugins/openchoreo-observability/src/api/LogRowActionRendererApi.test.ts
Introduces API contract for host-injectable per-row action renderers; defines LogRowActionBlueprint extension type; implements factory with first-renderer selection; includes tests.
openchoreo-observability /alpha plugin
plugins/openchoreo-observability/package.json, plugins/openchoreo-observability/src/alpha.tsx, plugins/openchoreo-observability/src/alpha.test.tsx, plugins/openchoreo-observability/src/index.ts
Adds ./alpha export; defines three backend clients, log-row-action registry, and feature-gated entity tabs for runtime logs/events/metrics/alerts/traces/incidents; re-exports log renderer API; includes extension tests.
openchoreo-observability runtime integration
plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx, plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx, plugins/openchoreo-observability/src/plugin.ts
Updates runtime logs pages to resolve row actions from prop or host-registered API fallback; refactors plugin.ts tab exports to lazy-loaded components instead of routable extensions.
openchoreo (main) /alpha plugin
plugins/openchoreo/package.json, plugins/openchoreo/src/alpha.tsx, plugins/openchoreo/src/alpha.test.tsx
Adds ./alpha export; defines OpenChoreo client API and extensive entity content/card blueprints across component/system/domain/managed-resource/environment/dataplane/workflow/observability/type kinds with kind filtering and observability feature gating; includes enumerated extension test.
platform-engineer-core /alpha plugin
plugins/platform-engineer-core/package.json, plugins/platform-engineer-core/src/alpha.tsx
Adds ./alpha export; creates PageBlueprint for /platform-engineer-view dashboard; registers createFrontendPlugin.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • openchoreo/backstage-plugins#335: Both PRs implement the observability "project runtime logs" UI by wiring the /logs route/tab and its page component.
  • openchoreo/backstage-plugins#544: Both PRs extend plugins/openchoreo-observability/src/index.ts exports to include log-row action renderer APIs and blueprints for external host consumption.
  • openchoreo/backstage-plugins#617: Implements the missing observability runtime-events feature that the main PR's entity tab routes would render.

Suggested reviewers

  • sameerajayasoma
  • stefinie123

Poem

🐰 Hopped through code, set alpha gates aflutter,
Legacy kept warm while features uncurl,
Plugins reborn in a feature-based flutter,
App and blueprints in a brand-new swirl. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is comprehensive and well-structured, covering purpose (NFS migration for plugin suite), approach (detailed changes to plugins and app shell), and known deferred items. However, it does not follow the repository's required template structure with sections like Goals, User stories, Release note, Documentation, etc. Consider restructuring the description to align with the repository template sections (Purpose, Goals, Approach, User stories, Release note, Documentation, etc.) for consistency with contribution guidelines.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately reflects the main objective: migrating the OpenChoreo Backstage portal from the legacy frontend system to the New Frontend System (NFS).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/apis/customOverrides.tsx`:
- Around line 132-150: The kindIcons map in customOverrides.tsx is missing an
entry for "clusterworkflow", causing App.tsx's kind:clusterworkflow to fall back
to the default icon; add a "clusterworkflow" key to the kindIcons object (same
icon as "workflow", i.e., PlayCircleOutlineIcon) so cluster workflow entities
render with the expected legacy icon; update the kindIcons object where other
mappings (environment, workflow, clusterworkflowplane, etc.) are defined.

In `@plugins/openchoreo/src/alpha.tsx`:
- Around line 39-41: The pluginId passed to createFrontendPlugin in
plugins/openchoreo/src/alpha.tsx is 'openchoreo' but must match the package.json
backstage.pluginId ('plugin-openchoreo'); update the pluginId in the
createFrontendPlugin call to exactly match the package metadata (use the
'plugin-openchoreo' string) so the plugin metadata/wiring works correctly, and
apply the same fix pattern for the other affected plugin (update
createFrontendPlugin's pluginId in the platform-engineer-core frontend entry to
match its package.json backstage.pluginId 'plugin-platform-engineer-core').
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a239483f-be48-4cfc-86ba-4c4a1bb8550a

📥 Commits

Reviewing files that changed from the base of the PR and between ffa762f and 0f42b20.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (19)
  • .changeset/migrate-portal-to-nfs.md
  • packages/app/package.json
  • packages/app/src/App.test.tsx
  • packages/app/src/App.tsx
  • packages/app/src/apis.ts
  • packages/app/src/apis/customOverrides.tsx
  • packages/app/src/components/DynamicSignInPage.tsx
  • packages/app/src/components/Root/Root.tsx
  • packages/app/src/index.tsx
  • plugins/openchoreo-ci/package.json
  • plugins/openchoreo-ci/src/alpha.tsx
  • plugins/openchoreo-observability/package.json
  • plugins/openchoreo-observability/src/alpha.tsx
  • plugins/openchoreo-workflows/package.json
  • plugins/openchoreo-workflows/src/alpha.tsx
  • plugins/openchoreo/package.json
  • plugins/openchoreo/src/alpha.tsx
  • plugins/platform-engineer-core/package.json
  • plugins/platform-engineer-core/src/alpha.tsx

Comment thread packages/app/src/apis/customOverrides.tsx Outdated
Comment thread plugins/openchoreo/src/alpha.tsx
Pulls @backstage/frontend-defaults, frontend-app-api, frontend-plugin-api,
core-compat-api, plugin-app, plugin-app-react forward to their ^0.17.0-line
versions so createFrontendPlugin/blueprint imports type-check against a
single resolved frontend-plugin-api. Required preparation before NFS plugin
conversion.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Expose the plugin via @backstage/frontend-plugin-api's createFrontendPlugin
in src/alpha.tsx so the NFS app shell can consume it directly. The legacy
src/plugin.ts and dev sandbox continue to work unchanged. Mirrors the
upstream pattern used by @backstage/plugin-api-docs and friends.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Expose the plugin as a createFrontendPlugin with the GenericWorkflowsClient
API and the GenericWorkflowsPage routable extension translated to a
PageBlueprint. Legacy src/plugin.ts remains unchanged.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Expose the plugin as a createFrontendPlugin with the
PlatformEngineerDashboardView translated to a PageBlueprint. Legacy
src/plugin.ts and dev sandbox unchanged.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Register the three observability backend APIs (observability, RCA agent,
FinOps agent) as ApiBlueprint extensions. The nine legacy routable extensions
(Metrics, Traces, RCA, Logs, Alerts, Wirelogs, Incidents, CostAnalysis) keep
flowing through src/plugin.ts because the host app mounts them with per-
mount props inside legacy EntityLayout.Route blocks.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Register the OpenChoreoClient API as an ApiBlueprint extension and expose
the plugin's three route refs (catalogEnvironment, accessControl,
resourceEnvironments). The four legacy routable extensions and four
component cards continue to flow through src/plugin.ts; the host app mounts
them as plain React components inside EntityLayout. Step 2 is now complete
for all five custom plugins.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…aults

Step 3a of the NFS migration:
- App.tsx swaps `createApp` from `@backstage/app-defaults` for the NFS one
  from `@backstage/frontend-defaults`. Existing apis/icons/components.SignInPage/
  themes flow through `convertLegacyAppOptions`; the entire FlatRoutes JSX tree
  including AppRouter and Root flows through `convertLegacyAppRoot`.
- index.tsx and App.test.tsx switch from `<App />` to rendering the JSX value
  `app` directly, matching the upstream NFS reference app.
- Two `app`-scoped API factories (`catalogGraphApiRef`,
  `entityPresentationApiRef`) are temporarily disabled — under NFS they
  collide with the upstream plugins that already own those API ids. The
  proper plugin-scoped overrides land in Step 3c.
- `__experimentalTranslations` (catalog-import strings) is deferred to 3c
  as a TranslationBlueprint module.

Dev server boots clean, sign-in + home page verified live.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Step 3b of the NFS migration:
- Wire the five custom plugins from Step 2 (openchoreo, openchoreo-ci,
  openchoreo-observability, openchoreo-workflows, platform-engineer-core) as
  NFS features via their `/alpha` default exports.
- Drop the now-duplicate `openChoreoCiClientApiRef` and
  `genericWorkflowsClientApiRef` factory registrations from `apis.ts`; the
  plugins own them now.
- Register `@backstage/plugin-scaffolder/alpha` so the legacy scaffolder
  route refs (used by `useRouteRef(scaffolderPlugin.routes.root)` inside
  `useKindCreateConfig`) resolve under NFS. Without it the catalog page
  crashes inside `ContextAwareCreateButton`.
- Defer the scaffolder form-decorators override (custom
  `openChoreoTokenDecorator`) to Step 3c; under NFS the app-scoped factory
  collides with the scaffolder plugin's own default.

Known regressions to fix in Step 3c:
- `/create` shows upstream's NFS scaffolder Templates page, not our
  `CustomTemplateListPage`/`CustomReviewStep`/`ScaffolderLayout` or the
  27 field extensions. They'll move to a scaffolder override + field-
  extension modules.
- Catalog graph relations and entity-presentation kind icons remain on
  upstream defaults.

Verified live: sign-in, home, catalog index, system entity page (all 9
observability tabs visible) render cleanly.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Part of Step 3c (scaffolder customizations).

Disable the upstream NFS \`page:scaffolder\` extension via
\`scaffolderPlugin.withOverrides\` so the legacy \`<ScaffolderPage>\` mount in
\`<FlatRoutes>\` (preserved via \`convertLegacyAppRoot\`) owns the page again.
The plugin's route refs and other extensions stay active so
\`useRouteRef(scaffolderPlugin.routes.root)\` still resolves.

\`convertLegacyAppRoot\` requires the route element to BE the routable
extension; it cannot see through wrapper components. So:
- Remove \`<ScaffolderLayout>\` and \`<ScaffolderPreselectionProvider>\` from
  the \`/create\` route element. \`<ScaffolderPage>\` is now the direct element.
- Hoist \`<ScaffolderPreselectionProvider>\` to wrap \`<Root>\` so the URL
  preselection still propagates to ProjectNamespaceField and
  NamespaceEntityPicker. The provider only reads \`useSearchParams\`, so
  wrapping the whole app is safe.
- \`ScaffolderLayout\` (a CSS-only width constraint for scaffolder cards) is
  dropped for now; the cards will be unconstrained until a future
  CSS-scoped replacement lands in \`buiOverrides.css\`.

Verified live: /create renders our \`CustomTemplateListPage\` (not upstream's
Templates UI), template wizard shows custom field extensions and
\`CustomReviewStep\`, catalog/home unchanged.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…errides

Part of Step 3c (continued).

Move the three upstream API factories that we customize under their owning
plugin's pluginId, replacing the `app`-scoped registrations that previously
caused API_FACTORY_CONFLICT under NFS:
- catalog-graph: \`api:catalog-graph\` override carries the OpenChoreo custom
  relations (deploysTo, hostedOn, instanceOf, …) and relation pairs.
- catalog: \`api:catalog/entity-presentation\` override carries the kind
  icons for Environment, DataPlane, DeploymentPipeline, etc.
- scaffolder: \`api:scaffolder/form-decorators\` override re-injects the
  \`openChoreoTokenDecorator\` for user-based authorization on scaffolder
  actions. The \`page:scaffolder\` extension disable from the previous
  commit moves into the same overrides module.

The override pattern uses \`pluginAlpha.getExtension(id).override({ params:
defineParams => defineParams({...}) })\` since ApiBlueprint takes its
params as a callback in the v0.17 frontend-plugin-api.

All three overrides live in a new \`packages/app/src/apis/customOverrides.tsx\`
to keep App.tsx tidy.

Verified live: catalog index, catalog graph, system entity Relations card,
scaffolder template wizard all render with our customizations active and
no startup conflicts.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…lugin

Final piece of Step 3c.

Add a TranslationBlueprint module that reinstates the catalog-import header
overrides (`Register an existing catalog entity` etc.) that previously rode
via createApp.__experimentalTranslations.

Register \`@backstage/plugin-catalog-import/alpha\` so the
\`routeRef{id=catalog-import}\` resolves under NFS; without it the legacy
\`<RequirePermission><CatalogImportPage /></...>\` mount inside <FlatRoutes>
fails at runtime with "Routable extension component … was not discovered".

Verified live: /catalog-import shows the customized title and supporting
text, no errors.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Step 3d.

Move \`DynamicSignInPage\` (the OIDC vs guest auth switcher) out of App.tsx
into its own \`packages/app/src/components/DynamicSignInPage.tsx\` and
register it through \`SignInPageBlueprint.make({ loader: () => import(...) })\`
inside the renamed \`customAppModule\` (was \`customTranslationsModule\`,
now holds both the sign-in extension and the translation overrides since
both target \`pluginId: 'app'\`).

Drop \`components.SignInPage: DynamicSignInPage\` from
\`convertLegacyAppOptions\` and the now-unused
\`SignInPage\`/\`configApiRef\`/\`useApi\`/\`openChoreoAuthApiRef\` imports from
App.tsx.

Verified live: signed out, the OpenChoreo \"Sign in using OpenChoreo\"
button appears via the NFS blueprint, OIDC redirect to Thunder works,
sign-in completes, home page renders.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Patch bump for the five custom plugins that gained `/alpha` NFS entry
points (openchoreo, openchoreo-ci, openchoreo-observability,
openchoreo-workflows, platform-engineer-core). Default export remains the
legacy `createPlugin` instance; new entry exposes the same plugin as a
`createFrontendPlugin` for use with `createApp({ features })`.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…ries

  The CI/workflows clients and catalog-graph factory moved out of app-scope apis.ts into plugin
  /alpha modules and customOverrides.tsx during the NFS migration; this test was still asserting
  them in the app-scoped apis array.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…injection

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…rints

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
… blueprints

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…blueprint

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
@kaviththiranga kaviththiranga force-pushed the backstage-migration-phase2 branch from 08f4fab to d4dd00f Compare June 16, 2026 10:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/apis/customOverrides.tsx`:
- Around line 149-153: The comment block starting at line 149 states that the
page:scaffolder extension is disabled, but this is inaccurate since the code at
line 272 actively overrides its loader. Update the comment to correctly reflect
that page:scaffolder is being overridden (not disabled) to inject the OpenChoreo
token for user-based authorization in scaffolder actions. Remove the misleading
statement about the legacy ScaffolderPage mount winning.

In `@packages/app/src/App.tsx`:
- Around line 140-153: The `/create` route in App.tsx currently uses
ScaffolderPage with minimal configuration while customOverrides.tsx
simultaneously registers an NFS override for page:scaffolder that loads
OpenChoreoScaffolderPage with full ScaffolderLayout and all 27 field extensions,
causing maintenance drift. Choose one approach: either remove or comment out the
NFS override registration for page:scaffolder in customOverrides.tsx to
eliminate the competing implementation, or replace the ScaffolderPage component
in the `/create` route definition with OpenChoreoScaffolderPage to consolidate
on the richer implementation. Apply the chosen fix consistently so there is a
single, clear source of truth for the create resource page.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: eef6f09d-a353-44fe-b71f-b4f936806300

📥 Commits

Reviewing files that changed from the base of the PR and between 08f4fab and d4dd00f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (35)
  • .changeset/fix-nfs-migration-followups.md
  • .changeset/migrate-portal-to-nfs.md
  • packages/app/package.json
  • packages/app/src/App.test.tsx
  • packages/app/src/App.tsx
  • packages/app/src/apis.test.ts
  • packages/app/src/apis.ts
  • packages/app/src/apis/customOverrides.tsx
  • packages/app/src/components/DynamicSignInPage.tsx
  • packages/app/src/components/Root/Root.tsx
  • packages/app/src/components/catalog/EntityPage.tsx
  • packages/app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx
  • packages/app/src/components/settings/OpenChoreoProviderSettings.tsx
  • packages/app/src/index.tsx
  • packages/app/src/kindIcons.ts
  • packages/app/src/scaffolder/ScaffolderPreselectionContext.tsx
  • plugins/openchoreo-ci/package.json
  • plugins/openchoreo-ci/src/alpha.tsx
  • plugins/openchoreo-observability/package.json
  • plugins/openchoreo-observability/src/alpha.tsx
  • plugins/openchoreo-observability/src/alpha/LogRowActionBlueprint.ts
  • plugins/openchoreo-observability/src/api/LogRowActionRendererApi.ts
  • plugins/openchoreo-observability/src/api/index.ts
  • plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsx
  • plugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsx
  • plugins/openchoreo-observability/src/index.ts
  • plugins/openchoreo-react/src/components/FeatureGate/FeatureGatedContent.tsx
  • plugins/openchoreo-react/src/components/FeatureGate/index.ts
  • plugins/openchoreo-react/src/index.ts
  • plugins/openchoreo-workflows/package.json
  • plugins/openchoreo-workflows/src/alpha.tsx
  • plugins/openchoreo/package.json
  • plugins/openchoreo/src/alpha.tsx
  • plugins/platform-engineer-core/package.json
  • plugins/platform-engineer-core/src/alpha.tsx
💤 Files with no reviewable changes (1)
  • packages/app/src/apis.ts
✅ Files skipped from review due to trivial changes (5)
  • packages/app/src/components/settings/OpenChoreoProviderSettings.tsx
  • plugins/openchoreo-observability/src/index.ts
  • packages/app/src/App.test.tsx
  • plugins/openchoreo-react/src/components/FeatureGate/index.ts
  • .changeset/migrate-portal-to-nfs.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/app/src/index.tsx
  • plugins/openchoreo-observability/package.json
  • plugins/openchoreo-ci/src/alpha.tsx
  • packages/app/package.json
  • packages/app/src/apis.test.ts
  • plugins/openchoreo-workflows/package.json
  • plugins/platform-engineer-core/src/alpha.tsx
  • plugins/openchoreo/package.json

Comment thread packages/app/src/apis/customOverrides.tsx
Comment thread packages/app/src/App.tsx Outdated
…y override"

This reverts commit bf2ac84.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
… api re-emission

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…ock scaffolder.root binding

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…derPage

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…caffolderLayout

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…utes

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
…ha extensions

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
@kaviththiranga kaviththiranga force-pushed the backstage-migration-phase2 branch from d4dd00f to 933b02f Compare June 17, 2026 00:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/app/src/apis/customOverrides.test.tsx (1)

33-46: ⚡ Quick win

Consider verifying specific extension names instead of just the count.

The test extracts extension ids on line 40 but only checks that exactly 5 exist. This is brittle—adding or removing an extension requires updating the hardcoded 5. Consider asserting the presence of the specific named extensions documented in the comment (e.g., 'catalog-import-overrides', 'investigate-log', 'openchoreo-about', 'workflows-or-external-ci') to make the test more robust and self-documenting.

♻️ Suggested enhancement
   const ids = extensions.map(e => e.id);
   // The host registers exactly five extensions on the app module today:
   // a SignInPage, a Translation override (catalog-import), a
   // LogRowAction renderer, the OpenChoreoAboutCard, and the
   // WorkflowsOrExternalCICard.
-  expect(ids).toHaveLength(5);
+  expect(ids).toHaveLength(5);
+  // Verify the known named extensions are present
+  expect(ids).toContain('catalog-import-overrides');
+  expect(ids).toContain('investigate-log');
+  expect(ids).toContain('openchoreo-about');
+  expect(ids).toContain('workflows-or-external-ci');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/apis/customOverrides.test.tsx` around lines 33 - 46, The
test in the 'registers extensions on the customAppModule' test case extracts
extension ids but only verifies the hardcoded count of 5 using
expect(ids).toHaveLength(5), which is brittle and requires manual updates when
extensions change. Replace the length assertion with specific checks for the
named extensions documented in the comment (such as 'catalog-import-overrides',
'investigate-log', 'openchoreo-about', and 'workflows-or-external-ci') by using
expect(ids).toContain() or similar matchers to verify that each expected
extension id is present in the ids array. This makes the test more resilient to
changes and self-documenting.
plugins/openchoreo/src/alpha.test.tsx (1)

61-77: 💤 Low value

Consider adding a type guard for improved type safety.

The tests use as any to access the internal plugin structure, which bypasses type checking. While this is acceptable and common for testing framework internals, you could improve type safety with a minimal interface:

interface PluginWithInternals {
  id: string;
  extensions: Array<{ id: string }>;
}

Then cast to this interface instead of any. This preserves the intent while catching potential structural changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/openchoreo/src/alpha.test.tsx` around lines 61 - 77, The test blocks
use `as any` casts when accessing the plugin's extensions property, which
bypasses type checking. Define a type guard interface (e.g.,
PluginWithInternals) that specifies the shape of the plugin with an extensions
property, then replace both `as any` casts with casts to this new interface
instead. This applies to both the first test checking for documented blueprint
extensions and the second test checking the extension count. The interface
should include the id property and the extensions array structure to maintain
type safety while still allowing internal plugin structure access in tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/fix-nfs-migration-followups.md:
- Around line 23-25: The changelog entry in the
`.changeset/fix-nfs-migration-followups.md` file incorrectly lists ten
observability tabs but the PR objectives only include seven shipped features:
runtime logs, events, metrics, alerts, traces, incidents, and analysis. Remove
the references to Wirelogs, RCA Reports, and Cost Analysis from the changelog
entry, updating the count from ten to seven to accurately reflect what is
actually being shipped in this change. Keep the reference to the registry API
for host-injected log-row action renderers as that remains part of the
contribution.

---

Nitpick comments:
In `@packages/app/src/apis/customOverrides.test.tsx`:
- Around line 33-46: The test in the 'registers extensions on the
customAppModule' test case extracts extension ids but only verifies the
hardcoded count of 5 using expect(ids).toHaveLength(5), which is brittle and
requires manual updates when extensions change. Replace the length assertion
with specific checks for the named extensions documented in the comment (such as
'catalog-import-overrides', 'investigate-log', 'openchoreo-about', and
'workflows-or-external-ci') by using expect(ids).toContain() or similar matchers
to verify that each expected extension id is present in the ids array. This
makes the test more resilient to changes and self-documenting.

In `@plugins/openchoreo/src/alpha.test.tsx`:
- Around line 61-77: The test blocks use `as any` casts when accessing the
plugin's extensions property, which bypasses type checking. Define a type guard
interface (e.g., PluginWithInternals) that specifies the shape of the plugin
with an extensions property, then replace both `as any` casts with casts to this
new interface instead. This applies to both the first test checking for
documented blueprint extensions and the second test checking the extension
count. The interface should include the id property and the extensions array
structure to maintain type safety while still allowing internal plugin structure
access in tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93b3f3dd-bf03-4f02-868e-d1a385ab01b8

📥 Commits

Reviewing files that changed from the base of the PR and between d4dd00f and 933b02f.

📒 Files selected for processing (18)
  • .changeset/fix-nfs-migration-followups.md
  • packages/app/src/App.tsx
  • packages/app/src/apis.test.ts
  • packages/app/src/apis.ts
  • packages/app/src/apis/customOverrides.test.tsx
  • packages/app/src/apis/customOverrides.tsx
  • packages/app/src/components/DynamicSignInPage.tsx
  • packages/app/src/components/Root/Root.tsx
  • packages/app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx
  • packages/app/src/components/settings/OpenChoreoProviderSettings.tsx
  • packages/app/src/kindIcons.ts
  • packages/app/src/scaffolder/ScaffolderPreselectionContext.tsx
  • plugins/openchoreo-observability/src/alpha.test.tsx
  • plugins/openchoreo-observability/src/api/LogRowActionRendererApi.test.ts
  • plugins/openchoreo-observability/src/api/LogRowActionRendererApi.ts
  • plugins/openchoreo-workflows/src/alpha.test.tsx
  • plugins/openchoreo/src/alpha.test.tsx
  • plugins/openchoreo/src/alpha.tsx
💤 Files with no reviewable changes (1)
  • packages/app/src/apis.ts
✅ Files skipped from review due to trivial changes (5)
  • plugins/openchoreo-observability/src/alpha.test.tsx
  • plugins/openchoreo-observability/src/api/LogRowActionRendererApi.test.ts
  • packages/app/src/components/settings/OpenChoreoProviderSettings.tsx
  • packages/app/src/kindIcons.ts
  • packages/app/src/components/DynamicSignInPage.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/app/src/scaffolder/ScaffolderPreselectionContext.tsx
  • packages/app/src/components/Root/Root.tsx
  • packages/app/src/components/scaffolder/OpenChoreoScaffolderPage.tsx
  • plugins/openchoreo/src/alpha.tsx
  • packages/app/src/apis/customOverrides.tsx
  • plugins/openchoreo-observability/src/api/LogRowActionRendererApi.ts
  • packages/app/src/App.tsx

Comment thread .changeset/fix-nfs-migration-followups.md Outdated
The createApp feature reorder let the NFS page:catalog/entity extension
shadow the legacy CatalogEntityPage mount, dropping the custom
CompactEntityHeader, hand-authored Overview JSX, and OpenChoreoEntityLayout
tab styles. Override the NFS loader to render entityPage inside
OpenChoreoCatalogEntityPage and remove the now-unused legacy route mount
and host-side overview EntityCardBlueprints.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Gate EntityLayoutWithDelete on useAsyncEntity, remove the double wrap in
OpenChoreoCatalogEntityPage (per-kind pages already provide it), and
swap observability entity-tab exports to React.lazy — the unbound
rootRouteRef crashed every observability tab.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Without their /alpha features in createApp, apiDocsConfigRef,
kubernetesApiRef, and the related kubernetes apis are missing from the
api holder. The Definition tab on kind:api entities and the Kubernetes
tab on annotated components throw NotImplementedError at render time.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
Both pages rendered the small "Catalog" / "Create" title link above the
real h1. The host's CustomCatalogPage and OpenChoreoScaffolderPage each
mount their own PageWithHeader, while the NFS PageBlueprint's PageLayout
emits another. Pass noHeader: true on the page:catalog and
page:scaffolder overrides, and delete the now-redundant legacy
<Route path="/catalog"> and <Route path="/create"> mounts (and their
27 field-extension imports) from App.tsx.

Signed-off-by: Kavith Lokuhewage <kaviththiranga@gmail.com>
<OpenChoreoCatalogEntityPage>
{entityPage}
</OpenChoreoCatalogEntityPage>
);

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.

We are replacing default entity page loader here, which means that the EntityContentBlueprint.make(…) declarations across the alpha files in plugins are ignored at render time . Every tab on the entity page comes exclusively from the EntityPage.tsx that we have written.
This contradicts with the purpose of EntityContentBlueprint declarations in the new frontend system, where the Entity page loader is supposed to read the registered pages in plugins viainputs.contents and render them.
Also now we have 2 declarations for each entity page tab (one contributed by the plugin alpha file and other in the EntityPage) and in order to actual get a new page rendered a developer need to edit our EntityPage.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that's deliberate. The page:catalog/entity override returns a fixed {entityPage} and intentionally doesn't read inputs.contents, so for this portal EntityPage.tsx stays the single source of truth for tabs.

The reason isn't the tabs — it's the cards. Each per-kind Overview composes a bespoke grid (EntityCatalogGraphCard, FailedBuildSnackbar, About/Links/Labels positioning, kind-specific layouts) and OpenChoreoEntityLayout adds the dropdown header and styled tab bar. inputs.contents only hands you a flat list of tab routes — no way to express "place these cards in this grid position on the Overview of kind:component, type:service." So the override is the trade: portal keeps its bespoke layouts.

The EntityContentBlueprint declarations in the plugin alpha files aren't dead — they're what external adopters get. A host that installs our plugins against the default upstream page:catalog/entity gets Build, Deploy, Logs, Events, Metrics, etc. mounted automatically via inputs.contents, no EntityPage.tsx required. The double-declaration is the price of supporting both: bespoke portal vs. batteries-included external host.

If we ever want third-party EntityContentBlueprints to appear here too, the override can switch to factory(originalFactory, { inputs }) and merge inputs.contents deduped by path — escape hatch noted at customOverrides.tsx:128-133.

@kaviththiranga kaviththiranga Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Its basically done to make the existing layouts work in our portal - but for external adopters, NFS path is recommended.

We need to rethink about the entity page layouts and possibly migrate our entity pages completely to EntityContentBlueprints - but that's a distraction I didn't want to drag into the migration task .

We can consider it post-migration.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants