feat(packaging): dynamically resolve installer version info from build context
This commit is contained in:
+123
-4
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user