#!/usr/bin/env python3 """ ObsiGate — Génération du manifeste de mise à jour Tauri (`latest.json`). L'updater Tauri attend un document JSON de la forme : { "version": "2.3.0", "notes": "…", "pub_date": "2026-09-12T10:00:00Z", "platforms": { "windows-x86_64": {"signature": "", "url": ""}, "linux-x86_64": {"signature": "", "url": ""} } } Ce module construit ce manifeste à partir des artefacts signés produits par `cargo tauri build` (avec `bundle.createUpdaterArtifacts: true`) et de leurs fichiers `.sig` (signature minisign). Usage en ligne de commande : python scripts/updater_manifest.py --tag v2.3.0 python scripts/updater_manifest.py --tag v2.3.0 --dry-run """ import argparse import datetime import json import pathlib import sys DEFAULT_GITEA_URL = "https://git.dracodev.net" DEFAULT_REPO = "Projets/ObsiGate" # Plateforme Tauri -> liste de motifs de bundles candidats (par ordre de priorité). # Le premier bundle disposant d'un fichier `.sig` est retenu. PLATFORM_BUNDLES: dict[str, list[str]] = { "windows-x86_64": [ "target/**/release/bundle/nsis/*-setup.exe", "target/**/release/bundle/nsis/*.exe", "target/**/release/bundle/msi/*.msi", ], "linux-x86_64": [ "target/**/release/bundle/appimage/*.AppImage", "target/**/release/bundle/deb/*.deb", ], } def read_version(tauri_conf: pathlib.Path) -> str: """Lit la version depuis `desktop/tauri.conf.json`.""" with open(tauri_conf, "r", encoding="utf-8") as f: data = json.load(f) return str(data.get("version", "0.0.0")) def _find_bundle_with_signature(desktop_dir: pathlib.Path, patterns: list[str]): """Retourne le premier bundle (le plus récent) disposant d'un `.sig`.""" for pattern in patterns: matches = [p for p in desktop_dir.glob(pattern) if p.is_file()] if not matches: continue bundle = max(matches, key=lambda p: p.stat().st_mtime) sig = bundle.with_name(bundle.name + ".sig") if sig.is_file(): return bundle, sig return None, None def build_updater_manifest( desktop_dir, tag: str, gitea_url: str = DEFAULT_GITEA_URL, repo: str = DEFAULT_REPO, notes: str = "", pub_date: str | None = None, ) -> dict | None: """Construit le manifeste `latest.json` à partir des artefacts signés. Retourne `None` si aucun artefact signé n'est trouvé. """ desktop_dir = pathlib.Path(desktop_dir) tauri_conf = desktop_dir / "tauri.conf.json" version = read_version(tauri_conf) if tauri_conf.exists() else "0.0.0" base_url = f"{gitea_url.rstrip('/')}/{repo.strip('/')}/releases/download/{tag}" platforms: dict[str, dict[str, str]] = {} for platform, patterns in PLATFORM_BUNDLES.items(): bundle, sig = _find_bundle_with_signature(desktop_dir, patterns) if bundle is None or sig is None: continue platforms[platform] = { "signature": sig.read_text(encoding="utf-8").strip(), "url": f"{base_url}/{bundle.name}", } if not platforms: return None if pub_date is None: pub_date = datetime.datetime.now(datetime.timezone.utc).strftime( "%Y-%m-%dT%H:%M:%SZ" ) return { "version": version, "notes": notes, "pub_date": pub_date, "platforms": platforms, } def main() -> int: if hasattr(sys.stdout, "reconfigure"): try: sys.stdout.reconfigure(encoding="utf-8") except Exception: pass parser = argparse.ArgumentParser( description="Générer le manifeste de mise à jour Tauri (latest.json)." ) parser.add_argument("--tag", "-t", help="Tag de version (ex: v2.3.0)") parser.add_argument("--gitea-url", default=DEFAULT_GITEA_URL) parser.add_argument("--repo", default=DEFAULT_REPO) parser.add_argument("--notes", default="", help="Notes de version") parser.add_argument( "--desktop-dir", default=None, help="Dossier desktop/ (défaut: /desktop)", ) parser.add_argument( "--output", default=None, help="Fichier de sortie (défaut: /latest.json)", ) parser.add_argument("--dry-run", action="store_true", help="Afficher sans écrire") args = parser.parse_args() root_dir = pathlib.Path(__file__).resolve().parent.parent desktop_dir = pathlib.Path(args.desktop_dir) if args.desktop_dir else root_dir / "desktop" output = pathlib.Path(args.output) if args.output else desktop_dir / "latest.json" tag = args.tag if not tag: version = read_version(desktop_dir / "tauri.conf.json") tag = f"v{version}" if not version.startswith("v") else version manifest = build_updater_manifest( desktop_dir, tag, args.gitea_url, args.repo, notes=args.notes ) if manifest is None: print("⚠️ Aucun artefact signé (.sig) trouvé — manifeste non généré.") print("💡 Build d'abord avec la clé updater définie :") print(" - Windows : .\\desktop\\build-windows.bat") print(" - Linux : ./desktop/build-linux.sh") return 1 content = json.dumps(manifest, indent=2, ensure_ascii=False) + "\n" if args.dry_run: print(content) print(f"🧪 Dry-run : rien écrit ({output})") return 0 output.parent.mkdir(parents=True, exist_ok=True) output.write_text(content, encoding="utf-8") print(f"✅ Manifeste écrit : {output}") print(f" Version : {manifest['version']}") print(f" Plateformes: {', '.join(manifest['platforms'])}") return 0 if __name__ == "__main__": sys.exit(main())