Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

home-manager: switch from client to agent

+82 -59
+2
AGENTS.md
··· 9 9 - Never break deployments or strand agents such that they cannot apply an upgrade. 10 10 - You can access the dev server live over tidewave project_eval, allowing for introspection of a live environment. 11 11 - Do not create worktrees unless explicitly asked. When asked, use: `git worktree add .worktrees/<name> -b <name>` 12 + - elixir dependencies are available in `deps`. Read code from there instead of using accessing the web or prompting the user. 13 + 12 14 13 15 ## Worktree setup 14 16
-1
justfile
··· 79 79 80 80 release-push: 81 81 git tag -a -m v$(cat VERSION) v$(cat VERSION) 82 - git push 83 82 git push --tags 84 83 jj git push 85 84 just release
+80 -58
nix/home/module.nix
··· 5 5 ... 6 6 }: 7 7 let 8 - cfg = config.services.sower.client; 8 + cfg = config.services.sower.agent; 9 9 json = pkgs.formats.json { }; 10 10 jsonType = json.type; 11 + 12 + jsonConfig = json.generate "sower-client.json" cfg.settings; 13 + 14 + stateDir = "${config.xdg.stateHome}/sower-agent"; 11 15 in 12 16 { 13 17 options = { 14 - services.sower.client = { 15 - enable = lib.mkEnableOption "Sower client"; 18 + services.sower.agent = { 19 + enable = lib.mkEnableOption "Sower agent"; 16 20 17 21 package = lib.mkOption { type = lib.types.package; }; 18 22 19 - onCalendar = lib.mkOption { 20 - type = lib.types.str; 21 - description = "OnCalendar for systemd timer on linux. See https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html#Calendar%20Events"; 22 - default = "daily"; 23 + activatorPackage = lib.mkOption { type = lib.types.package; }; 24 + 25 + accessTokenFile = lib.mkOption { 26 + type = lib.types.nullOr lib.types.path; 27 + description = "Path to file containing access token"; 28 + default = null; 23 29 }; 24 30 25 - config = lib.mkOption { 31 + settings = lib.mkOption { 26 32 type = lib.types.submodule { 27 33 freeformType = jsonType; 28 34 29 - options = { 30 - seed = { 31 - name = lib.mkOption { 32 - type = lib.types.str; 33 - description = "seed name"; 34 - default = config.home.username; 35 - }; 36 - 37 - type = lib.mkOption { 38 - type = lib.types.enum [ 39 - "home-manager" 40 - "nix-darwin" 41 - "nixos" 42 - ]; 43 - default = "home-manager"; 44 - }; 45 - }; 46 - }; 35 + options = { }; 47 36 }; 48 - description = "Sower configuration file"; 49 - default = null; 37 + description = "Sower agent configuration file"; 38 + default = { }; 50 39 }; 51 40 }; 52 41 }; ··· 56 45 { 57 46 home.packages = [ cfg.package ]; 58 47 59 - xdg.configFile."sower/client.json".source = lib.mkIf (cfg.config != null) ( 60 - json.generate "sower-client.json" cfg.config 61 - ); 48 + xdg.configFile."sower/client.json".source = lib.mkIf (cfg.settings != { }) jsonConfig; 62 49 } 63 50 64 51 (lib.mkIf pkgs.stdenv.isLinux { 65 - systemd.user.services.sower-client = { 52 + systemd.user.services.sower-agent = { 66 53 Service = { 67 - Environment = [ 68 - # prefer nix from the system 69 - "PATH=/run/current-system/sw/bin:${lib.makeBinPath [ config.nix.package ]}" 70 - "SOWER_CONFIG_FILE=%E/sower/client.json" 71 - ]; 72 - ExecStart = "${lib.getExe cfg.package} seed upgrade"; 73 - Type = "oneshot"; 54 + Environment = 55 + [ 56 + "PATH=/run/current-system/sw/bin:${lib.makeBinPath [ config.nix.package cfg.activatorPackage ]}" 57 + "SOWER_CONFIG_FILE=%E/sower/client.json" 58 + "RELEASE_MODE=interactive" 59 + "SHELL=${lib.getExe pkgs.bash}" 60 + ] 61 + ++ lib.optionals (cfg.accessTokenFile != null) [ 62 + "SOWER_ACCESS_TOKEN_FILE=${cfg.accessTokenFile}" 63 + ]; 64 + 65 + ExecStartPre = pkgs.writeShellScript "sower-agent-init" '' 66 + mkdir -p ${stateDir} 67 + if [ ! -e ${stateDir}/release-cookie ]; then 68 + ${lib.getExe pkgs.openssl} rand -hex 48 > ${stateDir}/release-cookie 69 + fi 70 + ''; 71 + ExecStart = pkgs.writeShellScript "sower-agent-start" '' 72 + RELEASE_COOKIE=$(cat ${stateDir}/release-cookie) 73 + export RELEASE_COOKIE 74 + exec ${lib.getExe cfg.package} start 75 + ''; 76 + ExecStop = pkgs.writeShellScript "sower-agent-stop" '' 77 + RELEASE_COOKIE=$(cat ${stateDir}/release-cookie) 78 + export RELEASE_COOKIE 79 + PID=$(${lib.getExe cfg.package} pid) 80 + ${lib.getExe cfg.package} stop 81 + while [ -d "/proc/$PID" ]; do sleep 1; done 82 + ''; 83 + ExecReload = pkgs.writeShellScript "sower-agent-reload" '' 84 + RELEASE_COOKIE=$(cat ${stateDir}/release-cookie) 85 + export RELEASE_COOKIE 86 + ${lib.getExe cfg.package} rpc "SowerAgent.request_reload()" 87 + ''; 88 + 89 + Type = "notify"; 90 + WatchdogSec = "10s"; 91 + 92 + Restart = "always"; 93 + RestartSec = "5"; 94 + RestartMaxDelaySec = "120s"; 95 + RestartSteps = "7"; 96 + 97 + WorkingDirectory = stateDir; 98 + 99 + MemoryAccounting = true; 100 + MemoryMax = "200M"; 74 101 }; 75 102 76 103 Unit = { 77 - # For sd-switch users, this prevents killing sower mid-upgrade 104 + # For sd-switch users, this prevents killing sower mid-deployment 78 105 X-SwitchMethod = "keep-old"; 79 106 }; 80 - }; 81 107 82 - systemd.user.timers.sower-client = { 83 - Install.WantedBy = [ "timers.target" ]; 84 - 85 - Timer = { 86 - OnCalendar = cfg.onCalendar; 87 - Persistent = true; 88 - }; 108 + Install.WantedBy = [ "default.target" ]; 89 109 }; 90 110 }) 91 111 92 112 (lib.mkIf pkgs.stdenv.isDarwin { 93 113 launchd = { 94 - agents.sower-client = { 114 + agents.sower-agent = { 95 115 enable = true; 96 116 config = { 97 - KeepAlive = false; 117 + KeepAlive = true; 98 118 ProgramArguments = [ 99 119 (lib.getExe cfg.package) 100 - "seed" 101 - "upgrade" 120 + "start" 102 121 ]; 103 - StartCalendarInterval = [ 122 + EnvironmentVariables = 104 123 { 105 - Hour = 1; 106 - Minute = 0; 124 + PATH = "/run/current-system/sw/bin:${lib.makeBinPath [ config.nix.package cfg.activatorPackage ]}"; 125 + SOWER_CONFIG_FILE = "${config.xdg.configHome}/sower/client.json"; 126 + RELEASE_MODE = "interactive"; 107 127 } 108 - ]; 109 - StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/sower-client-out.log"; 110 - StandardOutPath = "${config.home.homeDirectory}/Library/Logs/sower-client-err.log"; 128 + // lib.optionalAttrs (cfg.accessTokenFile != null) { 129 + SOWER_ACCESS_TOKEN_FILE = cfg.accessTokenFile; 130 + }; 131 + StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/sower-agent-err.log"; 132 + StandardOutPath = "${config.home.homeDirectory}/Library/Logs/sower-agent-out.log"; 111 133 }; 112 134 }; 113 135 };