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 をインストールします。
{ 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 の文法に関する参考
- 公式のマニュアル https://nixos.org/manual/nix/stable/#chap-writing-nix-expressions
- Nix Pills(公式ブログ) https://nixos.org/guides/nix-pills/basics-of-language.html
設定ファイルを読む
改めて最初に定義した設定ファイルを読んでみます。
{ 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 のマニュアルを参照してください。
{
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
などして既存のファイルを退避させておきましょう。