-
Notifications
You must be signed in to change notification settings - Fork 73
Expand file tree
/
Copy pathbuild.py
More file actions
executable file
Β·191 lines (154 loc) Β· 5.52 KB
/
build.py
File metadata and controls
executable file
Β·191 lines (154 loc) Β· 5.52 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#!/usr/bin/env python3
"""
Build script for OpenHands CLI using PyInstaller.
This script packages the OpenHands CLI into a standalone executable binary
using PyInstaller with the custom spec file.
"""
import argparse
import os
import shutil
import subprocess
import sys
from pathlib import Path
def clean_build_directories() -> None:
"""Clean up previous build artifacts."""
print("π§Ή Cleaning up previous build artifacts...")
build_dirs = ["build", "dist", "__pycache__"]
for dir_name in build_dirs:
if os.path.exists(dir_name):
print(f" Removing {dir_name}/")
shutil.rmtree(dir_name)
# Clean up .pyc files
for root, _dirs, files in os.walk("."):
for file in files:
if file.endswith(".pyc"):
os.remove(os.path.join(root, file))
print("β
Cleanup complete!")
def check_pyinstaller() -> bool:
"""Check if PyInstaller is available."""
try:
subprocess.run(
["uv", "run", "pyinstaller", "--version"], check=True, capture_output=True
)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
print(
"β PyInstaller is not available. Use --install-pyinstaller flag or install manually with:"
)
print(" uv add --dev pyinstaller")
return False
def build_executable(
spec_file: str = "openhands-cli.spec",
clean: bool = True,
install_pyinstaller: bool = False,
) -> bool:
"""Build the executable using PyInstaller."""
if clean:
clean_build_directories()
# Check if PyInstaller is available (installation is handled by build.sh)
if not check_pyinstaller():
return False
print(f"π¨ Building executable using {spec_file}...")
try:
# Run PyInstaller with uv
cmd = ["uv", "run", "pyinstaller", spec_file, "--clean"]
print(f"Running: {' '.join(cmd)}")
subprocess.run(cmd, check=True, capture_output=True, text=True)
print("β
Build completed successfully!")
# Check if the executable was created
dist_dir = Path("dist")
if dist_dir.exists():
executables = list(dist_dir.glob("*"))
if executables:
print("π Executable(s) created in dist/:")
for exe in executables:
size = exe.stat().st_size / (1024 * 1024) # Size in MB
print(f" - {exe.name} ({size:.1f} MB)")
else:
print("β οΈ No executables found in dist/ directory")
return True
except subprocess.CalledProcessError as e:
print(f"β Build failed: {e}")
if e.stdout:
print("STDOUT:", e.stdout)
if e.stderr:
print("STDERR:", e.stderr)
return False
def test_executable() -> bool:
"""Test the built executable."""
print("π§ͺ Testing the built executable...")
exe_path = Path("dist/openhands-cli")
if not exe_path.exists():
# Try with .exe extension for Windows
exe_path = Path("dist/openhands-cli.exe")
if not exe_path.exists():
print("β Executable not found!")
return False
try:
# Make executable on Unix-like systems
if os.name != "nt":
os.chmod(exe_path, 0o755)
# Run the executable with a timeout
result = subprocess.run(
[str(exe_path)], capture_output=True, text=True, timeout=30
)
if result.returncode == 0:
print("β
Executable test passed!")
print("Output preview:")
print(
result.stdout[:500] + "..."
if len(result.stdout) > 500
else result.stdout
)
return True
else:
print(f"β Executable test failed with return code {result.returncode}")
print("STDERR:", result.stderr)
return False
except subprocess.TimeoutExpired:
print(
"β οΈ Executable test timed out (this might be normal for interactive CLIs)"
)
return True
except Exception as e:
print(f"β Error testing executable: {e}")
return False
def main() -> int:
"""Main function."""
parser = argparse.ArgumentParser(description="Build OpenHands CLI executable")
parser.add_argument(
"--spec", default="openhands-cli.spec", help="PyInstaller spec file to use"
)
parser.add_argument(
"--no-clean", action="store_true", help="Skip cleaning build directories"
)
parser.add_argument(
"--no-test", action="store_true", help="Skip testing the built executable"
)
parser.add_argument(
"--install-pyinstaller",
action="store_true",
help="Install PyInstaller using uv before building",
)
args = parser.parse_args()
print("π OpenHands CLI Build Script")
print("=" * 40)
# Check if spec file exists
if not os.path.exists(args.spec):
print(f"β Spec file '{args.spec}' not found!")
return 1
# Build the executable
if not build_executable(
args.spec, clean=not args.no_clean, install_pyinstaller=args.install_pyinstaller
):
return 1
# Test the executable
if not args.no_test:
if not test_executable():
print("β Executable test failed, build process failed")
return 1
print("\nπ Build process completed!")
print("π Check the 'dist/' directory for your executable")
return 0
if __name__ == "__main__":
sys.exit(main())