FIx Maj exe

This commit is contained in:
2026-03-24 15:57:44 +01:00
parent 4cf30c6110
commit 8b5b92bb4f
4 changed files with 104 additions and 83 deletions

Binary file not shown.

View File

@@ -2,6 +2,9 @@
; Les chemins (path) sont relatifs a l'emplacement de l'exe ; Les chemins (path) sont relatifs a l'emplacement de l'exe
; Ajouter autant de sections [repo:NomDuRepo] que necessaire ; 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] [repo:Scripts]
url = http://192.168.1.235:3125/zogzog/Scripts url = http://192.168.1.235:3125/zogzog/Scripts

View File

@@ -17,6 +17,8 @@ import tkinter as tk
from tkinter import ttk, messagebox from tkinter import ttk, messagebox
from pathlib import Path from pathlib import Path
from datetime import datetime from datetime import datetime
import urllib.request
import urllib.error
# Forcer UTF-8 sur Windows # Forcer UTF-8 sur Windows
if sys.platform == "win32": if sys.platform == "win32":
@@ -97,120 +99,135 @@ def run_git(args, cwd=None):
# ── Auto-update du programme ───────────────────────────────────────────────── # ── 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(): 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). Retourne (needs_update: bool, info: str).
""" """
exe_dir = str(get_exe_dir()) repo_url, _ = _get_self_update_config()
if not repo_url:
if not os.path.isdir(os.path.join(exe_dir, ".git")): log.info("Auto-update: pas de section [self-update] dans config.ini, skip")
log.info("Auto-update: pas de .git dans le dossier de l'exe, skip")
return False, "" 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) version_url = f"{repo_url}/raw/branch/master/version.txt"
if code != 0: try:
return False, "Impossible de lire la branche" 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) log.info(f"Auto-update: version locale={VERSION} distante={remote_version}")
if code != 0:
log.warning(f"Auto-update: fetch echoue: {err}")
return False, f"Fetch echoue: {err}"
code, local_hash, _ = run_git(["rev-parse", "HEAD"], cwd=exe_dir) if _version_tuple(remote_version) <= _version_tuple(VERSION):
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:
log.info("Auto-update: programme a jour") log.info("Auto-update: programme a jour")
return False, "" return False, ""
# Compter les commits en retard info = f"Version actuelle : {VERSION}\nVersion disponible : {remote_version}"
code, log_out, _ = run_git( log.info(f"Auto-update: MAJ disponible - {remote_version}")
["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}")
return True, info return True, info
def do_self_update(): def do_self_update():
""" """
Met à jour le programme lui-même. Télécharge le nouvel exe depuis le serveur Gitea.
Sur Windows, un .exe en cours d'exécution ne peut pas être écrasé. Stratégie : télécharger dans .new, renommer l'exe actuel en .old, placer le nouveau.
Stratégie : renommer l'exe actuel en .old, puis reset hard sur origin.
Retourne (ok, message). 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) 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"
# 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
# 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_path = Path(sys.executable)
exe_old_path = exe_path.with_suffix(".exe.old") exe_old_path = exe_path.with_suffix(".exe.old")
try: exe_new_path = exe_path.with_suffix(".exe.new")
# 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}"
# Reset hard sur origin pour forcer la MAJ (methode la plus fiable) # Telecharger le nouvel exe
code, _, err = run_git(["reset", "--hard", f"origin/{branch}"], cwd=exe_dir) download_url = f"{repo_url}/raw/branch/master/{exe_name}"
log.info(f"Auto-update: telechargement de {download_url}")
if code != 0:
# Echec, restaurer l'ancien exe
if is_frozen and exe_old_path and exe_old_path.exists():
try: try:
exe_old_path.rename(Path(sys.executable)) req = urllib.request.Request(download_url, headers={"User-Agent": "GitUpdateChecker"})
log.info("Auto-update: exe restaure apres echec") with urllib.request.urlopen(req, timeout=60) as resp:
data = resp.read()
if len(data) < 1000:
log.error(f"Auto-update: fichier telecharge trop petit ({len(data)} octets)")
return False, "Le fichier telecharge semble invalide"
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_new_path.unlink()
except OSError: except OSError:
pass pass
log.error(f"Auto-update: reset echoue: {err}") return False, f"Erreur telechargement: {e}"
return False, f"Erreur mise a jour: {err}"
log.info("Auto-update: reset --hard OK") # Renommer : exe actuel -> .old
# Restaurer config.ini de l'utilisateur
if config_backup is not None:
try: try:
config_path.write_text(config_backup, encoding="utf-8") if exe_old_path.exists():
log.info("Auto-update: config.ini restaure") 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: except OSError as e:
log.warning(f"Auto-update: impossible de restaurer config.ini: {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}"
# 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:
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." return True, "Mise a jour reussie !\nLe programme va redemarrer."

1
version.txt Normal file
View File

@@ -0,0 +1 @@
0.4