Build and validate / build (push) Successful in 1m38s
Modeled on three researched precedents (see README): OpenWrt's Table of Hardware for the browsable sortable/filterable table UX, postmarketOS's working/community/testing tiers for the status field (collapsed to working/partial/broken here), and RaspiBlitz's scattered GitHub-issues approach as a negative example to avoid — hence structured YAML report files validated against a JSON Schema instead of free-text issue threads. - data/reports/*.yml + data/schema.json: one file per report, schema requires `issues` whenever status is partial/broken - scripts/build.py: validates every report and builds site/data.json, fails loudly on bad data (same idea as archy's own validate-app-manifest.sh) - site/: plain HTML/CSS/JS, no framework or build step, fetches data.json client-side — search, filter by status/form-factor, sortable columns, click a row for issues/notes detail - .github/ISSUE_TEMPLATE/hardware-report.yml: structured submission path for contributors who don't want to touch git directly - .gitea/workflows/ci.yml: runs the build/validate step on push and PRs Not yet deployed anywhere — see README's Deployment section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
60 lines
1.8 KiB
Python
Executable File
60 lines
1.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Build site/data.json from data/reports/*.yml.
|
|
|
|
Every report is validated against data/schema.json before being included —
|
|
a malformed report fails the build loudly instead of shipping a broken row
|
|
to the site (same idea as archy's own scripts/validate-app-manifest.sh:
|
|
fail the build, don't ship the drift).
|
|
"""
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
from jsonschema import Draft7Validator
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
REPORTS_DIR = ROOT / "data" / "reports"
|
|
SCHEMA_PATH = ROOT / "data" / "schema.json"
|
|
OUT_PATH = ROOT / "site" / "data.json"
|
|
|
|
|
|
def main() -> int:
|
|
schema = json.loads(SCHEMA_PATH.read_text())
|
|
validator = Draft7Validator(schema)
|
|
|
|
reports = []
|
|
errors = []
|
|
|
|
for path in sorted(REPORTS_DIR.glob("*.yml")):
|
|
try:
|
|
data = yaml.safe_load(path.read_text())
|
|
except yaml.YAMLError as e:
|
|
errors.append(f"{path.name}: invalid YAML — {e}")
|
|
continue
|
|
|
|
report_errors = sorted(validator.iter_errors(data), key=lambda e: e.path)
|
|
if report_errors:
|
|
for err in report_errors:
|
|
loc = ".".join(str(p) for p in err.path) or "(root)"
|
|
errors.append(f"{path.name}: {loc}: {err.message}")
|
|
continue
|
|
|
|
data["_source_file"] = path.name
|
|
reports.append(data)
|
|
|
|
if errors:
|
|
print(f"Build failed — {len(errors)} error(s):", file=sys.stderr)
|
|
for e in errors:
|
|
print(f" {e}", file=sys.stderr)
|
|
return 1
|
|
|
|
reports.sort(key=lambda r: (r["device_model"], r["tested_date"]))
|
|
OUT_PATH.write_text(json.dumps(reports, indent=2) + "\n")
|
|
print(f"Wrote {len(reports)} report(s) to {OUT_PATH.relative_to(ROOT)}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|