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:
44
README.md
44
README.md
@@ -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
|
`docker compose up` merges `compose.override.yml`, which mounts the source and
|
||||||
restarts the server on change. _(The Ory + Postgres services join this compose
|
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
|
## 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
|
**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.
|
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 system _(planned)_
|
||||||
|
|
||||||
The menu is **driven entirely by config** and assembled from two sources:
|
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)
|
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)
|
public/ Static assets under /public/ (css/styles.css + auth.css, favicon, robots.txt)
|
||||||
config/menu.ts Central menu override + branding (planned)
|
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)
|
docs/ Reference docs (plugin-contract.md — the authoritative plugin API)
|
||||||
e2e/ Playwright visual + functional E2E (Dockerfile.e2e + compose.e2e.yml run it)
|
e2e/ Playwright visual + functional E2E (Dockerfile.e2e + compose.e2e.yml run it)
|
||||||
html-css-foundation/ HTML design mockups — the source for the building-block
|
html-css-foundation/ HTML design mockups — the source for the building-block
|
||||||
|
|||||||
@@ -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
|
docker compose run --rm web npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Run one plugin against the host.** Drop the folder in `plugins/` and `docker compose up`;
|
2. **Run one plugin against the host.** Get the folder into the container's `/app/plugins/<id>`
|
||||||
the host discovers it. For an isolated harness, the §2 host exposes plugin injection
|
— either in your clone (the dev compose bind-mounts the tree) or by bind-mounting an external
|
||||||
(`createApp({ plugins: [myPlugin] })`) so a test can mount a single manifest and assert its
|
folder (README → *Where plugins live*) — and `docker compose up`; the host discovers it. For
|
||||||
routes, nav, and gating without the rest of the stack.
|
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
|
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
|
Playwright test in `e2e/`, side-effect-free so the suite stays `fullyParallel`. The test runs
|
||||||
|
|||||||
Reference in New Issue
Block a user