Motrix 提交质量门禁:每次提交前的必备检查与按变更类型选择验证手段
【免费下载链接】MotrixA full-featured download manager.项目地址: https://gitcode.com/GitHub_Trending/mo/Motrix
本文基于 Motrix 仓库的提交与质量规则 .claude/rules/commit-and-quality.md 展开,系统讲解该项目"每次提交前必须通过三道自动检查 + 按变更类型追加专项检查"的质量门禁体系:包括三条必跑命令(边界检查、Biome lint、TypeScript 类型检查)的准确用法与纪律要求,以及行为测试、E2E、i18n、文件命名、插件 Schema 对齐、第三方声明、Rust 原生宿主、打包发布等八类变更各自对应的专项检查命令。读完本文,你可以完整复现 Motrix 的提交前验证流程,并理解每条检查脚本背后的实现机制。
每次提交前必须通过的三道检查
规则文档的核心要求非常明确:在提交之前运行全部三条命令,并修复所有失败项,不允许"先提交后修":
pnpm run check:boundaries pnpm run lint pnpm exec tsc --noEmit这三条命令在 package.json 中均有对应定义,下面逐一结合源码讲解它们的实际行为与注意事项。
check:boundaries:架构分层的自动基线
check:boundaries对应脚本 scripts/check-boundaries.mjs,它通过grep -rnE对指定目录做导入模式扫描,共内置 9 条硬规则(源码中的rules数组),覆盖 .claude/rules/architecture.md 中定义的层级矩阵的核心约束,例如:
| 规则标签 | 扫描目录 | 模式要点 |
|---|---|---|
core must not import electron | src/core/ | from ['"]electron['"] |
core must not import fastify | src/core/ | from ['"]@?fastify |
shared must not use Node-specific APIs or globals | src/shared/ | node:前缀导入、process.、NodeJS.全局 |
renderer must not import core or main | src/renderer/ | 路径含core/或main/的导入 |
server must not import electron | src/server/ | from ['"]electron['"] |
server must not import src/main | src/server/ | @main/或src/main/导入 |
production source must not reference deployment staging contracts | src/ | electron-runtime-dependencies.json、.motrix-*-stage.json、dist/(electron|server)-app等部署期契约 |
add-task UI must not import transport or protocol commands | src/renderer/components/add-task/ | 对@renderer/lib/transport、@shared/protocol/commands的导入(白名单排除use-external-hdration.ts、drop-zone.tsx、add-task-form.tsx三个 IPC 感知文件) |
web-services must not reference Electron-only command symbols | src/renderer/platform/web-services.ts | PickSaveDir、CloseCurrentWindow、ResizeWindow、ShowMainWindow |
脚本的判定逻辑值得注意:grep退出码 1(无匹配)视为[PASS];退出码 0 时先经filterOutExceptions按文件后缀过滤白名单文件,再决定 PASS/FAIL;命中违规会打印path:line:content明细并以退出码 1 终止。个别规则支持except白名单,这正是文档强调"check:boundaries只是自动基线,不是完整的架构证明"的原因——并非每条架构例外都能被机器强制,因此规则同时要求开发者在提交前对照 architecture.md 人工复查本次改动引入的导入。
lint:与 CI 完全同构的 Biome 检查
pnpm run lint在 package.json 中定义为biome check .,即对整个仓库执行 Biome 检查。文档对此有两条强约束:
- 不得用更窄的路径列表替代——它必须与 CI 运行的命令完全一致;
- 不得用管道方式丢弃其退出码(例如
pnpm run lint | tee log.txt这类写法会让管道以tee的退出码为准,掩盖 lint 失败)。
检查行为由 biome.json 统一配置,关键配置包括:
- 格式化:2 空格缩进、80 列行宽、LF 换行;JS 单引号、JSX 双引号、分号
asNeeded、尾逗号es5; - Lint:启用
recommended预设,并打开react与test两个 domains 的recommended规则; - 文件命名:
useFilenamingConvention设为error级,强制源码文件名为 kebab-case; - 例外覆盖(overrides):
**/components/ui/**关闭noDangerouslySetInnerHtml与noArrayIndexKey;所有*.test.ts/tsx、*.spec.ts/tsx及测试目录关闭noNonNullAssertion与noExplicitAny;两个 registry fixture JSON 关闭格式化。 - 另外启用
vcs.useIgnoreFile(尊重.gitignore)与assist的organizeImports。
也就是说,pnpm run lint一条命令同时校验格式、导入排序、lint 规则与文件命名约定,这就是它能作为提交门禁的原因。
tsc --noEmit:全量类型检查
第三条pnpm exec tsc --noEmit使用仓库根 tsconfig.json 对整个工程做只检查、不产物的类型校验(devDependencies 中固定了typescript版本),确保本次改动不引入任何类型错误后再进入版本库。
暂存与差异检查纪律
文档对提交操作本身也规定了纪律,防止"检查过了但提交的不是同一份内容":
- 只
git add你打算提交的文件或 hunk; - 提交前检查
git diff --staged; - 运行
git diff --cached --check检测空白字符、冲突标记等低级问题; - 不要因为某个 CI job 是 non-blocking 的就故意隐瞒失败——非阻塞不等于可以忽略。
按变更类型追加的专项检查
三道必过检查是"底座",文档的 Change-Specific Checks 一节则要求:凡与本次变更匹配的检查,一条都不能少。完整映射如下表:
| 变更类型 | 需要运行的检查 |
|---|---|
| 行为或逻辑改动 | 针对改动的测试pnpm exec vitest run <test-path>;大范围横切改动用pnpm test |
| 浏览器 / Electron 用户流 | 受影响流程有 E2E 覆盖时运行pnpm test:e2e |
| 语言资源或 i18n 行为 | pnpm run check:i18n |
| 新增或重命名文件 | pnpm run check:file-names |
插件 manifest 契约或@motrix/plugin-manifest-schema | pnpm run check:schema-parity |
| 依赖、打包资产或许可证元数据 | pnpm run check:third-party-notices |
| 原生宿主(Rust) | 见下方 cargo 三连 |
| 打包或发布代码 | 运行tests/scripts/下对应聚焦测试及相关 verifier |
下面对每个专项的实现做源码级展开。
行为 / 逻辑改动:Vitest 聚焦测试
仓库使用 Vitest(pnpm test即vitest run,并带pretest钩子node scripts/ensure-native-abi.mjs node确保better-sqlite3等原生模块 ABI 与当前 Node 匹配)。聚焦跑测试时直接用pnpm exec vitest run <test-path>;只有当改动是"大范围横切"(例如动了src/core/中多处共享逻辑)时才升级到全量pnpm test。
浏览器 / Electron 用户流:Playwright E2E
pnpm test:e2e对应playwright test(package.json),其pretest:e2e钩子会先ensure:electron-runtime并执行ensure-native-abi.mjs electron,保证 Electron 运行时与原生 ABI 就绪。测试用例位于 e2e/ 目录(如 e2e/task-lifecycle.spec.ts、e2e/add-task.spec.ts),运行环境由 playwright.config.ts 配置。注意触发条件是"受影响流程存在 E2E 覆盖时"——改动了有对应 spec 的用户流就必须跑,不能以"改动很小"为由跳过。
i18n / 语言资源:check:i18n
pnpm run check:i18n实际是node --import tsx scripts/check-i18n.mjs,实现见 scripts/check-i18n.mjs。它默认以 src/shared/constants/locales.ts 中的SUPPORTED_LOCALES目录为基准、src/shared/locales/ 为资源目录,检查项包括:
- 目录与文件一一对应(目录有 locale 但缺 JSON 文件、或有文件但未注册,均报错);
- 将所有标量 key 扁平化后,以 fallback locale 为参照逐 locale 比对逻辑 key 集合(缺失/多余都会列出);
- 复数族完整性:基于
Intl.PluralRules的cardinal类别校验每个 locale 必需的_zero/_one/_two/_few/_many/_other变体是否齐全、是否存在该 locale 不支持的类别,并禁止 base key 与复数变体混用; - 插值占位符一致性:解析
{{value}}、{{- value}}、{{value, format}}三种 i18next 形态,跨 locale 比对同一逻辑 key 的占位符集合是否一致。
全部通过时输出类似check:i18n passed: N locales, M logical translation keys.。该脚本也接受--catalog-module/--locales-dir参数覆盖默认路径(可用--help查看)。
新增 / 重命名文件:check:file-names
scripts/check-file-names.mjs 通过git ls-files --cached --others --exclude-standard扫描 Git 可见文件,按扩展名套用命名约定:
.ts/.tsx/.js/.jsx/.mjs/.cjs/.mts/.cts/.css/.scss→ 主文件名必须为 kebab-case(^[a-z0-9]+(?:-[a-z0-9]+)*$);.py/.rs→ snake_case;- 例外:
src/bin/下的 Cargo 二进制允许 kebab-case(Cargo 惯例),docs/与graphify-out/前缀整体排除。
这与 biome.json 中useFilenamingConvention规则互为补充:Biome 管的是被 lint 到的源码文件,而check:file-names直接以 Git 索引为准,覆盖 BiomeignoreUnknown放过的文件,确保新增/重命名文件不会被遗漏。
插件 manifest 契约:check:schema-parity
pnpm run check:schema-parity对应 scripts/check-schema-parity.mjs,其设计意图在脚本头部注释中写得很清楚:真正的 schema 位于外部发布的@motrix/plugin-manifest-schema包,宿主侧 src/core/plugin/manifest/schema.ts 必须保持纯再导出——脚本要求该文件包含export * from '@motrix/plugin-manifest-schema',且去掉注释与该行后不得残留任何实质内容(例如私自引入 zod 定义、本地类型都算 "drift" 失败)。脚本注释还给出了修复方向:出现 drift 时不要往 facade 里抄 schema 代码,而是修改上游包并升级依赖版本。
依赖 / 资产 / 许可证元数据:check:third-party-notices
pnpm run check:third-party-notices的定义是(见 package.json):
pnpm run ensure:electron-runtime && node scripts/generate-third-party-notices.mjs --check \ && vitest run tests/check-third-party-notices.test.ts tests/generate-third-party-notices.test.ts即先用 scripts/generate-third-party-notices.mjs 的--check模式比对 THIRD_PARTY_NOTICES.md(及 THIRD_PARTY_NOTICES.zh-CN.md)是否与依赖/资产现状一致,再跑对应的两个聚焦测试。因此凡是动package.json依赖、内置打包资产或许可证元数据的提交,都必须通过它,避免第三方声明与实际分发内容脱节。
原生宿主 Rust 代码
改动 packages/native-host/ 下的 Rust 代码时,规则要求运行针对该包 manifest 的三连(--locked保证锁定依赖不变):
cargo fmt --manifest-path packages/native-host/Cargo.toml --all -- --check cargo clippy --manifest-path packages/native-host/Cargo.toml --all-targets --locked -- -D warnings cargo test --manifest-path packages/native-host/Cargo.toml --locked --all-targets对应源码位于 packages/native-host/src/(如broker_protocol.rs、endpoint.rs、launcher.rs等),集成测试在 packages/native-host/tests/(如host_integration.rs、flatpak_broker_integration.rs)。-D warnings意味着 Clippy 警告直接视为错误,与该包作为浏览器原生消息宿主(native messaging host)的严格质量要求一致。
打包 / 发布代码
改动打包或发布相关脚本(scripts/ 下的 staging、verify、assemble 系列)时,要求运行 tests/scripts/ 下的对应聚焦测试和相应 verifier(如verify-electron-package、verify-server-package、verify-appimage-artifact等),并且对于check:update-artifacts(即 scripts/verify-update-artifacts.mjs)这类检查,遵循 workflow 提供的参数,不要自行拼造。发布链路的总体政策(tag、签名、平台构建门控)在 .claude/rules/git-workflow.md 的 Release Safety 一节有专门约束,可与本文配套阅读。
biome --write 的正确用法
文档最后一条规则:pnpm exec biome check --write .只能用于已审查(reviewed)、可自动修复的问题,并且修复后必须重新运行上面三道必过检查确认没有引入新问题。换言之,--write不是"一键救火",而是修复手段之一,门禁本身仍然以只读检查命令的结果为准。
小结:这套门禁的设计取向
从规则文档与脚本实现可以归纳出 Motrix 提交质量门禁的三个取向:
- CI 同构:本地必跑命令与 CI 完全一致(
biome check .),且禁止管道丢弃退出码、禁止非阻塞 job 掩盖失败,保证"本地过了"等价于"CI 能过"; - 分层兜底:
check:boundaries是自动基线而非架构证明,人工需对照 architecture.md 复查变更导入; - 按变更面追加验证:八类专项检查各自有独立的脚本实现与测试兜底(如 i18n 的
Intl.PluralRules复数校验、schema 纯 facade 漂移检测、文件命名对 Git 索引的直接扫描),开发者按本次改动范围"取并集"执行即可。
配合 .claude/rules/git-workflow.md 中的 Conventional Commits 规范(<type>(<scope>): <summary>,type 限feat/fix/refactor/perf/test/docs/chore/ci/style)与分支、PR 政策,commit-and-quality.md构成 Motrix 从"写完代码"到"合入 main"的完整质量闭环。
【免费下载链接】MotrixA full-featured download manager.项目地址: https://gitcode.com/GitHub_Trending/mo/Motrix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考