Nix 源码获取与 fetchers:fetchFromGitHub、哈希与私有源

Nix 源码获取与 fetchers 实战:fetcher 的作用与固定哈希、fetchurl/fetchFromGitHub/fetchFromGitLab/fetchgit 选择、hash 格式与 SRI、私有仓库与凭据、补丁获取 fetchpatch、flake inputs 的 url 获取、版本与分支策略、常见坑与调试。

引言

derivation 的第一步永远是拿到源码。Nix 用 fetcher 把「下载」变成「可校验、可复现」的操作:每个 fetcher 都是固定哈希的 derivation,内容确定才能进缓存。本文讲清 fetcher 的选择(URL vs Git vs GitHub)、哈希格式与 SRI、私有仓库与凭据处理、flake inputs 的 url 写法,以及最常见的坑。

前置:/nix-package-management/(derivation 与 nixpkgs)、/nix-reproducible-hermetic-builds/(固定哈希)、/nix-flakes/(inputs 机制)。


目录


1. fetcher:把下载变成可校验的操作

1.1 为什么需要 fetcher

普通构建脚本里 curl https://... 是不可复现的——下载内容变了构建就变。fetcher 把下载包装成固定哈希 derivation:声明「我要内容=X 的东西」,下载后按哈希校验,不一致直接失败。这让「源码来源」变成可验证的事实。

1.2 fetcher 的通用形态

# 所有 fetcher 返回一个"内容确定"的 derivation
src = pkgs.fetchFromGitHub {
  owner = "NixOS";
  repo = "nix";
  rev = "2.20.0";
  hash = "sha256-...";   # 期望内容哈希
};

记忆:fetcher 把下载包装成固定哈希 derivation——声明期望哈希、下载后校验,让「源码来源」可验证、可缓存、可复现。


2. 按场景选 fetcher

场景fetcher说明
单个文件/归档fetchurl任意 URL 下载
GitHub 仓库快照fetchFromGitHub按 owner/repo/rev
GitLab 仓库fetchFromGitLab同 GitHub 形态
任意 Git 仓库fetchgit通用、含子模块
补丁fetchpatch获取统一 diff
flake 输入url 语法flake 专用获取

选型原则:GitHub 用 fetchFromGitHub、任意 Git 用 fetchgit、单文件用 fetchurl——专用 fetcher 更稳(自动处理归档格式)。

记忆:选 fetcher 看来源——GitHub 用 fetchFromGitHub、GitLab 用 fetchFromGitLab、任意 Git 用 fetchgit、单文件用 fetchurl、补丁用 fetchpatch;专用 fetcher 处理格式更稳。


3. fetchurl:下载单个文件

3.1 基础用法

src = pkgs.fetchurl {
  url = "https://example.com/files/tool-1.2.tar.gz";
  hash = "sha256-...";
};

3.2 传参给下载

src = pkgs.fetchurl {
  url = "https://example.com/api/1.2.zip";
  hash = "sha256-...";
  # 下载可选参数
  curlOpts = "-H 'Authorization: Bearer xxx'";   # 需要认证时(慎用,别硬编码)
};

3.3 多文件/多 URL

# 多个 URL 自动选可用(镜像)
srcs = map (url: pkgs.fetchurl { inherit url; hash = "sha256-..."; }) urls;

记忆:fetchurl 下载单个文件/归档,指定 url + hash;需要认证用 curlOpts(但别把密钥硬编码进 nix 表达式)。


4. fetchFromGitHub 与 fetchFromGitLab

4.1 fetchFromGitHub

src = pkgs.fetchFromGitHub {
  owner = "kubernetes";
  repo = "kubernetes";
  rev = "v1.30.0";
  hash = "sha256-...";
  # 可选:只取子目录/稀疏检出
  # sparseCheckout = [ "src" "pkg" ];
};

rev 用提交哈希或 tag。生产建议锁定具体提交哈希(比 tag 更防篡改)。

