Configure packaging and release workflow to ship exclusively compiled binaries

This commit is contained in:
2026-09-03 21:37:31 +02:00
parent 6d1edf850c
commit 52fd645f17
3 changed files with 187 additions and 40 deletions
+81 -37
View File
@@ -1,35 +1,63 @@
import os
import sys
import shutil
import tarfile
import zipfile
import zipapp
import hashlib
import platform
import subprocess
DIST_DIR = "dist"
OUT_DIR = "out"
DIST_DIR = os.path.abspath("dist")
OUT_DIR = os.path.abspath("out")
BUILD_TEMP = os.path.abspath("build_temp")
def make_zip(source_dir, output_zip):
with zipfile.ZipFile(output_zip, "w", zipfile.ZIP_DEFLATED) as zf:
for root, _, files in os.walk(source_dir):
for file in files:
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, source_dir)
zf.write(full_path, rel_path)
def clean_and_prep():
if os.path.exists(DIST_DIR):
shutil.rmtree(DIST_DIR)
os.makedirs(DIST_DIR, exist_ok=True)
if os.path.exists(BUILD_TEMP):
shutil.rmtree(BUILD_TEMP)
os.makedirs(BUILD_TEMP, exist_ok=True)
def make_tar_gz(source_dir, output_tar):
with tarfile.open(output_tar, "w:gz") as tf:
for root, _, files in os.walk(source_dir):
for file in files:
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, source_dir)
tf.add(full_path, arcname=rel_path)
def build_pyinstaller_binary(script_path, binary_name):
print(f"[*] Compiling {binary_name} with PyInstaller...")
cmd = [
sys.executable, "-m", "PyInstaller",
"--onefile",
"--clean",
"--distpath", DIST_DIR,
"--workpath", os.path.join(BUILD_TEMP, f"work_{binary_name}"),
"--specpath", os.path.join(BUILD_TEMP, f"spec_{binary_name}"),
"--name", binary_name,
script_path
]
res = subprocess.run(cmd, capture_output=True, text=True)
if res.returncode != 0:
print(f"[!] Compilation error for {binary_name}:\n{res.stderr}")
raise RuntimeError(f"Failed to build {binary_name}")
print(f"[+] Successfully compiled {binary_name}")
def generate_checksums(dist_dir):
checksum_file = os.path.join(dist_dir, "SHA256SUMS.txt")
def build_linux_zipapp_binary():
print("[*] Packaging Linux_Client.bin executable binary...")
app_dir = os.path.join(BUILD_TEMP, "linux_app")
os.makedirs(app_dir, exist_ok=True)
shutil.copy(os.path.join(OUT_DIR, "linux_client", "Linux_Client.py"), os.path.join(app_dir, "Linux_Client.py"))
bin_output = os.path.join(DIST_DIR, "Linux_Client.bin")
zipapp.create_archive(
source=app_dir,
target=bin_output,
interpreter="/usr/bin/env python3",
main="Linux_Client:main"
)
print(f"[+] Successfully generated {bin_output}")
def generate_checksums():
checksum_file = os.path.join(DIST_DIR, "SHA256SUMS.txt")
lines = []
for fname in sorted(os.listdir(dist_dir)):
for fname in sorted(os.listdir(DIST_DIR)):
if fname == "SHA256SUMS.txt":
continue
fpath = os.path.join(dist_dir, fname)
fpath = os.path.join(DIST_DIR, fname)
if os.path.isfile(fpath):
with open(fpath, "rb") as f:
digest = hashlib.sha256(f.read()).hexdigest()
@@ -38,29 +66,45 @@ def generate_checksums(dist_dir):
f.write("\n".join(lines) + "\n")
def main():
os.makedirs(DIST_DIR, exist_ok=True)
print("=" * 60)
print(" LOGAR Binary Packaging (Binaries Only)")
print(f" Platform: {platform.system()} ({platform.machine()})")
print("=" * 60)
server_src = os.path.join(OUT_DIR, "server")
win_src = os.path.join(OUT_DIR, "win_client")
linux_src = os.path.join(OUT_DIR, "linux_client")
clean_and_prep()
print("[*] Packaging Server shippable...")
make_tar_gz(server_src, os.path.join(DIST_DIR, "logar-server.tar.gz"))
make_zip(server_src, os.path.join(DIST_DIR, "logar-server.zip"))
is_windows = platform.system() == "Windows"
print("[*] Packaging Windows Client shippable...")
make_zip(win_src, os.path.join(DIST_DIR, "logar-win-client.zip"))
if is_windows:
# Build Windows client executable
win_client_script = os.path.join(OUT_DIR, "win_client", "Win_Client.py")
build_pyinstaller_binary(win_client_script, "Win_Client")
print("[*] Packaging Linux Client shippable...")
make_tar_gz(linux_src, os.path.join(DIST_DIR, "logar-linux-client.tar.gz"))
# Build Windows server executable
server_script = os.path.join(OUT_DIR, "server", "Server.py")
build_pyinstaller_binary(server_script, "Server")
print("[*] Generating SHA-256 checksums...")
generate_checksums(DIST_DIR)
# Build Linux client standalone binary
build_linux_zipapp_binary()
else:
# On Linux runner: Build native Linux binaries
linux_client_script = os.path.join(OUT_DIR, "linux_client", "Linux_Client.py")
build_pyinstaller_binary(linux_client_script, "Linux_Client.bin")
print(f"[+] All release artifacts assembled in '{DIST_DIR}/':")
server_script = os.path.join(OUT_DIR, "server", "Server.py")
build_pyinstaller_binary(server_script, "Server.bin")
generate_checksums()
# Clean temporary build directory
if os.path.exists(BUILD_TEMP):
shutil.rmtree(BUILD_TEMP, ignore_errors=True)
print("\n[+] Binary shipping artifacts assembled in 'dist/':")
for f in os.listdir(DIST_DIR):
sz = os.path.getsize(os.path.join(DIST_DIR, f))
print(f" - {f} ({sz} bytes)")
print(f" - {f} ({sz / (1024*1024):.2f} MB)" if sz > 1024*1024 else f" - {f} ({sz} bytes)")
print("=" * 60)
if __name__ == "__main__":
main()