LoginSignup
15
6

More than 3 years have passed since last update.

Nix と home-manager で宣言的に環境を管理する

Posted at

Nix パッケージマネージャーを入れたら、絶対に入れておきたいのが home-manager です。
home-manager は nix を用いてユーザー環境を宣言的に管理するためのツールで、

  • インストールするパッケージ
  • ~/.profile, ~/.bashrc, ~/config/fishなどの設定ファイル
  • fish やら tmux やら firefox やらのプラグイン
  • email のアカウント

など、様々なユーザー設定を一枚のファイルで宣言的に管理できるようにしてくれる優れものです。

nix と home-manager だけいれておけば、PC いれかえてもコマンド一発でセットアップ完了も夢ではありません。
(実際仕事で PC 入れ替えるときにそれをしました。)

詳しいことはhome-manager の github ページとかマニュアルを参照すれば分かるのですが、英語でしかも長いので、自分用に要点だけまとめようと思います。

home-manager のインストール

home-manager を使うには Nix パッケージマネージャーが必要なので、まずは Nix をインストールします。

> curl -L https://nixos.org/nix/install | sh
> nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs
> nix-channel --update

nixchannel --add <url> [channel-alias] でチャンネルの登録ができます。リポジトリの登録みたいなものです。

home-manager のインストールも、まずは home-manager のチャンネルの登録から行います。

> nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager
> nix-channel --update

そして以下のコマンドを打つことでインストールできます。

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

パッケージ管理

home-manager を使ってユーザー環境を設定するには、~/.config/nixpkgs/home.nixを作成します。

例えば、以下の設定は、git と GNU/hello をインストールします。

.config/nixpkgs/home.nix
{ pkgs, ... }:
{
    # ここは home-manager をインストールしたときに自動で入る
    programs.home-manager.enable = true;
    home.username = "hnakano";
    home.stateVersion = "20.09";

    # ここでインストールするパッケージを宣言する
    home.packages = [
        pkgs.git
        pkgs.hello
    ];
}

それで設定を反映するためには以下のコマンドを打ちます。

> home-manager switch

この設定ファイルは、Nix expression と呼ばれる独自の言語で書かれています。
正直ほとんど json みたいなものなので、あまり文法は気にしなくていいのですが、気になる人のために最低限の文法を解説します。

home-manager を使うための最低限の文法

repl

Nix expression の repl があります。コマンドラインで nix repl と入力すれば repl に入れます。

> nix repl
Welcome to Nix version 2.3.7. Type :? for help.

nix-repl>

:? と入力すれば repl コマンドのヘルプが表示されます。 :q で repl から抜けられます。

基本の型

数値、ブール、文字列の型があります。
文字列の結合は+です。

nix-repl> 1 #interger
1

nix-repl> 3.14 #float
3.14

nix-repl> true #boolean
true

nix-repl> "hogehoge" #string
"hogehoge"

nix-repl> "hoge" + "fuga" #string concatenate
"hogefuga"

let ... in

変数への値の代入は let ... in を使います。
セミコロンの位置に気をつけてください。

nix-repl> let x = 1; y = 2; z = x + y; in z * 2
6

repl の中でだけ、let ... in を使わずに値の代入ができます。
しかし、通常の Nix expression ではこのような書き方はできないので気をつけてください。

nix-repl> x = 3

nix-repl> y = 4

nix-repl> x + y
7

List

リストは[ a b c ] です。要素の区切りは半角スペースか改行です。
ちなみに型は一致させる必要がありません

nix-repl> [ 1 "hoge" true ]
[ 1 "hoge" true ]

nix-repl> [
            1
            2
            3
          ]
[ 1 2 3 ]

リストの要素にアクセスするにはbuiltins.elemAt というビルトイン関数を使います。関数適用にかっこは不要です。

nix-repl> x = [ 1 2 3 ]

nix-repl> builtins.elemAt x 0
1

nix-repl> builtins.elemAt x 1
2

関数

Nix の関数は arg: body という形です。コロンの左側が引数になります。
2 引数関数はarg1: arg2: bodyです。

nix-repl> hello =  x: "hello, " + x  

nix-repl> hello "world"
"hello, world"

基本的に Nix では無名関数しか作れません。

nix-repl> x: y: x + y
«lambda @ (string):1:1»

なので、関数に名前を付けたければ、let 式を使います

nix-repl> let
            add = x: y: x + y;
            add2 = add 2; #部分適用ができる
          in
          add2 3 #add 2 3 に同じ
5

Attribute set

{ key1 = value1; key2 = value2; } という形の、中括弧で key = value のペアを囲んだものを Attribute set といいます。
key = value; のペアのお尻に必ずセミコロンが必要です。

x.key1 という形で値にアクセスできます。

nix-repl> let
            x = {     
              hoge = "hoge";
              fuga = "fuga";
            };
          in x.hoge
"hoge"
attribute set を引数に取る関数

