Files
server-manager/build.py
chrome-storm-c442 eede67e6a9 feat: multi-type server support — SQL, Redis, Grafana, Prometheus, Telnet, WinRM, RDP/VNC
Full implementation of multi-type server management across GUI and CLI:

New clients: SQLClient (MariaDB/MSSQL/PostgreSQL), RedisClient, GrafanaClient,
PrometheusClient, TelnetSession, WinRMClient, RemoteDesktopLauncher.

New GUI tabs: QueryTab (SQL editor + Treeview), RedisTab (console + history),
GrafanaTab (dashboards + alerts), PrometheusTab (PromQL + targets),
PowershellTab (PS/CMD), LaunchTab (RDP/VNC external client).

Infrastructure: TAB_REGISTRY for conditional tabs per server type,
adaptive server_dialog fields, colored type badges in sidebar,
status checker for all types (SSH/TCP/SQL/Redis/HTTP), 100+ i18n keys.

CLI: ssh.py extended with --sql, --redis, --grafana-*, --prom-*, --ps, --cmd.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:35:24 -05:00

166 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""
Build script — creates platform-specific executables via PyInstaller.
Run on the target OS to build for that platform.
Usage:
python build.py # build for current platform
python build.py --clean # clean build artifacts first
"""
import os
import re
import sys
import shutil
import platform
# Add project root
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, PROJECT_DIR)
def auto_bump_version() -> str:
"""Auto-increment patch version in version.py on every build."""
ver_file = os.path.join(PROJECT_DIR, "version.py")
with open(ver_file, "r", encoding="utf-8") as f:
content = f.read()
match = re.search(r'__version__\s*=\s*"(\d+)\.(\d+)\.(\d+)"', content)
if not match:
print("ERROR: Cannot parse version from version.py")
sys.exit(1)
major, minor, patch = int(match.group(1)), int(match.group(2)), int(match.group(3))
new_patch = patch + 1
new_version = f"{major}.{minor}.{new_patch}"
content = re.sub(
r'__version__\s*=\s*"[\d.]+"',
f'__version__ = "{new_version}"',
content,
)
with open(ver_file, "w", encoding="utf-8") as f:
f.write(content)
print(f"Version bumped: {major}.{minor}.{patch} -> {new_version}")
return new_version
# Auto-bump unless --no-bump flag is passed
if "--no-bump" not in sys.argv:
_version = auto_bump_version()
else:
sys.argv.remove("--no-bump")
_version = None
from version import __version__, __app_name__
DIST_DIR = os.path.join(PROJECT_DIR, "dist")
BUILD_DIR = os.path.join(PROJECT_DIR, "build")
RELEASES_DIR = os.path.join(PROJECT_DIR, "releases")
def get_platform_tag() -> str:
system = platform.system().lower()
machine = platform.machine().lower()
arch_map = {
"x86_64": "x64", "amd64": "x64",
"x86": "x32", "i686": "x32", "i386": "x32",
"aarch64": "arm64", "arm64": "arm64",
"armv7l": "arm",
}
arch = arch_map.get(machine, machine)
os_map = {"windows": "win", "linux": "linux", "darwin": "mac"}
os_tag = os_map.get(system, system)
return f"{os_tag}-{arch}"
def clean():
for d in [DIST_DIR, BUILD_DIR]:
if os.path.exists(d):
shutil.rmtree(d)
for f in os.listdir(PROJECT_DIR):
if f.endswith(".spec"):
os.remove(os.path.join(PROJECT_DIR, f))
print("Cleaned build artifacts")
def build():
tag = get_platform_tag()
print(f"Building {__app_name__} v{__version__} for {tag}...")
system = platform.system().lower()
# PyInstaller command
cmd_parts = [
sys.executable, "-m", "PyInstaller",
"--onefile",
"--windowed",
f"--name={__app_name__}",
"--add-data", f"config/servers.example.json{os.pathsep}config",
"--add-data", f"tools/ssh.py{os.pathsep}tools",
"--add-data", f"tools/skill-ssh.md{os.pathsep}tools",
"--add-data", f"core/encryption.py{os.pathsep}core",
]
# Icon
icon_path = os.path.join(PROJECT_DIR, "assets", "icon.ico")
if os.path.exists(icon_path):
cmd_parts.extend(["--icon", icon_path])
# Hidden imports for customtkinter and connection libraries
cmd_parts.extend([
"--hidden-import", "customtkinter",
"--hidden-import", "PIL",
"--hidden-import", "pyotp",
"--hidden-import", "pyte",
"--hidden-import", "psutil",
"--hidden-import", "pymysql",
"--hidden-import", "psycopg2",
"--hidden-import", "pymssql",
"--hidden-import", "redis",
"--hidden-import", "requests",
"--hidden-import", "winrm",
"--hidden-import", "telnetlib3",
"--collect-all", "customtkinter",
])
cmd_parts.append("main.py")
os.chdir(PROJECT_DIR)
ret = os.system(" ".join(f'"{p}"' if " " in p else p for p in cmd_parts))
if ret != 0:
print(f"Build failed with code {ret}")
sys.exit(1)
# Move to releases
os.makedirs(RELEASES_DIR, exist_ok=True)
if system == "windows":
src = os.path.join(DIST_DIR, f"{__app_name__}.exe")
dst = os.path.join(RELEASES_DIR, f"{__app_name__}-v{__version__}-{tag}.exe")
elif system == "darwin":
src = os.path.join(DIST_DIR, __app_name__)
dst = os.path.join(RELEASES_DIR, f"{__app_name__}-v{__version__}-{tag}")
else:
src = os.path.join(DIST_DIR, __app_name__)
dst = os.path.join(RELEASES_DIR, f"{__app_name__}-v{__version__}-{tag}")
if os.path.exists(src):
shutil.copy2(src, dst)
size_mb = os.path.getsize(dst) / (1024 * 1024)
print(f"\nBuild complete: {dst} ({size_mb:.1f} MB)")
else:
print(f"Build output not found: {src}")
sys.exit(1)
if __name__ == "__main__":
if "--clean" in sys.argv:
clean()
build()