diff --git a/flake.nix b/flake.nix index 98ca3c7..e4e7271 100644 --- a/flake.nix +++ b/flake.nix @@ -100,7 +100,7 @@ ./home/${name} ]; extraSpecialArgs = { - inherit inputs outputs; + inherit inputs outputs self; }; }; in diff --git a/home/common/features/mutable-file.nix b/home/common/features/mutable-file.nix new file mode 100644 index 0000000..71e4992 --- /dev/null +++ b/home/common/features/mutable-file.nix @@ -0,0 +1,90 @@ +{ + config, + lib, + pkgs, + self, + ... +}: +let + cfg = config.home.mutableFiles; + repoPath = config.home.mutableFilesRepoPath; + flakePrefix = self.outPath; + + fileEntries = lib.attrsToList cfg; + + toRepoPath = storePath: repoPath + lib.removePrefix flakePrefix (toString storePath); + + checkScript = lib.concatMapStringsSep "\n" ( + { name, value }: + let + target = "${config.home.homeDirectory}/${name}"; + storePath = value.source; + originalPath = toRepoPath value.source; + in + '' + if [ -f "${target}" ] && ! ${lib.getExe' pkgs.diffutils "diff"} -q "${storePath}" "${target}" > /dev/null 2>&1; then + echo "" + echo "!! mutable file changed: ${name}" + echo " To bring changes upstream:" + echo " cp ${target} ${originalPath}" + echo "" + ${lib.getExe' pkgs.diffutils "diff"} -u "${storePath}" "${target}" || true + _mutable_changed=1 + fi + '' + ) fileEntries; + + copyScript = lib.concatMapStringsSep "\n" ( + { name, value }: + let + target = "${config.home.homeDirectory}/${name}"; + source = value.source; + dirName = builtins.dirOf target; + in + '' + mkdir -p "${dirName}" + cp -f "${source}" "${target}" + chmod ${if value.executable then "755" else "644"} "${target}" + '' + ) fileEntries; +in +{ + options.home.mutableFilesRepoPath = lib.mkOption { + type = lib.types.str; + description = "Absolute path to the nixos-config repo on disk."; + }; + + options.home.mutableFiles = lib.mkOption { + type = lib.types.attrsOf ( + lib.types.submodule { + options = { + source = lib.mkOption { + type = lib.types.path; + description = "Path to the source file."; + }; + executable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Whether the file should be executable."; + }; + }; + } + ); + default = { }; + description = "Files to copy (not symlink) into the home directory, with change detection."; + }; + + config = lib.mkIf (cfg != { }) { + home.activation.mutableFiles = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + _mutable_changed=0 + ${checkScript} + if [ "$_mutable_changed" -eq 1 ]; then + echo "" + echo "!! Aborting: mutable files have been modified outside of nix." + echo " Bring the changes upstream first, then re-run." + exit 1 + fi + ${copyScript} + ''; + }; +} diff --git a/home/common/programs/claude/default.nix b/home/common/programs/claude/default.nix index 6c407c2..3662f08 100644 --- a/home/common/programs/claude/default.nix +++ b/home/common/programs/claude/default.nix @@ -54,6 +54,6 @@ source = ./statusline.sh; executable = true; }; - home.file.".claude/settings.json".source = ./settings.json; + home.mutableFiles.".claude/settings.json".source = ./settings.json; home.file.".claude/CLAUDE.md".source = ./CLAUDE.md; } diff --git a/home/common/programs/default.nix b/home/common/programs/default.nix index 2cf5adc..9656fac 100644 --- a/home/common/programs/default.nix +++ b/home/common/programs/default.nix @@ -6,6 +6,7 @@ }: { imports = [ + ../features/mutable-file.nix ./zsh.nix ./neovim ./rust.nix @@ -23,6 +24,8 @@ ./ssh.nix ]; + home.mutableFilesRepoPath = "/home/naps62/projects/nixos-config"; + programs = { home-manager.enable = true; };