{ config, lib, pkgs, ... }: with lib; let cfg = config.malobeo.autoUpdate; in { options.malobeo.autoUpdate = { enable = mkOption { description = '' Enables a timer that periodically checks a hydra instance for the last build of the local system, and switches to it if it is different. Also enables periodical /nix/store GC. ''; type = types.bool; default = false; }; # https://hydra.dynamicdiscord.de/job/malobeo/infrastructure/louise/latest url = mkOption { description = lib.mdDoc '' Url to your hydra instace. like https://hydra.example.de ''; type = types.str; default = ""; }; project = mkOption { description = lib.mdDoc '' Name of the project at the hydra instace ''; type = types.str; default = ""; }; jobset = mkOption { description = lib.mdDoc '' Name of the jobset at the hydra instace ''; type = types.str; default = ""; }; cacheurl = mkOption { description = lib.mdDoc '' Url of the binary cache ''; type = types.str; default = cfg.url; }; }; config = { # the presence of this .service file signifies that the system is # autoupdate-enabled. it is checked to prevent autoupdating back # to a system without autoupdate when deploying with autoupdate # for the first time. systemd.services.autoupdate = lib.mkIf config.malobeo.autoUpdate.enable { wantedBy = [ "multi-user.target" ]; path = with pkgs; [ nix nettools curl jq ]; serviceConfig = { Type = "oneshot"; # switch-to-configuration may not return. HACK: cap running # time so that the timer can be scheduled again. TimeoutStartSec = "30min"; }; script = '' OLD=$(readlink /run/current-system) echo Current system: $(basename $OLD) NEW=$(curl -sLH "Accept: application/json" ${cfg.url}/job/${cfg.project}/${cfg.jobset}/${config.networking.hostName}/latest | jq -er .buildoutputs.out.path) if [ -z "$NEW" ] || [ "$NEW" = "null" ]; then echo "Unable to obtain updated system" exit 1 fi echo New system: $(basename $NEW) if [ "$OLD" != "$NEW" ]; then echo "Fetching new system built by ${cfg.url}/job/${cfg.project}/${cfg.jobset}" # this should fetch the new system from the binary cache nix copy --from ${cfg.cacheurl} "$NEW" if [ -e "$NEW/etc/systemd/system/autoupdate.timer" ]; then echo "Switch to the new system..." nix-env -p /nix/var/nix/profiles/system --set $NEW "$NEW/bin/switch-to-configuration" switch else echo "New system is not yet autoupdate-enabled. Refusing to switch into a dead end." fi else echo "No update required" fi ''; # don't let the switch kill this service, aborting the switch restartIfChanged = false; unitConfig.X-StopOnRemoval = false; # create timer startAt = "hourly"; }; nix = { # Show a diff when activating a new system except for microvms which handle this seperately #diffSystem = config.malobeo.deployment.server or "" == ""; #TODO: THIS WIPES HOSTS NIX STORE FROM WITHIN NIXOS-CONTAINER #gc = lib.mkIf config.malobeo.autoUpdate.enable { # automatic = true; # randomizedDelaySec = "6h"; # options = "--delete-older-than 21d"; #}; }; environment.systemPackages = [ ( # Provide a manual updating script that fetches the latest # updated+built system from Hydra pkgs.writeScriptBin "update-from-hydra" '' #! ${pkgs.runtimeShell} -e OLD=$(readlink /run/current-system) echo Current system: $(basename $OLD) NEW=$(curl -sLH "Accept: application/json" ${cfg.url}/job/${cfg.project}/${cfg.jobset}/${config.networking.hostName}/latest | ${pkgs.jq}/bin/jq -er .buildoutputs.out.path) if [ -z "$NEW" ] || [ "$NEW" = "null" ]; then echo "Unable to obtain updated system" exit 1 fi echo New system: $(basename $NEW) if [ "$OLD" != "$NEW" ]; then echo "Fetching new system built by ${cfg.url}/job/${cfg.project}/${cfg.jobset}" # this should fetch the new system from the binary cache nix copy --from ${cfg.cacheurl} "$NEW" echo "Switch to the new system..." nix-env -p /nix/var/nix/profiles/system --set $NEW "$NEW/bin/switch-to-configuration" switch else echo "No update required" fi '' ) ]; }; }