Homebrew管理下のCLIをNixに移してみる Home Manager篇
macOSにおけるパッケージマネージャといえばHomebrewが定番ですね。
ところで、最近は純粋関数型パッケージマネージャのNixが流行りで激アツです。Nix関連ツールのHome Managerを使うと、Homebrewのようなパッケージ管理ができるということを知り、やってみました。
前回の記事で作ったファイルを書き換える形で進めていくので、先に以下の記事をご確認ください。また、Apple Silicon Mac環境での作業を基に書いています。
導入
前回作ったflakeにhome-managerを導入します。
- inputsに追加
- outputsの引数に追加
- outputsの本体に
homeConfigurations
を追加
また、 homeConfigurations
でinputsを使いたい(home-managerの設定にinputsを渡したい)ので、outputsの引数部分に@ inputs
を追加します。
{
description = "Minimal package definition for aarch64-darwin";
inputs = {
# 略...
+ home-manager = {
+ url = "github:nix-community/home-manager";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
};
outputs = {
# 略...
- }: let
+ home-manager,
+ } @ inputs: let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system}.extend (
neovim-nightly-overlay.overlays.default
);
in {
# 略...
+ homeConfigurations = {
+ myHomeConfig = home-manager.lib.homeManagerConfiguration {
+ pkgs = import nixpkgs {
+ system = system;
+ };
+ extraSpecialArgs = {
+ inherit inputs;
+ };
+ modules = [
+ ./.config/home-manager/home.nix
+ ];
+ };
+ };
};
}
前回の記事ではnixpkgsのシステム指定(およびoverlayの指定)にextend
を使いましたが、今回の記事ではimport
を使っています。できることは同じだと思いますが、Web上のサンプルだとimport
を使っているものが多い印象です。
Home Managerの設定自体は別のファイルに書いていきます。筆者は上記のflakeをdotfilesリポジトリに置いているので、Home Managerの設定は./.config/home-manager/home.nix
に配置することにしました。パスはお好みで変更してください。
最初のhome.nixはこんな感じです。
{
inputs,
lib,
config,
pkgs,
...
}: let
username = "kawarimidoll";
in {
nixpkgs = {
config = {
allowUnfree = true;
};
};
home = {
username = username;
homeDirectory = "/Users/${username}";
# https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion
stateVersion = "24.05";
};
programs.home-manager.enable = true;
}
注意点としては、username
およびhomeDirectory
は現在のシステムの$USER
および$HOME
とそれぞれ一致している必要があります。Webで見つけたサンプルは、homeDirectory
を/home/${username}
としているものが多かったです。macOSではホームディレクトリが/Users/${username}
なので、以下の通り怒られます。
Error: HOME is set to "/Users/kawarimidoll" but we expect "/home/kawarimidoll"
また、stateVersion
は更新時には注意が必要なようですが、新規作成なので最新バージョンでいいでしょう。記事執筆時点では24.05
が最新です。
設定を保存したら、以下のコマンドでビルドします。myHomeConfig
はflakeのoutputsのhomeConfigurations
に設定したキーの名前です。
nix run nixpkgs#home-manager -- switch --flake .#myHomeConfig
nixはgitを使ってファイルを認識します。home.nixをgit addしておくことを忘れずに。
実行例スクショ
エラーなく完了すれば、nix run nixpkgs#home-manager -- generations
でgenerationsが確認できます。第一世代の誕生です。
パッケージ定義の記述
前回の記事で導入したパッケージをHome Managerの管理下に移動させていきます。Home Managerのほうの設定を少しずつ増やし、それに伴って旧設定を少しずつ消していきます。
nixpkgsから取り込む
flakeでhome-manager.lib.homeManagerConfiguration.pkgs
にnixpkgsを設定しているので、このファイル内ではpkgs
からnixpkgsのパッケージを取り込むことができます。以下のようにhome.packages
にパッケージを指定します。
{
# 略...
home = {
# 略...
+ packages = with pkgs; [
+ git
+ curl
+ ];
};
# 略...
}
with
は前回の記事で登場しました。つまりこう書いているのと同じです。
packages = [
pkgs.git
pkgs.curl
];
旧設定のほうからgitとcurlの設定を消します。
{
# 略...
outputs = {
# ...
}: let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system}.extend (
neovim-nightly-overlay.overlays.default
);
in {
packages.${system}.my-packages = pkgs.buildEnv {
name = "my-packages-list";
paths = with pkgs; [
- git
- curl
(vim.overrideAttrs (oldAttrs: {
# ...
}))
neovim
];
};
# ...
};
}
まだ旧設定が残っているので、nix run .#update
でgitとcurlの削除を反映したあと、Home Managerをビルドします。再びgitとcurlが使えるようになれば成功です。
nixpkgs以外のflakeから取り込む
前回と同様、nix-community/neovim-nightly-overlayを導入します。
nixpkgsの中にoverlaysを追加します。neovim-nightly-overlayはinputsを介して呼び出すことができます。
これでhome.packagesに追加したneovim
がnightlyになります。
{
inputs,
# ...
}: let
username = "kawarimidoll";
in {
nixpkgs = {
+ overlays = [
+ inputs.neovim-nightly-overlay.overlays.default
+ ];
config = {
allowUnfree = true;
};
};
home = {
# ...
packages = with pkgs; [
git
curl
+ neovim # nighly
];
};
# ...
}
旧設定からneovim関連の記述を消しておきましょう。
{
# 略...
outputs = {
# ...
}: let
system = "aarch64-darwin";
- pkgs = nixpkgs.legacyPackages.${system}.extend (
- neovim-nightly-overlay.overlays.default
- );
+ pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.my-packages = pkgs.buildEnv {
name = "my-packages-list";
paths = with pkgs; [
(vim.overrideAttrs (oldAttrs: {
# ...
}))
- neovim
];
};
# ...
};
}
GitHubから取り込む
最後にVimの最新版の取得・ビルドの設定を行います。overlaysに設定を追加します。prev.xxx
がオーバーレイ前のパッケージですね。
これでhome.packagesに追加したvim
がlatestになります。
{
inputs,
# ...
}: let
username = "kawarimidoll";
in {
nixpkgs = {
overlays = [
+ (final: prev: {
+ vim = prev.vim.overrideAttrs (oldAttrs: {
+ version = "latest";
+ src = inputs.vim-src;
+ configureFlags =
+ oldAttrs.configureFlags
+ ++ [
+ "--enable-terminal"
+ "--with-compiledby=nix-home-manager"
+ "--enable-luainterp"
+ "--with-lua-prefix=${prev.lua}"
+ "--enable-fail-if-missing"
+ ];
+ buildInputs =
+ oldAttrs.buildInputs
+ ++ [prev.gettext prev.lua prev.libiconv];
+ });
+ })
inputs.neovim-nightly-overlay.overlays.default
];
config = {
allowUnfree = true;
};
};
home = {
# ...
packages = with pkgs; [
git
curl
+ vim # latest
neovim # nighly
];
};
# ...
}
↑このprev.
もwith
を使ってまとめたかったのですが、うまいやり方がわかりませんでした。
これで旧設定からパッケージ定義がなくなりました。
{
# 略...
outputs = {
# ...
}: let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system}.my-packages = pkgs.buildEnv {
name = "my-packages-list";
paths = with pkgs; [
- (vim.overrideAttrs (oldAttrs: {
- # ...
- }))
];
};
# ...
};
}
コード整理
パッケージが導入できたので、flake.nixのoutputsから不要な記述を消します。my-packages関連の部分は完全に消すことができます。
定義削除後、nix profile remove my-packages
してmy-packagesのプロファイルを消しておきましょう。
また、Home Managerのビルドをupdateスクリプトにいれると便利でしょう。nix run .#update
でビルドできるようになります。
pkgs
のシステム指定もlet ... in
の方にまとめます。
outputs = {
self,
nixpkgs,
- neovim-nightly-overlay,
- vim-src,
home-manager,
+ ...
} @ inputs: let
system = "aarch64-darwin";
- pkgs = nixpkgs.legacyPackages.${system};
+ pkgs = import nixpkgs { inherit system; };
in {
- packages.${system}.my-packages = pkgs.buildEnv {
- name = "my-packages-list";
- paths = with pkgs; [
- ];
- };
apps.${system}.update = {
type = "app";
program = toString (pkgs.writeShellScript "update-script" ''
set -e
echo "Updating flake..."
nix flake update
- echo "Updating profile..."
- nix profile upgrade my-packages
+ echo "Updating home-manager..."
+ nix run nixpkgs#home-manager -- switch --flake .#myHomeConfig
echo "Update complete!"
'');
};
homeConfigurations = {
myHomeConfig = home-manager.lib.homeManagerConfiguration {
- pkgs = import nixpkgs {
- system = system;
- };
+ pkgs = pkgs;
extraSpecialArgs = {
inherit inputs;
};
modules = [
./.config/home-manager/home.nix
];
};
};
};
この記事で作ったnixファイルの完成版
{
description = "Minimal package definition for aarch64-darwin";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
neovim-nightly-overlay.url = "github:nix-community/neovim-nightly-overlay";
vim-src = {
url = "github:vim/vim";
flake = false;
};
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
home-manager,
...
} @ inputs: let
system = "aarch64-darwin";
pkgs = import nixpkgs { inherit system; };
in {
apps.${system}.update = {
type = "app";
program = toString (pkgs.writeShellScript "update-script" ''
set -e
echo "Updating flake..."
nix flake update
echo "Updating home-manager..."
nix run nixpkgs#home-manager -- switch --flake .#myHomeConfig
echo "Update complete!"
'');
};
homeConfigurations = {
myHomeConfig = home-manager.lib.homeManagerConfiguration {
pkgs = pkgs;
extraSpecialArgs = {
inherit inputs;
};
modules = [
./.config/home-manager/home.nix
];
};
};
};
}
{
inputs,
lib,
config,
pkgs,
...
}: let
username = "kawarimidoll";
in {
nixpkgs = {
overlays = [
(final: prev: {
vim = prev.vim.overrideAttrs (oldAttrs: {
version = "latest";
src = inputs.vim-src;
configureFlags =
oldAttrs.configureFlags
++ [
"--enable-terminal"
"--with-compiledby=nix-home-manager"
"--enable-luainterp"
"--with-lua-prefix=${prev.lua}"
"--enable-fail-if-missing"
];
buildInputs =
oldAttrs.buildInputs
++ [prev.gettext prev.lua prev.libiconv];
});
})
inputs.neovim-nightly-overlay.overlays.default
];
config = {
allowUnfree = true;
};
};
home = {
username = username;
homeDirectory = "/Users/${username}";
# https://nixos.wiki/wiki/FAQ/When_do_I_update_stateVersion
stateVersion = "24.05";
packages = with pkgs; [
git
curl
vim # latest
neovim # nighly
];
};
programs.home-manager.enable = true;
}
参考資料
この記事の設定をするにあたり、以下がたいへん参考になりました。
なお、Home ManagerはCLI導入専用のツールではありません。設定可能な項目は多岐にわたります。ドキュメントはこちら。
続編
以下の記事に続きます。
Discussion