Compare commits

...

2 Commits

Author SHA1 Message Date
b5068b3a97 . 2026-03-24 19:14:28 +01:00
6d29250fc4 v0.5.3 - Correction de 3 bugs
Changements :
- Fix _update_all : un seul _start_check() a la fin du batch au lieu d'un par depot (evite les refreshs concurrents)
- Fix check_repo : suppression du double appel reseau (ls-remote + fetch), le fetch seul detecte maintenant le mode hors ligne via les mots-cles d'erreur
- Fix timeout : clone passe de 30s a 300s, pull de 30s a 120s pour eviter les faux echecs sur repos volumineux

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 19:13:21 +01:00
4 changed files with 33 additions and 24 deletions

View File

@@ -6,7 +6,8 @@
"Bash(pip install:*)",
"Bash(pyinstaller --onefile --windowed --name \"GitUpdateChecker\" git_updater.py)",
"Bash(pyinstaller --onefile --console --name \"GitUpdateChecker\" --icon=NONE git_updater.py)",
"Bash(python -m PyInstaller --onefile --console --name \"GitUpdateChecker\" --icon=NONE git_updater.py)"
"Bash(python -m PyInstaller --onefile --console --name \"GitUpdateChecker\" --icon=NONE git_updater.py)",
"Bash(cmd /c build.bat)"
]
}
}

Binary file not shown.

View File

@@ -5,7 +5,7 @@ Accès lecture seule uniquement (fetch/pull/checkout, jamais de push).
Tous les chemins sont relatifs à l'emplacement de l'exécutable.
"""
VERSION = "0.5.2"
VERSION = "0.5.3"
import subprocess
import sys
@@ -77,13 +77,13 @@ def setup_logging():
log = setup_logging()
def run_git(args, cwd=None):
def run_git(args, cwd=None, timeout=30):
# -c safe.directory=* : évite l'erreur "dubious ownership" sur clé USB
cmd = ["git", "-c", "safe.directory=*"] + args
log.debug(f"git {' '.join(args)} (cwd={cwd})")
try:
result = subprocess.run(
cmd, cwd=cwd, capture_output=True, text=True, timeout=30,
cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout,
creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0,
)
if result.returncode != 0 and result.stderr.strip():
@@ -341,21 +341,19 @@ def check_repo(repo):
return result
result["branch"] = branch
# Vérifier que le remote est accessible via l'URL du config.ini
code, _, err = run_git(["ls-remote", "--exit-code", url, "HEAD"])
if code != 0:
log.warning(f"[{name}] Remote inaccessible: {url}")
result["error"] = "Depot hors ligne (remote inaccessible)"
result["offline"] = True
return result
# Mettre à jour l'URL origin si elle a changé dans config.ini
run_git(["remote", "set-url", "origin", url], cwd=local_path)
# Fetch
# Fetch (détecte aussi si le remote est inaccessible)
code, _, err = run_git(["fetch", "origin"], cwd=local_path)
if code != 0:
result["error"] = f"Erreur fetch : {err}"
offline_keywords = ["could not resolve", "connection refused", "unable to connect", "timed out", "the remote end hung up"]
if any(kw in err.lower() for kw in offline_keywords):
log.warning(f"[{name}] Remote inaccessible: {url}")
result["error"] = "Depot hors ligne (remote inaccessible)"
result["offline"] = True
else:
result["error"] = f"Erreur fetch : {err}"
return result
# Comparer HEAD local vs origin
@@ -436,7 +434,7 @@ def do_clone(repo):
"""Clone un dépôt."""
local_path = str(resolve_relative(repo["path"]))
log.info(f"Clonage: {repo['url']} -> {local_path}")
code, _, err = run_git(["clone", repo["url"], local_path])
code, _, err = run_git(["clone", repo["url"], local_path], timeout=300)
if code == 0:
log.info(f"Clonage reussi: {local_path}")
else:
@@ -447,7 +445,7 @@ def do_clone(repo):
def do_pull(local_path, branch):
"""Pull les mises à jour (lecture seule)."""
log.info(f"Pull: {local_path} (branche {branch})")
code, out, err = run_git(["pull", "origin", branch], cwd=local_path)
code, out, err = run_git(["pull", "origin", branch], cwd=local_path, timeout=120)
if code == 0:
log.info(f"Pull reussi: {local_path}")
else:
@@ -800,7 +798,7 @@ class App(tk.Tk):
return card
def _do_update(self, res):
def _do_update(self, res, batch=False):
"""Met à jour un dépôt (pull + restore)."""
name = res["name"]
log.info(f"[{name}] MAJ unitaire demandee")
@@ -859,7 +857,7 @@ class App(tk.Tk):
status = "SUCCES" if success else "ECHEC"
log.info(f"[{name}] MAJ unitaire terminee - {status}")
self._log_gui(f"[{name}] Termine - {status}", "success" if success else "error")
self.after(0, lambda: self._show_update_result(res, messages, success))
self.after(0, lambda: self._show_update_result(res, messages, success, batch=batch))
threading.Thread(target=work, daemon=True).start()
@@ -888,18 +886,28 @@ class App(tk.Tk):
threading.Thread(target=work, daemon=True).start()
def _show_update_result(self, res, messages, success):
title = "Mise a jour" if success else "Erreur"
messagebox.showinfo(title, f"{res['name']}\n\n" + "\n".join(messages))
self._start_check()
def _show_update_result(self, res, messages, success, batch=False):
if batch:
# En mode batch : pas de messagebox individuelle, on ne rafraichit qu'une fois à la fin
self._batch_remaining -= 1
if self._batch_remaining == 0:
self._start_check()
else:
title = "Mise a jour" if success else "Erreur"
messagebox.showinfo(title, f"{res['name']}\n\n" + "\n".join(messages))
self._start_check()
def _update_all(self):
"""Met à jour tous les dépôts qui ont des MAJ."""
to_update = [r for r in self.repo_results if not r["up_to_date"] and not r.get("error") and not r.get("needs_clone")]
if not to_update:
return
log.info(f"MAJ globale demandee - {len(to_update)} depot(s) a mettre a jour")
self._log_gui(f"MAJ globale : {len(to_update)} depot(s) a mettre a jour", "warning")
# Compteur pour n'appeler _start_check qu'une seule fois quand tous sont termines
self._batch_remaining = len(to_update)
for res in to_update:
self._do_update(res)
self._do_update(res, batch=True)
def _show_no_repos(self):
bg_card = "#313244"

View File

@@ -1 +1 @@
0.5.2
0.5.3