From 65df7654f959295f2e69ae3f28c58e9cfc95a5dd Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 19 Nov 2021 17:01:41 -0300 Subject: [PATCH] [appimage] fix: uninstalling the application when an update download fails --- CHANGELOG.md | 1 + bauh/gems/appimage/__init__.py | 3 +- bauh/gems/appimage/controller.py | 86 ++++++++++++++++++++++++-------- bauh/view/resources/locale/ca | 2 + bauh/view/resources/locale/de | 2 + bauh/view/resources/locale/en | 2 + bauh/view/resources/locale/es | 2 + bauh/view/resources/locale/fr | 2 + bauh/view/resources/locale/it | 2 + bauh/view/resources/locale/pt | 2 + bauh/view/resources/locale/ru | 2 + bauh/view/resources/locale/tr | 2 + 12 files changed, 85 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4b7cb0c..cd3712c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - AppImage - displaying updates without the associated download URL for some applications [#207](https://github.com/vinifmor/bauh/issues/207) + - uninstalling the application when an update download fails [#207](https://github.com/vinifmor/bauh/issues/207) - Flatpak - not displaying update components not associated with installed packages diff --git a/bauh/gems/appimage/__init__.py b/bauh/gems/appimage/__init__.py index aef041c2..3ecbc8c9 100644 --- a/bauh/gems/appimage/__init__.py +++ b/bauh/gems/appimage/__init__.py @@ -2,7 +2,7 @@ import os from pathlib import Path from typing import Optional -from bauh.api.constants import CONFIG_PATH, CACHE_PATH +from bauh.api.constants import CONFIG_PATH, CACHE_PATH, TEMP_DIR from bauh.commons import resource ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -21,6 +21,7 @@ DATABASES_TS_FILE = '{}/dbs.ts'.format(APPIMAGE_CACHE_PATH) DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(str(Path.home())) SUGGESTIONS_CACHED_FILE = '{}/suggestions.txt'.format(APPIMAGE_CACHE_PATH) SUGGESTIONS_CACHED_TS_FILE = '{}/suggestions.ts'.format(APPIMAGE_CACHE_PATH) +DOWNLOAD_DIR = f'{TEMP_DIR}/appimage/download' def get_icon_path() -> str: diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py index 602f527a..669acb6d 100644 --- a/bauh/gems/appimage/controller.py +++ b/bauh/gems/appimage/controller.py @@ -29,7 +29,7 @@ from bauh.commons.html import bold from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, run_cmd, SimpleProcess from bauh.gems.appimage import query, INSTALLATION_PATH, LOCAL_PATH, ROOT_DIR, \ CONFIG_DIR, UPDATES_IGNORED_FILE, util, get_default_manual_installation_file_dir, DATABASE_APPS_FILE, \ - DATABASE_RELEASES_FILE, DESKTOP_ENTRIES_PATH, APPIMAGE_CACHE_PATH, get_icon_path + DATABASE_RELEASES_FILE, DESKTOP_ENTRIES_PATH, APPIMAGE_CACHE_PATH, get_icon_path, DOWNLOAD_DIR from bauh.gems.appimage.config import AppImageConfigManager from bauh.gems.appimage.model import AppImage from bauh.gems.appimage.util import replace_desktop_entry_exec_command @@ -356,6 +356,11 @@ class AppImageManager(SoftwareManager): for req in requirements.to_upgrade: watcher.change_status("{} {} ({})...".format(self.i18n['manage_window.status.upgrading'], req.pkg.name, req.pkg.version)) + download_data = self._download(req.pkg, watcher) + + if not download_data: + return False + if not self.uninstall(req.pkg, root_password, watcher).success: watcher.show_message(title=self.i18n['error'], body=self.i18n['appimage.error.uninstall_current_version'], @@ -363,7 +368,7 @@ class AppImageManager(SoftwareManager): watcher.change_substatus('') return False - if not self.install(req.pkg, root_password, None, watcher).success: + if not self._install(pkg=req.pkg, watcher=watcher, pre_downloaded_file=download_data).success: watcher.change_substatus('') return False @@ -488,9 +493,38 @@ class AppImageManager(SoftwareManager): if RE_ICON_ENDS_WITH.match(f): return f - def install(self, pkg: AppImage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher) -> TransactionResult: - handler = ProcessHandler(watcher) + def _download(self, pkg: AppImage, watcher: ProcessWatcher) -> Optional[Tuple[str, str]]: + appimage_url = pkg.url_download_latest_version if pkg.update else pkg.url_download + file_name = appimage_url.split('/')[-1] + pkg.version = pkg.latest_version + pkg.url_download = appimage_url + try: + Path(DOWNLOAD_DIR).mkdir(exist_ok=True, parents=True) + except OSError: + watcher.show_message(title=self.i18n['error'], + body=self.i18n['error.mkdir'].format(dir=bold(DOWNLOAD_DIR)), + type_=MessageType.ERROR) + return + + file_path = f'{DOWNLOAD_DIR}/{file_name}' + downloaded = self.file_downloader.download(file_url=pkg.url_download, watcher=watcher, + output_path=file_path, cwd=str(Path.home())) + + if not downloaded: + watcher.show_message(title=self.i18n['error'], + body=self.i18n['appimage.install.download.error'].format(bold(pkg.url_download)), + type_=MessageType.ERROR) + return + + return file_name, file_path + + def install(self, pkg: AppImage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher) -> TransactionResult: + return self._install(pkg=pkg, watcher=watcher) + + def _install(self, pkg: AppImage, watcher: ProcessWatcher, pre_downloaded_file: Optional[Tuple[str, str]] = None): + + handler = ProcessHandler(watcher) out_dir = INSTALLATION_PATH + pkg.get_clean_name() counter = 0 while True: @@ -518,35 +552,45 @@ class AppImageManager(SoftwareManager): if not moved: watcher.show_message(title=self.i18n['error'].capitalize(), - body=self.i18n['appimage.install.imported.rename_error'].format(bold(pkg.local_file_path.split('/')[-1]), bold(output)), + body=self.i18n['appimage.install.imported.rename_error'].format( + bold(pkg.local_file_path.split('/')[-1]), bold(output)), type_=MessageType.ERROR) return TransactionResult.fail() else: - appimage_url = pkg.url_download_latest_version if pkg.update else pkg.url_download - file_name = appimage_url.split('/')[-1] - pkg.version = pkg.latest_version - pkg.url_download = appimage_url + download_data = pre_downloaded_file if pre_downloaded_file else self._download(pkg, watcher) - file_path = out_dir + '/' + file_name - downloaded = self.file_downloader.download(file_url=pkg.url_download, watcher=watcher, - output_path=file_path, cwd=str(Path.home())) + if not download_data: + return TransactionResult.fail() + + file_name, download_path = download_data[0], download_data[1] + + install_file_path = f'{out_dir}/{file_name}' + + try: + shutil.move(download_path, install_file_path) + except OSError: + watcher.show_message(title=self.i18n['error'], + body=self.i18n['error.mvfile'].formmat(src=bold(download_path), + dest=bold(install_file_path))) + return TransactionResult.fail() - if downloaded: watcher.change_substatus(self.i18n['appimage.install.permission'].format(bold(file_name))) - permission_given = handler.handle(SystemProcess(new_subprocess(['chmod', 'a+x', file_path]))) + permission_given = handler.handle(SystemProcess(new_subprocess(['chmod', 'a+x', install_file_path]))) if permission_given: watcher.change_substatus(self.i18n['appimage.install.extract'].format(bold(file_name))) try: - res, output = handler.handle_simple(SimpleProcess([file_path, '--appimage-extract'], cwd=out_dir)) + res, output = handler.handle_simple( + SimpleProcess([install_file_path, '--appimage-extract'], cwd=out_dir)) if 'Error: Failed to register AppImage in AppImageLauncherFS' in output: watcher.show_message(title=self.i18n['error'], - body=self.i18n['appimage.install.appimagelauncher.error'].format(appimgl=bold('AppImageLauncher'), app=bold(pkg.name)), + body=self.i18n['appimage.install.appimagelauncher.error'].format( + appimgl=bold('AppImageLauncher'), app=bold(pkg.name)), type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() @@ -570,7 +614,7 @@ class AppImageManager(SoftwareManager): if de_content: de_content = replace_desktop_entry_exec_command(desktop_entry=de_content, appname=pkg.name, - file_path=file_path) + file_path=install_file_path) extracted_icon = self._find_icon_file(extracted_folder) if extracted_icon: @@ -595,20 +639,18 @@ class AppImageManager(SoftwareManager): except: traceback.print_exc() - SymlinksVerifier.create_symlink(app=pkg, file_path=file_path, logger=self.logger, watcher=watcher) + SymlinksVerifier.create_symlink(app=pkg, file_path=install_file_path, logger=self.logger, + watcher=watcher) return TransactionResult(success=True, installed=[pkg], removed=[]) else: watcher.show_message(title=self.i18n['error'], body='Could extract content from {}'.format(bold(file_name)), type_=MessageType.ERROR) - else: - watcher.show_message(title=self.i18n['error'], - body=self.i18n['appimage.install.download.error'].format(bold(pkg.url_download)), - type_=MessageType.ERROR) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) return TransactionResult.fail() + def _gen_desktop_entry_path(self, app: AppImage) -> str: return '{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, app.get_clean_name()) diff --git a/bauh/view/resources/locale/ca b/bauh/view/resources/locale/ca index cbb8364c..6130cc83 100644 --- a/bauh/view/resources/locale/ca +++ b/bauh/view/resources/locale/ca @@ -255,6 +255,8 @@ downgraded=revertida download=download downloading=S’està baixant error=error +error.mkdir=Could not create directory {dir} +error.mvfile=Could not move {src} to {dest} example.short=e.g exit=surt file=file diff --git a/bauh/view/resources/locale/de b/bauh/view/resources/locale/de index fbce3aa0..25810101 100644 --- a/bauh/view/resources/locale/de +++ b/bauh/view/resources/locale/de @@ -254,6 +254,8 @@ downgraded=gedowngraded download=Download downloading=Download error=Fehler +error.mkdir=Could not create directory {dir} +error.mvfile=Could not move {src} to {dest} example.short=z.B. exit=Beenden file=file diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index 565c52f4..b34f41bf 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -254,6 +254,8 @@ downgraded=downgraded download=download downloading=Downloading error=error +error.mkdir=Could not create directory {dir} +error.mvfile=Could not move {src} to {dest} example.short=e.g exit=exit file=file diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index 99eb8c7d..4f1b179a 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -255,6 +255,8 @@ downgraded=versión revertida download=descarga downloading=Descargando error=error +error.mkdir=No se pudo crear el directorio {dir} +error.mvfile=No se pudo mover {src} a {dest} example.short=p.ej exit=salir file=archivo diff --git a/bauh/view/resources/locale/fr b/bauh/view/resources/locale/fr index da412ec7..449e17a9 100644 --- a/bauh/view/resources/locale/fr +++ b/bauh/view/resources/locale/fr @@ -252,6 +252,8 @@ downgraded=downgradé download=télécharger downloading=Téléchargement en cours error=erreur +error.mkdir=Could not create directory {dir} +error.mvfile=Could not move {src} to {dest} example.short=ex exit=sortir file=fichier diff --git a/bauh/view/resources/locale/it b/bauh/view/resources/locale/it index 88d58fe6..5d764ba1 100644 --- a/bauh/view/resources/locale/it +++ b/bauh/view/resources/locale/it @@ -256,6 +256,8 @@ downgraded=downgraded download=download downloading=Scaricamento error=errore +error.mkdir=Could not create directory {dir} +error.mvfile=Could not move {src} to {dest} example.short=e.g exit=esci file=file diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index 2166889e..b185674a 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -254,6 +254,8 @@ downgraded=versão revertida download=download downloading=Baixando error=erro +error.mkdir=Não foi possível criar o diretório {dir} +error.mvfile=Não foi possível mover {src} para {dest} example.short=ex exit=sair file=arquivo diff --git a/bauh/view/resources/locale/ru b/bauh/view/resources/locale/ru index fe95fa32..7def1dab 100644 --- a/bauh/view/resources/locale/ru +++ b/bauh/view/resources/locale/ru @@ -254,6 +254,8 @@ downgraded=Произведён откат download=Загрузка downloading=Загрузка error=Ошибка +error.mkdir=Could not create directory {dir} +error.mvfile=Could not move {src} to {dest} example.short=К примеру exit=Выход file=файл diff --git a/bauh/view/resources/locale/tr b/bauh/view/resources/locale/tr index 75821350..00406ce9 100644 --- a/bauh/view/resources/locale/tr +++ b/bauh/view/resources/locale/tr @@ -254,6 +254,8 @@ downgraded=düşürüldü download=indir downloading=İndiriliyor error=hata +error.mkdir=Could not create directory {dir} +error.mvfile=Could not move {src} to {dest} example.short=e.g exit=çıkış file=dosya