nix copy --from <attic> only pulls from that single store, so nixpkgs deps (e.g. sqlite) that Attic skips and leaves on cache.nixos.org were missing -> 'path is not valid'. Realize the closure through the machine's substituters so the app comes from the dev Attic and its upstream deps resolve from cache.nixos.org, without building from source.
91 lines
3.5 KiB
Nix
91 lines
3.5 KiB
Nix
{ 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
|
|
|
|
# Realize the closure via the machine's substituters. The app itself
|
|
# only exists in the dev Attic cache, but nixpkgs deps (e.g. sqlite)
|
|
# are signed by cache.nixos.org and Attic intentionally skips them to
|
|
# avoid re-uploading the world. Fetch those from cache.nixos.org so
|
|
# the whole closure resolves without ever building from source.
|
|
echo "radiantwave-updater: acquiring ''${NEW_PATH}"
|
|
${pkgs.nix}/bin/nix-store -r "$NEW_PATH" \
|
|
--option extra-substituters "https://attic.radiantwavetech.com/radiantwave-dev https://cache.nixos.org" \
|
|
--option extra-trusted-public-keys "radiantwave-dev:B8FhLu3MQ9JfeEIHIKNrKxnP+3akzzkqMbTZR4hRfj0= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" || {
|
|
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";
|
|
};
|
|
};
|
|
};
|
|
}
|