ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

devenv 中的 scripts 模块:用 Nix 声明式管理开发环境脚本的完整指南

devenv 中的 scripts 模块:用 Nix 声明式管理开发环境脚本的完整指南 开发工具CLI【免费下载链接】devenvFast, Declarative, Reproducible, and Composable Developer Environments using Nix项目地址https://gitcode.com/gh_mirrors/de/devenv点击查看免费下载导读在 devenv 项目中scripts模块提供了在devenv.nix中以声明式方式定义、分发和管理开发环境脚本的官方方案解决脚本定义在哪里、工具链如何保证对所有开发者一致可用这一经典问题。通过本文你将掌握exec、package、binary、packages、description五个核心属性的完整用法学会脚本内锁定包路径、按需注入运行期依赖、用任意语言编写脚本以及借助enterTest对脚本做自动化验证最终构建一套可复制、可测试、全团队一致的脚本体系。为什么需要 scripts 模块大多数项目都会散落大量 shell 脚本随之而来的问题非常现实脚本应该定义在哪里如何保证脚本依赖的工具curl、jq、python等对每个开发者都可用、且版本一致devenv 的scripts模块给出的答案是把脚本写进devenv.nix让脚本随环境一起构建。只要进入环境脚本就自动出现在$PATH中脚本所需的工具也由 Nix 锁定不再依赖开发者本机是否安装了对应软件。一个最简单的例子定义一个silly-example脚本抓取 HTTP 接口并用jq解析{ pkgs, ... }: { packages [ pkgs.curl pkgs.jq ]; # 见 [Packages](https://link.gitcode.com/i/16fede301c65a4ab07d7d0ba09e99d22) 章节 scripts.silly-example.exec curl https://httpbin.org/get?$1 | jq .args ; }由于脚本在进入环境时即被暴露其可执行文件会自动加入packages见 scripts.nix 中的config.packages lib.mapAttrsToList (_: script: script.scriptPackage) config.scripts因此可以直接依赖packages中声明的可执行程序$ devenv shell Building shell ... Entering shell ... (devenv) $ silly-example foo1 { foo: 1 }这里$1会被 shell 展开为foo1curl将其作为查询参数发送给httpbin.orgjq提取出args字段输出。整个流程中curl和jq的可用性由 Nix 环境保证而非依赖开发者本机。别名与参数转发脚本本质上是环境中的一个可执行文件因此天然支持别名和参数转发。下面这个例子展示了如何把参数原样转发给npxscripts.foo.exec npx foo/cli $; ;$会把调用脚本时传入的所有参数透传给npx foo/cli从而让foo成为某个 CLI 的便捷别名。这与普通 shell 函数的参数语义一致但因为包装发生在 Nix 层面工具的来源与版本都是确定的。从源码看这个机制是这样落地的scripts.nix 使用pkgs.writeScriptBin生成一个名为脚本名的可执行包装其内容大致为#!${pkgs.bash}/bin/sh ${binary} ${execScript} $即用bash作为解释器调用指定语言解释器默认是 bash 自身执行脚本内容并把命令行参数原样传递。因此$、$1、$*等一切 shell 参数语义都完整保留。运行时包Runtime packages有时某个工具只在一个特定脚本运行时才需要把它加进全局环境反而会造成污染。这时可以用packages属性为该脚本单独声明运行时包这些包只会在脚本执行时进入其PATH{ pkgs, ... }: { scripts.analyze-json { exec # Both curl and jq are available when this script runs curl https://httpbin.org/get?$1 | jq .args ; packages [ pkgs.curl pkgs.jq ]; description Fetch and analyze JSON; }; }packages属性确保这些工具在脚本的PATH中可用同时不污染全局开发环境。其底层实现见 scripts.nix当packages非空时生成脚本会在开头追加一行PATH${pkgs.lib.makeBinPath config.packages}:$PATH即把声明包的bin目录以:连接后前置到PATH上脚本运行完毕退出后这些环境变量即消失不会影响交互式 shell。在脚本内部锁定包路径另一种更硬核的做法是不使用PATH而是直接在脚本内容里通过字符串插值引用包的绝对路径。因为当包被插入字符串时得到的正是该包在 Nix store 中的实际路径{ pkgs, ... }: { scripts.silly-example.exec ${pkgs.curl}/bin/curl https://httpbin.org/get?$1 | ${pkgs.jq}/bin/jq .args ; }这样构建出来的脚本内容将是/nix/store/...-curl-x.x/bin/curl ...这样的绝对路径调用不依赖任何PATH解析彻底杜绝同名命令被其他版本抢先的可能。运行效果与前面完全一致$ devenv shell Building shell ... Entering shell ... (devenv) $ silly-example foo1 { foo: 1 }两种方式各有取舍packages属性更简洁、可读性更好路径插值更显式、可移植性脱离环境直接拷贝脚本更强。实际项目中可按需混用。用你喜欢的语言编写脚本exec的内容并不局限于 bash。通过package属性可以指定脚本的解释器/运行时配合binary指定可执行文件名脚本就能用任意语言编写。此外description可以为脚本添加说明这在enterShell中展示脚本清单时非常有用。{ pkgs, config, lib, ... }: { scripts.python-hello { exec print(Hello, world!) ; package config.languages.python.package; description hello world in Python; }; scripts.nushell-greet { exec def greet [name] { [hello $name] } greet world ; package pkgs.nushell; binary nu; description Greet in Nu Shell; }; scripts.file-example { exec ./file-script.sh; description Script loaded from external file; }; enterShell echo echo Helper scripts you can run to make your development richer: echo ${pkgs.gnused}/bin/sed -e s| |••|g -e s|| | EOF | ${pkgs.util-linuxMinimal}/bin/column -t | ${pkgs.gnused}/bin/sed -e s|^| | -e s|••| |g ${lib.generators.toKeyValue {} (lib.mapAttrs (name: value: value.description) config.scripts)} EOF echo ; }进入环境后enterShell会用sed/column把每个脚本的名字和描述渲染成对齐的清单类似devenv info的效果$ devenv shell Building shell ... Entering shell ... Helper scripts you can run to make your development richer: python-hello Hello world in Python nushell-greet Greet in Nu Shell file-example Script loaded from external file (devenv) $各属性语义速览结合 src/modules/scripts.nix 中的类型定义scripts.name的完整属性如下属性类型默认值说明execstr或path必填脚本执行内容或指向脚本文件的路径types.oneOf [ types.str types.path ]packagepackagepkgs.bash用于运行脚本的包默认是 bash即exec内容按 shell 脚本解释binarystr或nullnull覆盖package.meta.mainProgram推断出的可执行文件名用于包名与二进制名不一致的场景如 nushell 包的可执行文件叫nupackagespackage 列表[]仅在该脚本运行时加入PATH的包不污染全局环境descriptionstr脚本描述供devenv info、enterShell清单等展示使用关键实现细节二进制选择scripts.nix 中若显式指定了binary使用${pkgs.lib.getBin config.package}/bin/${config.binary}否则使用pkgs.lib.getExe config.package后者优先取package.meta.mainProgram声明的可执行文件。exec 的来源scripts.nix 中若exec是一个路径builtins.isPath则直接使用该文件否则通过pkgs.writeScript ${name}-script config.exec把字符串内容写成脚本文件。写入环境每个脚本的scriptPackage是一个内部生成的writeScriptBin包internal true并通过lib.hiPrio提高优先级它们统一被合并进config.packages见 scripts.nix因此脚本会随环境自动暴露在$PATH中无需手动chmod或安装。信息展示脚本还会以infoSections.scripts的形式进入devenv info输出见 scripts.nix 与 info.nix 的渲染逻辑方便团队快速了解环境里有哪些可用脚本。从外部文件加载脚本内容exec除了接收多行字符串也可以直接指向仓库里的一个脚本文件路径类型scripts.file-example { exec ./file-script.sh; description Script loaded from external file; };此时文件内容会被原样打包进脚本路径本身在 Nix 层被解析builtins.isPath判断因此不需要文件具备可执行权限devenv 会在构建时自动将其纳入writeScriptBin生成的包装中。这在脚本较长、需要版本管理或复用已有.sh文件时尤为实用——examples/scripts/file-script.sh 就是一个真实的例子。完整的可运行示例与自动化测试仓库的 examples/scripts/devenv.nix 集中演示了本节的全部能力全局packages中的jq供所有脚本使用、脚本私有的packages如cowsay、Python 与 NuShell 编写的脚本、binary覆盖、从文件加载脚本以及enterTest中的自动化验证enterTest echo Testing silly-example silly-example world | grep Hello echo Testing serious-example serious-example hello world | grep hello echo Testing python-hello python-hello | grep Hello echo Testing nushell-greet nushell-greet | grep hello echo Testing file-example file-example test args | grep This script was loaded from a file! ;这段enterTest展示了推荐做法在devenv test时对每个脚本做冒烟测试用grep校验输出内容从而保证脚本在环境升级、包版本变更后依然可用。仓库测试中同样大量采用这种模式例如 tests/prometheus/devenv.nix 里的scripts.ping-prometheus.exec、tests/mysql/devenv.nix 里的scripts.ping-mysql.exec等都是把脚本作为环境自检手段的真实案例。与 enterShell / tasks 的分工文档中特别提示对于进入 shell 时需要执行的操作建议优先使用 tasks 的before属性 而非enterShell。二者的定位不同scripts定义用户随时可以调用的命令重在提供能力enterShell进入环境时的即时动作适合展示信息如上面的脚本清单或一次性初始化tasks带依赖与执行顺序控制的初始化流程适合环境启动时的准备工作。tasks 对执行顺序和依赖提供更好的控制因此在多数进入环境即执行的场景下更推荐。而脚本因为具备描述、语言选择、独立 PATH 等特性更适合沉淀为团队共享的开发工具集。小结devenv 的scripts模块把脚本管理从散落的Makefile、package.jsonscripts 和手工安装的工具中解放出来声明式一个devenv.nix即定义全部脚本及其依赖可复现工具版本由 Nix 锁定所有开发者行为一致按需注入packages让运行时依赖只在脚本执行瞬间进入PATH多语言packagebinary支持任意语言编写脚本可测试配合enterTest可在devenv test中自动验证每个脚本。你可以在仓库的 scripts 模块实现 中查看全部源码细节参考 完整示例 与 示例文档 快速搭建自己的脚本体系。赞分享开发工具CLI【免费下载链接】devenvFast, Declarative, Reproducible, and Composable Developer Environments using Nix项目地址https://gitcode.com/gh_mirrors/de/devenv点击查看免费下载相关推荐devenv Inputs 完全指南用 Nix 声明式管理开发者环境的依赖devenv Inputs 完全指南用 Nix 声明式管理开发者环境的依赖 导读 inputs 输入是 devenv 中把项目之外的 Nix 代码引入开发工具CLI基于 Nix 的声明式开发环境devenv 的 languages.nix 模块与 Nix 工具链集成指南基于 Nix 的声明式开发环境devenv 的 languages.nix 模块与 Nix 工具链集成指南 devenv 是一个基于 Nix 的快速、声明式、开发工具CLIdevenv 语言模块实战用 Nix 搭建声明式 Pkl 开发环境devenv 语言模块实战用 Nix 搭建声明式 Pkl 开发环境 本篇技术指南聚焦 devenv 项目中的 languages.pkl 语言模块讲解如何通开发工具CLI上一篇告别龟速下载用Python脚本解锁百度网盘全速下载的秘密下一篇百度网盘直链解析技术深度解析绕过限速的架构实现与实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表