67b07634ae
- app/: Emdash scaffold (Astro 6, node target) with cmses/plugins/pages collections - app/seed/seed.json: WordPress→Emdash parity for kotkanagrilli.fi (~30 entries) - Dockerfile + docker/entrypoint.sh: multi-stage build, single PVC at /app/state - deploy/helm/: chart mirroring emdash-kotkanagrilli (single-replica, sqlite, kotkan) - deploy/fleet-overlay/: HelmRelease/source/image-automation templates for anton-helm-workloads (staging + production) - .woodpecker/container.yaml: arm64 build, three OCI tags per push (immutable 0.1.<pipeline> + floating <branch> + <branch>-latest) - .ddev/: local dev with nginx proxy to emdash on :4321 - README/DEPLOYMENT/ARCHITECTURE/CLAUDE: docs covering the three-repo pipeline (cms-plugins + anton-helm-workloads + Gitea OCI registry)
129 lines
5.6 KiB
Markdown
129 lines
5.6 KiB
Markdown
# Architecture
|
|
|
|
This file is the contract between the build (Dockerfile, Woodpecker, Helm chart)
|
|
and the FluxCD reconciliation in `anton-helm-workloads`. See `DEPLOYMENT.md` for
|
|
the operational walkthrough.
|
|
|
|
## Repo shape
|
|
|
|
```
|
|
cms-plugins/
|
|
├── app/ ← Emdash/Astro source
|
|
│ ├── astro.config.mjs ← node target, sqlite() + local()
|
|
│ ├── package.json
|
|
│ ├── src/ ← pages, layouts, components
|
|
│ └── seed/seed.json ← collections + kotkanagrilli plugin entries
|
|
├── Dockerfile ← multi-stage build, single PVC at /app/state
|
|
├── docker/entrypoint.sh ← runs `emdash init` on first boot
|
|
├── deploy/
|
|
│ ├── helm/ ← chart consumed by Flux directly from this repo
|
|
│ └── fleet-overlay/ ← HelmRelease/source/image-automation templates
|
|
│ ├── cms-plugins-staging/ ← copy → anton-helm-workloads/cms-plugins-staging/
|
|
│ └── cms-plugins-production/ ← copy → anton-helm-workloads/cms-plugins-production/
|
|
├── .woodpecker/container.yaml ← build pipeline
|
|
└── .ddev/ ← local dev (DDEV nginx → emdash service on :4321)
|
|
```
|
|
|
|
## Build pipeline
|
|
|
|
A push to `develop` / `staging` / `production` runs
|
|
`.woodpecker/container.yaml`. The pipeline labels itself `arch: arm64`
|
|
because the deploy target (`kotkan`) is an arm64 node, so building
|
|
natively avoids a cross-compile step.
|
|
|
|
For each build the pipeline pushes three OCI tags to
|
|
`git.oleks.space/oleks/cms-plugins`:
|
|
|
|
| Tag | Mutability | Purpose |
|
|
|---|---|---|
|
|
| `0.1.<pipeline>` | Immutable | Audit trail. Use as a rollback target. |
|
|
| `<branch>` | Floating | Watched by FluxCD's `ImagePolicy`. Retagged on every build. |
|
|
| `<branch>-latest` | Floating | Cosmetic. Resolves the chart's `image.tag` fallback to a real ref when the digest path is unset. |
|
|
|
|
Only the `staging` and `production` branches have an `ImagePolicy`, so
|
|
only those move pods.
|
|
|
|
## Container shape
|
|
|
|
Multi-stage Dockerfile:
|
|
|
|
- `deps` — `node:22-bookworm-slim` + `python3 make g++` (better-sqlite3
|
|
build deps). Falls back to `npm install` if there's no committed lockfile.
|
|
- `build` — `npm run build`.
|
|
- `runtime` — `node:22-bookworm-slim` + `tini`. Runs as uid 1001. Persistent
|
|
state symlinked from `/app/data.db` and `/app/uploads` → `/app/state/...`
|
|
so the one PVC mounted at `/app/state` covers both SQLite and uploaded
|
|
media.
|
|
|
|
Entrypoint runs `emdash init` on first boot (idempotent), then `exec`s
|
|
the Astro node server on port 4321.
|
|
|
|
## Helm chart
|
|
|
|
`deploy/helm/` ships with the app. The chart is **not** packaged or pushed
|
|
to an OCI registry — FluxCD's `GitRepository` source pulls it directly from
|
|
the matching branch of this repo. The `ignore` rule in `source.yaml`
|
|
restricts reconciliation to `/deploy/helm`, so app-source pushes don't
|
|
trigger chart re-reconcile.
|
|
|
|
Per-env values overlay `values.yaml` via `values-staging.yaml` /
|
|
`values-production.yaml` (for `helm upgrade` direct use) and via the
|
|
`values:` block in the FluxCD `HelmRelease`.
|
|
|
|
Key shape decisions:
|
|
|
|
- **`replicas: 1`, `strategy: Recreate`.** SQLite is single-writer.
|
|
- **`nodeSelector: kotkan`.** `local-path` PV is sticky to one node; emdash
|
|
is colocated with the legacy kotkanagrilli WP install.
|
|
- **Pinned by digest, not tag.** The HelmRelease sets
|
|
`values.image.digest: "<sha256:...>" # {"$imagepolicy": ...}` so that
|
|
`helm upgrade` detects a change when the floating tag is reassigned.
|
|
Without digest pinning, `staging` stays the literal string `"staging"`
|
|
through every build and Helm sees no spec change.
|
|
- **Single PVC, `helm.sh/resource-policy: keep`.** Losing the PVC means
|
|
losing all CMS data — uninstalls do not delete it.
|
|
|
|
## Flux contract
|
|
|
|
Two repos cooperate:
|
|
|
|
- **cms-plugins** (this one) exposes the chart at `deploy/helm/` on each
|
|
branch.
|
|
- **anton-helm-workloads** runs the show: `GitRepository` points at the
|
|
matching branch of cms-plugins, `HelmRelease` references
|
|
`chart: ./deploy/helm`, `ImagePolicy` + `ImageUpdateAutomation` watch
|
|
the floating tag and rewrite the digest setter back into
|
|
`helmrelease.yaml`.
|
|
|
|
For the workloads repo to write digest commits, a `GitRepository` named
|
|
`anton-workloads-image-automation` in `flux-system` namespace must exist
|
|
with write access to `anton-helm-workloads:main`. The emdash-kotkanagrilli
|
|
analogue is `oleks-fleet-image-automation` in the personal fleet repo.
|
|
|
|
## Data model
|
|
|
|
Three Emdash collections (`app/seed/seed.json`):
|
|
|
|
- **cmses** — CMS platforms. Seeded with WordPress + Emdash.
|
|
- **plugins** — one entry per plugin. Fields: title, purpose, source_cms,
|
|
target_cms, category, status (`port`/`built-in`/`saas`/`drop`/`gated`/`done`/`proposed`),
|
|
source_repo_url, target_repo_url, notes (portableText).
|
|
- **pages** — static content (About, Submit, etc.).
|
|
|
|
The seeded plugin entries come from
|
|
`~/projects/emdash.kotkanagrilli.fi/docs/parity.md` and the
|
|
`mu-plugins/` / `plugins-custom/` directories in
|
|
`~/projects/kotkanagrilli.fi/`. New entries are added through the Emdash
|
|
admin (`/_emdash/admin`).
|
|
|
|
## What this does NOT solve
|
|
|
|
- **Cloudflare Workers target.** The kotkanagrilli sister project has
|
|
Cloudflare boundary files (`src/worker.ts`, `wrangler.jsonc`) for a
|
|
later flip; this repo skips them. They can be added back from the
|
|
emdash-kotkanagrilli pattern when needed.
|
|
- **Authentication on the admin.** Emdash's default passkey auth runs;
|
|
the first visit to `/_emdash/admin` redirects to `/setup` and creates
|
|
the first admin.
|
|
- **Multi-language.** No i18n — the catalog is English-only by default.
|