From b136d1751b583f628c443216e70c82991cb10bb6 Mon Sep 17 00:00:00 2001 From: NeilClack Date: Fri, 28 Aug 2026 20:15:32 -0500 Subject: [PATCH] Merge -dev closures. Consolidates 8 closures to only 4 by using a git branch selector that sets the values for -dev variants of each closure. Reference the 'Verifying KioskConfig builds are in the right branch' note in Obsidian for more reference. --- .gitea/workflows/build-dev.yml | 6 +- AGENTS.md | 28 ++++---- flake.nix | 121 ++++++++++++--------------------- 3 files changed, 63 insertions(+), 92 deletions(-) diff --git a/.gitea/workflows/build-dev.yml b/.gitea/workflows/build-dev.yml index 665f111..6b8bc7d 100644 --- a/.gitea/workflows/build-dev.yml +++ b/.gitea/workflows/build-dev.yml @@ -22,7 +22,7 @@ jobs: run: | export NIXPKGS_ALLOW_UNFREE=1 . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh - nix build -L --impure .#nixosConfigurations.dev-iso.config.system.build.isoImage + nix build -L --impure .#nixosConfigurations.kiosk-iso.config.system.build.isoImage - name: Rename dev kiosk ISO run: | ISO_DATE=$(date +%Y-%m-%d_%H-%M-%S) @@ -49,7 +49,7 @@ jobs: run: | export NIXPKGS_ALLOW_UNFREE=1 . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh - nix build -L --impure .#nixosConfigurations.android-dev-iso.config.system.build.isoImage + nix build -L --impure .#nixosConfigurations.android-iso.config.system.build.isoImage - name: Rename dev android ISO run: | ISO_DATE=$(date +%Y-%m-%d_%H-%M-%S) @@ -76,7 +76,7 @@ jobs: run: | export NIXPKGS_ALLOW_UNFREE=1 . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh - nix build -L --impure .#nixosConfigurations.commercial-dev-iso.config.system.build.isoImage + nix build -L --impure .#nixosConfigurations.commercial-iso.config.system.build.isoImage - name: Rename dev commercial ISO run: | ISO_DATE=$(date +%Y-%m-%d_%H-%M-%S) diff --git a/AGENTS.md b/AGENTS.md index b4b741f..589965f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,10 +8,12 @@ Hosted on Gitea: `https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig` ## Prod/dev split -Single branch (`main`) for this repo. The production vs. dev split is at the **flake input + upgrade ref** level: +Two branches — `main` (prod) and `dev`. There are **no `-dev` configs**: each hardware target has exactly one config, and the branch selects its channel via the single `channel` block at the top of `flake.nix`: -- Production variants consume the `radiantwave` input (`?ref=main`) and auto-upgrade from `KioskConfig.git#` on `main`. -- `-dev` variants consume the `radiantwave-dev` input (`?ref=dev`) and auto-upgrade from `KioskConfig.git?ref=dev#-dev`. +- `main`: `channel.appInput = radiantwave` (main), `atticRepo = "radiantwave"`, `gitRef = ""` (default branch). +- `dev`: `channel.appInput = radiantwave-dev`, `atticRepo = "radiantwave-dev"`, `gitRef = "?ref=dev"`. + +Everything downstream (the configs, ISOs, updater module, autoUpgrade refs) reads from `channel`, so a `dev`→`main` merge carries your changes automatically — no copying between sibling configs. To flip a channel, change only those three `channel` values. When merging, reconcile just this one block. Both gitea repos are public: all git access is plain HTTPS (`git+https://...`), no SSH keys or access tokens on the kiosks. Don't reintroduce `git+ssh` URLs in flake/upgrade refs. @@ -36,16 +38,16 @@ nix eval .#nixosConfigurations --apply builtins.attrNames ## Config variants +One config per hardware target. Dev/prod is the branch, not the config — see `channel` in `flake.nix`: + | Name | Target hardware | Disk device | App input | |---|---|---|---| | `radiantwavekiosk` | Intel NUC 14 (NVMe) | `/dev/nvme0n1` | `radiantwave` (main) | -| `radiantwavekiosk-dev` | Intel NUC 14 (NVMe) | `/dev/nvme0n1` | `radiantwave-dev` (dev) | | `radiantwavekiosk-commercial` | Commercial HW (SATA) | `/dev/sda` | `radiantwave` (main) | -| `radiantwavekiosk-commercial-dev` | Commercial HW (SATA) | `/dev/sda` | `radiantwave-dev` (dev) | | `radiantwavekiosk-android` | Intel NUC 14, rotated display | `/dev/nvme0n1` | `radiantwave` (main) | -| `radiantwavekiosk-android-dev` | Intel NUC 14, rotated display | `/dev/nvme0n1` | `radiantwave-dev` (dev) | | `radiantwavekiosk-rpi` | Raspberry Pi 5 (SD image) | — | `radiantwave` (main) | -| `radiantwavekiosk-rpi-dev` | Raspberry Pi 5 (SD image) | — | `radiantwave-dev` (dev) | + +On the `dev` branch, the same four configs use `radiantwave-dev` and track `?ref=dev`. Every variant sets its own `networking.hostName` (same as the config name) and `system.configurationRevision`, so `tools/find_systems.zsh` can identify fleet members. @@ -56,11 +58,10 @@ Each ISO installs exactly one closure using that closure's disk layout (`mkIso` | ISO | Installs | Disk layout | |---|---|---| | `kiosk-iso` | `radiantwavekiosk` | NVMe (`disk-config.nix`) | -| `dev-iso` | `radiantwavekiosk-dev` | NVMe | | `commercial-iso` | `radiantwavekiosk-commercial` | SATA (`commercial-disk-config.nix`) | -| `commercial-dev-iso` | `radiantwavekiosk-commercial-dev` | SATA | | `android-iso` | `radiantwavekiosk-android` | NVMe | -| `android-dev-iso` | `radiantwavekiosk-android-dev` | NVMe | + +These same names build **dev-flavored** images when the flake is checked out on the `dev` branch (the channel block selects the dev app, attic repo, and upgrade ref), so there are no separate `-dev` ISOs. The installer lives once in `zero-touch-installer.nix` and is parameterized by `targetConfig`. Adding a new variant = add the `nixosConfigurations.*` entry, a matching `packages.x86_64-linux.*` line, and an ISO if needed. @@ -91,18 +92,19 @@ Regenerate when major hardware changes, not on a schedule — reports are snapsh - **Disk device differs by variant**: `disk-config.nix` uses `/dev/nvme0n1`, `commercial-disk-config.nix` uses `/dev/sda`. `mkIso` pairs each ISO with the correct layout automatically — keep new ISOs paired correctly. - **Changing a config name** requires updating that config's `upgradeFlakeRef` in `flake.nix` (it embeds the name). +- **The `channel` block is the dev/prod toggle** in `flake.nix` — `appInput`, `atticRepo`, `gitRef`. When merging `dev`→`main`, keep this block's values for the target branch; only this block differs between branches. - **`rotated_display.nix` uses `mkForce`** on `loginShellInit` — it replaces the entire kiosk startup script from `configuration.nix`; don't edit both. - **Auto-update**: `system.autoUpgrade` runs every 10 min (`*:00/10`) with `--refresh`, operation `switch`, no auto-reboot. A `radiantwave-backup` rsync runs before each attempt. The health-check/rollback service is written but commented out in `configuration.nix`. - **No binary cache**: kiosks fetch from cache.nixos.org only and compile RadiantWave itself from source during upgrades. Attic integration was planned (see `PLAN-update-system.md`) but never wired; the old netrc/JWT setup has been removed. - **Secrets**: none in-tree anymore (gitea repos are public). If either repo goes private, re-add auth via `nix.settings.access-tokens` in `configuration.nix`. -- **Flake inputs**: `radiantwave` tracks main, `radiantwave-dev` tracks `?ref=dev`; prod and `-dev` configs consume them respectively. Updating them independently is intentional. +- **Flake inputs**: `radiantwave` tracks main, `radiantwave-dev` tracks `?ref=dev`. The `channel` block picks which one the configs consume. Updating them independently is intentional. - **SSH keys on machines are inbound-only** (`authorizedKeys` for admin access); outbound git is HTTPS. The root key in `zero-touch-installer.nix` must stay in sync with `myKey` in `configuration.nix`. ## File map -- `flake.nix` — entry point: inputs, builders (`mkKioskSystem`, `mkRpiKioskSystem`, `mkIso`), all system definitions, facter report wiring +- `flake.nix` — entry point: inputs, the `channel` dev/prod selector, builders (`mkKioskSystem`, `mkRpiKioskSystem`, `mkIso`), all system definitions, facter report wiring - `configuration.nix` — shared NixOS config (packages, services, kiosk user, auto-upgrade, health check, VM variant) -- `zero-touch-installer.nix` — parameterized USB installer module used by all six ISOs +- `zero-touch-installer.nix` — parameterized USB installer module used by all three ISOs - `disk-config.nix` — NVMe disk layout (NUC) - `commercial-disk-config.nix` — SATA disk layout (commercial HW) - `rotated_display.nix` — portrait display override for Android variant diff --git a/flake.nix b/flake.nix index 5a191cd..ab07723 100644 --- a/flake.nix +++ b/flake.nix @@ -55,11 +55,29 @@ revision = self.rev or self.dirtyRev or null; + # DEV/PROD CHANNEL SELECTOR — the single block that differs between the + # `main` and `dev` branches. Everything downstream (the four configs, the + # ISOs, the updater module) is identical on both branches and pulls from + # this channel, so a `dev`-to-`main` merge carries your changes with it + # automatically. There are no more `-dev` sibling configs to copy. + # + # main: appInput = radiantwave (main), atticRepo = "radiantwave", + # gitRef = "" (default branch) + # dev: appInput = radiantwave-dev, atticRepo = "radiantwave-dev", + # gitRef = "?ref=dev" + # + # To flip branches, change ONLY these three values (then push/merge). + channel = { + appInput = radiantwave; # main + atticRepo = "radiantwave"; # main + gitRef = ""; # main + }; + # Live app-update side channel. The manifest is served by the - # fileserver; closures are fetched (never built) from the variant's - # Attic cache. `repo` selects the channel — "radiantwave" (prod, - # ?ref=main) or "radiantwave-dev" (dev, ?ref=dev). All variants follow - # this same update procedure; only the channel differs. + # fileserver; closures are fetched (never built) from the channel's + # Attic cache. `repo` selects the channel — "radiantwave" (main) or + # "radiantwave-dev" (dev). All variants follow this same update + # procedure; only the channel differs. radiantwaveUpdaterModule = { repo, baseline }: { services.radiantwaveUpdater = { enable = true; @@ -183,99 +201,58 @@ in { packages.x86_64-linux.radiantwavekiosk = self.nixosConfigurations.radiantwavekiosk.config.system.build.toplevel; - packages.x86_64-linux.radiantwavekiosk-dev = self.nixosConfigurations.radiantwavekiosk-dev.config.system.build.toplevel; packages.x86_64-linux.radiantwavekiosk-commercial = self.nixosConfigurations.radiantwavekiosk-commercial.config.system.build.toplevel; - packages.x86_64-linux.radiantwavekiosk-commercial-dev = self.nixosConfigurations.radiantwavekiosk-commercial-dev.config.system.build.toplevel; packages.x86_64-linux.radiantwavekiosk-android = self.nixosConfigurations.radiantwavekiosk-android.config.system.build.toplevel; - packages.x86_64-linux.radiantwavekiosk-android-dev = self.nixosConfigurations.radiantwavekiosk-android-dev.config.system.build.toplevel; packages.x86_64-linux.radiantwavekiosk-rpi = self.nixosConfigurations.radiantwavekiosk-rpi.config.system.build.toplevel; - packages.x86_64-linux.radiantwavekiosk-rpi-dev = self.nixosConfigurations.radiantwavekiosk-rpi-dev.config.system.build.toplevel; - # Kiosk systems. + # Kiosk systems — one per hardware target, no dev/prod siblings. # - # Production variants consume the `radiantwave` input (?ref=main); - # `-dev` variants consume `radiantwave-dev` (?ref=dev). All variants - # run the same live-app updater against their channel's Attic repo and - # manifest, and track this repo's matching branch via autoUpgrade. + # Every config consumes the channel selected at the top of this file + # (see `channel`): the RadiantWave app input, the Attic repo/manifest + # it fetches live updates from, and the KioskConfig git ref it + # auto-upgrades from. On `main` the channel is prod; on `dev` it is the + # dev channel. Because there is exactly one config per hardware type, + # changes made on `dev` reach prod via a plain merge — no copying. nixosConfigurations = { radiantwavekiosk = mkKioskSystem { system = "x86_64-linux"; - radiantwave = radiantwave; + radiantwave = channel.appInput; hostName = "radiantwavekiosk"; diskModule = ./disk-config.nix; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git#radiantwavekiosk"; + upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk"; extraModules = [ - (radiantwaveUpdaterModule { repo = "radiantwave"; baseline = radiantwave.packages.x86_64-linux.default; }) - ]; - }; - radiantwavekiosk-dev = mkKioskSystem { - system = "x86_64-linux"; - radiantwave = radiantwave-dev; - hostName = "radiantwavekiosk-dev"; - diskModule = ./disk-config.nix; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-dev"; - extraModules = [ - (radiantwaveUpdaterModule { repo = "radiantwave-dev"; baseline = radiantwave-dev.packages.x86_64-linux.default; }) + (radiantwaveUpdaterModule { repo = channel.atticRepo; baseline = channel.appInput.packages.x86_64-linux.default; }) ]; }; radiantwavekiosk-commercial = mkKioskSystem { system = "x86_64-linux"; - radiantwave = radiantwave; + radiantwave = channel.appInput; hostName = "radiantwavekiosk-commercial"; diskModule = ./commercial-disk-config.nix; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git#radiantwavekiosk-commercial"; + upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk-commercial"; extraModules = [ - (radiantwaveUpdaterModule { repo = "radiantwave"; baseline = radiantwave.packages.x86_64-linux.default; }) - ]; - }; - radiantwavekiosk-commercial-dev = mkKioskSystem { - system = "x86_64-linux"; - radiantwave = radiantwave-dev; - hostName = "radiantwavekiosk-commercial-dev"; - diskModule = ./commercial-disk-config.nix; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-commercial-dev"; - extraModules = [ - (radiantwaveUpdaterModule { repo = "radiantwave-dev"; baseline = radiantwave-dev.packages.x86_64-linux.default; }) + (radiantwaveUpdaterModule { repo = channel.atticRepo; baseline = channel.appInput.packages.x86_64-linux.default; }) ]; }; # Rotated screen for android-style displays. radiantwavekiosk-android = mkKioskSystem { system = "x86_64-linux"; - radiantwave = radiantwave; + radiantwave = channel.appInput; hostName = "radiantwavekiosk-android"; diskModule = ./disk-config.nix; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git#radiantwavekiosk-android"; + upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk-android"; extraModules = [ ./rotated_display.nix - (radiantwaveUpdaterModule { repo = "radiantwave"; baseline = radiantwave.packages.x86_64-linux.default; }) - ]; - }; - radiantwavekiosk-android-dev = mkKioskSystem { - system = "x86_64-linux"; - radiantwave = radiantwave-dev; - hostName = "radiantwavekiosk-android-dev"; - diskModule = ./disk-config.nix; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-android-dev"; - extraModules = [ - ./rotated_display.nix - (radiantwaveUpdaterModule { repo = "radiantwave-dev"; baseline = radiantwave-dev.packages.x86_64-linux.default; }) + (radiantwaveUpdaterModule { repo = channel.atticRepo; baseline = channel.appInput.packages.x86_64-linux.default; }) ]; }; # Raspberry Pi 5. radiantwavekiosk-rpi = mkRpiKioskSystem { - radiantwave = radiantwave; + radiantwave = channel.appInput; hostName = "radiantwavekiosk-rpi"; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git#radiantwavekiosk-rpi"; + upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk-rpi"; extraModules = [ - (radiantwaveUpdaterModule { repo = "radiantwave"; baseline = radiantwave.packages.aarch64-linux.default; }) - ]; - }; - radiantwavekiosk-rpi-dev = mkRpiKioskSystem { - radiantwave = radiantwave-dev; - hostName = "radiantwavekiosk-rpi-dev"; - upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-rpi-dev"; - extraModules = [ - (radiantwaveUpdaterModule { repo = "radiantwave-dev"; baseline = radiantwave-dev.packages.aarch64-linux.default; }) + (radiantwaveUpdaterModule { repo = channel.atticRepo; baseline = channel.appInput.packages.aarch64-linux.default; }) ]; }; @@ -283,6 +260,10 @@ # Each ISO installs exactly the named system closure using that # system's disk layout. NVMe layouts pair with ./disk-config.nix, # SATA with ./commercial-disk-config.nix — don't cross them. + # The same names build dev-flavored images when the flake is checked + # out on the `dev` branch (the channel block above selects the dev + # app, attic repo, and upgrade ref), so there are no separate -dev + # ISOs. kiosk-iso = mkIso { targetConfig = "radiantwavekiosk"; diskModule = ./disk-config.nix; @@ -295,18 +276,6 @@ targetConfig = "radiantwavekiosk-android"; diskModule = ./disk-config.nix; }; - dev-iso = mkIso { - targetConfig = "radiantwavekiosk-dev"; - diskModule = ./disk-config.nix; - }; - commercial-dev-iso = mkIso { - targetConfig = "radiantwavekiosk-commercial-dev"; - diskModule = ./commercial-disk-config.nix; - }; - android-dev-iso = mkIso { - targetConfig = "radiantwavekiosk-android-dev"; - diskModule = ./disk-config.nix; - }; }; }; }