#!/usr/bin/env python3 """ generate_version_json.py Auto-generates version.json by: 1. Reading the current version from project.godot 2. Auto-bumping the PATCH number 3. Reading player-friendly entries from CHANGELOG_DRAFT.md 4. Injecting them into version.json with a date stamp 5. Updating version in export_presets.cfg export paths 6. Clearing the [NEXT] section in CHANGELOG_DRAFT.md Usage: python3 tools/generate_version_json.py # Full release (CI) python3 tools/generate_version_json.py --local # Local: update versions but keep changelog python3 tools/generate_version_json.py --skip-changelog # Skip changelog update """ import json import re import os import sys import argparse from datetime import date PROJECT_GODOT = "project.godot" CHANGELOG_DRAFT = "CHANGELOG_DRAFT.md" VERSION_JSON = "assets/data/version.json" EXPORT_PRESETS = "export_presets.cfg" MANIFEST_URL = "https://raw.githubusercontent.com/adtpdn/tekton-updates/main/latest/patch.pck" # ─── Parse command line arguments ───────────────────────────────────────────── parser = argparse.ArgumentParser(description="Generate version.json and update version numbers") parser.add_argument("--local", action="store_true", help="Local mode: update version.json and export_presets but don't clear changelog") parser.add_argument("--skip-changelog", action="store_true", help="Skip updating CHANGELOG_DRAFT.md") args = parser.parse_args() # ─── 1. Read current version from project.godot ────────────────────────────── version_str = "2.1.5" with open(PROJECT_GODOT, "r") as f: for line in f: m = re.search(r'config/version="([^"]+)"', line) if m: version_str = m.group(1) break # ─── 2. Auto-bump patch number ─────────────────────────────────────────────── parts = version_str.split(".") parts[-1] = str(int(parts[-1]) + 1) new_version = ".".join(parts) print(f"Version: {version_str} -> {new_version}") # ─── 3. Read CHANGELOG_DRAFT.md ────────────────────────────────────────────── changelog_lines = [] in_next = False remaining_lines = [] with open(CHANGELOG_DRAFT, "r", encoding="utf-8") as f: raw = f.readlines() for line in raw: if line.strip() == "## [NEXT]": in_next = True continue if in_next: # Stop at next ## heading if line.startswith("## ") and "[NEXT]" not in line: in_next = False remaining_lines.append(line) elif line.strip(): # Strip leading "- " and emoji bullet clean = line.strip().lstrip("- ").strip() changelog_lines.append(clean) else: remaining_lines.append(line) if not changelog_lines: print("[INFO] No [NEXT] entries found in CHANGELOG_DRAFT.md -- skipping version bump and release generation.") exit(0) print(f"Changelog entries: {len(changelog_lines)}") # ─── 4. Load existing version.json ─────────────────────────────────────────── existing = {"releases": []} if os.path.exists(VERSION_JSON): with open(VERSION_JSON, "r") as f: existing = json.load(f) # ─── 5. Prepend new release ─────────────────────────────────────────────────── today = date.today().isoformat() new_release = { "version": new_version, "date": today, "pck_url": MANIFEST_URL, "pck_size": 0, "changelog": changelog_lines } # Remove any existing entry with the same version existing["releases"] = [r for r in existing["releases"] if r.get("version") != new_version] existing["releases"].insert(0, new_release) manifest = { "latest_version": new_version, "minimum_app_version": existing.get("minimum_app_version", "2.1.0"), "releases": existing["releases"] } with open(VERSION_JSON, "w", encoding="utf-8") as f: json.dump(manifest, f, indent="\t") f.write("\n") print(f"[OK] Written: {VERSION_JSON} (latest={new_version})") # ─── 6. Bump version in project.godot ──────────────────────────────────────── with open(PROJECT_GODOT, "r") as f: godot_content = f.read() godot_content = re.sub( r'(config/version=")[^"]+(")', rf'\g<1>{new_version}\g<2>', godot_content ) with open(PROJECT_GODOT, "w", encoding="utf-8") as f: f.write(godot_content) print(f"[OK] Bumped project.godot -> {new_version}") # ─── 7. Update version in export_presets.cfg ─────────────────────────────────── if os.path.exists(EXPORT_PRESETS): with open(EXPORT_PRESETS, "r", encoding="utf-8") as f: presets_content = f.read() # Replace version in desktop export paths (e.g., v2.1.7 -> v2.1.8) # Desktop: tekton_armageddon_v2.1.7.exe/zip/x86_64 # Android: tekton-dash-armageddon-v.2.1.5.apk (different format) desktop_pattern = r'tekton_armageddon_v\d+\.\d+\.\d+' presets_content = re.sub(desktop_pattern, f'tekton_armageddon_v{new_version}', presets_content) # Also update Android version if needed (different format) android_pattern = r'tekton-dash-armageddon-v\.\d+\.\d+\.\d+' presets_content = re.sub(android_pattern, f'tekton-dash-armageddon-v.{new_version}', presets_content) with open(EXPORT_PRESETS, "w", encoding="utf-8") as f: f.write(presets_content) print(f"[OK] Updated export paths in {EXPORT_PRESETS} -> v{new_version}") else: print(f"[WARN] {EXPORT_PRESETS} not found, skipping export path update") # ─── 8. Clear [NEXT] in CHANGELOG_DRAFT.md ─────────────────────────────────── if not args.local and not args.skip_changelog: archived_header = f"## [{new_version}] — {today}\n" archived_entries = "".join(f"- {line}\n" for line in changelog_lines) new_draft = "## [NEXT]\n\n" + archived_header + archived_entries if remaining_lines: new_draft += "\n" + "".join(remaining_lines) with open(CHANGELOG_DRAFT, "w", encoding="utf-8") as f: f.write(new_draft) print(f"[OK] Cleared [NEXT] in {CHANGELOG_DRAFT}, archived as [{new_version}]") else: print(f"[INFO] Skipped updating {CHANGELOG_DRAFT} (--local or --skip-changelog mode)") print(f"\nDone! Version bumped to v{new_version}.") if args.local: print("Local mode: Changelog not cleared. Run without --local to finalize release.")