diff --git a/GitUpdateChecker.exe b/GitUpdateChecker.exe index a0d108a..bf57d6e 100644 Binary files a/GitUpdateChecker.exe and b/GitUpdateChecker.exe differ diff --git a/config.ini b/config.ini index fa5a7a8..aebbd06 100644 --- a/config.ini +++ b/config.ini @@ -2,6 +2,9 @@ ; Les chemins (path) sont relatifs a l'emplacement de l'exe ; Ajouter autant de sections [repo:NomDuRepo] que necessaire +[self-update] +url = http://192.168.1.235:3125/zogzog/Lanceur-geco +exe_name = GitUpdateChecker.exe [repo:Scripts] url = http://192.168.1.235:3125/zogzog/Scripts diff --git a/git_updater.py b/git_updater.py index ef33040..b7fced9 100644 --- a/git_updater.py +++ b/git_updater.py @@ -17,6 +17,8 @@ import tkinter as tk from tkinter import ttk, messagebox from pathlib import Path from datetime import datetime +import urllib.request +import urllib.error # Forcer UTF-8 sur Windows if sys.platform == "win32": @@ -97,120 +99,135 @@ def run_git(args, cwd=None): # ── Auto-update du programme ───────────────────────────────────────────────── +def _version_tuple(v): + """Convertit '0.4' en (0, 4) pour comparaison.""" + try: + return tuple(int(x) for x in v.strip().split(".")) + except (ValueError, AttributeError): + return (0,) + + +def _get_self_update_config(): + """Lit la config [self-update] depuis config.ini. Retourne (url, exe_name) ou (None, None).""" + config_path = get_config_path() + if not config_path.exists(): + return None, None + config = configparser.ConfigParser() + config.read(config_path, encoding="utf-8") + if not config.has_section("self-update"): + return None, None + url = config.get("self-update", "url", fallback="").strip().rstrip("/") + exe_name = config.get("self-update", "exe_name", fallback="GitUpdateChecker.exe").strip() + if not url: + return None, None + return url, exe_name + + def check_self_update(): """ - Vérifie si le dossier de l'exe est un dépôt git avec des MAJ disponibles. + Vérifie si une nouvelle version est disponible sur le serveur Gitea. + Télécharge version.txt via HTTP et compare avec VERSION locale. Retourne (needs_update: bool, info: str). """ - exe_dir = str(get_exe_dir()) - - if not os.path.isdir(os.path.join(exe_dir, ".git")): - log.info("Auto-update: pas de .git dans le dossier de l'exe, skip") + repo_url, _ = _get_self_update_config() + if not repo_url: + log.info("Auto-update: pas de section [self-update] dans config.ini, skip") return False, "" - log.info("Auto-update: verification...") + log.info("Auto-update: verification via HTTP...") - code, branch, _ = run_git(["rev-parse", "--abbrev-ref", "HEAD"], cwd=exe_dir) - if code != 0: - return False, "Impossible de lire la branche" + version_url = f"{repo_url}/raw/branch/master/version.txt" + try: + req = urllib.request.Request(version_url, headers={"User-Agent": "GitUpdateChecker"}) + with urllib.request.urlopen(req, timeout=10) as resp: + remote_version = resp.read().decode("utf-8").strip() + except (urllib.error.URLError, OSError) as e: + log.warning(f"Auto-update: impossible de verifier la version distante: {e}") + return False, f"Impossible de contacter le serveur: {e}" - code, _, err = run_git(["fetch", "origin"], cwd=exe_dir) - if code != 0: - log.warning(f"Auto-update: fetch echoue: {err}") - return False, f"Fetch echoue: {err}" + log.info(f"Auto-update: version locale={VERSION} distante={remote_version}") - code, local_hash, _ = run_git(["rev-parse", "HEAD"], cwd=exe_dir) - code2, remote_hash, _ = run_git(["rev-parse", f"origin/{branch}"], cwd=exe_dir) - - if code != 0 or code2 != 0: - return False, "Impossible de comparer les commits" - - log.info(f"Auto-update: local={local_hash[:8]} remote={remote_hash[:8]} branche={branch}") - - if local_hash == remote_hash: + if _version_tuple(remote_version) <= _version_tuple(VERSION): log.info("Auto-update: programme a jour") return False, "" - # Compter les commits en retard - code, log_out, _ = run_git( - ["log", "--oneline", f"HEAD..origin/{branch}"], - cwd=exe_dir, - ) - count = len(log_out.splitlines()) if code == 0 and log_out else 0 - - if count == 0: - # Hashes differents mais aucun commit a tirer (local en avance) -> pas de MAJ - log.info("Auto-update: hashes differents mais 0 commit a tirer, skip") - return False, "" - - info = f"{count} commit(s) en retard sur origin/{branch}" - log.info(f"Auto-update: MAJ disponible - {info}") + info = f"Version actuelle : {VERSION}\nVersion disponible : {remote_version}" + log.info(f"Auto-update: MAJ disponible - {remote_version}") return True, info def do_self_update(): """ - Met à jour le programme lui-même. - Sur Windows, un .exe en cours d'exécution ne peut pas être écrasé. - Stratégie : renommer l'exe actuel en .old, puis reset hard sur origin. + Télécharge le nouvel exe depuis le serveur Gitea. + Stratégie : télécharger dans .new, renommer l'exe actuel en .old, placer le nouveau. Retourne (ok, message). """ - exe_dir = str(get_exe_dir()) + repo_url, exe_name = _get_self_update_config() + if not repo_url: + return False, "Configuration [self-update] manquante" + is_frozen = getattr(sys, "frozen", False) + if not is_frozen: + log.warning("Auto-update: mode script, telechargement non supporte") + return False, "Auto-update uniquement supporte en mode .exe" - code, branch, _ = run_git(["rev-parse", "--abbrev-ref", "HEAD"], cwd=exe_dir) - if code != 0: - return False, "Impossible de lire la branche" + exe_path = Path(sys.executable) + exe_old_path = exe_path.with_suffix(".exe.old") + exe_new_path = exe_path.with_suffix(".exe.new") - # Sauvegarder config.ini avant reset (le reset va l'ecraser) - config_path = get_exe_dir() / "config.ini" - config_backup = None - if config_path.exists(): - try: - config_backup = config_path.read_text(encoding="utf-8") - log.info("Auto-update: config.ini sauvegarde") - except OSError: - pass + # Telecharger le nouvel exe + download_url = f"{repo_url}/raw/branch/master/{exe_name}" + log.info(f"Auto-update: telechargement de {download_url}") - # Si on tourne en .exe, renommer l'exe actuel pour libérer le fichier - exe_old_path = None - if is_frozen: - exe_path = Path(sys.executable) - exe_old_path = exe_path.with_suffix(".exe.old") - try: - # Supprimer un ancien .old s'il existe - if exe_old_path.exists(): - exe_old_path.unlink() - # Windows permet de renommer un exe en cours d'exécution - exe_path.rename(exe_old_path) - log.info(f"Auto-update: exe renomme {exe_path.name} -> {exe_old_path.name}") - except OSError as e: - log.error(f"Auto-update: impossible de renommer l'exe: {e}") - return False, f"Impossible de renommer l'exe: {e}" + try: + req = urllib.request.Request(download_url, headers={"User-Agent": "GitUpdateChecker"}) + with urllib.request.urlopen(req, timeout=60) as resp: + data = resp.read() - # Reset hard sur origin pour forcer la MAJ (methode la plus fiable) - code, _, err = run_git(["reset", "--hard", f"origin/{branch}"], cwd=exe_dir) + if len(data) < 1000: + log.error(f"Auto-update: fichier telecharge trop petit ({len(data)} octets)") + return False, "Le fichier telecharge semble invalide" - if code != 0: - # Echec, restaurer l'ancien exe - if is_frozen and exe_old_path and exe_old_path.exists(): + with open(exe_new_path, "wb") as f: + f.write(data) + log.info(f"Auto-update: telecharge {len(data)} octets -> {exe_new_path.name}") + + except (urllib.error.URLError, OSError) as e: + log.error(f"Auto-update: echec telechargement: {e}") + if exe_new_path.exists(): try: - exe_old_path.rename(Path(sys.executable)) - log.info("Auto-update: exe restaure apres echec") + exe_new_path.unlink() except OSError: pass - log.error(f"Auto-update: reset echoue: {err}") - return False, f"Erreur mise a jour: {err}" + return False, f"Erreur telechargement: {e}" - log.info("Auto-update: reset --hard OK") + # Renommer : exe actuel -> .old + try: + if exe_old_path.exists(): + exe_old_path.unlink() + exe_path.rename(exe_old_path) + log.info(f"Auto-update: {exe_path.name} -> {exe_old_path.name}") + except OSError as e: + log.error(f"Auto-update: impossible de renommer l'exe: {e}") + if exe_new_path.exists(): + try: + exe_new_path.unlink() + except OSError: + pass + return False, f"Impossible de renommer l'exe: {e}" - # Restaurer config.ini de l'utilisateur - if config_backup is not None: + # Renommer : .new -> exe + try: + exe_new_path.rename(exe_path) + log.info(f"Auto-update: {exe_new_path.name} -> {exe_path.name}") + except OSError as e: + log.error(f"Auto-update: impossible de placer le nouvel exe: {e}") try: - config_path.write_text(config_backup, encoding="utf-8") - log.info("Auto-update: config.ini restaure") - except OSError as e: - log.warning(f"Auto-update: impossible de restaurer config.ini: {e}") + exe_old_path.rename(exe_path) + log.info("Auto-update: ancien exe restaure") + except OSError: + pass + return False, f"Impossible de placer le nouvel exe: {e}" return True, "Mise a jour reussie !\nLe programme va redemarrer." diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..bd73f47 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.4