Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
809882b333 | ||
|
|
0124e1c67b | ||
|
|
476202f8e2 |
@@ -10,6 +10,149 @@ and manages WiFi/provisioning on a headless Linux kiosk.
|
|||||||
**Audio:** SDL3_mixer
|
**Audio:** SDL3_mixer
|
||||||
**Target:** Linux kiosk (Wayland); developed on macOS
|
**Target:** Linux kiosk (Wayland); developed on macOS
|
||||||
|
|
||||||
|
---
|
||||||
|
## Version Deployment
|
||||||
|
> How to release a new version of RadiantWave, and what happens on the kiosks when you do.
|
||||||
|
|
||||||
|
### Two channels, two update paths
|
||||||
|
|
||||||
|
| Channel | App input | Attic cache | Manifest |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `dev` | `radiantwave-dev` (`?ref=dev`) | `attic.../radiantwave-dev` | `files.../radiantwave-dev/manifest.json` |
|
||||||
|
| `main` | `radiantwave` (`?ref=refs/tags/1.8`) | `attic.../radiantwave` | `files.../radiantwave/manifest.json` |
|
||||||
|
|
||||||
|
A kiosk gets RadiantWave by two independent paths:
|
||||||
|
|
||||||
|
- **Live app update** -- CI publishes a manifest; the kiosk's
|
||||||
|
`radiantwave-updater.timer` fetches the closure from Attic and swaps
|
||||||
|
`/var/lib/radiantwave/current`. Takes effect in ~2 min, no rebuild.
|
||||||
|
- **NixOS rebuild** -- `system.autoUpgrade` runs every 10 min against the
|
||||||
|
`KioskConfig` repo with `--refresh` and rebuilds the whole system closure.
|
||||||
|
|
||||||
|
`KioskConfig` enables `services.radiantwaveUpdater` on all four configs on
|
||||||
|
**both** branches, so the live path is always available.
|
||||||
|
|
||||||
|
**An app-only feature only needs the live app update path. You do not commit,
|
||||||
|
push, or rebuild `../KioskConfig`.** Its `flake.lock` pin of `radiantwave-dev`
|
||||||
|
only determines the closure baked into the ISO; the updater overwrites that
|
||||||
|
pointer as soon as it sees a newer manifest.
|
||||||
|
|
||||||
|
### Test a feature on the dev kiosk -- manual steps
|
||||||
|
|
||||||
|
**1. Build and test locally.**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
nix develop --command bash -c '
|
||||||
|
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
cmake --build build
|
||||||
|
./build/test_assets
|
||||||
|
'
|
||||||
|
```
|
||||||
|
|
||||||
|
Run each `./build/test_<name>` binary you touched -- there is no ctest
|
||||||
|
integration and no aggregate target. If you added a test file, register it as
|
||||||
|
a new `add_executable` in `CMakeLists.txt` first.
|
||||||
|
|
||||||
|
**2. Land it on `dev`.**
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git switch dev && git pull
|
||||||
|
git switch -c feature/<name>
|
||||||
|
# ... commit your work ...
|
||||||
|
git push -u origin feature/<name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Open a PR against `dev` and merge. The kiosk only ever builds `dev`.
|
||||||
|
|
||||||
|
**3. Dispatch the build workflow** on the RadiantWave repo in Gitea Actions.
|
||||||
|
|
||||||
|
**4. Verify.**
|
||||||
|
|
||||||
|
The Home footer's Version row shows the new short commit hash, and:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh kiosk@<dev-kiosk> 'readlink /var/lib/radiantwave/current'
|
||||||
|
curl -fsSL https://files.radiantwavetech.com/radiantwave-dev/manifest.json | jq .storePath
|
||||||
|
```
|
||||||
|
|
||||||
|
Both must match.
|
||||||
|
|
||||||
|
### Test a feature on the dev kiosk -- automated by CI
|
||||||
|
|
||||||
|
> Everything between dispatching `build.yml` and the kiosk running the new
|
||||||
|
> binary. No human step is required for any of it.
|
||||||
|
>
|
||||||
|
> **Trigger.** `workflow_dispatch` force-clones the `dev` branch
|
||||||
|
> (`REF=dev`, hardcoded -- you cannot pick a branch). A tag push clones the tag.
|
||||||
|
> Either way `APP_VERSION` becomes the short commit hash on dispatch, or
|
||||||
|
> `git describe --tags --always` on a tag.
|
||||||
|
>
|
||||||
|
> **Build.** Native `x86_64-linux` package, then the offline suite via
|
||||||
|
> `checks.<system>.default`, then the live portal tests from `nix develop`
|
||||||
|
> (`RWT_LIVE_*` secrets), then the `aarch64-linux` package and checks. The
|
||||||
|
> aarch64 build is emulated via binfmt/qemu-user and is the long pole.
|
||||||
|
>
|
||||||
|
> **Publish.** `attic push radiantwave:radiantwave-dev` with both
|
||||||
|
> `result-pkg` and `result-pkg-aarch64`, using `ATTIC_TOKEN_DEV`. Then it
|
||||||
|
> writes `{"version","storePath"}` from `readlink -f` of each result into
|
||||||
|
> `manifest.json` (x86) and `aarch64-manifest.json` (arm), and `scp`s both to
|
||||||
|
> `157.245.124.103:/mnt/infrastructure_data/files/radiantwave-dev/`, which is
|
||||||
|
> served as `https://files.radiantwavetech.com/radiantwave-dev/`.
|
||||||
|
>
|
||||||
|
> **Kiosk side.** `radiantwave-updater.timer` (`OnBootSec=2min`,
|
||||||
|
> `OnUnitActiveSec=2min`, `Persistent`) runs the oneshot
|
||||||
|
> `radiantwave-updater.service`, which curls the manifest, `jq`s `.storePath`,
|
||||||
|
> and compares it against `readlink /var/lib/radiantwave/current`. On a new
|
||||||
|
> path it fetches the closure, never building:
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> nix-store -r "$NEW_PATH" \
|
||||||
|
> --add-root /var/lib/radiantwave/.current-root \
|
||||||
|
> --option extra-substituters "<atticUrl> https://cache.nixos.org"
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> Then it swaps atomically with `ln -sfn` plus `mv -T`. Both Attic caches and
|
||||||
|
> their public keys are already trusted in `configuration.nix`, so the kiosk
|
||||||
|
> needs no credentials.
|
||||||
|
>
|
||||||
|
> **Failure mode.** The Attic push and manifest publish are ordinary steps, not
|
||||||
|
> `always()`, so **any failing test blocks publication** and the kiosk keeps
|
||||||
|
> running the previous build. Check the run's log before assuming a deploy
|
||||||
|
> landed.
|
||||||
|
|
||||||
|
### Gotchas
|
||||||
|
|
||||||
|
- **Dispatch hardcodes `dev`.** `build.yml` sets `REF=dev` on
|
||||||
|
`workflow_dispatch`; you cannot select a branch.
|
||||||
|
- **Push before you dispatch.** CI clones `dev` at dispatch time, so dispatching
|
||||||
|
first publishes the previous commit and the feature appears not to deploy.
|
||||||
|
- **Reboot briefly reverts the app.** `radiantwave-updater.nix` seeds the
|
||||||
|
pointer with tmpfiles type `L`, which re-points `current` at the ISO's baked
|
||||||
|
baseline on every boot. The 2-minute timer restores the live build shortly
|
||||||
|
after, so the window is brief but real.
|
||||||
|
- **A reboot does not lose kiosk data.** `radiantwave-backup.service` rsyncs
|
||||||
|
`~/.local/radiantwave` (minus `audio/`) before every upgrade attempt.
|
||||||
|
- **Don't bump `channel.appVersion` for an app-only deploy.** It only labels
|
||||||
|
the baked ISO baseline. Bump it when rebuilding a dev ISO so it advertises
|
||||||
|
the new fingerprint.
|
||||||
|
- **No local Linux builder on macOS.** `extra-platforms = x86_64-darwin` only,
|
||||||
|
so `nix build .#packages.x86_64-linux.default` and
|
||||||
|
`nixos-rebuild switch --target-host` are not available from a Mac. The
|
||||||
|
`orion-lxc` runner is the only `x86_64-linux` builder.
|
||||||
|
|
||||||
|
### Releasing to production
|
||||||
|
|
||||||
|
1. Merge `dev` -> `main` in `radiantwave`.
|
||||||
|
2. Tag and push: `git tag 1.9 && git push origin 1.9`. The tag push triggers
|
||||||
|
`build.yml`, which publishes to the `radiantwave` Attic cache and
|
||||||
|
`radiantwave/manifest.json` using `ATTIC_TOKEN_PROD`.
|
||||||
|
3. In `KioskConfig`, on `main`: set `radiantwave.url` to
|
||||||
|
`?ref=refs/tags/1.9`, set `channel.appVersion = "1.9"`, and run
|
||||||
|
`nix flake update radiantwave`. Commit both -- the lock and the channel
|
||||||
|
block must agree.
|
||||||
|
4. Its CI fails fast unless the baked baseline exactly matches the published
|
||||||
|
manifest, so this step cannot drift silently.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Project Layout
|
## Project Layout
|
||||||
@@ -564,34 +707,6 @@ status to decide which page to show.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Rebuild Progress
|
|
||||||
|
|
||||||
| Component | Status |
|
|
||||||
| ----------- | -------- |
|
|
||||||
| Entry / Event Loop | Done |
|
|
||||||
| Window + Renderer | Done |
|
|
||||||
| Page System | Done |
|
|
||||||
| PageManager (stack) | Done |
|
|
||||||
| KeybindManager | Done |
|
|
||||||
| Home Page | Done |
|
|
||||||
| Settings Page | Done |
|
|
||||||
| Provisioning FSM | — |
|
|
||||||
| Pages (11 types) | — |
|
|
||||||
| OpenGL Context | — |
|
|
||||||
| Virtual Framebuffer | — |
|
|
||||||
| Shader Manager | — |
|
|
||||||
| Font Manager | — |
|
|
||||||
| Line Patterns | — |
|
|
||||||
| Color Patterns | — |
|
|
||||||
| Mixer | — |
|
|
||||||
| Network / WiFi | — |
|
|
||||||
| Database (SQLite) | — |
|
|
||||||
| HTTP Client | — |
|
|
||||||
| Logging | — |
|
|
||||||
| Config / App State | — |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Common Pitfalls
|
## Common Pitfalls
|
||||||
|
|
||||||
- Do not commit audio assets (`.wav` files) -- downloaded at runtime.
|
- Do not commit audio assets (`.wav` files) -- downloaded at runtime.
|
||||||
|
|||||||
Reference in New Issue
Block a user