4.2 fetchFromGitLab

src = pkgs.fetchFromGitLab {
  domain = "gitlab.com";
  owner = "gitlab-org";
  repo = "gitlab";
  rev = "17.0.0";
  hash = "sha256-...";
};

记忆:fetchFromGitHub 按 owner/repo/rev 取仓库快照(rev 用提交哈希最稳)、fetchFromGitLab 同形态指定 domain;归档自动处理、内容哈希校验。


5. fetchgit:任意 Git 仓库与子模块

5.1 基础用法

src = pkgs.fetchgit {
  url = "https://example.com/myrepo.git";
  rev = "a1b2c3d4...";
  sha256 = "sha256-...";
};

5.2 子模块与分支

src = pkgs.fetchgit {
  url = "https://example.com/myrepo.git";
  rev = "a1b2c3d4...";
  sha256 = "sha256-...";
  fetchSubmodules = true;   # 拉取子模块(内容也进哈希)
  # 分支/标签:rev 支持分支名,但生产用哈希
};

5.3 什么时候用 fetchgit 而非 fetchFromGitHub

# 仓库不在 GitHub/GitLab(自建 git、Bitbucket 等)
# 需要子模块/深度控制
# 需要 clone 而非归档(build 依赖 .git 目录)
# 代价:比归档慢、哈希更易受环境扰动

记忆:fetchgit 通用拉任意 Git 仓库(含子模块 fetchSubmodules、.git 保留);非 GitHub/GitLab、需子模块或 clone 时用它,生产 rev 锁提交哈希。


6. 哈希格式与 SRI

6.1 两种格式

传统格式:sha256-<base64>(nix hash 输出的 base32 或 base64)
SRI 格式:sha256-<base64 of raw hash>("sha256-" + base64,自描述算法)

现代 nixpkgs 用 SRI 格式(前缀自带算法名,如 sha256-.../sha512-...)。flake 里补丁/源哈希也统一 SRI。

6.2 生成哈希

nix hash file ./mypkg.tar.gz        # 对文件算哈希
nix hash path ./mysource-dir        # 对目录算(fetchgit 用)
nix hash to-sri --type sha256 "base32..."   # 老格式转 SRI

6.3 哈希不匹配时的处理

# 构建报 "got: sha256-XXX",把 XXX 替换进表达式
# 但要警惕:改了 rev 却"复制 got 哈希"可能掩盖源变化
# 确认 got 的来源符合预期再替换

记忆:哈希用 SRI 格式(sha256- 自描述算法);nix hash file/path 生成、to-sri 转换;报错 got 值先确认来源再替换。


7. 私有仓库与凭据处理

7.1 私有的原则

# 私有源的最大原则:凭据不进 nix 表达式、不进 git
# 公开仓库永远别放 token;私有仓库也要最小化暴露

7.2 凭据注入方式

# 方式一:用 override 传下载参数(构建时提供)
src = pkgs.fetchgit {
  url = "https://user:${token}@git.example.com/repo.git";   # 别直接写 token!
  rev = "...";
  sha256 = "...";
};

正确的做法是把 token 放环境/机密管理,构建时注入,而不是写死在表达式里:

# 构建前从 secrets 提供
export NIX_CONFIG='extra-http-options = { headers = "Authorization: Bearer $(cat /run/secrets/token)"; }'

7.3 更安全的替代

# 优先考虑:把私有源先同步到一个内网镜像/缓存,公开获取
# 或用 sops-nix/agenix 托管凭据,构建时解密注入
# 无论如何:token 不进 git、不进公开配置

记忆:私有源凭据三原则——不进 nix 表达式、不进 git、最小暴露;用环境注入/NIX_CONFIG 头/agenix 托管,构建时解密,公开源优先走镜像。


8. fetchpatch:获取补丁

8.1 基础用法