attribute set を引数にとる関数は、普通に arg: body の形で定義できます。

nix-repl> let
            f = x: x.hoge + x.fuga;
          in f { hoge = "hoge"; fuga = "fuga"; }
"hogefuga"

しかし、その代わりに { key1, key2 }: body という形でも定義できます。
この形だと、引数の key の名前が厳密に一致しないとエラーになります。
{key, key2, ... }: body のように、引数に...を入れるとこのエラーを避けられます。

nix-repl> f = { hoge, fuga }: hoge + fuga

nix-repl> let x = { hoge = "hoge"; fuga = "fuga"; }; in f x
"hogefuga"

nix-repl> let y = { hoge = 3; fuga = 4; piyo = true; }; in f y
error: anonymous function at (string):1:2 called with unexpected argument 'piyo', at (string):1:50

nix-repl> g = { hoge, ... }: "hello, " + hoge

nix-repl> let y = { hoge = "world"; fuga = 4; piyo = true; }; in g y
"hello, world"

その他

Nix Expression の文法に関する参考

設定ファイルを読む

改めて最初に定義した設定ファイルを読んでみます。

./config/nixpkgs/home.nix
{ pkgs, ... }:
{
    # ここは home-manager をインストールしたときに自動で入る
    programs.home-manager.enable = true;
    home.username = "hnakano";
    home.stateVersion = "20.09";

    # ここでパッケージを宣言する
    home.packages = [
        pkgs.git
        pkgs.hello
    ];
}

まず、この式はpkgsというキーをもつ Attribute set を引数にとる関数であるということが分かります。
pkgs には通常 <nixpkgs> つまり、Nix のパッケージコレクションが入ります。

{ key1.key2 = value; } というのはネストした attribute set の構文糖で、{ key1 = { key2 = value; }; }を表しています。

構文糖を使わなければ、この設定ファイルは以下のようになります。

{ pkgs, ... }:
{
    programs = {
        home-manager = {
            enable = true;
        };
    };

    home = {
        username = "hnakano";
        stateVersion = "20.09";
        packages = [
            pkgs.git;
            pkgs.hello;
        ];
    };
}

即ち、この関数は { pkgs = ..; ... }という Attribute set を引数にとり、
{ programs = { ... }; home = { ... }; } という Attribute set を返す関数ということです。

home-managerコマンドは、この返り値を評価して設定をユーザー環境に反映します。

他の設定項目

home-manager を使って色々設定してみます。
home-manager で設定可能な内容はhome-manager のマニュアルを参照してください。

.config/nixpkgs/home.nix
{
  home.packages = with pkgs; [ nixfmt exa bat source-han-code-jp ];

  programs.git = {
    enable = true;
    userName = "hnakano863";
    userEmail = "myEmail@gmail.com";
    extraConfig.pull.rebase = false;
  };

  fonts.fontconfig.enable = true;

  programs.bash = {
    enable = true;
    profileExtra = ''
      export XDG_DATA_DIRS=$HOME/.nix-profile/share''${XDG_DATA_DIRS:+:}$XDG_DATA_DIRS
      export LIBGL_ALWAYS_INDIRECT=1
      export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0
      if [ -e $HOME/.nix-profile/etc/profile.d/nix.sh ]; then
          . $HOME/.nix-profile/etc/profile.d/nix.sh;
      fi
    '';
    initExtra = ''
      if [ -z $IN_NIX_SHELL ]; then exec fish fi
    '';
    shellAliases = {
      ls = "exa";
      cat = "bat";
    };
  };

  programs.fish = {
    enable = true;
    interactiveShellInit = ''
      set -gx fish_user_paths $HOME/go/bin $HOME/.local/bin
    '';
  };

  programs.tmux = {
    enable = true;
    clock24 = true;
    keyMode = "vi";
    shortcut = "a";
    terminal = "screen-256color";
  };
}

若干説明していない文法事項があるので、説明します。

まず、home.packagesのところでwith pkgs; [...] という形があります。
これは、いちいち [ pkgs.nixfmt pkgs.exa ... ] などとpkgs. を書くことを避けるための構文です。

次に、programs.bash.profileExtraなどで出て来るシングルクォート 2 つで囲まれている文字列ですが、
こちらは indented string というタイプの文字列です。

indented string の中ではインデントが無視されるので、シェルスクリプトを書くのに便利です。
実際、programs.bash.profileExtra~/.profileに反映されます。

最後に、programs.bash.enable = true; にしている場合、home-manager switch を実行すると、~/.profile~/.bashrcなどを置き換えようとします。
このとき、ユーザー環境にすでに~/.bashrcなどの既存の設定ファイルが存在する場合、home-managerコマンドは「設定ファイルあるんだが」とエラーを吐いて失敗してしまいます。
これを避けるために、事前にmv ~/.bashrc ~/.bashrc.bk などして既存のファイルを退避させておきましょう。

15
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
6