diff --git a/CHANGELOG.md b/CHANGELOG.md index d7530e93..48db72c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,8 +24,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Reading Snap suggestions now takes around 75% less time - Reading installed Flatpaks now takes around 45% less time - "snap" and "snapd" installation check latency -- Refreshing only the associated package type after a successful operation (install, uninstall, downgrade, ...) -- When a package is installed, it will be the first element of the table after refreshing +- Refreshing only the associated package type after a successful operation (uninstall, downgrade, ...) ( installation has a different treatment. See below ) +- Only the package status is changed after a successful installation ( not refreshing the whole table ) - Progress bar status can now be controlled by the software manager while an operation is being executed - Flatpak: showing runtime branches as versions when they are not available - better internet offline handling @@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixes - flatpak: cached app current version - flatpak: update notification for runtimes with the same name +- flatpak: some warnings are treated as errors after downgrading - disk loader not filling all requested cached data from the disk - Ubuntu root password check - [Ubuntu 19.04 pip3 install issue](https://github.com/vinifmor/bauh/issues/3) diff --git a/bauh/commons/system.py b/bauh/commons/system.py index 726b177a..647919d0 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -43,10 +43,10 @@ class SystemProcess: Represents a system process being executed. """ - def __init__(self, subproc: subprocess.Popen, success_phrase: str = None, wrong_error_phrase: str = '[sudo] password for', + def __init__(self, subproc: subprocess.Popen, success_phrases: List[str] = None, wrong_error_phrase: str = '[sudo] password for', check_error_output: bool = True, skip_stdout: bool = False, output_delay: float = None): self.subproc = subproc - self.success_phrase = success_phrase + self.success_phrases = success_phrases self.wrong_error_phrase = wrong_error_phrase self.check_error_output = check_error_output self.skip_stdout = skip_stdout @@ -111,7 +111,7 @@ class ProcessHandler: if line: self._notify_watcher(line) - if process.success_phrase and process.success_phrase in line: + if process.success_phrases and [p in line for p in process.success_phrases]: already_succeeded = True if not already_succeeded and process.output_delay: diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index 2de1bef1..5daea64b 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -1,3 +1,4 @@ +import json import os import re import shutil @@ -639,7 +640,17 @@ class ArchManager(SoftwareManager): return False def install(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher, skip_optdeps: bool = False) -> bool: - return self._install_from_aur(pkg.name, pkg.maintainer, root_password, ProcessHandler(watcher), dependency=False, skip_optdeps=skip_optdeps) + res = self._install_from_aur(pkg.name, pkg.maintainer, root_password, ProcessHandler(watcher), dependency=False, skip_optdeps=skip_optdeps) + + if res: + if os.path.exists(pkg.get_disk_data_path()): + with open(pkg.get_disk_data_path()) as f: + data = f.read() + if data: + data = json.loads(data) + pkg.fill_cached_data(data) + + return res def _is_wget_available(self): try: @@ -668,8 +679,7 @@ class ArchManager(SoftwareManager): return False def cache_to_disk(self, pkg: ArchPackage, icon_bytes: bytes, only_icon: bool): - if self.context.disk_cache and pkg.supports_disk_cache(): - pass + pass def requires_root(self, action: str, pkg: ArchPackage): return action != 'search' diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index 34e37b81..aa21e06f 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -1,3 +1,4 @@ +import traceback from datetime import datetime from threading import Thread from typing import List, Set, Type @@ -117,7 +118,7 @@ class FlatpakManager(SoftwareManager): commit = commits[commit_idx + 1] watcher.change_substatus(self.i18n['flatpak.downgrade.reverting']) watcher.change_progress(50) - success = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.downgrade(pkg.ref, commit, root_password), success_phrase='Updates complete.')) + success = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.downgrade(pkg.ref, commit, root_password), success_phrases=['Changes complete.', 'Updates complete.'])) watcher.change_progress(100) return success @@ -152,7 +153,19 @@ class FlatpakManager(SoftwareManager): return PackageHistory(pkg=pkg, history=commits, pkg_status_idx=status_idx) def install(self, pkg: FlatpakApplication, root_password: str, watcher: ProcessWatcher) -> bool: - return ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.install(pkg.id, pkg.origin))) + res = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.install(pkg.id, pkg.origin))) + + if res: + try: + fields = flatpak.get_fields(pkg.id, pkg.branch, ['Ref', 'Branch']) + + if fields: + pkg.ref = fields[0] + pkg.branch = fields[1] + except: + traceback.print_exc() + + return res def is_enabled(self): return self.enabled diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index 6af2c541..c1a00124 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -34,6 +34,22 @@ def get_app_info_fields(app_id: str, branch: str, fields: List[str] = [], check_ return data +def get_fields(app_id: str, branch: str, fields: List[str]) -> List[str]: + cmd = [BASE_CMD, 'info', app_id] + + if branch: + cmd.append(branch) + + info = new_subprocess(cmd).stdout + + res = [] + for o in new_subprocess(['grep', '-E', '({}):.+'.format('|'.join(fields)), '-o'], stdin=info).stdout: + if o: + res.append(o.decode().split(':')[-1].strip()) + + return res + + def is_installed(): version = get_version() return False if version is None else True @@ -282,5 +298,3 @@ def has_remotes_set() -> bool: def run(app_id: str): subprocess.Popen([BASE_CMD, 'run', app_id]) - - diff --git a/bauh/gems/flatpak/model.py b/bauh/gems/flatpak/model.py index 75dd55dd..bae5a7fb 100644 --- a/bauh/gems/flatpak/model.py +++ b/bauh/gems/flatpak/model.py @@ -21,13 +21,13 @@ class FlatpakApplication(SoftwarePackage): return self.description is None and self.icon_url def has_history(self): - return self.installed + return self.installed and self.ref def has_info(self): return self.installed def can_be_downgraded(self): - return self.installed + return self.installed and self.ref def get_type(self): return 'flatpak' diff --git a/bauh/view/core/controller.py b/bauh/view/core/controller.py index d133bf1b..f7a2ff67 100755 --- a/bauh/view/core/controller.py +++ b/bauh/view/core/controller.py @@ -262,12 +262,12 @@ class GenericSoftwareManager(SoftwareManager): man = self.map[app.__class__] return man if man and self._can_work(man) else None - def cache_to_disk(self, app: SoftwarePackage, icon_bytes: bytes, only_icon: bool): - if self.context.disk_cache and app.supports_disk_cache(): - man = self._get_manager_for(app) + def cache_to_disk(self, pkg: SoftwarePackage, icon_bytes: bytes, only_icon: bool): + if self.context.disk_cache and pkg.supports_disk_cache(): + man = self._get_manager_for(pkg) if man: - return man.cache_to_disk(app, icon_bytes=icon_bytes, only_icon=only_icon) + return man.cache_to_disk(pkg, icon_bytes=icon_bytes, only_icon=only_icon) def requires_root(self, action: str, app: SoftwarePackage): man = self._get_manager_for(app) diff --git a/bauh/view/core/downloader.py b/bauh/view/core/downloader.py index a31fb5d9..971160b0 100644 --- a/bauh/view/core/downloader.py +++ b/bauh/view/core/downloader.py @@ -41,7 +41,7 @@ class AdaptableFileDownloader(FileDownloader): return SystemProcess(new_subprocess(cmd=cmd, cwd=cwd), skip_stdout=True, check_error_output=False, - success_phrase='download completed', + success_phrases=['download completed'], output_delay=0.001) def _get_wget_process(self, url: str, output_path: str, cwd: str) -> SystemProcess: diff --git a/bauh/view/qt/apps_table.py b/bauh/view/qt/apps_table.py index aff9f36a..309620d1 100644 --- a/bauh/view/qt/apps_table.py +++ b/bauh/view/qt/apps_table.py @@ -136,6 +136,9 @@ class AppsTable(QTableWidget): menu_row.popup(QCursor.pos()) menu_row.exec_() + def refresh(self, pkg: PackageView): + self._update_row(pkg, update_check_enabled=False, change_update_col=False) + def fill_async_data(self): if self.window.pkgs: @@ -146,7 +149,7 @@ class AppsTable(QTableWidget): if self.download_icons: self.network_man.get(QNetworkRequest(QUrl(app_v.model.icon_url))) - self._update_row(idx, app_v, change_update_col=False) + self._update_row(app_v, change_update_col=False) app_v.status = PackageViewStatus.READY self.window.resize_and_center() @@ -199,7 +202,7 @@ class AppsTable(QTableWidget): if self.disk_cache and app.model.supports_disk_cache(): if not icon_was_cached or not os.path.exists(app.model.get_disk_icon_path()): - self.window.manager.cache_to_disk(app=app.model, icon_bytes=icon_data['bytes'], only_icon=True) + self.window.manager.cache_to_disk(pkg=app.model, icon_bytes=icon_data['bytes'], only_icon=True) def update_pkgs(self, pkg_views: List[PackageView], update_check_enabled: bool = True): self.setRowCount(len(pkg_views) if pkg_views else 0) @@ -207,16 +210,17 @@ class AppsTable(QTableWidget): if pkg_views: for idx, pkgv in enumerate(pkg_views): - self._update_row(idx, pkgv, update_check_enabled) + pkgv.table_index = idx + self._update_row(pkgv, update_check_enabled) - def _update_row(self, idx: int, pkg: PackageView, update_check_enabled: bool = True, change_update_col: bool = True): - self._set_col_name(idx, 0, pkg) - self._set_col_version(idx, 1, pkg) - self._set_col_description(idx, 2, pkg) - self._set_col_publisher(idx, 3, pkg) - self._set_col_type(idx, 4, pkg) - self._set_col_installed(idx, 5, pkg) - self._set_col_settings(idx, 6, pkg) + def _update_row(self, pkg: PackageView, update_check_enabled: bool = True, change_update_col: bool = True): + self._set_col_name(0, pkg) + self._set_col_version(1, pkg) + self._set_col_description(2, pkg) + self._set_col_publisher(3, pkg) + self._set_col_type(4, pkg) + self._set_col_installed(5, pkg) + self._set_col_settings(6, pkg) if change_update_col: col_update = None @@ -224,7 +228,7 @@ class AppsTable(QTableWidget): if update_check_enabled and pkg.model.update: col_update = UpdateToggleButton(pkg, self.window, self.i18n, pkg.model.update) - self.setCellWidget(idx, 7, col_update) + self.setCellWidget(pkg.table_index, 7, col_update) def _gen_row_button(self, text: str, style: str, callback) -> QWidget: col = QWidget() @@ -241,8 +245,7 @@ class AppsTable(QTableWidget): return col - def _set_col_installed(self, row: int, col: int, pkg: PackageView): - + def _set_col_installed(self, col: int, pkg: PackageView): if pkg.model.installed: if pkg.model.can_be_uninstalled(): def uninstall(): @@ -261,9 +264,9 @@ class AppsTable(QTableWidget): else: item = None - self.setCellWidget(row, col, item) + self.setCellWidget(pkg.table_index, col, item) - def _set_col_type(self, idx: int, col: int, pkg: PackageView): + def _set_col_type(self, col: int, pkg: PackageView): pixmap = self.cache_type_icon.get(pkg.model.get_type()) @@ -276,9 +279,9 @@ class AppsTable(QTableWidget): item.setPixmap(pixmap) item.setAlignment(Qt.AlignCenter) item.setToolTip('{}: {}'.format(self.i18n['type'], pkg.model.get_type().capitalize())) - self.setCellWidget(idx, col, item) + self.setCellWidget(pkg.table_index, col, item) - def _set_col_version(self, idx: int, col: int, pkg: PackageView): + def _set_col_version(self, col: int, pkg: PackageView): label_version = QLabel(pkg.model.version if pkg.model.version else '?') label_version.setAlignment(Qt.AlignCenter) @@ -300,9 +303,9 @@ class AppsTable(QTableWidget): label_version.setText(label_version.text() + ' > {}'.format(pkg.model.latest_version)) item.setToolTip(tooltip) - self.setCellWidget(idx, col, item) + self.setCellWidget(pkg.table_index, col, item) - def _set_col_name(self, idx: int, col: int, pkg: PackageView): + def _set_col_name(self, col: int, pkg: PackageView): item = QTableWidgetItem() item.setText(pkg.model.name if pkg.model.name else '...') item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) @@ -323,9 +326,9 @@ class AppsTable(QTableWidget): icon = icon_data['icon'] if icon_data else QIcon(pkg.model.get_default_icon_path()) item.setIcon(icon) - self.setItem(idx, col, item) + self.setItem(pkg.table_index, col, item) - def _set_col_description(self, idx: int, col: int, pkg: PackageView): + def _set_col_description(self, col: int, pkg: PackageView): item = QTableWidgetItem() item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) @@ -342,9 +345,9 @@ class AppsTable(QTableWidget): if pkg.model.description: item.setToolTip(pkg.model.description) - self.setItem(idx, col, item) + self.setItem(pkg.table_index, col, item) - def _set_col_publisher(self, idx: int, col: int, pkg: PackageView): + def _set_col_publisher(self, col: int, pkg: PackageView): item = QLabel() publisher = pkg.model.get_publisher() @@ -368,9 +371,9 @@ class AppsTable(QTableWidget): if publisher and full_publisher: item.setToolTip(self.i18n['publisher'].capitalize() + ((': ' + full_publisher) if full_publisher else '')) - self.setCellWidget(idx, col, item) + self.setCellWidget(pkg.table_index, col, item) - def _set_col_settings(self, idx: int, col: int, pkg: PackageView): + def _set_col_settings(self, col: int, pkg: PackageView): item = QToolBar() if pkg.model.can_be_run(): @@ -394,7 +397,7 @@ class AppsTable(QTableWidget): bt = IconButton(icon_path=resource.get_path('img/app_settings.svg'), action=handle_click, background='#12ABAB', tooltip=self.i18n['action.settings.tooltip']) item.addWidget(bt) - self.setCellWidget(idx, col, item) + self.setCellWidget(pkg.table_index, col, item) def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents): header_horizontal = self.horizontalHeader() diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index f7995e5b..1595cd52 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -179,7 +179,7 @@ class DowngradeApp(AsyncAction): success = False try: success = self.manager.downgrade(self.app.model, self.root_password, self) - except (requests.exceptions.ConnectionError, NoInternetException): + except (requests.exceptions.ConnectionError, NoInternetException) as e: success = False self.print(self.locale_keys['internet.required']) finally: @@ -215,7 +215,7 @@ class GetAppHistory(AsyncAction): if self.app: try: self.notify_finished({'history': self.manager.get_history(self.app.model)}) - except (requests.exceptions.ConnectionError, NoInternetException): + except (requests.exceptions.ConnectionError, NoInternetException) as e: self.notify_finished({'error': self.locale_keys['internet.required']}) finally: self.app = None @@ -266,7 +266,7 @@ class InstallPackage(AsyncAction): if self.pkg.model.supports_disk_cache(): icon_data = self.icon_cache.get(self.pkg.model.icon_url) - self.manager.cache_to_disk(app=self.pkg.model, + self.manager.cache_to_disk(pkg=self.pkg.model, icon_bytes=icon_data.get('bytes') if icon_data else None, only_icon=False) except (requests.exceptions.ConnectionError, NoInternetException): diff --git a/bauh/view/qt/view_model.py b/bauh/view/qt/view_model.py index 45c7dd0b..cdac15b8 100644 --- a/bauh/view/qt/view_model.py +++ b/bauh/view/qt/view_model.py @@ -14,6 +14,7 @@ class PackageView: self.model = model self.update_checked = model.update self.status = PackageViewStatus.LOADING + self.table_index = -1 def __repr__(self): return '{} ( {} )'.format(self.model.name, self.model.get_type()) diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index a377b22f..8b127ef3 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -883,8 +883,11 @@ class ManageWindow(QWidget): try: Path(log_path).mkdir(parents=True, exist_ok=True) - with open(log_path + '/{}.log'.format(int(time.time())), 'w+') as f: + log_file = log_path + '/{}.log'.format(int(time.time())) + with open(log_file, 'w+') as f: f.write(console_output) + + self.textarea_output.appendPlainText(self.i18n['console.install_logs.path'].format('"{}"'.format(log_file))) except: self.textarea_output.appendPlainText("[warning] Could not write install log file to '{}'".format(log_path)) @@ -892,7 +895,9 @@ class ManageWindow(QWidget): if self._can_notify_user(): util.notify_user(msg='{} ({}) {}'.format(res['pkg'].model.name, res['pkg'].model.get_type(), self.i18n['installed'])) - self.refresh_apps(top_app=res['pkg'], pkg_types={res['pkg'].model.__class__}) + self.ref_bt_installed.setVisible(False) + self.ref_bt_refresh.setVisible(True) + self.table_apps.refresh(res['pkg']) else: if self._can_notify_user(): util.notify_user('{}: {}'.format(res['pkg'].model.name, self.i18n['notification.install.failed'])) diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index 1c8be86c..edb145ed 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -116,4 +116,5 @@ style=style style.change.title=Style change style.change.question=To change the current style is necessary to restart {}. Proceed ? manage_window.bt_settings.tooltip=Click here to open extra actions -downloading=Downloading \ No newline at end of file +downloading=Downloading +console.install_logs.path=Installation logs can be found at {} \ No newline at end of file diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index 996be9b5..24d77102 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -118,4 +118,5 @@ style=estilo style.change.title=Cambio de estilo style.change.question=Para cambiar el estilo actual es necesario reiniciar {}. ¿Proceder? manage_window.bt_settings.tooltip=Haga clic aquí para abrir acciones adicionales -downloading=Descargando \ No newline at end of file +downloading=Descargando +console.install_logs.path=Los registros de instalación se pueden encontrar en {} \ No newline at end of file diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index 3918145d..b1ffa3e3 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -118,4 +118,5 @@ style=estilo style.change.title=Mudança de estilo style.change.question=Para alterar o estilo atual é necessário reiniciar o {}. Continuar ? manage_window.bt_settings.tooltip=Clique aqui para abrir ações adicionais -downloading=Baixando \ No newline at end of file +downloading=Baixando +console.install_logs.path=Os registros de instalação podem ser encontrados em {} \ No newline at end of file