Import from a URL in Nix

Michael Lynch

NixでURLからインポートする

私はまだNix初心者です。最近まで分からなかったことの一つに、configuration.nixファイルの一部をバージョン管理する方法がありました。

目的

Nixの設定ファイルをモジュール化して再利用できるようにしたいと考えています。そうすれば、システムやflakeに応じて、必要な設定ファイルだけを取り込めます。また、Nixの設定ファイルはすべてバージョン管理したいと思っています。そうすれば、システムごとに各ファイルの異なるバージョンを使えるため、すべてのシステムを同時に各設定ファイルの最新版へ更新する必要がありません。

ローカルファイルで再利用可能なbashエイリアスを作る

再利用したいNix設定の一つが、bashエイリアスです。現在使っているシステムの.bashrcには、次の行があります。

alias gc='git commit --message'
alias gs='git status'
alias td='pushd $(mktemp -d)'

bashで同じことを実現するには、NixOSシステム上に次の内容のファイルを作成します。私はshell.nixという名前にしましたが、名前は何でも構いません。

{
  programs.bash.shellAliases = {
    gc = "git commit --message";
    gs = "git status";
    td = "pushd $(mktemp -d)";
  };
}

このファイルを/tmp/shell.nixに保存した場合、importsにパスを追加することで、/etc/nixos/configuration.nixからインポートできます。

{
  imports = [
    "/tmp/shell.nix"
  ];
}

sudo nixos-rebuild switchを実行してからシェルを再起動すると、bashエイリアスが有効になったことを確認できます。

$ td
/tmp/tmp.awb7PW7aus ~

[/tmp/tmp.awb7PW7aus]$ pwd
/tmp/tmp.awb7PW7aus

NixファイルをURLに移す

上の方法でも動作しますが、Nixシステムごとに同じファイルをコピーしなければなりません。

そこで、ファイルを誰でもアクセスできるURLに置き、標準的なconfiguration.nixファイルからそのURLを参照するほうがよいと思いました。

次のようにconfiguration.nixを変更すると、リモートURLにある私のshell.nixファイルを取り込めます。

let
  shellAliases = builtins.fetchurl {
    url = "https://mtlynch.io/notes/nix-import-from-url/shell.nix";
  };
in {
  imports = [
    shellAliases
  ];
}

今回も、変更内容をconfiguration.nixに保存し、sudo nixos-rebuild switchを実行してシェルを再起動すると、NixがURLからbashエイリアスをインポートします。

fetchGitを使う(任意)

リモートのNixファイルを取得する別の方法として、公開Gitリポジトリに保存し、fetchGitでファイルを取得する方法もあります。

次は、公開GitHubリポジトリからshell.nixを取得するconfiguration.nixファイルの例です。

let
  repo = builtins.fetchGit {
    url = "https://github.com/mtlynch/nix-files";
    rev = "f98500a995cb5838e40be139a8327867faaff2d5";
  };
in {
  imports = [
    "${repo}/shell.nix"
  ];
}

urlは私のGitHubリポジトリの公開URLで、revはインポートしたいファイルのバージョンに対応するGitコミットハッシュです。

configuration.nixをこのように変更した後、sudo nixos-rebuild switchをもう一度実行すれば、NixがGitHubリポジトリからshell.nixファイルをインポートします。

より高度なシェル設定

私のbashエイリアスの大半は単純なワンライナーですが、もう少し複雑なものもいくつかあります。その場合はbash関数を定義し、その関数に短いbashエイリアスを割り当てます。

その一例がgcbmというbashエイリアスです。gcbm some-branchのように使うと、次の処理を行います。

  1. mainブランチをチェックアウトする。
  2. リモートリポジトリから最新の変更を取得する。
  3. some-branchという名前の新しいブランチをチェックアウトする。

このエイリアスの主要な処理は、git_sync_and_branchというbash関数で実装しています。shell.nixでは次のように定義します。

{
  programs.bash = {
    shellInit = ''
      function git_sync_and_branch {
        local readonly TARGET_BRANCH="''${1}"
        local readonly MAIN_BRANCH='master'

        git checkout "''${MAIN_BRANCH}" && \
          git pull origin "''${MAIN_BRANCH}" && \
          if [[ -n "''${TARGET_BRANCH}" ]]; then
            git checkout -b "''${TARGET_BRANCH}"
          fi
      }
      '';
    shellAliases = {
      gc = "git commit --message";
      gcbm = "git_sync_and_branch";
      gs = "git status";
      td = "pushd $(mktemp -d)";
    };
  };
}

注意点:ドル記号のエスケープ

bash関数をNixに移そうとして戸惑った点の一つは、${という並びをエスケープする必要があることです。そうしないと、Nixはそれをローカル変数として展開しようとします。しかし、それらはNixの変数ではなくbashの変数です。

最初、git_sync_and_branch bash関数の行の一つを次のように書こうとしました。

git checkout "${MAIN_BRANCH}"

するとNixは次のエラーを出しました。

error: undefined variable 'MAIN_BRANCH'

${の前に2つのシングルクォート('')を付けて${をエスケープする必要があります。次のように書きます。

git checkout "''${MAIN_BRANCH}"

Home Managerではだめなのか?

Nix設定をモジュール化する方法として、より一般的なのはHome Managerだと思います。

ただ、正直なところ、私はまだHome Managerの目的を理解していません。プロジェクトのREADMEには、次のように書かれています。

このプロジェクトは、Nixパッケージマネージャーと、Nixpkgsに含まれるNixライブラリを組み合わせて、ユーザー環境を管理するための基本的な仕組みを提供します。ユーザー固有の(グローバルではない)パッケージやdotfileを宣言的に設定できます。

これを読むと、Nixがすでに行っていることのように思えて、混乱します。

Home Managerを何度か覗いてみましたが、毎回、メリットがはっきりしないのに、新しいツールを学ぶためにかなりの時間を投資しなければならないように感じます。

いずれ、Home ManagerでNix設定を管理するメリットに気づくのかもしれません。ただ今のところは、標準のNixを使ってリモートのNixファイルを管理する、かなり分かりやすい方法を見つけています。

原文は Michael Lynch により に公開されました。

この記事は「gpt-5.6-terra」を使用して翻訳されました。