[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
- 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

View File

@@ -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:

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.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())

View File

@@ -255,6 +255,8 @@ downgraded=revertida
download=download
downloading=Sestà 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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=файл

View File

@@ -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