File size: 3,539 Bytes
95a8a23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6724416
 
 
 
 
 
 
 
 
 
 
95a8a23
 
 
 
 
 
 
 
 
 
 
 
 
bab19d3
 
 
 
 
 
 
 
95a8a23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/env python3
from __future__ import annotations
import re
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
errors: list[str] = []
warnings: list[str] = []


def fail(msg: str) -> None:
    errors.append(msg)


def warn(msg: str) -> None:
    warnings.append(msg)

version_file = ROOT / "VERSION"
version = version_file.read_text(encoding="utf-8").strip() if version_file.exists() else ""
if not re.fullmatch(r"\d+\.\d+\.\d+", version):
    fail("VERSION missing or not semantic x.y.z")

readme = (ROOT / "README.md").read_text(encoding="utf-8") if (ROOT / "README.md").exists() else ""
if not readme.startswith("---\n"):
    fail("README.md must start with Hugging Face YAML front matter")
else:
    end = readme.find("\n---", 4)
    if end < 0:
        fail("README.md YAML front matter is not closed")
    else:
        meta = readme[4:end]
        for required in ("title:", "sdk: docker", "app_port: 7860"):
            if required not in meta:
                fail(f"README.md metadata missing: {required}")

app_html = ROOT / "app.html"
if not app_html.exists():
    fail("root app.html is missing")
else:
    html = app_html.read_text(encoding="utf-8")
    if '__APP_VERSION__' not in html:
        fail("app.html must use __APP_VERSION__ placeholder")
    if 'data-view="projectPackage"' not in html:
        fail("project data package sidebar entry is missing")

ui = ROOT / "ui_server.py"
if not ui.exists():
    fail("ui_server.py is missing")
else:
    text = ui.read_text(encoding="utf-8")
    if 'with_name("VERSION")' not in text:
        fail("ui_server.py must load VERSION as the single version source")
    if 'with_name("app.html")' not in text:
        fail("ui_server.py must serve root app.html")
    if 'templates/app.html' in text:
        fail("ui_server.py still references templates/app.html")

if (ROOT / "templates" / "app.html").exists():
    fail("duplicate templates/app.html must not be shipped")

for forbidden in (".pytest_cache", "__pycache__"):
    matches = [p for p in ROOT.rglob(forbidden) if p.is_dir()]
    if matches:
        fail(f"forbidden cache directory present: {matches[0].relative_to(ROOT)}")
if list(ROOT.rglob("*.pyc")):
    fail("compiled .pyc files must not be shipped")


for required in (
    ROOT / "static" / "css" / "app.css",
    ROOT / "static" / "js" / "app.js",
    ROOT / "views" / "admin.py",
    ROOT / "services" / "project_planner.py",
    ROOT / "routes" / "data_health.py",
):
    if not required.exists():
        fail(f"P0 architecture file missing: {required.relative_to(ROOT)}")

for name in ("Dockerfile", "start.sh", "requirements.txt"):
    if not (ROOT / name).exists():
        fail(f"deployment file missing: {name}")

if version and f"Current UI release: **v{version}**" not in readme:
    warn("README current release line is not synchronized with VERSION")

for msg in warnings:
    print(f"[preflight] WARNING: {msg}")
if errors:
    for msg in errors:
        print(f"[preflight] ERROR: {msg}", file=sys.stderr)
    sys.exit(1)
import shutil as _shutil, subprocess as _subprocess
node_bin=_shutil.which("node")
frontend_smoke=ROOT/"scripts"/"frontend_runtime_smoke.js"
if node_bin and frontend_smoke.exists():
    proc=_subprocess.run([node_bin,str(frontend_smoke)],cwd=ROOT,capture_output=True,text=True)
    if proc.returncode!=0:
        error("frontend runtime smoke failed: "+(proc.stderr.strip() or proc.stdout.strip())[:500])

print(f"[preflight] OK: v{version}; single template; clean deployment tree")