[improvement][aur] retrieving and displaying all transitive required dependencies

This commit is contained in:
Vinícius Moreira
2019-12-04 12:47:10 -03:00
parent 2aec03586a
commit e01249f49c
23 changed files with 428 additions and 81 deletions

View File

@@ -6,7 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.7.4] 2019-12
### Improvements
- Some AUR labels have been changed to not confuse the user
- AUR:
- retrieving and displaying all transitive required dependencies ( it can be disabled via the new environment variable **BAUH_ARCH_CHECK_SUBDEPS=0** )
- displaying **makedepends** and **checkdepends** in the info window
- Some AUR labels have been changed to not confuse the user
- Minor UI improvements
### Fixes
- AUR:

View File

@@ -126,7 +126,8 @@ will be pre-downloaded faster ( it does **NOT** modify your **pacman** settings
( For more information about these optimizations, have a look at [Makepkg](https://wiki.archlinux.org/index.php/Makepkg) )
- Arch package memory-indexer running every 20 minutes. This memory index is used when AUR Api cannot handle the amount of results found for a given search. It can be disabled via the environment variable **BAUH_ARCH_AUR_INDEX_UPDATER=0**.
- If some of your installed packages are not categorized, send an e-mail to **bauh4linux@gmail.com** informing their names and categories in the following format: ```name=category1[,category2,category3,...]```
- Transitive dependencies checking can be disabled through the environment variable **BAUH_ARCH_CHECK_SUBDEPS=0**. The dependency checking process will be
faster, but the application will ask for a new confirmation every time a not installed dependency is detected.
### General settings
You can change some application settings via environment variables or arguments (type ```bauh --help``` to get more information).

View File

@@ -4,6 +4,8 @@ from typing import Set, List
from bauh.api.http import HttpClient
import urllib.parse
from bauh.gems.arch.exceptions import PackageNotFoundException
URL_INFO = 'https://aur.archlinux.org/rpc/?v=5&type=info&'
URL_SRC_INFO = 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h='
URL_SEARCH = 'https://aur.archlinux.org/rpc/?v=5&type=search&arg='
@@ -46,5 +48,18 @@ class AURClient:
return info
def get_all_dependencies(self, name: str) -> Set[str]:
deps = set()
info = self.get_src_info(name)
if not info:
raise PackageNotFoundException(name)
for attr in ('makedepends', 'depends', 'checkdepends'):
if info.get(attr):
deps.update(info[attr])
return deps
def _map_names_as_queries(self, names) -> str:
return '&'.join(['arg[{}]={}'.format(i, urllib.parse.quote(n)) for i, n in enumerate(names)])

View File

@@ -1,4 +1,4 @@
from typing import Set
from typing import Set, List, Tuple
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.view import MultipleSelectComponent, InputOption
@@ -25,7 +25,7 @@ def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatch
default_options=None)
install = watcher.request_confirmation(title=i18n['arch.install.optdeps.request.title'],
body='<p>{}.</p><p>{}.</p>'.format(i18n['arch.install.optdeps.request.body'].format(bold(pkgname)), i18n['arch.install.optdeps.request.help']),
body='<p>{}.</p><p>{}:</p>'.format(i18n['arch.install.optdeps.request.body'].format(bold(pkgname)), i18n['arch.install.optdeps.request.help']),
components=[view_opts],
confirmation_label=i18n['install'].capitalize(),
deny_label=i18n['do_not.install'].capitalize())
@@ -34,14 +34,14 @@ def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatch
return {o.value for o in view_opts.values}
def request_install_missing_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: I18n) -> bool:
msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format(bold(pkgname)))
def request_install_missing_deps(pkgname: str, deps: List[Tuple[str,str]], watcher: ProcessWatcher, i18n: I18n) -> bool:
msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format(name=bold(pkgname), deps=bold(len(deps))))
opts = []
for p, m in pkg_mirrors.items():
op = InputOption('{} ( {}: {} )'.format(p, i18n['repository'], m.upper()), p)
for dep in deps:
op = InputOption('{} ( {}: {} )'.format(dep[0], i18n['repository'], dep[1].upper()), dep[0])
op.read_only = True
op.icon_path = _get_mirror_icon(m)
op.icon_path = _get_mirror_icon(dep[1])
opts.append(op)
comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts))

View File

