From 98367219dc34c9ec9443c3eb48419756554f0d99 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Tue, 22 Oct 2019 11:53:56 -0300 Subject: [PATCH] [snap] feature -> allowing the user to install an application when a stable channel is not available --- CHANGELOG.md | 4 ++++ bauh/api/abstract/view.py | 3 --- bauh/commons/system.py | 5 ++++- bauh/gems/snap/controller.py | 31 +++++++++++++++++++++++++++--- bauh/gems/snap/resources/locale/en | 4 +++- bauh/gems/snap/resources/locale/es | 4 +++- bauh/gems/snap/resources/locale/pt | 4 +++- bauh/gems/snap/snap.py | 7 ++++--- bauh/view/qt/components.py | 2 +- bauh/view/resources/locale/en | 5 ++++- bauh/view/resources/locale/es | 5 ++++- bauh/view/resources/locale/pt | 5 ++++- 12 files changed, 62 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7113837..8bcd2e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [0.7.1] 2019- +### Features +- Snap: + - if the **stable** channel is not available while an application is being installed, a popup is displayed allowing the user to choose a different one ( e.g: dbeaver-ce ) + ### Improvements - 3 password attempts for root authentication - AUR: diff --git a/bauh/api/abstract/view.py b/bauh/api/abstract/view.py index 64136a4f..3caf1dc1 100644 --- a/bauh/api/abstract/view.py +++ b/bauh/api/abstract/view.py @@ -59,9 +59,6 @@ class SingleSelectComponent(InputViewComponent): def __init__(self, type_: SelectViewType, label: str, options: List[InputOption], default_option: InputOption = None, max_per_line: int = 1, id_: str = None): super(SingleSelectComponent, self).__init__(id_=id_) - if options is None or len(options) < 2: - raise Exception("'options' must be a list with at least 2 elements") - self.type = type_ self.label = label self.options = options diff --git a/bauh/commons/system.py b/bauh/commons/system.py index ab37903f..6a555fbb 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -100,7 +100,7 @@ class ProcessHandler: if self.watcher: self.watcher.print(msg) - def handle(self, process: SystemProcess) -> bool: + def handle(self, process: SystemProcess, error_output: StringIO = None) -> bool: self._notify_watcher(' '.join(process.subproc.args) + '\n') already_succeeded = False @@ -126,6 +126,9 @@ class ProcessHandler: if line: self._notify_watcher(line) + if error_output is not None: + error_output.write(line) + if process.check_error_output: if process.wrong_error_phrase and process.wrong_error_phrase in line: continue diff --git a/bauh/gems/snap/controller.py b/bauh/gems/snap/controller.py index 220b90e3..0ab35e23 100644 --- a/bauh/gems/snap/controller.py +++ b/bauh/gems/snap/controller.py @@ -1,3 +1,4 @@ +import re import time from datetime import datetime from threading import Thread @@ -8,13 +9,15 @@ from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \ SuggestionPriority +from bauh.api.abstract.view import SingleSelectComponent, SelectViewType, InputOption from bauh.commons.html import bold -from bauh.commons.system import SystemProcess, ProcessHandler +from bauh.commons.system import SystemProcess, ProcessHandler, new_root_subprocess from bauh.gems.snap import snap, suggestions from bauh.gems.snap.constants import SNAP_API_URL from bauh.gems.snap.model import SnapApplication from bauh.gems.snap.worker import SnapAsyncDataLoader, CategoriesDownloader +RE_AVAILABLE_CHANNELS = re.compile(re.compile(r'(\w+)\s+(snap install.+)')) class SnapManager(SoftwareManager): @@ -125,9 +128,31 @@ class SnapManager(SoftwareManager): raise Exception("'get_history' is not supported by {}".format(pkg.__class__.__name__)) def install(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool: - res = ProcessHandler(watcher).handle(SystemProcess(subproc=snap.install_and_stream(pkg.name, pkg.confinement, root_password))) + res, output = ProcessHandler(watcher).handle_simple(snap.install_and_stream(pkg.name, pkg.confinement, root_password)) - if res: + if 'error:' in output: + res = False + if 'not available on stable' in output: + channels = RE_AVAILABLE_CHANNELS.findall(output) + + if channels: + opts = [InputOption(label=c[0], value=c[1]) for c in channels] + channel_select = SingleSelectComponent(type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0]) + if watcher.request_confirmation(title=self.i18n['snap.install.available_channels.title'], + body=self.i18n['snap.install.available_channels.body'].format(bold(self.i18n['stable']), bold(pkg.name)) + ':', + components=[channel_select], + confirmation_label=self.i18n['continue'], + deny_label=self.i18n['cancel']): + self.logger.info("Installing '{}' with the custom command '{}'".format(pkg.name, channel_select.value)) + res = ProcessHandler(watcher).handle(SystemProcess(new_root_subprocess(channel_select.value.value.split(' '), root_password=root_password))) + + if res: + pkg.has_apps_field = snap.has_apps_field(pkg.name, self.ubuntu_distro) + + return res + else: + self.logger.error("Could not find available channels in the installation output: {}".format(output)) + else: pkg.has_apps_field = snap.has_apps_field(pkg.name, self.ubuntu_distro) return res diff --git a/bauh/gems/snap/resources/locale/en b/bauh/gems/snap/resources/locale/en index 3426cdc3..633cd875 100644 --- a/bauh/gems/snap/resources/locale/en +++ b/bauh/gems/snap/resources/locale/en @@ -2,4 +2,6 @@ gem.snap.info=Applications published at https://snapcraft.io/store snap.notification.snapd_unavailable={} seems not to be installed or enabled. {} packages will not be available. snap.notification.snap.disable=If you do not want to use Snap applications, uncheck {} in {} snap.action.refresh.status=Refreshing -snap.action.refresh.label=Refresh \ No newline at end of file +snap.action.refresh.label=Refresh +snap.install.available_channels.title=Available channels +snap.install.available_channels.body=There is no {} channel available for {}. But there are these others available below ( select one if you want to continue ) \ No newline at end of file diff --git a/bauh/gems/snap/resources/locale/es b/bauh/gems/snap/resources/locale/es index 0d2f1a11..0989e73b 100644 --- a/bauh/gems/snap/resources/locale/es +++ b/bauh/gems/snap/resources/locale/es @@ -13,4 +13,6 @@ snap.info.size=tamaño snap.notification.snapd_unavailable={} no parece estar instalado o habilitado. Los paquetes {} estarán indisponibles. snap.notification.snap.disable=Si no desea usar aplicativos Snap, desmarque {} en {} snap.action.refresh.status=Actualizando -snap.action.refresh.label=Actualizar \ No newline at end of file +snap.action.refresh.label=Actualizar +snap.install.available_channels.title=Canales disponibles +snap.install.available_channels.body=No hay un canal {} ( stable ) disponible para {}. Pero hay estos otros disponibles abajo ( seleccione uno si desea continuar ) \ No newline at end of file diff --git a/bauh/gems/snap/resources/locale/pt b/bauh/gems/snap/resources/locale/pt index 0828ae2d..e93ae589 100644 --- a/bauh/gems/snap/resources/locale/pt +++ b/bauh/gems/snap/resources/locale/pt @@ -13,4 +13,6 @@ snap.info.size=tamanho snap.notification.snapd_unavailable={} não parece estar instalado ou habilitado. Os pacotes {} estarão indisponíveis. snap.notification.snap.disable=Se não deseja usar aplicativos Snap, desmarque {} em {} snap.action.refresh.status=Atualizando -snap.action.refresh.label=Atualizar \ No newline at end of file +snap.action.refresh.label=Atualizar +snap.install.available_channels.title=Canais disponíveis +snap.install.available_channels.body=Não há um canal {} ( stable ) disponível para {}. Porém existem estes outros disponíveis abaixo ( selecione algum se quiser continuar ) \ No newline at end of file diff --git a/bauh/gems/snap/snap.py b/bauh/gems/snap/snap.py index 77384628..b2c252cc 100644 --- a/bauh/gems/snap/snap.py +++ b/bauh/gems/snap/snap.py @@ -3,7 +3,7 @@ import re import subprocess from typing import List -from bauh.commons.system import new_root_subprocess, run_cmd, new_subprocess +from bauh.commons.system import new_root_subprocess, run_cmd, new_subprocess, SimpleProcess from bauh.gems.snap.model import SnapApplication BASE_CMD = 'snap' @@ -176,14 +176,15 @@ def uninstall_and_stream(app_name: str, root_password: str): return new_root_subprocess([BASE_CMD, 'remove', app_name], root_password) -def install_and_stream(app_name: str, confinement: str, root_password: str) -> subprocess.Popen: +def install_and_stream(app_name: str, confinement: str, root_password: str) -> SimpleProcess: install_cmd = [BASE_CMD, 'install', app_name] # default if confinement == 'classic': install_cmd.append('--classic') - return new_root_subprocess(install_cmd, root_password) + # return new_root_subprocess(install_cmd, root_password) + return SimpleProcess(install_cmd, root_password=root_password) def downgrade_and_stream(app_name: str, root_password: str) -> subprocess.Popen: diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py index 0942b49d..7175d8a7 100644 --- a/bauh/view/qt/components.py +++ b/bauh/view/qt/components.py @@ -67,7 +67,7 @@ class ComboBoxQt(QComboBox): class RadioSelectQt(QGroupBox): def __init__(self, model: SingleSelectComponent): - super(RadioSelectQt, self).__init__(model.label + ' :') + super(RadioSelectQt, self).__init__(model.label + ' :' if model.label else None) self.model = model self.setStyleSheet("QGroupBox { font-weight: bold }") diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index 04523f1a..9e34ffbe 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -141,6 +141,9 @@ filetransfer=file transfer packagemanager=package manager sportsgame=sports game arcadegame=arcade game +player=player screenshots,download.running=downloading image screenshots.download.no_content=No content to display -screenshots.download.no_response=Image not found \ No newline at end of file +screenshots.download.no_response=Image not found +continue=continue +stable=stable \ No newline at end of file diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index b9e055b8..78237bcb 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -182,8 +182,11 @@ filetransfer=transferencia de archivos packagemanager=administrador de paquetes sportsgame=juego de deportes arcadegame=juego arcade +player=jugador screenshots.bt_next.label=próxima screenshots.bt_back.label=anterior screenshots,download.running=descargando imagen screenshots.download.no_content=No hay contenido para mostrar -screenshots.download.no_response=Imagen no encontrada \ No newline at end of file +screenshots.download.no_response=Imagen no encontrada +continue=continuar +stable=estable \ No newline at end of file diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index 53a7a50d..045d61c7 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -185,8 +185,11 @@ viewer=visualizador packagemanager=gerenciador de pacotes sportsgame=jogo de esportes arcadegame=jogo arcade +player=jogador screenshots.bt_next.label=próxima screenshots.bt_back.label=anterior screenshots,download.running=baixando imagem screenshots.download.no_content=Sem conteúdo para exibir -screenshots.download.no_response=Imagem não encontrada \ No newline at end of file +screenshots.download.no_response=Imagem não encontrada +continue=continuar +stable=estável \ No newline at end of file