❄️

怠惰なHomebrewユーザーでもNixを使いたい!!

に公開

はじめに

最近巷で話題のNix。耳にする頻度がだんだん増えてきました。
何やらNixを使うとHomebrewとは違って宣言的にパッケージやmacOSの設定を管理できるらしいじゃないですか。

奥が深いらしいNixですが様々な記事を参考にしつつ怠惰な自分でもNixを使って楽にパッケージやOSの設定を管理できたので書き残しておきます。

宣言的に管理したいというモチベーションだけでNixを使用しているため本来の思想や使用用途から外れたことをしているかもしれません。ご容赦くださいmm

自分は怠惰なのでNixとは何か、Nixの使い方についての詳細は省きます!!
👆が分かっていなくても楽に管理できるのが目的なので!!
この記事を見てNixが面白いと思った方は自分でさらに奥深くへ進んでみてください。
そして自分に色々教えてください。

対象読者

  • Homebrewを使っている人
  • Nixについてなんとなくしか知らない人
  • なんでもいいから楽にパッケージやmacOS設定を宣言的管理したい人
  • dotfilesの概念を理解している人

要約

最終的なディレクトリ構成は👇のようになりました。

~/dotfiles
├─ nix
│  ├─ flake.nix # メインファイル
│  ├─ flake.lock
│  ├─ pkgs.nix # Nixpkgsに存在するCLI管理
│  └─ nix-darwin
│     ├─ config.nix # nix-darwinの管轄ファイル
│     ├─ homebrew.nix # GUI、Nixpkgsに存在しないCLI、フォント管理
│     └─ system.nix # macOSの設定管理
└─ ... # 他のdotfiles設定ファイル

各ファイルの内容は👇のアコーディオン内を参照してください。

flake.nix
flake.nix
{
  description = "My Dotfiles.";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
    nix-darwin = {
      url = "github:LnL7/nix-darwin";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs =
    {
      self,
      nixpkgs,
      nix-darwin,
    }:
    let
      system = "aarch64-darwin";
      pkgsConfig = import ./pkgs.nix { inherit nixpkgs system; };
      pkgs = pkgsConfig.pkgs;
    in
    {
      packages.${system}.お好きなパッケージ名 = pkgs.buildEnv {
        name = "お好きなパッケージ名";
        paths = pkgsConfig.myPackages;
      };

      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 nix
            echo "Updating nix-darwin..."
            nix run nix-darwin -- switch --flake .#お好きなホスト構成名
            echo "Update complete!"
          ''
        );
      };

      darwinConfigurations.お好きなホスト構成名 = nix-darwin.lib.darwinSystem {
        system = system;
        modules = [ ./nix-darwin/config.nix ];
      };
    };
}
pkgs.nix
pkgs.nix
{ nixpkgs, system }:
let
  pkgs = import nixpkgs {
    inherit system;
    config.allowUnfreePredicate =
      pkg:
      builtins.elem (nixpkgs.lib.getName pkg) [
        "claude-code"
      ];
  };