@@ -6,7 +6,7 @@ import subprocess
import time
from pathlib import Path
from threading import Thread
from typing import List, Set, Type
from typing import List, Set, Type, Tuple
import requests
@@ -22,6 +22,7 @@ from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, r
from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, pkgbuild, message, confirmation, disk, git, suggestions, \
gpg, URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH
from bauh.gems.arch.aur import AURClient
from bauh.gems.arch.depedencies import DependenciesAnalyser
from bauh.gems.arch.mapper import ArchDataMapper
from bauh.gems.arch.model import ArchPackage
from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater, ArchCompilationOptimizer
@@ -62,6 +63,7 @@ class ArchManager(SoftwareManager):
self.categories_mapper = CategoriesDownloader('AUR', context.http_client, context.logger, self, self.context.disk_cache,
URL_CATEGORIES_FILE, CATEGORIES_CACHE_DIR, CATEGORIES_FILE_PATH)
self.categories = {}
self.deps_analyser = DependenciesAnalyser(self.aur_client)
def _upgrade_search_result(self, apidata: dict, installed_pkgs: dict, downgrade_enabled: bool, res: SearchResult, disk_loader: DiskCacheLoader):
app = self.mapper.map_api_data(apidata, installed_pkgs['not_signed'], self.categories)
@@ -219,7 +221,7 @@ class ArchManager(SoftwareManager):
break
watcher.change_substatus(self.i18n['arch.downgrade.install_older'])
return self._make_pkg(pkg.name, pkg.maintainer, root_password, handler, app_build_dir, clone_path, dependency=False, skip_optdeps=True)
return self._build(pkg.name, pkg.maintainer, root_password, handler, app_build_dir, clone_path, dependency=False, skip_optdeps=True)
else:
watcher.show_message(title=self.i18n['arch.downgrade.error'],
body=self.i18n['arch.downgrade.impossible'].format(pkg.name),
@@ -308,11 +310,17 @@ class ArchManager(SoftwareManager):
srcinfo = self.aur_client.get_src_info(pkg.name)
if srcinfo:
if srcinfo.get('makedepends'):
info['12_makedepends'] = srcinfo['makedepends']
if srcinfo.get('depends'):
info['11_dependson'] = srcinfo['depends']
info['13_dependson'] = srcinfo['depends']
if srcinfo.get('optdepends'):
info['12_optdepends'] = srcinfo['optdepends']
info['14_optdepends'] = srcinfo['optdepends']
if srcinfo.get('checkdepends'):
info['15_checkdepends'] = srcinfo['checkdepends']
if pkg.pkgbuild:
info['00_pkg_build'] = pkg.pkgbuild
@@ -355,34 +363,34 @@ class ArchManager(SoftwareManager):
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def _install_deps(self, pkg_mirrors: dict, root_password: str, handler: ProcessHandler, change_progress: bool = False) -> str:
def _install_deps(self, deps: List[Tuple[str, str]], root_password: str, handler: ProcessHandler, change_progress: bool = False) -> str:
"""
:param pkg_mirrors:
:param pkgs_repos:
:param root_password:
:param handler:
:return: not installed dependency
"""
progress_increment = int(100 / len(pkg_mirrors))
progress_increment = int(100 / len(deps))
progress = 0
self._update_progress(handler.watcher, 1, change_progress)
for pkgname, mirror in pkg_mirrors.items():
handler.watcher.change_substatus(self.i18n['arch.install.dependency.install'].format(bold('{} ()'.format(pkgname, mirror))))
if mirror == 'aur':
installed = self._install_from_aur(pkgname=pkgname, maintainer=None, root_password=root_password, handler=handler, dependency=True, change_progress=False)
for dep in deps:
handler.watcher.change_substatus(self.i18n['arch.install.dependency.install'].format(bold('{} ()'.format(dep[0], dep[1]))))
if dep[1] == 'aur':
installed = self._install_from_aur(pkgname=dep[0], maintainer=None, root_password=root_password, handler=handler, dependency=True, change_progress=False)
else:
installed = self._install(pkgname=pkgname, maintainer=None, root_password=root_password, handler=handler, install_file=None, mirror=mirror, change_progress=False)
installed = self._install(pkgname=dep[0], maintainer=None, root_password=root_password, handler=handler, install_file=None, mirror=dep[1], change_progress=False)
if not installed:
return pkgname
return dep[0]
progress += progress_increment
self._update_progress(handler.watcher, progress, change_progress)
self._update_progress(handler.watcher, 100, change_progress)
def _map_mirrors(self, pkgnames: Set[str]) -> dict:
pkg_mirrors = pacman.get_mirrors(pkgnames) # getting mirrors set
def _map_repos(self, pkgnames: Set[str]) -> dict:
pkg_mirrors = pacman.get_repositories(pkgnames) # getting mirrors set
if len(pkgnames) != len(pkg_mirrors): # checking if any dep not found in the distro mirrors are from AUR
nomirrors = {p for p in pkgnames if p not in pkg_mirrors}
@@ -423,12 +431,14 @@ class ArchManager(SoftwareManager):
return True
def _make_pkg(self, pkgname: str, maintainer: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
def _build(self, pkgname: str, maintainer: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
self._pre_download_source(pkgname, project_dir, handler.watcher)
self._update_progress(handler.watcher, 50, change_progress)
if not self._check_deps(pkgname, root_password, handler, project_dir):
check_subdeps = bool(int(os.getenv('BAUH_ARCH_CHECK_SUBDEPS', 1)))
if not self._handle_deps_and_keys(pkgname, root_password, handler, project_dir, check_subdeps=check_subdeps):
return False
# building main package
@@ -458,35 +468,74 @@ class ArchManager(SoftwareManager):
return False
def _check_deps(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str) -> bool:
def _map_missing_deps(self, deps: List[str], watcher: ProcessWatcher, check_subdeps: bool = True) -> List[Tuple[str, str]]:
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in deps}
dep_repos = self._map_repos(depnames)
if len(depnames) != len(dep_repos): # checking if a dependency could not be found in any mirror
for dep in depnames:
if dep not in dep_repos:
message.show_dep_not_found(dep, self.i18n, watcher)
return
sorted_deps = [] # it will hold the proper order to install the missing dependencies
repo_deps, aur_deps = set(), set()
for dep, repo in dep_repos.items():
if repo == 'aur':
aur_deps.add(dep)
else:
repo_deps.add(dep)
if check_subdeps:
for deps in ((repo_deps, 'repo'), (aur_deps, 'aur')):
if deps[0]:
missing_subdeps = self.deps_analyser.get_missing_dependencies_from(deps[0], deps[1])
if missing_subdeps:
for dep in missing_subdeps:
if not dep[1]:
message.show_dep_not_found(dep[0], self.i18n, watcher)
return
for dep in missing_subdeps:
sorted_deps.append(dep)
for dep, repo in dep_repos.items():
if repo != 'aur':
sorted_deps.append((dep, repo))
for dep in aur_deps:
sorted_deps.append((dep, 'aur'))
return sorted_deps
def _handle_deps_and_keys(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str, check_subdeps: bool = True) -> bool:
handler.watcher.change_substatus(self.i18n['arch.checking.deps'].format(bold(pkgname)))
check_res = makepkg.check(pkgdir, handler)
if check_res:
if check_res.get('missing_deps'):
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in check_res['missing_deps']}
dep_mirrors = self._map_mirrors(depnames)
sorted_deps = self._map_missing_deps(check_res['missing_deps'], handler.watcher, check_subdeps=check_subdeps)
if len(depnames) != len(dep_mirrors): # checking if a dependency could not be found in any mirror
for dep in depnames:
if dep not in dep_mirrors:
message.show_dep_not_found(dep, self.i18n, handler.watcher)
return False
if sorted_deps is None:
return False
handler.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(pkgname)))
if not confirmation.request_install_missing_deps(pkgname, dep_mirrors, handler.watcher, self.i18n):
if not confirmation.request_install_missing_deps(pkgname, sorted_deps, handler.watcher, self.i18n):
handler.watcher.print(self.i18n['action.cancelled'])
return False
dep_not_installed = self._install_deps(dep_mirrors, root_password, handler, change_progress=False)
dep_not_installed = self._install_deps(sorted_deps, root_password, handler, change_progress=False)
if dep_not_installed:
message.show_dep_not_installed(handler.watcher, pkgname, dep_not_installed, self.i18n)
return False
# it is necessary to re-check because missing PGP keys are only notified when there are none missing
return self._check_deps(pkgname, root_password, handler, pkgdir)
return self._handle_deps_and_keys(pkgname, root_password, handler, pkgdir, check_subdeps=False)
if check_res.get('gpg_key'):
if handler.watcher.request_confirmation(title=self.i18n['arch.aur.install.unknown_key.title'],
@@ -520,7 +569,7 @@ class ArchManager(SoftwareManager):
if not to_install:
return True
pkg_mirrors = self._map_mirrors(to_install)
pkg_mirrors = self._map_repos(to_install)
if pkg_mirrors:
final_optdeps = {dep: {'desc': odeps.get(dep), 'mirror': pkg_mirrors.get(dep)} for dep, mirror in pkg_mirrors.items()}
@@ -642,15 +691,15 @@ class ArchManager(SoftwareManager):
if uncompress:
uncompress_dir = '{}/{}'.format(app_build_dir, pkgname)
return self._make_pkg(pkgname=pkgname,
maintainer=maintainer,
root_password=root_password,
handler=handler,
build_dir=app_build_dir,
project_dir=uncompress_dir,
dependency=dependency,
skip_optdeps=skip_optdeps,
change_progress=change_progress)
return self._build(pkgname=pkgname,
maintainer=maintainer,
root_password=root_password,
handler=handler,
build_dir=app_build_dir,
project_dir=uncompress_dir,
dependency=dependency,
skip_optdeps=skip_optdeps,
change_progress=change_progress)
finally:
if os.path.exists(app_build_dir):
handler.handle(SystemProcess(new_subprocess(['rm', '-rf', app_build_dir])))

View File

@@ -0,0 +1,95 @@
from threading import Thread
from typing import Set, List, Tuple
from bauh.gems.arch import pacman
from bauh.gems.arch.aur import AURClient
class DependenciesAnalyser:
def __init__(self, aur_client: AURClient):
self.aur_client = aur_client
def _fill_mirror(self, name: str, output: List[Tuple[str, str]]):
mirror = pacman.read_repository_from_info(name)
if mirror:
output.append((name, mirror))
return
guess = pacman.guess_repository(name)
if guess:
output.append(guess)
return
aur_info = self.aur_client.get_src_info(name)
if aur_info:
output.append((name, 'aur'))
return
output.append((name, ''))
def get_missing_dependencies(self, names: Set[str], mirror: str = None) -> List[Tuple[str, str]]:
missing_names = pacman.check_missing(names)
if missing_names:
missing_root = []
threads = []
if not mirror:
for name in missing_names:
t = Thread(target=self._fill_mirror, args=(name, missing_root))
t.start()
threads.append(t)
for t in threads:
t.join()
threads.clear()
# checking if there is any unknown dependency:
for dep in missing_root:
if not dep[1]:
return missing_root
else:
for missing in missing_names:
missing_root.append((missing, mirror))
missing_sub = []
for dep in missing_root:
subdeps = self.aur_client.get_all_dependencies(dep[0]) if dep[1] == 'aur' else pacman.read_dependencies(dep[0])
if subdeps:
missing_subdeps = self.get_missing_dependencies(subdeps)
# checking if there is any unknown:
if missing_subdeps:
for subdep in missing_subdeps:
if not subdep[0]:
missing_sub.extend(missing_subdeps)
break
missing_sub.extend(missing_subdeps)
return [*missing_sub, *missing_root]
def get_missing_dependencies_from(self, names: Set[str], mirror: str) -> List[Tuple[str, str]]:
missing = []
for dep in names:
subdeps = self.aur_client.get_all_dependencies(dep) if mirror == 'aur' else pacman.read_dependencies(dep)
if subdeps:
missing_subdeps = self.get_missing_dependencies(subdeps)
if missing_subdeps:
missing.extend(missing_subdeps)
for subdep in missing_subdeps: # checking if there is any unknown:
if not subdep[0]:
return missing
return missing

View File

@@ -0,0 +1,5 @@
class PackageNotFoundException(Exception):
def __init__(self, name: str):
self.name = name

View File

@@ -3,9 +3,11 @@ from threading import Thread
from typing import List, Set, Tuple
from bauh.commons.system import run_cmd, new_subprocess, new_root_subprocess, SystemProcess
from bauh.gems.arch.exceptions import PackageNotFoundException
RE_DEPS = re.compile(r'[\w\-_]+:[\s\w_\-\.]+\s+\[\w+\]')
RE_OPTDEPS = re.compile(r'[\w\._\-]+\s*:')
RE_DEP_NOTFOUND = re.compile(r'error:.+\'(.+)\'')
def is_enabled() -> bool:
@@ -13,7 +15,7 @@ def is_enabled() -> bool:
return res and not res.strip().startswith('which ')
def get_mirror(pkg: str) -> Tuple[str, str]:
def get_configured_repository(pkg: str) -> Tuple[str, str]:
res = run_cmd('pacman -Ss {}'.format(pkg))
if res:
@@ -24,7 +26,7 @@ def get_mirror(pkg: str) -> Tuple[str, str]:
return data[1].split(' ')[0], data[0]
def get_mirrors(pkgs: Set[str]) -> dict:
def get_repositories(pkgs: Set[str]) -> dict:
pkgre = '|'.join(pkgs).replace('+', r'\+').replace('.', r'\.')
searchres = new_subprocess(['pacman', '-Ss', pkgre]).stdout
@@ -41,7 +43,7 @@ def get_mirrors(pkgs: Set[str]) -> dict:
if not_found: # if there are some packages not found, try to find via the single method:
for dep in not_found:
mirror_data = get_mirror(dep)
mirror_data = get_configured_repository(dep)
if mirror_data:
mirrors[mirror_data[0]] = mirror_data[1]
@@ -49,7 +51,7 @@ def get_mirrors(pkgs: Set[str]) -> dict:
return mirrors
def is_available_from_mirrors(pkg_name: str) -> bool:
def is_available_in_repositories(pkg_name: str) -> bool:
return bool(run_cmd('pacman -Ss ' + pkg_name))
@@ -236,3 +238,141 @@ def list_ignored_packages(config_path: str = '/etc/pacman.conf') -> Set[str]:
pacman_conf.terminate()
grep.terminate()
return ignored
def check_missing(names: Set[str]) -> Set[str]:
installed = new_subprocess(['pacman', '-Qq', *names])
not_installed = set()
for o in installed.stderr:
if o:
err_line = o.decode()
if err_line:
not_found = RE_DEP_NOTFOUND.findall(err_line)
if not_found:
not_installed.update(not_found)
return not_installed
def read_repository_from_info(name: str) -> str:
info = new_subprocess(['pacman', '-Si', name])
not_found = False
for o in info.stderr:
if o:
err_line = o.decode()
if RE_DEP_NOTFOUND.findall(err_line):
not_found = True
if not_found:
return
mirror = None
for o in new_subprocess(['grep', '-Po', "Repository\s+:\s+\K.+"], stdin=info.stdout).stdout:
if o:
line = o.decode().strip()
if line:
mirror = line
return mirror
def guess_repository(name: str) -> Tuple[str, str]:
res = run_cmd('pacman -Ss {}'.format(name.split('=')[0] if '=' in name else name))
if res:
lines = res.split('\n')
if lines:
data = lines[0].split('/')
return data[1].split(' ')[0], data[0]
def read_dependencies(name: str) -> Set[str]:
dep_info = new_subprocess(['pacman', '-Si', name])
not_found = False
for o in dep_info.stderr:
if o:
err_line = o.decode()
if err_line:
if RE_DEP_NOTFOUND.findall(err_line):
not_found = True
if not_found:
raise PackageNotFoundException(name)
depends_on = set()
for out in new_subprocess(['grep', '-Po', 'Depends\s+On\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
if out:
line = out.decode().strip()
if line:
depends_on.update(line.split(' '))
return depends_on
"""
def check_uninstalled(names: List[str], stop_recursion_if_no_mirror: bool = True) -> Dict[str, str]:
missing = {}
installed = new_subprocess(['pacman', '-Qq', *names])
not_installed = []
for o in installed.stderr:
if o:
err_line = o.decode()
if err_line:
not_found = RE_DEP_NOTFOUND.findall(err_line)
if not_found:
not_installed.extend(not_found)
if not_installed:
missing_mirrors = False
for dep in not_installed:
pkgname = dep
not_installed_info = new_subprocess(['pacman', '-Si', dep])
mirror = None
for o in new_subprocess(['grep', '-Po', "Repository\s+:\s+\K.+"], stdin=not_installed_info.stdout).stdout:
if o:
line = o.decode().strip()
if line:
mirror = line
if not mirror:
mirror_data = get_configured_repository(dep)
if mirror_data:
pkgname = mirror_data[0]
mirror = mirror_data[1]
missing[pkgname] = mirror
if mirror is None:
missing_mirrors = True
if stop_recursion_if_no_mirror and missing_mirrors:
return missing
subdeps_mirrors = dict()
for pkg in missing:
subdeps_mirrors.update(check_uninstalled(pkg))
missing.update(subdeps_mirrors, stop_recursion_if_no_mirror=stop_recursion_if_no_mirror)
return missing
"""

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=té un conflicte amb
arch.install.conflict.popup.title=Sha detectat un conflicte
arch.install.conflict.popup.body=Les aplicacions {} estan en conflicte. Heu de desinstal·lar una per a instal·lar laltra. Voleu continuar?
arch.missing_deps.title=Dependències mancants
arch.missing_deps.body=Shan dinstal·lar les dependències següents abans de continuar amb la instal·lació de {}
arch.missing_deps.body=Shan dinstal·lar les {deps} dependències següents abans de continuar amb la instal·lació de {name}
arch.downgrade.error=Error
arch.downgrade.impossible=No sha pogut revertir la versió de {}
aur.history.1_version=versió
@@ -69,8 +69,10 @@ aur.info.09_last_modified=darrera modificació
aur.info.10_url=url de baixada
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependències
aur.info.12_optdepends=dependències opcionals
aur.info.12_makedepends=dependències de compilació
aur.info.13_dependson=dependències dinstal·lació
aur.info.14_optdepends=dependències opcionals
aur.info.15_checkdepends=dependències de verificació
aur.info.14_installed_files=Fitxers instal·lats
arch.install.dep_not_found.title=No sha trobat la dependència
arch.install.dep_not_found.body=No sha trobat la dependència requerida {} a lAUR ni als servidors rèplica per defecte. Sha cancel·lat la instal·lació.

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=AUR Pakete werden von einer unabhängigen Nutzergemeinsc
arch.install.conflict.popup.title=Konflikt entdeckt
arch.install.conflict.popup.body=Die Anwendungen {} können nicht gleichzeitig installiert sein. Du musst eine deinstallieren um die andere zu installieren. Fortfahren?
arch.missing_deps.title=Fehlende Abhängigkeiten
arch.missing_deps.body=Die folgenden Abhängigkeiten müssten installiert sein, bevor mit der {} Installation fortgefahren werden kann
arch.missing_deps.body=Die folgenden {deps} Abhängigkeiten müssten installiert sein, bevor mit der {name} Installation fortgefahren werden kann
arch.downgrade.error=Fehler
arch.downgrade.impossible=Downgrade von {} ist nicht möglich
aur.history.1_version=Version
@@ -37,8 +37,10 @@ aur.info.09_last_modified=Zuletzt verändert
aur.info.10_url=url zum Herunterladen
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=Abhängigkeiten
aur.info.12_optdepends=optionale Abhängigkeiten
aur.info.12_makedepends=Kompilierungsabhängigkeiten
aur.info.13_dependson=Installationsabhängigkeiten
aur.info.14_optdepends=optionale Abhängigkeiten
aur.info.15_checkdepends=Überprüfungsabhängigkeiten
aur.info.14_installed_files=Installationsdateien
arch.install.dep_not_found.title=Abhängigkeit nicht gefunden
arch.install.dep_not_found.body=Nötige Abhängigkeit {} wurde weder im AUR noch in den standard Spiegelservern gefunden. Installation abgebrochen

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=AUR packages are maintained by an independent user commu
arch.install.conflict.popup.title=Conflict detected
arch.install.conflict.popup.body=The applications {} are in conflict. You must uninstall one to install the other. Continue ?
arch.missing_deps.title=Missing dependencies
arch.missing_deps.body=The following dependencies must be installed before the {} installation continues
arch.missing_deps.body=The following {deps} dependencies must be installed before the {name} installation continues
arch.downgrade.error=Error
arch.downgrade.impossible=It is not possible to downgrade {}
aur.history.1_version=version
@@ -37,8 +37,10 @@ aur.info.09_last_modified=last modified
aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependencies
aur.info.12_optdepends=optional dependencies
aur.info.12_makedepends=compilation dependencies
aur.info.13_dependson=installation dependencies
aur.info.14_optdepends=optional dependencies
aur.info.15_checkdepends=checking dependencies
aur.info.14_installed_files=Installed files
arch.install.dep_not_found.title=Dependency not found
arch.install.dep_not_found.body=Required dependency {} was not found in AUR nor in default mirrors. Installation cancelled.

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=conflicta
arch.install.conflict.popup.title=Conflicto detectado
arch.install.conflict.popup.body=Los aplicativos {} estan en conflicto. Debe desinstalar uno para instalar el otro. ¿Continuar?
arch.missing_deps.title=Dependencias faltantes
arch.missing_deps.body=Deben instalarse las siguientes dependencias antes de continuar la instalación de {}
arch.missing_deps.body=Deben instalarse las siguientes {deps} dependencias antes de continuar la instalación de {name}
arch.downgrade.error=Error
arch.downgrade.impossible=No es posible revertir la versión de {}
aur.history.1_version=versión
@@ -69,8 +69,10 @@ aur.info.09_last_modified=última modificación
aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependencias
aur.info.12_optdepends=dependencias opcionales
aur.info.12_makedepends=dependencias para compilacion
aur.info.13_dependson=dependencias para instalación
aur.info.14_optdepends=dependencias opcionales
aur.info.15_checkdepends=dependencias para verificación
aur.info.14_installed_files=Archivos instalados
arch.install.dep_not_found.title=Dependencia no encontrada
arch.install.dep_not_found.body=No se encontró la dependencia requerida {} en AUR ni en los espejos predeterminados. Instalación cancelada.

View File

@@ -4,7 +4,7 @@ gem.aur.install.warning=I pacchetti AUR sono gestiti da una comunità di utenti
arch.install.conflict.popup.title=Conflitto rilevato
arch.install.conflict.popup.body=Le applicazioni {} sono in conflitto. È necessario disinstallarne una per installare l'altra. Continua ?
arch.missing_deps.title=Dipendenze mancanti
arch.missing_deps.body=Le seguenti dipendenze devono essere installate prima che l'installazione di {} continui
arch.missing_deps.body=Le seguenti {deps} dipendenze devono essere installate prima che l'installazione di {name} continui
arch.downgrade.error=Errore
arch.downgrade.impossible=Non è possibile effettuare il downgrade {}
aur.history.1_version=versione
@@ -37,8 +37,10 @@ aur.info.09_last_modified=ultima modifica
aur.info.10_url=url di download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dipendenze
aur.info.12_optdepends=dipendenze opzionali
aur.info.12_makedepends=dipendenze di compilazione
aur.info.13_dependson=dipendenze di installazione
aur.info.14_optdepends=dipendenze opzionali
aur.info.15_checkdepends=dipendenze di verifica
aur.info.14_installed_files=File installati
arch.install.dep_not_found.title=Dipendenza non trovata
arch.install.dep_not_found.body=La dipendenza richiesta {} non è stata trovata in AUR né nei mirror predefiniti. Installazione annullata.

View File

@@ -36,7 +36,7 @@ aur.info.conflicts with=conflita
arch.install.conflict.popup.title=Conflito detectado
arch.install.conflict.popup.body=Os aplicativos {} estão em conflito. Você precisa desinstalar um para instalar o outro. Continuar ?
arch.missing_deps.title=Dependências ausentes
arch.missing_deps.body=As seguintes dependências devem ser instaladas antes de continuar com a instalação de {}
arch.missing_deps.body=As seguintes {deps} dependências devem ser instaladas antes de continuar com a instalação de {name}
arch.downgrade.error=Erro
arch.downgrade.impossible=Não é possível reverter a versão de {}
aur.history.1_version=versão
@@ -69,8 +69,10 @@ aur.info.09_last_modified=última modificação
aur.info.10_url=url download
aur.info.11_pkg_build_url=url pkgbuild
aur.info.13_pkg_build=pkgbuild
aur.info.11_dependson=dependências
aur.info.12_optdepends=dependências opcionais
aur.info.12_makedepends=dependências para compilação
aur.info.13_dependson=dependências para instalação
aur.info.14_optdepends=dependências opcionais
aur.info.15_checkdepends=dependências para verificação
aur.info.14_installed_files=Arquivos instalados
arch.install.dep_not_found.title=Dependência não encontrada
arch.install.dep_not_found.body=A dependência {} não foi encontrado no AUR nem nos espelhos padrões. Instalação cancelada.

View File

@@ -161,8 +161,11 @@ class SnapManager(SoftwareManager):
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])
body = '<p>{}.</p>'.format(self.i18n['snap.install.available_channels.message'].format(bold(self.i18n['stable']), bold(pkg.name)))
body += '<p>{}:</p>'.format(self.i18n['snap.install.available_channels.help'])
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)) + ':',
body=body,
components=[channel_select],
confirmation_label=self.i18n['continue'],
deny_label=self.i18n['cancel']):

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Sembla que lAPI de {} no està disponible
snap.action.refresh.status=Sestà actualitzant
snap.action.refresh.label=Actualitza
snap.install.available_channels.title=Canals disponibles
snap.install.available_channels.body=No hi ha un canal {} (stable) disponible per a {}. Però més avall nhi ha els següents (seleccioneu-ne un si voleu continuar)
snap.install.available_channels.message=No hi ha un canal {} (stable) disponible per a {}. Però més avall nhi ha els següents
snap.install.available_channels.help=Seleccioneu-ne un si voleu continuar

