mcpField
N
nix-best-practices

nix-best-practices

github.com/alleneubank开发50548 次安装18 周前更新
代码仓库

关于 nix-best-practices

Nix Best Practices 是关于配置 Nix flake、overlay、开发环境和非自由软件包的参考技能,面向使用 Nix 项目的开发者,提供命令示例与故障排查指引。

主要功能

  1. 01组织 flake 并声明输入
  2. 02使用 follows 共享 nixpkgs 输入
  3. 03应用软件包 overlay
  4. 04配置非自由软件包
  5. 05设置开发环境与 direnv

适用场景

  1. 01为项目搭建 Nix flake
  2. 02避免重复的 nixpkgs 输入
  3. 03在开发环境中提供原生依赖
  4. 04排查常见 flake 和软件包错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/alleneubank/claude-code --skill nix-best-practices

安装后在 Claude Code 中输入/nix-best-practices确认已加载。

详细介绍

Nix 最佳实践

Flake 结构

标准的 flake.nix 结构:

{
  description = "Project description";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
        };
      in {
        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [
            # packages here
          ];
        };
      });
}

Follows 模式(避免重复下载 Nixpkgs)

添加 overlay 输入时,使用 follows 共享父级 nixpkgs,避免下载多个版本:

inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

  # Overlay follows parent nixpkgs
  some-overlay.url = "github:owner/some-overlay";
  some-overlay.inputs.nixpkgs.follows = "nixpkgs";

  # Chain follows through intermediate inputs
  another-overlay.url = "github:owner/another-overlay";
  another-overlay.inputs.nixpkgs.follows = "some-overlay";
};

即使没有直接使用,也必须在 outputs 函数中列出所有输入:

outputs = { self, nixpkgs, some-overlay, another-overlay, ... }:

应用 Overlays

Overlays 用于修改或向 nixpkgs 添加软件包:

let
  pkgs = import nixpkgs {
    inherit system;
    overlays = [
      overlay1.overlays.default
      overlay2.overlays.default
      # Inline overlay
      (final: prev: {
        myPackage = prev.myPackage.override { ... };
      })
    ];
  };
in

处理非自由软件包

选项 1:nixpkgs-unfree(团队推荐)

使用 numtide/nixpkgs-unfree 来处理受 EULA 许可的软件包,无需用户配置:

inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  nixpkgs-unfree.url = "github:numtide/nixpkgs-unfree/nixos-unstable";
  nixpkgs-unfree.inputs.nixpkgs.follows = "nixpkgs";

  # Unfree overlay follows nixpkgs-unfree
  proprietary-tool.url = "github:owner/proprietary-tool-overlay";
  proprietary-tool.inputs.nixpkgs.follows = "nixpkgs-unfree";
};

此链路为:proprietary-tool → nixpkgs-unfree → nixpkgs

选项 2:用户配置

用户在 ~/.config/nixpkgs/config.nix 中添加:

{ allowUnfree = true; }
选项 3:指定软件包(Flake)
let
  pkgs = import nixpkgs {
    inherit system;
    config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
      "specific-package"
    ];
  };
in

注意:flake.nix 中的 config.allowUnfree 对 nix develop 无效——请使用 nixpkgs-unfree 或用户配置。

创建二进制 Overlay 仓库

当 nixpkgs 构建的社区版本缺少某些功能时(对于开放核心工具很常见),创建一个获取官方二进制文件的 overlay。

