feat: freeze typed metrics registry and bounded exemplars

This commit is contained in:
2026-08-14 11:52:00 +03:00
parent 17c8e3a8f0
commit 8e709acea9
29 changed files with 1690 additions and 103 deletions
+7
View File
@@ -100,6 +100,13 @@ python3 scripts/check-config-boundaries.py --root .
committed machine contract. PostgreSQL DDL вне `crank-registry::migrations`
блокируется Rust module boundary check.
## Typed metrics contract
`just metrics-contract-check` сверяет Rust registry с versioned JSON snapshot и
проверяет, что production-код объявляет метрики только через `crank-metrics`.
Checker распознаёт прямые macro calls, imports, aliases и re-exports; новые
Rust-файлы можно передать явно через `--files`.
## `check-community-scope.sh`
Проверяет, что в community-репозиторий не попали функции и тексты за пределами
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import re
import subprocess
import sys
from pathlib import Path
MAX_FILES = 20_000
MAX_FILE_BYTES = 4 * 1024 * 1024
ALLOWED_ADAPTERS = {
"crates/crank-observability/src/instrumentation.rs",
"crates/crank-observability/src/prometheus.rs",
}
PATTERNS = (
re.compile(r"(?<![A-Za-z0-9_])(?:::)?metrics\s*::\s*(?:counter|gauge|histogram|describe_counter|describe_gauge|describe_histogram)\s*!"),
re.compile(r"\b(?:pub\s+)?use\s+(?:::)?metrics\s*(?:::\s*(?:\{|counter|gauge|histogram)|\s+as\s+)"),
re.compile(r"\bextern\s+crate\s+metrics(?:\s+as\s+\w+)?"),
re.compile(r"\b(?:set_global_recorder|PrometheusRecorder|DebuggingRecorder)\b"),
)
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Enforce crank-metrics ownership.")
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument("--files", nargs="*")
return parser.parse_args(argv)
def discover(root: Path) -> list[str]:
result = subprocess.run(
["git", "ls-files", "--cached", "--others", "--exclude-standard", "-z", "--", "apps/**/*.rs", "crates/**/*.rs"],
cwd=root,
check=True,
stdout=subprocess.PIPE,
)
return sorted(path.decode("utf-8") for path in result.stdout.split(b"\0") if path)
def resolve(root: Path, supplied: str) -> tuple[str, Path] | None:
if "\0" in supplied:
return None
logical = Path(supplied)
if logical.is_absolute() or ".." in logical.parts:
return None
path = root / logical
if path.is_symlink() or not path.is_file():
return None
try:
canonical = path.resolve(strict=True)
relative = canonical.relative_to(root)
except (OSError, ValueError):
return None
if canonical != path.absolute():
return None
return relative.as_posix(), canonical
def scan(logical: str, path: Path) -> bool:
if logical.startswith("crates/crank-metrics/"):
return False
if "/tests/" in logical or logical.startswith("tests/"):
return False
try:
if path.stat().st_size > MAX_FILE_BYTES:
return True
text = path.read_text(encoding="utf-8")
except (OSError, UnicodeError):
return True
for pattern in PATTERNS:
if not pattern.search(text):
continue
if logical in ALLOWED_ADAPTERS and pattern is PATTERNS[0] and "describe_" in pattern.search(text).group(0):
continue
if logical == "crates/crank-observability/src/prometheus.rs" and pattern is PATTERNS[3]:
continue
return True
return False
def main(argv: list[str]) -> int:
args = parse_args(argv)
root = args.root.resolve()
try:
supplied = args.files if args.files is not None else discover(root)
except (OSError, subprocess.SubprocessError, UnicodeError):
print("error: metrics boundary discovery failed", file=sys.stderr)
return 1
if len(supplied) > MAX_FILES:
print("error: metrics boundary input limit exceeded", file=sys.stderr)
return 1
violations: list[str] = []
for index, item in enumerate(sorted(set(supplied))):
resolved = resolve(root, item)
if resolved is None:
violations.append(f"input[{index}]: invalid path")
continue
logical, path = resolved
if path.suffix == ".rs" and scan(logical, path):
violations.append(f"{logical}: direct metrics declaration")
if violations:
for violation in violations[:1000]:
print(f"error: {violation}", file=sys.stderr)
if len(violations) > 1000:
print(f"error: diagnostics omitted={len(violations) - 1000}", file=sys.stderr)
return 1
print(f"Metrics boundary check passed: files={len(supplied)}")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
+1 -7
View File
@@ -22,6 +22,7 @@ echo "Rust module boundary check: checking module-level imports"
python3 "$ROOT_DIR/scripts/check-config-boundaries.py" --root "$ROOT_DIR" || status=1
python3 "$ROOT_DIR/scripts/check-migration-boundaries.py" --root "$ROOT_DIR" || status=1
python3 "$ROOT_DIR/scripts/check-metrics-boundaries.py" --root "$ROOT_DIR" || status=1
check_no_match \
"admin-api service modules must not depend on axum HTTP types" \
@@ -50,13 +51,6 @@ check_no_match \
'^\s*use\s+(axum|sqlx)(::|[;\{])' \
"$ROOT_DIR/crates/crank-runtime/src"
check_no_match \
"product modules must record metrics only through crank-metrics" \
'(^|[^[:alnum:]_])(::)?metrics::(counter|gauge|histogram)!' \
"$ROOT_DIR/apps" \
"$ROOT_DIR/crates" \
--glob '!**/crank-metrics/**'
if (( status != 0 )); then
cat >&2 <<'EOF'