Docs: release/rollout flow after the instance-ownership refactor
Test / test (push) Successful in 23s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-24 21:49:31 +02:00
co-authored by Claude Fable 5
parent 8858336ecc
commit 74965376bd
+54 -15
View File
@@ -38,9 +38,10 @@ it special-cases none of it.
## Binaries ## Binaries
- `portal` — the server (Leptos SSR + hydrate, Axum underneath). - `portal` — the server (Leptos SSR + hydrate, Axum underneath).
- `question_lint` — headless content validation, run by the - `question_lint` — headless content validation, shipped inside every
`questions` repo's CI against a prebuilt copy this repo's deploy release artifact so each content repo's CI lints with the exact
publishes; also works offline: `question_lint --path <dir>`. portal version its instance runs; also works offline:
`question_lint --path <dir>`.
## Development ## Development
@@ -50,17 +51,55 @@ cargo test --features ssr # engine tests
cargo build --features ssr --bin question_lint cargo build --features ssr --bin question_lint
``` ```
Runtime configuration is env vars (see `src/main.rs` and Runtime configuration is env vars (see `src/main.rs`, and each
`.gitea/workflows/deploy.yml`): `NATS_URL`, `CONTENT_REPO`/ content repo's `.gitea/workflows/deploy.yml` for the values in
`CONTENT_BRANCH`, Kanidm OIDC (`KANIDM_URL`, `OAUTH2_CLIENT_*`), production): `NATS_URL`, `CONTENT_REPO`/`CONTENT_BRANCH`, Kanidm OIDC
optional `GARAGE_*` for uploads, `GITEA_API_TOKEN` for authenticated (`KANIDM_URL`, `OAUTH2_CLIENT_*`), optional `GARAGE_*` for uploads,
resource pulls, `AUTOMATION_READ_TOKEN` for the automation KV read `GITEA_API_TOKEN` for authenticated resource pulls,
endpoint. `AUTOMATION_READ_TOKEN` for the automation KV read endpoint.
## Deploy ## Release and deploy
Pushing `main` triggers `.gitea/workflows/deploy.yml` on the Portal doesn't deploy itself — it publishes versions, and each site
bare-metal runner: release build, ship to decides when to take one. The whole day-to-day surface is two
`/srv/app/uhhm-portal/releases/<sha>`, flip the `current` symlink, commands:
restart `app@uhhm-portal`, reload Caddy. Content changes never come
through here — they hot-reload live from the `questions` repo. **Cut a release** (here):
```sh
cargo release patch # or minor / major
```
That bumps `Cargo.toml`, tags `v<version>`, and pushes; CI
(`.gitea/workflows/publish.yml`) reacts to the tag, builds once, and
attaches `portal-v<version>.tar.gz` (`portal` + `question_lint` +
`site/`) to the Gitea release. Plain pushes to `main` only run the
tests (`test.yml`) — nothing reaches production from this repo.
**Roll it out** (in a content repo): edit one line in that repo's
`.gitea/workflows/deploy.yml`
```yaml
env:
PORTAL_RELEASE: v0.1.1 # <- bump, commit, push
```
Its CI downloads the pinned artifact, ships
`/srv/app/<instance>/releases/<tag>`, flips `current`, rewrites the
instance env from that repo's own Actions variables/secrets, restarts
`app@<instance>`, and refreshes the Caddy route. Every rollout is a
commit, so reverting a bad version is `git revert` + push. Sites
upgrade independently: uhhm.no (uhhm/questions → `app@uhhm-portal`,
:3010) and redoal.com (redoal/questions → `app@redoal-portal`,
:3020) can pin different versions.
Content changes never come through any of this — they hot-reload
live from the content repos over NATS.
**Add a site**: new content repo with a copy of an existing
`deploy.yml` (change `INSTANCE`, port, domains), Actions variables
(`KANIDM_URL`, `OAUTH2_CLIENT_ID`, `PUBLIC_URL`, `SITE_NAME`) and
secrets (`NATS_URL`, `OAUTH2_CLIENT_SECRET`,
`PORTAL_GITEA_API_TOKEN` — Gitea reserves the `GITEA_` prefix for
secret names), a Kanidm OAuth2 client, and DNS. `site.yaml` in the
content repo handles all branding; no portal changes needed.