{ pkgs, lib, config, ... }: with lib; let fromAddress = config.email.fromAddress; fromIdentity = config.email.fromIdentity; toAddress = config.email.adminEmail; cfg = config.system.autoUpgrade; in { options.system.autoUpgrade = { sendEmail = mkOption { type = types.bool; default = false; description = mdDoc '' Whether to send a status email after an upgrade. ''; }; }; config = mkIf cfg.enable { systemd.services.nixos-upgrade.script = mkOverride 50 ( let nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild"; date = "${pkgs.coreutils}/bin/date"; readlink = "${pkgs.coreutils}/bin/readlink"; grep = "${pkgs.gnugrep}/bin/grep"; shutdown = "${config.systemd.package}/bin/shutdown"; sendmail = "${pkgs.system-sendmail}/bin/sendmail"; upgradeFlag = optional (cfg.channel == null) "--upgrade"; in '' set -o pipefail upgrade() { ${nixos-rebuild} ${cfg.operation} ${toString (cfg.flags ++ upgradeFlag)} } indent() { while read -r line ; do echo " $line" done <<< "$1" } start_time="$(${date})" reboot_allowed="no" do_reboot="no" exit_code=0 ${optionalString cfg.allowReboot '' reboot_allowed="yes" ${optionalString (cfg.rebootWindow != null) '' current_time="$(${date} +%H:%M)" lower="${cfg.rebootWindow.lower}" upper="${cfg.rebootWindow.upper}" if [[ "''${lower}" < "''${upper}" ]]; then if [[ "''${current_time}" > "''${lower}" ]] && \ [[ "''${current_time}" < "''${upper}" ]]; then reboot_allowed="yes" else reboot_allowed="no" fi else # lower > upper, so we are crossing midnight (e.g. lower=23h, upper=6h) # we want to reboot if cur > 23h or cur < 6h if [[ "''${current_time}" < "''${upper}" ]] || \ [[ "''${current_time}" > "''${lower}" ]]; then reboot_allowed="yes" else reboot_allowed="no" fi fi ''} ''} output_file="$(mktemp)" upgrade 2>&1 | tee "$output_file" || exit_code=$? upgrade_output="$(cat "$output_file")" rm -f "$output_file" send_email=no email_subject="Upgrade succeeded" email_body="The system upgrade started at $start_time has succeeded." if [ "$exit_code" -ne 0 ] ; then send_email=yes email_subject="Upgrade failed (exit code $exit_code)" email_body="The system upgrade started at $start_time has failed with exit code $exit_code." fi possible_warnings="$(${grep} -e "^trace:" <<<"$upgrade_output" || true)" if [ "$possible_warnings" != "" ] ; then send_email=yes email_subject="$email_subject with warnings" email_body="$(cat <<-EOF $email_body These trace messages and warnings were encountered: --------------------------------------------------- $possible_warnings EOF )" fi booted_version="$(${readlink} /run/booted-system/{initrd,kernel,kernel-modules})" built_version="$(${readlink} /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})" if [ "$booted_version" != "$built_version" ] ; then version_comparison="$(cat <<-EOF The booted kernel version $(indent "$booted_version") does not match currently active $(indent "$built_version") . EOF )" echo "$version_comparison" send_email=yes email_subject="$email_subject, reboot required" email_body="$(cat <<-EOF $email_body A reboot is required, because: ------------------------------ $version_comparison EOF )" if [ "$reboot_allowed" = "yes" ] && [ $exit_code -eq 0 ] ; then email_body="$(printf "%s\n%s" "$email_body" "The system will reboot now.")" do_reboot="yes" fi fi ${optionalString cfg.sendEmail '' if [ "$send_email" = "yes" ] ; then ${sendmail} -t -X - <<-EOF To: ${toAddress} From: ${fromIdentity} Subject: $email_subject Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=UTF-8 X-Priority: 3 $email_body Full upgrade output: -------------------- $upgrade_output EOF fi ''} if [ "$do_reboot" = "yes" ] ; then echo "Rebooting system." ${shutdown} -r +1 fi exit $exit_code '' ); }; }