asdf 核心贡献指南:从环境搭建、Bats 测试到 Conventional Commits 的完整开发流程
2026/9/11 23:46:13 网站建设 项目流程

asdf 核心贡献指南:从环境搭建、Bats 测试到 Conventional Commits 的完整开发流程

【免费下载链接】asdfExtendable version manager with support for Ruby, Node.js, Elixir, Erlang & more项目地址: https://gitcode.com/GitHub_Trending/as/asdf

本文以 docs/ja-jp/contribute/core.md(及对应的英文版 docs/contribute/core.md)为主体,结合仓库内的 scripts/lint.bash、scripts/test.bash、scripts/checkstyle.py、release-please-config.json、.github/workflows/semantic-pr.yml 以及 test/ 目录中的大量 Bats 测试用例,系统讲解如何为 asdf 核心仓库做贡献:从克隆仓库、安装开发工具链,到本地构建与调试、编写与运行 Bats 测试,再到遵循 Conventional Commits 规范提交 Pull Request 的完整工作流。读完本文,你将能够独立搭建 asdf 核心开发环境,理解其 Lint/Format/Test 三件套的使用方式,写出符合项目规范的测试与提交信息,并掌握利用.git-blame-ignore-revs减少代码审查噪音的实用技巧。

概览:asdf 核心贡献意味着什么

asdf 是一个可扩展的版本管理器,支持 Ruby、Node.js、Elixir、Erlang 等多种运行时,其核心仓库既包含大量 Shell(Bash)实现与 Bats 测试,也在向 Go 方向演进(见仓库根目录的 go.mod 与 cmd/ 目录)。对核心仓库做贡献,通常意味着:

  • 修复asdf命令本身的 Bug(如安装、卸载、版本解析、shim 生成等);
  • 为新的功能或行为补充 Bats 集成测试;
  • 改进 Shell 脚本的格式、静态检查与可维护性;
  • 更新文档并遵循 Conventional Commits 规范提交 PR。

本指南正是围绕这一流程展开。整份指南对应的顶层入口见 CONTRIBUTING.md,其中描述了 Bug 报告、功能提案与文档改进等更多贡献途径;本文聚焦于「核心代码开发」这一条主线。

初始搭建:克隆仓库并准备开发工具链

1. Fork 并克隆仓库

在 GitHub 上 Forkasdf仓库,或将默认分支克隆到本地:

# clone your fork git clone https://github.com/<GITHUB_USER>/asdf.git # or clone asdf git clone https://github.com/asdf-vm/asdf.git

2. 使用 asdf 自身管理核心开发工具

asdf 核心开发所用的工具版本定义在仓库根目录的.tool-versions文件中。当前仓库中该文件内容为:

bats 1.8.2 shellcheck 0.10.0 shfmt 3.6.0 golang 1.26.3

即核心开发需要 Bats(测试框架)、ShellCheck(静态分析)、shfmt(Shell 格式化器)以及 Go 工具链(golang)。如果你希望用 asdf 自己来管理这些工具,先添加对应的插件:

asdf plugin add bats https://github.com/timgluz/asdf-bats.git asdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git asdf plugin add shfmt https://github.com/luizm/asdf-shfmt.git

然后一次性安装.tool-versions中声明的全部版本:

asdf install

3. 一个重要提醒:开发时是否用 asdf 管理工具

文档特别提醒:在本地机器上开发时,可能最好不要使用 asdf 来管理这些开发工具。原因很直白——你在开发 asdf 的过程中可能会改动并破坏某些功能,而这些功能恰好是支撑你自身开发工具链(Bats、ShellCheck、shfmt)所依赖的,一旦 break 就会连带影响你的日常开发。此时一个更稳妥的做法是直接安装这些工具的独立版本:

  • bats-core:Bash 自动测试系统,用于对 Bash 或 POSIX 兼容脚本做单元测试;
  • shellcheck:Shell 脚本静态分析工具;
  • shfmt:带 Bash 支持的 Shell 解析器、格式化器与解释器。

此外,从 .tool-versions 可以看出 Go(golang 1.26.3)也是核心开发的一部分——这与仓库中 go.mod、internal/ 下的 Go 实现以及 cmd/asdf/main.go 入口相吻合(在 Go 测试代码中,HOMEASDF_BIN等环境变量即由 Go 侧测试代码定义,见 test/test_helpers.bash 中的注释说明)。

开发:在不动已安装 asdf 的前提下试运行你的改动

使用$ASDF_DIR指向克隆仓库

当你修改了 asdf 源码,想在不影响已安装 asdf 的情况下试运行改动,可以设置$ASDF_DIR环境变量指向克隆仓库的路径,并把该目录下的binshims目录临时加到PATH最前面:

export ASDF_DIR=/path/to/your/asdf-clone export PATH="$ASDF_DIR/bin:$ASDF_DIR/shims:$PATH"

这样,asdf命令就会优先解析到你克隆的仓库中。同样的思路也体现在测试基础设施中:Bats 测试通过setup_asdf_dir()ASDF_DIR指向临时目录,并执行PATH="$ASDF_BIN:$ASDF_DIR/shims:$PATH"来隔离被测环境(参见 test/test_helpers.bash)。

提交前:格式化、Lint 与测试

在 commit 或 push 到远端之前,建议先在本地完成格式化、Lint 与测试。文档给出的命令如下:

# Lint ./scripts/lint.bash --check # Fix & Format ./scripts/lint.bash --fix # Test: all tests ./scripts/test.bash # Test: for specific command bats test/list_commands.bash

注意:文档原文示例中的test/list_commands.bash是示意路径,当前仓库中真实的测试文件位于 test/ 目录(如 test/list_command.bats、test/install_command.bats 等),运行单个测试时请替换为你实际要调试的文件。

scripts/lint.bash到底检查什么

