Skip to content

[poc]lightweight plugin system to the Eclipse Che Dashboard#1552

Open
olexii4 wants to merge 14 commits into
mainfrom
plugins
Open

[poc]lightweight plugin system to the Eclipse Che Dashboard#1552
olexii4 wants to merge 14 commits into
mainfrom
plugins

Conversation

@olexii4
Copy link
Copy Markdown
Contributor

@olexii4 olexii4 commented May 4, 2026

What does this PR do?

Add lightweight plugin system to the Eclipse Che Dashboard.

It lets optional features be developed, versioned, and distributed
independently of the dashboard core, without forking the main repository.

The system has three parts:

  1. packages/dashboard-plugins — shared type package and runtime registry
    (the "plugin SDK"). Published as @eclipse-che/dashboard-plugins.
  2. Plugin bundles — independent source trees in a separate repository
    (olexii4/che-dashboard-plugins),
    released as ZIP archives on GitHub Releases.
  3. Dashboard integration — a thin wiring layer (scripts/fetch-plugins.sh,
    scripts/prepare-plugins.sh, src/plugins/) that downloads and mounts
    plugins into the dashboard build.

packages/dashboard-plugins — the Plugin SDK

Purpose

Defines the TypeScript contracts (types, interfaces) that both the dashboard
core and plugin authors depend on. Also provides the runtime plugin registry
used by the frontend at startup.

No business logic lives here. The package exists purely to establish a
stable API surface between the dashboard and its plugins.

Package contents

packages/dashboard-plugins/
├── src/
│   ├── types.ts         # All plugin interfaces (FrontendPlugin, PluginSlots, …)
│   ├── registry.ts      # Runtime plugin registration and retrieval
│   ├── index.ts         # Public re-exports
│   └── frontend/
│       └── PluginSlot.tsx  # React slot component + slot getter functions

Type reference

PluginManifest

interface PluginManifest {
  id: string;          // unique plugin identifier
  name: string;
  version: string;
  description: string;
  enabled: boolean;    // if false, plugin is ignored at registration time
}

FrontendPlugin

interface FrontendPlugin {
  manifest: PluginManifest;
  reducerKey: string;          // key in the Redux root reducer
  reducer: Reducer;            // Redux reducer for plugin state
  bootstrap?: (store: Store) => Promise<void>;  // called once after store init
  slots: PluginSlots;
  workspaceHooks?: WorkspaceHooks;
}

PluginSlots — extension points

Slot Type Where rendered
workspaceCreation ComponentType Create Workspace page
workspaceDetailsOverview ComponentType Workspace Details — Overview tab
workspacesListColumn ColumnDefinition Workspaces list table — extra column
userPreferencesTab TabDefinition User Preferences — extra tab
factoryParams FactoryParamExtension Factory URL parameter parsing
navigationItems NavigationItemDefinition[] Sidebar navigation
loaderTabs LoaderTabDefinition[] Starting workspace — loader tabs

NavigationItemDefinition

interface NavigationItemDefinition {
  to: string;                               // route path, e.g. '/devfiles'
  label: string;                            // static label fallback
  labelSelector?: (state: unknown) => string; // dynamic label from Redux state
  insertAfter?: string;                     // to-path of the item to insert after
}

insertAfter controls ordering without the plugin knowing the full nav list.
Example: insertAfter: '/create-workspace' places "Devfiles" immediately after
"Create Workspace".

LoaderTabDefinition

interface LoaderTabDefinition {
  key: string;          // tab eventKey, e.g. 'DevWorkspace'
  title: string;        // tab label
  component: ComponentType<{ workspace: unknown; isActive: boolean }>;
  insertAfter?: string; // key of the tab to insert after
}

WorkspaceHooks

interface WorkspaceHooks {
  // Called when a workspace is created — lets a plugin inject DevWorkspace patches
  onWorkspaceCreate?: (workspace, factoryParams) => workspace;
  // Called when a workspace is started — lets a plugin block or mutate the start
  onWorkspaceStart?: (workspace) => workspace | null;
}

Registry API (used by the dashboard core)

// Register on startup (called from src/plugins/index.ts)
registerFrontendPlugin(plugin: FrontendPlugin): void

// Read at render time (called by Navigation, Loader, UserPreferences, etc.)
getRegisteredFrontendPlugins(): FrontendPlugin[]
getPluginNavigationItems(): NavigationItemDefinition[]
getPluginLoaderTabs(): LoaderTabDefinition[]
getPluginTabs(): TabDefinition[]       // User Preferences tabs
getPluginColumns(): ColumnDefinition[] // Workspaces list columns

Screenshot/screencast of this PR

What issues does this PR fix or reference?

Is it tested? How?

Release Notes

Docs PR

@olexii4 olexii4 requested review from akurinnoy and ibuziuk as code owners May 4, 2026 00:47
@che-bot
Copy link
Copy Markdown
Contributor

che-bot commented May 4, 2026

