Nix Flake モジュールのオプションからドキュメントを自動生成する

Cspell というものがあります。ソースコード中の単語のスペルをチェックしてくれるツールです。うっかりタイポして面倒なエラーになるのは嫌なので自分がソースコードを書くときはチェックをしています。

cspell.org

それを nix flake check コマンドの中でやってしまおうというのが cspell-nix です。11 月ごろに作りました。

github.com

cspell-nix は Flake Parts のモジュールとして提供しています。

flake.parts

引数などはモジュール定義を見れば書いてあるのですが、わざわざそのためにソースコードを見るのは不要な部分も多いですし Commonmark などで書いてレンダーされた結果を見れるようにしたいです。しかし手でドキュメントを書いていくのはソースコードとの乖離などに注意する必要があります。そこでドキュメントを自動生成できるようにしました。

pkgs.nixosOptionsDoc

pkgs から nixosOptionsDoc という関数が定義されていて、これでドキュメントの生成ができます。できるのですが、なかなかつまづきました。

まず、nixosOptionsDoc にコメントが書かれていないためソースコードを見て使い方を推定する必要があります。ソースコードを辿ると nixpkgs/nixos/lib/make-options-doc/default.nix にコメントが書かれていてこれがヒントになります。

  Generates documentation for [nix modules](https://nix.dev/tutorials/module-system/index.html).

  It uses the declared `options` to generate documentation in various formats.

  # Outputs

  This function returns an attribute set with the following entries.

  ## optionsCommonMark

  Documentation in CommonMark text format.

  ## optionsJSON

  All options in a JSON format suitable for further automated processing.

  `example.json`
  ```json
  {
    ...
    "fileSystems.<name>.options": {
      "declarations": ["nixos/modules/tasks/filesystems.nix"],
      "default": {
        "_type": "literalExpression",
        "text": "[\n  \"defaults\"\n]"
      },
      "description": "Options used to mount the file system.",
      "example": {
        "_type": "literalExpression",
        "text": "[\n  \"data=journal\"\n]"
      },
      "loc": ["fileSystems", "<name>", "options"],
      "readOnly": false,
      "type": "non-empty (list of string (with check: non-empty))"
      "relatedPackages": "- [`pkgs.tmux`](\n    https://search.nixos.org/packages?show=tmux&sort=relevance&query=tmux\n  )\n",
    },
    ...
  }
  ```

  ## optionsAsciiDoc

  Documentation rendered as AsciiDoc. This is useful for e.g. man pages.

  > Note: NixOS itself uses this output to to build the configuration.nix man page"

  ## optionsNix

  All options as a Nix attribute set value, with the same schema as `optionsJSON`.

  # Example

  ## Example: NixOS configuration

  ```nix
  let
    # Evaluate a NixOS configuration
    eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
      modules = [
        ./module.nix
      ];
    };
  in
    pkgs.nixosOptionsDoc {
      inherit (eval) options;
    }
  ```

  ## Example: non-NixOS modules

  `nixosOptionsDoc` can also be used to build documentation for non-NixOS modules.

  ```nix
  let
    eval = lib.evalModules {
      modules = [
        ./module.nix
      ];
    };
  in
    pkgs.nixosOptionsDoc {
      inherit (eval) options;
    }
  ```

次に Flake モジュールを評価する必要があるのですが、flake-parts.lib.evalFlakeModule では評価後の perSystem のモジュールの options を評価するとよく分からないエラーになりました1。また、flake-parts.lib.evalFlakeModule を使用すると Flake Parts が提供するオプションまでドキュメントになってしまいます。これは要りません。なので自分のモジュールだけ単品で lib.evalModules で評価することにしました。しかし、これだと flake.apps などを参照する config の方がエラーになるので options だけのモジュールに分割して lib.evalModules に渡す必要がありました。

この辺りを乗り越えるとモジュールのオプションを Commonmark や Asciidoc に変換できるようになります。

出力したドキュメントはこんな感じになりました。

おしまい


  1. モジュール引数に pkgs がないというエラーなのですが、そんなことはないはず……