如何在不抛出“无法将集合强制转换为字符串”的情况下覆盖 Nix 导数?

How can I override a Nix derivative without throwing `cannot coerce a set to a string`?

或者,目标:如何以声明的方式从 Nix unstable 获取单个包?

我是 NixOS 的新手,目前正在尝试安装比我的 NixOS 版本(最新稳定版)的默认 0.5.2 更新的 Consul 版本。我正在尝试通过覆盖 /etc/nix/configuration.nix 中的导数来实现这一点。

我想保持 运行ning 稳定,但我发现 unstable 已经有了我想要的 Consul 版本 (0.7.0),所以我决定使用这个包的属性作为覆盖 https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/consul/default.nix

的起点

我将它的大部分复制到我的 configuration.nix 中,这里是相关部分:

nixpkgs.config.packageOverrides = pkgs: rec {
  consul = pkgs.lib.overrideDerivation pkgs.consul (attrs: rec {
    version = "0.7.0";
    name = "consul-${version}";
    rev = "v${version}";

    goPackagePath = "github.com/hashicorp/consul";

    src = pkgs.fetchFromGitHub {
      owner = "hashicorp";
      repo = "consul";
      inherit rev;
      sha256 = "04h5y5vixjh9np9lsrk02ypbqwcq855h7l1jlnl1vmfq3sfqjds7";
    };

    # Keep consul.ui for backward compatability
    passthru.ui = pkgs.consul-ui;
  });
};

environment.systemPackages = with pkgs; [
  vim
  which
  telnet
  consul-ui
  consul-alerts
  consul-template
  consul
];

我 运行宁 nix-build (Nix) 1.11.2 抛出:

$ nixos-rebuild switch
building Nix...
building the system configuration...
error: cannot coerce a set to a string, at /etc/nixos/configuration.nix:19:7
(use ‘--show-trace’ to show detailed location information)

当我查看第 19 行时,name 设置为 "consul-${version}"

为什么这里要进行类型转换?任何提示将不胜感激!

我也想知道是否有更好的方法 运行 只是一个不稳定的包,但是从 configuration.nix 声明地这样做,而不是命令式地这样做?

欢迎来到Nix/NixOS :)

每当您需要了解有关错误的更多信息时,您可以使用 --show-trace,这会给您带来更详细的错误。在您的情况下,您会看到类似

的内容
error: while evaluating the attribute ‘passthru’ of the derivation ‘consul-0.7.0’ at /home/rok/tmp/consul.nix:6:3:
cannot coerce a set to a string, at /home/rok/tmp/consul.nix:6:3

这应该指出错误实际上发生在通路线路上。如果您将其注释掉,它可能会生成。我假设这里有一些递归调用,当它试图评估 consul/consul-ui 包时发生错误。

至于仅覆盖来自不稳定渠道的一个包,需要这样的东西

let
  unstable_pkgs = import ./path/to/unstabe/nixpkgs {};
  # or
  # unstable_pkgs = import (pkgs.fetchFromGitHub {...}) {};
in {
  ...
  nixpkgs.config.packageOverrides = pkgs: rec {
    consul = unstable_pkgs.consul;
  };
  ...
}

我还没有尝试过上面的方法,但我假设它会起作用。

补充一下 Rok 所说的:

Which should point you that an error actually happens at passthru, line. If you comment it out it will probably build. I'm assuming some recursive calls are at play here and error occurs when it tries to evaluate consul/consul-ui packages.

如果您刚刚开始,您可以安全地忽略下面的内容,也许会回来阅读它if/when您对细节感到好奇。

这里的问题是 overrideDerivation 是一种覆盖事物的低级方法。在 stdenv.mkDerivation 后面,我们有一个小得多的原始函数,叫做 derivationderivation 函数采用一些属性,并且(或多或少 - 请参阅文档了解更详细的信息)只是在构建期间将这些属性作为环境变量传递。另一方面,stdenv.mkDerivation 函数在顶部有一大堆智能,在将它们传递给 derivation 之前按摩赋予它的属性——在某些情况下,就像passthru,它根本不会将属性传递给 derivation

回到 overrideDerivation:它接受 stdenv.mkDerivation 将传递给 derivation 的最终调整属性,并且在此之前它允许您使用函数覆盖这些属性你给它(例如,这意味着,在那个时候,passthru 已经被删除)。当你的函数添加一个 passthru 时,它进入 derivation,然后它想要将 passthru 的值强制转换为一个字符串,这样它就可以使 passthru 成为一个环境变量在构建过程中;然而,因为 passthru 现在指向一个属性集,并且不支持这种强制转换,Nix 然后抱怨。

所以这让我们陷入了一个奇怪的境地。为了说明,我将在此处复制 consul 包的源代码:

{ stdenv, lib, buildGoPackage, consul-ui, fetchFromGitHub }:

buildGoPackage rec {
  name = "consul-${version}";
  version = "0.6.4";
  rev = "v${version}";

  goPackagePath = "github.com/hashicorp/consul";

  src = fetchFromGitHub {
    owner = "hashicorp";
    repo = "consul";
    inherit rev;
    sha256 = "0p6m2rl0d30w418n4fzc4vymqs3vzfa468czmy4znkjmxdl5vp5a";
  };

  # Keep consul.ui for backward compatability
  passthru.ui = consul-ui;
}

(注意 buildGoPackagestdenv.mkDerivation 的包装。)

您可能熟悉例如consul.override,它允许您提供不同的输入(例如 consul-uibuildGoPackage 的不同版本),但它不允许您覆盖 的内容不是 输入(例如 srcpassthru 等)。同时,overrideDerivation 允许您修改给 derivation 的属性,但 不能 stdenv.mkDerivation 的属性。理想情况下会有一些介于两者之间的东西,这将允许操纵给予 stdenv.mkDerivation 的属性,并且碰巧有一个开放的 PR 来解决这个问题:

https://github.com/NixOS/nixpkgs/pull/18660