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:
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,