Aspire CLI 的 Nix 官方打包:从 flake 安装到固定输出二进制包全解析
2026/9/18 4:31:55 网站建设 项目流程

Aspire CLI 的 Nix 官方打包:从 flake 安装到固定输出二进制包全解析

【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspire

导读

本文基于 Aspire 仓库中第一方 Nix 打包目录 eng/nix/README.md 展开,完整讲解 Aspire CLI 的 Nix 分发方案:它如何以"固定输出二进制包"(fixed-output binary package)的形式消费官方 GitHub Release 归档,如何通过nix runnix profile add、项目 flake 与 overlay 四种方式被使用,以及版本清单versions.json在稳定版发布流水线中是如何被自动或手动更新的。读完本文,你将掌握在 NixOS 与 macOS(darwin)上安装 Aspire CLI 的全部姿势、底层 derivation 的构建细节,以及仓库侧维护 Nix 清单的完整发布契约。

一、设计定位:不编译源码,只搬运官方归档

eng/nix/目录存放的是 Aspire 官方维护的 Nix 打包(first-party packaging),其核心设计原则在 eng/nix/README.md 中说得非常明确:

  • 二进制包而非源码包:derivation 通过fetchurl抓取当前系统对应的官方版本化 GitHub Release 归档,按哈希固定(pinned by hash),解包完整归档内容,并在包输出中写入 Nix 安装路由的 sidecar 文件;
  • 不参与源码构建:它不会从源码构建 Aspire 仓库,也不依赖 .NET SDK、dotnet publish等构建链路。

这意味着 Nix 用户拿到的aspire与官方发布归档完全同源(同一份aspire-cli-<rid>-<version>.tar.gz),只是被 Nix 以可复现、可回滚、可卸载的方式纳管。

顶层 flake.nix 是这个打包的对外入口:它读取 eng/nix/versions.json 中的清单(manifest),为每个受支持系统通过pkgs.callPackage ./eng/nix/package.nix构造aspire-cli包,并同时暴露packagesappsoverlayschecksformatter五组输出。

二、四种使用方式:从零配置到项目集成

2.1 直接运行:nix run

最轻量的用法是直接运行 CLI,无需任何配置:

nix run github:microsoft/aspire#aspire-cli

对应 flake 中的apps输出——flake.nix为每个系统注册了apps.aspire-cli(program 指向${self.packages.${system}.aspire-cli}/bin/aspire),并将apps.default指向同一程序。因此nix run无需 clone 仓库,即可在当前 shell 中临时获得一个可用的aspire

2.2 永久安装:nix profile add

nix profile add github:microsoft/aspire#aspire-cli

这会把aspire-cli装入当前用户的 Nix profile,二进制进入 PATH,之后可以直接敲aspire命令使用,且与 Nix profile 的升级/回滚机制天然集成。

2.3 在项目 flake 中引用(推荐做法)

README 推荐在项目 flake 中让 Aspire 的nixpkgs输入跟随项目自己钉住的nixpkgs

