diff --git a/app/updater.py b/app/updater.py new file mode 100644 index 0000000..d26b025 --- /dev/null +++ b/app/updater.py @@ -0,0 +1,129 @@ +"""Mise a jour automatique depuis Gitea.""" + +import os +import sys +import json +import urllib.request +import urllib.error +import subprocess +import tempfile +import shutil + +GITEA_API = "https://git.copydev.fr/api/v1" +REPO = "jules/copydev-imposing-tool" +BRANCH = "master" + +# Version actuelle (incrementee a chaque release) +VERSION = "1.0.0" + + +def get_remote_version(): + """Recuperer la version distante depuis le fichier version.txt sur Gitea.""" + try: + url = f"{GITEA_API}/repos/{REPO}/raw/version.txt?ref={BRANCH}" + req = urllib.request.Request(url, headers={"User-Agent": "CopydevImposingTool"}) + with urllib.request.urlopen(req, timeout=10) as resp: + return resp.read().decode().strip() + except Exception: + return None + + +def check_for_update(): + """Verifier si une mise a jour est disponible. + Retourne (disponible: bool, version_distante: str ou None) + """ + remote = get_remote_version() + if not remote: + return False, None + if remote != VERSION: + return True, remote + return False, remote + + +def get_app_dir(): + """Trouver le dossier de l'application.""" + if getattr(sys, 'frozen', False): + return os.path.dirname(sys.executable) + return os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def do_update(): + """Telecharger et appliquer la mise a jour. + Retourne (success: bool, message: str) + """ + app_dir = get_app_dir() + + # Verifier si git est disponible + git_cmd = _find_git() + if not git_cmd: + return False, "Git introuvable. Reinstallez l'application." + + # Si c'est un repo git, faire un pull + git_dir = os.path.join(app_dir, ".git") + if os.path.isdir(git_dir): + try: + result = subprocess.run( + [git_cmd, "pull", "origin", BRANCH], + cwd=app_dir, capture_output=True, text=True, timeout=60 + ) + if result.returncode == 0: + return True, f"Mise a jour reussie.\n{result.stdout}" + return False, f"Erreur git pull:\n{result.stderr}" + except Exception as e: + return False, str(e) + + # Sinon, telecharger l'archive zip depuis Gitea + try: + url = f"{GITEA_API}/repos/{REPO}/archive/{BRANCH}.zip" + req = urllib.request.Request(url, headers={"User-Agent": "CopydevImposingTool"}) + with urllib.request.urlopen(req, timeout=120) as resp: + tmp = tempfile.mktemp(suffix=".zip") + with open(tmp, "wb") as f: + f.write(resp.read()) + + # Extraire + import zipfile + tmp_dir = tempfile.mkdtemp() + with zipfile.ZipFile(tmp, "r") as z: + z.extractall(tmp_dir) + + # Trouver le dossier extrait + extracted = os.listdir(tmp_dir) + if extracted: + src = os.path.join(tmp_dir, extracted[0]) + # Copier les fichiers + for item in os.listdir(src): + s = os.path.join(src, item) + d = os.path.join(app_dir, item) + if os.path.isdir(s): + if os.path.exists(d): + shutil.rmtree(d) + shutil.copytree(s, d) + else: + shutil.copy2(s, d) + + # Nettoyer + os.remove(tmp) + shutil.rmtree(tmp_dir) + return True, "Mise a jour reussie depuis l'archive." + except Exception as e: + return False, str(e) + + +def _find_git(): + """Trouver l'executable git.""" + # Git systeme + try: + result = subprocess.run(["git", "--version"], capture_output=True, timeout=5) + if result.returncode == 0: + return "git" + except Exception: + pass + + # Git portable (installeur) + app_dir = get_app_dir() + portable = os.path.join(app_dir, "..", "git", "cmd", "git.exe") + if os.path.exists(portable): + return portable + + return None diff --git a/build_exe.bat b/build_exe.bat new file mode 100644 index 0000000..711b9c8 --- /dev/null +++ b/build_exe.bat @@ -0,0 +1,79 @@ +@echo off +chcp 65001 >nul 2>&1 +echo ============================================================ +echo Compilation Copydev Imposing Tool (.exe) +echo ============================================================ +echo. + +:: Verifier Python +python --version >nul 2>&1 +if %ERRORLEVEL% neq 0 ( + echo [ERREUR] Python non installe. + echo Installez Python 3.12+ depuis python.org + pause + exit /b 1 +) + +:: Installer les dependances +echo [1/3] Installation des dependances... +pip install PyQt6 pypdf reportlab pypdfium2 pyinstaller +echo. + +:: Compiler +echo [2/3] Compilation de l'executable... +echo (ca peut prendre 5-10 minutes) +pyinstaller --onefile --noconsole --name CopydevImposingTool ^ + --add-data "resources;resources" ^ + --add-data "version.txt;." ^ + --hidden-import pypdf ^ + --hidden-import pypdf._crypt_providers ^ + --hidden-import pypdf._crypt_providers._fallback ^ + --hidden-import reportlab ^ + --hidden-import reportlab.pdfgen ^ + --hidden-import reportlab.pdfgen.canvas ^ + --hidden-import reportlab.lib ^ + --hidden-import reportlab.lib.pagesizes ^ + --hidden-import reportlab.lib.units ^ + --hidden-import reportlab.lib.colors ^ + --hidden-import reportlab.pdfbase ^ + --hidden-import reportlab.pdfbase._fontdata ^ + --hidden-import reportlab.pdfbase.pdfmetrics ^ + --hidden-import reportlab.pdfbase._fontdata_enc_winansi ^ + --hidden-import reportlab.pdfbase._fontdata_enc_macroman ^ + --hidden-import reportlab.pdfbase._fontdata_enc_standard ^ + --hidden-import reportlab.pdfbase._fontdata_enc_symbol ^ + --hidden-import reportlab.pdfbase._fontdata_enc_zapfdingbats ^ + --hidden-import reportlab.pdfbase._fontdata_widths_courier ^ + --hidden-import reportlab.pdfbase._fontdata_widths_courierbold ^ + --hidden-import reportlab.pdfbase._fontdata_widths_courieroblique ^ + --hidden-import reportlab.pdfbase._fontdata_widths_courierboldoblique ^ + --hidden-import reportlab.pdfbase._fontdata_widths_helvetica ^ + --hidden-import reportlab.pdfbase._fontdata_widths_helveticabold ^ + --hidden-import reportlab.pdfbase._fontdata_widths_helveticaoblique ^ + --hidden-import reportlab.pdfbase._fontdata_widths_helveticaboldoblique ^ + --hidden-import reportlab.pdfbase._fontdata_widths_timesroman ^ + --hidden-import reportlab.pdfbase._fontdata_widths_timesbold ^ + --hidden-import reportlab.pdfbase._fontdata_widths_timesitalic ^ + --hidden-import reportlab.pdfbase._fontdata_widths_timesbolditalic ^ + --hidden-import reportlab.pdfbase._fontdata_widths_symbol ^ + --hidden-import reportlab.pdfbase._fontdata_widths_zapfdingbats ^ + --hidden-import pypdfium2 ^ + --hidden-import pypdfium2._helpers ^ + --hidden-import pypdfium2._helpers.document ^ + --hidden-import pypdfium2._helpers.page ^ + --hidden-import pypdfium2._helpers.bitmap ^ + main.py + +echo. +if exist "dist\CopydevImposingTool.exe" ( + echo [3/3] Executable compile avec succes ! + echo. + echo dist\CopydevImposingTool.exe + echo. + dir dist\CopydevImposingTool.exe +) else ( + echo [ERREUR] La compilation a echoue. + echo Verifiez les erreurs ci-dessus. +) +echo. +pause diff --git a/main.py b/main.py index c6b65a3..e6e4bdf 100644 --- a/main.py +++ b/main.py @@ -191,6 +191,26 @@ def main(): if not _check_license(): sys.exit(1) + # Verification de mise a jour silencieuse + from app.updater import check_for_update, VERSION + update_available, remote_version = check_for_update() + if update_available: + reply = QMessageBox.question( + None, "Mise a jour disponible", + f"Une nouvelle version est disponible : {remote_version}\n" + f"(version actuelle : {VERSION})\n\n" + "Voulez-vous mettre a jour maintenant ?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No + ) + if reply == QMessageBox.StandardButton.Yes: + from app.updater import do_update + success, msg = do_update() + if success: + QMessageBox.information(None, "Mise a jour", f"{msg}\n\nL'application va redemarrer.") + os.execv(sys.executable, [sys.executable] + sys.argv) + else: + QMessageBox.warning(None, "Mise a jour", f"Echec de la mise a jour :\n{msg}") + from app.main_window import MainWindow window = MainWindow() window.show() diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +1.0.0