尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

dependabot-core 的 silent 生态:一个零网络请求的“哑”包管理器与 updater 的端到端测试体系

dependabot-core 的 silent 生态:一个零网络请求的“哑”包管理器与 updater 的端到端测试体系 dependabot-core 的 silent 生态一个零网络请求的“哑”包管理器与 updater 的端到端测试体系【免费下载链接】dependabot-core Dependabots core logic for creating update PRs.项目地址: https://gitcode.com/GitHub_Trending/de/dependabot-core本文以 silent/README.md 为核心讲解 dependabot-core 中名为silent的测试专用生态ecosystem的完整设计它如何用纯本地 JSON 文件模拟一个包管理器的全部组件如何用 txtar 格式的端到端测试脚本驱动 Dependabot CLI 跑通完整的更新流程以及如何通过 silent/tests/silent_test.go 对更新器输出的 PR 内容进行断言。读完后你能掌握为 updater 编写无网络、可复现集成测试的完整方法论并理解 silent 生态各组件fetcher / parser / checker / updater与 updater 主流程的对接点。缘起为什么 updater 需要一个“哑”生态silent/README.md 开宗明义这个生态专门用于对updater代码做集成测试真实测试用例都放在 silent/tests/testdata 目录中。README 中给出了三条动机都是历史痛点早期用大型 rspec fixtures 测试 updater。fixture 文件把单个测试的上下文拆得到处都是“让人难以把测试当成一个整体来看”依赖真实生态做测试等于测了太多东西。用真实的 npm、bundler 等生态去验证 updater 逻辑时生态本身的行为也成了被测对象干扰了断言目标真实生态会发起真实网络请求。有些 native helper 根本无法 mock导致测试不稳定且无法在离线环境运行。解决方案就是造一个不发任何网络请求的新生态——这就是 silent沉默名字的由来。README 将其概括为“它是生态的最小化实现用包含 JSON 的文件作为可用版本的清单。”silent 生态的实现一切来自本地文件silent 生态的代码位于 silent/lib/dependabot/silent/体量极小但完整覆盖了 Dependabot 生态需要注册的全部组件。silent/lib/dependabot/silent.rb 负责把各组件 require 进来完成注册并额外做了两件值得注意的事require dependabot/pull_request_creator/labeler Dependabot::PullRequestCreator::Labeler .register_label_details(silent, name: silent_package_manager, colour: 000000) require dependabot/dependency Dependabot::Dependency .register_production_check(silent, -(groups) { groups.empty? || groups.include?(prod) })前者为 silent 的 PR 注册标签见 silent.rb#L14-L20后者定义了“生产依赖”的判定规则——组为空或包含prod即视为生产依赖这两处细节都是 updater 生成 PR 标签和依赖类型时依赖的钩子。下面按 updater 的调用链顺序逐个组件拆解其“哑”的实现方式。版本发现UpdateChecker 从仓库文件读取版本清单这是整个 silent 生态的核心设计。silent/lib/dependabot/silent/update_checker.rb 中fetch_dependency_metadata方法直接从仓库工作目录读取与依赖同名的文件def fetch_dependency_metadata version_file File.join(repo_contents_path, dependency.name) return { versions [] } unless File.exist?(version_file) # the available versions are stored in a file in the repo # thats why this package manager is silent, makes no requests JSON.parse(File.read(version_file)) rescue JSON::ParserError raise Dependabot::DependencyFileNotParseable, T.must(dependency_files.first).path end代码注释直接点题“可用版本保存在仓库里的一个文件中这就是这个包管理器叫 silent 的原因它不发起任何请求”。每个依赖dependency-a对应的版本文件内容形如{ versions: [ 1.2.3, 1.2.4, 1.2.5 ] }在latest_versionupdate_checker.rb#L21-L28中逻辑是取available_versions的最大值并先经过filter_ignored_versions过滤被ignore掉的版本——若过滤后为空且允许抛错则抛出Dependabot::AllVersionsIgnored见 update_checker.rb#L91-L97这一异常正是测试 silent/tests/testdata/su-err-all-versions-ignored.txt 所验证的场景。此外还有两个细节Git 依赖模拟git_dependency?判断依赖版本是否为 40 位字符串SHA 长度若是则从版本文件的git字段取下一个版本update_checker.rb#L76-L84用于测试 git 依赖更新路径对应 silent/tests/testdata/vu-group-semver-git.txt 等用例安全更新lowest_security_fix_version复用公共的VersionFilters.filter_vulnerable_versions结合 updater 输入中的security-advisories过滤出最低修复版本安全更新类测试如su-err-not-vulnerable就建立在这条链路上。清单解析FileFetcher 与 FileParsersilent/lib/dependabot/silent/file_fetcher.rb 是最简单的组件——它只抓取仓库根目录下的manifest.json这一个文件def fetch_files [manifest].compact end def manifest fetch_file_if_present(manifest.json) endsilent/lib/dependabot/silent/file_parser.rb 的parse方法解析该 JSON为每个键生成依赖并支持两种形态JSON.parse(manifest_content).each do |name, info| dependency_set parse_single_dependency(name, info) if info.key?(version) dependency_set parse_multiple_dependency(name, info) if info.key?(versions) end单版本形态dependency-a: { version: 1.2.3 }是常规用法可选携带group字段会进入依赖的 requirements 组file_parser.rb#L52-L65用于测试 dependency groups多版本形态versions: [...]模仿 npm_and_yarn 的行为解析成一个 Dependency但把全部版本塞进metadata[:all_versions]file_parser.rb#L66-L77用于测试同一依赖出现在多处版本时的更新逻辑。另外ecosystem方法file_parser.rb#L31-L48会从 manifest 的silent.version元数据读取包管理器版本默认2。对应的 silent/lib/dependabot/silent/package_manager.rb 中声明SUPPORTED_SILENT_VERSIONS为2、DEPRECATED_SILENT_VERSIONS为1——silent 生态自己也完整实现了多版本包管理器机制。文件更新FileUpdater 回写 manifestsilent/lib/dependabot/silent/file_updater.rb 的updated_file_content把更新后的版本写回 manifest JSONinfo[version] requirements(file).first.requirement_string if info[depends-on] # also bump dependants to the same version original_content[info[depends-on]][version] requirements(file).first.requirement_string end两个测试专用的小机关藏在这里多版本更新时会删掉versions键模拟“所有版本收敛到同一版本”而依赖名为dont-update-any-files时直接返回空数组file_updater.rb#L13-L14专门用于测试“检查出有更新但文件无变化”这类边界场景。depends-on字段则用于构造依赖间版本联动的场景。彻底“哑”掉网络MetadataFinder 与测试输入silent/lib/dependabot/silent/metadata_finder.rb 的look_up_source把所有源的 hostname 硬编码为127.0.0.1# Use 127.0.0.1 as a non-routable hostname to avoid network requests # This ensures the silent package manager remains truly silent Dependabot::Source.new( provider: example, hostname: 127.0.0.1, api_endpoint: http://127.0.0.1/api/v3, repo: dependency.name, ... )用不可路由的127.0.0.1作为 API 端点即使代码路径走到“访问远程仓库”也会安全失败从实现层面保证 silent 生态真正零外联。测试输入文件里同样遵循这一约定例如 silent/tests/testdata/su-basic.txt 中source: directory: / provider: example hostname: 127.0.0.1 api-endpoint: http://127.0.0.1/api/v3 repo: dependabot/smoke-testsrequirement.rb 与 version.rb 则是薄封装前者继承公共Dependabot::Requirement并按分隔符拆分复合要求后者继承Dependabot::Version分别通过Dependabot::Utils注册让通用版本/要求解析机制可以无缝复用。如何阅读测试txtar 脚本 Dependabot CLIsilent/README.md 的“ How to read the tests”一节是理解 silent/tests/testdata 下 60 个用例的钥匙。测试基于rsc.io/script库用txtar 格式的.txt文件同时定义“执行步骤”和“测试现场的文件”。一个 txtar 测试文件的结构分三段以 silent/tests/testdata/su-basic.txt 为例第一段命令区。文件顶部是实际执行的 Dependabot CLI 命令dependabot update -f input.yml --local . --updater-image ghcr.io/dependabot/dependabot-updater-silent--local .表示用当前目录即 txtar 定义的文件现场作为输入--updater-image指定使用 silent 专用的 updater 镜像。随后是断言命令例如pr-created expected.json。su-basic.txt 实际演示了三段流程新建 PRstderr断言输出created | dependency-a ( from 1.2.3 to 1.2.4 )、以及两次更新已有 PR的 rebase 场景——通过input-rebase-old.yml/input-rebase-new.yml中的updating-a-pull-request: true和existing-pull-requests字段注入既有 PR 元数据su-basic.txt#L69-L135断言则换成pr-updated expected.json。第二段文件现场。用-- 文件名 --分隔符声明执行时磁盘上的文件例如 manifest-- manifest.json -- { dependency-a: { version: 1.2.3 } }第三段期望输出与版本清单。断言用的expected.json更新后 manifest 应成的样子如1.2.5和 silent 生态读取的可用版本文件dependency-a加上驱动整个流程的input.yml作业输入文件-- input.yml -- job: package-manager: silent source: directory: / provider: example hostname: example.com api-endpoint: https://example.com/api/v3 repo: dependabot/smoke-tests从 testdata 的文件命名可以直观看出测试覆盖面su-*是安全更新security-update场景vu-*是常规版本更新version-update场景err-*是异常路径前缀之后则是具体行为标签group、multidir、glob、rebase、multi-ecosystem等。silent 的价值正体现在这里它让dependency groups、多目录、多生态、冷却期、glob 匹配这些 updater 层特性都拥有了独立的、单文件自包含的集成测试。断言命令集README 列出了一套刻意保持精简约小的断言命令命令作用stdout断言上一条dependabot命令的 stdout 中出现过某行文本stderr同上但检查 stderrpr-created断言“创建的 PR”中有一个其更新的依赖文件与给定文件内容匹配pr-updated同pr-created但只匹配“被更新的 PR”还可以在任何命令前加!如! pr-created断言该命令应当失败——包括dependabot命令本身这就是su-err-*系列用例验证错误路径的方式。断言的底层实现Go 测试驱动器这些断言命令由 silent/tests/silent_test.go 注册并驱动。测试入口用script.Engine加载testdata/*.txt下所有用例silent_test.go#L17-L28Commands()中把dependabot注册为外部程序pr-created/pr-updated注册为自定义命令silent_test.go#L34-L43。prCheckersilent_test.go#L84-L165揭示了断言的具体机制updater 容器把每个 PR 事件以 JSON 行形式打到 stdout驱动器逐行解析筛出type为create_pull_request或update_pull_request的事件再将其data.updated-dependency-files里每个文件的content反序列化为 JSON与测试文件中声明的期望 JSON 做深度相等比较。要求该 PR 更新的文件数量恰好等于断言参数中给出的文件数且每个文件都命中期望值——多个期望文件支持同时传入pr file1 [file2...]用于一次 PR 更新多个 manifest 的场景。匹配失败时给出“创建了 N 个 PR 但无一匹配”或“没有创建 PR”的明确错误信息。运行测试与镜像构建按 silent/README.md 的“Executing the tests”一节运行前提只有一个安装 Docker 和 Dependabot CLI 并放入 PATH。因为 silent 生态零网络请求测试本身不需要任何远端凭据。运行全部测试script/updater-e2e见 script/updater-e2e运行匹配名称的部分测试把名字片段作为参数传入例如script/updater-e2e group会跑完所有文件名含group的用例如vu-group-semver.txt、su-group-pattern.txt等。silent 的 updater 镜像由 silent/Dockerfile 定义只有三行有效内容以ghcr.io/dependabot/dependabot-updater-core为基础镜像把silent、common两个目录和updater目录拷入其余运行时环境完全复用公共 core 镜像。这也解释了为什么测试命令要用--updater-image ghcr.io/dependabot/dependabot-updater-silent它是 core 镜像 silent 生态代码的最小叠加。小结silent 生态是 dependabot-core 中一个“以被测环境换确定性”的范例用 8 个百行以内的 Ruby 文件实现了一个完整生态把版本发现、要求解析、安全过滤、多版本依赖、组标记、错误路径这些 updater 真正关心的行为全部收敛到本地 JSON 文件 txtar 脚本 JSON 深度比较断言的闭环里。它的每个组件update_checker.rb、file_parser.rb、file_updater.rb、metadata_finder.rb都保留了对 updater 主流程的真实注册与调用接口因此 silent/tests/testdata 中的 60 个用例实际验证的是完整的 updater 决策链——从文件抓取到 PR 内容生成——而测试本身可以做到离线、快速、单文件可读。这正是 silent/README.md 开头那句话的完整落地“这个生态用于对 updater 代码做集成测试。”【免费下载链接】dependabot-core Dependabots core logic for creating update PRs.项目地址: https://gitcode.com/GitHub_Trending/de/dependabot-core创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表