feat(demo): deploy/demo and the wipe + baseline workflows (step 5) #227

Merged
whitlocktech merged 1 commits from feature/demo-deploy into main 2026-10-27 12:31:48 -05:00
Member

What & why

Step 5 of the demo plan (website#222, docs/website/DEMO_MODE.md §7 and §10). These are the files that deploy and wipe demo.runicgateway.com on the demo-site runner. Decisions DM22–DM24 were taken today and are recorded in the docs PR.

File What it does
deploy/demo/docker-compose.yml Runs the app and its database only; no bot and no ntfy (§5). Sets DEMO_MODE=true and adds a healthcheck on /status. Mounts the fixture at /app/deploy/demo/seed.json. MODULES comes from state/modules.env as a second env_file, so the workflow never edits the .env that holds the secrets. The project name is fixed (rg-demo) so the smoke check can join the network.
deploy/demo/.env.example Lists every variable the host needs. Two would have stopped a first deploy: BOT_INTERNAL_KEY, because the app refuses to start in production without it even with no bot (found by running it), and MODULE_SOURCE_HOSTS, because a fresh database seeds its install allowlist from it, so without it the declared module is refused.
deploy/demo/seed.json The four logins, site_title and status_message, two news posts, a wiki page, and one published event, "Server greeting", whose step is rust.command.run running say …. Also the rig's server link and demo-player's Steam link. Passwords, the Steam id and the sidecar URL/token are "$env:NAME".
deploy/demo/rig.mjs Dependency-free and run in node:22.23.4-alpine. plan picks the latest bundle current.json (read through /contents/, not the CDN-cached /raw/) plus the newest Module-Rust release with an equal protocol, and keeps the previous set when none matches. restore kills the rig, writes a changed bundle (plugin .cs files, sidecar, launcher, chmod each one, plus the rig's rust-link/bundle.json), deletes the three directories, unpacks demo-baseline.tar.gz, then starts the rig. baseline does a power stop (which saves the world, DM22), compresses, renames and starts. smoke waits for the site, then waits a bounded time for the rig to report in.
.gitea/workflows/demo-wipe.yml Runs every 2 h on the hour (UTC), on a push to deploy/demo/**, and by hand. Order: plan → rig → down -v / pull / up / seed:demo → smoke. A failed rig restore still wipes the site, then fails the run. Shares concurrency group demo-rig with the baseline workflow.
.gitea/workflows/demo-baseline.yml workflow_dispatch only.
deploy/demo/README.md One-time host setup and what state/ holds.

How it was tested

  • Site half, locally, against an image built from main with Module-Rust 0.6.0 assembled from edge the way its release CI does it. Ran down -v → up → healthy → seed:demo with this fixture. Results: the 4 accounts, content (2 posts, 1 wiki page, 1 event, which validated and published), rust seeded its server and link, and demo_state was stamped. GET /public/settings lists 3 logins (play-review is hidden) with resetsAt on the next even hour. GET /public/rust/servers shows the demo server. A multipart POST gets 403.
  • rig.mjs plan against the real Gitea read bundle 2026.10.22 (protocol 14). It correctly refuses for now: no released module speaks protocol 14 until Module-Rust#64 releases v0.6.0. smoke joined rg-demo_default, saw the site, and correctly failed on a rig that was offline.
  • Pterodactyl file calls were run on the offline egg-oxide test server, not the demo rig, with no power calls and no plugin changes. Compress, then rename to demo-baseline.tar.gz (1.3 MB), then delete the three directories (32 entries → 0), then decompress. Decompress is synchronous: all 32 entries were back when it answered (220 ms). An 8 MiB binary files/write landed byte-for-byte, and chmod 755 was verified. Cleaned up afterwards, so egg-oxide is as it was.
  • Both workflows parse (PyYAML) and contain no empty ${{ }}.

Not yet run: the first real deploy, the baseline capture and the §10 walk. They wait on two things: the host's .env, and Module-Rust#64 merging so v0.6.0 is released.

Note: rust-oxide's identifier is now 6eb9d621; my notes had e6758c06. The workflows default to the new one, and the PTERODACTYL_SERVER variable overrides it.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • No AI tools were used to produce this contribution.
  • AI tools were used. Tool(s): Claude Code (Claude Opus 5.5). I have reviewed and understand
    every change, and take responsibility for it. AI-authored commits are
    marked with a Co-Authored-By / Assisted-By trailer.

License

  • I agree that my contribution is licensed under this project's license
    (GNU GPL v3.0 or later), and I have the right to contribute it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

## What & why Step 5 of the demo plan (website#222, `docs/website/DEMO_MODE.md` §7 and §10). These are the files that deploy and wipe demo.runicgateway.com on the `demo-site` runner. Decisions DM22–DM24 were taken today and are recorded in the docs PR. | File | What it does | | --- | --- | | `deploy/demo/docker-compose.yml` | Runs the app and its database only; no bot and no ntfy (§5). Sets `DEMO_MODE=true` and adds a healthcheck on `/status`. Mounts the fixture at `/app/deploy/demo/seed.json`. `MODULES` comes from `state/modules.env` as a second `env_file`, so the workflow never edits the `.env` that holds the secrets. The project name is fixed (`rg-demo`) so the smoke check can join the network. | | `deploy/demo/.env.example` | Lists every variable the host needs. Two would have stopped a first deploy: **`BOT_INTERNAL_KEY`**, because the app refuses to start in production without it even with no bot (found by running it), and **`MODULE_SOURCE_HOSTS`**, because a fresh database seeds its install allowlist from it, so without it the declared module is refused. | | `deploy/demo/seed.json` | The four logins, `site_title` and `status_message`, two news posts, a wiki page, and one published event, "Server greeting", whose step is `rust.command.run` running `say …`. Also the rig's server link and `demo-player`'s Steam link. Passwords, the Steam id and the sidecar URL/token are `"$env:NAME"`. | | `deploy/demo/rig.mjs` | Dependency-free and run in `node:22.23.4-alpine`. `plan` picks the latest bundle `current.json` (read through `/contents/`, not the CDN-cached `/raw/`) plus the newest Module-Rust release with an equal `protocol`, and keeps the previous set when none matches. `restore` kills the rig, writes a changed bundle (plugin `.cs` files, sidecar, launcher, `chmod` each one, plus the rig's `rust-link/bundle.json`), deletes the three directories, unpacks `demo-baseline.tar.gz`, then starts the rig. `baseline` does a power stop (which saves the world, DM22), compresses, renames and starts. `smoke` waits for the site, then waits a bounded time for the rig to report in. | | `.gitea/workflows/demo-wipe.yml` | Runs every 2 h on the hour (UTC), on a push to `deploy/demo/**`, and by hand. Order: plan → rig → `down -v` / `pull` / `up` / `seed:demo` → smoke. A failed rig restore still wipes the site, then fails the run. Shares concurrency group `demo-rig` with the baseline workflow. | | `.gitea/workflows/demo-baseline.yml` | `workflow_dispatch` only. | | `deploy/demo/README.md` | One-time host setup and what `state/` holds. | ## How it was tested - **Site half, locally**, against an image built from `main` with Module-Rust 0.6.0 assembled from `edge` the way its release CI does it. Ran `down -v` → `up` → healthy → `seed:demo` with this fixture. Results: the 4 accounts, content (2 posts, 1 wiki page, 1 event, which validated and published), `rust` seeded its server and link, and `demo_state` was stamped. `GET /public/settings` lists 3 logins (`play-review` is hidden) with `resetsAt` on the next even hour. `GET /public/rust/servers` shows the `demo` server. A multipart POST gets `403`. - **`rig.mjs plan`** against the real Gitea read bundle `2026.10.22` (protocol 14). It correctly refuses for now: no released module speaks protocol 14 until Module-Rust#64 releases v0.6.0. **`smoke`** joined `rg-demo_default`, saw the site, and correctly failed on a rig that was offline. - **Pterodactyl file calls** were run on the offline **`egg-oxide`** test server, not the demo rig, with no power calls and no plugin changes. Compress, then rename to `demo-baseline.tar.gz` (1.3 MB), then delete the three directories (32 entries → 0), then decompress. Decompress is **synchronous**: all 32 entries were back when it answered (220 ms). An 8 MiB binary `files/write` landed byte-for-byte, and `chmod 755` was verified. Cleaned up afterwards, so `egg-oxide` is as it was. - Both workflows parse (PyYAML) and contain no empty `${{ }}`. **Not yet run:** the first real deploy, the baseline capture and the §10 walk. They wait on two things: the host's `.env`, and Module-Rust#64 merging so v0.6.0 is released. **Note:** `rust-oxide`'s identifier is now **`6eb9d621`**; my notes had `e6758c06`. The workflows default to the new one, and the `PTERODACTYL_SERVER` variable overrides it. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [ ] No AI tools were used to produce this contribution. - [x] AI tools were used. Tool(s): `Claude Code (Claude Opus 5.5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` / `Assisted-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-26 12:24:20 -05:00
feat(demo): deploy/demo and the wipe + baseline workflows (step 5)
All checks were successful
PR Checks / bot-tests (pull_request) Successful in 50s
PR Checks / client-build (pull_request) Successful in 57s
PR Checks / server-tests (pull_request) Successful in 12m25s
d48d8c473c
The public demo's deployment (docs/website/DEMO_MODE.md §7, §10 step 5):

- deploy/demo/docker-compose.yml: the app and its database only, DEMO_MODE
  on, a /status healthcheck, the fixture mounted for seed:demo, MODULES read
  from state/modules.env so the workflow never edits the .env with secrets.
- deploy/demo/.env.example: every variable the host needs, including
  BOT_INTERNAL_KEY (the app refuses to start in production without it) and
  MODULE_SOURCE_HOSTS (a fresh database's install allowlist).
- deploy/demo/seed.json: the four logins, settings, two posts, a wiki page,
  a published "Server greeting" event on a Rust console step, the rig's
  server link and demo-player's Steam link; secrets as "$env:NAME".
- deploy/demo/rig.mjs: plan (latest Rust bundle + newest Module-Rust on the
  same protocol, keeping the previous set when none matches), restore (kill,
  write a changed bundle, delete + unpack demo-baseline.tar.gz, start),
  baseline (power stop saves the world, DM22; compress, rename, start) and
  smoke (the site, then the rig reporting in), on the Pterodactyl client API.
- .gitea/workflows/demo-wipe.yml: every 2 h on the hour UTC, on a push to
  deploy/demo/, and by hand; rig first, then down -v / pull / up / seed.
- .gitea/workflows/demo-baseline.yml: by hand only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech approved these changes 2026-10-27 12:31:41 -05:00
whitlocktech merged commit 247323ff05 into main 2026-10-27 12:31:48 -05:00
whitlocktech deleted branch feature/demo-deploy 2026-10-27 12:31:49 -05:00
Sign in to join this conversation.
No description provided.