rhi

Coordinated trade for connected markets
git clone https://radroots.dev/git/rhi.git
Log | Files | Refs | README | LICENSE

commit 408a8f874643f245e6d992f043bd6e9b79289f77
parent a993592640d581277b5bf9c6eae36682cebbe2a2
Author: triesap <tyson@radroots.org>
Date:   Tue,  3 Mar 2026 22:42:01 +0000

ci: enforce required coverage surface and prune unused deps

Diffstat:
MCargo.lock | 195-------------------------------------------------------------------------------
MCargo.toml | 3---
MMakefile | 3++-
Mcontract/coverage/POLICY.md | 1+
Acontract/coverage/include.txt | 5+++++
Mscripts/ci/verify_coverage.py | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/features/trade_listing/handlers/dvm.rs | 1-
7 files changed, 59 insertions(+), 200 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -685,17 +685,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -716,7 +705,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", - "futures-macro", "futures-sink", "futures-task", "memchr", @@ -781,25 +769,6 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -901,12 +870,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] name = "hyper" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -916,11 +879,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", "pin-utils", @@ -1215,82 +1176,6 @@ dependencies = [ ] [[package]] -name = "jsonrpsee" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" -dependencies = [ - "jsonrpsee-core", - "jsonrpsee-server", - "jsonrpsee-types", - "tokio", -] - -[[package]] -name = "jsonrpsee-core" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "jsonrpsee-types", - "parking_lot", - "pin-project", - "rand 0.9.0", - "rustc-hash", - "serde", - "serde_json", - "thiserror 2.0.12", - "tokio", - "tower", - "tracing", -] - -[[package]] -name = "jsonrpsee-server" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f" -dependencies = [ - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "route-recognizer", - "serde", - "serde_json", - "soketto", - "thiserror 2.0.12", - "tokio", - "tokio-stream", - "tokio-util", - "tower", - "tracing", -] - -[[package]] -name = "jsonrpsee-types" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" -dependencies = [ - "http", - "serde", - "serde_json", - "thiserror 2.0.12", -] - -[[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1641,26 +1526,6 @@ dependencies = [ ] [[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2031,7 +1896,6 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "jsonrpsee", "radroots-core", "radroots-events", "radroots-events-codec", @@ -2039,13 +1903,11 @@ dependencies = [ "radroots-nostr", "radroots-runtime", "radroots-trade", - "reqwest", "serde", "serde_json", "thiserror 1.0.69", "tokio", "tracing", - "uuid", ] [[package]] @@ -2075,12 +1937,6 @@ dependencies = [ ] [[package]] -name = "route-recognizer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" - -[[package]] name = "rust-ini" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2365,22 +2221,6 @@ dependencies = [ ] [[package]] -name = "soketto" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures", - "http", - "httparse", - "log", - "rand 0.8.5", - "sha1", -] - -[[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2618,18 +2458,6 @@ dependencies = [ ] [[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] name = "tokio-tungstenite" version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2646,20 +2474,6 @@ dependencies = [ ] [[package]] -name = "tokio-util" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] name = "toml" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2966,15 +2780,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "uuid" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" -dependencies = [ - "getrandom 0.3.2", -] - -[[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -33,11 +33,8 @@ radroots-trade = { workspace = true } anyhow = { version = "1" } clap = { version = "4", features = ["derive"] } -jsonrpsee = { version = "0.26", features = ["server"] } -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } serde = { version = "1", default-features = false } serde_json = { version = "1", default-features = false } tokio = { version = "1", features = ["full"] } thiserror = { version = "1" } tracing = { version = "0.1" } -uuid = { version = "1.16.0", features = ["v4"] } diff --git a/Makefile b/Makefile @@ -2,6 +2,7 @@ COVERAGE_OUTPUT_DIR := target/coverage COVERAGE_SUMMARY := $(COVERAGE_OUTPUT_DIR)/summary.json COVERAGE_LCOV := $(COVERAGE_OUTPUT_DIR)/lcov.info COVERAGE_THRESHOLDS := contract/coverage/thresholds.toml +COVERAGE_INCLUDE := contract/coverage/include.txt .PHONY: coverage-report coverage-gate @@ -16,4 +17,4 @@ coverage-report: @echo "coverage lcov: $(COVERAGE_LCOV)" coverage-gate: coverage-report - python3 scripts/ci/verify_coverage.py --thresholds $(COVERAGE_THRESHOLDS) --summary $(COVERAGE_SUMMARY) --lcov $(COVERAGE_LCOV) + python3 scripts/ci/verify_coverage.py --thresholds $(COVERAGE_THRESHOLDS) --summary $(COVERAGE_SUMMARY) --lcov $(COVERAGE_LCOV) --include $(COVERAGE_INCLUDE) diff --git a/contract/coverage/POLICY.md b/contract/coverage/POLICY.md @@ -24,6 +24,7 @@ all thresholds are merge-blocking and release-blocking. - evaluate coverage for the repository crate, not only aggregated workspace totals - fail hard when any required metric is below threshold, including regions - fail hard when required branch records are missing +- fail hard when required covered files in `contract/coverage/include.txt` are absent from summary output ## local and ci contract diff --git a/contract/coverage/include.txt b/contract/coverage/include.txt @@ -0,0 +1,5 @@ +# required source files that must appear in coverage summary output +src/adapters/nostr/event.rs +src/features/trade_listing/state.rs +src/lib.rs +src/main.rs diff --git a/scripts/ci/verify_coverage.py b/scripts/ci/verify_coverage.py @@ -14,6 +14,7 @@ def parse_args() -> argparse.Namespace: parser.add_argument("--thresholds", required=True, type=pathlib.Path) parser.add_argument("--summary", required=True, type=pathlib.Path) parser.add_argument("--lcov", required=True, type=pathlib.Path) + parser.add_argument("--include", required=False, type=pathlib.Path) return parser.parse_args() @@ -42,6 +43,45 @@ def read_totals(path: pathlib.Path) -> dict[str, object]: return totals +def read_covered_files(path: pathlib.Path) -> set[str]: + payload = json.loads(path.read_text(encoding="utf-8")) + data = payload.get("data") + if not isinstance(data, list) or not data: + raise ValueError("coverage summary missing data entries") + files = data[0].get("files") + if not isinstance(files, list): + raise ValueError("coverage summary missing files") + + repo_root = pathlib.Path.cwd().resolve() + covered: set[str] = set() + for entry in files: + if not isinstance(entry, dict): + continue + raw_name = entry.get("filename") + if not isinstance(raw_name, str): + continue + filename = pathlib.Path(raw_name) + if filename.is_absolute(): + try: + rel = filename.resolve().relative_to(repo_root) + covered.add(rel.as_posix()) + continue + except ValueError: + pass + covered.add(filename.as_posix()) + return covered + + +def read_required_files(path: pathlib.Path) -> set[str]: + required: set[str] = set() + for raw_line in path.read_text(encoding="utf-8").splitlines(): + line = raw_line.strip() + if not line or line.startswith("#"): + continue + required.add(pathlib.Path(line).as_posix()) + return required + + def metric_percent(totals: dict[str, object], metric: str) -> float: metric_obj = totals.get(metric) if not isinstance(metric_obj, dict): @@ -78,6 +118,7 @@ def main() -> int: args = parse_args() thresholds = read_thresholds(args.thresholds) totals = read_totals(args.summary) + covered_files = read_covered_files(args.summary) checks = [ ("lines", metric_percent(totals, "lines"), thresholds["line_percent"]), @@ -111,6 +152,15 @@ def main() -> int: if lcov_branches <= 0: errors.append("lcov report has no branch records") + if args.include is not None: + required_files = read_required_files(args.include) + missing = sorted(required_files - covered_files) + if missing: + errors.append( + "summary is missing required covered files: " + + ", ".join(missing) + ) + print( "coverage totals: " f"lines={metric_percent(totals, 'lines'):.4f}% " @@ -118,6 +168,7 @@ def main() -> int: f"branches={metric_percent(totals, 'branches'):.4f}% " f"regions={metric_percent(totals, 'regions'):.4f}%" ) + print(f"coverage files counted: {len(covered_files)}") if errors: for error in errors: print(f"coverage gate error: {error}", file=sys.stderr) diff --git a/src/features/trade_listing/handlers/dvm.rs b/src/features/trade_listing/handlers/dvm.rs @@ -1254,7 +1254,6 @@ fn ensure_transition( } } -#[cfg_attr(coverage_nightly, coverage(off))] pub async fn handle_error( error: TradeListingDvmError, event: &RadrootsNostrEvent,