From f765ba9a39a9249e91817a4b0951af9d0a7b7873 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:34:20 -0500 Subject: [PATCH 1/4] Refactor: make home-manager actually record changes I'm not entirely sure this whole rigamaroll was worth it, but basically I noticed that for some reason Ansible was skipping doing the home-manager derivation when running playbooks back-to-back. So I fixed that, or so I think. To make sure the derivation only happens when the repo is updated, I added an activation script to the home-manager config. Yes, technically the Nix part of the repo could be untouched and yet still trigger a rebuild, but that's fine. I hope this convoluted logic tree doesn't break down. --- homes/common.nix | 10 ++++++++++ playbook.yaml | 50 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/homes/common.nix b/homes/common.nix index 25e476c..5f52601 100644 --- a/homes/common.nix +++ b/homes/common.nix @@ -60,6 +60,16 @@ in { tsed = "tailscale set --exit-node="; }; sessionPath = [ "$HOME/.local/bin" ]; + activation = { + rec-hm-git-hash = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + cd "$HOME/.config/home-manager" || exit 1 + if [ -z "$(git status --porcelain --untracked-files=no)" ]; then + run echo "$(git rev-parse HEAD)" | tee $HOME/.cache/hm-git-hash + else + run echo '*' | tee $HOME/.cache/hm-git-hash + fi + ''; + }; }; programs = { home-manager = { enable = true; }; diff --git a/playbook.yaml b/playbook.yaml index 37a1a6f..0d857a7 100644 --- a/playbook.yaml +++ b/playbook.yaml @@ -239,7 +239,7 @@ state: latest method: user -# Install home-manager for the first time +# Pull the latest home-manager configuration - name: Ensure home-manager configuration is up-to-date hosts: fedora become: false @@ -262,23 +262,55 @@ when: not home_manager_repo.stat.exists register: home_manager_clone changed_when: home_manager_clone.rc == 0 + # Only run an ssh clone/pull if home-manager was present from the very + # beginning of the playbook. They need to be part of the same play, + # otherwise the hash is lost downstream. These steps assume that SSH access + # to the git repo has been established - something that cannot possibly have + # happened during the first run. + - name: Update the home-manager config repo + ansible.builtin.git: + repo: git@code.millironx.com:millironx/nix-dotfiles.git # noqa: latest + dest: "{{ ansible_env.HOME }}/.config/home-manager" + clone: true + update: true + register: home_manager_pull + when: home_manager_repo.stat.exists + + # Install home-manager for the first time + # home-manager bootstraps itself via the nix command. If the home-manager + # command is not available, then it will need to bootstrap itself - name: Determine if home-manager is installed ansible.builtin.stat: path: "{{ ansible_env.HOME }}/.nix-profile/bin/home-manager" register: home_manager_exists - name: Init home-manager ansible.builtin.shell: | - /nix/var/nix/profiles/default/bin/nix run home-manager switch --flake ~/.config/home-manager#{{ ansible_user_id }}@{{ ansible_hostname }} + /nix/var/nix/profiles/default/bin/nix run home-manager -- switch --flake ~/.config/home-manager#{{ ansible_user_id }}@{{ ansible_hostname }} when: not home_manager_exists.stat.exists register: home_manager_init changed_when: home_manager_init.rc == 0 - - name: Update home-manager git config - ansible.builtin.shell: | - cd $HOME/.config/home-manager || exit 1 \ - && git pull \ - && cd - - changed_when: false + + # There are machines with a working home-manager config without a hash file. + # Make sure that those machines have a working hash file for future use. + - name: Safety check for home-manager hash file + ansible.builtin.file: + path: "{{ ansible_env.HOME }}/.cache/hm-git-hash" + state: touch + mode: "644" + # So now we're at the part where we're assuming that the home-manager repo + # is in place, and also that home-manager has already been bootstrapped. + # We'll use the activation script from our home-manager config that records + # the hash of the repo at the time of derivation and compare that against + # the hash found by Ansible's git pull. We will skip running the derivation + # if the hashes match + - name: Find home-manager's latest commit hash + ansible.builtin.slurp: + src: "{{ ansible_env.HOME }}/.cache/hm-git-hash" + register: home_manager_hash - name: Update home-manager derivation ansible.builtin.shell: | $HOME/.nix-profile/bin/home-manager switch --flake ~/.config/home-manager#{{ ansible_user_id }}@{{ ansible_hostname }} - changed_when: false + register: home_manager_derivation + changed_when: home_manager_derivation.rc == 0 + when: | + home_manager_exists.stat.exists and (home_manager_hash.content | b64decode | trim) != home_manager_pull.after From 796a55f6022f660e757749f06d4d878fd868ee56 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:40:39 -0500 Subject: [PATCH 2/4] Add aliass for home-manager commands --- homes/common.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homes/common.nix b/homes/common.nix index 5f52601..61939c2 100644 --- a/homes/common.nix +++ b/homes/common.nix @@ -58,6 +58,10 @@ in { "tailscale set --exit-node=$(tailscale exit-node suggest | awk '{print $4}' | head -n1)"; # tsed - TailScale Exit node Disconnect tsed = "tailscale set --exit-node="; + hms = + "home-manager switch --flake ~/.config/home-manager#$USER@$(hostname -s)"; + hmb = + "home-manager build --flake ~/.config/home-manager#$USER@$(hostname -s)"; }; sessionPath = [ "$HOME/.local/bin" ]; activation = { From cd42efa8d40f716c20d5b44542a7f5b180d41462 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:34:20 -0500 Subject: [PATCH 3/4] Refactor: make home-manager actually record changes I'm not entirely sure this whole rigamaroll was worth it, but basically I noticed that for some reason Ansible was skipping doing the home-manager derivation when running playbooks back-to-back. So I fixed that, or so I think. To make sure the derivation only happens when the repo is updated, I added an activation script to the home-manager config. Yes, technically the Nix part of the repo could be untouched and yet still trigger a rebuild, but that's fine. I hope this convoluted logic tree doesn't break down. --- homes/common.nix | 10 ++++++++++ playbook.yaml | 50 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/homes/common.nix b/homes/common.nix index 25e476c..23b1864 100644 --- a/homes/common.nix +++ b/homes/common.nix @@ -60,6 +60,16 @@ in { tsed = "tailscale set --exit-node="; }; sessionPath = [ "$HOME/.local/bin" ]; + activation = { + recordHmGitHash = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + cd "$HOME/.config/home-manager" || exit 1 + if [ -z "$(${pkgs.git}/bin/git status --porcelain --untracked-files=no)" ]; then + run echo "$(${pkgs.git}/bin/git rev-parse HEAD)" | tee $HOME/.cache/hm-git-hash + else + run echo '*' | tee $HOME/.cache/hm-git-hash + fi + ''; + }; }; programs = { home-manager = { enable = true; }; diff --git a/playbook.yaml b/playbook.yaml index 37a1a6f..0d857a7 100644 --- a/playbook.yaml +++ b/playbook.yaml @@ -239,7 +239,7 @@ state: latest method: user -# Install home-manager for the first time +# Pull the latest home-manager configuration - name: Ensure home-manager configuration is up-to-date hosts: fedora become: false @@ -262,23 +262,55 @@ when: not home_manager_repo.stat.exists register: home_manager_clone changed_when: home_manager_clone.rc == 0 + # Only run an ssh clone/pull if home-manager was present from the very + # beginning of the playbook. They need to be part of the same play, + # otherwise the hash is lost downstream. These steps assume that SSH access + # to the git repo has been established - something that cannot possibly have + # happened during the first run. + - name: Update the home-manager config repo + ansible.builtin.git: + repo: git@code.millironx.com:millironx/nix-dotfiles.git # noqa: latest + dest: "{{ ansible_env.HOME }}/.config/home-manager" + clone: true + update: true + register: home_manager_pull + when: home_manager_repo.stat.exists + + # Install home-manager for the first time + # home-manager bootstraps itself via the nix command. If the home-manager + # command is not available, then it will need to bootstrap itself - name: Determine if home-manager is installed ansible.builtin.stat: path: "{{ ansible_env.HOME }}/.nix-profile/bin/home-manager" register: home_manager_exists - name: Init home-manager ansible.builtin.shell: | - /nix/var/nix/profiles/default/bin/nix run home-manager switch --flake ~/.config/home-manager#{{ ansible_user_id }}@{{ ansible_hostname }} + /nix/var/nix/profiles/default/bin/nix run home-manager -- switch --flake ~/.config/home-manager#{{ ansible_user_id }}@{{ ansible_hostname }} when: not home_manager_exists.stat.exists register: home_manager_init changed_when: home_manager_init.rc == 0 - - name: Update home-manager git config - ansible.builtin.shell: | - cd $HOME/.config/home-manager || exit 1 \ - && git pull \ - && cd - - changed_when: false + + # There are machines with a working home-manager config without a hash file. + # Make sure that those machines have a working hash file for future use. + - name: Safety check for home-manager hash file + ansible.builtin.file: + path: "{{ ansible_env.HOME }}/.cache/hm-git-hash" + state: touch + mode: "644" + # So now we're at the part where we're assuming that the home-manager repo + # is in place, and also that home-manager has already been bootstrapped. + # We'll use the activation script from our home-manager config that records + # the hash of the repo at the time of derivation and compare that against + # the hash found by Ansible's git pull. We will skip running the derivation + # if the hashes match + - name: Find home-manager's latest commit hash + ansible.builtin.slurp: + src: "{{ ansible_env.HOME }}/.cache/hm-git-hash" + register: home_manager_hash - name: Update home-manager derivation ansible.builtin.shell: | $HOME/.nix-profile/bin/home-manager switch --flake ~/.config/home-manager#{{ ansible_user_id }}@{{ ansible_hostname }} - changed_when: false + register: home_manager_derivation + changed_when: home_manager_derivation.rc == 0 + when: | + home_manager_exists.stat.exists and (home_manager_hash.content | b64decode | trim) != home_manager_pull.after From dd47f04c9b8f7e41f212f588c534adf85ad50aa5 Mon Sep 17 00:00:00 2001 From: "Thomas A. Christensen II" <25492070+MillironX@users.noreply.github.com> Date: Wed, 23 Jul 2025 13:40:39 -0500 Subject: [PATCH 4/4] Add aliass for home-manager commands --- homes/common.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/homes/common.nix b/homes/common.nix index 23b1864..15a4ac9 100644 --- a/homes/common.nix +++ b/homes/common.nix @@ -58,6 +58,10 @@ in { "tailscale set --exit-node=$(tailscale exit-node suggest | awk '{print $4}' | head -n1)"; # tsed - TailScale Exit node Disconnect tsed = "tailscale set --exit-node="; + hms = + "home-manager switch --flake ~/.config/home-manager#$USER@$(hostname -s)"; + hmb = + "home-manager build --flake ~/.config/home-manager#$USER@$(hostname -s)"; }; sessionPath = [ "$HOME/.local/bin" ]; activation = {