summaryrefslogtreecommitdiff
path: root/modules/nixos/personal/system.nix
diff options
context:
space:
mode:
authorquentin@aristote.fr <quentin@aristote.fr>2025-03-22 18:19:29 +0100
committerquentin@aristote.fr <quentin@aristote.fr>2025-03-22 21:09:17 +0100
commitf2e0f4c86f3318cc6df018b1948dd5bade77f082 (patch)
tree91eaef3ca670be49d86b731a816bb9ed5bd4e1fb /modules/nixos/personal/system.nix
parent65eb03f15116223871d06254dc453abc79bcffaa (diff)
nixos: split nix and system; use own autoUpgrade script
Diffstat (limited to 'modules/nixos/personal/system.nix')
-rw-r--r--modules/nixos/personal/system.nix220
1 files changed, 220 insertions, 0 deletions
diff --git a/modules/nixos/personal/system.nix b/modules/nixos/personal/system.nix
new file mode 100644
index 0000000..3bd3716
--- /dev/null
+++ b/modules/nixos/personal/system.nix
@@ -0,0 +1,220 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}: let
+ cfg = config.personal.system;
+ cfgRemote = cfg.autoUpgrade.remoteBuilding;
+ cfgNix = config.nix;
+ cfgLuks = config.boot.initrd.luks.devices;
+
+ name = config.networking.hostName;
+in {
+ options.personal.system = {
+ flake = lib.mkOption {
+ type = with lib.types; nullOr str;
+ default = null;
+ };
+ autoUpgrade = {
+ enable = lib.mkEnableOption "automatic system and nixpkgs upgrade";
+ autoUpdateInputs = lib.mkOption {
+ type = with lib.types; listOf str;
+ default = ["nixpkgs" "my-nixpkgs/nur" "nixos-hardware"];
+ };
+ checkHosts = lib.mkOption {
+ type = with lib.types; listOf str;
+ default = with builtins; concatMap (match "https://([^/]*)/?") cfgNix.settings.substituters;
+ };
+ remoteBuilding = {
+ enable = lib.mkEnableOption "remote building of the system configuration";
+ builder = {
+ hostName = lib.mkOption {
+ type = lib.types.str;
+ default = "hephaistos";
+ };
+ domain = lib.mkOption {type = lib.types.str;};
+ user = lib.mkOption {
+ type = lib.types.str;
+ default = name;
+ };
+ protocol = lib.mkOption {
+ type = lib.types.str;
+ # Nix custom ssh-variant that avoids lots of "trusted-users" settings pain
+ default = "ssh-ng";
+ };
+ speedFactor = lib.mkOption {
+ type =
+ lib.types.int;
+ default = 8;
+ };
+ };
+ };
+ };
+ };
+
+ config = let
+ hasFlake = cfg.flake != null;
+ hasFlakeInputs = cfg.autoUpgrade.autoUpdateInputs != [];
+
+ reboot = config.system.autoUpgrade.allowReboot;
+ nixosRebuild = "nixos-rebuild ${toString config.system.autoUpgrade.flags}";
+
+ remoteBuilder = with cfgRemote.builder; "${hostName}.${domain}";
+
+ checkNetwork = {
+ path = [pkgs.unixtools.ping];
+ # Check network connectivity
+ preStart = "(${lib.concatMapStringsSep " && " (host: "ping -c 1 ${host}") cfg.autoUpgrade.checkHosts}) || kill -s SIGUSR1 $$";
+ unitConfig = {
+ StartLimitIntervalSec = 300;
+ StartLimitBurst = 5;
+ };
+ serviceConfig = {
+ Restart = "on-abort";
+ RestartSec = 30;
+ };
+ };
+ in
+ lib.mkMerge [
+ (lib.mkIf hasFlake {
+ system.autoUpgrade.flake = cfg.flake;
+ systemd.services.flake-update = lib.mkIf hasFlakeInputs (lib.mkMerge [
+ checkNetwork
+ {
+ description = "Update flake inputs";
+ serviceConfig.Type = "oneshot";
+ script = "nix flake update --commit-lock-file --flake ${cfg.flake} " + lib.concatStringsSep " " cfg.autoUpgrade.autoUpdateInputs;
+ before = ["nixos-upgrade.service"];
+ requiredBy = ["nixos-upgrade.service"];
+ path = [pkgs.git cfgNix.package];
+ personal.monitor = true;
+ }
+ ]);
+
+ programs.git = lib.mkIf (lib.hasPrefix "git+file" cfg.flake) {
+ enable = true;
+ config.user = lib.mkDefault {
+ name = "Root user of ${name}";
+ email = "root@${name}";
+ };
+ };
+ })
+
+ (
+ lib.mkIf (cfg.autoUpgrade.enable && cfgRemote.enable) {
+ assertions = [
+ {
+ assertion = hasFlake && lib.hasPrefix "git+file://" cfg.flake;
+ message = "Auto remote upgrade is only supported when the system is specified by a flake with path of the shape 'git+file://...'";
+ }
+ ];
+
+ personal.system.autoUpgrade.checkHosts = lib.mkOptionDefault [remoteBuilder];
+
+ programs.ssh = {
+ extraConfig = ''
+ Host ${remoteBuilder}
+ IdentitiesOnly yes
+ IdentityFile /etc/ssh/remoteBuilder
+ User ${cfgRemote.builder.user}
+ '';
+ knownHosts."${remoteBuilder}".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHvtqi8tziBuviUV8LDK2ddQQUbHdJYB02dgWTK5Olxq";
+ };
+ }
+ )
+
+ (lib.mkIf cfg.autoUpgrade.enable {
+ personal.boot.unattendedReboot = lib.mkIf reboot true;
+ system.autoUpgrade = {
+ enable = true;
+ flags = lib.optional (!hasFlake) "--upgrade-all";
+ };
+ systemd.services.nixos-upgrade = lib.mkMerge [
+ checkNetwork
+ {
+ path =
+ lib.optional reboot pkgs.coreutils
+ ++ [
+ (
+ if cfgRemote.enable
+ then cfgNix.package
+ else pkgs.nixos-rebuild
+ )
+ ]
+ ++ lib.optional (reboot && cfgLuks ? crypt) pkgs.cryptsetup;
+ personal.monitor = true;
+ script = lib.mkForce (lib.concatStrings [
+ ''
+ ## build configuration
+ ''
+ (
+ let
+ in
+ if cfgRemote.enable
+ then ''
+ # update remote flake
+ pushd ${lib.removePrefix "git+file://" cfg.flake}
+ git push --force ${cfgRemote.builder.hostName} master
+ popd
+ # build remotely
+ config=$(ssh ${remoteBuilder} -- \
+ 'nix build --print-out-paths \
+ git+file://$(pwd)/nixos-configuration#nixosConfigurations.${name}.config.system.build.toplevel')
+ # copy result locally
+ nix-copy-closure --from ${remoteBuilder} "$config"
+ # create new generation
+ nix-env --profile /nix/var/nix/profiles/system \
+ --set "$config"
+
+ switch="$config/bin/switch-to-configuration"
+ ''
+ else ''
+ switch="${nixosRebuild}"
+ ''
+ )
+ ''
+ ## check whether a reboot is necessary"
+ ''
+ (
+ if reboot
+ then ''
+ $switch boot
+ booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})"
+ built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
+ reboot="$([ "$booted" = "$built" ] || echo true)"
+ ''
+ else ''
+ reboot=""
+ ''
+ )
+ ''
+ ## switch to new configuration
+ ''
+ (let
+ ifcrypt = lib.optionalString (cfgLuks ? crypt);
+ crypt = cfgLuks.crypt.device;
+ luksKey = x: "/etc/luks/keys/" + x;
+ in ''
+ if [ "$reboot" ]
+ then
+ ${ifcrypt ''
+ cryptsetup luksAddKey ${crypt} ${luksKey "tmp"} \
+ --key-file ${luksKey "master"} \
+ --verbose
+ ''}
+ shutdown -r now ${ifcrypt ''
+ || cryptsetup luksRemoveKey ${crypt} \
+ --key-file ${luksKey "tmp"} \
+ --verbose
+ ''}
+ else
+ $switch switch
+ fi
+ '')
+ ]);
+ }
+ ];
+ })
+ ];
+}