gem selector sketch

This commit is contained in:
Vinicius Moreira
2019-09-11 16:00:02 -03:00
parent 696b6169ac
commit ba5e1da11b
79 changed files with 4117 additions and 112 deletions

View File

@@ -0,0 +1,4 @@
import os
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
BUILD_DIR = '/tmp/bauh/aur'

28
bauh/gems/arch/aur.py Normal file
View File

@@ -0,0 +1,28 @@
import re
from typing import Set, List
from bauh.api.http import HttpClient
URL_INFO = 'https://aur.archlinux.org/rpc/?v=5&type=info&'
URL_SEARCH = 'https://aur.archlinux.org/rpc/?v=5&type=search&arg='
def map_pkgbuild(pkgbuild: str) -> dict:
return {attr: val.replace('"', '').replace("'", '').replace('(', '').replace(')', '') for attr, val in re.findall(r'\n(\w+)=(.+)', pkgbuild)}
class AURClient:
def __init__(self, http_client: HttpClient):
self.http_client = http_client
self.names_index = set()
def search(self, words: str) -> dict:
return self.http_client.get_json(URL_SEARCH + words)
def get_info(self, names: Set[str]) -> List[dict]:
res = self.http_client.get_json(URL_INFO + self._map_names_as_queries(names))
return res['results'] if res and res.get('results') else []
def _map_names_as_queries(self, names) -> str:
return '&'.join(['arg[{}]={}'.format(i, n) for i, n in enumerate(names)])

View File

@@ -0,0 +1,28 @@
from typing import Set
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.view import MultipleSelectComponent, InputOption
from bauh.commons.html import bold
def request_optional_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: dict) -> Set[str]:
view_opts = MultipleSelectComponent(label='',
options=[InputOption('{} ( {} )'.format(p, m.upper()), p) for p, m in
pkg_mirrors.items()])
install = watcher.request_confirmation(title=i18n['arch.install.optdeps.request.title'],
body='<p>{}</p>'.format(i18n['arch.install.optdeps.request.body'].format(bold(pkgname)) + ':'),
components=[view_opts],
confirmation_label=i18n['install'],
deny_label=i18n['cancel'])
if install:
return {o.value for o in view_opts.values}
def request_install_missing_deps(pkgname: str, pkg_mirrors: dict, watcher: ProcessWatcher, i18n: dict) -> bool:
deps_str = ''.join(['<br/><span style="font-weight:bold"> - {} ( {} )</span>'.format(d, m.upper()) for d, m in pkg_mirrors.items()])
msg = '<p>{}</p>'.format(i18n['arch.missing_deps.body'].format('<span style="font-weight: bold">{}</span>'.format(pkgname) + ':<br/>' + deps_str))
msg += i18n['ask.continue']
return watcher.request_confirmation(i18n['arch.missing_deps.title'], msg)

View File

@@ -0,0 +1,570 @@
import os
import re
import shutil
import time
from pathlib import Path
from threading import Thread
from typing import List, Set, Type
from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePackage, PackageSuggestion
from bauh.api.abstract.view import MessageType
from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, run_cmd, new_root_subprocess
from bauh.gems.arch import BUILD_DIR, aur, pacman, makepkg, pkgbuild, message, confirmation, disk, git, suggestions
from bauh.gems.arch.aur import AURClient
from bauh.gems.arch.mapper import ArchDataMapper
from bauh.gems.arch.model import ArchPackage
from bauh.commons.html import bold
from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater
URL_GIT = 'https://aur.archlinux.org/{}.git'
URL_PKG_DOWNLOAD = 'https://aur.archlinux.org/cgit/aur.git/snapshot/{}.tar.gz'
URL_SRC_INFO = 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h='
RE_SPLIT_VERSION = re.compile(r'(=|>|<)')
class ArchManager(SoftwareManager):
def __init__(self, context: ApplicationContext):
super(ArchManager, self).__init__(context=context)
self.aur_cache = context.cache_factory.new()
# context.disk_loader_factory.map(ArchPackage, self.aur_cache) TODO
self.mapper = ArchDataMapper(http_client=context.http_client)
self.i18n = context.i18n
self.aur_client = AURClient(context.http_client)
self.names_index = {}
self.aur_index_updater = AURIndexUpdater(context, self)
self.dcache_updater = ArchDiskCacheUpdater(context.logger)
self.logger = context.logger
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'])
app.downgrade_enabled = downgrade_enabled
if app.installed:
res.installed.append(app)
if disk_loader:
disk_loader.fill(app)
else:
res.new.append(app)
Thread(target=self.mapper.fill_package_build, args=(app,)).start()
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1) -> SearchResult:
downgrade_enabled = git.is_enabled()
res = SearchResult([], [], 0)
installed = {}
read_installed = Thread(target=lambda: installed.update(pacman.list_and_map_installed()))
read_installed.start()
api_res = self.aur_client.search(words)
if api_res and api_res.get('results'):
read_installed.join()
for pkgdata in api_res['results']:
self._upgrade_search_result(pkgdata, installed, downgrade_enabled, res, disk_loader)
else: # if there are no results from the API (it could be because there were too many), tries the names index:
if self.names_index:
to_query = set()
for norm_name, real_name in self.names_index.items():
if words in norm_name:
to_query.add(real_name)
if len(to_query) == 25:
break
pkgsinfo = self.aur_client.get_info(to_query)
if pkgsinfo:
read_installed.join()
for pkgdata in pkgsinfo:
self._upgrade_search_result(pkgdata, installed, res)
res.total = len(res.installed) + len(res.new)
return res
def _fill_aur_pkgs(self, not_signed: dict, apps: list, disk_loader: DiskCacheLoader):
downgrade_enabled = git.is_enabled()
pkgsinfo = self.aur_client.get_info(not_signed.keys())
if pkgsinfo:
for pkgdata in pkgsinfo:
app = self.mapper.map_api_data(pkgdata, not_signed)
app.downgrade_enabled = downgrade_enabled
if disk_loader:
disk_loader.fill(app)
apps.append(app)
def _fill_mirror_pkgs(self, mirrors: dict, apps: list):
# TODO
for name, data in mirrors.items():
app = ArchPackage(name=name, version=data.get('version'), latest_version=data.get('version'), description=data.get('description'))
app.installed = True
app.mirror = '' # TODO
app.update = False # TODO
apps.append(app)
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None) -> SearchResult:
installed = pacman.list_and_map_installed()
apps = []
if installed and installed['not_signed']:
self._fill_aur_pkgs(installed['not_signed'], apps, disk_loader)
return SearchResult(apps, None, len(apps))
def downgrade(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher) -> bool:
handler = ProcessHandler(watcher)
app_build_dir = '{}/build_{}'.format(BUILD_DIR, int(time.time()))
watcher.change_progress(5)
try:
if not os.path.exists(app_build_dir):
build_dir = handler.handle(SystemProcess(new_subprocess(['mkdir', '-p', app_build_dir])))
if build_dir:
watcher.change_progress(10)
watcher.change_substatus(self.i18n['arch.clone'].format(bold(pkg.name)))
clone = handler.handle(SystemProcess(subproc=new_subprocess(['git', 'clone', URL_GIT.format(pkg.name)], cwd=app_build_dir), check_error_output=False))
watcher.change_progress(30)
if clone:
watcher.change_substatus(self.i18n['arch.downgrade.reading_commits'])
clone_path = '{}/{}'.format(app_build_dir, pkg.name)
pkgbuild_path = '{}/PKGBUILD'.format(clone_path)
commits = run_cmd("git log", cwd=clone_path)
watcher.change_progress(40)
if commits:
commit_list = re.findall(r'commit (.+)\n', commits)
if commit_list:
if len(commit_list) > 1:
for idx in range(1, len(commit_list)):
commit = commit_list[idx]
with open(pkgbuild_path) as f:
pkgdict = aur.map_pkgbuild(f.read())
if not handler.handle(SystemProcess(subproc=new_subprocess(['git', 'reset', '--hard', commit], cwd=clone_path), check_error_output=False)):
watcher.print('Could not downgrade anymore. Aborting...')
return False
if '{}-{}'.format(pkgdict.get('pkgver'), pkgdict.get('pkgrel')) == pkg.version:
# current version found
watcher.change_substatus(self.i18n['arch.downgrade.version_found'])
break
watcher.change_substatus(self.i18n['arch.downgrade.install_older'])
return self._make_pkg(pkg.name, 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),
type_=MessageType.ERROR)
return False
watcher.show_message(title=self.i18n['error'], body=self.i18n['arch.downgrade.no_commits'], type_=MessageType.ERROR)
return False
finally:
if os.path.exists(app_build_dir):
handler.handle(SystemProcess(subproc=new_subprocess(['rm', '-rf', app_build_dir])))
return False
def clean_cache_for(self, pkg: ArchPackage):
if os.path.exists(pkg.get_disk_cache_path()):
shutil.rmtree(pkg.get_disk_cache_path())
def update(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher) -> bool:
return self.install(pkg=pkg, root_password=root_password, watcher=watcher, skip_optdeps=True)
def _uninstall(self, pkg_name: str, root_password: str, handler: ProcessHandler) -> bool:
return handler.handle(SystemProcess(new_root_subprocess(['pacman', '-R', pkg_name, '--noconfirm'], root_password)))
def uninstall(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher) -> bool:
handler = ProcessHandler(watcher)
watcher.change_progress(10)
info = pacman.get_info_dict(pkg.name)
watcher.change_progress(50)
if info.get('required by'):
pkname = '"{}"'.format(pkg.name)
msg = '{}:\n\n{}\n\n{}'.format(self.i18n['arch.uninstall.required_by'].format(pkname), info['required by'], self.i18n['arch.uninstall.required_by.advice'].format(pkname))
watcher.show_message(title=self.i18n['error'], body=msg, type_=MessageType.WARNING)
return False
uninstalled = self._uninstall(pkg.name, root_password, handler)
watcher.change_progress(100)
return uninstalled
def get_managed_types(self) -> Set["type"]:
return {ArchPackage}
def get_info(self, pkg: ArchPackage) -> dict:
if pkg.installed:
t = Thread(target=self.mapper.fill_package_build, args=(pkg,))
t.start()
info = pacman.get_info_dict(pkg.name)
t.join()
if pkg.pkgbuild:
info['13_pkg_build'] = pkg.pkgbuild
return info
else:
info = {
'01_id': pkg.id,
'02_name': pkg.name,
'03_version': pkg.version,
'04_popularity': pkg.popularity,
'05_votes': pkg.votes,
'06_package_base': pkg.package_base,
'07_maintainer': pkg.maintainer,
'08_first_submitted': pkg.first_submitted,
'09_last_modified': pkg.last_modified,
'10_url': pkg.url_download,
}
res_srcinfo = self.context.http_client.get(URL_SRC_INFO + pkg.name)
if res_srcinfo and res_srcinfo.text:
info['11_dependson'] = ' '.join(pkgbuild.read_depends_on(res_srcinfo.text))
info['12_optdepends'] = ' '.join(pkgbuild.read_optdeps(res_srcinfo.text))
if pkg.pkgbuild:
info['13_pkg_build'] = pkg.pkgbuild
else:
info['11_pkg_build_url'] = pkg.get_pkg_build_url()
return info
def get_history(self, pkg: ArchPackage) -> PackageHistory:
temp_dir = '{}/build_{}'.format(BUILD_DIR, int(time.time()))
try:
Path(temp_dir).mkdir(parents=True)
run_cmd('git clone ' + URL_GIT.format(pkg.name), print_error=False, cwd=temp_dir)
clone_path = '{}/{}'.format(temp_dir, pkg.name)
pkgbuild_path = '{}/PKGBUILD'.format(clone_path)
commits = git.list_commits(clone_path)
if commits:
history, status_idx = [], 0
for idx, commit in enumerate(commits):
with open(pkgbuild_path) as f:
pkgdict = aur.map_pkgbuild(f.read())
if '{}-{}'.format(pkgdict.get('pkgver'), pkgdict.get('pkgrel')) == pkg.version:
status_idx = idx
history.append({'1_version': pkgdict['pkgver'], '2_release': pkgdict['pkgrel'],
'3_date': commit['date']}) # the number prefix is to ensure the rendering order
if idx + 1 < len(commits):
if not run_cmd('git reset --hard ' + commits[idx + 1]['commit'], cwd=clone_path):
break
return PackageHistory(pkg=pkg, history=history, pkg_status_idx=status_idx)
finally:
if os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
def _install_deps(self, deps: Set[str], pkg_mirrors: dict, root_password: str, handler: ProcessHandler, change_progress: bool = False) -> str:
"""
:param deps:
:param pkg_mirrors:
:param root_password:
:param handler:
:return: not installed dependency
"""
progress_increment = int(100 / len(deps))
progress = 0
self._update_progress(handler.watcher, 1, change_progress)
for pkgname in deps:
mirror = pkg_mirrors[pkgname]
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, root_password=root_password, handler=handler, dependency=True, change_progress=False)
else:
installed = self._install(pkgname=pkgname, root_password=root_password, handler=handler, install_file=None, mirror=mirror, change_progress=False)
if not installed:
return pkgname
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
if len(pkgnames) != 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}
for pkginfo in self.aur_client.get_info(nomirrors):
if pkginfo.get('Name') in nomirrors:
pkg_mirrors[pkginfo['Name']] = 'aur'
return pkg_mirrors
def _make_pkg(self, pkgname: str, root_password: str, handler: ProcessHandler, build_dir: str, project_dir: str, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
self._update_progress(handler.watcher, 50, change_progress)
if not self._install_missings_deps(pkgname, root_password, handler, project_dir):
return False
# building main package
handler.watcher.change_substatus(self.i18n['arch.building.package'].format(bold(pkgname)))
pkgbuilt = handler.handle(SystemProcess(new_subprocess(['makepkg', '-Acsmf'], cwd=project_dir)))
self._update_progress(handler.watcher, 65, change_progress)
if pkgbuilt:
gen_file = [fname for root, dirs, files in os.walk(build_dir) for fname in files if re.match(r'^{}-.+\.tar\.xz'.format(pkgname), fname)]
if not gen_file:
handler.watcher.print('Could not find generated .tar.xz file. Aborting...')
return False
install_file = '{}/{}'.format(project_dir, gen_file[0])
if self._install(pkgname=pkgname, root_password=root_password, mirror='aur', handler=handler, install_file=install_file, pkgdir=project_dir, change_progress=change_progress):
if dependency or skip_optdeps:
return True
handler.watcher.change_substatus(self.i18n['arch.optdeps.checking'].format(bold(pkgname)))
if self._install_optdeps(pkgname, root_password, handler, project_dir, change_progress=change_progress):
return True
return False
def _install_missings_deps(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str) -> bool:
handler.watcher.change_substatus(self.i18n['arch.checking.deps'].format(bold(pkgname)))
missing_deps = makepkg.check_missing_deps(pkgdir, handler.watcher)
if missing_deps:
depnames = {RE_SPLIT_VERSION.split(dep)[0] for dep in missing_deps}
dep_mirrors = self._map_mirrors(depnames)
for dep in depnames: # cheking if a dependency could not be found in any mirror
if dep not in dep_mirrors:
message.show_dep_not_found(dep, self.i18n, handler.watcher)
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):
handler.watcher.print(self.i18n['action.cancelled'])
return False
dep_not_installed = self._install_deps(depnames, dep_mirrors, 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
return True
def _install_optdeps(self, pkgname: str, root_password: str, handler: ProcessHandler, pkgdir: str, change_progress: bool = True) -> bool:
with open('{}/.SRCINFO'.format(pkgdir)) as f:
odeps = pkgbuild.read_optdeps(f.read())
if not odeps:
return True
installed = pacman.list_installed()
to_install = {d for d in odeps if d not in installed}
if not to_install:
return True
pkg_mirrors = self._map_mirrors(to_install)
if pkg_mirrors:
deps_to_install = confirmation.request_optional_deps(pkgname, pkg_mirrors, handler.watcher, self.i18n)
if not deps_to_install:
return True
else:
dep_not_installed = self._install_deps(deps_to_install, pkg_mirrors, root_password, handler, change_progress=True)
if dep_not_installed:
message.show_optdep_not_installed(dep_not_installed, handler.watcher, self.i18n)
return False
return True
def _install(self, pkgname: str, root_password: str, mirror: str, handler: ProcessHandler, install_file: str = None, pkgdir: str = '.', change_progress: bool = True):
check_install_output = []
pkgpath = install_file if install_file else pkgname
handler.watcher.change_substatus(self.i18n['arch.checking.conflicts'].format(bold(pkgname)))
for check_out in new_root_subprocess(['pacman', '-U', pkgpath], root_password=root_password, cwd=pkgdir).stdout:
if check_out:
check_install_output.append(check_out.decode())
self._update_progress(handler.watcher, 70, change_progress)
if check_install_output and 'conflict' in check_install_output[-1]:
conflicting_apps = [w[0] for w in re.findall(r'((\w|\-|\.)+)\s(and|are)', check_install_output[-1])]
conflict_msg = ' {} '.format(self.i18n['and']).join(conflicting_apps)
if not handler.watcher.request_confirmation(title=self.i18n['arch.install.conflict.popup.title'],
body=self.i18n['arch.install.conflict.popup.body'].format(conflict_msg)):
handler.watcher.print(self.i18n['action.cancelled'])
return False
else: # uninstall conflicts
self._update_progress(handler.watcher, 75, change_progress)
to_uninstall = [conflict for conflict in conflicting_apps if conflict != pkgname]
for conflict in to_uninstall:
handler.watcher.change_substatus(self.i18n['arch.uninstalling.conflict'].format(bold(conflict)))
if not self._uninstall(conflict, root_password, handler):
handler.watcher.show_message(title=self.i18n['error'],
body=self.i18n['arch.uninstalling.conflict.fail'].format('"{}"'.format(conflict)),
type_=MessageType.ERROR)
return False
handler.watcher.change_substatus(self.i18n['arch.installing.package'].format(bold(pkgname)))
self._update_progress(handler.watcher, 80, change_progress)
installed = handler.handle(pacman.install_as_process(pkgpath=pkgpath, root_password=root_password, aur=install_file is not None, pkgdir=pkgdir))
self._update_progress(handler.watcher, 95, change_progress)
if installed and self.context.disk_cache:
handler.watcher.change_substatus(self.i18n['status.caching_data'].format(bold(pkgname)))
disk.save(pkgname, mirror)
self._update_progress(handler.watcher, 100, change_progress)
return installed
def _update_progress(self, watcher: ProcessWatcher, val: int, change_progress: bool):
if change_progress:
watcher.change_progress(val)
def _install_from_aur(self, pkgname: str, root_password: str, handler: ProcessHandler, dependency: bool, skip_optdeps: bool = False, change_progress: bool = True) -> bool:
app_build_dir = '{}/build_{}'.format(BUILD_DIR, int(time.time()))
try:
if not os.path.exists(app_build_dir):
build_dir = handler.handle(SystemProcess(new_subprocess(['mkdir', '-p', app_build_dir])))
self._update_progress(handler.watcher, 10, change_progress)
if build_dir:
file_url = URL_PKG_DOWNLOAD.format(pkgname)
file_name = file_url.split('/')[-1]
handler.watcher.change_substatus('{} {}'.format(self.i18n['arch.downloading.package'], bold(file_name)))
download = handler.handle(SystemProcess(new_subprocess(['wget', file_url], cwd=app_build_dir)))
if download:
self._update_progress(handler.watcher, 30, change_progress)
handler.watcher.change_substatus('{} {}'.format(self.i18n['arch.uncompressing.package'], bold(file_name)))
uncompress = handler.handle(SystemProcess(new_subprocess(['tar', 'xvzf', '{}.tar.gz'.format(pkgname)], cwd=app_build_dir)))
self._update_progress(handler.watcher, 40, change_progress)
if uncompress:
uncompress_dir = '{}/{}'.format(app_build_dir, pkgname)
return self._make_pkg(pkgname=pkgname,
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])))
return False
def install(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher, skip_optdeps: bool = False) -> bool:
return self._install_from_aur(pkg.name, root_password, ProcessHandler(watcher), dependency=False, skip_optdeps=skip_optdeps)
def _is_wget_available(self):
try:
new_subprocess(['wget', '--version'])
return True
except FileNotFoundError:
return False
def is_enabled(self) -> bool:
try:
return pacman.is_enabled() and self._is_wget_available()
except FileNotFoundError:
return False
def is_downgrade_enabled(self) -> bool:
try:
new_subprocess(['git', '--version'])
return True
except FileNotFoundError:
return False
def cache_to_disk(self, pkg: ArchPackage, icon_bytes: bytes, only_icon: bool):
if self.context.disk_cache and pkg.supports_disk_cache():
pass
def requires_root(self, action: str, pkg: ArchPackage):
return action != 'search'
def prepare(self):
self.dcache_updater.start()
self.aur_index_updater.start()
def list_updates(self) -> List[PackageUpdate]:
return [PackageUpdate(app.id, app.latest_version, 'aur') for app in self.read_installed(disk_loader=None).installed if app.update]
def list_warnings(self) -> List[str]:
warnings = []
if not pacman.is_enabled():
warnings.append(self.i18n['arch.warning.disabled'].format(bold('pacman')))
if not self._is_wget_available():
warnings.append(self.i18n['arch.warning.disabled'].format(bold('wget')))
if not git.is_enabled():
warnings.append(self.i18n['arch.warning.git'].format(bold('git')))
return warnings
def list_suggestions(self, limit: int) -> List[PackageSuggestion]:
res = []
sugs = [(i, p) for i, p in suggestions.ALL.items()]
sugs.sort(key=lambda t: t[1].value, reverse=True)
if limit > 0:
sugs = sugs[0:limit]
sug_names = {s[0] for s in sugs}
api_res = self.aur_client.get_info(sug_names)
if api_res:
for pkg in api_res:
if pkg.get('Name') in sug_names:
res.append(PackageSuggestion(self.mapper.map_api_data(pkg, {}), suggestions.ALL.get(pkg['Name'])))
return res

179
bauh/gems/arch/disk.py Normal file
View File

@@ -0,0 +1,179 @@
import json
import os
import re
from pathlib import Path
from typing import Set, List
from bauh.gems.arch import pacman
from bauh.gems.arch.model import ArchPackage
RE_DESKTOP_ENTRY = re.compile(r'(Exec|Icon)\s*=\s*(.+)')
RE_CLEAN_NAME = re.compile(r'^(\w+)-?|_?.+')
def write(app: ArchPackage):
Path(app.get_disk_cache_path()).mkdir(parents=True, exist_ok=True)
with open(app.get_disk_data_path(), 'w+') as f:
f.write(json.dumps(app.get_data_to_cache()))
def fill_icon_path(app: ArchPackage, icon_paths: List[str], only_exact_match: bool):
ends_with = re.compile(r'.+/{}\.(png|svg)$'.format(app.name), re.IGNORECASE)
for path in icon_paths:
if ends_with.match(path):
app.icon_path = path
return
if not only_exact_match:
pkg_icons_path = pacman.list_icon_paths({app.name})
if pkg_icons_path:
app.icon_path = pkg_icons_path[0]
def set_icon_path(app: ArchPackage, icon_name: str = None):
installed_icons = pacman.list_icon_paths(app.name)
if installed_icons:
exact_match = re.compile(r'.+/{}\..+$'.format(icon_name.split('.')[0] if icon_name else app.name))
for icon_path in installed_icons:
if exact_match.match(icon_path):
app.icon_path = icon_path
break
def save(pkgname: str, mirror: str):
app = ArchPackage(name=pkgname, mirror=mirror)
desktop_files = pacman.list_desktop_entries({pkgname})
if desktop_files:
if len(desktop_files) > 1:
exact_match = [f for f in desktop_files if f.endswith('{}.desktop'.format(pkgname))]
to_parse = exact_match[0] if exact_match else desktop_files[0]
else:
to_parse = desktop_files[0]
app.desktop_entry = to_parse
with open(to_parse) as f:
desktop_entry = f.read()
for field in RE_DESKTOP_ENTRY.findall(desktop_entry):
if field[0] == 'Exec':
app.command = field[1].strip()
elif field[0] == 'Icon':
app.icon_path = field[1].strip()
if app.icon_path and '/' not in app.icon_path: # if the icon full path is not defined
set_icon_path(app, app.icon_path)
else:
bin_paths = pacman.list_bin_paths({pkgname})
if bin_paths:
if len(bin_paths) > 1:
exact_match = [p for p in bin_paths if p.endswith('/' + pkgname)]
app.command = exact_match[0] if exact_match else bin_paths[0]
else:
app.command = bin_paths[0]
set_icon_path(app)
Path(app.get_disk_cache_path()).mkdir(parents=True, exist_ok=True)
with open(app.get_disk_data_path(), 'w+') as f:
f.write(json.dumps(app.get_data_to_cache()))
def save_several(pkgnames: Set[str], mirror: str, overwrite: bool = True) -> int:
to_cache = {n for n in pkgnames if overwrite or not os.path.exists(ArchPackage.disk_cache_path(n, mirror))}
desktop_files = pacman.list_desktop_entries(to_cache)
no_desktop_files = {}
to_write = []
if desktop_files:
desktop_matches, no_exact_match = {}, set()
for pkg in to_cache: # first try to find exact matches
ends_with = re.compile('.+/{}.desktop$'.format(pkg), re.IGNORECASE)
for f in desktop_files:
if ends_with.match(f):
desktop_matches[pkg] = f
break
if pkg not in desktop_matches:
no_exact_match.add(pkg)
if no_exact_match: # check every not matched app individually
for pkg in no_exact_match:
entries = pacman.list_desktop_entries({pkg})
if entries:
desktop_matches[pkg] = entries[0]
if not desktop_matches:
no_desktop_files = to_cache
else:
if len(desktop_matches) != len(to_cache):
no_desktop_files = {p for p in to_cache if p not in desktop_matches}
pkgs, apps_icons_noabspath = [], []
for pkgname, file in desktop_matches.items():
p = ArchPackage(name=pkgname, mirror=mirror)
p.desktop_entry = file
with open(file) as f:
desktop_entry = f.read()
for field in RE_DESKTOP_ENTRY.findall(desktop_entry):
if field[0] == 'Exec':
p.command = field[1].strip()
elif field[0] == 'Icon':
p.icon_path = field[1].strip()
if p.icon_path and '/' not in p.icon_path: # if the icon full path is not defined
apps_icons_noabspath.append(p)
pkgs.append(p)
if apps_icons_noabspath:
icon_paths = pacman.list_icon_paths({app.name for app in apps_icons_noabspath})
if icon_paths:
for p in apps_icons_noabspath:
fill_icon_path(p, icon_paths, False)
for p in pkgs:
to_write.append(p)
if no_desktop_files:
pkgs = {ArchPackage(name=n, mirror=mirror) for n in no_desktop_files}
bin_paths = pacman.list_bin_paths(no_desktop_files)
if bin_paths:
for p in pkgs:
ends_with = re.compile(r'.+/{}$'.format(p.name), re.IGNORECASE)
for path in bin_paths:
if ends_with.match(path):
p.command = path
break
icon_paths = pacman.list_icon_paths(no_desktop_files)
if icon_paths:
for p in pkgs:
fill_icon_path(p, icon_paths, only_exact_match=True)
for p in pkgs:
to_write.append(p)
if to_write:
for p in to_write:
write(p)
return len(to_write)
return 0

29
bauh/gems/arch/git.py Normal file
View File

@@ -0,0 +1,29 @@
from datetime import datetime
from typing import List
from bauh.commons.system import new_subprocess
def is_enabled() -> bool:
try:
new_subprocess(['git', '--version'])
return True
except FileNotFoundError:
return False
def list_commits(proj_dir:str) -> List[dict]:
logs = new_subprocess(['git', 'log', '--date=iso'], cwd=proj_dir).stdout
commits, commit = [], {}
for out in new_subprocess(['grep', '-E', 'commit|Date:'], stdin=logs).stdout:
if out:
line = out.decode()
if line.startswith('commit'):
commit['commit'] = line.split(' ')[1].strip()
elif line.startswith('Date'):
commit['date'] = datetime.fromisoformat(line.split(':')[1].strip())
commits.append(commit)
commit = {}
return commits

28
bauh/gems/arch/makepkg.py Normal file
View File

@@ -0,0 +1,28 @@
import re
from typing import List
from bauh.api.abstract.handler import ProcessWatcher
from bauh.commons.system import new_subprocess
RE_DEPS_PATTERN = re.compile(r'\n?\s+->\s(.+)\n')
def check_missing_deps(pkgdir: str, watcher: ProcessWatcher) -> List[str]:
depcheck = new_subprocess(['makepkg', '--check'], cwd=pkgdir)
for o in depcheck.stdout:
if o:
watcher.print(o.decode())
error_lines = []
for s in depcheck.stderr:
if s:
line = s.decode()
print(line)
error_lines.append(line)
if error_lines:
error_str = ''.join(error_lines)
if 'Missing dependencies' in error_str:
return RE_DEPS_PATTERN.findall(error_str)

55
bauh/gems/arch/mapper.py Normal file
View File

@@ -0,0 +1,55 @@
from datetime import datetime
from bauh.api.http import HttpClient
from bauh.gems.arch.model import ArchPackage
URL_PKG_DOWNLOAD = 'https://aur.archlinux.org/{}'
class ArchDataMapper:
def __init__(self, http_client: HttpClient):
self.http_client = http_client
def fill_api_data(self, pkg: ArchPackage, package: dict, fill_version: bool = True):
version = package.get('Version')
if version:
version = version.split(':')
version = version[0] if len(version) == 1 else version[1]
pkg.id = package.get('ID')
pkg.name = package.get('Name')
if fill_version:
pkg.version = version
pkg.latest_version = version
pkg.description = package.get('Description')
pkg.package_base = package.get('PackageBase')
pkg.popularity = package.get('Popularity')
pkg.votes = package.get('NumVotes')
pkg.maintainer = package.get('Maintainer')
pkg.url_download = URL_PKG_DOWNLOAD.format(package['URLPath']) if package.get('URLPath') else None
pkg.first_submitted = datetime.fromtimestamp(package['FirstSubmitted']) if package.get('FirstSubmitted') else None
pkg.last_modified = datetime.fromtimestamp(package['LastModified']) if package.get('LastModified') else None
pkg.update = pkg.version and pkg.latest_version and pkg.latest_version > pkg.version
def fill_package_build(self, pkg: ArchPackage):
res = self.http_client.get(pkg.get_pkg_build_url())
if res and res.status_code == 200 and res.text:
pkg.pkgbuild = res.text
def map_api_data(self, apidata: dict, installed: dict) -> ArchPackage:
data = installed.get(apidata.get('Name'))
app = ArchPackage(name=apidata.get('Name'), installed=bool(data), mirror='aur')
if data:
app.version = data.get('version')
app.description = data.get('description')
self.fill_api_data(app, apidata, fill_version=not data)
return app

20
bauh/gems/arch/message.py Normal file
View File

@@ -0,0 +1,20 @@
from bauh.api.abstract.handler import ProcessWatcher
from bauh.api.abstract.view import MessageType
def show_dep_not_installed(watcher: ProcessWatcher, pkgname: str, depname: str, i18n: dict):
watcher.show_message(title=i18n['error'],
body=i18n['arch.install.dependency.install.error'].format('"{}"'.format(depname), '"{}"'.format(pkgname)),
type_=MessageType.ERROR)
def show_dep_not_found(depname: str, i18n: dict, watcher: ProcessWatcher):
watcher.show_message(title=i18n['arch.install.dep_not_found.title'],
body=i18n['arch.install.dep_not_found.body'].format('"{}"'.format(depname)),
type_=MessageType.ERROR)
def show_optdep_not_installed(depname: str, watcher: ProcessWatcher, i18n: dict):
watcher.show_message(title=i18n['error'],
body=i18n['arch.install.optdep.error'].format('"{}"'.format(depname)),
type_=MessageType.ERROR)

109
bauh/gems/arch/model.py Normal file
View File

@@ -0,0 +1,109 @@
import datetime
from bauh.api.abstract.model import SoftwarePackage
from bauh.api.constants import CACHE_PATH
from bauh.commons import resource
from bauh.gems.arch import ROOT_DIR
class ArchPackage(SoftwarePackage):
def __init__(self, name: str = None, version: str = None, latest_version: str = None, description: str = None,
package_base: str = None, votes: int = None, popularity: float = None,
first_submitted: datetime.datetime = None, last_modified: datetime.datetime = None,
maintainer: str = None, url_download: str = None, pkgbuild: str = None, mirror: str = None,
desktop_entry: str = None, installed: bool = False):
super(ArchPackage, self).__init__(name=name, version=version, latest_version=latest_version, description=description, installed=installed)
self.package_base = package_base
self.votes = votes
self.popularity = popularity
self.maintainer = maintainer
self.url_download = url_download
self.first_submitted = first_submitted
self.last_modified = last_modified
self.pkgbuild = pkgbuild
self.mirror = mirror
self.command = None
self.icon_path = None
self.downgrade_enabled = False
self.desktop_entry = desktop_entry
@staticmethod
def disk_cache_path(pkgname: str, mirror: str):
return CACHE_PATH + '/arch/installed/' + ('aur' if mirror == 'aur' else 'mirror') + '/' + pkgname
def get_pkg_build_url(self):
if self.package_base:
return 'https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=' + self.package_base
def has_history(self):
return self.installed
def has_info(self):
return True
def can_be_installed(self):
return super(ArchPackage, self).can_be_installed() and self.url_download
def can_be_downgraded(self):
return self.installed and self.downgrade_enabled
def get_type(self):
return 'aur' if self.mirror == 'aur' else 'arch'
def get_default_icon_path(self) -> str:
return self.get_type_icon_path()
def get_disk_icon_path(self) -> str:
return self.icon_path
def get_type_icon_path(self):
return resource.get_path('img/arch.png', ROOT_DIR) # TODO change icon when from mirrors
def is_application(self):
return True
def supports_disk_cache(self):
return True
def get_disk_cache_path(self) -> str:
if self.name:
return self.disk_cache_path(self.name, self.mirror)
def get_data_to_cache(self) -> dict:
cache = {}
# required attrs to cache
for a in {'command', 'icon_path', 'mirror'}:
val = getattr(self, a)
if val:
cache[a] = val
else:
return None
if self.desktop_entry:
cache['desktop_entry'] = self.desktop_entry
return cache
def fill_cached_data(self, data: dict):
for a in {'command', 'icon_path', 'mirror', 'desktop_entry'}:
val = data.get(a)
if val:
setattr(self, a, val)
if a == 'icon_path':
self.icon_url = val
def can_be_run(self) -> bool:
# only returns if there is a desktop entry set for the application to avoid running command-line applications
return bool(self.desktop_entry)
def get_command(self) -> str:
return self.command
def get_publisher(self):
return self.maintainer

131
bauh/gems/arch/pacman.py Normal file
View File

@@ -0,0 +1,131 @@
import re
from typing import List, Set
from bauh.commons.system import run_cmd, new_subprocess, new_root_subprocess, SystemProcess
def is_enabled() -> bool:
try:
new_subprocess(['pacman', '--version'])
return True
except FileNotFoundError:
return False
def get_mirrors(pkgs: Set[str]) -> dict:
pkgre = '|'.join(pkgs)
searchres = new_subprocess(['pacman', '-Ss', pkgre]).stdout
mirrors = {}
for line in new_subprocess(['grep', '-E', '.+/({}) '.format(pkgre)], stdin=searchres).stdout:
if line:
match = line.decode()
for p in pkgs:
if p in match:
mirrors[p] = match.split('/')[0]
return mirrors
def is_available_from_mirrors(pkg_name: str) -> bool:
return bool(run_cmd('pacman -Ss ' + pkg_name))
def get_info(pkg_name) -> str:
return run_cmd('pacman -Qi ' + pkg_name)
def get_info_list(pkg_name: str) -> List[tuple]:
info = get_info(pkg_name)
if info:
return re.findall(r'(\w+\s?\w+)\s+:\s+(.+(\n\s+.+)*)', info)
def get_info_dict(pkg_name: str) -> dict:
info_list = get_info_list(pkg_name)
if info_list:
info_dict = {}
for info_data in info_list:
attr = info_data[0].lower()
info_dict[attr] = info_data[1] if '\n' not in info_data[1] else ' '.join([l.strip() for l in info_data[1].split('\n')])
if info_dict[attr] == 'None':
info_dict[attr] = None
return info_dict
def list_installed() -> Set[str]:
return {out.decode().strip() for out in new_subprocess(['pacman', '-Qq']).stdout if out}
def list_and_map_installed() -> dict: # returns a dict with with package names as keys and versions as values
installed = new_subprocess(['pacman', '-Qq']).stdout # retrieving all installed package names
allinfo = new_subprocess(['pacman', '-Qi'], stdin=installed).stdout # retrieving all installed packages info
pkgs, current_pkg = {'mirrors': {}, 'not_signed': {}}, {}
for out in new_subprocess(['grep', '-E', '(Name|Description|Version|Validated By)'], stdin=allinfo).stdout: # filtering only the Name and Validated By fields:
if out:
line = out.decode()
if line.startswith('Name'):
current_pkg['name'] = line.split(':')[1].strip()
elif line.startswith('Version'):
version = line.split(':')
current_pkg['version'] = version[len(version) - 1].strip()
elif line.startswith('Description'):
current_pkg['description'] = line.split(':')[1].strip()
elif line.startswith('Validated'):
if line.split(':')[1].strip().lower() == 'none':
pkgs['not_signed'][current_pkg['name']] = {'version': current_pkg['version'], 'description': current_pkg['description']}
current_pkg = {}
return pkgs
def install_as_process(pkgpath: str, root_password: str, aur: bool, pkgdir: str = '.') -> SystemProcess:
if aur:
cmd = ['pacman', '-U', pkgpath, '--noconfirm'] # pkgpath = install file path
else:
cmd = ['pacman', '-S', pkgpath, '--noconfirm'] # pkgpath = pkgname
return SystemProcess(new_root_subprocess(cmd, root_password, cwd=pkgdir))
def list_desktop_entries(pkgnames: Set[str]) -> List[str]:
if pkgnames:
installed_files = new_subprocess(['pacman', '-Qlq', *pkgnames]).stdout
desktop_files = []
for out in new_subprocess(['grep', '-E', '.desktop'], stdin=installed_files).stdout:
if out:
desktop_files.append(out.decode().strip())
return desktop_files
def list_icon_paths(pkgnames: Set[str]) -> List[str]:
installed_files = new_subprocess(['pacman', '-Qlq', *pkgnames]).stdout
icon_files = []
for out in new_subprocess(['grep', '-E', '.(png|svg|jpeg)'], stdin=installed_files).stdout:
if out:
icon_files.append(out.decode().strip())
return icon_files
def list_bin_paths(pkgnames: Set[str]) -> List[str]:
installed_files = new_subprocess(['pacman', '-Qlq', *pkgnames]).stdout
bin_paths = []
for out in new_subprocess(['grep', '-E', '^/usr/bin/.+'], stdin=installed_files).stdout:
if out:
bin_paths.append(out.decode().strip())
return bin_paths

View File

@@ -0,0 +1,13 @@
import re
from typing import Set
RE_PKGBUILD_OPTDEPS = re.compile(r"optdepends = ([^:\s]+)")
RE_PKGBUILD_DEPSON = re.compile(r"depends = ([^:\s]+)")
def read_optdeps(srcinfo: str) -> Set[str]:
return set(RE_PKGBUILD_OPTDEPS.findall(srcinfo))
def read_depends_on(srcinfo: str) -> Set[str]:
return set(RE_PKGBUILD_DEPSON.findall(srcinfo))

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

View File

@@ -0,0 +1,49 @@
gem.arch.label=Arch ( AUR )
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.downgrade.error=Error
arch.downgrade.impossible=It is not possible to downgrade {}
aur.history.1_version=version
aur.history.2_release=release
aur.history.3_date=date
arch.downloading.package=Downloading the package
arch.uncompressing.package=Uncompressing the package
arch.checking.deps=Checking {} dependencies
arch.missing_deps_found=Missing dependencies for {}
arch.building.package=Building package {}
arch.checking.conflicts=Checking any conflicts with {}
arch.installing.package=Installing {} package
arch.uninstalling.conflict=Uninstalling conflicting package {}
arch.uninstalling.conflict.fail=It was not possible to uninstall the conflicting package {}
arch.clone=Cloning the AUR repository {}
arch.downgrade.reading_commits=Reading the repository commits
arch.downgrade.version_found=Current package version found
arch.downgrade.install_older=Installing older version
aur.info.01_id=id
aur.info.02_name=name
aur.info.03_version=version
aur.info.04_popularity=popularity
aur.info.05_votes=votes
aur.info.06_package_base=package base
aur.info.07_maintainer=maintainer
aur.info.08_first_submitted=first submitted
aur.info.09_last_modified=app.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
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.
arch.install.dependency.install=Installing package dependency {}
arch.install.dependency.install.error=Could not install dependent package {}. Installation of {} aborted.
arch.uninstall.required_by={} cannot be uninstalled because it is necessary for these following packages to work
arch.uninstall.required_by.advice=Uninstall them first before uninstalling {}.
arch.install.optdeps.request.title=Optional dependencies
arch.install.optdeps.request.body={} was succesfully installed ! There are some optional associated packages that you might want to install as well (check those you want)
arch.install.optdep.error=Could not install the optional package {}
arch.optdeps.checking=Checking {} optional dependencies
arch.warning.disabled={} seems not to be installed. It will not be possible to manage Arch / AUR packages.
arch.warning.git={} seems not to be installed. It will not be possible to downgrade AUR packages.

View File

@@ -0,0 +1,81 @@
gem.arch.label=Arch ( AUR )
aur.info.architecture=arquitectura
aur.info.architecture.any=cualquier
aur.info.build date=fecha de construcción
aur.info.depends on=depende
aur.info.description=descripción
aur.info.install date=fecha de instalación
aur.info.install reason=razón da instalación
aur.info.install reason.explicitly installed=instalado explícitamente
aur.info.install script=script de instalación
aur.info.install script.no=ninguno
aur.info.installed size=tamaño da instalación
aur.info.licenses=licencias
aur.info.name=nombre
aur.info.optional deps=dependencias opcionales
aur.info.packager=empaquetador
aur.info.packager.unknown packager=desconocido
aur.info.url=url
aur.info.version=versión
aur.info.arch=arquitectura
aur.info.arch.any=cualquier
aur.info.depends=depende
aur.info.pkgdesc=descripción
aur.info.pkgname=nombre
aur.info.pkgrel=lanzamiento
aur.info.pkgver=versión
aur.info.source=origen
aur.info.optdepends=dependencias opcionales
aur.info.license=licencia
aur.info.validpgpkeys=llaves PGP válidas
aur.info.options=opciones
aur.info.provides=provee
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.downgrade.error=Error
arch.downgrade.impossible=No es posible revertir la versión de {}
aur.history.1_version=versión
aur.history.2_release=lanzamiento
aur.history.3_date=fecha
arch.downloading.package=Descargando el paquete
arch.uncompressing.package=Descomprimindo el paquete
arch.checking.deps=Verificando las dependencias de {}
arch.missing_deps_found=Dependencias faltantes para {}
arch.building.package=Construyendo el paquete {}
arch.checking.conflicts=Verificando se hay conflictos con {}
arch.installing.package=Instalando el paquete {}
arch.uninstalling.conflict=Eliminando el paquete conflictivo {}
arch.uninstalling.conflict.fail=No fue posible desinstalar el paquete conflictivo {}
arch.clone=Clonando el repositorio {} de AUR
arch.downgrade.reading_commits=Leyendo los commits del repositorio
arch.downgrade.version_found=Version actual del paquete encontrada
arch.downgrade.install_older=Instalando versión anterior
aur.info.01_id=id
aur.info.02_name=nombre
aur.info.03_version=versión
aur.info.04_popularity=popularidad
aur.info.05_votes=votos
aur.info.06_package_base=paquete base
aur.info.07_maintainer=mantenedor
aur.info.08_first_submitted=primero envio
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
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.
arch.install.dependency.install=Instalando el paquete dependiente {}
arch.install.dependency.install.error=No se pudo instalar el paquete dependiente {}. Instalación de {} abortada.
arch.uninstall.required_by=No se puede desinstalar {} porque es necesario para que los siguientes paquetes funcionen
arch.uninstall.required_by.advice=Debe desinstalarlos primero antes de desinstalar {}
arch.install.optdeps.request.title=Dependencias opcionales
arch.install.optdeps.request.body=¡{} se instaló correctamente! También hay algunos paquetes opcionales asociados que es posible que desee instalar (marque los que desee)
arch.install.optdep.error=No se pudo instalar el paquete opcional {}
arch.optdeps.checking=Verificando las dependencias opcionales de {}
arch.warning.disabled={} parece no estar instalado. No será posible administrar paquetes Arch / AUR.
arch.warning.git={} parece no estar instalado. No será posible revertir las versiones de paquetes Arch / AUR.

