refactor: review fixes, DRY coverage, filter test nav

- Extract compute_coverage to common.py (was duplicated)
- Filter test cores from nav and emulator index
- Use absolute URL for README download links
- Consistent page titles with site name suffix
- Safer mkdocs.yml nav rewrite with regex
- Build all_platform_names once in gap analysis
This commit is contained in:
Abdessamad Derraz
2026-03-18 11:05:13 +01:00
parent e218763500
commit 54c0f1d27e
3 changed files with 40 additions and 59 deletions
+21
View File
@@ -217,6 +217,27 @@ def resolve_local_file(
return None, "not_found" return None, "not_found"
def compute_coverage(platform_name: str, platforms_dir: str, db: dict) -> dict:
"""Compute BIOS coverage for a platform using verify logic."""
from verify import verify_platform
config = load_platform_config(platform_name, platforms_dir)
result = verify_platform(config, db)
present = result["ok"] + result["untested"]
pct = (present / result["total"] * 100) if result["total"] > 0 else 0
return {
"platform": config.get("platform", platform_name),
"total": result["total"],
"verified": result["ok"],
"untested": result["untested"],
"missing": result["missing"],
"present": present,
"percentage": pct,
"mode": config.get("verification_mode", "existence"),
"details": result["details"],
"config": config,
}
def safe_extract_zip(zip_path: str, dest_dir: str) -> None: def safe_extract_zip(zip_path: str, dest_dir: str) -> None:
"""Extract a ZIP file safely, preventing zip-slip path traversal.""" """Extract a ZIP file safely, preventing zip-slip path traversal."""
dest = os.path.realpath(dest_dir) dest = os.path.realpath(dest_dir)
+3 -26
View File
@@ -18,33 +18,10 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.dirname(__file__))
from common import load_database, load_platform_config from common import load_database, compute_coverage
try:
import yaml
except ImportError:
yaml = None
SITE_URL = "https://abdess.github.io/retrobios/" SITE_URL = "https://abdess.github.io/retrobios/"
RELEASE_URL = "../../releases/latest" RELEASE_URL = "https://github.com/Abdess/retrobios/releases/latest"
def _compute_coverage(platform_name: str, platforms_dir: str, db: dict) -> dict:
from verify import verify_platform
config = load_platform_config(platform_name, platforms_dir)
result = verify_platform(config, db)
present = result["ok"] + result["untested"]
pct = (present / result["total"] * 100) if result["total"] > 0 else 0
return {
"platform": config.get("platform", platform_name),
"total": result["total"],
"verified": result["ok"],
"untested": result["untested"],
"missing": result["missing"],
"present": present,
"percentage": pct,
"mode": config.get("verification_mode", "existence"),
}
def generate_readme(db: dict, platforms_dir: str) -> str: def generate_readme(db: dict, platforms_dir: str) -> str:
@@ -61,7 +38,7 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
coverages = {} coverages = {}
for name in platform_names: for name in platform_names:
try: try:
coverages[name] = _compute_coverage(name, platforms_dir, db) coverages[name] = compute_coverage(name, platforms_dir, db)
except FileNotFoundError: except FileNotFoundError:
pass pass
+16 -33
View File
@@ -26,7 +26,7 @@ except ImportError:
sys.exit(1) sys.exit(1)
sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.dirname(__file__))
from common import load_database, load_platform_config from common import load_database, load_platform_config, compute_coverage
DOCS_DIR = "docs" DOCS_DIR = "docs"
SITE_NAME = "RetroBIOS" SITE_NAME = "RetroBIOS"
@@ -67,25 +67,6 @@ def _status_icon(pct: float) -> str:
# Coverage computation (reuses verify.py logic) # Coverage computation (reuses verify.py logic)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def _compute_coverage(platform_name: str, platforms_dir: str, db: dict) -> dict:
from verify import verify_platform
config = load_platform_config(platform_name, platforms_dir)
result = verify_platform(config, db)
present = result["ok"] + result["untested"]
pct = (present / result["total"] * 100) if result["total"] > 0 else 0
return {
"platform": config.get("platform", platform_name),
"total": result["total"],
"verified": result["ok"],
"untested": result["untested"],
"missing": result["missing"],
"present": present,
"percentage": pct,
"mode": config.get("verification_mode", "existence"),
"details": result["details"],
"config": config,
}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Load emulator profiles # Load emulator profiles
@@ -169,7 +150,7 @@ def generate_home(db: dict, coverages: dict, emulator_count: int,
def generate_platform_index(coverages: dict) -> str: def generate_platform_index(coverages: dict) -> str:
lines = [ lines = [
"# Platforms", f"# Platforms - {SITE_NAME}",
"", "",
"| Platform | Coverage | Verification | Status |", "| Platform | Coverage | Verification | Status |",
"|----------|----------|-------------|--------|", "|----------|----------|-------------|--------|",
@@ -276,7 +257,7 @@ def _group_by_manufacturer(db: dict) -> dict[str, dict[str, list]]:
def generate_systems_index(manufacturers: dict) -> str: def generate_systems_index(manufacturers: dict) -> str:
lines = [ lines = [
"# Systems", f"# Systems - {SITE_NAME}",
"", "",
"| Manufacturer | Consoles | Files |", "| Manufacturer | Consoles | Files |",
"|-------------|----------|-------|", "|-------------|----------|-------|",
@@ -355,13 +336,14 @@ def generate_system_page(
def generate_emulators_index(profiles: dict) -> str: def generate_emulators_index(profiles: dict) -> str:
lines = [ lines = [
"# Emulators", f"# Emulators - {SITE_NAME}",
"", "",
"| Engine | Type | Systems | Files |", "| Engine | Type | Systems | Files |",
"|--------|------|---------|-------|", "|--------|------|---------|-------|",
] ]
unique = {k: v for k, v in profiles.items() if v.get("type") != "alias"} unique = {k: v for k, v in profiles.items() if v.get("type") not in ("alias", "test")}
test_cores = {k: v for k, v in profiles.items() if v.get("type") == "test"}
aliases = {k: v for k, v in profiles.items() if v.get("type") == "alias"} aliases = {k: v for k, v in profiles.items() if v.get("type") == "alias"}
for name in sorted(unique.keys()): for name in sorted(unique.keys()):
@@ -494,6 +476,11 @@ def generate_gap_analysis(
total_in_repo = 0 total_in_repo = 0
total_missing = 0 total_missing = 0
# Build global set of all platform-declared filenames (once)
all_platform_names = set()
for pfiles in platform_files.values():
all_platform_names.update(pfiles)
emulator_gaps = [] emulator_gaps = []
for emu_name, profile in sorted(profiles.items()): for emu_name, profile in sorted(profiles.items()):
if profile.get("type") == "alias": if profile.get("type") == "alias":
@@ -502,11 +489,6 @@ def generate_gap_analysis(
if not files: if not files:
continue continue
# Collect all files declared by any platform (global check)
all_platform_names = set()
for pfiles in platform_files.values():
all_platform_names.update(pfiles)
undeclared = [] undeclared = []
for f in files: for f in files:
fname = f.get("name", "") fname = f.get("name", "")
@@ -676,7 +658,7 @@ def generate_mkdocs_nav(
slug = mfr.lower().replace(" ", "-") slug = mfr.lower().replace(" ", "-")
system_nav.append({mfr: f"systems/{slug}.md"}) system_nav.append({mfr: f"systems/{slug}.md"})
unique_profiles = {k: v for k, v in profiles.items() if v.get("type") != "alias"} unique_profiles = {k: v for k, v in profiles.items() if v.get("type") not in ("alias", "test")}
emu_nav = [{"Overview": "emulators/index.md"}] emu_nav = [{"Overview": "emulators/index.md"}]
for name in sorted(unique_profiles.keys()): for name in sorted(unique_profiles.keys()):
display = unique_profiles[name].get("emulator", name) display = unique_profiles[name].get("emulator", name)
@@ -734,7 +716,7 @@ def main():
coverages = {} coverages = {}
for name in sorted(platform_names): for name in sorted(platform_names):
try: try:
cov = _compute_coverage(name, args.platforms_dir, db) cov = compute_coverage(name, args.platforms_dir, db)
coverages[name] = cov coverages[name] = cov
print(f" {cov['platform']}: {cov['present']}/{cov['total']} ({_pct(cov['present'], cov['total'])})") print(f" {cov['platform']}: {cov['present']}/{cov['total']} ({_pct(cov['present'], cov['total'])})")
except FileNotFoundError as e: except FileNotFoundError as e:
@@ -793,9 +775,10 @@ def main():
with open("mkdocs.yml") as f: with open("mkdocs.yml") as f:
content = f.read() content = f.read()
# Replace or append nav section # Replace nav section (everything from \nnav: to the next top-level key or EOF)
import re
if "\nnav:" in content: if "\nnav:" in content:
content = content[:content.index("\nnav:") + 1] + nav_yaml content = re.sub(r'\nnav:.*', '\n' + nav_yaml.rstrip(), content, count=1, flags=re.DOTALL)
else: else:
content += "\n" + nav_yaml content += "\n" + nav_yaml
with open("mkdocs.yml", "w") as f: with open("mkdocs.yml", "w") as f: