☰
Repowise Coverage 完全指南:测试覆盖率报告接入、补丁覆盖率门禁与 per-test 映射实战
2026/10/9 1:48:20 网站建设 项目流程

【免费下载链接】repowise

Codebase intelligence for AI and humans: code health scores, auto-generated docs, git analytics, dead code detection, and architectural decisions via MCP.

项目地址:https://gitcode.com/gh_mirrors/re/repowise
点击查看免费下载

导读:本文以 Repowise 的coverage命令组(plugins/shared/commands/coverage.md)为骨架,完整讲解如何把 LCOV、Cobertura、Clover、Go cover profile、JaCoCo、coverage.py.coverage等报告摄入本地索引,如何让覆盖率点亮repowise health的未测试热点标记、为repowise impacted-tests构建测试到代码的映射,以及如何在 CI 中用repowise coverage check对一次变更的补丁覆盖率(patch coverage)进行门槛判定。读完你将掌握coverage status / add / check的完整命令面、.repowise/config.yaml中coverage:块的每一个配置键,以及基于源码实现的底层原理。

一、命令定位:一条纯摄入、零 LLM 的覆盖率链路

Repowise 的coverage命令组是覆盖率报告的唯一入口(源码见 coverage_cmd.py 的模块 docstring):

  • coverage add摄入按文件的行/分支覆盖率,为已测试文件清除untested_hotspot(未测试热点)标记;
  • 当报告携带**上下文(contexts)**时,额外构建per-test 的 "test-to-code" 映射,这正是repowise impacted-tests回答"哪些测试覆盖了这次改动"的数据基础;
  • repowise health在评分时直接折叠已摄入的覆盖率——它不再自带 coverage 参数(源码注释明确:"It no longer takes a coverage flag of its own",见 health_cmd/command.py);
  • coverage check在 CI 中对变更做补丁覆盖率门槛判定,不需要索引、不需要 API Key。

整条链路是纯报告摄入(pure report ingestion),不调用任何 LLM,摄入结果落在本地索引中。

二、前置条件:先 init,再谈覆盖率

执行任何coverage子命令前,仓库必须已被索引:

  1. 如果.repowise/不存在,命令会提示This repo isn't indexed yet. Run repowise init first.并停止;
  2. 源码中的实际校验更具体:coverage add在数据库里找不到仓库行或找不到任何已索引文件时,会分别提示No index yet — run repowise init once before adding coverage.与No indexed files found — run repowise init first.(见 coverage_cmd.py)。

三、三种运行模式($ARGUMENTS分发)

默认/status模式——查看已摄入内容:

repowise coverage status

根据参数分发到不同子命令:

$ARGUMENTS实际执行说明
status/showrepowise coverage status展示已摄入的行/分支覆盖率与 per-test 映射状态
报告路径(coverage.lcov、lcov.info、coverage.xml、coverage.out、jacoco.xml、.coverage等)repowise coverage add <path>摄入单个报告
add且无路径repowise coverage add自动发现常见报告路径
多个路径repowise coverage add <a> <b>合并摄入,命中优先(hit wins)
check/gate/patch coveragerepowise coverage check [REVSPEC]补丁覆盖率门槛判定

coverage add的常用旗标:

  • --verbose:打印摄入管线的调试日志;
  • --path <dir>:指向不同的仓库(默认为当前目录或 workspace 主仓库);
  • --format <parser>:强制指定解析器,而不是从内容自动嗅探。

四、coverage add深入:格式、自动发现、路径解析与 per-test 映射

4.1 支持的报告格式(解析器注册表)

格式嗅探与解析在 detector.py 的PARSERS注册表中统一注册,--format的可选值即该表键:

解析器键典型报告文件说明
lcovcoverage/lcov.info、lcov.infolcov / nyc / c8 /cargo llvm-cov输出
coberturacoverage.xmlCobertura XML
cloverclover.xmlClover XML
repowise-json任意 JSON(json是别名)见 4.2 的归一化 JSON 结构
go-coverprofilecoverage.outgo test -coverprofile;语句块会被展开为行,百分比按行计算
jacocojacoco.xml读取逐行<sourcefile>数据;包路径(如com/foo/Bar.java)按后缀匹配映射到仓库文件

格式由detect_format通过内容嗅探(JSON 以{开头、XML 以<开头、mode:开头为 Go profile、TN:/SF:开头为 lcov),因此无需按扩展名猜测。解析器只依赖 Python 标准库,不引入 XML 三方库。

4.2 归一化 JSON:repowise-coverage-v1

为让任意覆盖率工具的输出只需归一化一次即可喂给coverage add,项目定义了显式 JSON 形状(见 coverage/README.md):

{ "format": "repowise-coverage-v1", "commit_sha": "abc123", "files": { "src/foo.py": { "line_coverage_pct": 87.5, "branch_coverage_pct": 70.0, "covered_lines": [1, 2, 5], "total_coverable_lines": 40 } } }
  • files也可以是对象列表,每个对象自带file_path;
  • 容错规则:line_coverage_pct、covered_lines、total_coverable_lines三者任意两个即可锚定一个文件,三者皆缺的条目被跳过(缺省 ≠ 0);
  • 文件键统一为仓库相对、正斜杠 POSIX路径。

4.3 自动发现(auto-discovery)

不带路径执行repowise coverage add时,会按默认 glob 扫描文件系统(coverage/lcov.info、.coverage、**/cobertura.xml等),并额外查找仓库根下的.coverage文件(这是唯一能仅凭文件名识别的 per-test 产物,见 coverage_cmd.py)。发现数量有硬上限_MAX_ARTIFACTS = 50(discovery.py),同时会修剪 vendored 目录。什么都没发现时命令打印生成建议(如coverage run --contexts=test -m pytest或cargo llvm-cov --lcov --output-path coverage/lcov.info)并以非零码退出。

4.4 路径解析:最长尾段重叠 + 命中优先合并

几乎没有任何覆盖率工具直接输出 repowise 的规范键(仓库相对 POSIX 路径):lcov / nyc / c8 / cargo-llvm-cov 写绝对路径,Cobertura 写相对自身<source>根的路径。build_coverage_map的路径解析采用最长尾段重叠(longest trailing-segment overlap)匹配,真正的平局时拒绝猜测;多个报告合并时命中优先(hit-wins)。解析结果携带matched/unmatched/ambiguous诊断信息,保证覆盖率绝不会静默显示为 0%(见 coverage/README.md)。

CLI 层会打印Ingested coverage for N file(s) (M exact, K resolved),并单独提示未能映射到仓库树的报告文件;当超过一半报告未映射时会以红色醒目提示这是"部分摄入",仅描述仓库的一个片段,需要修复coverage.strip_prefix/coverage.path_prefix后重跑。

4.5 参数与进阶旗标

repowise coverage add # 发现 lcov.info、.coverage ... repowise coverage add coverage/lcov.info repowise coverage add .coverage # 由 coverage.py 构建 per-test 映射 repowise coverage add web.lcov api.lcov # 合并,命中优先 repowise coverage add 'artifacts/**/lcov.info' repowise coverage add web/coverage/lcov.info=web # PATH=PREFIX 为报告前缀补路径
  • --strict:当部分报告文件未能映射到仓库树时同样失败退出;
  • 退出码语义:什么都没存进去就退出非零,因此repowise coverage add ... || exit 1能把"完整摄入"和"空操作"区分开;--strict下未映射文件也触发失败(见 coverage_cmd.py);
  • PATH=PREFIX语法相当于coverage.paths里一条{path, path_prefix}条目,为报告内所有路径前置前缀;
  • 摄入时用get_head_commit打上实时 HEAD的时间戳(而不是索引里的repo_row.head_commit),因为覆盖率描述的是工作树,避免提交后未 update 导致标签错位(源码注释提及 issue #1747,见 coverage_cmd.py)。

4.6 per-test 映射:只在报告携带上下文时构建

这是原文档强调的核心差异点:

  • coverage add总是存储按文件的行/分支覆盖率;
  • per-test 映射(test-to-code map)只在报告携带 per-test 上下文时构建:
    • coverage.py 的.coverage,需以coverage run --contexts=test生成(SQLite 二进制,经魔数SQLite format 3\x00识别,不进入文本解析路径);
    • 或一份 per-test lcov(带TN段)。
  • 不带上下文的报告只摄入聚合数据,命令会明确说明这一点;对.coverage且无上下文的情况,还会专门提示no per-test contexts (ran without --contexts=test); per-test map skipped.。

构建成功后打印Built the test-to-code map: N test->file record(s).,若存在重复或超上限(MAX_TEST_COVERAGE_ROWS)会标注丢弃数。repowise coverage status会分别展示聚合覆盖率(文件数、行/分支百分比、匹配报告比例、新鲜度)与映射(测试数、源文件数、记录数)。

五、配置文件:.repowise/config.yaml的coverage:块

全部键可选,默认值即可零配置自动发现(repowise init/repowise update期间生效),由CoverageConfig.from_repo_config()解析(discovery.py):

coverage: auto_discover: true # 索引期间发现报告 artifacts: # 覆盖默认发现 glob - coverage/lcov.info paths: # 显式报告路径或 glob(跳过发现) - build/coverage/lcov.info - {path: "api/**/lcov.info", path_prefix: api} # 按报告前缀 format: lcov # 强制解析器(否则按内容嗅探) strip_prefix: build # 从报告路径去掉前导前缀再匹配 path_prefix: packages/web # 匹配前给报告路径前置前缀 ignore: ["gen/"] # gitignore 风格 glob,覆盖率排除项 reingest_on_update: false # 每次 update 重新解析(默认复用 DB 行) fail_under: 80 # `coverage check` 的补丁覆盖率门槛 min_coverable_lines: 5 # 小改动容忍度 fail_under_risky: 85 # 仅对高风险文件的更严门槛 max_drop: 0.5 # 与基线相比项目覆盖率最多允许下降的点数 fail_under_branches: 90 # 变更行分支覆盖率门槛 gates: # 路径作用域门禁(PathGate) - {name: api, paths: ["/services/api/"], fail_under: 85}

关键语义:

  • coverage.paths非空时跳过发现,{path, path_prefix}映射条目为每个匹配报告覆盖全局path_prefix;
  • coverage.ignore在计算补丁覆盖率前排除变更文件、在存储前排除报告条目;
  • 每条gates由name、paths(gitignore 风格 glob,至少一个非!排除)、可选fail_under(0-100)、可选informational组成;合法条目解析为PathGate,非法条目留下gate_errors消息,coverage check见到任何错误消息都拒绝运行;
  • 配置值不可用时门禁会主动失败而非静默失效:例如coverage.fail_under不是 0-100 数字时coverage check以config_invalid退出码 2 停止(见 coverage_check_cmd.py)。

六、CI 门禁:repowise coverage check

coverage check报告的是补丁覆盖率——变更中可执行行被测试运行到的比例。它只需 git 和一份覆盖率报告,不需要索引、不需要 API Key,因此天然适合 CI 运行。

6.1 REVSPEC:描述"这次变更"

  • origin/main...HEAD:分支分叉以来做了什么(PR 视角,推荐);
  • base..head:区间 diff;
  • 单个 commit;
  • 缺省时,基线来自 CI 的 PR 变量(GitHub、GitLab、Jenkins、Bitbucket),否则取远端默认分支的...HEAD。

6.2 基础用法与选项

repowise coverage check origin/main...HEAD --report coverage/lcov.info --fail-under 80
选项说明
--report FILE可重复;路径或 glob(如artifacts/**/lcov.info),相对 cwd,多个报告命中优先合并;省略时按coverage.paths配置,否则自动发现
--fail-under PCT低于该百分比退出 1;默认取coverage.fail_under(0-100)
--report-format <parser>强制解析器,替代内容嗅探
--format github在 GitHub Actions 中输出::error::注释并写入 step summary($GITHUB_STEP_SUMMARY)
--format json/--format markdown供其他消费者使用;table为默认终端格式

其他格式变体:--format github是CI_FORMATS = ("table", "json", "markdown", "github")之一,所有门禁命令共用(ci.py)。

6.3 退出码约定(0 / 1 / 2)

  • 0:门禁通过,或"无可判定内容"(含小改动容忍场景);
  • 1(EXIT_GATE_FAILED):补丁覆盖率低于--fail-under;某个非 informational 的路径作用域门禁失败;风险文件的覆盖率低于--fail-under-risky;变更行分支覆盖率低于--fail-under-branches;项目覆盖率较基线跌幅超过--max-drop;
  • 2(EXIT_CANNOT_EVALUATE):门禁无法运行——没有报告、--report匹配不到文件、报告不可读、未知修订、历史缺失、配置非法、浅克隆下使用--fail-under-risky、报告无逐行分支数据却要求--fail-under-branches、--max-drop缺少基线测量等。

6.4 小改动容忍与高风险文件门禁

  • --min-coverable-lines N:变更的可执行行数少于 N 时,照常报告但不让门禁失败(默认coverage.min_coverable_lines),避免"几行改动被 80% 门槛误杀";
  • --fail-under-risky PCT:单独给风险文件设门槛——有索引时指热点(hotspot)、bug magnet 及其依赖计数;仅凭 git 时指 bug 修复历史最多的前四分之一文件。没有风险文件被改动则本门禁不生效;浅克隆(shallow clone)无法信任 bug 修复历史,此时该门禁以history_shallow退出 2,提示fetch-depth: 0或git fetch --unshallow;
  • --fail-under-branches PCT:变更行的分支覆盖率门槛,与补丁覆盖率并列展示、绝不混算;变更行不含分支行则不判定,报告完全没有逐行分支数据时退出 2。

6.5 项目覆盖率回退检测

  • --base-report FILE:提供在变更基线提交(A...B的 merge-base)处测得的报告,用于对比项目覆盖率,并列出本次变更之外覆盖率发生变化(被间接影响)的文件,展示 Before / After / Newly uncovered lines / Cause;
  • --max-drop PCT:项目覆盖率较基线跌幅超过该点数即失败。基线的另一种来源是索引中在基线提交处摄入的覆盖率(仅有总量,无逐文件明细)。缺少可用基线时,若由旗标显式要求则退出 2,仅由配置要求则降级为提示注记。

6.6 索引增强:风险列、测试建议与提示

有索引时,coverage check输出会进一步增强:

  • 每个变更文件带上风险(git bug 修复历史 + 索引中的 hotspot / bug magnet / 依赖计数),表格按风险从高到低排序;
  • 每个未覆盖区间会指名要扩展哪个测试文件(Extend列);这些 hint 只是建议,索引缺失或损坏绝不改变门禁裁决;
  • 报告路径默认按git ls-files解析,因此本次变更新增的文件也能解析——而缓存自基线分支的索引并不知道它(见 coverage_check_cmd.py 的模块 docstring)。

七、与health/impacted-tests的联动

摄入完成后建议执行repowise health:未测试热点标记会基于新覆盖率刷新。链路为:

coverage run --contexts=test -m pytest # 生成带上下文的报告 repowise coverage add .coverage # 摄入聚合 + 构建 test-to-code 映射 repowise health # 热点标记按覆盖率刷新 repowise impacted-tests <diff> # 用 per-test 映射回答"哪些测试覆盖此次改动"

impacted-tests在映射为空时(map_empty)、陈旧时(measured 不在 change.base/change.head 上)会明确提示重新运行repowise coverage add,绝不凭空编造空映射(见 impacted_tests_cmd.py)。因此,想让 impacted-tests 给出逐行精确答案,就必须用带上下文的报告摄入。

八、路径作用域门禁与建议生成:suggest-gates

repowise coverage suggest-gates依据CODEOWNERS、仓库顶层包(git ls-files分析)、有索引时的图社区三类来源,输出一段可直接粘贴到coverage:行下方的 YAML 门禁块(只输出建议、绝不写配置、不设阈值):

repowise coverage suggest-gates repowise coverage suggest-gates --format json

粘贴后为每个门禁补上fail_under,即得到按模块/团队拆分的路径作用域门禁(如 API 模块 85%、服务目录 90%)。前两类来源不需要索引。

九、端到端工作流示例

本地:先索引,再摄入带上下文的覆盖率,然后查看状态:

repowise init coverage run --contexts=test -m pytest --cov=. --cov-report=lcov -o coverage/lcov.info repowise coverage add coverage/lcov.info .coverage --verbose repowise coverage status repowise health

CI(GitHub Actions):无需索引、无需 API Key,直接对 PR 门禁:

repowise coverage check origin/main...HEAD \ --report coverage/lcov.info \ --fail-under 80 \ --fail-under-risky 90 \ --fail-under-branches 85 \ --base-report base/coverage/lcov.info \ --max-drop 0.5 \ --format github

该命令 0 = 通过,1 = 门禁失败,2 = 无法判定(多半是浅克隆或报告缺失)。若仓库已有索引,check甚至可以在磁盘上没有报告时读取索引中存储的覆盖率——但前提是该覆盖率在变更 HEAD 处测得且携带可执行行数据,否则以coverage_stale/no_line_data拒绝判定。

十、结语

Repowise 的覆盖率体系把三件事收敛到一个命令组里:摄入(coverage add,纯报告解析、零 LLM)、检视(coverage status)、门禁(coverage check,仅需 git + 报告)。配合coverage.py --contexts=test与 per-test lcov,它能同时服务两个下游——health的未测试热点标记与impacted-tests的测试到代码映射;配合.repowise/config.yaml的coverage:块与suggest-gates,团队可以把"补丁覆盖率不低于 80%、风险文件不低于 90%、项目覆盖率不倒退"沉淀为可复用的 CI 策略。想深入了解实现细节,可继续阅读 coverage_cmd.py、coverage_check_cmd.py 与 coverage 子包 README。

【免费下载链接】repowise

Codebase intelligence for AI and humans: code health scores, auto-generated docs, git analytics, dead code detection, and architectural decisions via MCP.

项目地址:https://gitcode.com/gh_mirrors/re/repowise
点击查看免费下载
上一篇:D2RML终极指南:暗黑2重制版多开工具完全解析
下一篇:终极Checkpoint开发者指南:从源码编译到功能扩展的完整步骤

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

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

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

立即咨询