Files
grayjay/update_fdroid_index.py
T
2026-02-19 14:06:53 +01:00

171 lines
5.3 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import glob
import hashlib
import datetime
import os
import re
import shutil
import subprocess
import sys
import tempfile
from typing import Optional
APK_URL = "https://releases.grayjay.app/app-universal-release.apk"
FDROID_REPO_SSH = "git@gitlab.futo.org:fdroid/repo-v2.git"
FDROID_INDEX_PATH = "apps/Grayjay/index.yml"
UNIVERSAL_APK_GLOB = "app/build/outputs/apk/stable/release/*universal*.apk"
GIT_USER_NAME = "koen"
GIT_USER_EMAIL = "koen@futo.org"
class Fatal(Exception):
pass
def run(cmd: list[str], *, cwd: Optional[str] = None) -> str:
p = subprocess.run(cmd, cwd=cwd, check=False, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if p.returncode != 0:
raise Fatal(f"Command failed ({p.returncode}): {' '.join(cmd)}\n{p.stdout}")
return p.stdout.strip()
def sha256_of_file(path: str) -> str:
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(1024 * 1024), b""):
h.update(chunk)
return h.hexdigest()
def pick_universal_apk() -> str:
matches = sorted(glob.glob(UNIVERSAL_APK_GLOB))
if not matches:
raise Fatal(f"No universal APK found via glob: {UNIVERSAL_APK_GLOB}")
for m in matches:
base = os.path.basename(m)
if "app-stable-universal" in base:
return m
return matches[-1]
def get_release_date_today() -> str:
return datetime.datetime.now(datetime.timezone.utc).date().isoformat()
def get_version_code_from_tag() -> int:
tag = os.environ.get("CI_COMMIT_TAG", "").strip()
if not tag:
tag = run(["git", "describe", "--tags"]).strip()
m = re.search(r"(\d+)", tag)
if not m:
raise Fatal(f"Could not parse an integer versionCode from tag '{tag}'")
return int(m.group(1))
def update_index_yml(path: str, sha256sum: str, date_str: str, version_code: int) -> None:
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()
url_line_idx = None
url_line_indent = ""
for i, line in enumerate(lines):
stripped = line.lstrip()
if stripped.startswith("- url:") and APK_URL in stripped:
url_line_idx = i
url_line_indent = line[: len(line) - len(stripped)]
break
if url_line_idx is None:
raise Fatal(f"Did not find an apk entry with url {APK_URL} in {path}")
def is_url_line_same_level(s: str) -> bool:
st = s.lstrip()
indent = s[: len(s) - len(st)]
return st.startswith("- url:") and indent == url_line_indent
end = len(lines)
for j in range(url_line_idx + 1, len(lines)):
if is_url_line_same_level(lines[j]):
end = j
break
child_indent = url_line_indent + " "
found_sha = found_date = found_vc = False
for j in range(url_line_idx + 1, end):
st = lines[j].lstrip()
indent = lines[j][: len(lines[j]) - len(st)]
if st.startswith("sha256sum:"):
lines[j] = f"{indent}sha256sum: {sha256sum}\n"
found_sha = True
elif st.startswith("date:"):
lines[j] = f"{indent}date: {date_str}\n"
found_date = True
elif st.startswith("version-code:"):
lines[j] = f"{indent}version-code: {version_code}\n"
found_vc = True
insert_pos = url_line_idx + 1
to_insert: list[str] = []
if not found_sha:
to_insert.append(f"{child_indent}sha256sum: {sha256sum}\n")
if not found_date:
to_insert.append(f"{child_indent}date: {date_str}\n")
if not found_vc:
to_insert.append(f"{child_indent}version-code: {version_code}\n")
if to_insert:
lines[insert_pos:insert_pos] = to_insert
with open(path, "w", encoding="utf-8") as f:
f.writelines(lines)
def main() -> int:
version_code = get_version_code_from_tag()
date_str = get_release_date_today()
apk_path = pick_universal_apk()
print(f"Computing sha256 for {apk_path} ...")
sha = sha256_of_file(apk_path)
print(f"sha256: {sha}")
print(f"date: {date_str}")
print(f"version-code: {version_code}")
tmpdir = tempfile.mkdtemp(prefix="fdroid-repo-")
try:
print(f"Cloning {FDROID_REPO_SSH} ...")
run(["git", "clone", "--depth", "1", FDROID_REPO_SSH, tmpdir])
run(["git", "config", "user.name", GIT_USER_NAME], cwd=tmpdir)
run(["git", "config", "user.email", GIT_USER_EMAIL], cwd=tmpdir)
index_path = os.path.join(tmpdir, FDROID_INDEX_PATH)
if not os.path.exists(index_path):
raise Fatal(f"Missing {FDROID_INDEX_PATH} in repo-v2")
update_index_yml(index_path, sha, date_str, version_code)
run(["git", "add", FDROID_INDEX_PATH], cwd=tmpdir)
diff_rc = subprocess.run(["git", "diff", "--cached", "--quiet"], cwd=tmpdir).returncode
if diff_rc == 0:
print("No changes to commit.")
return 0
msg = f"Grayjay: update sha/date/version-code to {version_code} ({date_str})"
run(["git", "commit", "-m", msg], cwd=tmpdir)
run(["git", "push"], cwd=tmpdir)
print("Pushed update to fdroid/repo-v2.")
return 0
finally:
shutil.rmtree(tmpdir, ignore_errors=True)
if __name__ == "__main__":
try:
raise SystemExit(main())
except Fatal as e:
print(f"ERROR: {e}", file=sys.stderr)
raise SystemExit(2)