[appimage] fix: uninstalling the application when an update download fails

This commit is contained in:
Vinicius Moreira
2021-11-19 17:01:41 -03:00
parent e930a97966
commit 65df7654f9
12 changed files with 85 additions and 23 deletions

View File

@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- AppImage - AppImage
- displaying updates without the associated download URL for some applications [#207](https://github.com/vinifmor/bauh/issues/207) - 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 - Flatpak
- not displaying update components not associated with installed packages - not displaying update components not associated with installed packages

View File

@@ -2,7 +2,7 @@ import os
from pathlib import Path from pathlib import Path
from typing import Optional 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 from bauh.commons import resource
ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) 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())) DESKTOP_ENTRIES_PATH = '{}/.local/share/applications'.format(str(Path.home()))
SUGGESTIONS_CACHED_FILE = '{}/suggestions.txt'.format(APPIMAGE_CACHE_PATH) SUGGESTIONS_CACHED_FILE = '{}/suggestions.txt'.format(APPIMAGE_CACHE_PATH)
SUGGESTIONS_CACHED_TS_FILE = '{}/suggestions.ts'.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: def get_icon_path() -> str:

View File

@@ -29,7 +29,7 @@ from bauh.commons.html import bold
from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, run_cmd, SimpleProcess from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, run_cmd, SimpleProcess
from bauh.gems.appimage import query, INSTALLATION_PATH, LOCAL_PATH, ROOT_DIR, \ 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, \ 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.config import AppImageConfigManager
from bauh.gems.appimage.model import AppImage from bauh.gems.appimage.model import AppImage
from bauh.gems.appimage.util import replace_desktop_entry_exec_command from bauh.gems.appimage.util import replace_desktop_entry_exec_command
@@ -356,6 +356,11 @@ class AppImageManager(SoftwareManager):
for req in requirements.to_upgrade: for req in requirements.to_upgrade:
watcher.change_status("{} {} ({})...".format(self.i18n['manage_window.status.upgrading'], req.pkg.name, req.pkg.version)) 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: if not self.uninstall(req.pkg, root_password, watcher).success:
watcher.show_message(title=self.i18n['error'], watcher.show_message(title=self.i18n['error'],
body=self.i18n['appimage.error.uninstall_current_version'], body=self.i18n['appimage.error.uninstall_current_version'],
@@ -363,7 +368,7 @@ class AppImageManager(SoftwareManager):
watcher.change_substatus('') watcher.change_substatus('')
return False 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('') watcher.change_substatus('')
return False return False
@@ -488,9 +493,38 @@ class AppImageManager(SoftwareManager):
if RE_ICON_ENDS_WITH.match(f): if RE_ICON_ENDS_WITH.match(f):
return f return f
def install(self, pkg: AppImage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher) -> TransactionResult: def _download(self, pkg: AppImage, watcher: ProcessWatcher) -> Optional[Tuple[str, str]]:
handler = ProcessHandler(watcher) 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() out_dir = INSTALLATION_PATH + pkg.get_clean_name()
counter = 0 counter = 0
while True: while True:
@@ -518,35 +552,45 @@ class AppImageManager(SoftwareManager):
if not moved: if not moved:
watcher.show_message(title=self.i18n['error'].capitalize(), 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) type_=MessageType.ERROR)
return TransactionResult.fail() return TransactionResult.fail()
else: else:
appimage_url = pkg.url_download_latest_version if pkg.update else pkg.url_download download_data = pre_downloaded_file if pre_downloaded_file else self._download(pkg, watcher)
file_name = appimage_url.split('/')[-1]
pkg.version = pkg.latest_version
pkg.url_download = appimage_url
file_path = out_dir + '/' + file_name if not download_data:
downloaded = self.file_downloader.download(file_url=pkg.url_download, watcher=watcher, return TransactionResult.fail()
output_path=file_path, cwd=str(Path.home()))
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))) 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: if permission_given:
watcher.change_substatus(self.i18n['appimage.install.extract'].format(bold(file_name))) watcher.change_substatus(self.i18n['appimage.install.extract'].format(bold(file_name)))
try: 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: if 'Error: Failed to register AppImage in AppImageLauncherFS' in output:
watcher.show_message(title=self.i18n['error'], 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) type_=MessageType.ERROR)
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir]))) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
return TransactionResult.fail() return TransactionResult.fail()
@@ -570,7 +614,7 @@ class AppImageManager(SoftwareManager):
if de_content: if de_content:
de_content = replace_desktop_entry_exec_command(desktop_entry=de_content, de_content = replace_desktop_entry_exec_command(desktop_entry=de_content,
appname=pkg.name, appname=pkg.name,
file_path=file_path) file_path=install_file_path)
extracted_icon = self._find_icon_file(extracted_folder) extracted_icon = self._find_icon_file(extracted_folder)
if extracted_icon: if extracted_icon:
@@ -595,20 +639,18 @@ class AppImageManager(SoftwareManager):
except: except:
traceback.print_exc() 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=[]) return TransactionResult(success=True, installed=[pkg], removed=[])
else: else:
watcher.show_message(title=self.i18n['error'], watcher.show_message(title=self.i18n['error'],
body='Could extract content from {}'.format(bold(file_name)), body='Could extract content from {}'.format(bold(file_name)),
type_=MessageType.ERROR) 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]))) handler.handle(SystemProcess(new_subprocess(['rm', '-rf', out_dir])))
return TransactionResult.fail() return TransactionResult.fail()
def _gen_desktop_entry_path(self, app: AppImage) -> str: def _gen_desktop_entry_path(self, app: AppImage) -> str:
return '{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, app.get_clean_name()) return '{}/bauh_appimage_{}.desktop'.format(DESKTOP_ENTRIES_PATH, app.get_clean_name())