in
{
  inherit pkgs;

  myPackages = with pkgs; [
    ### CLI Applications
    nil
    nixfmt-rfc-style
    fzf
    bat
    ripgrep
    eza
    fish
    gh
    git
    just
    claude-code
    terminal-notifier
    gemini-cli
    jankyborders
    mise
    kubectl
  ];
}
nix-darwin/config.nix
nix-darwin/config.nix
{ pkgs, ... }:
{
  imports = [
    ./system.nix
    ./homebrew.nix
  ];
}
nix-darwin/homebrew.nix
nix-darwin/homebrew.nix
{ pkgs, ... }:
{
  homebrew = {
    enable = true;
    onActivation = {
      autoUpdate = true;
      cleanup = "uninstall";
    };
    caskArgs = {
      appdir = "~/Applications";
    };
    brews = [
      ### CLI Applications not available in nixpkgs
      "fisher"
    ];
    taps = [
      "nikitabobko/tap"
    ];
    casks = [
      ### GUI Applications
      "wezterm"
      "1password"
      "1password-cli"
      "aerospace"
      "cursor"
      "firefox"
      "obsidian"
      "raycast"
      "stats"
      "shottr"
      "notchnook"
      "jordanbaird-ice"
      "kap"
      "keycastr"
      "zed"
      "arc"

      ### Fonts
      "font-google-sans-code"
      "font-jetbrains-mono-nerd-font"
    ];
  };
}
nix-darwin/system.nix
nix-darwin/system.nix
{ pkgs, ... }:
{
  nix = {
    optimise.automatic = true;
    settings = {
      experimental-features = "nix-command flakes";
      max-jobs = 8;
    };
  };

  system = {
    stateVersion = 6;
    primaryUser = "Macのユーザー名";

    keyboard = {
      enableKeyMapping = true;
      # CapsLockをControlにマッピング
      remapCapsLockToControl = true;
    };

    defaults = {
      CustomUserPreferences = {
        "com.apple.desktopservices" = {
          DSDontWriteNetworkStores = true;
        };
      };
      WindowManager = {
        # デスクトップ上のアイテムを非表示にする
        StandardHideDesktopIcons = true;
      };
      controlcenter = {
        # メニューバーにAirDropを表示しない
        AirDrop = false;
        # メニューバーにバッテリー残量を表示しない(Statsで表示しているため)
        BatteryShowPercentage = false;
        # メニューバーにBluetoothを表示する
        Bluetooth = true;
      };
      dock = {
        # アプリケーションスイッチャーを全てのディスプレイに表示する
        appswitcher-all-displays = true;
        # Dockの自動非表示を有効にする
        autohide = true;
        # Mission ControlのExposeでアプリケーションをグループ化する
        expose-group-apps = true;
        # Dockのホバー時の拡大サイズ
        largesize = 48;
        # Dockのホバー時の拡大を有効にする
        magnification = true;
        # Dockの位置を左側に設定する
        orientation = "left";
        # 最近使用したアプリケーションをDockに表示しない
        show-recents = false;
        # 開いているアプリケーションのみをDockに表示する
        static-only = true;
        # Dock内のアイコンサイズ
        tilesize = 32;
        # ホットコーナー(左下)にLaunchpadを設定する
        wvous-bl-corner = 11;
        # ホットコーナー(右下)にApplication Windowsを設定する
        wvous-br-corner = 3;
        # ホットコーナー(左上)に通知センターを設定する
        wvous-tl-corner = 12;
        # ホットコーナー(右上)にMission Controlを設定する
        wvous-tr-corner = 2;
      };
      finder = {
        # 拡張子を常に表示する
        AppleShowAllExtensions = true;
        # 隠しファイルを常に表示する
        AppleShowAllFiles = true;
        # Finderアイコンをデスクトップに表示しない
        CreateDesktop = false;
        # 拡張子を変更するときに警告を表示しない
        FXEnableExtensionChangeWarning = false;
        # リストビューをデフォルトにする
        FXPreferredViewStyle = "Nlsv";
        # CD, DVDなどをデスクトップに表示しない
        ShowRemovableMediaOnDesktop = false;
        # Finderの下部にステータスバーを表示する
        ShowStatusBar = true;
        # 完全パスを表示する
        _FXShowPosixPathInTitle = true;
      };
      magicmouse = {
        # 左右クリックを有効にする
        MouseButtonMode = "TwoButton";
      };
      menuExtraClock = {
        # メニューバーの時計を24時間表示にする
        Show24Hour = true;
        # メニューバーに余裕がある場合は日付を表示する
        ShowDate = 0;
        # 日付を表示する
        ShowDayOfMonth = true;
        # 曜日を表示する
        ShowDayOfWeek = true;
        # 秒は表示しない
        ShowSeconds = false;
      };
      spaces = {
        # ディスプレイごとに異なるスペースを使用する
        spans-displays = false;
      };
      universalaccess = {
        # カーソルのサイズ
        mouseDriverCursorSize = 2.0;
      };
    };
  };
}

中身を見てみる

さて、それぞれのファイルがどんな役割を持っているか、実際に見ていきましょう。

flake.nix

これがNixプロジェクトのエントリーポイントです。最初は複雑に見えますが、基本的には「何を使うか(inputs)」と「何を作るか(outputs)」を定義しているだけです。

まず、自分のCLIツール群をまとめてインストールできるようにする部分です

packages.${system}.お好きなパッケージ名 = pkgs.buildEnv {
  name = "お好きなパッケージ名";
  paths = pkgsConfig.myPackages;
};

初回は以下のコマンド一発で全部インストールします。

nix profile install .#お好きなパッケージ名

次に、更新作業を自動化する部分

