feat(packaging): include Inno Setup installers, Linux scripts, and bundles in release distribution
CI Test Suite / Run Component Tests & Pipeline Verification (push) Successful in 1m29s

This commit is contained in:
2026-09-26 13:20:41 +02:00
parent a496b7e822
commit b0bf322275
4 changed files with 78 additions and 9 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[Setup] [Setup]
AppName=LOGAR Client AppName=LOGAR Client
AppVersion=3.0.0 AppVersion=3.0.1
DefaultDirName={autopf}\LOGAR DefaultDirName={autopf}\LOGAR
OutputDir=..\dist OutputDir=..\dist
OutputBaseFilename=LOGAR-Client-Setup OutputBaseFilename=LOGAR-Client-Setup
+1 -1
View File
@@ -1,6 +1,6 @@
[Setup] [Setup]
AppName=LOGAR Server AppName=LOGAR Server
AppVersion=3.0.0 AppVersion=3.0.1
DefaultDirName={autopf}\LOGAR-Server DefaultDirName={autopf}\LOGAR-Server
OutputDir=..\dist OutputDir=..\dist
OutputBaseFilename=LOGAR-Server-Setup OutputBaseFilename=LOGAR-Server-Setup
+59 -2
View File
@@ -139,10 +139,13 @@ def create_windows_exe_zipapp(script_name, exe_name):
def compile_inno_setup_installers(): def compile_inno_setup_installers():
"""Compiles Inno Setup installers if iscc or innosetup wrapper is available.""" """Compiles Inno Setup installers if iscc or innosetup wrapper is available."""
iscc_cmd = None iscc_cmd = None
user_iscc = os.path.expanduser(r"~\AppData\Local\Programs\Inno Setup 6\ISCC.exe")
if shutil.which("iscc"): if shutil.which("iscc"):
iscc_cmd = ["iscc"] iscc_cmd = ["iscc"]
elif shutil.which("innosetup"): elif shutil.which("innosetup"):
iscc_cmd = ["innosetup"] iscc_cmd = ["innosetup"]
elif os.path.exists(user_iscc):
iscc_cmd = [user_iscc]
elif os.path.exists("C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe"): elif os.path.exists("C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe"):
iscc_cmd = ["C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe"] iscc_cmd = ["C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe"]
elif os.path.exists("C:\\Program Files\\Inno Setup 6\\ISCC.exe"): elif os.path.exists("C:\\Program Files\\Inno Setup 6\\ISCC.exe"):
@@ -192,6 +195,26 @@ def build_windows():
client_bin = os.path.join(DIST_DIR, "Win_Client.exe") client_bin = os.path.join(DIST_DIR, "Win_Client.exe")
server_bin = os.path.join(DIST_DIR, "Server.exe") server_bin = os.path.join(DIST_DIR, "Server.exe")
# Windows Zip Bundles
import zipfile
if os.path.exists(client_bin):
client_zip = os.path.join(DIST_DIR, "LOGAR-Windows-Client.zip")
with zipfile.ZipFile(client_zip, "w", zipfile.ZIP_DEFLATED) as zf:
zf.write(client_bin, arcname="Win_Client.exe")
sample_cfg = os.path.join(ROOT_DIR, "out", "win_client", "client_config.sample.json")
if os.path.exists(sample_cfg):
zf.write(sample_cfg, arcname="client_config.sample.json")
print(f"[+] Packaged Windows Client Zip: {os.path.basename(client_zip)}")
if os.path.exists(server_bin):
server_zip = os.path.join(DIST_DIR, "LOGAR-Windows-Server.zip")
with zipfile.ZipFile(server_zip, "w", zipfile.ZIP_DEFLATED) as zf:
zf.write(server_bin, arcname="Server.exe")
sample_cfg = os.path.join(ROOT_DIR, "server_config.sample.json")
if os.path.exists(sample_cfg):
zf.write(sample_cfg, arcname="server_config.sample.json")
print(f"[+] Packaged Windows Server Zip: {os.path.basename(server_zip)}")
sums = [] sums = []
if os.path.exists(client_bin): if os.path.exists(client_bin):
client_hash = calculate_sha256(client_bin) client_hash = calculate_sha256(client_bin)
@@ -209,7 +232,7 @@ def build_windows():
print(f"[!] Warning: Expected {server_bin} was not found.") print(f"[!] Warning: Expected {server_bin} was not found.")
# Inno Setup output checksums if present # Inno Setup output checksums if present
for inst in ["LOGAR-Client-Setup.exe", "LOGAR-Server-Setup.exe"]: for inst in ["LOGAR-Client-Setup.exe", "LOGAR-Server-Setup.exe", "LOGAR-Windows-Client.zip", "LOGAR-Windows-Server.zip"]:
inst_path = os.path.join(DIST_DIR, inst) inst_path = os.path.join(DIST_DIR, inst)
if os.path.exists(inst_path): if os.path.exists(inst_path):
inst_hash = calculate_sha256(inst_path) inst_hash = calculate_sha256(inst_path)
@@ -221,7 +244,7 @@ def build_windows():
def build_linux(): def build_linux():
print("[*] Compiling Linux standalone binaries...") print("[*] Compiling Linux standalone binaries and packaging installers...")
is_linux_host = platform.system() == "Linux" is_linux_host = platform.system() == "Linux"
if is_linux_host: if is_linux_host:
@@ -254,9 +277,37 @@ def build_linux():
print("[!] Note: Host platform is not Linux. Generating executable zipapps for Linux target.") print("[!] Note: Host platform is not Linux. Generating executable zipapps for Linux target.")
build_linux_zipapp_fallback() build_linux_zipapp_fallback()
# Copy Linux installation scripts directly to dist
for sh in ["install_linux_client.sh", "install_linux_server.sh"]:
sh_src = os.path.join(ROOT_DIR, "compilation", sh)
if os.path.exists(sh_src):
sh_dst = os.path.join(DIST_DIR, sh)
shutil.copy(sh_src, sh_dst)
print(f"[+] Copied Linux installer script: {sh}")
# Package Linux tarballs
import tarfile
client_bin = os.path.join(DIST_DIR, "Linux_Client.bin") client_bin = os.path.join(DIST_DIR, "Linux_Client.bin")
server_bin = os.path.join(DIST_DIR, "Server.bin") server_bin = os.path.join(DIST_DIR, "Server.bin")
if os.path.exists(client_bin):
client_tar = os.path.join(DIST_DIR, "LOGAR-Linux-Client.tar.gz")
with tarfile.open(client_tar, "w:gz") as tf:
tf.add(client_bin, arcname="Linux_Client.bin")
client_sh = os.path.join(DIST_DIR, "install_linux_client.sh")
if os.path.exists(client_sh):
tf.add(client_sh, arcname="install_linux_client.sh")
print(f"[+] Packaged Linux Client Tarball: {os.path.basename(client_tar)}")
if os.path.exists(server_bin):
server_tar = os.path.join(DIST_DIR, "LOGAR-Linux-Server.tar.gz")
with tarfile.open(server_tar, "w:gz") as tf:
tf.add(server_bin, arcname="Server.bin")
server_sh = os.path.join(DIST_DIR, "install_linux_server.sh")
if os.path.exists(server_sh):
tf.add(server_sh, arcname="install_linux_server.sh")
print(f"[+] Packaged Linux Server Tarball: {os.path.basename(server_tar)}")
sums = [] sums = []
if os.path.exists(client_bin): if os.path.exists(client_bin):
client_hash = calculate_sha256(client_bin) client_hash = calculate_sha256(client_bin)
@@ -273,6 +324,12 @@ def build_linux():
else: else:
print(f"[!] Warning: Expected {server_bin} was not found.") print(f"[!] Warning: Expected {server_bin} was not found.")
for item in ["install_linux_client.sh", "install_linux_server.sh", "LOGAR-Linux-Client.tar.gz", "LOGAR-Linux-Server.tar.gz"]:
item_path = os.path.join(DIST_DIR, item)
if os.path.exists(item_path):
item_hash = calculate_sha256(item_path)
sums.append(f"{item_hash} {item}")
sums_path = os.path.join(DIST_DIR, "SHA256SUMS_linux.txt") sums_path = os.path.join(DIST_DIR, "SHA256SUMS_linux.txt")
with open(sums_path, "w", encoding="utf-8") as f: with open(sums_path, "w", encoding="utf-8") as f:
f.write("\n".join(sums) + "\n") f.write("\n".join(sums) + "\n")
+17 -5
View File
@@ -13,9 +13,21 @@ import time
DEFAULT_GITEA_URL = os.environ.get("GITEA_SERVER_URL", "https://gitea.eibl.tech") DEFAULT_GITEA_URL = os.environ.get("GITEA_SERVER_URL", "https://gitea.eibl.tech")
DEFAULT_REPO = os.environ.get("GITEA_REPOSITORY", "me0nline/LOGAR") DEFAULT_REPO = os.environ.get("GITEA_REPOSITORY", "me0nline/LOGAR")
import base64
def get_auth_header(token):
if not token:
return ""
if token.startswith("Basic ") or token.startswith("token ") or token.startswith("Bearer "):
return token
if ":" in token:
b64 = base64.b64encode(token.encode("utf-8")).decode("ascii")
return f"Basic {b64}"
return f"token {token}"
def get_existing_assets(base_url, repo, release_id, token): def get_existing_assets(base_url, repo, release_id, token):
url = f"{base_url}/api/v1/repos/{repo}/releases/{release_id}/assets" url = f"{base_url}/api/v1/repos/{repo}/releases/{release_id}/assets"
req = urllib.request.Request(url, headers={"Authorization": f"token {token}", "Accept": "application/json"}) req = urllib.request.Request(url, headers={"Authorization": get_auth_header(token), "Accept": "application/json"})
try: try:
with urllib.request.urlopen(req) as resp: with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode("utf-8")) return json.loads(resp.read().decode("utf-8"))
@@ -24,7 +36,7 @@ def get_existing_assets(base_url, repo, release_id, token):
def delete_asset(base_url, repo, release_id, asset_id, token): def delete_asset(base_url, repo, release_id, asset_id, token):
url = f"{base_url}/api/v1/repos/{repo}/releases/{release_id}/assets/{asset_id}" url = f"{base_url}/api/v1/repos/{repo}/releases/{release_id}/assets/{asset_id}"
req = urllib.request.Request(url, method="DELETE", headers={"Authorization": f"token {token}"}) req = urllib.request.Request(url, method="DELETE", headers={"Authorization": get_auth_header(token)})
try: try:
with urllib.request.urlopen(req) as resp: with urllib.request.urlopen(req) as resp:
pass pass
@@ -47,7 +59,7 @@ def upload_file_to_release(base_url, repo, release_id, token, file_path, max_ret
for attempt in range(1, max_retries + 1): for attempt in range(1, max_retries + 1):
req = urllib.request.Request(url, data=file_bytes, method="POST") req = urllib.request.Request(url, data=file_bytes, method="POST")
req.add_header("Authorization", f"token {token}") req.add_header("Authorization", get_auth_header(token))
req.add_header("Content-Type", "application/octet-stream") req.add_header("Content-Type", "application/octet-stream")
req.add_header("Accept", "application/json") req.add_header("Accept", "application/json")
@@ -73,14 +85,14 @@ def upload_file_to_release(base_url, repo, release_id, token, file_path, max_ret
def create_or_get_release(base_url, repo, tag, token, title=None, notes=None): def create_or_get_release(base_url, repo, tag, token, title=None, notes=None):
url = f"{base_url}/api/v1/repos/{repo}/releases" url = f"{base_url}/api/v1/repos/{repo}/releases"
headers = { headers = {
"Authorization": f"token {token}", "Authorization": get_auth_header(token),
"Content-Type": "application/json", "Content-Type": "application/json",
"Accept": "application/json" "Accept": "application/json"
} }
# Check if release exists # Check if release exists
check_url = f"{base_url}/api/v1/repos/{repo}/releases/tags/{urllib.parse.quote(tag)}" check_url = f"{base_url}/api/v1/repos/{repo}/releases/tags/{urllib.parse.quote(tag)}"
check_req = urllib.request.Request(check_url, headers={"Authorization": f"token {token}"}) check_req = urllib.request.Request(check_url, headers={"Authorization": get_auth_header(token)})
try: try:
with urllib.request.urlopen(check_req) as resp: with urllib.request.urlopen(check_req) as resp:
existing = json.loads(resp.read().decode("utf-8")) existing = json.loads(resp.read().decode("utf-8"))