模式(参见 0xBigBoss/atlas-overlay、0xBigBoss/bun-overlay)
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};

        version = "1.0.0";

        # Platform-specific binaries
        sources = {
          "x86_64-linux" = {
            url = "https://example.com/tool-linux-amd64-v${version}";
            sha256 = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
          };
          "aarch64-linux" = {
            url = "https://example.com/tool-linux-arm64-v${version}";
            sha256 = "sha256-BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=";
          };
          "x86_64-darwin" = {
            url = "https://example.com/tool-darwin-amd64-v${version}";
            sha256 = "sha256-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=";
          };
          "aarch64-darwin" = {
            url = "https://example.com/tool-darwin-arm64-v${version}";
            sha256 = "sha256-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=";
          };
        };

        source = sources.${system} or (throw "Unsupported system: ${system}");

        toolPackage = pkgs.stdenv.mkDerivation {
          pname = "tool";
          inherit version;

          src = pkgs.fetchurl {
            inherit (source) url sha256;
          };

          sourceRoot = ".";
          dontUnpack = true;

          installPhase = ''
            mkdir -p $out/bin
            cp $src $out/bin/tool
            chmod +x $out/bin/tool
          '';

          meta = with pkgs.lib; {
            description = "Tool description";
            homepage = "https://example.com";
            license = licenses.unfree;  # or appropriate license
            platforms = builtins.attrNames sources;
          };
        };
      in {
        packages.default = toolPackage;
        packages.tool = toolPackage;

        overlays.default = final: prev: {
          tool = toolPackage;
        };
      })
    // {
      overlays.default = final: prev: {
        tool = self.packages.${prev.system}.tool;
      };
    };
}
获取 SHA256 哈希值
nix-prefetch-url https://example.com/tool-linux-amd64-v1.0.0
# Returns hash in base32, convert to SRI format:
nix hash to-sri --type sha256 <base32-hash>

也可以直接使用 SRI:

nix-prefetch-url --type sha256 https://example.com/tool-linux-amd64-v1.0.0

开发 Shell 模式

基础 Shell
devShells.default = pkgs.mkShell {
  buildInputs = with pkgs; [
    nodejs
    python3
  ];

  shellHook = ''
    echo "Dev environment ready"
  '';
};
使用环境变量
devShells.default = pkgs.mkShell {
  buildInputs = with pkgs; [ postgresql ];

  # Set at shell entry
  DATABASE_URL = "postgres://localhost/dev";

  # Or in shellHook for dynamic values
  shellHook = ''
    export PROJECT_ROOT="$(pwd)"
  '';
};
原生依赖项(C 库)
devShells.default = pkgs.mkShell {
  buildInputs = with pkgs; [
    openssl
    postgresql
  ];

  # Expose headers and libraries
  shellHook = ''
    export C_INCLUDE_PATH="${pkgs.openssl.dev}/include:$C_INCLUDE_PATH"
    export LIBRARY_PATH="${pkgs.openssl.out}/lib:$LIBRARY_PATH"
    export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PATH"
  '';
};

Direnv 集成

Flake 项目的 .envrc:

use flake

对于不使用 nixpkgs-unfree 的非自由软件包:

export NIXPKGS_ALLOW_UNFREE=1
use flake --impure

常用命令

# Update all inputs
nix flake update

# Update specific input
nix flake update some-input

# Check flake validity
nix flake check

# Show flake metadata
nix flake metadata

# Enter dev shell
nix develop

# Run command in dev shell
nix develop -c <command>

# Build package
nix build .#packageName

# Run package
nix run .#packageName

故障排除

“unexpected argument” 错误

必须在 outputs 函数中列出所有输入:

# Wrong
outputs = { self, nixpkgs }: ...

# Right (if you have more inputs)
outputs = { self, nixpkgs, other-input, ... }: ...
使用 nix develop 时出现非自由软件包错误

flake.nix 中的 config.allowUnfree 不会传递到 nix develop。请使用:

  1. nixpkgs-unfree 输入(推荐)
  2. 用户的 ~/.config/nixpkgs/config.nix
  3. NIXPKGS_ALLOW_UNFREE=1 nix develop --impure
重复下载 Nixpkgs

使用 follows 将输入链到同一个 nixpkgs 源。

Overlay 未应用

导入 nixpkgs 时,确保 overlay 位于 overlays 列表中:

pkgs = import nixpkgs {
  inherit system;
  overlays = [ my-overlay.overlays.default ];
};
哈希不匹配

使用 nix-prefetch-url 重新获取并更新哈希。当上游在同一 URL 更新二进制文件时,哈希会发生变化。

评论

支持 Markdown