Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ build:windows --host_cxxopt=/std:c++17
# Avoid cache thrashing, but allow integration tests to find "bazel" on the PATH.
common --incompatible_strict_action_env
common --test_env=PATH

# Coverage runs use the Rust LCOV merger from //tools/coverage instead of
# Bazel's built-in one. Besides merging each test's raw tracefiles into its
# coverage.dat, it enforces the per-target line-coverage minimums declared
# via //tools/coverage:defs.bzl (targets opt in through their `env` attr).
# Scoped to the `coverage` command, and Bazel only invokes an LCOV merger in
# coverage mode anyway, so plain `bazel test` runs are never affected.
coverage --coverage_output_generator=//tools/coverage:lcov_merger
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
if [ -n "${BAZEL_OUTPUT_USER_ROOT:-}" ]; then
BAZEL_STARTUP=(--output_user_root="${BAZEL_OUTPUT_USER_ROOT}")
fi
~/go/bin/bazelisk "${BAZEL_STARTUP[@]}" coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/go/... --enable_bzlmod=true --enable_workspace=false
~/go/bin/bazelisk "${BAZEL_STARTUP[@]}" coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/coverage/... //tools/go/... --enable_bzlmod=true --enable_workspace=false
- name: Upload coverage report
uses: actions/upload-artifact@v4
if: always()
Expand Down
33 changes: 32 additions & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ bazel_dep(name = "rules_proto", version = "7.1.0")
bazel_dep(name = "rules_java", version = "9.7.0")
bazel_dep(name = "rules_kotlin", version = "2.4.0")
bazel_dep(name = "rules_license", version = "1.0.0")

# kt_jvm_test hardcodes its _lcov_merger to Bazel's built-in merger instead of
# reading the coverage fragment's output_generator configuration field, so it
# ignores --coverage_output_generator (see tools/coverage/README.md — that flag
# is how per-target coverage minimums are enforced). Patch it to use the
# configuration field, like rules_go/rules_rust/rules_java do. Root-module-only:
# overrides are ignored when bazel-diff is consumed as a dependency.
single_version_override(
module_name = "rules_kotlin",
patch_strip = 1,
patches = ["//tools/coverage:rules_kotlin_lcov_merger.patch"],
)

bazel_dep(name = "rules_jvm_external", version = "6.10")

# Add protobuf and grpc for Bazel 9 compatibility
Expand All @@ -39,11 +52,29 @@ bazel_dep(name = "rules_python", version = "1.8.4", dev_dependency = True)
# whose line coverage is gated at >=90% in CI). Marked dev_dependency so consumers
# of bazel-diff as a module don't inherit rules_go/gazelle via MVS.
bazel_dep(name = "rules_go", version = "0.60.0", dev_dependency = True)
bazel_dep(name = "gazelle", version = "0.45.0", dev_dependency = True)
bazel_dep(name = "gazelle", version = "0.47.0", dev_dependency = True)

go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk", dev_dependency = True)
go_sdk.download(version = "1.23.1")

# Rust support is internal to building/testing bazel-diff: it builds the LCOV
# merger (//tools/coverage) that `bazel coverage` runs use to merge per-test
# tracefiles and enforce per-target coverage minimums. Marked dev_dependency
# so consumers of bazel-diff as a module don't inherit rules_rust via MVS.
bazel_dep(name = "rules_rust", version = "0.73.0", dev_dependency = True)

rust = use_extension("@rules_rust//rust:extensions.bzl", "rust", dev_dependency = True)
rust.toolchain(
edition = "2021",
versions = ["1.90.0"],
)
use_repo(rust, "rust_toolchains")

register_toolchains(
"@rust_toolchains//:all",
dev_dependency = True,
)

maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
name = "bazel_diff_maven",
Expand Down
992 changes: 493 additions & 499 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ generate-readme:

.PHONY: coverage
coverage:
bazel coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/go/...
bazel coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/coverage/... //tools/go/...
bazel run //tools:coverage-check -- bazel-out/_coverage/_coverage_report.dat
bazel run //tools:coverage-check -- --include tools/go/ --threshold 90 bazel-out/_coverage/_coverage_report.dat

Expand All @@ -39,6 +39,6 @@ coverage-test:

.PHONY: coverage-html
coverage-html:
bazel coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/go/...
bazel coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/coverage/... //tools/go/...
bazel run //tools:coverage-check -- bazel-out/_coverage/_coverage_report.dat --html coverage-html
@echo "Open coverage-html/index.html in a browser to inspect."
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ make coverage
```

This invokes
`bazel coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/go/...`
`bazel coverage --combined_report=lcov //cli/... //tools:coverage_check_test //tools/coverage/... //tools/go/...`
and then runs `//tools:coverage-check` twice against the resulting LCOV report — once for
the Kotlin main sources and once scoped to `tools/go/` (`--include tools/go/`). The check is
a Python `py_binary` ([`tools/coverage_check.py`](tools/coverage_check.py)) that prints a
Expand All @@ -925,6 +925,34 @@ If you've already produced a coverage report and just want to re-check the thres
The enforcement logic itself is tested under `//tools:coverage_check_test` — run it
directly with `make coverage-test` (or `bazel test //tools:coverage_check_test`).

### Per-target coverage minimums

In addition to the repo-wide gate above, individual test targets declare their own
line-coverage minimums, enforced *during* the coverage run itself by a Rust LCOV
merger ([`tools/coverage/`](tools/coverage/)) that replaces Bazel's built-in one
(`coverage --coverage_output_generator=//tools/coverage:lcov_merger` in `.bazelrc`).
Bazel only invokes the merger for `bazel coverage`, so plain `bazel test` runs are
unaffected. A target opts in through its `env` attribute via
`//tools/coverage:defs.bzl`:

```starlark
load("//tools/coverage:defs.bzl", "coverage_enforced_test")

coverage_enforced_test(
rule = go_test, # any test rule with the standard `env` attribute
name = "sample_test",
coverage_include = ["tools/go/"],
...
)
```

The default minimum is 90%. Go (`//tools/go/sample:sample_test`), Rust
(`//tools/coverage:lcov_merger_test`) and the primary-owner Kotlin/JVM tests
under `//cli` all carry such minimums. When a target's merged report falls
below its minimum, the coverage run fails that target and the test log
contains a per-file breakdown. See
[`tools/coverage/README.md`](tools/coverage/README.md) for details.

For an interactive HTML report (annotated source with covered/uncovered lines
highlighted), use `make coverage-html`. This requires the `lcov` package
(`brew install lcov` on macOS, `apt-get install lcov` on Debian/Ubuntu) and writes
Expand Down
Loading
Loading