• 作成:

haskell.nixのnix flake checkでHaskellの警告をエラーとして扱う

背景

nix flake checkで全体のテストを行うようにしました。

問題

nix flake checkでのビルドはデフォルトでは警告をエラーとして扱わないため、そのままではHaskellの警告が出てもCIで失敗しない。

私の思想

私は全体的には警告はエラーとして扱わないべきだと考えています。

実行環境とかライブラリのパッチアップデートで出る警告は、実行不可能なエラーとは分けて考えるべきだからです。

例えばライブラリのアップデートでimportが不要になった場合などをエラーにするべきだとは思えません。

本来警告が出るだけで動くプログラムを、警告が出るだけで動かなくするのはナンセンスです。

しかしCIでは警告はエラーとして扱うべきです。開発者のupstreamでは警告はその場所で取り除くべきです。

なのでcabalファイル自体でGHCのオプションに-Werrorを追加するのは行いたくないです。

使えなかった方法

環境変数での切り替えはNix Flakesが純粋的に評価を行うので注入が困難です。

nix flake checkには--argオプションは無いようでした。

実装

nix flake checkでは警告時にエラーになれば良いので、 haskell.nixのmodulesに以下のように設定を追加しました。

# `nix flake check`レベルではcabalの警告をエラーとして扱います。
# ライブラリの問題ない範囲の不一致とか考えるとcabalの警告はエラーにしないべきですが、
# CIでは通したくないので警告も含めてエラーにします。
# CI専用に環境を分離するのも手ですが、
# 元々`nix flake check`では最適化を無効にするのが面倒なので時間がかかるため、
# あまり反復的に実行しません。
# 反復的に実行してテストの結果とかを確認するのには普通は`cabal test`のような言語固有のコマンドを使います。
# `cabal test`の方が実行するテストをフィルタリングとかも簡単に出来ますし。
# そのことを考えると本番を考えてエラーにしても構わないでしょう。
# flake参照された時にnixpkgsをfollowすると問題ない警告をエラーにしてしまうかもしれないのが懸念点ですが、
# 少なくとも現在は考慮する必要はないでしょう。
# 注意点として、`src`や`test`はビルドされますが、executableである`app`はビルドされません。
# 「appはエントリーポイントとしてのみ使う」習慣を守っていれば問題にはならないです。
(
  { lib, config, ... }:
  {
    # パッケージたちをハードコーディングすると変更忘れが発生するので`config.package-keys`で取得。
    options.packages = lib.genAttrs config.package-keys (
      _name:
      lib.mkOption {
        type = lib.types.submodule (
          { config, lib, ... }:
          # `cabal.project`に`source-repository-package`などで書かれていたりする、
          # 外部パッケージは変更したくないので、
          # `isProject`でフィルタリングしています。
          lib.mkIf config.package.isProject {
            # この書き方で上書きではなく追加として扱われる。
            ghcOptions = [ "-Werror" ];
          }
        );
      }
    );
  }
)

nixでcheckする分には依存関係とかも完全に管理されるので、ライブラリ由来の警告とかがエラーとして扱われても問題ないと思います。