Import from a URL in Nix

Michael Lynch

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

原文は Michael Lynch により に公開されました。 このブログを購読する

私はまだ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を参照する形にしたいと思います。

リモートURLから私のshell.nixファイルを取り込むために、configuration.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関数を定義し、その関数に対して短いエイリアスを作成しています。

一例が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ライブラリを併用して、ユーザー環境を管理するための基本的なシステムを提供します。ユーザー固有の(グローバルではない)パッケージやdotfilesを宣言的に設定できます。

これは私には混乱を招きます。なぜなら、Nixがすでにやっていることのように聞こえるからです。

Home Managerを何度か覗いてみましたが、そのたびに、この新しいツールを学ぶために多くの労力をかける必要があるのに、得られるメリットが自分にははっきりしないと感じます。

いつかHome ManagerでNix設定を管理する利点に気づくときが来るかもしれませんが、今のところ、標準のNixでリモートのNixファイルを管理するかなりシンプルな方法を見つけました。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント