mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 00:04: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/).
|
||||
|
||||
## [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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
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 )
|
||||
@@ -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
|
||||
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 )
|
||||
@@ -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
|
||||
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
|
||||
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:
|
||||
|
||||
@@ -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 }")
|
||||
|
||||
|
||||
@@ -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
|
||||
screenshots.download.no_response=Image not found
|
||||
continue=continue
|
||||
stable=stable
|
||||
@@ -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
|
||||
screenshots.download.no_response=Imagen no encontrada
|
||||
continue=continuar
|
||||
stable=estable
|
||||
@@ -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
|
||||
screenshots.download.no_response=Imagem não encontrada
|
||||
continue=continuar
|
||||
stable=estável
|
||||
Reference in New Issue
Block a user