Skip to content

feat: integrate dynamic plugin infrastructure - #1109

Closed
b3lix wants to merge 2 commits into
freeipa:mainfrom
b3lix:plugin-infrastructure-poc
Closed

feat: integrate dynamic plugin infrastructure#1109
b3lix wants to merge 2 commits into
freeipa:mainfrom
b3lix:plugin-infrastructure-poc

Conversation

@b3lix

@b3lix b3lix commented May 5, 2026

Copy link
Copy Markdown
Collaborator

Add plugin loading support to the Modern WebUI using @freeipa/plugin-sdk. Plugins are discovered via a server manifest and loaded at runtime using dynamic import(), without requiring a rebuild of the host application. Changes:

  • Expose shared dependencies (React, PatternFly, Redux) for plugins
  • Load plugins asynchronously before initial render
  • Support dynamic route and navigation item registration from plugins
  • Support dynamic Redux reducer injection via store.replaceReducer()
  • Add Dashboard page with ExtensionSlot for plugin-contributed widgets

Summary by Sourcery

Integrate a dynamic plugin infrastructure into the Modern WebUI and introduce a dashboard landing page that exposes extension points for plugin-provided content.

New Features:

  • Add runtime plugin loading and registration via @freeipa/plugin-sdk, including shared dependency exposure and lifecycle hooks.
  • Enable plugins to contribute navigation items and routes that are rendered alongside core application routes.
  • Introduce a Dashboard page as the new default landing view, with an extension slot for plugin-contributed widgets.

Enhancements:

  • Allow dynamic Redux reducer injection at runtime to support plugin-managed state.
  • Load plugins asynchronously during application bootstrap before the initial React render to ensure plugin contributions are available early.

Build:

  • Add @freeipa/plugin-sdk as an application dependency.

Add plugin loading support to the Modern WebUI using @freeipa/plugin-sdk.
Plugins are discovered via a server manifest and loaded at runtime using
dynamic import(), without requiring a rebuild of the host application.
Changes:
- Expose shared dependencies (React, PatternFly, Redux) for plugins
- Load plugins asynchronously before initial render
- Support dynamic route and navigation item registration from plugins
- Support dynamic Redux reducer injection via store.replaceReducer()
- Add Dashboard page with ExtensionSlot for plugin-contributed widgets

Signed-off-by: Erik Belko <ebelko@redhat.com>
@b3lix b3lix self-assigned this May 5, 2026
@b3lix b3lix added the WIP Work in Progress (do not merge) label May 5, 2026

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • The async bootstrap flow currently assumes loadPlugins and pluginRegistry.runPhase('ready') always succeed; consider wrapping these in a try/catch with logging and a fallback to rendering the core app so a plugin failure doesn’t prevent the UI from loading entirely.
  • In injectPluginReducer, silently returning when a key already exists can hide plugin integration issues; consider at least logging a warning or throwing for duplicate reducer keys so plugin collisions are easier to diagnose.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The async `bootstrap` flow currently assumes `loadPlugins` and `pluginRegistry.runPhase('ready')` always succeed; consider wrapping these in a try/catch with logging and a fallback to rendering the core app so a plugin failure doesn’t prevent the UI from loading entirely.
- In `injectPluginReducer`, silently returning when a key already exists can hide plugin integration issues; consider at least logging a warning or throwing for duplicate reducer keys so plugin collisions are easier to diagnose.

## Individual Comments

### Comment 1
<location path="src/main.tsx" line_range="55-68" />
<code_context>
-    </Provider>
-  </React.StrictMode>
-);
+async function bootstrap() {
+  await loadPlugins();
+  await pluginRegistry.runPhase("ready");
+
+  root.render(
+    <React.StrictMode>
+      <Provider store={store}>
+        <BrowserRouter basename={URL_PREFIX}>
+          <App />
+        </BrowserRouter>
+      </Provider>
+    </React.StrictMode>
+  );
+}
+
+bootstrap();
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider adding basic error handling around the async bootstrap pipeline.

`loadPlugins()` and `pluginRegistry.runPhase("ready")` are awaited without any error handling, so a failure in any plugin will prevent the app from rendering and leave users on a blank page. Wrap these calls in a `try/catch` that logs the error and still renders either the core app without the failing plugin(s) or a dedicated error screen, so one bad plugin doesn’t break the whole UI.

```suggestion
async function bootstrap() {
  try {
    await loadPlugins();
    await pluginRegistry.runPhase("ready");

    root.render(
      <React.StrictMode>
        <Provider store={store}>
          <BrowserRouter basename={URL_PREFIX}>
            <App />
          </BrowserRouter>
        </Provider>
      </React.StrictMode>
    );
  } catch (error) {
    // Ensure a plugin failure doesn't leave the user on a blank screen
    console.error("Failed to initialize plugins during bootstrap:", error);

    root.render(
      <React.StrictMode>
        <Provider store={store}>
          <BrowserRouter basename={URL_PREFIX}>
            <div role="alert" style={{ padding: 24 }}>
              <h1>We couldn’t load all extensions</h1>
              <p>
                Some parts of the application failed to start correctly. You can try
                refreshing the page. If the problem persists, please contact support.
              </p>
            </div>
          </BrowserRouter>
        </Provider>
      </React.StrictMode>
    );
  }
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/main.tsx
Comment on lines +55 to +68
async function bootstrap() {
await loadPlugins();
await pluginRegistry.runPhase("ready");

root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter basename={URL_PREFIX}>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Consider adding basic error handling around the async bootstrap pipeline.

loadPlugins() and pluginRegistry.runPhase("ready") are awaited without any error handling, so a failure in any plugin will prevent the app from rendering and leave users on a blank page. Wrap these calls in a try/catch that logs the error and still renders either the core app without the failing plugin(s) or a dedicated error screen, so one bad plugin doesn’t break the whole UI.

Suggested change
async function bootstrap() {
await loadPlugins();
await pluginRegistry.runPhase("ready");
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter basename={URL_PREFIX}>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
}
async function bootstrap() {
try {
await loadPlugins();
await pluginRegistry.runPhase("ready");
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter basename={URL_PREFIX}>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
} catch (error) {
// Ensure a plugin failure doesn't leave the user on a blank screen
console.error("Failed to initialize plugins during bootstrap:", error);
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter basename={URL_PREFIX}>
<div role="alert" style={{ padding: 24 }}>
<h1>We couldn’t load all extensions</h1>
<p>
Some parts of the application failed to start correctly. You can try
refreshing the page. If the problem persists, please contact support.
</p>
</div>
</BrowserRouter>
</Provider>
</React.StrictMode>
);
}
}

Signed-off-by: Erik Belko <ebelko@redhat.com>
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

This PR has not received any attention in 60 days.

@github-actions github-actions Bot added the stale This PR/issue is stale and will be closed label Jul 4, 2026
@carma12

carma12 commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Hi @b3lix - Do you plan to continue working on this PR?

@github-actions github-actions Bot removed the stale This PR/issue is stale and will be closed label Jul 6, 2026
@duzda

duzda commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Closing, as duplicate of #1110, also is present in it's own repo.

@duzda duzda closed this Jul 9, 2026
@b3lix

b3lix commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator Author

This PR was opened for review of PoC, infrastructure has its own repository. This can be reopened in future.

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

Labels

WIP Work in Progress (do not merge)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants