fix(cli): make coverage add exit non-zero when it stores nothing - #1751
Conversation
|
✅ Health of changed files: 6.7 → 7.2 (+0.4) 📋 At a glance ✅ Health gate: passed 📌 Before you merge
🔎 More signals (2)🗺️ Change map flowchart LR
subgraph PR ["Changed in this PR (1 with dependents)"]
f_packages_cli_src_repowise_cli_commands_coverage_cmd_py[".../commands/coverage_cmd.py 🔥"]:::changed
end
t_tests_unit_cli_test_format_json_rollout_py(["✅ .../cli/test_format_json_rollout.py"]):::guard
t_tests_unit_cli_test_format_json_rollout_py -.-> f_packages_cli_src_repowise_cli_commands_coverage_cmd_py
classDef changed fill:#dbeafe,stroke:#1d4ed8,color:#1e3a5f
classDef warn fill:#fef3c7,stroke:#b45309,color:#78350f
classDef guard fill:#dcfce7,stroke:#15803d,color:#14532d
Solid arrows: code that imports the changed files (0 direct dependents, from the last indexed snapshot). Dashed: history/tests. 🔥 Hotspot touched (1)
👀 Suggested reviewers @RaghavChamadiya 📊 See the full report for this PR |
|
Ran the dependent suite the bot flagged: It touches |
|
Thanks @NgoQuocViet2001, and thanks for asking on the issue before picking a side on the partial case rather than guessing. Two things you got right that I want to name. Returning a bool from To answer your question: keep One thing I would like fixed before merge, because it is in the new flag's main use case.
if resolved.files:
await save_coverage_files(...)
agg_matched = resolved.matched
console.print(...)
skipped = resolved.unmatched + resolved.ambiguous
unmapped = len(skipped)So when the aggregate leg maps nothing at all, Hoisting the count out of the Two non-blocking notes, take them or leave them: The strict failure prints after the success guidance, so a failing The second test monkeypatches On CI: it has not run the matrix here, and that is not your doing. Main has been red since #1755 for an unrelated reason, two generated files edited at their output instead of their source. #1762 fixes it. Once that lands, rebase on main and I will approve the workflow run so you get a clean signal. Ping me when it is rebased with the |
|
#1762 is merged, so main is green again and the rebase I mentioned is unblocked. Once that is pushed with the |
A refresh is commonly scripted as `repowise coverage add ... || exit 1`, but every no-op path returned 0: no report discovered, no index, no indexed files, and nothing mapped all printed a message and returned. CI recorded a successful refresh while the coverage store was unchanged. _do() now answers whether anything was stored and the command turns that into the exit status. The discovery miss raises directly, since it happens before the ingest starts. Partial ingests keep exiting 0 by default -- a report that maps most of its files is still a real refresh -- with a documented --strict for CI that wants to fail on any report file that did not map to the repo tree. Fixes repowise-dev#1745
The --strict count was read inside `if resolved.files:`, so the one run where every report file failed to map was also the run that saw zero unmapped files. Concretely: `coverage add --strict .coverage lcov.info` where the .coverage carries contexts and every path in lcov.info fails to map. The per-test map builds, so map_records is non-empty and the 'nothing mapped' branch is skipped; unmapped is 0, so the strict branch is skipped too; the command exits 0. Total mapping loss passing a strict run -- the flag's main use case. Count it from `resolved` whenever agg_paths ran, and report the strict failure before the success guidance so a failing run no longer advises `repowise health` and then says it failed. Reported by @RaghavChamadiya.
17c73a9 to
42f68bf
Compare
|
@RaghavChamadiya rebased on The hoist. You're right, and the repro you wrote out is the flag's main use case, which makes it worse than an edge case. for path, err in errors:
console.print(f"[yellow] {path.name}: {err}[/yellow]")
# Counted from `resolved`, not from inside the `resolved.files`
# branch below: total mapping loss leaves `files` empty, so
# reading the count in there reported 0 report files unmapped
# for the one run where every single one of them was.
skipped = resolved.unmatched + resolved.ambiguous
unmapped = len(skipped)
if resolved.files:
...The shape of the bug is worth naming: the count was only read on the success path, so the total-loss case was invisible precisely because it had nothing to succeed with. Both non-blocking notes taken. The strict check moved above the success guidance, so a failing run no longer says "Run On the second test — agreed, and I'd rather not leave that gap unmarked. The two new guards are explicitly structural, and say so in the docstring: they assert If you'd rather have a real behavioural test, it is doable by mocking the session plus On Verification
|
|
The Merging. Two things worth saying out loud rather than holding you for.
The exit-status change is a CLI contract change. Anyone running Thanks for coming back to this one. |
RaghavChamadiya
left a comment
There was a problem hiding this comment.
Verified against current main: applies clean, targeted tests green locally, CI green. Merging.
Fixes #1745.
The problem
Every no-op path through
coverage addprinted a message and returned, so the process exited 0:0repowise initnot run)000A refresh scripted as
repowise coverage add ... \|\| exit 1therefore could not tell a complete ingest from one that stored nothing, and CI recorded success over a stale or empty coverage store.The change
_do()now answers whether anything was stored, and the command turns that answer into the exit status. The discovery miss raises directly, since it happens before the ingest starts and has no_do()to report through.Partial ingests
Kept at 0 by default and put behind
--strict, which is the second option the issue offers. A report that maps most of its files is still a real refresh, and failing it by default would break every setup with a build-prefix mismatch it has already learned to live with — whereas a--strictrun is opt-in and says what it found:Happy to flip that to fail-by-default if you would rather; it is a one-line change and I asked on the issue before picking a side.
Not addressed here: the machine-readable requested/mapped/skipped/stored counts, and the
.coverage-contexts documentation ambiguity. Both are worth doing and neither belongs in an exit-status fix.Test plan
uv run pytest tests/unit/cli/test_coverage_cmd.py -q— 7 passed (3 pre-existing + 4 new).uv run pytest tests/unit/cli/ tests/unit/health/test_coverage_discovery.py -q— 2192 passed, 2 skipped, 2 xfailed.uv run ruff checkanduv run ruff format --checkon both changed files — clean, and the tree was already format-clean before the change.coverage_cmd.pyreverted, 3 of the new assertions fail — so they are pinned to the behaviour, not to the test setup.New coverage: the discovery miss exits 1 with its message intact, and a parametrised case pins the wiring in both directions (stored → 0, nothing stored → 1), which is the branch every one of the four no-op paths funnels through. Plus
--helpdocumenting--strict.