mirror of
https://github.com/thegatesbrowser/thegates.git
synced 2025-09-02 11:25:51 -04:00
deployment scripts
This commit is contained in:
parent
03fbf12b9a
commit
eff4c36456
2 changed files with 350 additions and 0 deletions
239
deployment/export_project.py
Normal file
239
deployment/export_project.py
Normal file
|
@ -0,0 +1,239 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Automated Godot export script.
|
||||
|
||||
Behavior:
|
||||
- On Linux: exports release build for preset "Linux/X11" (export project)
|
||||
and exports release pack for preset "Windows Desktop" (export pck).
|
||||
- On macOS: exports release build for preset "macOS" (export project).
|
||||
- On Windows: exports release build for preset "Windows Desktop" (export project).
|
||||
|
||||
All exports use the export paths defined in app/export_presets.cfg.
|
||||
For pack export, the output .pck path is derived from the preset's export_path
|
||||
by replacing ".exe" with ".pck" (or appending ".pck" if no extension found).
|
||||
|
||||
Special case: when exporting Windows pack from Linux host, override output path to
|
||||
"/media/common/Projects/thegates-folder/AppBuilds/Windows/TheGates.pck".
|
||||
|
||||
Editor binaries to use:
|
||||
- Linux: godot/bin/godot.linuxbsd.editor.dev.x86_64.llvm
|
||||
- macOS: godot/bin/godot.macos.editor.dev.arm64
|
||||
- Windows: godot/bin/godot.windows.editor.dev.x86_64.exe
|
||||
|
||||
Only release builds are exported (uses --export-release).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
APP_DIR = REPO_ROOT / "app"
|
||||
EXPORT_PRESETS_PATH = APP_DIR / "export_presets.cfg"
|
||||
|
||||
|
||||
LINUX_EDITOR_RELATIVE = Path("godot/bin/godot.linuxbsd.editor.dev.x86_64.llvm")
|
||||
MACOS_EDITOR_RELATIVE = Path("godot/bin/godot.macos.editor.dev.arm64")
|
||||
WINDOWS_EDITOR_RELATIVE = Path("godot/bin/godot.windows.editor.dev.x86_64.exe")
|
||||
|
||||
LINUX_WINDOWS_PCK_OVERRIDE_PATH = Path("/media/common/Projects/thegates-folder/AppBuilds/Windows/TheGates.pck")
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExportPreset:
|
||||
index: int
|
||||
name: str
|
||||
platform: Optional[str]
|
||||
export_path: Optional[str]
|
||||
|
||||
|
||||
def read_export_presets(cfg_path: Path) -> Dict[str, ExportPreset]:
|
||||
"""Parse Godot export_presets.cfg and return presets keyed by name.
|
||||
|
||||
We only care about fields under [preset.N]: name, platform, export_path.
|
||||
"""
|
||||
if not cfg_path.exists():
|
||||
raise FileNotFoundError(f"export presets not found: {cfg_path}")
|
||||
|
||||
presets: Dict[str, ExportPreset] = {}
|
||||
current: Optional[ExportPreset] = None
|
||||
|
||||
with cfg_path.open("r", encoding="utf-8") as f:
|
||||
for raw_line in f:
|
||||
line = raw_line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith("["):
|
||||
# Starting a new section.
|
||||
if line.startswith("[preset.") and line.endswith("]"):
|
||||
try:
|
||||
idx_str = line[len("[preset.") : -1]
|
||||
idx = int(idx_str)
|
||||
except ValueError:
|
||||
current = None
|
||||
else:
|
||||
current = ExportPreset(index=idx, name="", platform=None, export_path=None)
|
||||
else:
|
||||
# Any non-preset section ends current preset parsing
|
||||
current = None
|
||||
continue
|
||||
|
||||
if current is None:
|
||||
continue
|
||||
|
||||
if line.startswith("name="):
|
||||
current.name = line.split("=", 1)[1].strip().strip('"')
|
||||
elif line.startswith("platform="):
|
||||
current.platform = line.split("=", 1)[1].strip().strip('"')
|
||||
elif line.startswith("export_path="):
|
||||
current.export_path = line.split("=", 1)[1].strip().strip('"')
|
||||
|
||||
# Once we have a name, keep it indexed so later lookups are easy
|
||||
if current.name:
|
||||
presets[current.name] = current
|
||||
|
||||
return presets
|
||||
|
||||
|
||||
def ensure_editor_binary() -> Path:
|
||||
system = platform.system()
|
||||
if system == "Linux":
|
||||
editor_rel = LINUX_EDITOR_RELATIVE
|
||||
elif system == "Darwin":
|
||||
editor_rel = MACOS_EDITOR_RELATIVE
|
||||
elif system == "Windows":
|
||||
editor_rel = WINDOWS_EDITOR_RELATIVE
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported OS for this script: {system}")
|
||||
|
||||
editor_path = (REPO_ROOT / editor_rel).resolve()
|
||||
if not editor_path.exists():
|
||||
raise FileNotFoundError(
|
||||
f"Godot editor binary not found: {editor_path}\n"
|
||||
f"Expected relative path: {editor_rel}"
|
||||
)
|
||||
return editor_path
|
||||
|
||||
|
||||
def run_cmd(cmd: List[str], cwd: Path) -> None:
|
||||
print(f"Running: {shlex.join(cmd)}\n in: {cwd}")
|
||||
subprocess.run(cmd, cwd=str(cwd), check=True)
|
||||
|
||||
|
||||
def derive_pck_path_from_export_path(export_path: str) -> Path:
|
||||
path = Path(export_path)
|
||||
if path.suffix.lower() == ".exe":
|
||||
return path.with_suffix(".pck")
|
||||
if path.suffix:
|
||||
# Replace any existing extension with .pck
|
||||
return path.with_suffix(".pck")
|
||||
return path.with_suffix(".pck")
|
||||
|
||||
|
||||
def export_linux_and_windows_pack(editor: Path, presets: Dict[str, ExportPreset]) -> None:
|
||||
# Linux project export (uses export path defined in preset)
|
||||
linux_preset_name = "Linux/X11"
|
||||
if linux_preset_name not in presets:
|
||||
raise KeyError(f"Preset not found: {linux_preset_name}")
|
||||
|
||||
run_cmd(
|
||||
[str(editor), "--headless", "--export-release", linux_preset_name],
|
||||
cwd=APP_DIR,
|
||||
)
|
||||
|
||||
# Windows pack export (override .pck path per requirement when on Linux)
|
||||
windows_preset_name = "Windows Desktop"
|
||||
win_preset = presets.get(windows_preset_name)
|
||||
if win_preset is None:
|
||||
raise KeyError(f"Preset not found: {windows_preset_name}")
|
||||
|
||||
# Use the override path instead of preset-defined path
|
||||
pck_out_path = LINUX_WINDOWS_PCK_OVERRIDE_PATH
|
||||
|
||||
# Ensure output directory exists for the pack
|
||||
pck_out_dir = Path(pck_out_path).parent
|
||||
try:
|
||||
pck_out_dir.mkdir(parents=True, exist_ok=True)
|
||||
except Exception:
|
||||
# If directory is not creatable (e.g., Windows drive on Linux), let Godot handle/raise.
|
||||
pass
|
||||
|
||||
run_cmd(
|
||||
[
|
||||
str(editor),
|
||||
"--headless",
|
||||
"--export-pack",
|
||||
windows_preset_name,
|
||||
str(pck_out_path),
|
||||
],
|
||||
cwd=APP_DIR,
|
||||
)
|
||||
|
||||
|
||||
def export_macos(editor: Path, presets: Dict[str, ExportPreset]) -> None:
|
||||
mac_preset_name = "macOS"
|
||||
if mac_preset_name not in presets:
|
||||
raise KeyError(f"Preset not found: {mac_preset_name}")
|
||||
|
||||
run_cmd(
|
||||
[str(editor), "--headless", "--export-release", mac_preset_name],
|
||||
cwd=APP_DIR,
|
||||
)
|
||||
|
||||
|
||||
def export_windows(editor: Path, presets: Dict[str, ExportPreset]) -> None:
|
||||
windows_preset_name = "Windows Desktop"
|
||||
if windows_preset_name not in presets:
|
||||
raise KeyError(f"Preset not found: {windows_preset_name}")
|
||||
|
||||
run_cmd(
|
||||
[str(editor), "--headless", "--export-release", windows_preset_name],
|
||||
cwd=APP_DIR,
|
||||
)
|
||||
|
||||
|
||||
def import_project(editor: Path) -> None:
|
||||
# Ensure all assets are imported prior to export
|
||||
run_cmd([str(editor), "--headless", "--import"], cwd=APP_DIR)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
presets = read_export_presets(EXPORT_PRESETS_PATH)
|
||||
editor = ensure_editor_binary()
|
||||
system = platform.system()
|
||||
|
||||
# Always import project before any export
|
||||
import_project(editor)
|
||||
|
||||
if system == "Linux":
|
||||
export_linux_and_windows_pack(editor, presets)
|
||||
elif system == "Darwin":
|
||||
export_macos(editor, presets)
|
||||
elif system == "Windows":
|
||||
export_windows(editor, presets)
|
||||
else:
|
||||
raise RuntimeError(f"Unsupported OS: {system}")
|
||||
|
||||
print("All exports completed successfully.")
|
||||
return 0
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Export command failed with exit code {e.returncode}")
|
||||
return e.returncode
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
|
111
deployment/zip_builds_linux.py
Executable file
111
deployment/zip_builds_linux.py
Executable file
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Zip builder for TheGates builds.
|
||||
|
||||
Usage:
|
||||
python3 zip_builds.py 0.17.2
|
||||
|
||||
Creates, in-place:
|
||||
Linux/TheGates_Linux_<version>.zip containing: TheGates.x86_64, sandbox/
|
||||
Windows/TheGates_Windows_<version>.zip containing: TheGates.exe, TheGates.pck, sandbox/
|
||||
|
||||
By default, refuses to overwrite existing zip files. Use --force to overwrite.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
from zipfile import ZipFile, ZIP_DEFLATED
|
||||
|
||||
|
||||
def validate_version(version: str) -> None:
|
||||
pattern = re.compile(r"^[0-9]+(\.[0-9]+){1,3}$")
|
||||
if not pattern.match(version):
|
||||
raise ValueError(
|
||||
f"Invalid version '{version}'. Expected format like 0.17.2"
|
||||
)
|
||||
|
||||
|
||||
def ensure_exists(path: Path) -> None:
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"Missing required path: {path}")
|
||||
|
||||
|
||||
def zip_entries(base_dir: Path, entries: list[str], output_zip: Path, overwrite: bool) -> None:
|
||||
if output_zip.exists():
|
||||
if not overwrite:
|
||||
raise FileExistsError(
|
||||
f"Output already exists: {output_zip}. Use --force to overwrite."
|
||||
)
|
||||
output_zip.unlink()
|
||||
|
||||
# Ensure base directory exists
|
||||
ensure_exists(base_dir)
|
||||
|
||||
with ZipFile(output_zip, mode="w", compression=ZIP_DEFLATED) as zf:
|
||||
for entry_name in entries:
|
||||
entry_path = base_dir / entry_name
|
||||
ensure_exists(entry_path)
|
||||
|
||||
if entry_path.is_file():
|
||||
# Store at top-level inside the archive
|
||||
zf.write(entry_path, arcname=entry_name)
|
||||
else:
|
||||
# Walk directory and add files with relative paths rooted at base_dir
|
||||
for file_path in entry_path.rglob("*"):
|
||||
if file_path.is_file():
|
||||
arcname = file_path.relative_to(base_dir)
|
||||
zf.write(file_path, arcname=str(arcname))
|
||||
|
||||
|
||||
def build_linux_zip(root: Path, version: str, overwrite: bool) -> Path:
|
||||
linux_dir = root / "Linux"
|
||||
output_zip = linux_dir / f"TheGates_Linux_{version}.zip"
|
||||
entries = [
|
||||
"TheGates.x86_64",
|
||||
"sandbox",
|
||||
]
|
||||
zip_entries(linux_dir, entries, output_zip, overwrite)
|
||||
return output_zip
|
||||
|
||||
|
||||
def build_windows_zip(root: Path, version: str, overwrite: bool) -> Path:
|
||||
windows_dir = root / "Windows"
|
||||
output_zip = windows_dir / f"TheGates_Windows_{version}.zip"
|
||||
entries = [
|
||||
"TheGates.exe",
|
||||
"TheGates.pck",
|
||||
"sandbox",
|
||||
]
|
||||
zip_entries(windows_dir, entries, output_zip, overwrite)
|
||||
return output_zip
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Zip Linux and Windows builds.")
|
||||
parser.add_argument("version", help="App version, e.g. 0.17.2")
|
||||
parser.add_argument(
|
||||
"--force",
|
||||
action="store_true",
|
||||
help="Overwrite existing zip files if they exist.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
validate_version(args.version)
|
||||
|
||||
root = Path(__file__).resolve().parent
|
||||
|
||||
linux_zip = build_linux_zip(root, args.version, args.force)
|
||||
print(f"Created: {linux_zip}")
|
||||
|
||||
windows_zip = build_windows_zip(root, args.version, args.force)
|
||||
print(f"Created: {windows_zip}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue