quality: strengthen metis hygiene contracts and platform metrics

This commit is contained in:
Brad Stein 2026-04-17 04:31:21 -03:00
parent 642b0606e2
commit 1b62d20320
2 changed files with 156 additions and 0 deletions

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import json
import os
from pathlib import Path
import urllib.request
import xml.etree.ElementTree as ET
@ -80,6 +81,25 @@ def _post_text(url: str, payload: str) -> None:
raise RuntimeError(f"metrics push failed status={resp.status}")
def _count_source_files_over_limit(repo_root: Path, max_lines: int = 500) -> int:
"""Count source files above the configured line budget."""
count = 0
for rel_root in ("cmd", "pkg", "scripts", "testing"):
base = repo_root / rel_root
if not base.exists():
continue
for path in base.rglob("*"):
if not path.is_file():
continue
if path.suffix not in {".go", ".py", ".sh"}:
continue
lines = len(path.read_text(encoding="utf-8", errors="ignore").splitlines())
if lines > max_lines:
count += 1
return count
def main() -> int:
coverage_path = os.getenv("COVERAGE_JSON", "build/coverage.json")
junit_path = os.getenv("JUNIT_XML", "build/junit.xml")
@ -92,6 +112,7 @@ def main() -> int:
build_number = os.getenv("BUILD_NUMBER", "")
commit = os.getenv("GIT_COMMIT", "")
strict = os.getenv("METRICS_STRICT", "") == "1"
repo_root = Path(__file__).resolve().parents[1]
if not os.path.exists(coverage_path):
raise RuntimeError(f"missing coverage file {coverage_path}")
@ -101,6 +122,7 @@ def main() -> int:
coverage = _load_coverage(coverage_path)
totals = _load_junit(junit_path)
test_exit_code = _load_exit_code(test_exit_code_path)
source_lines_over_500 = _count_source_files_over_limit(repo_root, max_lines=500)
passed = max(totals["tests"] - totals["failures"] - totals["errors"] - totals["skipped"], 0)
outcome = "ok"
@ -131,6 +153,10 @@ def main() -> int:
f'metis_quality_gate_run_status{{suite="{suite}",status="failed"}} {1 if outcome == "failed" else 0}',
"# TYPE metis_quality_gate_coverage_percent gauge",
f'metis_quality_gate_coverage_percent{{suite="{suite}"}} {coverage:.3f}',
"# TYPE platform_quality_gate_workspace_line_coverage_percent gauge",
f'platform_quality_gate_workspace_line_coverage_percent{{suite="{suite}"}} {coverage:.3f}',
"# TYPE platform_quality_gate_source_lines_over_500_total gauge",
f'platform_quality_gate_source_lines_over_500_total{{suite="{suite}"}} {source_lines_over_500}',
"# TYPE metis_quality_gate_build_info gauge",
f"metis_quality_gate_build_info{_label_str(labels)} 1",
]
@ -154,6 +180,7 @@ def main() -> int:
"tests_errors": totals["errors"],
"tests_skipped": totals["skipped"],
"coverage_percent": round(coverage, 3),
"source_lines_over_500": source_lines_over_500,
"test_exit_code": test_exit_code,
},
indent=2,

View File

@ -11,6 +11,7 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
@ -151,6 +152,134 @@ func TestCoveragePolicy(t *testing.T) {
}
}
func TestStructureHygiene(t *testing.T) {
root := repoRoot(t)
genericNames := map[string]struct{}{
"tmp": {},
"temp": {},
"foo": {},
"bar": {},
"baz": {},
"misc": {},
"new": {},
"old": {},
"final": {},
"wip": {},
}
maxDepth := 8
var violations []string
for _, relRoot := range []string{"cmd", "pkg", "scripts", "testing"} {
base := filepath.Join(root, relRoot)
walkSourceFiles(t, base, func(path string, info os.DirEntry) error {
if info.IsDir() {
return nil
}
switch filepath.Ext(path) {
case ".go", ".py", ".sh":
default:
return nil
}
relative := rel(root, path)
depth := len(strings.Split(relative, "/"))
if depth > maxDepth {
violations = append(violations, fmt.Sprintf("%s: depth %d > %d", relative, depth, maxDepth))
}
baseName := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
for _, tok := range strings.FieldsFunc(baseName, func(r rune) bool {
return r == '_' || r == '-'
}) {
if _, found := genericNames[strings.ToLower(tok)]; found {
violations = append(violations, fmt.Sprintf("%s: non-descriptive token %q", relative, tok))
}
}
return nil
})
}
if len(violations) > 0 {
sort.Strings(violations)
t.Fatalf("structure hygiene violations: %s", strings.Join(violations, ", "))
}
}
func TestCodeSmellContracts(t *testing.T) {
root := repoRoot(t)
patterns := []struct {
re *regexp.Regexp
scope []string
message string
}{
{
re: regexp.MustCompile(`\bpanic\(`),
scope: []string{"cmd", "pkg"},
message: "avoid panic in production Go code",
},
{
re: regexp.MustCompile(`\blog\.Fatalf\(`),
scope: []string{"pkg"},
message: "avoid log.Fatalf in pkg code",
},
{
re: regexp.MustCompile(`\bfmt\.Print(f|ln)?\(`),
scope: []string{"pkg"},
message: "avoid fmt.Print* in pkg code",
},
}
type hit struct {
path string
line int
text string
msg string
}
var hits []hit
for _, rule := range patterns {
for _, relRoot := range rule.scope {
base := filepath.Join(root, relRoot)
walkSourceFiles(t, base, func(path string, info os.DirEntry) error {
if info.IsDir() || filepath.Ext(path) != ".go" || strings.HasSuffix(path, "_test.go") {
return nil
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineNo := 0
for scanner.Scan() {
lineNo++
line := scanner.Text()
if rule.re.MatchString(line) {
hits = append(hits, hit{
path: rel(root, path),
line: lineNo,
text: strings.TrimSpace(line),
msg: rule.message,
})
}
}
return scanner.Err()
})
}
}
if len(hits) > 0 {
sort.Slice(hits, func(i, j int) bool {
if hits[i].path == hits[j].path {
return hits[i].line < hits[j].line
}
return hits[i].path < hits[j].path
})
lines := make([]string, 0, len(hits))
for _, h := range hits {
lines = append(lines, fmt.Sprintf("%s:%d (%s) %s", h.path, h.line, h.msg, h.text))
}
t.Fatalf("code smell violations: %s", strings.Join(lines, ", "))
}
}
func countLines(path string) (int, error) {
f, err := os.Open(path)
if err != nil {