Click here to review and test in web IDE: Contribute

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 4, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: olexii4

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@olexii4 olexii4 changed the title [poc] lightweight plugin system to the Eclipse Che Dashboard [poc][wip] lightweight plugin system to the Eclipse Che Dashboard May 4, 2026
@olexii4 olexii4 marked this pull request as draft May 4, 2026 00:57
@olexii4 olexii4 force-pushed the plugins branch 6 times, most recently from fffca8b to 48b1f58 Compare May 4, 2026 13:37
@olexii4 olexii4 force-pushed the plugins branch 11 times, most recently from a4c8897 to 0fb931b Compare May 5, 2026 13:40
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

Docker image build succeeded: quay.io/eclipse/che-dashboard:pr-1552 (linux/amd64, linux/arm64)

kubectl patch command
kubectl patch -n eclipse-che "checluster/eclipse-che" --type=json -p="[{"op": "replace", "path": "/spec/components/dashboard/deployment", "value": {containers: [{image: "quay.io/eclipse/che-dashboard:pr-1552", name: che-dashboard}]}}]"

1 similar comment
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

Docker image build succeeded: quay.io/eclipse/che-dashboard:pr-1552 (linux/amd64, linux/arm64)

kubectl patch command
kubectl patch -n eclipse-che "checluster/eclipse-che" --type=json -p="[{"op": "replace", "path": "/spec/components/dashboard/deployment", "value": {containers: [{image: "quay.io/eclipse/che-dashboard:pr-1552", name: che-dashboard}]}}]"

Introduces @eclipse-che/dashboard-plugins — the extension SDK for
Eclipse Che Dashboard plugins:

packages/dashboard-plugins/src/
- types.ts: FrontendPlugin, BackendPlugin, PluginManifest, PluginSlots,
  TabDefinition, ColumnDefinition, NavigationItemDefinition,
  LoaderTabDefinition, WorkspaceHooks interfaces; ComponentType<any>
  replaced with ComponentType<Record<string, unknown>>
- registry.ts: registerFrontendPlugin/BackendPlugin, bootstrapPlugins,
  runPluginCreateHooks, runPluginStartHooks, getPluginFactoryParams;
  duplicate-ID guard; debug console.log calls removed
- frontend/PluginSlot.tsx: React component that renders plugin-provided
  content into named slots; generic collectFromPlugins<T,R> helper
- backend/index.ts, index.ts: public API re-exports

packages/dashboard-frontend/src/plugin-registry/
- index.ts: runtime registry (registerFrontendPlugin, bootstrapPlugins,
  getPluginTabs, getPluginNavigationItems, getPluginLoaderTabs)
- PluginSlot.tsx: PluginSlot component consumed by dashboard pages

Removes postinstall hook from package.json — plugin fetching is an
explicit step (yarn prepare:plugins) and must not run on every install.

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
olexii4 added 3 commits May 6, 2026 19:27
…rd core

Minimal glue code connecting the plugin system to the running dashboard.
No plugin-specific logic lives here — only registration hooks and slots.

Backend:
- app.ts: register plugin routes via symlink paths
- devworkspaceClient/index.ts: remove plugin-specific aiProviderKeyApi
  and aiRegistryApi getters — plugin routes instantiate services directly
- devworkspaceClient/types/index.ts: remove IAiProviderKeyApi,
  IAiRegistryApi interfaces and their entries from IDevWorkspaceClient
- devworkspaceClient/__mocks__/index.ts: remove stubs for removed getters
- routes/api/websocket.ts: add CONFIGMAP channel subscription stub
- tsconfig.json: add paths for plugin symlinks

Common:
- dto/api/webSocket.ts: add CONFIGMAP channel to Channel enum
- dto/api/index.ts: add IAiAgentRegistry, IAiRegistry, IAiProviderKey DTOs

Frontend:
- store/rootReducer.ts: register plugin reducers via symlink paths
- services/bootstrap/index.ts: call bootstrapPlugins(); add dispatch helper
- Layout/Navigation/MainList.tsx: insert plugin navigation items
- Layout/Sidebar/index.tsx + index.module.css: agent nav section
- pages/GetStarted/index.tsx: PluginSlot for workspaceCreation
- pages/Loader/index.tsx: plugin loader tabs via getPluginLoaderTabs()
- pages/UserPreferences/index.tsx: widen activeTabKey to string for
  plugin-defined tab keys; remove AI_PROVIDER_KEYS from core enum
- Routes/index.tsx: lazy-loaded plugin routes
- services/helpers/types.ts: remove AI_PROVIDER_KEYS from UserPreferencesTab
- services/helpers/location.ts: widen to accept string for plugin tabs
- webpack.config.common.js: suppress TS6059 for symlinked plugin files;
  add per-plugin alias resolution before the general '@' alias
- index.tsx: service worker registration via try/catch

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
- overrides.css: WCAG colour contrast improvements; label-required styling
  via global danger colour token; dark theme label contrast overrides
- DevfileViewer: refactor to @uiw/react-codemirror with useMemo theming;
  remove obsolete snapshot; update test to use direct unit testing
- BackupStatusBadge, ExpandableWarning: CSS module and component fixes
- BasicViewer, ResourceIcon, Workspace/Status: minor CSS adjustments
- websocketClient: add CONFIGMAP channel subscription helpers
- WorkspacesList: AI tool column via plugin slot; Rows.tsx update
- UserPreferences/GitConfig/Form/SectionUser: validation improvements
- backend schemas.ts: add DevWorkspace and Devfile schema route constants
- patchOptions.ts, restParams.ts, backend package.json: minor additions

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
scripts/fetch-plugins.sh (new):
- Downloads plugin ZIPs from GitHub Releases for a given repo and ref
- PLUGINS_LIST unset: queries Releases API, installs all plugins found
- PLUGINS_LIST set (JSON array): installs only the listed plugins
- LOCAL_PLUGINS: skips download, copies from a local checkout instead
- Uses read loop instead of mapfile for Alpine sh / POSIX compatibility

scripts/prepare-plugins.sh (new):
- Wires fetched plugin files into the dashboard source tree
- Generates packages/dashboard-frontend/src/plugins/index.ts

build/dockerfiles/Dockerfile:
- ARG PLUGINS_REPO, PLUGINS_REF, PLUGINS_LIST to control which plugins
  are fetched and from where
- Fetches and prepares plugins before yarn build:packages
- PLUGINS_LIST unset = install all; JSON array = install subset

run/local-patch.sh (new): helper for local development patching

.deps: update dependency declarations
.gitignore: ignore plugins/ directory and generated index

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
@olexii4 olexii4 force-pushed the plugins branch 2 times, most recently from a660342 to 4af7524 Compare May 6, 2026 16:42
- dashboard-plugins/package.json: add --passWithNoTests so jest exits
  cleanly when no test files exist yet in the SDK package
- Layout/Sidebar/__tests__: mock NavigationAgentList; update snapshot
  to reflect the new mainNavWrapper + agentNavWrapper sidebar layout
- pages/GetStarted/__tests__: update snapshot for PluginSlot spacer
- services/bootstrap/__tests__: mock subscribeToAgentPodChanges and
  requestAiAgentRegistry; update imports to plugin symlink paths

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
olexii4 added 8 commits May 6, 2026 21:49
Plugin routes agents.ts, devfiles.ts now use relative imports for
helpers that were moved into the plugin repository, fixing the webpack
'Can't resolve @/routes/api/helpers/*' build errors.

Fix is in olexii4/che-dashboard-plugins@9d7c0b2

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
The private dispatch<T>() helper returns T but TypeScript cannot prove
T is a Promise at the call site, causing TS2571 ('Object is of type
unknown') when .catch() is chained. Revert to the explicit 3-argument
thunk invocation for the one case that needs .catch() on the result.

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
tabOrder.indexOf() previously rejected string when activeTabKey was
widened to UserPreferencesTab | string. Cast tabOrder to
ReadonlyArray<string> for the indexOf call so plugin-registered tab keys
(plain strings) are accepted.

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
Signed-off-by: Oleksii Orel <oorel@redhat.com>
Signed-off-by: Oleksii Orel <oorel@redhat.com>
Signed-off-by: Oleksii Orel <oorel@redhat.com>
Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

Docker image build succeeded: quay.io/eclipse/che-dashboard:pr-1552 (linux/amd64, linux/arm64)

kubectl patch command
kubectl patch -n eclipse-che "checluster/eclipse-che" --type=json -p="[{"op": "replace", "path": "/spec/components/dashboard/deployment", "value": {containers: [{image: "quay.io/eclipse/che-dashboard:pr-1552", name: che-dashboard}]}}]"

- EXCLUDED/prod.md: remove 10 stale entries for packages no longer in
  production deps (moved to plugins repo); add
  @eclipse-che/common@workspace:packages/common (local workspace package,
  unresolvable by dash-licenses)
- EXCLUDED/dev.md: remove 2 stale entries; add
  @eclipse-che/license-tool@2.0.0 (dev tool, unresolvable by dash-licenses)
- prod.md / dev.md: regenerate from current yarn.lock

Assisted-by: Claude Sonnet 4.6
Signed-off-by: Oleksii Orel <oorel@redhat.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

Docker image build succeeded: quay.io/eclipse/che-dashboard:pr-1552 (linux/amd64, linux/arm64)

kubectl patch command
kubectl patch -n eclipse-che "checluster/eclipse-che" --type=json -p="[{"op": "replace", "path": "/spec/components/dashboard/deployment", "value": {containers: [{image: "quay.io/eclipse/che-dashboard:pr-1552", name: che-dashboard}]}}]"

@olexii4 olexii4 marked this pull request as ready for review May 7, 2026 11:52
@olexii4 olexii4 changed the title [poc][wip] lightweight plugin system to the Eclipse Che Dashboard [poc]lightweight plugin system to the Eclipse Che Dashboard May 7, 2026
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 17, 2026

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

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.

2 participants