{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/<your-pinned-branch-or-rev>"; aspire.url = "github:microsoft/aspire"; aspire.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { nixpkgs, aspire, ... }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; in { devShells.${system}.default = pkgs.mkShell { packages = [ aspire.packages.${system}.aspire-cli ]; }; }; }

这里有两个值得注意的版本控制点:

  • nixpkgs输入控制用于求值 derivation 的 Nix 包集合(如stdenvfetchurlmakeWrappericuopensslzlib等依赖版本);
  • Aspire CLI 版本aspire输入自身的 revision 以及 eng/nix/versions.json 中钉住的版本决定。

通过aspire.inputs.nixpkgs.follows = "nixpkgs",两个 flake 共享同一个nixpkgs,避免重复下载与潜在的依赖不一致。

2.4 通过 overlay 注入pkgs.aspire-cli

如果你更习惯 overlay 风格(例如在自己的 configuration.nix 或 flake 中统一管理 overlay),可以这样使用:

{ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/<your-pinned-branch-or-rev>"; aspire.url = "github:microsoft/aspire"; aspire.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { nixpkgs, aspire, ... }: let system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; overlays = [ aspire.overlays.default ]; }; in { devShells.${system}.default = pkgs.mkShell { packages = [ pkgs.aspire-cli ]; }; }; }

overlay 的实现位于 flake.nix 的overlays.default:它向final包集合注入aspire-cli = final.callPackage ./eng/nix/package.nix { inherit manifest; },因此注入后你就可以像使用任何 nixpkgs 包一样使用pkgs.aspire-cli

三、支持的系统与 RID 映射

Aspire CLI 的发布归档按运行时标识符(RID)命名,Nix 侧则按自己的系统名消费。两者的一一映射关系(来自 eng/nix/README.md 与 update-versions.sh 中的systems数组):

Nix systemAspire CLI runtime identifier
x86_64-linuxlinux-x64
aarch64-linuxlinux-arm64
x86_64-darwinosx-x64
aarch64-darwinosx-arm64

该清单固定维护在 update-versions.sh 中(顺序即versions.json的写入顺序,保证重复运行产生最小 diff),并由versions.json中的systems字段驱动flake.nixsupportedSystems——即只有清单里存在的系统才会被生成packages/apps/checks输出。

对于不支持的平台,package.nix 会在求值期直接抛错:

Aspire CLI does not publish a Nix package for system '<system>'.

(见entry = manifest.systems.${system} or (throw ...)的逻辑。)

四、derivation 深度拆解:固定输出二进制包如何组装

package.nix 是整个打包的核心,值得逐段拆解。

4.1 输入与参数

{ lib, stdenv, fetchurl, makeWrapper, autoPatchelfHook ? null, icu, openssl, zlib, manifest ? builtins.fromJSON (builtins.readFile ./versions.json), }:
  • manifest默认直接读取同目录的versions.json,也可由 flake/overlay 显式传入(flake.nix就是通过callPackage显式inherit manifest传入的,保证 flake 与包定义共享同一份数据);
  • Linux 侧需要icuopensslzlib以及stdenv.cc.cc.lib,用于给原生二进制提供动态库依赖;autoPatchelfHook用于自动修补 ELF 的 RPATH。

4.2 系统分发与依赖装配

system = stdenv.hostPlatform.system; entry = manifest.systems.${system} or (throw "Aspire CLI does not publish a Nix package for system '${system}'."); isLinux = stdenv.hostPlatform.isLinux; linuxLibraries = [ icu openssl zlib stdenv.cc.cc.lib ]; linuxLibraryPath = lib.makeLibraryPath linuxLibraries;
  • 从清单中取出当前系统的entry(含ridarchiveNameurlhash);
  • 仅在 Linux 上引入autoPatchelfHook与动态库依赖(darwin 平台依赖系统自带库,不需要这些输入)。

4.3 固定输出的源码抓取

src = fetchurl { inherit (entry) url hash; };

这是"固定输出"(fixed-output)的关键:url指向版本化的releases/download/v<version>/aspire-cli-<rid>-<version>.tar.gzhash为对应的 Nix SRI 哈希(sha512-...形式)。Nix 会校验下载内容与哈希完全一致,一旦上游变更即失效,从而保证可复现性。

同时注意:url 字段永远是公开的版本化 release 下载地址(见 README 与脚本注释),而不是可变的aka.ms渠道重定向——这保证了固定输出 fetch 的确定性。

4.4 构建阶段:纯"搬运 + 包装"

sourceRoot = "."; nativeBuildInputs = [ makeWrapper ] ++ lib.optionals isLinux [ autoPatchelfHook ]; buildInputs = lib.optionals isLinux linuxLibraries; dontConfigure = true; dontBuild = true; dontStrip = true; installPhase = '' runHook preInstall mkdir -p "$out/lib/aspire-cli" "$out/bin" cp -R . "$out/lib/aspire-cli/" chmod 755 "$out/lib/aspire-cli/aspire" printf '%s\n' '{"source":"nix"}' > "$out/lib/aspire-cli/.aspire-install.json" makeWrapper "$out/lib/aspire-cli/aspire" "$out/bin/aspire" ${lib.optionalString isLinux ''--prefix LD_LIBRARY_PATH : "${linuxLibraryPath}"''} runHook postInstall '';

这一步揭示了包内的最终布局:

  1. 完整归档原样保留:Release 归档已经包含完整的 CLI 包(自包含的 .NET 原生二进制),因此dontConfigure/dontBuild/dontStrip全部置真,只做解包搬运;
  2. 二进制与 sidecar 同目录存放:真实二进制放在$out/lib/aspire-cli/aspire,旁边写入.aspire-install.json,内容为{"source":"nix"}
  3. 对外包装$out/bin/aspire是由makeWrapper生成的用户入口,在 Linux 上通过--prefix LD_LIBRARY_PATH注入icuopensslzlibglibc的运行库路径(autoPatchelfHook负责把 ELF 的依赖补到可解析状态)。

4.5 sidecar:Nix 在 Aspire 安装路由体系中的身份

.aspire-install.json正是 Aspire CLI 的"安装路由 sidecar"(install-route sidecar)。在源码侧,InstallSource.cs 定义了完整的安装来源枚举,其中Nix对应的线格式(wire string)正是"nix"

/// <summary>Nix package or flake install.</summary> Nix, // ... internal const string NixWire = "nix";

CLI 在运行时通过读取二进制旁的.aspire-install.jsonsource字段判断自己是从哪种途径安装的(script / pr / winget / brew / dotnet-tool / localhive / nix)。这一身份在 docs/specs/install-routes.md 中有详细契约说明,并直接影响 CLI 的自更新行为与数据目录选择:

  • Nix 安装被归类为只读包管理器路由:包输出位于只读的 Nix store 中,CLI 不应尝试写回二进制所在目录;
  • 因此 Nix 路由回落到默认 Aspire home(设置了ASPIRE_HOME时用该环境变量,否则为$HOME/.aspire)作为数据目录,见 install-routes.md 中对nix的说明;
  • BundleService.ComputeDefaultExtractDir是布局选择的唯一事实来源,布局是 sidecarsource值的纯函数;未知source与已知只读路由(如nix)都回落到默认 Aspire home,避免向二进制旁写入。

4.6 包元数据

passthru = { inherit manifest; inherit (entry) archiveName rid; }; meta = { description = "Command line tool for Aspire developers"; homepage = "https://aspire.dev"; changelog = "https://github.com/microsoft/aspire/releases/tag/${manifest.releaseTag}"; license = lib.licenses.mit; sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; platforms = builtins.attrNames manifest.systems; mainProgram = "aspire"; };

passthru暴露manifestarchiveNamerid供外部消费;meta中值得注意的两点:sourceProvenance明确声明这是binaryNativeCode(二进制原生代码包,而非源码构建产物),mainProgram = "aspire"nix run等工具能自动找到可执行入口。

五、版本清单versions.json:固定哈希的数据源

versions.json 是 flake 的"真相文件",当前示例(仓库现状)为版本13.4.6、release tagv13.4.6,包含四个系统的条目。每个条目形如:

"x86_64-linux": { "rid": "linux-x64", "archiveName": "aspire-cli-linux-x64-13.4.6.tar.gz", "url": "https://github.com/microsoft/aspire/releases/download/v13.4.6/aspire-cli-linux-x64-13.4.6.tar.gz", "hash": "sha512-kYL/Wsf5TjmZRkAScf0dyxAfwp4MiZm2COlH9wI1TTCwnbTes6dunDqx3OBMc2eEe4or50PFjnqjpD5CIw8IRQ==" }
  • version/releaseTag:稳定版号与其对应的v前缀 tag;
  • rid:归档的运行时标识符;
  • archiveName:归档文件名,遵循aspire-cli-<rid>-<version>.tar.gz约定;
  • url:不可变的版本化下载地址(GitHub immutable release);
  • hash:Nix SRI 格式(sha512-<base64>)的固定输出哈希。

由于flake.nix通过builtins.fromJSON (builtins.readFile ./eng/nix/versions.json)读取清单,修改这个文件即修改 flake 的分发内容——这也是下文版本更新机制的核心对象。

六、版本更新机制:自动流水线与手动脚本双通道

6.1 自动通道:随稳定版发布联动

README 描述的更新链路为(细节可交叉印证 docs/release-process.md 与 docs/ci/native-cli-packaging.md):

  1. 稳定版发布时,release-publish-nugetAzure DevOps 流水线派发.github/workflows/update-nix-cli-flake.yml
  2. 流水线从签名源码构建的BlobArtifacts中读取aspire-cli-*.tar.gz.sha512校验和(也就是它校验并上传到 release 的同一批构件),作为输入传给 workflow;
  3. workflow 据此构建清单——清单基于构建产物而非 GitHub Release,这样在 release 仍是未发布的draft时就能生成清单(GitHub 不可变 release 的约束:draft 资产不会通过公开的releases/download/...URL 提供);
  4. workflow 将 Nix 清单变更提交到release-github-tasks.yml创建的update-baseline-<version>分支,并创建/更新基线 PR,使发布后的稳定版更新聚合在一起。

合并基线 PR 就是仓库内 Nix 的 "ship" 步骤:它把main上的 flake 元数据更新为指向 GitHub release 资产与哈希(url字段始终是公开的版本化下载 URL,发布管理员把 draft 转为正式发布后即生效)。本仓库没有单独的 Nix registry 发布步骤

6.2 手动通道:针对已发布版本

已经发布的稳定版,可直接运行:

eng/nix/update-versions.sh --version <stable-release-version>

脚本默认行为是下载每个官方.sha512校验和资产(公开下载 URL,curl),换算成 Nix SRI 哈希后重写versions.json

6.3 离线模式:release 流水线的调用契约

当发布流水线运行时,它改用--sha512 <rid>=<hex>参数直接传入四个平台的十六进制摘要,完全不读 release

eng/nix/update-versions.sh --version 13.4.6 \ --sha512 osx-arm64=<hex> \ --sha512 osx-x64=<hex> \ --sha512 linux-arm64=<hex> \ --sha512 linux-x64=<hex>

脚本头注释明确说明了这一离线契约。脚本实现中有几个值得注意的工程细节:

  • 离线模式强约束:只要传入任意一个--sha512,就进入离线模式,且四个平台必须全部提供sha512_override_for逐 RID 查找,缺失即报错),不允许混用"部分传入 + 部分 curl",保证结果可复现;
  • 版本校验:只接受稳定版x.y.z(去掉前导v/V后匹配^[0-9]+\.[0-9]+\.[0-9]+$),prerelease/daily 渠道有意不写入——因为 Nix 固定输出 fetch 必须保证消费者钉住仓库后仍可复现;
  • 校验和解析.sha512资产可能是纯 hex 或hex + 文件名两列格式,脚本只取第一 token(read -r checksum _);
  • hex → SRI 转换hex_sha512_to_sri先规范化大小写与空白(兼容 Windows/Unix 换行),校验必须是 128 位 hex,再用xxd -r -p | base64输出sha512-<base64>格式;
  • 原子写入:先写versions.json.tmp兄弟文件,四个平台全部解析、哈希成功后才mv覆盖正式清单,curl/哈希失败不会留下半成品;
  • bash 3.2 兼容sha512_override_for特意不用关联数组(sha512_override_for注释说明),保证在 macOS 自带的旧 bash 上本地测试也能运行。

无论自动还是手动,清单始终使用版本化 GitHub release URL而非可变的aka.ms渠道重定向,确保 Nix 固定输出 fetch 的可复现性。

七、与上游 nixpkgs 的关系

README 明确说明:本 flake 是仓库内的第一方包,而上游NixOS/nixpkgs中的 derivation 可以复用相同的 release 资产 URL 形态、RID 映射与nix安装路由 sidecar 行为,从而在 nixpkgs 中提供pkgs.aspire-cli体验。也就是说:

  • 本仓库的打包定义了"标准形态"(归档命名、哈希、sidecar、布局);
  • 上游 nixpkgs 包与该形态保持一致即可让用户通过普通 nixpkgs 渠道安装;
  • 二者面向同一份官方 release 资产,来源一致、行为一致。

八、实践小结与适用前提

场景推荐方式命令/配置
临时体验nix runnix run github:microsoft/aspire#aspire-cli
本机常驻nix profile addnix profile add github:microsoft/aspire#aspire-cli
项目 devShell 集成跟随式 flake input见 2.3 节示例
统一 overlay 管理aspire.overlays.default见 2.4 节示例
手动升级(已发布版本)update-versions.sheng/nix/update-versions.sh --version <稳定版>
发布流水线离线升级--sha512离线模式见 6.3 节

适用前提与限制

  • 仅支持四个平台(见第三节映射表),其余系统在求值期直接抛错;
  • 只跟踪稳定版x.y.z,preview/daily 渠道不会进入versions.json
  • 安装后属于只读包管理器路由:CLI 数据落到默认 Aspire home(ASPIRE_HOME$HOME/.aspire),不会修改 store 内的二进制;
  • 该打包消费官方 release 二进制归档,不在 Nix 侧从源码构建 Aspire 仓库。

延伸阅读:安装路由 sidecar 的完整契约见 docs/specs/install-routes.md;Nix 打包在发布流程中的位置见 docs/release-process.md;原生 CLI 打包与签名相关细节见 docs/ci/native-cli-packaging.md;sidecar 的读写与原子更新实现见 src/Aspire.Cli/Acquisition/InstallSidecarWriter.cs 与 src/Aspire.Cli/Acquisition/InstallSidecarReader.cs。

【免费下载链接】aspireAspire is the tool for code-first, extensible, observable dev and deploy.项目地址: https://gitcode.com/GitHub_Trending/as/aspire

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询