From 41d7d66a56bea8fc3a387c26e29c5d48fed17792 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 30 Sep 2026 18:18:53 +0200 Subject: [PATCH] feat(packaging): dynamically resolve installer version info from build context --- .gitignore | 4 + compilation/installer_client.iss | 14 +++- compilation/installer_client.nsi | 17 ++++- compilation/installer_server.iss | 14 +++- compilation/installer_server.nsi | 17 ++++- compilation/package_dist.py | 127 ++++++++++++++++++++++++++++++- 6 files changed, 177 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 042b984..dafdd6e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,10 @@ out/ *.dll *.so *.dylib +*.nsh +*.iss.tmp +version.nsh +version.iss # Database files & state *.db diff --git a/compilation/installer_client.iss b/compilation/installer_client.iss index 78073ba..0a4ef43 100644 --- a/compilation/installer_client.iss +++ b/compilation/installer_client.iss @@ -1,7 +1,15 @@ +#ifFileExists("version.iss") + #include "version.iss" +#endif + +#ifndef AppVersion + #define AppVersion "0.3.5.1" +#endif + [Setup] AppId={{9C4A6F1D-8975-476E-8512-4217DE029A88}} AppName=LOGAR Client -AppVersion=0.3.5.1 +AppVersion={#AppVersion} AppPublisher=LOGAR AppPublisherURL=https://logar.eibl.tech AppSupportURL=https://logar.eibl.tech @@ -19,9 +27,9 @@ WizardImageFile=assets\wizard_sidebar.bmp WizardSmallImageFile=assets\wizard_small.bmp VersionInfoCompany=LOGAR VersionInfoDescription=LOGAR Windows Edge Log Forwarder Setup -VersionInfoVersion=0.3.5.1 +VersionInfoVersion={#AppVersion} VersionInfoProductName=LOGAR Client -VersionInfoProductVersion=0.3.5.1 +VersionInfoProductVersion={#AppVersion} [Files] Source: "..\dist\Win_Client.exe"; DestDir: "{app}"; Flags: ignoreversion diff --git a/compilation/installer_client.nsi b/compilation/installer_client.nsi index c7eb57c..df7caa2 100644 --- a/compilation/installer_client.nsi +++ b/compilation/installer_client.nsi @@ -9,13 +9,24 @@ RequestExecutionLevel admin BrandingText "LOGAR Centralized Log Aggregator && Forwarder" -VIProductVersion "0.3.5.1" +!ifFileExists "version.nsh" + !include "version.nsh" +!endif + +!ifndef APP_VERSION + !define APP_VERSION "0.0.0.0" +!endif +!ifndef DISPLAY_VERSION + !define DISPLAY_VERSION "${APP_VERSION}" +!endif + +VIProductVersion "${APP_VERSION}" VIAddVersionKey "ProductName" "LOGAR Client" VIAddVersionKey "CompanyName" "LOGAR" VIAddVersionKey "LegalCopyright" "© 2026 LOGAR" VIAddVersionKey "FileDescription" "LOGAR Windows Edge Log Forwarder Setup" -VIAddVersionKey "FileVersion" "0.3.5.1" -VIAddVersionKey "ProductVersion" "0.3.5.1" +VIAddVersionKey "FileVersion" "${APP_VERSION}" +VIAddVersionKey "ProductVersion" "${DISPLAY_VERSION}" # --- Visual Graphics & Branding --- !define MUI_ICON "assets/logo.ico" diff --git a/compilation/installer_server.iss b/compilation/installer_server.iss index 4101521..b50fe01 100644 --- a/compilation/installer_server.iss +++ b/compilation/installer_server.iss @@ -1,7 +1,15 @@ +#ifFileExists("version.iss") + #include "version.iss" +#endif + +#ifndef AppVersion + #define AppVersion "0.3.5.1" +#endif + [Setup] AppId={{4F3D675E-1E4E-43B6-8C84-B47DE290BE4A}} AppName=LOGAR Server -AppVersion=0.3.5.1 +AppVersion={#AppVersion} AppPublisher=LOGAR AppPublisherURL=https://logar.eibl.tech AppSupportURL=https://logar.eibl.tech @@ -19,9 +27,9 @@ WizardImageFile=assets\wizard_sidebar.bmp WizardSmallImageFile=assets\wizard_small.bmp VersionInfoCompany=LOGAR VersionInfoDescription=LOGAR Cloud Hub & TCP Ingestion Server Setup -VersionInfoVersion=0.3.5.1 +VersionInfoVersion={#AppVersion} VersionInfoProductName=LOGAR Server -VersionInfoProductVersion=0.3.5.1 +VersionInfoProductVersion={#AppVersion} [Files] Source: "..\dist\Server.exe"; DestDir: "{app}"; Flags: ignoreversion diff --git a/compilation/installer_server.nsi b/compilation/installer_server.nsi index 65c4d12..498927c 100644 --- a/compilation/installer_server.nsi +++ b/compilation/installer_server.nsi @@ -8,13 +8,24 @@ RequestExecutionLevel admin BrandingText "LOGAR Centralized Log Aggregator && Ingestion Hub" -VIProductVersion "0.3.5.1" +!ifFileExists "version.nsh" + !include "version.nsh" +!endif + +!ifndef APP_VERSION + !define APP_VERSION "0.0.0.0" +!endif +!ifndef DISPLAY_VERSION + !define DISPLAY_VERSION "${APP_VERSION}" +!endif + +VIProductVersion "${APP_VERSION}" VIAddVersionKey "ProductName" "LOGAR Server" VIAddVersionKey "CompanyName" "LOGAR" VIAddVersionKey "LegalCopyright" "© 2026 LOGAR" VIAddVersionKey "FileDescription" "LOGAR Cloud Hub & TCP Ingestion Server Setup" -VIAddVersionKey "FileVersion" "0.3.5.1" -VIAddVersionKey "ProductVersion" "0.3.5.1" +VIAddVersionKey "FileVersion" "${APP_VERSION}" +VIAddVersionKey "ProductVersion" "${DISPLAY_VERSION}" # --- Visual Graphics & Branding --- !define MUI_ICON "assets/logo.ico" diff --git a/compilation/package_dist.py b/compilation/package_dist.py index 97ba729..f0b6bab 100644 --- a/compilation/package_dist.py +++ b/compilation/package_dist.py @@ -290,6 +290,115 @@ def ensure_libpython(): pass +import re + + +def get_context_version(): + """ + Extracts the current release version from the build context: + 1. Environment variables (APP_VERSION, LOGAR_VERSION, GITEA_REF_NAME, GITHUB_REF_NAME, GITHUB_REF) + 2. Git tag via `git describe --tags --exact-match` or `git describe --tags` + 3. RELEASE_NOTES.md top header + 4. README.md version badge + Returns tuple: (display_version, four_part_version) + e.g. ('0.3.5.1', '0.3.5.1') or ('0.3.5', '0.3.5.0') + """ + raw_version = "" + + # 1. Environment variables + for env_var in ["APP_VERSION", "LOGAR_VERSION", "GITEA_REF_NAME", "GITHUB_REF_NAME", "GITHUB_REF"]: + val = os.environ.get(env_var, "").strip() + if val: + if val.startswith("refs/tags/"): + val = val[len("refs/tags/"):] + if val.startswith("v") and len(val) > 1 and val[1].isdigit(): + val = val[1:] + if any(c.isdigit() for c in val): + raw_version = val + break + + # 2. Git command + if not raw_version: + try: + res = subprocess.run(["git", "describe", "--tags", "--exact-match"], cwd=ROOT_DIR, capture_output=True, text=True, check=False) + if res.returncode == 0 and res.stdout.strip(): + tag = res.stdout.strip() + if tag.startswith("v") and len(tag) > 1 and tag[1].isdigit(): + tag = tag[1:] + raw_version = tag + except Exception: + pass + + if not raw_version: + try: + res = subprocess.run(["git", "describe", "--tags"], cwd=ROOT_DIR, capture_output=True, text=True, check=False) + if res.returncode == 0 and res.stdout.strip(): + tag = res.stdout.strip().split("-")[0] + if tag.startswith("v") and len(tag) > 1 and tag[1].isdigit(): + tag = tag[1:] + raw_version = tag + except Exception: + pass + + # 3. RELEASE_NOTES.md + if not raw_version: + rel_notes = os.path.join(ROOT_DIR, "RELEASE_NOTES.md") + if os.path.exists(rel_notes): + try: + with open(rel_notes, "r", encoding="utf-8") as f: + content = f.read() + m = re.search(r"##\s*\[v?([0-9.]+)\]", content) + if m: + raw_version = m.group(1).strip() + except Exception: + pass + + # 4. README.md + if not raw_version: + readme = os.path.join(ROOT_DIR, "README.md") + if os.path.exists(readme): + try: + with open(readme, "r", encoding="utf-8") as f: + content = f.read() + m = re.search(r"version-v?([0-9.]+)-blue", content) + if m: + raw_version = m.group(1).strip() + except Exception: + pass + + # Fallback + if not raw_version: + raw_version = "0.3.5.1" + + # Clean display version (strip non-standard characters, keep digits and dots) + m_clean = re.search(r"([0-9]+(?:\.[0-9]+)*)", raw_version) + display_version = m_clean.group(1) if m_clean else raw_version + + # Format 4-part numeric version for NSIS VIProductVersion (X.X.X.X) + parts = display_version.split(".") + while len(parts) < 4: + parts.append("0") + four_part_version = ".".join(parts[:4]) + + return display_version, four_part_version + + +def write_version_headers(display_version, four_part_version): + """Writes dynamic version header files for NSIS and Inno Setup compilers.""" + compilation_dir = os.path.join(ROOT_DIR, "compilation") + os.makedirs(compilation_dir, exist_ok=True) + + nsh_file = os.path.join(compilation_dir, "version.nsh") + with open(nsh_file, "w", encoding="utf-8") as f: + f.write(f'; Auto-generated version header from build context\n!define APP_VERSION "{four_part_version}"\n!define DISPLAY_VERSION "{display_version}"\n') + + iss_file = os.path.join(compilation_dir, "version.iss") + with open(iss_file, "w", encoding="utf-8") as f: + f.write(f'; Auto-generated version header from build context\n#define AppVersion "{display_version}"\n') + + print(f"[+] Dynamic context version resolved: {display_version} (NSIS VIProductVersion: {four_part_version})") + + def compile_nsis_installers(): """Compiles NSIS installers if makensis is available (native Linux/Windows).""" makensis_bin = ensure_makensis() @@ -297,13 +406,20 @@ def compile_nsis_installers(): print("[!] Note: NSIS compiler (makensis) not found in PATH or standard directories.") return False - makensis_cmd = [makensis_bin] + display_ver, vi_ver = get_context_version() + write_version_headers(display_ver, vi_ver) + compilation_dir = os.path.join(ROOT_DIR, "compilation") + makensis_cmd = [ + makensis_bin, + f"-DAPP_VERSION={vi_ver}", + f"-DDISPLAY_VERSION={display_ver}" + ] success = True for nsi in ["installer_client.nsi", "installer_server.nsi"]: nsi_path = os.path.join(compilation_dir, nsi) if os.path.exists(nsi_path): - print(f"[*] Compiling NSIS script: {nsi} via {makensis_cmd[0]}...") + print(f"[*] Compiling NSIS script: {nsi} (v{display_ver}) via {makensis_cmd[0]}...") try: subprocess.run(makensis_cmd + [nsi_path], cwd=compilation_dir, check=True) print(f"[+] Successfully compiled NSIS installer for {nsi}") @@ -338,12 +454,15 @@ def compile_inno_setup_installers(): print("[!] Note: Inno Setup compiler (iscc / innosetup) not available on host. Skipping installer .exe creation.") return False + display_ver, vi_ver = get_context_version() + write_version_headers(display_ver, vi_ver) + for iss in ["installer_client.iss", "installer_server.iss"]: iss_path = os.path.join(ROOT_DIR, "compilation", iss) if os.path.exists(iss_path): - print(f"[*] Compiling Inno Setup script: {iss}...") + print(f"[*] Compiling Inno Setup script: {iss} (v{display_ver})...") try: - subprocess.run(iscc_cmd + [iss_path], check=True) + subprocess.run(iscc_cmd + [f"/DAppVersion={display_ver}", iss_path], check=True) print(f"[+] Successfully compiled {iss}") except Exception as e: print(f"[!] Warning: Failed to compile {iss} with {iscc_cmd}: {e}")