Featured image of post Nixとhome-managerにdotfiles管理を移行する

Nixとhome-managerにdotfiles管理を移行する

あけましておめでとうございます。 2023年気持ちも新たに、まずはdotfilesの見直しから開始しました。

これまでは、Emacs以外の fishgit などのconfigはpCloud経由で管理し、シンボリックリンク経由で利用していました。 またWSL環境やPC/Mac環境の再構築をする中で、config以外の exafzf といった普段利用しているパッケージのインストールも毎回必要なため、 homebrewやlinuxbrewを含めたインストール手順を実行していました。

これらを一元管理し、更に自動化したいなぁと思いつついつの間にやら数年経っていた^^;ところ、Nixを知りました。 当時NixOSというディストロがあるということは目にしていたのですが、クロスプラットフォーム(Win除く)なNixというパッケージ管理システムがあることは知らず、 今回これはよさそうだということで、重い腰を上げて移行してみることにしました。

Nixとは

Nixは純粋関数的パッケージマネージャです。公式ドキュメントによると、以下のように説明されています。

Nix is a purely functional package manager. This means that it treats packages like values in purely functional programming languages such as Haskell — they are built by functions that don’t have side-effects, and they never change after they have been built.

ちょっとわかりにくいですが、NixにはNix expressionという独自の言語があり、これによって各Nixのパッケージが記述されています。 これはビルド環境や手順、依存関係などを記述するものなのですが、これが関数的言語であり、同一の入力に対して同一の出力を保証しているということを言っていると思われます。

Nixpkgs

この記述されたNixのパッケージ群がNix Packages collection(Nixpkgs)で、いわゆるパッケージリポジトリに相当するものですが、コンセプトにある通りこの中身はNix expressionによる記述の集合です。 実際のパッケージはここから出力されたものが https://cache.nixos.org でcacheされており、可能であればそこから取得できるようになっています。

Nix Packages collection. Contribute to NixOS/nixpkgs development by creating an account on GitHub.
GitHub - NixOS/nixpkgs: Nix Packages collection

たとえばnodejsを例にすると、このコードにnodejs.orgからソースを取得して依存関係の解決とビルドする方法がNix expressionで記載されている様子がわかります。

Nix Profiles

NixマニュアルのProfilesに説明があるのですが、パッケージのインストール先は /nix/store 以下に集約され、直接/usr/binなどに展開されません。 更にNixではユーザー環境も同様にNix以下で管理され、version管理されています。各versionに含まれるパッケージの違いを管理することで、環境構成のアトミック性とロールバックの仕組みを担保しているようです。

パッケージの構成管理を記述するためにもNix expressionが利用できます。 ~/.config/nixpkgs/config.nix: にglobal configurationを配置することができ、そこでNixで解決するパッケージを記述できる旨がNixpkgsマニュアルのDeclarative Package Managementに紹介されています。

以前はNixのマニュアルにもconfigに関する記載があったらしいのですが、22.11の今日現在そのような記述はないようです。 Nixはドキュメントが結構散らかっている印象で、初学者には中々把握しずらい印象があります。 実際Hacker Newsなどでもそのようなコメントを見かけることもありましたので、どうもそういうもののようです。

新しいNixのドキュメントプロジェクト nix.dev (2024/4/11追記)

上記のような学習コストの高さ、ドキュメントの複雑さを解消すべく新しい情報源の整備をするNix Bookというプロジェクトがありました。

ただ気がついたらここは閉鎖されていて、nix.devプロジェクトに統合されていました。

Nixパッケージマネージャのインストール

以下の home-manager を利用するには、Nixパッケージマネージャをインストールする必要があります。

NixOS以外の各OSもしくはWSL2環境でのインストール方法があり、公式ページに詳細があります。 今回はMac OSに導入するので、以下を実行します。