从源码看,scripts/lint.bash 要求必须从仓库根目录执行(脚本会通过git rev-parse --show-toplevel与当前目录比对,不一致则直接报错退出,见第 126-135 行)。它依次执行四类检查,且同时支持--check(发现问题即报错)与--fix(自动修复)两种模式:

  1. shfmt 风格检查run_shfmt_stylecheck):对internal/completions/*.bashscripts/*.bashtest/test_helpers.bash以及test/fixtures/下各 dummy 插件的bin/*文件以--language-dialect bash --indent 2检查;对test/*.bats--language-dialect bats --indent 2检查。--check模式用--diff展示差异,--fix模式用--write直接落盘。

  2. 自定义 Python 风格检查run_custom_python_stylecheck):调用 scripts/checkstyle.py。这是一个正则规则驱动的检查器,内置了 5 条 shellcheck 覆盖不到的规则,例如:

    • no-double-backslashprintf "%s\\n"中多余的转义反斜杠;
    • no-pwd-capture:要求用$PWD而不是$(pwd)
    • no-test-double-equals:要求[ a = b ]而不是[ a == b ]
    • no-function-keyword:只允许fn_name() { ... }风格,禁止function fn关键字;
    • no-verbose-redirection:要求用&>/dev/null替代>/dev/null 2>&1

    这些规则都带有正/负向正则测试(可用./scripts/checkstyle.py --internal-test-regex自检)。需要注意的是:--fix模式需要 python3 环境,若本地没有 python3,脚本会打印警告并跳过此步骤;但在 CI(GITHUB_ACTIONS环境变量存在)中缺少 python3 则会直接报错退出。

  3. ShellCheck 静态分析run_shellcheck_linter):对.bash文件使用--shell bash --external-sources,对.bats文件使用--shell bats --external-source,覆盖范围与 shfmt 相同。

  4. fish_indent 检查run_fish_linter):对internal/completions/asdf.fish做格式化检查(同样在 CI 下缺少fish_indent会报错,本地则跳过)。

此外脚本中还保留了 Elvish、Nushell、PowerShell 的 lint 占位(注释形式),并注明 Elvish 尚无成熟 lint/format 工具、Nushell 暂无相应工具,这些注释可以作为了解项目现状的参考。

Go 侧的工程化命令(补充)

除了 Shell 侧脚本,仓库根目录的 Makefile 还提供了一套 Go 工程命令,与文档描述的「本地先验证再提交」理念一致:

make fmt # go fmt + gofumpt make lint # staticcheck + revive make vet # go vet make test # go test -coverprofile ... -race ./... make audit # verify + vet + test make build # go build 生成 ./asdf 二进制

如果你的改动涉及 Go 代码(如 internal/ 下的实现),这两套工具链需要同时通过。

代码规范细节:.gitignore.git-blame-ignore-revs

.gitignore的职责边界

仓库的 .gitignore 只负责忽略项目特定的文件,当前内容为:

/installs /downloads /shims repository .vagrant keyrings /tmp dist/ # ignore build binary asdf

其中installsdownloadsshimstmp是 asdf 运行时产生的数据目录,asdf是构建产物二进制。而每个开发者 OS、工具、工作流相关的文件(如编辑器的临时文件)不应提交到仓库的.gitignore,而应放在你自己的全局.gitignore配置中,这样每个仓库都能自动继承,也避免污染共享仓库。文档中推荐了相关博客作为进一步参考。

.git-blame-ignore-revsgit blame更干净

大规模格式重排(如整库 shfmt 格式化)会让git blame充满噪音——每个格式化提交都会把大段代码标记为「最后一手改动」。asdf 使用 .git-blame-ignore-revs 来解决这个问题:该文件列出了一批只做格式调整、无实质逻辑变化的 commit(当前仓库中记录了b8dc5f1604...(Run shfmt on bash files)、d81b81f9de...(fix: Remove == inside [)等 9 个 commit),执行 blame 时自动跳过它们:

git blame --ignore-revs-file .git-blame-ignore-revs ./test/install_command.bats

如果不想每次手动带参数,可以配置 Git 全局或仓库级选项,让每次blame调用都自动读取该文件:

git config blame.ignoreRevsFile .git-blame-ignore-revs

也可以让 IDE 使用该文件。以 VSCode + GitLens 为例,在.vscode/settings.json中写入:

{ "gitlens.advanced.blame.customArguments": [ "--ignore-revs-file", ".git-blame-ignore-revs" ] }

关于git blame的更多细节可参阅 Git 官方文档。

Bats 测试:asdf 核心的测试体系

如何运行测试

在本地执行全部测试:

./scripts/test.bash

从 scripts/test.bash 源码看,它同样要求从仓库根目录执行,然后调用 bats 并携带--timing --print-output-on-failure参数;如果检测到parallel命令,还会追加--jobs 2 --no-parallelize-within-files以并行加速(CI 环境中若缺少 GNU parallel 会直接报错)。因此本地安装 GNU parallel 可以显著加快测试。

写测试前必读的三样东西

文档要求,在编写测试之前务必先通读:

  1. test/ 目录下已有的测试(当前仓库包含install_command.batsset_command.batsshim_exec.batsversion_commands.bats等 20 余个.bats文件);
  2. bats-core 的官方文档;
  3. scripts/test.bash 中使用的既有 Bats 配置。

测试基础设施速览

仓库的测试通过 test/test_helpers.bash 提供辅助函数,理解它们有助于写出符合项目惯例的测试:

  • setup_asdf_dir():为每个测试建立隔离的$HOME$ASDF_DIR$ASDF_DATA_DIR,并把被测的binshims目录注入PATH
  • install_mock_plugin/install_dummy_plugin系列:把test/fixtures/dummy_plugin等夹具复制为插件并初始化 Git 仓库(test/fixtures/dummy_plugin 等夹具可在 test/fixtures/ 下找到);
  • install_mock_plugin_version/install_dummy_version:在installs/<plugin>/<version>下创建假安装目录,用于测试版本相关命令;
  • init_git_repo():以--initial-branch=master初始化夹具仓库并完成首次提交;
  • clean_asdf_dir():清理测试目录并取消相关环境变量。

这些辅助函数会被install_command.batsuninstall_command.batsset_command.bats等大量测试文件复用,是理解 asdf 测试风格的最佳入口。

Bats 调试技巧:用-t>&3输出到终端

Bats 的调试有时比较棘手。默认情况下,测试中被执行的命令的 stdout/stderr 会被 bats 吞掉(除非失败,否则不展示)。文档推荐使用-t开启 TAP 输出,配合特殊文件描述符>&3,可以在测试执行过程中即时打印内容,极大简化调试:

# test/some_tests.bats printf "%s\n" "Will not be printed during bats test/some_tests.bats" printf "%s\n" "Will be printed during bats -t test/some_tests.bats" >&3

即:普通printfbats test/some_tests.bats运行时不会显示,而写入>&3的输出在bats -t test/some_tests.bats下会实时打印到终端。该机制在 bats-core 文档的 "Printing to the Terminal" 一节有更详细的说明。

测试的必要性

文档以醒目提示强调:请为你的改动编写测试!新功能必须有测试覆盖(这是硬性要求),Bug 修复附上测试也能显著加快 Review 速度。在提交 Pull Request 之前,请确保新代码路径已有对应的测试用例。

提交与发布:Pull Request、Conventional Commits 与 Release Please

提交信息格式:Conventional Commits

asdf 使用自动发布工具 Release Please,其依据是自上次发布以来的提交历史。因此提交信息必须遵循 Conventional Commits 规范——它在默认分支上定义了提交消息格式,也即 Pull Request 标题的格式。该规范由 GitHub Actionamannn/action-semantic-pull-request强制校验,仓库中的 .github/workflows/semantic-pr.yml 即该 Action 的实际配置,并额外限制了允许的 scope 列表(docswebsiteplugincompletionsdepsgolang-rewrite等)。

Conventional Commit 的格式如下:

<type>[optional scope][optional !]: <description> <!-- examples --> fix: some fix feat: a new feature docs: some documentation update docs(website): some change for the website feat!: feature with breaking change

可用的<types>完整列表为:featfixdocsstylerefactorperftestbuildcichorerevert

type 与 SemVer 版本的对应关系:

  • !:表示这是一个破坏性变更(breaking change);
  • fix:触发 SemVerpatch版本提升;
  • feat:触发 SemVerminor版本提升;
  • <type>!(如feat!fix!):触发 SemVermajor版本提升。

Pull Request 的标题必须遵循该格式,这是合并的前置条件。

Release Please 与版本管理

仓库根目录的 release-please-config.json 是发布配置的仓库内证据:包类型为go,开启bump-minor-pre-major(1.x 之前以 minor 递增),changelog-typesfeat/fix/docs分别归入 Features/Patches/Documentation 三个章节,并指定SECURITY.md、多语言 getting-started 文档(含 docs/ja-jp/guide/getting-started.md)以及 cmd/asdf/main.go 中的版本信息作为发布时需要同步更新的额外文件。发布的版本清单记录在 .release-please-manifest.json,当前版本号为0.16.x阶段(可结合 version.txt 与 docs/ja-jp/guide/upgrading-to-v0-16.md 了解对应升级说明)。

进阶:把 asdf 打包进 Docker 镜像

asdf-alpine 和 asdf-ubuntu 是持续进行的社区项目,为部分 asdf 工具提供 Docker 化镜像。这些镜像既可以用作开发服务器的基础镜像,也可以直接用于运行生产应用——如果你需要把 asdf 管理的运行时部署到容器环境,这两个项目值得关注(它们不属于本仓库,请到对应项目了解详情)。

常见问题与检查清单

  • 脚本必须在仓库根目录运行scripts/lint.bashscripts/test.bash都会校验当前目录是否为仓库根目录,否则直接报错。
  • 本地没有 python3 / fish_indent / parallel 怎么办:本地环境下对应检查会被跳过并给出[WARNING](Go 工具链与 bats 不受影响);但 CI 环境下缺少这些依赖会直接失败。
  • 改动会不会影响我的开发环境:文档建议开发 asdf 时谨慎使用 asdf 管理开发工具,防止改坏自身依赖;如需隔离,使用$ASDF_DIR+ 临时 PATH 的方式试运行。
  • PR 标题规范:必须使用 Conventional Commit 格式,否则会被 .github/workflows/semantic-pr.yml 拦截。
  • 提交前自查./scripts/lint.bash --check./scripts/test.bash→ 为改动补充 Bats 测试 → 检查git blame噪音(用.git-blame-ignore-revs)。

按照上述流程,你就可以在保持本地开发环境安全的前提下,完成「克隆 → 装工具 → 改代码 → 试运行 → Lint/Format → 写测试 → 跑测试 → 提交 PR」的完整 asdf 核心贡献闭环。

【免费下载链接】asdfExtendable version manager with support for Ruby, Node.js, Elixir, Erlang & more项目地址: https://gitcode.com/GitHub_Trending/as/asdf

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

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

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

立即咨询