View File

@@ -255,6 +255,8 @@ downgraded=revertida
download=download download=download
downloading=Sestà baixant downloading=Sestà baixant
error=error error=error
error.mkdir=Could not create directory {dir}
error.mvfile=Could not move {src} to {dest}
example.short=e.g example.short=e.g
exit=surt exit=surt
file=file file=file

View File

@@ -254,6 +254,8 @@ downgraded=gedowngraded
download=Download download=Download
downloading=Download downloading=Download
error=Fehler error=Fehler
error.mkdir=Could not create directory {dir}
error.mvfile=Could not move {src} to {dest}
example.short=z.B. example.short=z.B.
exit=Beenden exit=Beenden
file=file file=file

View File

@@ -254,6 +254,8 @@ downgraded=downgraded
download=download download=download
downloading=Downloading downloading=Downloading
error=error error=error
error.mkdir=Could not create directory {dir}
error.mvfile=Could not move {src} to {dest}
example.short=e.g example.short=e.g
exit=exit exit=exit
file=file file=file

View File

@@ -255,6 +255,8 @@ downgraded=versión revertida
download=descarga download=descarga
downloading=Descargando downloading=Descargando
error=error error=error
error.mkdir=No se pudo crear el directorio {dir}
error.mvfile=No se pudo mover {src} a {dest}
example.short=p.ej example.short=p.ej
exit=salir exit=salir
file=archivo file=archivo

View File

@@ -252,6 +252,8 @@ downgraded=downgradé
download=télécharger download=télécharger
downloading=Téléchargement en cours downloading=Téléchargement en cours
error=erreur error=erreur
error.mkdir=Could not create directory {dir}
error.mvfile=Could not move {src} to {dest}
example.short=ex example.short=ex
exit=sortir exit=sortir
file=fichier file=fichier

View File

@@ -256,6 +256,8 @@ downgraded=downgraded
download=download download=download
downloading=Scaricamento downloading=Scaricamento
error=errore error=errore
error.mkdir=Could not create directory {dir}
error.mvfile=Could not move {src} to {dest}
example.short=e.g example.short=e.g
exit=esci exit=esci
file=file file=file

View File

@@ -254,6 +254,8 @@ downgraded=versão revertida
download=download download=download
downloading=Baixando downloading=Baixando
error=erro 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 example.short=ex
exit=sair exit=sair
file=arquivo file=arquivo

View File

@@ -254,6 +254,8 @@ downgraded=Произведён откат
download=Загрузка download=Загрузка
downloading=Загрузка downloading=Загрузка
error=Ошибка error=Ошибка
error.mkdir=Could not create directory {dir}
error.mvfile=Could not move {src} to {dest}
example.short=К примеру example.short=К примеру
exit=Выход exit=Выход
file=файл file=файл

View File

@@ -254,6 +254,8 @@ downgraded=düşürüldü
download=indir download=indir
downloading=İndiriliyor downloading=İndiriliyor
error=hata error=hata
error.mkdir=Could not create directory {dir}
error.mvfile=Could not move {src} to {dest}
example.short=e.g example.short=e.g
exit=çıkış exit=çıkış
file=dosya file=dosya