
no-mistakes 测试约定指南e2e 边界划分、临时守护进程所有权与 CI 分片策略【免费下载链接】no-mistakesgit push no-mistakes项目地址: https://gitcode.com/GitHub_Trending/no/no-mistakes本文基于 no-mistakes 仓库中.agents/skills/testing-conventions/SKILL.md的测试约定系统讲解该 Go 项目在测试策略、进程隔离、环境隔离与 CI 分片上的工程规范。你将理解哪些行为必须走 e2e、哪些可以单测e2e 守护进程如何被登记-回收以及 Windows CI 为何按 git 密集程度分三片——这些约定既是给 AI Agent 的指令也是为开发者和贡献者准备的测试纪律。为什么需要一份测试约定技能no-mistakes 是一个以git push no-mistakes为核心交互的自动化流水线工具开发者把分支推送到本地守护进程由它驱动 Claude/Codex/OpenCode 等 Agent 完成修复、评审、CI 与 PR 发布。这样的项目天然横跨多个进程和 I/O 边界CLI 参数、配置加载、git 操作、Agent 派生、守护进程协调、stdout/stderr、录制 fixture测试稍有不慎就会污染开发者的真实~/.no-mistakes状态、遗留游离进程或在 CI 上产生难以定位的偶发失败。.agents/skills/testing-conventions/SKILL.md正是为回答何时用 e2e、何时单测、临时守护进程归谁管、环境变量怎么隔离、Windows 上怎么跑而编写的内部技能user-invocable: false、internal: true其触发场景覆盖新增或修改测试、e2e harness、测试进程隔离、CI 测试分片。核心原则按是否跨越进程/I/O 边界选择测试层级约定第一条给出了明确的二分法优先使用 e2e 测试覆盖跨越进程或 I/O 边界的行为CLI 标志、配置加载、git 操作、Agent 派生spawning、守护进程协调、stdout/stderr、录制 fixture。对纯辅助函数pure helpers使用单元测试在速度与失败定位上追求精确。优先在临时目录中创建真实 git 仓库而不是重度 mock。harness 正是这样做的NewHarness用os.MkdirTemp建出nm-e2e-*根目录在internal/e2e/harness.go#L86-L186中依次准备工作克隆WorkDir、裸上游UpstreamDir、NM_HOME与HOME并真实执行git init、git commit、git remote add origin、git pushinternal/e2e/harness.go#L232-L282。选择判断标准一句话概括凡是换一个真实运行环境就会变的行为都进 e2e凡是纯计算、无外部状态的行为都单测。e2e 套件构建标签、Makefile 入口与扫描范围e2e 套件位于e2e构建标签之后典型文件以//go:build e2e开头如internal/e2e/harness.go#L1因此不会混入常规的go test -race ./...即make test见 Makefile。入口make e2e实际执行bash scripts/e2e.shMakefile。默认扫描范围scripts/e2e.sh在无参数时以-tagse2e -count1 -timeout 480s ./internal/e2e/... ./internal/pipeline/steps/...运行scripts/e2e.sh覆盖用户旅程矩阵与 pipeline step 本地 e2e 测试。新增 step 级 e2e 测试也必须置于e2e标签之后否则会被常规go test意外编译、或脱离 e2e 套件的隔离环境运行。临时 e2e 守护进程的所有权模型e2e 测试驱动真实 no-mistakes 二进制必然会在每个临时NM_HOME下启动守护进程。约定明确这些守护进程归internal/e2edaemon包统一管理核心是精确清单exact inventory 并发上限 有界 argv 校验 多层 reaper。清单inventory与并发槽环境变量NM_E2E_DAEMON_INVENTORY指定清单目录未设置时回落到系统临时目录下的no-mistakes-e2e-daemon-inventoryinternal/e2edaemon/inventory.go#L14-L29、DirFromEnv。每条清单记录一个临时守护进程的归属ID、NMHome、PID、二进制路径NMBin、进程指纹ProcessHash、属主 PID 与注册时间Entry结构internal/e2edaemon/inventory.go#L31-L40。并发上限NM_E2E_DAEMON_MAX默认 2DefaultMaxConcurrent 2internal/e2edaemon/inventory.go#L23把一次被中断的套件的爆炸半径限制在 2 个进程内。harness 通过e2edaemon.Acquire(h.NMHome, h.NMBin, 2*time.Minute)获得所有权internal/e2e/harness.go#L178-L182在每次执行命令后调用SyncPID把守护进程 PID 同步进清单internal/e2e/harness.go#L310-L321最后在t.Cleanup中Releaseinternal/e2e/harness.go#L184。有界回收绝不碰共享服务ReapAll在清理前先做硬性安全检查isAllowedTempRoot拒绝默认共享根~/.no-mistakes含符号链接解析后的路径并要求NM_HOME看起来像临时目录包含nm-e2e、nm-eval-、nmh-、/tmp/、/private/tmp/、Windows\temp\等特征段否则只移除清单行、绝不发信号internal/e2edaemon/reaper.go#L107-L140。这与永远不要把 inventory 回收指向共享的~/.no-mistakes服务的约定一一对应代码注释也直言这是即使清单损坏也不会碰共享用户根目录的硬门。回收优先用清单中记录的二进制执行daemon stop优雅停止再按 PID 或进程指纹核对 argv 后终止进程树无指纹时用MatchesDaemonRoot二次确认目标确属该临时根internal/e2edaemon/reaper.go#L49-L105。多层 reaper 与 SIGKILL 边界约定指出临时守护进程的回收分布在三层harness 的t.Cleanupshutdowninternal/e2e/harness.go#L717-L733包级TestMainscripts/e2e.sh的 EXIT/INT/TERM trapscripts/e2e.sh。scripts/e2e.sh同时承担套件级职责为本次调用创建 mode 0700 的清单目录并写入owner.pid设置并发上限并在启动时预回收上一次被强杀包装器遗留的清单NM_E2E_REAP_ABANDONED1触发ReapAbandoned见 scripts/e2e.sh 与internal/e2edaemon/inventory.go#L277-L335。文件头注释如实划定了边界SIGKILL 包装 shell 时 EXIT trap 不会执行磁盘清单会在下次套件启动时被预回收补齐而go test子进程被中断、超时或 SIGKILL 时包装 shell 仍在运行trap 可正常回收。外部 sleep-loop keepalive保活 shell不在回收范围内——这类进程不登记在清单中也不应成为清理目标。回归测试internal/e2edaemon/*_test.go是这些所有权与回收机制的回归防线覆盖清单读写、并发槽、孤儿回收、临时根判定等行为。环境隔离GIT_CONFIG_COUNT、NM_HOME与paths.New()防线约定用三条规则防止测试触碰真实用户状态shell 出 git 的包在TestMain中os.Unsetenv(GIT_CONFIG_COUNT)防止 Agent harness 注入的环境GIT_CONFIG_*泄漏进测试。需要验证注入配置的测试再用t.Setenv(GIT_CONFIG_COUNT, 1)显式重置。这一模式出现在internal/git/git_test.go#L27-L29、internal/daemon/helpers_test.go#L78-L80、internal/gate/gate_test.go#L20-L22、internal/pipeline/steps/steps_test.go#L13-L15与internal/pipeline/steps/citest/main_test.go#L12等位置。能启动守护进程或触碰环境状态的包cmd/no-mistakes、internal/cli、internal/update使用包级TestMain把NM_HOME和HOME指向全新临时目录并禁用遥测与更新检查环境变量保证完整测试运行绝不触碰真实~/.no-mistakes。paths.New()在go test下拒绝默认根internal/paths/paths.go#L23-L24中testing.Testing() NO_MISTAKES_ALLOW_DEFAULT_ROOT_IN_TESTS ! 1时直接报错NM_HOME must be set under go test。因此触碰应用状态的测试必须设置NM_HOME为临时目录只有生产默认路径测试本身可以通过NO_MISTAKES_ALLOW_DEFAULT_ROOT_IN_TESTS1显式豁免。通用纪律用t.TempDir()隔离文件系统用t.Setenv()隔离环境变量——这既是 Go 测试最佳实践也是本仓库避免测试间互相污染、偶发失败的硬性要求。pipeline step 测试fakecli 小助手与绝不重链 race 二进制pipeline stepCI/PR 步骤测试需要在 PATH 上提供gh/glab/git假命令。仓库的解法是独立的小型可执行文件internal/pipeline/fakecli入口文件由stepstest.Init在每个测试进程内构建一次internal/pipeline/steps/internal/stepstest/helpers.go#L278-L320按需以gh、glab、git等名字软链到 PATH 上。通过FAKE_CLI_MODE环境变量分发到不同假实现gh/glab的 PR/MR 与 auth 响应、git-passthrough转发给真实 git、git-move-head-passthrough模拟 push 前 HEAD 被重置、git-intervening-push-passthrough模拟并发 interloper push 触发 force-with-lease 拒绝、git-reset-after-commit-passthrough模拟提交后越带复位、ci-gh/ci-glab系列CI 状态轮询与重跑等。关键纪律绝不把 race 插桩后的测试二进制重链为这些名字——因为-race下每次假调用都要 re-exec 自身代价约为 0.8s/次fakecli 头部注释明确说明了这点internal/pipeline/fakecli/main.go#L1-L4。Windows CI进程派生瓶颈、三片矩阵与超时语义约定对 Windows CI 的分析非常具体背后是 GitHub Hosted Windows runner4 核与 git 密集包的高昂进程派生成本git 密集包在 Windows 上约为 Linux 的 10 倍耗时internal/git从 5.7s 到 53sinternal/branchsync从 31s 到 415s。因此 Windows 矩阵拆成3 个 shard使每个 job 的墙钟时间落在timeout-minutes: 40内且即便卡死也会以go test -timeout15 分钟的失败浮出水面而不是无证据的 job 取消windows-steps单独运行./internal/pipeline/steps/...含steps/citestwindows-git其余 git 密集包internal/git、internal/branchsync、internal/gate、internal/evidence、internal/daemon、internal/evalwindows-corego list剩余部分用NM_CI_WINDOWS_GIT_EXCLUDE前两个 shard 的并集过滤。把 steps 与其他 git 密集包合并曾使windows-git有约 21 分钟的底线耗时所以拆分是必要的该约束由TestCIWorkflow_WindowsHangSurfacesAsGoTimeoutNotJobCancellation锁定。长耗时的 git 密集包不要放在串行关键路径上internal/branchsync用t.Parallel()正是为此。CI 工作流中保留 Defender 扫描排除步骤其注释说明了理由回归测试为TestCIWorkflow_WindowsTestsRunWithScanExclusions与TestCIWorkflow_WindowsHangSurfacesAsGoTimeoutNotJobCancellation。Go 隐式平台约束与文件命名陷阱Go 会从文件名后缀推断隐式 GOOS 约束*_windows_test.go或_linux、_darwin只在对应平台编译。关于 Windows 的、且平台无关的测试不要命名为*_windows_test.go——否则它在其他平台被静默跳过造成只有 Windows 上才跑的虚假覆盖。仓库中cmd/recordfixture/opencode_unix_test.go、internal/daemon/service_systemd_test.go、internal/winproc/harden_windows_test.go等平台专属文件即按此约定组织。平台陷阱macOS 上 git 段错误与无视超时的游离进程最后一条约定是排障指导针对两个容易误判的现象macOS 上 git 密集包在-race下偶发git cmd: signal: segmentation fault。这不是 git 或仓库的 bug~/Library/Logs/DiagnosticReports/*.ips会记录崩溃为procName: pkg.test, parentProc: pkg.test, asi: crashed on child side of fork pre-exec——fork 出的子进程在execve前就死了。先确认这个签名再决定是否去 Go 代码里排查CI 跑在 Linux 与 Windows 上不受此影响。一个pkg.test -test.timeout...进程在高 CPU 下看似无视自身 deadline。同样的 fork 机制可以解释pre-exec 子进程继承了父进程的名称、argv 与 cwd它并不是正在运行的测试二进制测试侧超时对它不适用。internal/procreap正是通过 cwd 来回收这类进程的见internal/procreap/cwd_linux.go与internal/procreap/proc_unix.go中的 cwd 读取逻辑。把约定落进日常工作无论你是为 no-mistakes 贡献代码的开发者还是在该仓库上执行修改任务的 Agent都可以把这份测试约定当作决策清单新行为跨进程/IO 边界放 e2ee2e标签 internal/e2e/...或 step 旁并确保临时守护进程走e2edaemon所有权。新包会启动守护进程或触碰环境抄cmd/no-mistakes、internal/cli的包级TestMain模式临时NM_HOME/HOME 关遥测/更新检查。新包会 shell 出 gitTestMain里 unsetGIT_CONFIG_COUNT。新 step 测试需要gh/glab/git复用internal/pipeline/fakecli由stepstest.Init构建。改动涉及 Windows CI 分片遵循windows-steps/windows-git/windows-core三分片与NM_CI_WINDOWS_GIT_EXCLUDE机制别把 git 密集包放回串行关键路径。遇到诡异平台现象先对照本约定的 macOS fork 陷阱与清单回收边界再用internal/procreap与 DiagnosticReports 定位。这些约定并非抽象教条而是与 scripts/e2e.sh、internal/e2edaemon、internal/e2e/harness.go、internal/paths/paths.go、internal/pipeline/fakecli/main.go 等源码一一对应的可验证工程实践——阅读源码可以确认每一条规则的来龙去脉。【免费下载链接】no-mistakesgit push no-mistakes项目地址: https://gitcode.com/GitHub_Trending/no/no-mistakes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考