ryota2357

dotfiles での Nix 設定分割

投稿日:

更新日:

tag icon svg Nix

この記事は ryota2357 Advent Calendar 2024 の 7 日目の記事です。

dotfiles に Nix を導入してから 4 ヶ月ほどたった。 大量に設定をしているわけではないが、それでもファイルを分割・整理しないとやや管理が大変となる。 2024 年末の僕の Nix ファイルの分割方法について書いてく。

ディレクトリ構造

僕の dotfiles のディレクトリ構造の主要部分だけ切り出したものは次である。

.
├── README.md
├── flake.lock
├── flake.nix
├── bin/...
├── config-files
│   ├── _generate_nix
│   ├── _generated.nix
│   └── ...
├── nix
│   ├── apps
│   │   └── default.nix
│   ├── darwin
│   │   ├── default.nix
│   │   ├── homebrew.nix
│   │   ├── nix.nix
│   │   └── system.nix
│   ├── formatter
│   │   └── default.nix
│   └── home
│       ├── default.nix
│       └── modules/..
├── shell
│   ├── fish/...
│   └── zsh/..
├── tmux/...
└── vim/..

僕は Home Manager も使用しているが、これに寄せることはせず ファイル配置ツールとして使用している。 そのため、vim や各種 shell の設定は通常(?)のシンボリックリンクを使用した dotfiles の構成と同じようにそれぞれのディレクトリに格納されている。

nix/ 以下が Nix の各種設定が書かれておいて、flake.nix から import さてている。flake.nix から順に見ていく。

flake.nix

flake.nix は次のような感じである。


{
  description = "ryota2357's Nix configuration";
  inputs = { 省略 };
  outputs =
    {
      self,
      nixpkgs,
      home-manager,
      nix-darwin,
      neovim-nightly-overlay,
    }:
    let
      system = "aarch64-darwin";
    in
    {
      apps = (
        import ./nix/apps {
          inherit nixpkgs;
        }
      );

      formatter = (
        import ./nix/formatter {
          inherit nixpkgs;
        }
      );

      darwinConfigurations = (
        import ./nix/darwin {
          inherit system nix-darwin;
        }
      );

      homeConfigurations = (
        import ./nix/home {
          inherit system home-manager;
          username = "ryota2357";
          pkgs = import nixpkgs {
            inherit system;
            overlays = [
              neovim-nightly-overlay.overlays.default
            ];
          };
        }
      );
    };
}

上から nix run, nix fmt, nix-darwin, Home Manager の設定を import している。 flake.nix では使用するものを列挙するのみで、具体的な設定をせず、その設定に必要なものを注入するにとどめ、各 Nix ツールの独立を明確にしている。

nix/ 以下

順に簡単に説明する。

nix/apps

このディレクトリ以下にあるのは default.nix のみで、 nix run で実行できるものを用意している。

僕は home-manager コマンドをグローバルにインストールしていないので、次のようにして home-manager switch を実行している。

$ nix run .#home-manager-switch default

nix/formatter

ここは説明することがないので省略する。

nix/darwin

このディレクトリ以下の default.nix は次の通りである。

{
  system,
  nix-darwin,
}:
{
  default = nix-darwin.lib.darwinSystem {
    inherit system;
    modules = [
      {
        services.nix-daemon.enable = true;
        nix = import ./nix.nix;
        system = import ./system.nix;
        homebrew = import ./homebrew.nix;
      }
    ];
  };
}

nix, system, homebrew の Attribute Set はここに書かず import で分割している。

modules アトリビュートにはファイルパスを指定するものを多く見かけるが、nix 式を書くことができる。 この default.nix のように Attribute Set を置くこともできるし、次のような関数を置くこともできる。

...
    modules = [
      { pkgs, ... }:
      {
        ...
      }
    ]
...

nix/home

このディレクトリ以下の default.nix は次の通りである。

{
  system,
  username,
  home-manager,
  pkgs,
}:
let
  isDarwin = builtins.elem system [
    "aarch64-darwin"
    "x86_64-darwin"
  ];
in
{
  default = home-manager.lib.homeManagerConfiguration {
    inherit pkgs;
    extraSpecialArgs = {
      config-file = import ../../config-files/_generated.nix;
    };
    modules = [
      {
        home = {
          inherit username;
          homeDirectory = if isDarwin then "/Users/${username}" else throw "unsupported system: ${system}";
          stateVersion = "24.11"; # https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion
        };
      }
      ./modules/direnv.nix
      ...
      ./modules/vim.nix
      {
        home.file.".local/bin".source = ../../bin;
        home.packages = with pkgs; [
          ffmpeg
          fzf
          ...
        ];
      }
    ];
  };
}

./modules/_.nix には次のような感じで pkgs.* と各ツールの設定ファイルがどこにあり、どこに配置するのかを記述している。 次は ./modules/tmux.nix である。

{ pkgs, ... }:
{
  home.packages = [ pkgs.tmux ];
  xdg.configFile =
    let
      s = name: { source = ../../../tmux + "/${name}"; };
    in
    {
      "tmux/tmux.conf" = s "tmux.conf";
      "tmux/config" = s "config";
    };
}

programs.tmux で Nix 式を使用して設定をするのではなく、それぞれのツールの設定はそれぞれのファイルに記述し、Home Manager で配置するという方法をとっている。

tmux や vim は複数の設定ファイルがあるので、dotfiles/vim や dotfiles/tmux に配置して、それらを source している。 しかし、LaTeX や git, fd など設定が 1 ファイルのものを dotfiles/latex や dotfiles/git などとディレクトリを作るのは少しやりすぎなように思える。 なので、そのような設定が 1 ファイルなものは dotfiles/config-files 以下にまとめている。

nix/home/default.nix の中では、extraSpecialArgsconfig-file = import ../../config-files/_generated.nix; でその config-files/ を import している。

_generated とあるが、複雑なものではなく次ような Attribute Set が記述されているだけである。

# This file is generated by _generate_nix. Do not modify it manually.
{
  "alacritty.toml" = ./. + "/alacritty.toml";
  ...
  "wezterm.lua" = ./. + "/wezterm.lua";
}

これは次の bash スクリプトで生成している。

#!/usr/bin/env bash

set -euo pipefail
cd "$(dirname "$0")" || exit 1

main() {
    local output_file="_generated.nix"
    > "$output_file"

    write() {
        echo "$1" >> "$output_file"
    }

    write "# This file is generated by _generate_nix. Do not modify it manually."
    write "{"
    local file
    for file in *; do
        [[ -f $file && $file != _* ]] || continue
        write "  \"$file\" = ./. + \"/$file\";"
    done
    write "}"

    echo "Successfully generated $output_file"
}

main

まとめ

いろんな人の設定分割をみたが、僕は今の所この分割方法に落ち着いている。 この方法は僕の Home Manager の使い方に合っていると思っている。

この記事ではざっくりと nix ファイルの内容を説明した。より詳細は ryota2357/dotfiles を確認して欲しい。 分割方法が変わった時はまた記事を書こうと思う。