patch = pkgs.fetchpatch {
  url = "https://github.com/upstream/repo/commit/abc123.patch";
  hash = "sha256-...";
  # 可选:从补丁中排除某些文件
  # excludes = [ "CHANGELOG.md" ];
};

8.2 与 overrideAttrs 配合

myPkg = pkgs.mypkg.overrideAttrs (old: {
  patches = (old.patches or []) ++ [
    (pkgs.fetchpatch { url = "..."; hash = "sha256-..."; })
  ];
});

8.3 补丁获取的注意

# fetchpatch 只接受统一 diff(git format-patch 输出)
# 上游把同一 commit 改了 hash → 构建失败,hash 需要更新
# 补丁内容进 derivation 哈希 → 补丁变化会影响整个包重建

记忆:fetchpatch 获取统一 diff 补丁,配 overrideAttrs.patches 打入;上游改 commit 会 hash 失败需更新——补丁变化会触发整包重建。


9. flake inputs 的 url 获取

9.1 在 flake 里获取源码

{
  inputs = {
    # 直接按 URL 声明输入
    myrepo = {
      url = "github:owner/repo";          # GitHub
      flake = false;                       # 当作普通源码而非 flake
    };
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    other = { url = "git+https://git.example.com/repo"; };  # 任意 git
  };
  outputs = { self, myrepo, ... }@inputs: {
    packages.${system}.default = pkgs.stdenv.mkDerivation {
      pname = "from-flake-input";
      version = "0.1";
      src = inputs.myrepo;    # flake=false 时 src 直接可用
    };
  };
}

9.2 lock 文件的角色

# flake.lock 锁定每个 input 的 rev + 哈希
# 更新 input:nix flake update <name> / nix flake lock
# 锁定保证:别人 clone 后拿到与开发者完全相同的源码

记忆:flake 里源码获取用 inputs url(github:/git+https:),flake=false 让输入成为普通源码直接当 src;flake.lock 锁定 rev+哈希,nix flake update 显式升级。


10. 速查表与一句话记忆

fetcher场景一句话
fetchurl单文件/归档任意 URL 下载
fetchFromGitHubGitHub 仓库owner/repo/rev
fetchFromGitLabGitLab 仓库同 GitHub 形态
fetchgit任意 Git含子模块/clone
fetchpatch补丁统一 diff
flake url inputflake 内源码github:/git+https:

一句话记忆:源码获取全看 fetcher——fetchurl 下载单文件/归档、fetchFromGitHub/fetchFromGitLab 按 owner/repo/rev 取平台仓库快照、fetchgit 拉任意 Git(含子模块)、fetchpatch 取补丁配 overrideAttrs.patches、flake 里用 inputs url(github:/git+https:,flake=false 当普通源码);所有 fetcher 都是固定哈希 derivation——SRI 格式哈希自描述算法、nix hash file/path 生成、报错 got 值先确认来源再替换;私有源凭据三原则「不进表达式、不进 git、最小暴露」,用环境注入/agenix 托管、公开源优先走镜像;flake.lock 锁定 rev+哈希保证他人与开发者拿到完全相同的源码——「下载可校验、来源可追溯」是 Nix 供应链的底气。


延伸阅读

  • /nix-package-management/ — derivation 与 nixpkgs
  • /nix-reproducible-hermetic-builds/ — 固定哈希与确定性
  • /nix-package-patching/ — overrideAttrs 与补丁
  • /nix-flakes/ — flake inputs 与 lock
  • /nix-debugging-error-handling/ — 哈希错误排查
  • [[devops]] — 供应链与依赖管理
  • Nix fetchers 手册
  • Nix 哈希生成指南

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「nix」更多文章

  1. Nix 构建调试与错误排查:常见错误、trace 与诊断手段
  2. NixOS 虚拟机与集成测试:nixosTest 框架与系统级验证
  3. 可复现与封闭构建:固定哈希、网络隔离与确定性