#!/usr/bin/env python3 """Publish an APK: copy it in, write the manifest, commit. Reads the version and build number out of amber-adult's pubspec so the two cannot drift — a manifest advertising a build number the APK does not carry is an update the device installs and is then offered again on every launch. """ import hashlib import json import pathlib import re import shutil import subprocess import sys HERE = pathlib.Path(__file__).resolve().parent PUBSPEC = HERE.parent / "amber-adult" / "pubspec.yaml" BASE_URL = "https://media-updates.petruzalekr.cz" def pubspec_version() -> tuple[str, int]: m = re.search(r"^version:\s*([0-9.]+)\+([0-9]+)\s*$", PUBSPEC.read_text(encoding="utf-8"), re.M) if not m: sys.exit(f"no version line in {PUBSPEC}") return m.group(1), int(m.group(2)) def main() -> None: if len(sys.argv) < 2: sys.exit("usage: release.py [notes]") apk = pathlib.Path(sys.argv[1]).resolve() notes = sys.argv[2] if len(sys.argv) > 2 else "" if not apk.is_file(): sys.exit(f"no such APK: {apk}") version, build = pubspec_version() public = HERE / "public" public.mkdir(exist_ok=True) name = f"media-{build}.apk" dest = public / name shutil.copy2(apk, dest) digest = hashlib.sha256(dest.read_bytes()).hexdigest() manifest = { "version": version, "buildNumber": build, "notes": notes, "sha256": digest, "size": dest.stat().st_size, "url": f"{BASE_URL}/{name}", } # ensure_ascii=False: the notes are Czech and get read by a person as # often as by the app. (public / "manifest.json").write_text( json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") # A stable name, so "where do I download it" has one permanent answer # instead of one that has to be looked up in the manifest each time. A # symlink rather than a copy: git stores it as a path, so it costs bytes # rather than another 34MB per release. latest = public / "latest.apk" latest.unlink(missing_ok=True) latest.symlink_to(name) subprocess.run(["git", "add", "-A"], cwd=HERE, check=True) subprocess.run( ["git", "commit", "-m", f"Release {version}+{build}"], cwd=HERE, check=True) print(f"staged {name} ({dest.stat().st_size // 1024 // 1024} MB), " f"version {version}+{build}") print("push to deploy") if __name__ == "__main__": main()