Document how plugins get into the container (README); bind-mount to /app/plugins/<id> or bake in, front-and-center under Building a plugin

This commit is contained in:
2026-06-16 11:58:28 +02:00
parent 09d616ddd3
commit 0ebe8144c2
2 changed files with 48 additions and 6 deletions

View File

@@ -112,7 +112,8 @@ docker compose up # http://localhost:3000, live reload via `node --wa
`docker compose up` merges `compose.override.yml`, which mounts the source and
restarts the server on change. _(The Ory + Postgres services join this compose
file as they land — planned.)_
file as they land — planned.)_ To work on your own plugin, see
[Where plugins live](#where-plugins-live-and-how-to-mount-them).
## Configuration
@@ -213,6 +214,45 @@ it — the plugin holds no state of its own (see below). Each plugin is
**self-contained** (its own nav, routes, views, CSS), so installing one is "drop
the folder, restart." An operator stays in control via a central override.
### Where plugins live (and how to mount them)
The host scans **`/app/plugins/`** inside the `web` container — so "installing a
plugin" means getting its folder there. There are two ways, depending on where the
plugin's source lives:
**1. In your clone (the default dev loop).** Create `plugins/<id>/` in the working
tree. `docker compose up` already bind-mounts the whole tree (`compose.override.yml`:
`.:/app`), so the folder is live in the container — restart to pick it up. This is the
"copy the example plugin and go" path.
**2. A plugin kept in its own repo, or added to a prebuilt image.** Bind-mount the
plugin folder onto `/app/plugins/<id>` with a small compose override. Plugins are
stateless, so mount it read-only:
```yaml
# compose.plugins.yml — mount external plugin folders into the host
services:
web:
volumes:
- ../scheduling-plugin:/app/plugins/scheduling:ro # host path : /app/plugins/<id>
```
```bash
# Dev: list the files explicitly (a third file disables the implicit override merge)
docker compose -f compose.yml -f compose.override.yml -f compose.plugins.yml up
# Prod (image already built, no source mount):
docker compose -f compose.yml -f compose.plugins.yml up -d
```
A named volume or volume container works the same way (target `/app/plugins/<id>`),
but a bind mount matches the edit-and-reload loop. For a **baked** production image,
just keep the plugin in the build context and it's `COPY`'d in at build time — pinned
and reproducible; mount a volume only to add plugins to an already-built image.
> _(Planned, §2.)_ Discovery — scanning `plugins/`, importing each `plugin.ts`, and
> validating it — is the next plugin-host work. The mount mechanics above are how the
> files get into the container regardless of how discovery lands.
## The menu system _(planned)_
The menu is **driven entirely by config** and assembled from two sources:
@@ -393,7 +433,7 @@ src/plugin.ts Plugin contract: manifest types, definePlugin(), version +
views/ Core EJS templates (index = the app-shell People dashboard, 403/404/500, partials/ incl. app shell, nav tree, filter bar, data table, pagination, form field, auth card, menu/popover, theme switch, icon sprite)
public/ Static assets under /public/ (css/styles.css + auth.css, favicon, robots.txt)
config/menu.ts Central menu override + branding (planned)
plugins/ Drop-in plugin folders, auto-discovered (planned)
plugins/ Drop-in plugin folders (scanned at /app/plugins; bind-mount or bake in) (planned)
docs/ Reference docs (plugin-contract.md — the authoritative plugin API)
e2e/ Playwright visual + functional E2E (Dockerfile.e2e + compose.e2e.yml run it)
html-css-foundation/ HTML design mockups — the source for the building-block

View File

@@ -234,10 +234,12 @@ A plugin is a normal folder of TypeScript, so an author tests it the same way th
docker compose run --rm web npm test
```
2. **Run one plugin against the host.** Drop the folder in `plugins/` and `docker compose up`;
the host discovers it. For an isolated harness, the §2 host exposes plugin injection
(`createApp({ plugins: [myPlugin] })`) so a test can mount a single manifest and assert its
routes, nav, and gating without the rest of the stack.
2. **Run one plugin against the host.** Get the folder into the container's `/app/plugins/<id>`
— either in your clone (the dev compose bind-mounts the tree) or by bind-mounting an external
folder (README → *Where plugins live*) — and `docker compose up`; the host discovers it. For
an isolated harness, the §2 host exposes plugin injection (`createApp({ plugins: [myPlugin] })`)
so a test can mount a single manifest and assert its routes, nav, and gating without the rest
of the stack.
3. **E2E the user-facing flow.** Per AGENTS.md §6, every plugin page/form ships *with* a
Playwright test in `e2e/`, side-effect-free so the suite stays `fullyParallel`. The test runs