1
sh <(curl -L https://nixos.org/nix/install)

これでNixパッケージマネージャを利用できるようになりました。(実際にはnix-shellなども使えるので純粋にパッケージマネージャだけでもない)

home-manager

home-managerは上記のNixパッケージマネージャを利用して、ローカルユーザー環境で利用するパッケージの管理とdotfilesを一元的に管理するための仕組みです。

Manage a user environment using Nix [maintainer=@rycee] - GitHub - nix-community/home-manager: Manage a user environment using Nix [maintainer=@rycee]
GitHub - nix-community/home-manager: Manage a user environment using Nix  [maintainer=@rycee]

単純に必要なパッケージを記述、管理できるだけでなく、例えば .config/fish/config.fish ファイルや .config/git/config ファイルなどもNix expressionの中に一緒に書き込んで管理できる特徴があります。

インストール

MacOSへのインストールでは、standalone installationに従ってインストールを行います。

NixのChannelとして、home-managerに必要なパッケージがまとめられていますので、それを追加します。 masterブランチをターゲットにすることもできますが、私は最新のリリースバージョンで固定して追加します。

1
2
nix-channel --add https://github.com/nix-community/home-manager/archive/release-22.11.tar.gz home-manager
nix-channel --update

マニュアルには以下の設定もした方がよいとあります。 NIX_PATH はNixがパッケージが検索をするためのPATHのようで、どうもここに上記で設定したユーザーのChannel設定が通っている必要があるようです。

1
export NIX_PATH=$HOME/.nix-defexpr/channels:/nix/var/nix/profiles/per-user/root/channels${NIX_PATH:+:$NIX_PATH}

そして、home-mamagerをインストールします。

1
nix-shell '<home-manager>' -A install

最後に、 hm-session-vars.sh のソースをするように設定します。 これは、中身を見るとわかりますが、home-managerで設定したdotfilesの結果必要になった環境変数が書きこまれるファイルのようです。 つまりこれをシェルのconfigなどに記述しておかないとhome-managerの設定が一部反映されなくなるようなので注意が必要です。

home.nixの記述

home-managerのインストールが完了すると、 .config/nix/home.nix が以下の雛形で生成されています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{ config, lib, pkgs, ... }:

with lib;

{
  # Home Manager needs a bit of information about you and the
  # paths it should manage.
  home.username = "tomoyukim";
  home.homeDirectory = "/Users/tomoyukim";

  # This value determines the Home Manager release that your
  # configuration is compatible with. This helps avoid breakage
  # when a new Home Manager release introduces backwards
  # incompatible changes.
  #
  # You can update Home Manager without changing this value. See
  # the Home Manager release notes for a list of state version
  # changes in each release.
  home.stateVersion = "22.11";

  # Let Home Manager install and manage itself.
  programs.home-manager.enable = true;
}

ここに、自身で必要なパッケージとdotfilesの記述をしていきます。

パッケージ単体だけ

ripgrep などのようにパッケージインストールだけでよいものは、以下のように記述を追加していくだけです。

1
2
3
4
5
6
home.packages = [
  pkgs.ripgrep
  pkgs.silver-searcher
  pkgs.fd
  ...
];

何らかのconfigも一緒に記述したいもの

git のようにconfigファイルやプラグイン、環境変数設定なども一緒に管理する必要があるものは programs を利用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
programs.git = {
  enable = true;
  userName = "tomoyukim";
  userEmail = "tomoyukim@outlook.com";
  extraConfig = {
    core = {
      editor = "'/usr/local/bin/emacsclient -t' --alternate-editor vim";
    };
    init = {
      defaultBranch = "main";
    };
    ...
  };
  ...
};

どのような programs がどのようなconfigを持っているのかは、home-managerリポジトリのmodules/programsを直接参照するとわかります。

パッケージとdotfilesの反映

準備ができたら、アクティベーションを実行します。

1
home-manager switch

このとき、もし既存のconfigなどがある場合はエラーしますので退避しておく必要があります。

生成されたconfigは /nix/store/0z7f1q...-home-manager-files/.config/git/config のようにNixストアへのシンボリックリンクになっており、直接編集はできません。 あくまでも編集は home.nix で実施し、それを反映させていく、という運用になります。

編集の履歴はNixの仕組みで管理されており、

1
home-manager generations

とすると、それまでの生成されたconfigの履歴を確認することができます。 またNixの仕組みでロールバックすることもできるようになっています。

まとめ

Nix+home-managerによるパッケージとdotfilesの管理への移行についてまとめてみました。 基本的なシェル環境はこれですっきり包括的に管理できるようになった印象があります。

なお、Emacsはここに混ぜるとちょっと扱いにくそうだったので別管理のまま除外しています。 また、シェル環境以外の設定(.e.g AlfredのconfigやKalabinerのconfigなど)はこのスキームに乗せられないため引き続き別の手段が必要そうです。

2023年は継続利用して育てていきつつ、いずれPCの乗り換えなどでその効果を実感できるんじゃないかと期待しています。

Built with Hugo
テーマ StackJimmy によって設計されています。