Nix User Repository に自作パッケージを追加する

みなさん Nix は使用していますか?

シェル環境を非破壊的にセットアップできて便利ですよね。

Nix はそれだけでなく、自作ソフトウェアのビルドツールとして利用でき、パッケージの作成と配布の手段としても利用できます。

Nix のパッケージリポジトリーとしてはみなさんご存じ Nixpkgs があります。

search.nixos.org

でも、使われるか分からない自作ソフトウェアを Nixpkgs に公開するのはちょっと気が引けるわけです。

そういうときのために Nix User Repository(NUR)があります。

nur.nix-community.org

どういうパッケージを NUR に登録するとよいかはドキュメントに次のように書いてあります。

  • Packages that are only interesting for a small audience
  • Pre-releases
  • Old versions of packages that are no longer in Nixpkgs, but needed for legacy reason (i.e. old versions of GCC/LLVM)
  • Automatic generated package sets (i.e. generate packages sets from PyPi or CPAN)
  • Software with opinionated patches
  • Experiments

ここまでは、「パッケージを公開したいけど Nixpkgs に公開するほどでもない」という側面から NUR の存在意義を見ました。

逆に次のように思う人もいるはずです。

pkgs.fetchzip や(Flakes なら)inputs で任意のパッケージを取ってこれるんだからわざわざ NUR に登録しなくてもいいのでは?」

確かにそれはそうで、その上で NUR に登録する利点としては NUR のウェブ UI で検索ができるのでユーザーが存在に気付きやすくなるなどです。

Package search for NUR

NUR を使うとき

一旦 NUR を使うときはどう使うのかを見ていきましょう。

次のコードは NUR のパッケージをシェルで使う場合の例で、NUR 公式ドキュメントからの引用です。

(1) の箇所で NUR を取得し、(2) の箇所でオーバーレイを追加し、(3) の箇所でパッケージ pkgs.nur.repos.mic92.hello-nur を参照しています。mic92リポジトリー名(通常はユーザー名と同じ)で hello-nur がパッケージ名です。

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    nur = {                                               # (1)
      url = "github:nix-community/NUR";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, flake-utils, nur }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          overlays = [ nur.overlay ];                     # (2)
        };
      in
      {
        devShells.default = pkgs.mkShell {
          packages = [ pkgs.nur.repos.mic92.hello-nur ];  # (3)
        };
      }
    );
}

パッケージのほかにも Nix ライブラリーや Nix OS モジュール・Nixpkgs オーバーレイが提供され(でき)ます。詳しくは公式ドキュメントを参照してください。

NUR のしくみ

NUR は NUR の Git リポジトリーで完結するわけではありません。NUR Git リポジトリーは個々人のリポジトリーへのリンクを repos.json に持ち定期的に個々人のリポジトリーをマージします。

{
    "repos": {
        ⋮
        "kakkun61": {
            "github-contact": "kakkun61",
            "url": "https://github.com/kakkun61/nur-packages"
        },
        ⋮
}

個々人のリポジトリーでは default.nixlib modules overlays それからパッケージのデリベーションを提供します。

{ pkgs ? import <nixpkgs> { } }:
{
  lib = import ./lib { inherit pkgs; }; # functions
  modules = import ./modules; # NixOS modules
  overlays = import ./overlays; # nixpkgs overlays

  wd = pkgs.callPackage ./pkgs/wd { };
}

自作パッケージの追加のしかた

まずは前述の「個々人のリポジトリー」を自分用に作成します。リポジトリーのテンプレートが用意されているのでそれを利用します。

github.com

そして自作ソフトウェアのパッケージを default.nix で提供できるようにします。そのとき、default.nixpkgs を引数に取ること・builtins.fetchTarball ではなく pkgs.fetchzip を使うこと・デリベーションは broken = true; に設定すること、などルールがあるのでドキュメントやテンプレートのコメントを読みましょう。 自分の場合は次のようなファイルを pkgs/wd/default.nix に用意しました。

{ pkgs, fetchzip }:

let
  version = "80e0814140c33201adf0e5213c426fb7bd5f47cf";
  project =
    fetchzip {
      url = "https://github.com/kakkun61/wd/tarball/${version}";
      sha256 = "sha256-usZTBF9pk0IsGdZb6IqEHBEaBX7mnXutykpm9TJHAWk=";
      extension = "tar.gz";
    };
in
(import "${project}/linux" { inherit pkgs; }).default.overrideAttrs {
  broken = true;
}

そしてルートの default.nix でこのファイルを読んでいます。

そしてこれらがちゃんと評価実行ができるか確認します。

ここまでできたら NUR に自分のリポジトリーを登録する PR を出します。PR タイトルのフォーマットが指定されているだけで特に概要に追記することなどはありませんでした。自分の PR はこれです。

github.com

PR がマージされれば NUR が取り込んでくれるのを待ちます。すぐに更新してほしい場合は HTTP エンドポイントがあるので通知します。テンプレートの GitHub Actions に実装されているのでしたい人はセットアップしましょう。

自分のページが NUR のウェブサイトに表示されましたか?おめでとうございます!