mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 02:24:16 +02:00
gem selector sketch
This commit is contained in:
4
bauh/gems/arch/__init__.py
Normal file
4
bauh/gems/arch/__init__.py
Normal 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
28
bauh/gems/arch/aur.py
Normal 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)])
|
||||
28
bauh/gems/arch/confirmation.py
Normal file
28
bauh/gems/arch/confirmation.py
Normal 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)
|
||||
570
bauh/gems/arch/controller.py
Normal file
570
bauh/gems/arch/controller.py
Normal 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
179
bauh/gems/arch/disk.py
Normal 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
29
bauh/gems/arch/git.py
Normal 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
28
bauh/gems/arch/makepkg.py
Normal 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
55
bauh/gems/arch/mapper.py
Normal 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
20
bauh/gems/arch/message.py
Normal 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
109
bauh/gems/arch/model.py
Normal 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
131
bauh/gems/arch/pacman.py
Normal 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
|
||||
13
bauh/gems/arch/pkgbuild.py
Normal file
13
bauh/gems/arch/pkgbuild.py
Normal 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))
|
||||
BIN
bauh/gems/arch/resources/img/arch.png
Executable file
BIN
bauh/gems/arch/resources/img/arch.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 905 B |
49
bauh/gems/arch/resources/locale/en
Normal file
49
bauh/gems/arch/resources/locale/en
Normal 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.
|
||||
81
bauh/gems/arch/resources/locale/es
Normal file
81
bauh/gems/arch/resources/locale/es
Normal 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.
|
||||
81
bauh/gems/arch/resources/locale/pt
Normal file
81
bauh/gems/arch/resources/locale/pt
Normal 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.
|
||||
3
bauh/gems/arch/suggestions.py
Normal file
3
bauh/gems/arch/suggestions.py
Normal 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
53
bauh/gems/arch/worker.py
Normal 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))
|
||||
Reference in New Issue
Block a user