Commit 61065af898ba
Changed files (3)
modules
harmonia
modules/harmonia/default.nix
@@ -1,6 +1,7 @@
{
config,
lib,
+ pkgs,
...
}:
@@ -53,29 +54,194 @@ in
Lower values = higher priority.
'';
};
- };
- };
- config = mkIf cfg.enable {
- services.harmonia = {
- enable = true;
- signKeyPaths = [ cfg.signKeyPath ];
- settings = {
- bind = "[::]:${toString cfg.port}";
- workers = cfg.workers;
- max_connection_rate = cfg.maxConnectionRate;
- priority = cfg.priority;
+ builder = {
+ enable = mkEnableOption "Nightly cache pre-population builds";
+
+ systems = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [
+ "aomi"
+ "kyushu"
+ ];
+ description = ''
+ List of NixOS system configurations to build nightly.
+ These should be system names from the flake's nixosConfigurations.
+ '';
+ };
+
+ flakePath = mkOption {
+ type = types.str;
+ default = "/home/vincent/src/home";
+ description = ''
+ Path to the flake repository containing system configurations.
+ '';
+ };
+
+ schedule = mkOption {
+ type = types.str;
+ default = "daily";
+ example = "02:00";
+ description = ''
+ When to run the cache builder. Can be a systemd calendar expression
+ or one of: hourly, daily, weekly, monthly.
+ '';
+ };
+
+ notification = {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ description = "Enable ntfy notifications for build status";
+ };
+
+ ntfyUrl = mkOption {
+ type = types.str;
+ default = "https://ntfy.sbr.pm";
+ description = "ntfy server URL";
+ };
+
+ topic = mkOption {
+ type = types.str;
+ default = "builds";
+ description = "ntfy topic to publish to";
+ };
+
+ tokenFile = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ description = "Path to file containing ntfy auth token";
+ };
+ };
};
};
-
- networking.firewall.allowedTCPPorts = [ cfg.port ];
-
- # Ensure the signing key file exists and has correct permissions
- assertions = [
- {
- assertion = cfg.signKeyPath != "";
- message = "services.harmonia-cache.signKeyPath must be set";
- }
- ];
};
+
+ config = mkIf cfg.enable (mkMerge [
+ # Harmonia cache server
+ {
+ services.harmonia = {
+ enable = true;
+ signKeyPaths = [ cfg.signKeyPath ];
+ settings = {
+ bind = "[::]:${toString cfg.port}";
+ workers = cfg.workers;
+ max_connection_rate = cfg.maxConnectionRate;
+ priority = cfg.priority;
+ };
+ };
+
+ networking.firewall.allowedTCPPorts = [ cfg.port ];
+
+ # Ensure the signing key file exists and has correct permissions
+ assertions = [
+ {
+ assertion = cfg.signKeyPath != "";
+ message = "services.harmonia-cache.signKeyPath must be set";
+ }
+ ];
+ }
+
+ # Cache pre-population builder
+ (mkIf cfg.builder.enable {
+ assertions = [
+ {
+ assertion = cfg.builder.systems != [ ];
+ message = "services.harmonia-cache.builder.systems must not be empty when builder is enabled";
+ }
+ ];
+
+ systemd.services.harmonia-cache-builder = {
+ description = "Build NixOS systems for cache pre-population";
+ wants = [ "network-online.target" ];
+ after = [
+ "network-online.target"
+ "harmonia.service"
+ ];
+
+ serviceConfig = {
+ Type = "oneshot";
+ User = "root";
+ # Set a reasonable timeout (2 hours)
+ TimeoutStartSec = "2h";
+ };
+
+ script =
+ let
+ notifyStart = optionalString cfg.builder.notification.enable ''
+ ${pkgs.curl}/bin/curl -X POST \
+ ${
+ optionalString (cfg.builder.notification.tokenFile != null)
+ ''-H "Authorization: Bearer $(${pkgs.coreutils}/bin/tr -d '\n' < ${cfg.builder.notification.tokenFile})" \''
+ } \
+ -H "Title: Cache Builder Starting (${config.networking.hostName})" \
+ -d "Building ${toString (length cfg.builder.systems)} system(s) for cache pre-population" \
+ ${cfg.builder.notification.ntfyUrl}/${cfg.builder.notification.topic} || true
+ '';
+
+ notifySuccess = optionalString cfg.builder.notification.enable ''
+ ${pkgs.curl}/bin/curl -X POST \
+ ${
+ optionalString (cfg.builder.notification.tokenFile != null)
+ ''-H "Authorization: Bearer $(${pkgs.coreutils}/bin/tr -d '\n' < ${cfg.builder.notification.tokenFile})" \''
+ } \
+ -H "Title: Cache Builder Complete (${config.networking.hostName})" \
+ -H "Tags: white_check_mark" \
+ -d "Successfully built ${toString (length cfg.builder.systems)} system(s)" \
+ ${cfg.builder.notification.ntfyUrl}/${cfg.builder.notification.topic} || true
+ '';
+
+ notifyFailure = optionalString cfg.builder.notification.enable ''
+ ${pkgs.curl}/bin/curl -X POST \
+ ${
+ optionalString (cfg.builder.notification.tokenFile != null)
+ ''-H "Authorization: Bearer $(${pkgs.coreutils}/bin/tr -d '\n' < ${cfg.builder.notification.tokenFile})" \''
+ } \
+ -H "Title: Cache Builder Failed (${config.networking.hostName})" \
+ -H "Tags: x,warning" \
+ -H "Priority: high" \
+ -d "Failed to build systems. Check logs: journalctl -u harmonia-cache-builder.service" \
+ ${cfg.builder.notification.ntfyUrl}/${cfg.builder.notification.topic} || true
+ '';
+
+ buildSystems = concatMapStringsSep "\n" (system: ''
+ echo "Building system: ${system}"
+ ${pkgs.nixos-rebuild}/bin/nixos-rebuild build --flake ${cfg.builder.flakePath}#${system} || {
+ echo "Failed to build ${system}"
+ ${notifyFailure}
+ exit 1
+ }
+ '') cfg.builder.systems;
+ in
+ ''
+ set -euo pipefail
+
+ ${notifyStart}
+
+ cd ${cfg.builder.flakePath}
+
+ # Update flake inputs
+ echo "Updating flake inputs..."
+ ${pkgs.git}/bin/git pull || echo "Warning: Failed to pull latest changes"
+
+ ${buildSystems}
+
+ echo "All systems built successfully!"
+ ${notifySuccess}
+ '';
+ };
+
+ systemd.timers.harmonia-cache-builder = {
+ description = "Timer for harmonia cache pre-population";
+ wantedBy = [ "timers.target" ];
+
+ timerConfig = {
+ OnCalendar = cfg.builder.schedule;
+ Persistent = true;
+ RandomizedDelaySec = "30m";
+ };
+ };
+ })
+ ]);
}
systems/aion/extra.nix
@@ -94,6 +94,23 @@ in
port = 5000;
workers = 4;
priority = 30;
+
+ # Nightly cache pre-population
+ builder = {
+ enable = true;
+ systems = [
+ "aion" # Self
+ "athena" # RPi4
+ "demeter" # RPi4
+ "aix" # RPi4
+ "rhea" # Media server
+ ];
+ schedule = "02:30"; # 2:30 AM daily (offset from aomi)
+ notification = {
+ enable = true;
+ tokenFile = config.age.secrets."ntfy-token".path;
+ };
+ };
};
wireguard = {
systems/aomi/extra.nix
@@ -73,6 +73,21 @@
port = 5000;
workers = 4;
priority = 30;
+
+ # Nightly cache pre-population
+ builder = {
+ enable = true;
+ systems = [
+ "aomi" # Self
+ "kyushu" # Work laptop
+ "sakhalin" # Server
+ ];
+ schedule = "02:00"; # 2 AM daily
+ notification = {
+ enable = true;
+ tokenFile = config.age.secrets."ntfy-token".path;
+ };
+ };
};
# Remote build system