mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-06 22:54:16 +02:00
improvement: only changing the installed package status ( not refreshing the whole table ) | fix: flatpak downgrading error handling
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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']))
|
||||
|
||||
@@ -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
|
||||
downloading=Downloading
|
||||
console.install_logs.path=Installation logs can be found at {}
|
||||
@@ -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
|
||||
downloading=Descargando
|
||||
console.install_logs.path=Los registros de instalación se pueden encontrar en {}
|
||||
@@ -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
|
||||
downloading=Baixando
|
||||
console.install_logs.path=Os registros de instalação podem ser encontrados em {}
|
||||
Reference in New Issue
Block a user