mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-09 08:44:15 +02:00
[snap] feature -> allowing the user to install an application when a stable channel is not available
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 )
|
||||||
@@ -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 )
|
||||||
@@ -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 )
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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 }")
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user