View File

@@ -5,7 +5,8 @@ snap.notifications.api.unavailable=Es scheint als ob die {} API aktuell nicht ve
snap.action.refresh.status=Erneuern
snap.action.refresh.label=Erneuern
snap.install.available_channels.title=Verfügbare Channels
snap.install.available_channels.body=Es ist kein {} Channel verfügbar für {}. Es gibt jedoch folgende (wähle einen aus um fortzufahren)
snap.install.available_channels.message=Es ist kein {} Channel verfügbar für {}. Es gibt jedoch folgende
snap.install.available_channels.help=Wähle einen aus um fortzufahren
snap.info.commands=Kommandos
snap.info.contact=Kontakt
snap.info.description=Beschreibung

View File

@@ -5,7 +5,8 @@ snap.notifications.api.unavailable=It seems the {} API is unavailable at the mom
snap.action.refresh.status=Refreshing
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 below ( select one if you want to continue )
snap.install.available_channels.message=There is no {} channel available for {}. But there are these below
snap.install.available_channels.help=Select one if you want to continue
snap.info.commands=commands
snap.info.contact=contact
snap.info.description=description

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Parece que la API de {} no está disponible e
snap.action.refresh.status=Actualizando
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 abajo ( seleccione uno si desea continuar )
snap.install.available_channels.message=No hay un canal {} ( stable ) disponible para {}. Pero hay estos otros abajo
snap.install.available_channels.help=Seleccione uno si desea continuar

