[snap] feature -> allowing the user to install an application when a stable channel is not available

This commit is contained in:
Vinicius Moreira
2019-10-22 11:53:56 -03:00
parent 696ce1c79e
commit 98367219dc
12 changed files with 62 additions and 17 deletions

View File

@@ -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/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.7.1] 2019- ## [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 ### Improvements
- 3 password attempts for root authentication - 3 password attempts for root authentication
- AUR: - AUR:

View File

@@ -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): 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_) 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.type = type_
self.label = label self.label = label
self.options = options self.options = options

View File

@@ -100,7 +100,7 @@ class ProcessHandler:
if self.watcher: if self.watcher:
self.watcher.print(msg) 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') self._notify_watcher(' '.join(process.subproc.args) + '\n')
already_succeeded = False already_succeeded = False
@@ -126,6 +126,9 @@ class ProcessHandler:
if line: if line:
self._notify_watcher(line) self._notify_watcher(line)
if error_output is not None:
error_output.write(line)
if process.check_error_output: if process.check_error_output:
if process.wrong_error_phrase and process.wrong_error_phrase in line: if process.wrong_error_phrase and process.wrong_error_phrase in line:
continue continue

View File

@@ -1,3 +1,4 @@
import re
import time import time
from datetime import datetime from datetime import datetime
from threading import Thread 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.handler import ProcessWatcher
from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \ from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \
SuggestionPriority SuggestionPriority
from bauh.api.abstract.view import SingleSelectComponent, SelectViewType, InputOption
from bauh.commons.html import bold 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 import snap, suggestions
from bauh.gems.snap.constants import SNAP_API_URL from bauh.gems.snap.constants import SNAP_API_URL
from bauh.gems.snap.model import SnapApplication from bauh.gems.snap.model import SnapApplication
from bauh.gems.snap.worker import SnapAsyncDataLoader, CategoriesDownloader from bauh.gems.snap.worker import SnapAsyncDataLoader, CategoriesDownloader
RE_AVAILABLE_CHANNELS = re.compile(re.compile(r'(\w+)\s+(snap install.+)'))
class SnapManager(SoftwareManager): class SnapManager(SoftwareManager):
@@ -125,9 +128,31 @@ class SnapManager(SoftwareManager):
raise Exception("'get_history' is not supported by {}".format(pkg.__class__.__name__)) raise Exception("'get_history' is not supported by {}".format(pkg.__class__.__name__))
def install(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool: 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) pkg.has_apps_field = snap.has_apps_field(pkg.name, self.ubuntu_distro)
return res return res

View File

@@ -3,3 +3,5 @@ snap.notification.snapd_unavailable={} seems not to be installed or enabled. {}
snap.notification.snap.disable=If you do not want to use Snap applications, uncheck {} in {} snap.notification.snap.disable=If you do not want to use Snap applications, uncheck {} in {}
snap.action.refresh.status=Refreshing snap.action.refresh.status=Refreshing
snap.action.refresh.label=Refresh 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 )

View File

@@ -14,3 +14,5 @@ snap.notification.snapd_unavailable={} no parece estar instalado o habilitado. L
snap.notification.snap.disable=Si no desea usar aplicativos Snap, desmarque {} en {} snap.notification.snap.disable=Si no desea usar aplicativos Snap, desmarque {} en {}
snap.action.refresh.status=Actualizando snap.action.refresh.status=Actualizando
snap.action.refresh.label=Actualizar 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 )

View File

@@ -14,3 +14,5 @@ snap.notification.snapd_unavailable={} não parece estar instalado ou habilitado
snap.notification.snap.disable=Se não deseja usar aplicativos Snap, desmarque {} em {} snap.notification.snap.disable=Se não deseja usar aplicativos Snap, desmarque {} em {}
snap.action.refresh.status=Atualizando snap.action.refresh.status=Atualizando
snap.action.refresh.label=Atualizar 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 )

View File

@@ -3,7 +3,7 @@ import re
import subprocess import subprocess
from typing import List 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 from bauh.gems.snap.model import SnapApplication
BASE_CMD = 'snap' 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) 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 install_cmd = [BASE_CMD, 'install', app_name] # default
if confinement == 'classic': if confinement == 'classic':
install_cmd.append('--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: def downgrade_and_stream(app_name: str, root_password: str) -> subprocess.Popen:

View File

@@ -67,7 +67,7 @@ class ComboBoxQt(QComboBox):
class RadioSelectQt(QGroupBox): class RadioSelectQt(QGroupBox):
def __init__(self, model: SingleSelectComponent): 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.model = model
self.setStyleSheet("QGroupBox { font-weight: bold }") self.setStyleSheet("QGroupBox { font-weight: bold }")

View File

@@ -141,6 +141,9 @@ filetransfer=file transfer
packagemanager=package manager packagemanager=package manager
sportsgame=sports game sportsgame=sports game
arcadegame=arcade game arcadegame=arcade game
player=player
screenshots,download.running=downloading image screenshots,download.running=downloading image
screenshots.download.no_content=No content to display screenshots.download.no_content=No content to display
screenshots.download.no_response=Image not found screenshots.download.no_response=Image not found
continue=continue
stable=stable

View File

@@ -182,8 +182,11 @@ filetransfer=transferencia de archivos
packagemanager=administrador de paquetes packagemanager=administrador de paquetes
sportsgame=juego de deportes sportsgame=juego de deportes
arcadegame=juego arcade arcadegame=juego arcade
player=jugador
screenshots.bt_next.label=próxima screenshots.bt_next.label=próxima
screenshots.bt_back.label=anterior screenshots.bt_back.label=anterior
screenshots,download.running=descargando imagen screenshots,download.running=descargando imagen
screenshots.download.no_content=No hay contenido para mostrar screenshots.download.no_content=No hay contenido para mostrar
screenshots.download.no_response=Imagen no encontrada screenshots.download.no_response=Imagen no encontrada
continue=continuar
stable=estable

View File

@@ -185,8 +185,11 @@ viewer=visualizador
packagemanager=gerenciador de pacotes packagemanager=gerenciador de pacotes
sportsgame=jogo de esportes sportsgame=jogo de esportes
arcadegame=jogo arcade arcadegame=jogo arcade
player=jogador
screenshots.bt_next.label=próxima screenshots.bt_next.label=próxima
screenshots.bt_back.label=anterior screenshots.bt_back.label=anterior
screenshots,download.running=baixando imagem screenshots,download.running=baixando imagem
screenshots.download.no_content=Sem conteúdo para exibir screenshots.download.no_content=Sem conteúdo para exibir
screenshots.download.no_response=Imagem não encontrada screenshots.download.no_response=Imagem não encontrada
continue=continuar
stable=estável