FIx Maj exe
This commit is contained in:
Binary file not shown.
@@ -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
|
||||||
|
|||||||
183
git_updater.py
183
git_updater.py
@@ -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
1
version.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0.4
|
||||||
Reference in New Issue
Block a user