View File

@@ -5,4 +5,5 @@ snap.notifications.api.unavailable=Sembra che l'API {} non sia al momento dispon
snap.action.refresh.status=Ripristinare
snap.action.refresh.label=Ripristina
snap.install.available_channels.title=Canali disponibili
snap.install.available_channels.body=Non esiste un {} canale disponibile per {}. Ma ci sono questi sotto (selezionane uno se vuoi continuare)
snap.install.available_channels.message=Non esiste un {} canale disponibile per {}. Ma ci sono questi sotto
snap.install.available_channels.help=Selezionane uno se vuoi continuare

View File

@@ -15,4 +15,5 @@ snap.notifications.api.unavailable=Parece que a API de {} está indisponível no
snap.action.refresh.status=Atualizando
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 abaixo ( selecione algum se quiser continuar )
snap.install.available_channels.message=Não há um canal {} ( stable ) disponível para {}. Porém existem estes outros abaixo
snap.install.available_channels.help=Selecione algum se quiser continuar

View File

@@ -1,6 +1,7 @@
from typing import List
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QMessageBox, QVBoxLayout, QLabel, QWidget, QScrollArea, QFrame
from bauh.api.abstract.view import ViewComponent, SingleSelectComponent, MultipleSelectComponent
from bauh.view.qt import css
@@ -10,7 +11,8 @@ from bauh.view.util.translation import I18n
class ConfirmationDialog(QMessageBox):
def __init__(self, title: str, body: str, i18n: I18n, components: List[ViewComponent] = None, confirmation_label: str = None, deny_label: str = None):
def __init__(self, title: str, body: str, i18n: I18n, screen_size: QSize, components: List[ViewComponent] = None,
confirmation_label: str = None, deny_label: str = None):
super(ConfirmationDialog, self).__init__()
self.setWindowTitle(title)
self.setStyleSheet('QLabel { margin-right: 25px; }')
@@ -26,8 +28,15 @@ class ConfirmationDialog(QMessageBox):
self.layout().addWidget(QLabel(body), 0, 1)
if components:
comps_container = QWidget(parent=self)
scroll = QScrollArea(self)
scroll.setFrameShape(QFrame.NoFrame)
scroll.setWidgetResizable(True)
comps_container = QWidget()
comps_container.setLayout(QVBoxLayout())
scroll.setWidget(comps_container)
height = 0
for idx, comp in enumerate(components):
if isinstance(comp, SingleSelectComponent):
@@ -37,9 +46,16 @@ class ConfirmationDialog(QMessageBox):
else:
raise Exception("Cannot render instances of " + comp.__class__.__name__)
height += inst.sizeHint().height()
comps_container.layout().addWidget(inst)
self.layout().addWidget(comps_container, 1, 1)
height = height if height < screen_size.height() / 2 else height / 2
if height < 100:
height = 100
scroll.setFixedHeight(height)
self.layout().addWidget(scroll, 1, 1)
self.exec_()

View File

@@ -379,7 +379,8 @@ class ManageWindow(QWidget):
i18n=self.i18n,
components=msg['components'],
confirmation_label=msg['confirmation_label'],
deny_label=msg['deny_label'])
deny_label=msg['deny_label'],
screen_size=self.screen_size)
res = diag.is_confirmed()
self.thread_animate_progress.animate()
self.signal_user_res.emit(res)