#!/usr/bin/env python3 from __future__ import annotations import argparse import hashlib import mimetypes import os import re import shutil import sqlite3 import uuid from datetime import UTC, datetime from pathlib import Path def main() -> None: parser = argparse.ArgumentParser(description="Publish a QMAX Android APK to the local Argus data store.") parser.add_argument("source", type=Path) parser.add_argument("version") parser.add_argument("notes") args = parser.parse_args() source = args.source.resolve(strict=True) data_root = Path(os.environ.get("ARGUS_DATA", "/mnt/data/argus")).resolve(strict=True) db_path = data_root / "argus.db" packages_root = data_root / "Packages" slug = "qmax" channel = "stable" platform = "android" release_id = str(uuid.uuid4()).upper() now = datetime.now(UTC).isoformat(timespec="microseconds") safe_version = re.sub(r"[^a-zA-Z0-9._-]+", "-", args.version).strip("-._") stored_name = f"{datetime.now(UTC):%Y%m%d%H%M%S}-{safe_version}-{release_id.replace('-', '')}.apk" stored_relative_path = f"{slug}/{stored_name}" target_path = packages_root / stored_relative_path target_path.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(source, target_path) size_bytes = target_path.stat().st_size sha256 = hashlib.sha256(target_path.read_bytes()).hexdigest() content_type = mimetypes.guess_type(source.name)[0] or "application/vnd.android.package-archive" connection = sqlite3.connect(db_path) try: connection.execute("PRAGMA foreign_keys = ON") connection.execute("BEGIN") app_id = connection.execute('SELECT "Id" FROM "Apps" WHERE "Slug" = ?', (slug,)).fetchone()[0] connection.execute( ''' UPDATE "Apps" SET "Name" = ?, "Summary" = ?, "Description" = ?, "RepositoryUrl" = ?, "HomepageUrl" = ?, "IsListed" = 1, "UpdatedAt" = ? WHERE "Id" = ? ''', ( "QMAX", "Multi-user Android client for MAX through a self-hosted PyMax bridge.", "QMAX connects multiple Android users to isolated MAX accounts through one self-hosted server and per-user PyMax sessions.", "https://git.kusoft.xyz/sevenhill/QMAX", "https://qmax.kusoft.xyz", now, app_id, ), ) duplicate = connection.execute( 'SELECT 1 FROM "Releases" WHERE "CatalogAppId" = ? AND "Version" = ? AND "Channel" = ? AND "Platform" = ?', (app_id, args.version, channel, platform), ).fetchone() if duplicate: raise RuntimeError(f"QMAX {args.version} is already published") connection.execute( ''' INSERT INTO "Releases" ("Id", "CatalogAppId", "Version", "Channel", "Platform", "PackageKind", "OriginalFileName", "StoredRelativePath", "ContentType", "PackageSizeBytes", "Sha256", "Notes", "PublishedAt") VALUES (?, ?, ?, ?, ?, 'apk', ?, ?, ?, ?, ?, ?, ?) ''', ( release_id, app_id, args.version, channel, platform, source.name, stored_relative_path, content_type, size_bytes, sha256, args.notes, now, ), ) connection.commit() except Exception: connection.rollback() target_path.unlink(missing_ok=True) raise finally: connection.close() print(f"PUBLISHED={args.version}") print(f"SIZE={size_bytes}") print(f"SHA256={sha256}") if __name__ == "__main__": main()