Ajout updater auto + build_exe.bat + version.txt
- app/updater.py : verifie les mises a jour depuis Gitea, telecharge via git pull ou archive zip - version.txt : version courante (1.0.0) - build_exe.bat : script de compilation PyInstaller pour Windows avec tous les hidden-imports necessaires (reportlab fonts, pypdfium2) - Au lancement, propose la mise a jour si nouvelle version disponible - Apres mise a jour, redemarre automatiquement Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
129
app/updater.py
Normal file
129
app/updater.py
Normal file
@@ -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
|
||||
79
build_exe.bat
Normal file
79
build_exe.bat
Normal file
@@ -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
|
||||
20
main.py
20
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()
|
||||
|
||||
1
version.txt
Normal file
1
version.txt
Normal file
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
Reference in New Issue
Block a user