Add live RadiantWave updater (dev variants)
Add a radiantwave-updater NixOS module that polls a manifest on the fileserver and swaps the app pointer /var/lib/radiantwave/current to new closures fetched from the dev Attic cache. Closures are fetched with 'nix copy --from', never built from source; the swap is atomic via mv -T. Enable it on the -dev variants only for now: the login shell launches from /var/lib/radiantwave/current/bin/radiantwave when enabled, and from the immutable system path otherwise, so prod behavior is unchanged. The pointer is seeded on first boot to the baseline copy already baked into the closure (no duplication).
This commit is contained in:
+10
-2
@@ -1,7 +1,15 @@
|
|||||||
{ pkgs, radiantwave, upgradeFlakeRef, radiantwavePlymouth, ... }:
|
{ config, pkgs, radiantwave, upgradeFlakeRef, radiantwavePlymouth, ... }:
|
||||||
|
|
||||||
let
|
let
|
||||||
radiantApp = radiantwave.packages.${pkgs.system}.default;
|
radiantApp = radiantwave.packages.${pkgs.system}.default;
|
||||||
|
# When the live-updater module is enabled (currently dev variants only),
|
||||||
|
# RadiantWave launches through the mutable /var/lib/radiantwave/current
|
||||||
|
# pointer so it can be swapped without a full nixos-rebuild. Otherwise it
|
||||||
|
# launches straight from the immutable system closure as before.
|
||||||
|
launchBin =
|
||||||
|
if config.services.radiantwaveUpdater.enable or false
|
||||||
|
then "/var/lib/radiantwave/current/bin/radiantwave"
|
||||||
|
else "/run/current-system/sw/bin/radiantwave";
|
||||||
myKey = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL4qWNZnYU5JaqR9c2qh6oFZktzVNBOx42PfDaGofCL6 cnclack@pm.me macbook" ];
|
myKey = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL4qWNZnYU5JaqR9c2qh6oFZktzVNBOx42PfDaGofCL6 cnclack@pm.me macbook" ];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@@ -159,7 +167,7 @@ in
|
|||||||
# Release the splash screen right before the compositor takes over the display
|
# Release the splash screen right before the compositor takes over the display
|
||||||
sudo -n plymouth quit --wait || true
|
sudo -n plymouth quit --wait || true
|
||||||
|
|
||||||
exec dbus-run-session cage -- /run/current-system/sw/bin/radiantwave -d -s > /tmp/kiosk.log 2>&1
|
exec dbus-run-session cage -- ${launchBin} -d -s > /tmp/kiosk.log 2>&1
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,18 @@
|
|||||||
|
|
||||||
revision = self.rev or self.dirtyRev or null;
|
revision = self.rev or self.dirtyRev or null;
|
||||||
|
|
||||||
|
# Live app-update side channel, dev-only for now. The manifest is
|
||||||
|
# served by the fileserver; closures are fetched (never built) from the
|
||||||
|
# dev Attic cache. Promotes to prod once validated.
|
||||||
|
radiantwaveDevUpdaterModule = { baseline }: {
|
||||||
|
services.radiantwaveUpdater = {
|
||||||
|
enable = true;
|
||||||
|
manifestUrl = "https://files.radiantwavetech.com/radiantwave-dev/manifest.json";
|
||||||
|
atticUrl = "https://attic.radiantwavetech.com/radiantwave-dev";
|
||||||
|
baselinePackage = baseline;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
mkKioskSystem = {
|
mkKioskSystem = {
|
||||||
system,
|
system,
|
||||||
radiantwave,
|
radiantwave,
|
||||||
@@ -79,6 +91,7 @@
|
|||||||
system.configurationRevision = revision;
|
system.configurationRevision = revision;
|
||||||
}
|
}
|
||||||
./configuration.nix
|
./configuration.nix
|
||||||
|
./radiantwave-updater.nix
|
||||||
disko.nixosModules.disko
|
disko.nixosModules.disko
|
||||||
diskModule
|
diskModule
|
||||||
nixos-facter-modules.nixosModules.facter
|
nixos-facter-modules.nixosModules.facter
|
||||||
@@ -116,6 +129,7 @@
|
|||||||
];
|
];
|
||||||
})
|
})
|
||||||
./configuration.nix
|
./configuration.nix
|
||||||
|
./radiantwave-updater.nix
|
||||||
radiantwave.nixosModules.default
|
radiantwave.nixosModules.default
|
||||||
{
|
{
|
||||||
networking.hostName = hostName;
|
networking.hostName = hostName;
|
||||||
@@ -194,6 +208,9 @@
|
|||||||
hostName = "radiantwavekiosk-dev";
|
hostName = "radiantwavekiosk-dev";
|
||||||
diskModule = ./disk-config.nix;
|
diskModule = ./disk-config.nix;
|
||||||
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-dev";
|
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-dev";
|
||||||
|
extraModules = [
|
||||||
|
(radiantwaveDevUpdaterModule { baseline = radiantwave-dev.packages.x86_64-linux.default; })
|
||||||
|
];
|
||||||
};
|
};
|
||||||
radiantwavekiosk-commercial = mkKioskSystem {
|
radiantwavekiosk-commercial = mkKioskSystem {
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
@@ -208,6 +225,9 @@
|
|||||||
hostName = "radiantwavekiosk-commercial-dev";
|
hostName = "radiantwavekiosk-commercial-dev";
|
||||||
diskModule = ./commercial-disk-config.nix;
|
diskModule = ./commercial-disk-config.nix;
|
||||||
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-commercial-dev";
|
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-commercial-dev";
|
||||||
|
extraModules = [
|
||||||
|
(radiantwaveDevUpdaterModule { baseline = radiantwave-dev.packages.x86_64-linux.default; })
|
||||||
|
];
|
||||||
};
|
};
|
||||||
# Rotated screen for android-style displays.
|
# Rotated screen for android-style displays.
|
||||||
radiantwavekiosk-android = mkKioskSystem {
|
radiantwavekiosk-android = mkKioskSystem {
|
||||||
@@ -228,6 +248,7 @@
|
|||||||
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-android-dev";
|
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-android-dev";
|
||||||
extraModules = [
|
extraModules = [
|
||||||
./rotated_display.nix
|
./rotated_display.nix
|
||||||
|
(radiantwaveDevUpdaterModule { baseline = radiantwave-dev.packages.x86_64-linux.default; })
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
# Raspberry Pi 5.
|
# Raspberry Pi 5.
|
||||||
@@ -240,6 +261,9 @@
|
|||||||
radiantwave = radiantwave-dev;
|
radiantwave = radiantwave-dev;
|
||||||
hostName = "radiantwavekiosk-rpi-dev";
|
hostName = "radiantwavekiosk-rpi-dev";
|
||||||
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-rpi-dev";
|
upgradeFlakeRef = "git+https://gitea.radiantwavetech.com/RadiantWaveTech/KioskConfig.git?ref=dev#radiantwavekiosk-rpi-dev";
|
||||||
|
extraModules = [
|
||||||
|
(radiantwaveDevUpdaterModule { baseline = radiantwave-dev.packages.aarch64-linux.default; })
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# === USB Images ===
|
# === USB Images ===
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.radiantwaveUpdater;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.radiantwaveUpdater = {
|
||||||
|
enable = lib.mkEnableOption "live RadiantWave app updates via a polled manifest";
|
||||||
|
|
||||||
|
manifestUrl = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "HTTP(S) URL of the JSON manifest announcing the current app store path.";
|
||||||
|
};
|
||||||
|
|
||||||
|
atticUrl = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
description = "Attic cache URL to fetch the app closure from (fetch-only, never builds).";
|
||||||
|
};
|
||||||
|
|
||||||
|
baselinePackage = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
description = "Copy of RadiantWave baked into the system closure, used to seed the runtime pointer before the first update.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
# Runtime pointer the login shell launches RadiantWave from. Seeded on
|
||||||
|
# first boot to the baseline copy already baked into the system closure
|
||||||
|
# (so the ISO carries it with no duplication); the updater service later
|
||||||
|
# re-points it atomically to newer closures fetched from Attic.
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"L /var/lib/radiantwave/current - - - - ${cfg.baselinePackage.outPath}"
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services.radiantwave-updater = {
|
||||||
|
description = "Poll the RadiantWave manifest and swap the live app pointer";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
set -u
|
||||||
|
|
||||||
|
NEW_PATH=$(${pkgs.curl}/bin/curl -fsSL "${cfg.manifestUrl}" \
|
||||||
|
| ${pkgs.jq}/bin/jq -er .storePath) || {
|
||||||
|
echo "radiantwave-updater: failed to fetch/parse manifest from ${cfg.manifestUrl}"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
CURRENT=$(${pkgs.coreutils}/bin/readlink /var/lib/radiantwave/current 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [ -z "$NEW_PATH" ] || [ "$NEW_PATH" = "$CURRENT" ]; then
|
||||||
|
echo "radiantwave-updater: already current (''${CURRENT})"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fetch the closure from the signed Attic cache only. This never
|
||||||
|
# builds from source; it fails outright if the path isn't there.
|
||||||
|
echo "radiantwave-updater: acquiring ''${NEW_PATH}"
|
||||||
|
${pkgs.nix}/bin/nix copy --from "${cfg.atticUrl}" "$NEW_PATH" || {
|
||||||
|
echo "radiantwave-updater: nix copy failed for ''${NEW_PATH}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Atomic swap: the app never observes a half-updated pointer.
|
||||||
|
${pkgs.coreutils}/bin/ln -sfn "$NEW_PATH" /var/lib/radiantwave/current.tmp
|
||||||
|
${pkgs.coreutils}/bin/mv -T /var/lib/radiantwave/current.tmp /var/lib/radiantwave/current
|
||||||
|
echo "radiantwave-updater: swapped ''${CURRENT} -> ''${NEW_PATH}"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.radiantwave-updater = {
|
||||||
|
description = "Check for RadiantWave app updates";
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnBootSec = "2min";
|
||||||
|
OnUnitActiveSec = "2min";
|
||||||
|
Persistent = true;
|
||||||
|
Unit = "radiantwave-updater.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user