Dotfiles managed with Nix
0
fork

Configure Feed

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

fix: complete declarative dotfile config wiring

+146 -54
+8
README.md
··· 37 37 | Command | Effect | 38 38 | --- | --- | 39 39 | `just build` | Build the configuration for the current machine | 40 + | `just check` | Evaluate the flake and build the current machine configuration | 40 41 | `just switch` | Apply the configuration for the current machine | 41 42 | `just hm-build <host>` | Build a specific Linux Home Manager target | 42 43 | `just hm-switch <host>` | Apply a specific Linux Home Manager target | 44 + 45 + ## Managing App Configs 46 + 47 + 1. Put the app files under `config/<app>/`. 48 + 2. Add the app to `configs.nix`. 49 + 3. Omit `paths` to link the whole directory to `~/.config/<app>`. 50 + 4. Use `paths` when an app needs file-level links or non-standard destinations such as `.aerospace.toml`. 43 51 44 52 ## Adding A Machine 45 53
+50 -6
_configs.nix
··· 1 + { lib }: 1 2 let 2 - mkConfig = name: options: { 3 - active = options.active or true; 4 - configDir = options.configDir or true; 5 - dest = options.dest or ""; 6 - }; 3 + checkPaths = name: paths: 4 + let 5 + required = [ "src" "dest" ]; 6 + missingFields = lib.unique (lib.concatMap 7 + (path: builtins.filter (field: !(builtins.hasAttr field path)) required) 8 + paths); 9 + sourceLabel = path: if path.src == "" then "<app root>" else path.src; 10 + sourcePath = path: 11 + ./config + "/${name}${lib.optionalString (path.src != "") "/${path.src}"}"; 12 + in 13 + if paths == [] then 14 + throw "configs.${name}.paths must not be empty" 15 + else if missingFields != [] then 16 + throw "configs.${name}.paths entries are missing: ${lib.concatStringsSep ", " missingFields}" 17 + else 18 + let 19 + missingSources = lib.unique (builtins.map sourceLabel (builtins.filter 20 + (path: !(builtins.pathExists (sourcePath path))) 21 + paths)); 22 + in 23 + if missingSources != [] then 24 + throw "configs.${name} references missing sources under config/${name}: ${lib.concatStringsSep ", " missingSources}" 25 + else 26 + true; 27 + 28 + mkConfig = name: opts: 29 + let 30 + configDir = opts.configDir or true; 31 + paths = 32 + if opts ? paths then 33 + opts.paths 34 + else if configDir then 35 + [ 36 + { 37 + src = ""; 38 + dest = ".config/${name}"; 39 + } 40 + ] 41 + else 42 + throw "configs.${name} must define paths when configDir = false"; 43 + in 44 + assert checkPaths name paths; 45 + { 46 + active = opts.active or true; 47 + inherit configDir paths; 48 + }; 49 + result = lib.mapAttrs mkConfig (import ./configs.nix); 7 50 in 8 51 9 - lib.mapAttrs mkConfig (import ./configs.nix) 52 + # Evaluates `result` (1st) recursively and evaluates and returns `result` (2nd). 53 + builtins.deepSeq result result
+2
_hosts.nix
··· 7 7 # username 8 8 # homeDirectory 9 9 # flakeDirectory 10 + # deployDirectory 10 11 # } 11 12 mkHost = host: 12 13 let ··· 20 21 host // { 21 22 inherit homeDirectory; 22 23 flakeDirectory = host.flakeDirectory or "${homeDirectory}/.dot"; 24 + deployDirectory = host.deployDirectory or "${homeDirectory}/.dot-deploy"; 23 25 }; 24 26 in 25 27
+62 -8
configs.nix
··· 2 2 # 3 3 # Attrset: 4 4 # name: { (string - the name of the application directory) 5 - # active: bool - default `true`; whether the configuration has to be 6 - # linked or not 7 - # configDir: bool - default `true`; whether the configuration has to be 8 - # placed in ~/.config or not 9 - # dest: string - path relative to the home directory where the 10 - # configuration has to be set; configDir *must* be `false` 11 - # in order to use this field 5 + # active: bool - default `true`; whether the configuration has to be 6 + # linked or not 7 + # configDir: bool - default `true`; whether the whole app directory should be 8 + # linked to ~/.config/<name> when `paths` is omitted 9 + # paths: [ 10 + # { 11 + # src: string - path relative to the app config "~/.dot/config/app-name" 12 + # dest: string - path relative to home directory 13 + # } 14 + # { ... } 15 + # ] 12 16 # } 13 17 { 14 - rio: { active: false } 18 + aerospace = { 19 + configDir = false; 20 + paths = [ 21 + { 22 + src = "aerospace.toml"; 23 + dest = ".aerospace.toml"; 24 + } 25 + ]; 26 + }; 27 + 28 + alacritty = { }; 29 + ghostty = { }; 30 + helix = { }; 31 + karabiner = { }; 32 + kitty = { }; 33 + pet = { }; 34 + rio = { }; 35 + sketchybar = { }; 36 + 37 + skhd = { 38 + configDir = false; 39 + paths = [ 40 + { 41 + src = "skhdrc"; 42 + dest = ".skhdrc"; 43 + } 44 + ]; 45 + }; 46 + 47 + starship = { 48 + paths = [ 49 + { 50 + src = "starship.toml"; 51 + dest = ".config/starship.toml"; 52 + } 53 + ]; 54 + }; 55 + 56 + wezterm = { }; 57 + 58 + yabai = { 59 + configDir = false; 60 + paths = [ 61 + { 62 + src = "yabairc"; 63 + dest = ".yabairc"; 64 + } 65 + ]; 66 + }; 67 + 68 + zed = { }; 15 69 }
+3 -2
flake.nix
··· 30 30 inherit lib isDarwin; 31 31 }; 32 32 33 - configs = import ./_configs.nix 33 + configs = import ./_configs.nix { inherit lib; }; 34 34 35 35 # Import nixpkgs for one target system with the repo's shared policy. 36 36 mkPkgs = system: import nixpkgs { ··· 52 52 mkHome = _: host: home-manager.lib.homeManagerConfiguration { 53 53 pkgs = mkPkgs host.system; 54 54 extraSpecialArgs = { 55 - inherit inputs; 55 + inherit inputs configs; 56 + inherit (host) flakeDirectory; 56 57 }; 57 58 modules = [ (mkHomeModule host) ]; 58 59 };
+9 -38
home/dotfiles.nix
··· 4 4 let 5 5 active = lib.filterAttrs (_: opts: opts.active) configs; 6 6 7 - # { 8 - # name: the app name 9 - # target: the configuration final destination 10 - # } 11 - mkList = app: opts: 12 - let 13 - 14 - target = opts.target 15 - if opts.configDir then 16 - target = ".config" 17 - 7 + mkSource = app: src: 8 + let 9 + suffix = lib.optionalString (src != "") "/${src}"; 10 + in 11 + config.lib.file.mkOutOfStoreSymlink "${flakeDirectory}/config/${app}${suffix}"; 18 12 19 - in 20 - { 21 - inherit app 13 + mkEntry = app: path: { 14 + home.file."${path.dest}".source = mkSource app path.src; 22 15 }; 23 16 24 - finalList = lib.mapAttrsToList mkList active; 25 - 26 - mkConfig = app: 27 - { 28 - home.file.".config/${app}".source = config.lib.file.mkOutOfStoreSymlink "${flakeDirectory}/config/${app}"; 29 - }; 17 + mkEntries = app: opts: lib.map (path: mkEntry app path) opts.paths; 30 18 31 19 in 32 20 33 - lib.mapAttrsToList 34 - 35 - # lib.mkMerge (lib.forEach [ 36 - # # "aerospace" 37 - # # "alacritty" 38 - # # "ghostty" 39 - # # "helix" 40 - # # "karabiner" 41 - # # "kitty" 42 - # # "pet" 43 - # "rio" 44 - # # "sketchybar" 45 - # # "skhd" 46 - # # "starship" 47 - # # "wezterm" 48 - # # "yabai" 49 - # # "zed" 50 - # ] mkConfig) 21 + lib.mkMerge (lib.flatten (lib.mapAttrsToList mkEntries active))
+12
justfile
··· 16 16 home-manager build --flake ".#{{current_host}}" 17 17 fi 18 18 19 + check: 20 + #!/usr/bin/env bash 21 + set -euo pipefail 22 + printf 'Checking host %s\n' "{{current_host}}" 23 + nix flake check --show-trace -L 24 + if [ "$(uname -s)" = "Darwin" ] 25 + then 26 + nix build ".#darwinConfigurations.{{current_host}}.system" --show-trace -L 27 + else 28 + nix build ".#homeConfigurations.{{current_host}}.activationPackage" --show-trace -L 29 + fi 30 + 19 31 switch: 20 32 #!/usr/bin/env bash 21 33 printf 'Using host %s\n' "{{current_host}}"