-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild_backend.py
More file actions
133 lines (106 loc) · 3.77 KB
/
build_backend.py
File metadata and controls
133 lines (106 loc) · 3.77 KB
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env python3
"""Build script — bundles the Vera Python backend via PyInstaller.
Usage:
python build_backend.py # Build for current platform
python build_backend.py --clean # Clean previous builds first
"""
from __future__ import annotations
import argparse
import io
import os
import platform
import shutil
import subprocess
import sys
from pathlib import Path
# Force UTF-8 output on Windows CI runners (cp1252 can't encode emoji)
if sys.stdout.encoding and sys.stdout.encoding.lower() != "utf-8":
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
ROOT = Path(__file__).parent
DIST_DIR = ROOT / "dist" / "vera-server"
DATA_TEMPLATE_DIR = ROOT / "data"
ENV_EXAMPLE = ROOT / ".env.example"
SYSTEM = platform.system()
EXE_NAME = "vera-server.exe" if SYSTEM == "Windows" else "vera-server"
def clean() -> None:
"""Remove previous build artifacts."""
for d in ["build", "dist"]:
p = ROOT / d
if p.exists():
print(f"🧹 Removing {p}")
shutil.rmtree(p)
def build() -> None:
"""Run PyInstaller with the spec file."""
spec = ROOT / "vera.spec"
if not spec.exists():
print("❌ vera.spec not found — run from the project root")
sys.exit(1)
print("🔨 Running PyInstaller...")
result = subprocess.run(
[sys.executable, "-m", "PyInstaller", str(spec), "--noconfirm"],
cwd=str(ROOT),
)
if result.returncode != 0:
print("❌ PyInstaller build failed")
sys.exit(1)
def copy_data() -> None:
"""Copy data/ template and .env.example alongside the built executable."""
if DATA_TEMPLATE_DIR.exists():
dest = DIST_DIR / "data"
if dest.exists():
shutil.rmtree(dest)
shutil.copytree(str(DATA_TEMPLATE_DIR), str(dest))
print(f"📂 Copied data/ → {dest}")
if ENV_EXAMPLE.exists():
dest = DIST_DIR / ".env.example"
shutil.copy2(str(ENV_EXAMPLE), str(dest))
print(f"📄 Copied .env.example → {dest}")
def validate() -> None:
"""Quick smoke test: run the built executable with --help."""
exe = DIST_DIR / EXE_NAME
if not exe.exists():
print(f"⚠️ Built executable not found at {exe}")
return
print(f"✅ Built executable: {exe}")
print(f"📦 Bundle size: {sum(f.stat().st_size for f in DIST_DIR.rglob('*') if f.is_file()) / 1024 / 1024:.1f} MB")
try:
result = subprocess.run(
[str(exe), "--help"],
capture_output=True,
text=True,
timeout=30,
)
if result.returncode == 0:
print("✅ Executable runs successfully")
else:
print(f"⚠️ Executable returned code {result.returncode}")
if result.stderr:
print(f" stderr: {result.stderr[:200]}")
except subprocess.TimeoutExpired:
print("⚠️ Executable timed out (may be normal for server startup)")
except Exception as e:
print(f"⚠️ Could not validate executable: {e}")
def main() -> None:
parser = argparse.ArgumentParser(description="Build Vera backend with PyInstaller")
parser.add_argument("--clean", action="store_true", help="Clean previous builds first")
args = parser.parse_args()
print(f"""
===========================================
Vera Backend Builder v0.5.1
Platform: {SYSTEM}
===========================================
""")
if args.clean:
clean()
build()
copy_data()
validate()
print(f"""
Done! Backend build complete.
Output: {DIST_DIR}
Executable: {DIST_DIR / EXE_NAME}
Next: cd electron && npm run build
""")
if __name__ == "__main__":
main()