View File

@@ -0,0 +1,81 @@
gem.arch.label=Arch ( AUR )
aur.info.architecture=arquitetura
aur.info.architecture.any=qualquer
aur.info.build date=data de construção
aur.info.depends on=depende
aur.info.description=descrição
aur.info.install date=data de instalação
aur.info.install reason=razão da instalação
aur.info.install reason.explicitly installed=instalado explicitamente
aur.info.install script=script de instalação
aur.info.install script.no=nenhum
aur.info.installed size=tamanho da instalação
aur.info.licenses=licenças
aur.info.name=nome
aur.info.optional deps=dependências opcionais
aur.info.packager=empacotador
aur.info.packager.unknown packager=desconhecido
aur.info.url=url
aur.info.version=versão
aur.info.arch=arquitetura
aur.info.arch.any=qualquer
aur.info.depends=depende
aur.info.pkgdesc=descrição
aur.info.pkgname=nome
aur.info.pkgrel=lançamento
aur.info.pkgver=versão
aur.info.source=origem
aur.info.optdepends=dependências opcionais
aur.info.license=licença
aur.info.validpgpkeys=chaves PGP válidas
aur.info.options=opções
aur.info.provides=provê
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.downgrade.error=Erro
arch.downgrade.impossible=Não é possível reverter a versão de {}
aur.history.1_version=versão
aur.history.2_release=lançamento
aur.history.3_date=data
arch.downloading.package=Baixando o pacote
arch.uncompressing.package=Descompactando o pacote
arch.checking.deps=Checando as dependências de {}
arch.missing_deps_found=Dependencias ausentes para {}
arch.building.package=Construindo o pacote {}
arch.checking.conflicts=Verificando se há conflitos com {}
arch.installing.package=Instalando o pacote {}
arch.uninstalling.conflict=Desinstalando o pacote conflitante {}
arch.uninstalling.conflict.fail=Não foi possível desinstalar o pacote conflitante {}
arch.clone=Clonando o repositório {} do AUR
arch.downgrade.reading_commits=Lendo os commits do repositório
arch.downgrade.version_found=Versão atual do pacote encontrada
arch.downgrade.install_older=Instalando versão anterior
aur.info.01_id=id
aur.info.02_name=nome
aur.info.03_version=versão
aur.info.04_popularity=popularidade
aur.info.05_votes=votos
aur.info.06_package_base=pacote base
aur.info.07_maintainer=mantenedor
aur.info.08_first_submitted=primeira submissão
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
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.
arch.install.dependency.install=Instalando o pacote dependente {}
arch.install.dependency.install.error=Não foi possível instalar o pacote dependente {}. Instalação de {} abortada.
arch.uninstall.required_by={} não pode ser desinstalado porque ele é necessário para o funcionamento dos seguintes pacotes
arch.uninstall.required_by.advice=Desinstale eles primeiro antes de desinstalar {}
arch.install.optdeps.request.title=Dependências opcionais
arch.install.optdeps.request.body={} foi instalado com sucesso ! Existem alguns pacotes opcionais associados que talvez você também queira instalar (marque os desejados)
arch.install.optdep.error=Não foi possível instalar o pacote opcional {}
arch.optdeps.checking=Verificando as dependências opcionais de {}
arch.warning.disabled={} parece não estar instalado. Não será possível gerenciar pacotes Arch / AUR.
arch.warning.git={} parece não estar instalado. Não será possível reverter versões de pacotes Arch / AUR.

View File

@@ -0,0 +1,3 @@
from bauh.api.abstract.model import SuggestionPriority
ALL = {'google-chrome': SuggestionPriority.HIGH}

53
bauh/gems/arch/worker.py Normal file
View File

@@ -0,0 +1,53 @@
import logging
import os
import time
from multiprocessing import Process
from threading import Thread
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager
from bauh.gems.arch import pacman, disk
URL_INDEX = 'https://aur.archlinux.org/packages.gz'
URL_INFO = 'https://aur.archlinux.org/rpc/?v=5&type=info&arg={}'
class AURIndexUpdater(Thread):
def __init__(self, context: ApplicationContext, man: SoftwareManager):
super(AURIndexUpdater, self).__init__(daemon=True)
self.http_client = context.http_client
self.logger = context.logger
self.man = man
def run(self):
while True:
self.logger.info('Pre-indexing AUR packages in memory')
res = self.http_client.get(URL_INDEX)
if res and res.text:
self.man.names_index = {n.replace('-', '').replace('_', '').replace('.', ''): n for n in res.text.split('\n') if n and not n.startswith('#')}
self.logger.info('Pre-indexed {} AUR package names in memory'.format(len(self.man.names_index)))
else:
self.logger.warning('No data returned from: {}'.format(URL_INDEX))
time.sleep(5 * 60) # updates every 5 minutes
class ArchDiskCacheUpdater(Thread if bool(os.getenv('BAUH_DEBUG', 0)) else Process):
def __init__(self, logger: logging.Logger):
super(ArchDiskCacheUpdater, self).__init__(daemon=True)
self.logger = logger
def run(self):
self.logger.info('Pre-caching installed AUR packages data to disk')
installed = pacman.list_and_map_installed()
saved = 0
if installed and installed['not_signed']:
saved = disk.save_several({app for app in installed['not_signed']}, 'aur', overwrite=False)
self.logger.info('Pre-cached data of {} AUR packages to disk'.format(saved))