Artie external plugin - #1927
Conversation
Signed-off-by: Arturo Reuschenbach Puncernau <reuschenbach@gmail.com>
Signed-off-by: Arturo Reuschenbach Puncernau <reuschenbach@gmail.com>
|
There was a problem hiding this comment.
Pull request overview
This PR adds a proof-of-concept “external plugin” flow by building the apps/template app as a standalone container image and teaching the Greenhouse shell to load a plugin module from a runtime path.
Changes:
- Added a Dockerfile for the Template app and a GitHub Actions workflow to build/push a Template plugin image.
- Injected a PoC Template plugin config into Greenhouse’s plugin registry.
- Extended
usePluginLoaderto optionally load plugin modules from a provided path and expose a loading error state to the UI.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/template/docker/Dockerfile | Builds a static Template app and serves it via nginx for external-plugin style hosting. |
| .github/workflows/build-push-template-image.yaml | CI workflow to build/push the Template plugin image to GHCR. |
| apps/greenhouse/src/routes/__root.tsx | Adds a PoC Template plugin entry into the config received from Kubernetes. |
| apps/greenhouse/src/hooks/usePluginLoader.ts | Adds optional remote/dynamic module loading and error tracking. |
| apps/greenhouse/src/components/Extension.tsx | Wires a PoC pluginPath and renders an error state when plugin load fails. |
Suppressed comments (3)
apps/greenhouse/src/hooks/usePluginLoader.ts:57
- Remote modules loaded via pluginPath are cached under appName, which can collide with bundled modules (and also makes switching pluginPath versions impossible). A minimal safe approach is to only cache bundled modules until the cache key includes pluginPath.
// Cache it for next time
if (module) {
moduleCache.set(appName, module)
}
apps/greenhouse/src/hooks/usePluginLoader.ts:96
- usePluginLoader treats a cached bundled module as "already loaded" even when pluginPath is provided, so switching a plugin to load remotely will never call getApp(). If pluginPath is set, ignore the appName-only cache for initial state so the remote module can load.
// Check if module is already cached - if so, start with it loaded!
const cachedModule = moduleCache.get(pluginName)
const [app, setApp] = useState<PluginModule | null>(cachedModule || null)
const [isLoading, setIsLoading] = useState(!cachedModule) // Only show loading if not cached
const [error, setError] = useState<Error | null>(null) // NEW
apps/greenhouse/src/hooks/usePluginLoader.ts:114
- If getApp() returns null (unknown plugin or failed remote import with no fallback), the hook currently sets isLoading=false and renders an empty container with no error. Convert a null module into an Error so Extension can show the failure state.
const appModule = await getApp(pluginName, pluginPath) // Pass pluginPath
if (!cancelled) {
setApp(appModule)
setIsLoading(false)
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // Merge with Kubernetes configs | ||
| const allConfigs = { | ||
| ...kubernetesConfig, | ||
| template: templateConfig, | ||
| } |
| build-and-push: | ||
| name: Build and Push Template Plugin Image | ||
| runs-on: [ubuntu-latest] |
| // Return cached module immediately if available | ||
| if (moduleCache.has(appName)) { | ||
| return moduleCache.get(appName)! | ||
| } |
Signed-off-by: Arturo Reuschenbach Puncernau <reuschenbach@gmail.com>
Signed-off-by: Arturo Reuschenbach Puncernau <reuschenbach@gmail.com>
Signed-off-by: Arturo Reuschenbach Puncernau <reuschenbach@gmail.com>
Signed-off-by: Arturo Reuschenbach Puncernau <reuschenbach@gmail.com>
Summary
Changes Made
Related Issues
Screenshots (if applicable)
Testing Instructions
pnpm ipnpm TASKChecklist
PR Manifesto
Review the PR Manifesto for best practises.