Files
KioskConfig/flake.nix
T
NeilClack c684387f60
build-prod-android.yml / build-android (push) Failing after 2s
build-prod-commercial.yml / build-commercial (push) Failing after 1s
build-prod-kiosk.yml / build-kiosk (push) Failing after 2s
Merge pull request 'Dev' (#9) from dev into main
Reviewed-on: #9
2026-10-01 22:28:47 +00:00

293 lines
12 KiB
Nix

{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
nixos-facter-modules.url = "github:numtide/nixos-facter-modules";
radiantwave.url = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/radiantwave.git?ref=main";
radiantwave-dev.url = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/radiantwave.git?ref=dev";
nixos-raspberrypi.url = "github:nvmd/nixos-raspberrypi/main";
};
outputs =
{
self,
nixpkgs,
disko,
nixos-facter-modules,
radiantwave,
radiantwave-dev,
nixos-raspberrypi,
...
}@inputs:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
# Plymouth theme: renders the RadiantWave SVG to a full-HD splash.png
# at build time (librsvg), so the theme is hermetic and resolution-
# independent. The .plymouth file's /usr paths are rewritten to $out
# (the NixOS module's initrd fixup rewrites store paths again).
mkRadiantwavePlymouth = targetPkgs:
targetPkgs.stdenv.mkDerivation {
pname = "radiantwave-plymouth";
version = "1.0.0";
nativeBuildInputs = [ targetPkgs.librsvg ];
src = ./assets/RWT-Circular-Color-ForOnDark.svg;
dontUnpack = true;
buildPhase = ''
mkdir -p $out/share/plymouth/themes/radiantwave
rsvg-convert \
--keep-aspect-ratio \
--background-color=#111318 \
--width=1280 \
--height=720 \
--output=$out/share/plymouth/themes/radiantwave/splash.png \
$src
sed 's,/usr/,'"$out"'/,g' ${./plymouth-theme/radiantwave.plymouth} \
> $out/share/plymouth/themes/radiantwave/radiantwave.plymouth
cp ${./plymouth-theme/radiantwave.script} \
$out/share/plymouth/themes/radiantwave/radiantwave.script
'';
};
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
};
# Exposed so CI (see .gitea/workflows/build-prod-*.yml) and humans can
# verify which channel this checkout resolves to. On `main` this must be
# "radiantwave"; on `dev` it must be "radiantwave-dev". The prod
# workflows fail fast if this isn't the production channel, so a merge
# that brings a dev-pointing channel block into `main` breaks the build
# instead of silently shipping dev builds as prod ISOs.
channelName = if channel.appInput == radiantwave then "radiantwave" else "radiantwave-dev";
# Live app-update side channel. The manifest is served by the
# 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;
baselinePackage = baseline;
manifestUrl = "https://files.radiantwavetech.com/${repo}/manifest.json";
atticUrl = "https://attic.radiantwavetech.com/${repo}";
};
};
mkKioskSystem = {
system,
radiantwave,
upgradeFlakeRef,
hostName,
diskModule,
extraModules ? [],
}:
nixpkgs.lib.nixosSystem {
inherit system;
specialArgs = {
inherit radiantwave;
inherit upgradeFlakeRef;
radiantwavePlymouth = mkRadiantwavePlymouth;
};
modules = [
{ nixpkgs.config.allowUnfree = true; }
{
networking.hostName = hostName;
system.configurationRevision = revision;
}
./configuration.nix
./radiantwave-updater.nix
disko.nixosModules.disko
diskModule
nixos-facter-modules.nixosModules.facter
# Hardware facter report. This is a NUC 14 report currently
# applied to every x86 variant (including commercial SATA
# hardware). To use per-model reports: generate them on each
# hardware target (procedure in AGENTS.md), commit them as
# facter-<model>.json, and select per variant here, e.g.:
# { facter.reportPath = ./facter-commercial.json; }
{ facter.reportPath = ./facter.json; }
radiantwave.nixosModules.default
] ++ extraModules;
};
mkRpiKioskSystem = {
radiantwave,
upgradeFlakeRef,
hostName,
extraModules ? [],
}:
nixos-raspberrypi.lib.nixosSystem {
specialArgs = {
inherit radiantwave upgradeFlakeRef;
inherit nixos-raspberrypi;
radiantwavePlymouth = mkRadiantwavePlymouth;
};
modules = [
({ ... }: {
imports = with nixos-raspberrypi.nixosModules; [
sd-image
raspberry-pi-5.base
raspberry-pi-5.page-size-16k
raspberry-pi-5.display-vc4
];
})
./configuration.nix
./radiantwave-updater.nix
radiantwave.nixosModules.default
{
networking.hostName = hostName;
system.configurationRevision = revision;
boot.loader.systemd-boot.enable = nixpkgs.lib.mkForce false;
boot.loader.efi.canTouchEfiVariables = nixpkgs.lib.mkForce false;
boot.loader.raspberry-pi.bootloader = "kernel";
nix.settings.extra-substituters = [
"https://nixos-raspberrypi.cachix.org"
];
nix.settings.extra-trusted-public-keys = [
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
];
}
] ++ extraModules;
};
# Zero-touch USB installer image. `targetConfig` names the kiosk system
# to install: the closure comes from the packages output below and the
# partitioning script from the paired disk module, so the ISO always
# installs the matching system for its hardware.
mkIso = { targetConfig, diskModule }:
nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
specialArgs = {
inherit inputs;
inherit self;
};
modules = [
"${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix"
disko.nixosModules.disko
(import ./zero-touch-installer.nix { inherit targetConfig; })
diskModule
({ pkgs, ... }: {
environment.systemPackages = [
disko.packages.${system}.default
];
isoImage.storeContents = [
self.packages.x86_64-linux.${targetConfig}
];
environment.etc."disko-config.nix".source = diskModule;
})
];
};
in
{
inherit channelName;
packages.x86_64-linux.radiantwavekiosk = self.nixosConfigurations.radiantwavekiosk.config.system.build.toplevel;
packages.x86_64-linux.radiantwavekiosk-commercial = self.nixosConfigurations.radiantwavekiosk-commercial.config.system.build.toplevel;
packages.x86_64-linux.radiantwavekiosk-android = self.nixosConfigurations.radiantwavekiosk-android.config.system.build.toplevel;
packages.x86_64-linux.radiantwavekiosk-rpi = self.nixosConfigurations.radiantwavekiosk-rpi.config.system.build.toplevel;
# Kiosk systems — one per hardware target, no dev/prod siblings.
#
# 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 = channel.appInput;
hostName = "radiantwavekiosk";
diskModule = ./disk-config.nix;
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk";
extraModules = [
(radiantwaveUpdaterModule { repo = channel.atticRepo; baseline = channel.appInput.packages.x86_64-linux.default; })
];
};
radiantwavekiosk-commercial = mkKioskSystem {
system = "x86_64-linux";
radiantwave = channel.appInput;
hostName = "radiantwavekiosk-commercial";
diskModule = ./commercial-disk-config.nix;
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk-commercial";
extraModules = [
(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 = channel.appInput;
hostName = "radiantwavekiosk-android";
diskModule = ./disk-config.nix;
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk-android";
extraModules = [
./rotated_display.nix
(radiantwaveUpdaterModule { repo = channel.atticRepo; baseline = channel.appInput.packages.x86_64-linux.default; })
];
};
# Raspberry Pi 5.
radiantwavekiosk-rpi = mkRpiKioskSystem {
radiantwave = channel.appInput;
hostName = "radiantwavekiosk-rpi";
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git${channel.gitRef}#radiantwavekiosk-rpi";
extraModules = [
(radiantwaveUpdaterModule { repo = channel.atticRepo; baseline = channel.appInput.packages.aarch64-linux.default; })
];
};
# === USB Images ===
# 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;
};
commercial-iso = mkIso {
targetConfig = "radiantwavekiosk-commercial";
diskModule = ./commercial-disk-config.nix;
};
android-iso = mkIso {
targetConfig = "radiantwavekiosk-android";
diskModule = ./disk-config.nix;
};
};
};
}