apps.${system}.update = {
  type = "app";
  program = toString (
    pkgs.writeShellScript "update-script" ''
      # 各種アップデート処理が書かれている
    ''
  );
};

これでnix run .#updateを実行するだけで、flakeの更新からnix-darwinの適用まで全部やってくれます。
基本的にはどの設定に関しても変更を加えた場合は上記コマンドを実行するだけでOKです。
このコマンドに関しては👇の記事を参考にさせていただきました🙇‍♂️

https://zenn.dev/kawarimidoll/articles/0a4ec8bab8a8ba

最後に、nix-darwinとの連携部分です。
nix-darwinについては公式ドキュメントを参照してください。
macOSのあれこれを宣言的に設定できるプロジェクトです。

https://github.com/nix-darwin/nix-darwin

darwinConfigurations.お好きなホスト構成名 = nix-darwin.lib.darwinSystem {
  system = system;
  modules = [ ./nix-darwin/config.nix ];
};

pkgs.nix

ここでNixpkgsからインストールするCLIツールを定義します。
Nixpkgsに存在しないパッケージ以外はここで管理します。本来であればNixpkgs以外からでもインストールできるのですが怠惰なのでHomebrewに一任してます。

claude-codeのような非フリーソフトウェアを使いたい場合は、こんな感じで許可してあげる必要があります。

config.allowUnfreePredicate =
  pkg:
  builtins.elem (nixpkgs.lib.getName pkg) [
    "claude-code"
  ];
myPackages = with pkgs; [
  ### CLI Applications
  nil
  nixfmt-rfc-style
  fzf
  bat
  ripgrep
  # どんどん追加していく
];

with pkgs;を使うことで、いちいちpkgs.を書かなくて済むのが地味に嬉しいです。

使いたいツールがNixpkgsにあるかどうかは👇で検索できます。

https://search.nixos.org/packages

nix-darwin/config.nix

nix-darwinの設定をテーマごとに管理できます。
現時点ではhomebrew管理、macOS設定管理だけです。

imports = [
  ./system.nix
  ./homebrew.nix
];

nix-darwin/homebrew.nix

GUIアプリケーションやNixpkgsにないものはここでHomebrewを使って管理します。最初は「結局Homebrew使うんかい!」と思いましたが、宣言的に管理できればなんでも良いのです。

自分はいつも brew searchで検索してから入れてます。

自分はこんな感じで使い分けてます

  • GUIアプリ → casks(WezTermとか1Passwordとか)
  • Nixpkgsにないツール → brews(fisherくらいしか使ってないけど)
  • フォント → casks

nix-darwin/system.nix

nix-darwinを使用するとシステム環境設定でポチポチやる作業が全部コードで管理できます。
設定可能な項目は👇の通りです。

https://nix-darwin.github.io/nix-darwin/manual/

実際の運用

初回セットアップ

  1. Nixをインストール

https://github.com/DeterminateSystems/nix-installer

  1. dotfilesリポジトリをclone
  2. nix profile install .#お好きなパッケージ名, nix run .#updateを実行
  3. 終わり!

非常に簡単でした。

日々のアップデート

自分はnix run .#updateを定期的に実行するだけです。これだけで

  • flakeのロックファイルが更新される
  • Nixのプロファイルがアップグレードされる
  • nix-darwinの設定が適用される
  • Homebrewのパッケージも更新される

全部自動でやってくれるので本当に楽です。

パッケージの追加

新しいツールを使いたくなったら

  1. pkgs.nix, homebrew.nixに追加する
  • CLIの場合
    • nixpkgsを検索し存在すればpkgs.nixに、なければhomebrew.nixに追加する
  • GUIの場合
    • homebrew.nixに追加する
  1. nix run .#updateで適用

これだけです。

最後に

怠惰な自分でも宣言的にパッケージやmacOSの設定を管理することができました。
Nixに関してまだまだ知識が浅いので、もっと理解度を高めていきたいと思っています。

参考まで、自分のdotfiles貼っておきます👇

https://github.com/ayuukumakuma/dotfiles

ここまでお付き合いいただきありがとうございました!!

参考文献

https://zenn.dev/kawarimidoll/articles/0a4ec8bab8a8ba

https://zenn.dev/asa1984/books/nix-introduction

https://zenn.dev/asa1984/books/nix-hands-on

GitHubで編集を提案
SMARTCAMP Engineer Blog

Discussion