Ajout log dans interface
Passe a la version 0.2
This commit is contained in:
Binary file not shown.
109
git_updater.py
109
git_updater.py
@@ -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.
|
Tous les chemins sont relatifs à l'emplacement de l'exécutable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "0.1"
|
VERSION = "0.2"
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -460,9 +460,13 @@ class App(tk.Tk):
|
|||||||
date_label = ttk.Label(self, text=datetime.now().strftime(" %d/%m/%Y %H:%M:%S"), style="Status.TLabel")
|
date_label = ttk.Label(self, text=datetime.now().strftime(" %d/%m/%Y %H:%M:%S"), style="Status.TLabel")
|
||||||
date_label.pack(anchor="w", padx=15)
|
date_label.pack(anchor="w", padx=15)
|
||||||
|
|
||||||
|
# Panneau principal (PanedWindow vertical : cartes en haut, log en bas)
|
||||||
|
paned = ttk.PanedWindow(self, orient="vertical")
|
||||||
|
paned.pack(fill="both", expand=True, padx=15, pady=10)
|
||||||
|
|
||||||
# Zone scrollable pour les repos
|
# Zone scrollable pour les repos
|
||||||
container = ttk.Frame(self)
|
container = ttk.Frame(paned)
|
||||||
container.pack(fill="both", expand=True, padx=15, pady=10)
|
paned.add(container, weight=3)
|
||||||
|
|
||||||
self.canvas = tk.Canvas(container, bg=bg, highlightthickness=0)
|
self.canvas = tk.Canvas(container, bg=bg, highlightthickness=0)
|
||||||
scrollbar = ttk.Scrollbar(container, orient="vertical", command=self.canvas.yview)
|
scrollbar = ttk.Scrollbar(container, orient="vertical", command=self.canvas.yview)
|
||||||
@@ -478,6 +482,35 @@ class App(tk.Tk):
|
|||||||
# Scroll avec la molette
|
# Scroll avec la molette
|
||||||
self.canvas.bind_all("<MouseWheel>", lambda e: self.canvas.yview_scroll(int(-1 * (e.delta / 120)), "units"))
|
self.canvas.bind_all("<MouseWheel>", lambda e: self.canvas.yview_scroll(int(-1 * (e.delta / 120)), "units"))
|
||||||
|
|
||||||
|
# Panneau de log en bas
|
||||||
|
log_frame = tk.Frame(paned, bg="#181825")
|
||||||
|
paned.add(log_frame, weight=1)
|
||||||
|
|
||||||
|
log_header = tk.Frame(log_frame, bg="#181825")
|
||||||
|
log_header.pack(fill="x", padx=8, pady=(6, 2))
|
||||||
|
tk.Label(log_header, text="Journal des operations", bg="#181825", fg="#a6adc8", font=("Segoe UI", 9, "bold")).pack(side="left")
|
||||||
|
tk.Button(log_header, text="Effacer", bg="#313244", fg="#cdd6f4", bd=0, font=("Segoe UI", 8),
|
||||||
|
command=self._clear_log_panel, activebackground="#45475a", activeforeground="#cdd6f4").pack(side="right")
|
||||||
|
|
||||||
|
self.log_text = tk.Text(log_frame, bg="#11111b", fg="#cdd6f4", font=("Consolas", 9),
|
||||||
|
height=8, bd=0, highlightthickness=0, wrap="word",
|
||||||
|
state="disabled", padx=8, pady=4)
|
||||||
|
log_scroll = ttk.Scrollbar(log_frame, orient="vertical", command=self.log_text.yview)
|
||||||
|
self.log_text.configure(yscrollcommand=log_scroll.set)
|
||||||
|
|
||||||
|
self.log_text.pack(side="left", fill="both", expand=True, padx=(8, 0), pady=(0, 8))
|
||||||
|
log_scroll.pack(side="right", fill="y", padx=(0, 8), pady=(0, 8))
|
||||||
|
|
||||||
|
# Tags couleur pour le log GUI
|
||||||
|
self.log_text.tag_configure("info", foreground="#cdd6f4")
|
||||||
|
self.log_text.tag_configure("success", foreground="#a6e3a1")
|
||||||
|
self.log_text.tag_configure("warning", foreground="#f9e2af")
|
||||||
|
self.log_text.tag_configure("error", foreground="#f38ba8")
|
||||||
|
self.log_text.tag_configure("file_add", foreground="#a6e3a1")
|
||||||
|
self.log_text.tag_configure("file_mod", foreground="#f9e2af")
|
||||||
|
self.log_text.tag_configure("file_del", foreground="#f38ba8")
|
||||||
|
self.log_text.tag_configure("dim", foreground="#6c7086")
|
||||||
|
|
||||||
# Boutons en bas
|
# Boutons en bas
|
||||||
btn_frame = ttk.Frame(self)
|
btn_frame = ttk.Frame(self)
|
||||||
btn_frame.pack(fill="x", padx=15, pady=(0, 15))
|
btn_frame.pack(fill="x", padx=15, pady=(0, 15))
|
||||||
@@ -504,6 +537,26 @@ class App(tk.Tk):
|
|||||||
|
|
||||||
threading.Thread(target=work, daemon=True).start()
|
threading.Thread(target=work, daemon=True).start()
|
||||||
|
|
||||||
|
def _log_gui(self, message, tag="info"):
|
||||||
|
"""Ajoute une ligne dans le panneau de log."""
|
||||||
|
def _append():
|
||||||
|
self.log_text.configure(state="normal")
|
||||||
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||||
|
self.log_text.insert("end", f"[{timestamp}] ", "dim")
|
||||||
|
self.log_text.insert("end", f"{message}\n", tag)
|
||||||
|
self.log_text.see("end")
|
||||||
|
self.log_text.configure(state="disabled")
|
||||||
|
# Appel thread-safe
|
||||||
|
if threading.current_thread() is threading.main_thread():
|
||||||
|
_append()
|
||||||
|
else:
|
||||||
|
self.after(0, _append)
|
||||||
|
|
||||||
|
def _clear_log_panel(self):
|
||||||
|
self.log_text.configure(state="normal")
|
||||||
|
self.log_text.delete("1.0", "end")
|
||||||
|
self.log_text.configure(state="disabled")
|
||||||
|
|
||||||
def _handle_self_update(self, needs_update, info):
|
def _handle_self_update(self, needs_update, info):
|
||||||
"""Gère le résultat de l'auto-update."""
|
"""Gère le résultat de l'auto-update."""
|
||||||
if needs_update:
|
if needs_update:
|
||||||
@@ -540,6 +593,7 @@ class App(tk.Tk):
|
|||||||
self.btn_refresh.state(["disabled"])
|
self.btn_refresh.state(["disabled"])
|
||||||
self.btn_update_all.state(["disabled"])
|
self.btn_update_all.state(["disabled"])
|
||||||
self.status_label.configure(text="Verification en cours...")
|
self.status_label.configure(text="Verification en cours...")
|
||||||
|
self._log_gui("Verification des depots...", "info")
|
||||||
self.repos_config = load_repos()
|
self.repos_config = load_repos()
|
||||||
|
|
||||||
if not self.repos_config:
|
if not self.repos_config:
|
||||||
@@ -573,6 +627,7 @@ class App(tk.Tk):
|
|||||||
total = len(self.repo_results)
|
total = len(self.repo_results)
|
||||||
up = sum(1 for r in self.repo_results if r["up_to_date"])
|
up = sum(1 for r in self.repo_results if r["up_to_date"])
|
||||||
log.info(f"Resultat: {up}/{total} depots a jour")
|
log.info(f"Resultat: {up}/{total} depots a jour")
|
||||||
|
self._log_gui(f"Verification terminee : {up}/{total} depots a jour", "success" if up == total else "warning")
|
||||||
self.status_label.configure(text=f"{up}/{total} depots a jour")
|
self.status_label.configure(text=f"{up}/{total} depots a jour")
|
||||||
self.btn_refresh.state(["!disabled"])
|
self.btn_refresh.state(["!disabled"])
|
||||||
|
|
||||||
@@ -678,7 +733,9 @@ class App(tk.Tk):
|
|||||||
|
|
||||||
def _do_update(self, res):
|
def _do_update(self, res):
|
||||||
"""Met à jour un dépôt (pull + restore)."""
|
"""Met à jour un dépôt (pull + restore)."""
|
||||||
log.info(f"[{res['name']}] MAJ unitaire demandee")
|
name = res["name"]
|
||||||
|
log.info(f"[{name}] MAJ unitaire demandee")
|
||||||
|
self._log_gui(f"[{name}] Mise a jour en cours...", "info")
|
||||||
if "_btn" in res:
|
if "_btn" in res:
|
||||||
res["_btn"].state(["disabled"])
|
res["_btn"].state(["disabled"])
|
||||||
|
|
||||||
@@ -688,41 +745,60 @@ class App(tk.Tk):
|
|||||||
local_path = res["local_path"]
|
local_path = res["local_path"]
|
||||||
branch = res["branch"]
|
branch = res["branch"]
|
||||||
|
|
||||||
|
tag_map = {"A": "file_add", "M": "file_mod", "D": "file_del", "R": "file_mod"}
|
||||||
|
|
||||||
if res["commits"]:
|
if res["commits"]:
|
||||||
log.info(f"[{res['name']}] Pull de {len(res['commits'])} commit(s)...")
|
log.info(f"[{name}] Pull de {len(res['commits'])} commit(s)...")
|
||||||
|
self._log_gui(f"[{name}] Telechargement de {len(res['commits'])} commit(s)...", "info")
|
||||||
ok, out, err = do_pull(local_path, branch)
|
ok, out, err = do_pull(local_path, branch)
|
||||||
if ok:
|
if ok:
|
||||||
msg = f"Pull OK : {len(res['commits'])} commits telecharges."
|
msg = f"Pull OK : {len(res['commits'])} commits telecharges."
|
||||||
log.info(f"[{res['name']}] {msg}")
|
log.info(f"[{name}] {msg}")
|
||||||
|
self._log_gui(f"[{name}] {msg}", "success")
|
||||||
|
# Logger chaque fichier distant
|
||||||
|
for f in res.get("files", []):
|
||||||
|
tag = tag_map.get(f["status_char"], "info")
|
||||||
|
self._log_gui(f" [{f['status']:>9}] {f['file']}", tag)
|
||||||
messages.append(msg)
|
messages.append(msg)
|
||||||
else:
|
else:
|
||||||
msg = f"Erreur pull : {err}"
|
msg = f"Erreur pull : {err}"
|
||||||
log.error(f"[{res['name']}] {msg}")
|
log.error(f"[{name}] {msg}")
|
||||||
|
self._log_gui(f"[{name}] {msg}", "error")
|
||||||
messages.append(msg)
|
messages.append(msg)
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
if res["local_changes"]:
|
if res["local_changes"]:
|
||||||
log.info(f"[{res['name']}] Restauration de {len(res['local_changes'])} fichier(s)...")
|
log.info(f"[{name}] Restauration de {len(res['local_changes'])} fichier(s)...")
|
||||||
|
self._log_gui(f"[{name}] Restauration de {len(res['local_changes'])} fichier(s)...", "info")
|
||||||
ok, err = do_restore(local_path)
|
ok, err = do_restore(local_path)
|
||||||
if ok:
|
if ok:
|
||||||
msg = f"Restauration OK : {len(res['local_changes'])} fichiers restaures."
|
msg = f"Restauration OK : {len(res['local_changes'])} fichiers restaures."
|
||||||
log.info(f"[{res['name']}] {msg}")
|
log.info(f"[{name}] {msg}")
|
||||||
|
self._log_gui(f"[{name}] {msg}", "success")
|
||||||
|
# Logger chaque fichier restauré
|
||||||
|
for f in res["local_changes"]:
|
||||||
|
tag = tag_map.get(f["status_char"], "info")
|
||||||
|
self._log_gui(f" [Restaure] {f['file']}", tag)
|
||||||
messages.append(msg)
|
messages.append(msg)
|
||||||
else:
|
else:
|
||||||
msg = f"Erreur restauration : {err}"
|
msg = f"Erreur restauration : {err}"
|
||||||
log.error(f"[{res['name']}] {msg}")
|
log.error(f"[{name}] {msg}")
|
||||||
|
self._log_gui(f"[{name}] {msg}", "error")
|
||||||
messages.append(msg)
|
messages.append(msg)
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
status = "SUCCES" if success else "ECHEC"
|
status = "SUCCES" if success else "ECHEC"
|
||||||
log.info(f"[{res['name']}] MAJ unitaire terminee - {status}")
|
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))
|
||||||
|
|
||||||
threading.Thread(target=work, daemon=True).start()
|
threading.Thread(target=work, daemon=True).start()
|
||||||
|
|
||||||
def _do_clone(self, res):
|
def _do_clone(self, res):
|
||||||
"""Clone un dépôt."""
|
"""Clone un dépôt."""
|
||||||
log.info(f"[{res['name']}] Clonage demande - {res['url']}")
|
name = res["name"]
|
||||||
|
log.info(f"[{name}] Clonage demande - {res['url']}")
|
||||||
|
self._log_gui(f"[{name}] Clonage en cours depuis {res['url']}...", "info")
|
||||||
if "_btn" in res:
|
if "_btn" in res:
|
||||||
res["_btn"].state(["disabled"])
|
res["_btn"].state(["disabled"])
|
||||||
|
|
||||||
@@ -731,11 +807,13 @@ class App(tk.Tk):
|
|||||||
def work():
|
def work():
|
||||||
ok, err = do_clone(repo)
|
ok, err = do_clone(repo)
|
||||||
if ok:
|
if ok:
|
||||||
msg = f"Depot '{res['name']}' clone avec succes !"
|
msg = f"Depot '{name}' clone avec succes !"
|
||||||
log.info(f"[{res['name']}] {msg}")
|
log.info(f"[{name}] {msg}")
|
||||||
|
self._log_gui(f"[{name}] {msg}", "success")
|
||||||
else:
|
else:
|
||||||
msg = f"Erreur de clonage : {err}"
|
msg = f"Erreur de clonage : {err}"
|
||||||
log.error(f"[{res['name']}] {msg}")
|
log.error(f"[{name}] {msg}")
|
||||||
|
self._log_gui(f"[{name}] {msg}", "error")
|
||||||
self.after(0, lambda: messagebox.showinfo("Clonage", msg))
|
self.after(0, lambda: messagebox.showinfo("Clonage", msg))
|
||||||
self.after(100, self._start_check)
|
self.after(100, self._start_check)
|
||||||
|
|
||||||
@@ -750,6 +828,7 @@ class App(tk.Tk):
|
|||||||
"""Met à jour tous les dépôts qui ont des MAJ."""
|
"""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")]
|
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")]
|
||||||
log.info(f"MAJ globale demandee - {len(to_update)} depot(s) a mettre a jour")
|
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")
|
||||||
for res in to_update:
|
for res in to_update:
|
||||||
self._do_update(res)
|
self._do_update(res)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user