diff --git a/flake.lock b/flake.lock index 84063af..8e318b4 100644 --- a/flake.lock +++ b/flake.lock @@ -1,31 +1,5 @@ { "nodes": { - "colmena": { - "inputs": { - "flake-compat": "flake-compat", - "flake-utils": "flake-utils", - "nix-github-actions": "nix-github-actions", - "nixpkgs": [ - "nixpkgs-unstable" - ], - "stable": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1739900653, - "narHash": "sha256-hPSLvw6AZQYrZyGI6Uq4XgST7benF/0zcCpugn/P0yM=", - "owner": "zhaofengli", - "repo": "colmena", - "rev": "2370d4336eda2a9ef29fce10fa7076ae011983ab", - "type": "github" - }, - "original": { - "owner": "zhaofengli", - "repo": "colmena", - "type": "github" - } - }, "disko": { "inputs": { "nixpkgs": [ @@ -50,11 +24,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1650374568, - "narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=", + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "b4a34015c698c7793d592d66adbab377907a2be8", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -66,11 +40,11 @@ "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -95,38 +69,7 @@ "type": "github" } }, - "flake-compat_4": { - "flake": false, - "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "flake-utils": { - "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { "inputs": { "systems": "systems" }, @@ -144,7 +87,7 @@ "type": "github" } }, - "flake-utils_3": { + "flake-utils_2": { "inputs": { "systems": "systems_2" }, @@ -164,8 +107,8 @@ }, "ghostty": { "inputs": { - "flake-compat": "flake-compat_2", - "flake-utils": "flake-utils_2", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", "nixpkgs-stable": [ "nixpkgs" ], @@ -191,7 +134,7 @@ }, "git-hooks": { "inputs": { - "flake-compat": "flake-compat_3", + "flake-compat": "flake-compat_2", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs-unstable" @@ -213,7 +156,7 @@ }, "git-hooks_2": { "inputs": { - "flake-compat": "flake-compat_4", + "flake-compat": "flake-compat_3", "gitignore": "gitignore_2", "nixpkgs": [ "slippi", @@ -295,7 +238,7 @@ }, "helix": { "inputs": { - "flake-utils": "flake-utils_3", + "flake-utils": "flake-utils_2", "nixpkgs": [ "nixpkgs-unstable" ], @@ -373,27 +316,6 @@ "type": "github" } }, - "nix-github-actions": { - "inputs": { - "nixpkgs": [ - "colmena", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1729742964, - "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=", - "owner": "nix-community", - "repo": "nix-github-actions", - "rev": "e04df33f62cdcf93d73e9a04142464753a16db67", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nix-github-actions", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1741600792, @@ -444,7 +366,6 @@ }, "root": { "inputs": { - "colmena": "colmena", "disko": "disko", "ghostty": "ghostty", "git-hooks": "git-hooks", diff --git a/flake.nix b/flake.nix index 3e13211..3ebd89c 100644 --- a/flake.nix +++ b/flake.nix @@ -25,9 +25,6 @@ formatter = uGenPkgs (p: p.nixfmt-rfc-style); - colmena = import ./lib/colmena inputs; - colmenaHive = inputs.colmena.lib.makeHive inputs.self.outputs.colmena; - /* TODO: nix-on-droid for phone terminal usage? mobile-nixos? TODO: nix-darwin for work? @@ -73,10 +70,6 @@ ghostty.inputs.nixpkgs-unstable.follows = "nixpkgs-unstable"; ghostty.inputs.nixpkgs-stable.follows = "nixpkgs"; - colmena.url = "github:zhaofengli/colmena"; - colmena.inputs.nixpkgs.follows = "nixpkgs-unstable"; - colmena.inputs.stable.follows = "nixpkgs"; - # nnf.url = "github:thelegy/nixos-nftables-firewall?rev=71fc2b79358d0dbacde83c806a0f008ece567b7b"; mobile-nixos = { diff --git a/lib/colmena/default.nix b/lib/colmena/default.nix deleted file mode 100644 index f80ca61..0000000 --- a/lib/colmena/default.nix +++ /dev/null @@ -1,70 +0,0 @@ -{ - home-manager, - nixpkgs-unstable, - self, - ... -}@inputs: -{ - meta = - let - nixpkgsSet = - nixpkgs: - (import nixpkgs { - system = "x86_64-linux"; - overlays = [ self.outputs.flakeLib.forSelfOverlay ]; - }); - nixpkgs = nixpkgsSet nixpkgs-unstable; - stable = nixpkgsSet nixpkgs; - in - { - inherit nixpkgs; - nodeNixpkgs = { - # router = stable; - beefcake = stable; - }; - specialArgs = { - inherit home-manager; - hardware = inputs.hardware.outputs.nixosModules; - diskoConfigurations = inputs.self.outputs.diskoConfigurations; - }; - }; - - # TODO: setup builders? - foxtrot = - { - # name, - # nodes, - # pkgs, - ... - }: - { - deployment = { - # Allow local deployment with `colmena apply-local` - allowLocalDeployment = true; - - # Disable SSH deployment. This node will be skipped in a - # normal`colmena apply`. - targetHost = null; - }; - - imports = [ - inputs.self.outputs.nixosModules.default - (import ./../../packages/hosts/foxtrot.nix) - ]; - - # boot.isContainer = true; - # time.timeZone = nodes.host-b.config.time.timeZone; - }; - beefcake = - { ... }: - { - deployment = { - buildOnTarget = true; - }; - - imports = [ - inputs.self.outputs.nixosModules.default - (import ./../../packages/hosts/beefcake.nix) - ]; - }; -} diff --git a/packages/default.nix b/packages/default.nix index 8ec7afe..01fa1da 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -7,6 +7,7 @@ in iosevka = pkgs.callPackage ./iosevka.nix { }; iosevkaLyteTermSubset = pkgs.callPackage ./iosevkaLyteTermSubset.nix { }; installer = pkgs.callPackage ./installer.nix { }; + upgrader = pkgs.callPackage ./upgrader { }; ghostty-terminfo = pkgs.callPackage ./ghostty-terminfo.nix { }; forgejo-actions-container = pkgs.callPackage ./forgejo-actions-container.nix { }; } diff --git a/packages/upgrader/default.nix b/packages/upgrader/default.nix new file mode 100644 index 0000000..257983b --- /dev/null +++ b/packages/upgrader/default.nix @@ -0,0 +1,116 @@ +{ + pkgs, + ... +}: +pkgs.writeShellApplication { + name = "upgrader"; + runtimeInputs = with pkgs; [ + ripgrep + fzf + jq + gawk + ]; + text = '' + CHECK_TIMEOUT=120 + + usage() { + exit_code="''${1:-0}" + echo "upgrader TARGET_HOST" + echo " -h,--help Show help" + echo " --wait-and-rollback GEN Wait CHECK_TIMEOUT seconds and rollback to the boot generation [REMOTE]" + echo " --ignore-lock Ignore locks" + exit "$exit_code" + } + + ilog() { + echo "$1" >> .upgrader.log + } + + wait_and_rollback() { + if [[ ! $EUID = 0 ]]; then + exit 1 + fi + touch .upgrader.watcher.lck + trap "" EXIT + trap "echo 'watcher: exiting' >> .upgrader.log; rm -f .upgrader.watcher.lck" EXIT + boot_gen="$(cat /boot/loader/loader.conf | rg 'default\s*nixos-generation-(\d+)\.conf' -r '\$1'")" + ilog "watcher: boot gen is '$boot_gen'" + while ! pgrep nixos-rebuild &>/dev/null; do + sleep 1; + done + ilog 'watcher: nixos-rebuild started' + while pgrep nixos-rebuild &>/dev/null; do + sleep 1 + done + ilog 'watcher: nixos-rebuild finished' + for i in $(seq 0 "$CHECK_TIMEOUT"); do + ilog "watcher: wait $i" + sleep 1 + done + ilog "watcher: was not killed, rolling back to boot gen '$boot_gen'" + nix-env --switch-generation "$gen" -p /nix/var/nix/profiles/system + /nix/var/nix/profiles/system/bin/switch-to-configuration switch + exit 1 + } + + [[ $# -lt 1 ]] && { printf "error: no TARGET_HOST argument provided\n" >&2; usage 1; } + target_host="$1" + shift + + ignore_locks=0 + + while [ "$#" -gt 0 ]; do + case "$1" in + --wait-and-rollback) + wait_and_rollback "$@" + ;; + --ignore-locks) + ignore_locks=1 + break + ;; + -h | --help | help) + usage 0 + ;; + *) + echo "error: unexpected argument '$1' (if you meant to pass this to the underlying command(s), use '-- $1')" + usage 1 + esac + shift + done + + ssh "$target_host" true + echo "SSH access to '$target_host' confirmed!" + + # TODO: install upgrader on remote host and know how to call it + + log() { + echo "$1" + ssh "$target_host" 'echo "$(date) $1" >> .upgrader.log' + } + + remote_bg_job() { + ssh "$target_host" 'nohup sh -c "( ( '"$1"' ) & )"' + } + + if [[ $ignore_locks == 0 ]] && ssh "$target_host" "stat .upgrader.lck &>/dev/null" &>/dev/null; then + log ".upgrader.lck exists on '$target_host', refusing to upgrade" + exit 1 + fi + + # TODO: handle boot-level failures? + + boot_gen="$(ssh "$target_host" "cat /boot/loader/loader.conf | rg 'default\s*nixos-generation-(\d+)\.conf' -r '\$1'")" + log "Current Boot Generation for '$target_host': $boot_gen" + + # TODO: start some kind of "waiter" + remote_bg_job 'remote_upgrader --wait-and-rollback' + + log "Upgrading '$target_host' (may require sudo prompt)..." + ssh -t "$target_host" "touch .upgrader.lck; sudo nixos-rebuild test --flake git+https://git.lyte.dev/lytedev/nix" + + ssh -t "$target_host" 'remote_upgrader --kill-waiter' + + log 'Promoting current configuration to boot generation (may require sudo prompt)...' + ssh -t "$target_host" 'sudo /run/current-system/bin/switch-to-configuration switch' + ''; +}