mirror of
https://github.com/spoopyghosty0/frameport.git
synced 2026-10-06 10:00:36 +02:00
FramePort finds new GitHub releases by itself and updates in one click: sidebar card + Library bar, a dialog with the release notes, "Update and restart" (download, SHA256SUMS check, Windows signer check, swap script, relaunch), optional automatic installs at the next start. CLI: --version, `frameport update [--check] [--yes]` and a once-a-day hint. One version source (_version.py); releases also ship the CLI wheel and "What's new" from the annotated tag; CI runs an update smoke test with each OS's archive.
73 lines
3.1 KiB
Python
73 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Build a native FramePort app bundle for the current OS.
|
|
|
|
python scripts/package.py # flet build (Flutter; best result, needs the Flutter SDK - flet installs it)
|
|
python scripts/package.py --pyinstaller # flet pack (PyInstaller one-folder app; no Flutter SDK needed)
|
|
|
|
Bundled data (catalog, prebuilt artifacts, Frame agent, bootstrap script) is copied into src/frameport/_data first,
|
|
which is where the installed app looks for it (see core/paths.py). Java/overport/apksigner are NOT bundled: the app
|
|
downloads and manages them on first run (Tools page), so bundles stay small and tools stay current.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import platform
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
DATA = ROOT / "src/frameport/_data"
|
|
TARGET = {"Windows": "windows", "Darwin": "macos", "Linux": "linux"}[platform.system()]
|
|
|
|
|
|
def version() -> str:
|
|
text = (ROOT / "src/frameport/_version.py").read_text(encoding="utf-8")
|
|
return re.search(r'__version__\s*=\s*"([^"]+)"', text).group(1)
|
|
|
|
|
|
def check_tag() -> None:
|
|
"""A release build (tag vX.Y.Z) must carry that version, or the app's update check would compare wrongly."""
|
|
ref = os.environ.get("GITHUB_REF_NAME", "")
|
|
if os.environ.get("GITHUB_REF_TYPE") == "tag" and ref.startswith("v") and ref[1:] != version():
|
|
raise SystemExit(f"tag {ref} doesn't match src/frameport/_version.py ({version()}): bump it first")
|
|
|
|
|
|
def stage_data():
|
|
shutil.rmtree(DATA, ignore_errors=True)
|
|
for name in ("catalog", "artifacts", "agent", "bootstrap"):
|
|
shutil.copytree(ROOT / name, DATA / name)
|
|
print(f"staged data in {DATA}")
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--pyinstaller", action="store_true")
|
|
ap.add_argument("--keep-data", action="store_true", help="leave src/frameport/_data after building")
|
|
args = ap.parse_args()
|
|
check_tag()
|
|
print(f"FramePort {version()}")
|
|
stage_data()
|
|
try:
|
|
if args.pyinstaller:
|
|
cmd = ["flet", "pack", str(ROOT / "src/main.py"), "--name", "FramePort", "--product-name", "FramePort",
|
|
"--add-data", f"{DATA}{';' if TARGET == 'windows' else ':'}frameport/_data", "--distpath", str(ROOT / "dist")]
|
|
else:
|
|
# --yes: install the Flutter SDK etc. without prompting; --no-rich-output: plain logs (CI, Windows consoles)
|
|
cmd = ["flet", "build", TARGET, str(ROOT), "--project", "FramePort", "--product", "FramePort",
|
|
"--module-name", "main", "--output", str(ROOT / "dist" / TARGET), "--yes", "--no-rich-output"]
|
|
if TARGET == "macos": # flet's default bundled Python lacks prebuilt cryptography wheels for both Mac archs
|
|
cmd += ["--python-version", "3.12", "--arch", "arm64"] # Apple Silicon; x86_64 cross-build fails
|
|
print(" ".join(cmd))
|
|
return subprocess.call(cmd, cwd=ROOT)
|
|
finally:
|
|
if not args.keep_data:
|
|
shutil.rmtree(DATA, ignore_errors=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|