From cdd2658687f4a564339682a4a8412321466d5d6d Mon Sep 17 00:00:00 2001 From: Jan Feil <11638228+jfeil@users.noreply.github.com> Date: Thu, 11 May 2023 22:48:48 +0200 Subject: [PATCH] Fix self updater --- RegeltestCreator.pyw | 54 ++++++++++++++++++++++++++++++++++++++++++- RegeltestCreator.spec | 2 +- requirements.txt | 3 ++- src/basic_config.py | 14 ++++------- src/updater.py | 37 +++++++++-------------------- updater.sh | 17 -------------- 6 files changed, 71 insertions(+), 56 deletions(-) delete mode 100644 updater.sh diff --git a/RegeltestCreator.pyw b/RegeltestCreator.pyw index b827161..24aef3d 100644 --- a/RegeltestCreator.pyw +++ b/RegeltestCreator.pyw @@ -1,7 +1,13 @@ import logging +import os +import shutil import sys +import time -from PySide6.QtWidgets import QApplication +import psutil +from PySide6.QtCore import Signal, QThread +from PySide6.QtGui import Qt +from PySide6.QtWidgets import QApplication, QDialog, QVBoxLayout, QLabel from src.basic_config import log_level from src.database import db @@ -12,8 +18,54 @@ logging.getLogger('sqlalchemy.engine').setLevel(log_level) logging.getLogger().setLevel(log_level) +class UpdateWorker(QThread): + finished = Signal() + + def __init__(self, original_path: str, old_pid: int): + self.original_path = original_path + self.old_pid = old_pid + + super().__init__() + + def run(self): + time.sleep(0.5) + while psutil.pid_exists(self.old_pid): + time.sleep(0.2) + os.remove(self.original_path) + shutil.copy(sys.executable, self.original_path) + self.finished.emit() + + +class UpdateFinishDialog(QDialog): + def __init__(self, original_path: str, old_pid: str): + super().__init__() + self.setWindowTitle("Update abschließen") + self.setModal(True) + self.setFixedSize(300, 100) + + layout = QVBoxLayout(self) + self.label = QLabel("Letzte Update-Schritte durchführen...", self) + self.label.setAlignment(Qt.AlignCenter) + layout.addWidget(self.label) + + self.worker = UpdateWorker(original_path, int(old_pid)) + self.worker.finished.connect(self.close) + self.worker.start() + + def closeEvent(self, event): + # Prevent closing the dialog while the background task is running + if self.worker.isRunning(): + event.ignore() + else: + event.accept() + + def run(): app = QApplication(sys.argv) + if len(sys.argv) == 3: + test = UpdateFinishDialog(sys.argv[1], sys.argv[2]) + test.exec() + pass main_window = MainWindow() main_window.initialize() main_window.show() diff --git a/RegeltestCreator.spec b/RegeltestCreator.spec index 1e01d12..56f76f2 100644 --- a/RegeltestCreator.spec +++ b/RegeltestCreator.spec @@ -7,7 +7,7 @@ block_cipher = None a = Analysis(['RegeltestCreator.pyw'], pathex=[], binaries=[], - datas=[('alembic', 'alembic'), ('alembic.ini', '.'), ('updater.sh', '.')], + datas=[('alembic', 'alembic'), ('alembic.ini', '.')], hiddenimports=['logging.config'], hookspath=[], runtime_hooks=[], diff --git a/requirements.txt b/requirements.txt index 6a2feb6..308bb21 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,5 @@ markdown2==2.4.3 lxml==4.9.1 Jinja2==3.1.2 alembic==1.8.1 -aiohttp~=3.8.4 \ No newline at end of file +aiohttp~=3.8.4 +psutil~=5.9.5 \ No newline at end of file diff --git a/src/basic_config.py b/src/basic_config.py index 01cf955..6cbcc2d 100644 --- a/src/basic_config.py +++ b/src/basic_config.py @@ -36,6 +36,8 @@ app_version = version.parse(app_version) api_url = "https://api.github.com/repos/jfeil/RegeltestCreator/releases" +dev_release = "?per_page=1" +stable_release = "/latest" database_name = "database.db" @@ -63,16 +65,8 @@ def check(cur_version, release_info): download_url = download_urls[current_platform] return release_info['tag_name'], release_info['body'], release_info['html_url'], download_url - releases = json.loads(requests.get(api_url).text) - latest_dev_release = None - latest_release = None - for release in releases: - if not latest_dev_release and release['prerelease']: - latest_dev_release = release - elif not latest_release and not release['prerelease']: - latest_release = release - if latest_release and latest_dev_release: - break + latest_dev_release = json.loads(requests.get(api_url + dev_release).text)[0] + latest_release = json.loads(requests.get(api_url + stable_release).text) return check(app_version, latest_release), check(app_version, latest_dev_release) diff --git a/src/updater.py b/src/updater.py index fe0447a..14b3a8d 100644 --- a/src/updater.py +++ b/src/updater.py @@ -1,6 +1,5 @@ import os import shutil -import stat import subprocess import sys from typing import Optional @@ -10,7 +9,7 @@ from PySide6.QtCore import Signal, QThread, Qt from PySide6.QtWidgets import QDialog, QMessageBox -from src.basic_config import app_dirs, current_platform, base_path, is_bundled +from src.basic_config import app_dirs, current_platform, is_bundled from src.ui_update_checker import Ui_UpdateChecker @@ -65,27 +64,6 @@ def display(self): f'{release_notes}{download_link}') def update(self) -> None: - def install_update(): - updater_script_unix = "updater.sh" - os.rename(os.path.join(base_path, updater_script_unix), - os.path.join(app_dirs.user_cache_dir, updater_script_unix)) - if current_platform == 'Darwin' or current_platform == 'Linux': - os.chmod(os.path.join(app_dirs.user_cache_dir, updater_script_unix), - stat.S_IXUSR | stat.S_IRUSR) - os.chmod(os.path.join(app_dirs.user_cache_dir, executable_name), - stat.S_IXUSR | stat.S_IRUSR) - - if current_platform == 'Windows': - command = f'timeout 10 & ' \ - f'copy "{download_path}" "{sys.executable}" & ' \ - f'del "{download_path}" & ' \ - f'"{sys.executable}"' - subprocess.Popen(command, shell=True) - elif current_platform == 'Darwin' or current_platform == 'Linux': - subprocess.Popen(" ".join([os.path.join(app_dirs.user_cache_dir, updater_script_unix), - sys.executable, str(os.getpid()), download_path]), shell=True) - sys.exit(0) - def progressbar_tracking(value): self.ui.download_progress.setValue(value) if value != 100: @@ -97,7 +75,8 @@ def progressbar_tracking(value): info_box.setInformativeText("Dabei wird die Applikation beendet und nach " "erfolgreichem Update erneut gestartet.") info_box.exec() - install_update() + subprocess.Popen([download_path, sys.executable, str(os.getpid())]) + sys.exit(0) if not self.download_link: return @@ -109,16 +88,22 @@ def progressbar_tracking(value): if not os.path.isdir(app_dirs.user_cache_dir): os.makedirs(app_dirs.user_cache_dir) - path, executable_name = os.path.split(sys.executable) + executable_name = "RegeltestCreator" + + if current_platform == 'Darwin': + executable_name += ".app.zip" + elif current_platform == 'Windows': + executable_name += ".exe" download_path = os.path.join(app_dirs.user_cache_dir, executable_name) request = requests.get(self.download_link, stream=True) filesize = request.headers['Content-Length'] + file_handle = open(download_path, 'wb+') downloadThread = DownloadThread(request, filesize, file_handle, buffer=10240) downloadThread.download_progress.connect(progressbar_tracking) - downloadThread.start() + downloadThread.run() class DownloadThread(QThread): diff --git a/updater.sh b/updater.sh deleted file mode 100644 index 05d2137..0000000 --- a/updater.sh +++ /dev/null @@ -1,17 +0,0 @@ -echo "Waiting 10 seconds to close the application..." -sleep 10s -# get Program process -echo "Checking if it is closed..." -if ps -p "$2" > /dev/null -then - echo "Force closing the programm..." - kill "$2" - sleep 5s - # Do something knowing the pid exists, i.e. the process with $PID is running -fi -echo "Deleting the old executable..." -rm "$1" -echo "Moving the new executable..." -mv "$3" "$1" -echo "Done! Starting the closed app!" -"$1" \ No newline at end of file