{ config, lib, pkgs, namespace, ... }: with lib; let name = "attic"; cfg = config.${namespace}.services.${name}; ntfyFailScript = pkgs.writeShellScript "nix-rebuild-cache-notify-failure" '' HOST="$(${pkgs.hostname}/bin/hostname)" ${pkgs.curl}/bin/curl -sf \ --user "$NTFY_USER:$NTFY_PASSWORD" \ -H "Title: Nix cache rebuild FAILED on $HOST" \ -H "Priority: high" \ -H "Tags: rotating_light,nix_snowflake" \ -d "The weekly nix-rebuild-cache job failed. Check: journalctl -u nix-rebuild-cache.service" \ "https://ntfy.mjallen.dev/builds" || true ''; atticConfig = lib.${namespace}.mkModule { inherit config name; description = "attic Service"; options = { }; moduleConfig = { services.atticd = { inherit (cfg) environmentFile; enable = true; settings = { listen = "${cfg.listenAddress}:${toString cfg.port}"; storage = { type = "local"; path = "${cfg.configDir}/atticd"; }; }; }; # Include the attic watch-store service and rebuild cache services systemd.services = { attic-watch-store = { enable = true; description = "watch store for cache"; serviceConfig = { Type = "simple"; User = "admin"; Group = "jallen-nas"; WorkingDirectory = "/etc/nixos"; StandardOutput = "journal+console"; StandardError = "journal+console"; Restart = "always"; RestartSec = "5"; }; script = '' ${pkgs.attic-client}/bin/attic watch-store nas-cache ''; wantedBy = [ "multi-user.target" ]; }; nix-rebuild-cache = { enable = true; description = "Rebuild NixOS configurations for cache"; serviceConfig = { Type = "oneshot"; User = "admin"; Group = "jallen-nas"; WorkingDirectory = "/etc/nixos"; StandardOutput = "journal+console"; StandardError = "journal+console"; Restart = "no"; TimeoutStartSec = "2h"; EnvironmentFile = [ config.sops.templates."ntfy.env".path ]; }; unitConfig.OnFailure = "nix-rebuild-cache-notify-failure.service"; path = with pkgs; [ nix git coreutils gnugrep gnused openssh ]; script = '' #!/usr/bin/env bash if [ -d .git ]; then git pull || echo "Warning: Could not pull latest changes" git stash git pull || echo "Warning: Could not pull latest changes after stash" return 1 fi echo "Updating flake at $(date)" if nix flake update; then echo "flake updated successfully at $(date)" else echo "failed to update flake $(date)" fi if nix flake check; then echo "flake checked successfully at $(date)" else echo "flake check failed at $(date)" git reset --hard fi if nh os build --hostname=jallen-nas --out-link=result-nas; then echo "nas built successfully at $(date)" fi; if nh os build --hostname=nuc-nixos --out-link=result-nuc; then echo "nuc built successfully at $(date)" fi; if nh os build --hostname=matt-nixos --out-link=result-desktop; then echo "desktop built successfully at $(date)" fi; if nh os build --hostname=steamdeck --out-link=result-steamdeck; then echo "steamdeck built successfully at $(date)" fi; if nh os build --hostname=pi5 --out-link=result-pi5; then echo "pi5 built successfully at $(date)" fi; ''; }; nix-rebuild-cache-notify-failure = { description = "Notify ntfy on nix-rebuild-cache failure"; serviceConfig = { Type = "oneshot"; ExecStart = "${ntfyFailScript}"; EnvironmentFile = [ config.sops.templates."ntfy.env".path ]; }; }; }; # Include timers for cache rebuilds systemd.timers = { nix-rebuild-cache = { description = "Timer for rebuilding NixOS configurations cache"; wantedBy = [ "timers.target" ]; timerConfig = { OnCalendar = "weekly"; Persistent = true; RandomizedDelaySec = "24h"; }; }; }; # Configure distributed builds nix = { settings.builders-use-substitutes = true; distributedBuilds = true; buildMachines = [ { hostName = "pi5.local"; system = "aarch64-linux"; maxJobs = 4; sshUser = "matt"; supportedFeatures = [ "nixos-test" "benchmark" "big-parallel" "kvm" ]; } ]; }; }; }; in { imports = [ atticConfig ]; }