mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-06 21:44:16 +02:00
[arch] feature: allowing AUR packages to be installed when bauh is launched by the root user
This commit is contained in:
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [0.9.23]
|
||||
### Features
|
||||
- Arch
|
||||
- allowing AUR packages to be installed when bauh is launched by the root user [#196](https://github.com/vinifmor/bauh/issues/196)
|
||||
- it creates a non-root user called **bauh-aur** for building the packages (`useradd` and `runuser` commands must be installed)
|
||||
|
||||
|
||||
## [0.9.22] 2021-11-30
|
||||
### Improvements
|
||||
- General
|
||||
|
||||
@@ -240,6 +240,9 @@ suggestions:
|
||||
|
||||
- If you have AUR added as a repository on you pacman configuration, make sure to disable bauh's support (through the settings described below)
|
||||
- AUR package compilation may require additional installed packages to work properly. Some of them are defined on the field `optdepends` of the [PKGBUILD](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=bauh)
|
||||
- for a **root** user the following additional applications must be installed:
|
||||
- `useradd`: required to create a simple user named **bauh-aur** (since **makepkg** does not allow building packages as the **root** user)
|
||||
- `runuser`: required to run commands as another user
|
||||
- **Repository packages currently do not support the following actions: Downgrade and History**
|
||||
- If some of your installed packages are not categorized, open a PullRequest to the **bauh-files** repository changing [categories.txt](https://github.com/vinifmor/bauh-files/blob/master/arch/categories.txt)
|
||||
- During bauh initialization a full AUR normalized index is saved at `~/.cache/bauh/arch/aur/index.txt`
|
||||
|
||||
@@ -4,11 +4,16 @@ from pathlib import Path
|
||||
from bauh import __app_name__
|
||||
from bauh.api import user
|
||||
|
||||
|
||||
def get_temp_dir(username: str) -> str:
|
||||
return f'/tmp/{__app_name__}@{username}'
|
||||
|
||||
|
||||
CACHE_DIR = f'/var/cache/{__app_name__}' if user.is_root() else f'{Path.home()}/.cache/{__app_name__}'
|
||||
CONFIG_DIR = f'/etc/{__app_name__}' if user.is_root() else f'{Path.home()}/.config/{__app_name__}'
|
||||
USER_THEMES_DIR = f'/usr/share/{__app_name__}/themes' if user.is_root() else f'{Path.home()}/.local/share/{__app_name__}/themes'
|
||||
DESKTOP_ENTRIES_DIR = '/usr/share/applications' if user.is_root() else f'{Path.home()}/.local/share/applications'
|
||||
TEMP_DIR = f'/tmp/{__app_name__}@{getuser()}'
|
||||
TEMP_DIR = get_temp_dir(getuser())
|
||||
LOGS_DIR = f'{TEMP_DIR}/logs'
|
||||
AUTOSTART_DIR = f'/etc/xdg/autostart' if user.is_root() else f'{Path.home()}/.config/autostart'
|
||||
BINARIES_DIR = f'/usr/local/bin' if user.is_root() else f'{Path.home()}/.local/bin'
|
||||
|
||||
@@ -67,12 +67,15 @@ class SimpleProcess:
|
||||
def __init__(self, cmd: List[str], cwd: str = '.', expected_code: int = 0,
|
||||
global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: str = DEFAULT_LANG, root_password: str = None,
|
||||
extra_paths: Set[str] = None, error_phrases: Set[str] = None, wrong_error_phrases: Set[str] = None,
|
||||
shell: bool = False,
|
||||
success_phrases: Set[str] = None, extra_env: Optional[Dict[str, str]] = None):
|
||||
shell: bool = False, success_phrases: Set[str] = None, extra_env: Optional[Dict[str, str]] = None,
|
||||
custom_user: Optional[str] = None):
|
||||
pwdin, final_cmd = None, []
|
||||
|
||||
self.shell = shell
|
||||
if root_password is not None:
|
||||
|
||||
if custom_user:
|
||||
final_cmd.extend(['runuser', '-u', custom_user, '--'])
|
||||
elif root_password is not None:
|
||||
final_cmd.extend(['sudo', '-S'])
|
||||
pwdin = self._new(['echo', root_password], cwd, global_interpreter, lang).stdout
|
||||
|
||||
@@ -219,14 +222,10 @@ class ProcessHandler:
|
||||
|
||||
|
||||
def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False, print_error: bool = True,
|
||||
cwd: str = '.', global_interpreter: bool = USE_GLOBAL_INTERPRETER, extra_paths: Set[str] = None) -> str:
|
||||
cwd: str = '.', global_interpreter: bool = USE_GLOBAL_INTERPRETER, extra_paths: Set[str] = None,
|
||||
custom_user: Optional[str] = None) -> str:
|
||||
"""
|
||||
runs a given command and returns its default output
|
||||
:param cmd:
|
||||
:param expected_code:
|
||||
:param ignore_return_code:
|
||||
:param print_error:
|
||||
:param global_interpreter
|
||||
:return:
|
||||
"""
|
||||
args = {
|
||||
@@ -239,13 +238,14 @@ def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False,
|
||||
if not print_error:
|
||||
args["stderr"] = subprocess.DEVNULL
|
||||
|
||||
res = subprocess.run(cmd, **args)
|
||||
final_cmd = f"runuser -u {custom_user} -- {cmd}" if custom_user else cmd
|
||||
res = subprocess.run(final_cmd, **args)
|
||||
return res.stdout.decode() if ignore_return_code or res.returncode == expected_code else None
|
||||
|
||||
|
||||
def new_subprocess(cmd: List[str], cwd: str = '.', shell: bool = False, stdin = None,
|
||||
global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: str = DEFAULT_LANG,
|
||||
extra_paths: Set[str] = None) -> subprocess.Popen:
|
||||
extra_paths: Set[str] = None, custom_user: Optional[str] = None) -> subprocess.Popen:
|
||||
args = {
|
||||
"stdout": PIPE,
|
||||
"stderr": PIPE,
|
||||
@@ -255,7 +255,8 @@ def new_subprocess(cmd: List[str], cwd: str = '.', shell: bool = False, stdin =
|
||||
"stdin": stdin if stdin else subprocess.DEVNULL
|
||||
}
|
||||
|
||||
return subprocess.Popen(cmd, **args)
|
||||
final_cmd = ['runuser', '-u', custom_user, '--', *cmd] if custom_user else cmd
|
||||
return subprocess.Popen(final_cmd, **args)
|
||||
|
||||
|
||||
def new_root_subprocess(cmd: List[str], root_password: str, cwd: str = '.',
|
||||
@@ -302,8 +303,9 @@ def get_human_size_str(size) -> str:
|
||||
return str(int_size)
|
||||
|
||||
|
||||
def run(cmd: List[str], success_code: int = 0) -> Tuple[bool, str]:
|
||||
p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL)
|
||||
def run(cmd: List[str], success_code: int = 0, custom_user: Optional[str] = None) -> Tuple[bool, str]:
|
||||
final_cmd = ['runuser', '-u', custom_user, '--', *cmd] if custom_user else cmd
|
||||
p = subprocess.run(final_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL)
|
||||
return p.returncode == success_code, p.stdout.decode()
|
||||
|
||||
|
||||
@@ -327,9 +329,13 @@ def check_enabled_services(*names: str) -> Dict[str, bool]:
|
||||
return {s: status[i].strip().lower() == 'enabled' for i, s in enumerate(names) if s}
|
||||
|
||||
|
||||
def execute(cmd: str, shell: bool = False, cwd: Optional[str] = None, output: bool = True, custom_env: Optional[dict] = None, stdin: bool = True) -> Tuple[int, Optional[str]]:
|
||||
def execute(cmd: str, shell: bool = False, cwd: Optional[str] = None, output: bool = True, custom_env: Optional[dict] = None,
|
||||
stdin: bool = True, custom_user: Optional[str] = None) -> Tuple[int, Optional[str]]:
|
||||
|
||||
final_cmd = f"runuser -u {custom_user} -- {cmd}" if custom_user else cmd
|
||||
|
||||
params = {
|
||||
'args': cmd.split(' ') if not shell else [cmd],
|
||||
'args': final_cmd.split(' ') if not shell else [final_cmd],
|
||||
'stdout': subprocess.PIPE if output else subprocess.DEVNULL,
|
||||
'stderr': subprocess.STDOUT if output else subprocess.DEVNULL,
|
||||
'shell': shell
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from bauh import __app_name__
|
||||
from bauh.api.paths import CONFIG_DIR, TEMP_DIR, CACHE_DIR
|
||||
from bauh.api.paths import CONFIG_DIR, TEMP_DIR, CACHE_DIR, get_temp_dir
|
||||
from bauh.commons import resource
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
BUILD_DIR = f'{TEMP_DIR}/arch'
|
||||
ARCH_CACHE_DIR = f'{CACHE_DIR}/arch'
|
||||
CATEGORIES_FILE_PATH = f'{ARCH_CACHE_DIR}/categories.txt'
|
||||
URL_CATEGORIES_FILE = f'https://raw.githubusercontent.com/vinifmor/{__app_name__}-files/master/arch/categories.txt'
|
||||
@@ -21,6 +21,10 @@ EDITABLE_PKGBUILDS_FILE = f'{ARCH_CONFIG_DIR}/aur/editable_pkgbuilds.txt'
|
||||
IGNORED_REBUILD_CHECK_FILE = f'{ARCH_CONFIG_DIR}/aur/ignored_rebuild_check.txt'
|
||||
|
||||
|
||||
def get_pkgbuild_dir(user: Optional[str] = None) -> str:
|
||||
return f'{get_temp_dir(user) if user else TEMP_DIR}/arch'
|
||||
|
||||
|
||||
def get_icon_path() -> str:
|
||||
return resource.get_path('img/arch.svg', ROOT_DIR)
|
||||
|
||||
|
||||
@@ -42,10 +42,6 @@ KNOWN_LIST_FIELDS = ('validpgpkeys',
|
||||
'conflicts')
|
||||
|
||||
|
||||
def map_pkgbuild(pkgbuild: str) -> dict:
|
||||
return {attr: val.replace('"', '').replace("'", '').replace('(', '').replace(')', '') for attr, val in re.findall(r'\n(\w+)=(.+)', pkgbuild)}
|
||||
|
||||
|
||||
def map_srcinfo(string: str, pkgname: Optional[str], fields: Set[str] = None) -> dict:
|
||||
subinfos, subinfo = [], {}
|
||||
|
||||
@@ -260,3 +256,4 @@ class AURClient:
|
||||
|
||||
def is_supported(arch_config: dict) -> bool:
|
||||
return arch_config['aur'] and git.is_installed()
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from bauh.commons.config import YAMLConfigManager
|
||||
from bauh.gems.arch import CONFIG_FILE, BUILD_DIR
|
||||
from bauh.gems.arch import CONFIG_FILE, get_pkgbuild_dir
|
||||
|
||||
|
||||
def get_build_dir(arch_config: dict) -> str:
|
||||
def get_build_dir(arch_config: dict, user: Optional[str]) -> str:
|
||||
build_dir = arch_config.get('aur_build_dir')
|
||||
|
||||
if not build_dir:
|
||||
build_dir = BUILD_DIR
|
||||
build_dir = get_pkgbuild_dir(user)
|
||||
|
||||
Path(build_dir).mkdir(parents=True, exist_ok=True)
|
||||
return build_dir
|
||||
|
||||
|
||||
|
||||
@@ -10,12 +10,14 @@ import traceback
|
||||
from datetime import datetime
|
||||
from math import floor
|
||||
from pathlib import Path
|
||||
from pwd import getpwnam
|
||||
from threading import Thread
|
||||
from typing import List, Set, Type, Tuple, Dict, Iterable, Optional, Collection
|
||||
|
||||
import requests
|
||||
from dateutil.parser import parse as parse_date
|
||||
|
||||
from bauh import __app_name__
|
||||
from bauh.api.abstract.controller import SearchResult, SoftwareManager, ApplicationContext, UpgradeRequirements, \
|
||||
TransactionResult, SoftwareAction
|
||||
from bauh.api.abstract.disk import DiskCacheLoader
|
||||
@@ -25,20 +27,19 @@ from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePacka
|
||||
from bauh.api.abstract.view import MessageType, FormComponent, InputOption, SingleSelectComponent, SelectViewType, \
|
||||
ViewComponent, PanelComponent, MultipleSelectComponent, TextInputComponent, TextInputType, \
|
||||
FileChooserComponent, TextComponent
|
||||
from bauh.api.paths import TEMP_DIR
|
||||
from bauh.api.exception import NoInternetException
|
||||
from bauh.api.paths import TEMP_DIR
|
||||
from bauh.commons import system
|
||||
from bauh.api import user
|
||||
from bauh.commons.boot import CreateConfigFile
|
||||
from bauh.commons.category import CategoriesDownloader
|
||||
from bauh.commons.html import bold
|
||||
from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, run_cmd, SimpleProcess
|
||||
from bauh.commons.util import datetime_as_milis
|
||||
from bauh.commons.view_utils import new_select
|
||||
from bauh.gems.arch import aur, pacman, makepkg, message, confirmation, disk, git, \
|
||||
from bauh.gems.arch import aur, pacman, message, confirmation, disk, git, \
|
||||
gpg, URL_CATEGORIES_FILE, CATEGORIES_FILE_PATH, CUSTOM_MAKEPKG_FILE, SUGGESTIONS_FILE, \
|
||||
get_icon_path, database, mirrors, sorting, cpu_manager, UPDATES_IGNORED_FILE, \
|
||||
ARCH_CONFIG_DIR, EDITABLE_PKGBUILDS_FILE, URL_GPG_SERVERS, BUILD_DIR, rebuild_detector
|
||||
ARCH_CONFIG_DIR, EDITABLE_PKGBUILDS_FILE, URL_GPG_SERVERS, rebuild_detector, makepkg, sshell
|
||||
from bauh.gems.arch.aur import AURClient
|
||||
from bauh.gems.arch.config import get_build_dir, ArchConfigManager
|
||||
from bauh.gems.arch.dependencies import DependenciesAnalyser
|
||||
@@ -48,6 +49,7 @@ from bauh.gems.arch.mapper import AURDataMapper
|
||||
from bauh.gems.arch.model import ArchPackage
|
||||
from bauh.gems.arch.output import TransactionStatusHandler
|
||||
from bauh.gems.arch.pacman import RE_DEP_OPERATORS
|
||||
from bauh.gems.arch.proc_util import write_as_user
|
||||
from bauh.gems.arch.updates import UpdatesSummarizer
|
||||
from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater, ArchCompilationOptimizer, RefreshMirrors, \
|
||||
SyncDatabases
|
||||
@@ -247,6 +249,7 @@ class ArchManager(SoftwareManager):
|
||||
self.index_aur = None
|
||||
self.re_file_conflict = re.compile(r'[\w\d\-_.]+:')
|
||||
self.disk_cache_updater = disk_cache_updater
|
||||
self.pkgbuilder_user: Optional[str] = f'{__app_name__}-aur' if context.root_user else None
|
||||
|
||||
@staticmethod
|
||||
def get_aur_semantic_search_map() -> Dict[str, str]:
|
||||
@@ -673,32 +676,39 @@ class ArchManager(SoftwareManager):
|
||||
|
||||
return SearchResult(pkgs, None, len(pkgs))
|
||||
|
||||
def _downgrade_aur_pkg(self, context: TransactionContext):
|
||||
def _downgrade_aur_pkg(self, context: TransactionContext) -> bool:
|
||||
if not self.add_package_builder_user(context.handler):
|
||||
return False
|
||||
|
||||
if context.commit:
|
||||
self.logger.info("Package '{}' current commit {}".format(context.name, context.commit))
|
||||
else:
|
||||
self.logger.warning("Package '{}' has no commit associated with it. Downgrading will only compare versions.".format(context.name))
|
||||
|
||||
context.build_dir = '{}/build_{}'.format(get_build_dir(context.config), int(time.time()))
|
||||
context.build_dir = f'{get_build_dir(context.config, self.pkgbuilder_user)}/build_{int(time.time())}'
|
||||
|
||||
try:
|
||||
if not os.path.exists(context.build_dir):
|
||||
build_dir = context.handler.handle(SystemProcess(new_subprocess(['mkdir', '-p', context.build_dir])))
|
||||
build_dir, build_dir_error = sshell.mkdir(dir_path=context.build_dir, custom_user=self.pkgbuilder_user)
|
||||
|
||||
if build_dir:
|
||||
if not build_dir:
|
||||
context.watcher.print(build_dir_error)
|
||||
else:
|
||||
context.handler.watcher.change_progress(10)
|
||||
base_name = context.get_base_name()
|
||||
context.watcher.change_substatus(self.i18n['arch.clone'].format(bold(context.name)))
|
||||
cloned, _ = context.handler.handle_simple(git.clone_as_process(url=URL_GIT.format(base_name), cwd=context.build_dir))
|
||||
clone_dir = f'{context.build_dir}/{base_name}'
|
||||
cloned, _ = context.handler.handle_simple(git.clone(url=URL_GIT.format(base_name),
|
||||
target_dir=clone_dir,
|
||||
custom_user=self.pkgbuilder_user))
|
||||
context.watcher.change_progress(30)
|
||||
|
||||
if cloned:
|
||||
context.watcher.change_substatus(self.i18n['arch.downgrade.reading_commits'])
|
||||
clone_path = '{}/{}'.format(context.build_dir, base_name)
|
||||
context.project_dir = clone_path
|
||||
srcinfo_path = '{}/.SRCINFO'.format(clone_path)
|
||||
context.project_dir = clone_dir
|
||||
srcinfo_path = f'{clone_dir}/.SRCINFO'
|
||||
|
||||
logs = git.log_shas_and_timestamps(clone_path)
|
||||
logs = git.log_shas_and_timestamps(clone_dir)
|
||||
context.watcher.change_progress(40)
|
||||
|
||||
if not logs or len(logs) == 1:
|
||||
@@ -719,7 +729,7 @@ class ArchManager(SoftwareManager):
|
||||
self.logger.warning("Could not find '{}' target commit to revert to".format(context.name))
|
||||
else:
|
||||
context.watcher.change_substatus(self.i18n['arch.downgrade.version_found'])
|
||||
checkout_proc = new_subprocess(['git', 'checkout', target_commit], cwd=clone_path)
|
||||
checkout_proc = new_subprocess(['git', 'checkout', target_commit], cwd=clone_dir, custom_user=self.pkgbuilder_user)
|
||||
if not context.handler.handle(SystemProcess(checkout_proc, check_error_output=False)):
|
||||
context.watcher.print("Could not rollback to current version's commit")
|
||||
return False
|
||||
@@ -738,7 +748,7 @@ class ArchManager(SoftwareManager):
|
||||
with open(srcinfo_path) as f:
|
||||
pkgsrc = aur.map_srcinfo(string=f.read(), pkgname=context.name, fields=srcfields)
|
||||
|
||||
reset_proc = new_subprocess(['git', 'reset', '--hard', commit], cwd=clone_path)
|
||||
reset_proc = new_subprocess(['git', 'reset', '--hard', commit], cwd=clone_dir, custom_user=self.pkgbuilder_user)
|
||||
if not context.handler.handle(SystemProcess(reset_proc, check_error_output=False)):
|
||||
context.handler.watcher.print('Could not downgrade anymore. Aborting...')
|
||||
return False
|
||||
@@ -752,18 +762,18 @@ class ArchManager(SoftwareManager):
|
||||
|
||||
if commit_found:
|
||||
context.watcher.change_substatus(self.i18n['arch.downgrade.version_found'])
|
||||
checkout_proc = new_subprocess(['git', 'checkout', commit_found], cwd=clone_path)
|
||||
checkout_proc = new_subprocess(['git', 'checkout', commit_found], cwd=clone_dir, custom_user=self.pkgbuilder_user)
|
||||
if not context.handler.handle(SystemProcess(checkout_proc, check_error_output=False)):
|
||||
context.watcher.print("Could not rollback to current version's commit")
|
||||
return False
|
||||
|
||||
reset_proc = new_subprocess(['git', 'reset', '--hard', commit_found], cwd=clone_path)
|
||||
reset_proc = new_subprocess(['git', 'reset', '--hard', commit_found], cwd=clone_dir, custom_user=self.pkgbuilder_user)
|
||||
if not context.handler.handle(SystemProcess(reset_proc, check_error_output=False)):
|
||||
context.watcher.print("Could not downgrade to previous commit of '{}'. Aborting...".format(commit_found))
|
||||
return False
|
||||
|
||||
break
|
||||
elif current_version == context.get_version(): # current version found:
|
||||
elif current_version == context.get_version():
|
||||
commit_found, commit_date = commit, date
|
||||
|
||||
context.watcher.change_substatus(self.i18n['arch.downgrade.install_older'])
|
||||
@@ -825,10 +835,11 @@ class ArchManager(SoftwareManager):
|
||||
return self._install(context)
|
||||
|
||||
def downgrade(self, pkg: ArchPackage, root_password: str, watcher: ProcessWatcher) -> bool:
|
||||
self.aur_client.clean_caches()
|
||||
if not self._check_action_allowed(pkg, watcher):
|
||||
if not self.check_action_allowed(pkg, watcher):
|
||||
return False
|
||||
|
||||
self.aur_client.clean_caches()
|
||||
|
||||
handler = ProcessHandler(watcher)
|
||||
|
||||
if self._is_database_locked(handler, root_password):
|
||||
@@ -857,14 +868,6 @@ class ArchManager(SoftwareManager):
|
||||
if os.path.exists(pkg.get_disk_cache_path()):
|
||||
shutil.rmtree(pkg.get_disk_cache_path())
|
||||
|
||||
def _check_action_allowed(self, pkg: ArchPackage, watcher: ProcessWatcher) -> bool:
|
||||
if user.is_root() and pkg.repository == 'aur':
|
||||
watcher.show_message(title=self.i18n['arch.install.aur.root_error.title'],
|
||||
body=self.i18n['arch.install.aur.root_error.body'],
|
||||
type_=MessageType.ERROR)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_database_locked(self, handler: ProcessHandler, root_password: str) -> bool:
|
||||
if os.path.exists('/var/lib/pacman/db.lck'):
|
||||
handler.watcher.print('pacman database is locked')
|
||||
@@ -1155,16 +1158,13 @@ class ArchManager(SoftwareManager):
|
||||
|
||||
pkg_sizes[req.pkg.name] = req.required_size
|
||||
|
||||
if aur_pkgs and not self._check_action_allowed(aur_pkgs[0], watcher):
|
||||
return False
|
||||
|
||||
arch_config = self.configman.get_config()
|
||||
aur_supported = bool(aur_pkgs) or aur.is_supported(arch_config)
|
||||
|
||||
self._sync_databases(arch_config=arch_config, aur_supported=aur_supported,
|
||||
root_password=root_password, handler=handler)
|
||||
|
||||
if repo_pkgs:
|
||||
if repo_pkgs and self.check_action_allowed(repo_pkgs[0], watcher):
|
||||
if not self._upgrade_repo_pkgs(to_upgrade=[p.name for p in repo_pkgs],
|
||||
to_remove={r.pkg.name for r in requirements.to_remove} if requirements.to_remove else None,
|
||||
handler=handler,
|
||||
@@ -1177,7 +1177,7 @@ class ArchManager(SoftwareManager):
|
||||
elif requirements.to_remove and not self._remove_transaction_packages({r.pkg.name for r in requirements.to_remove}, handler, root_password):
|
||||
return False
|
||||
|
||||
if aur_pkgs:
|
||||
if aur_pkgs and self.check_action_allowed(aur_pkgs[0], watcher) and self.add_package_builder_user(handler):
|
||||
watcher.change_status('{}...'.format(self.i18n['arch.upgrade.upgrade_aur_pkgs']))
|
||||
|
||||
self.logger.info("Retrieving the 'last_modified' field for each package to upgrade")
|
||||
@@ -1581,21 +1581,21 @@ class ArchManager(SoftwareManager):
|
||||
self.logger.warning("Package '{}' has no commit associated with it. Current history status may not be correct.".format(pkg.name))
|
||||
|
||||
arch_config = self.configman.get_config()
|
||||
temp_dir = '{}/build_{}'.format(get_build_dir(arch_config), int(time.time()))
|
||||
temp_dir = f'{get_build_dir(arch_config, self.pkgbuilder_user)}/build_{int(time.time())}'
|
||||
|
||||
try:
|
||||
Path(temp_dir).mkdir(parents=True)
|
||||
base_name = pkg.get_base_name()
|
||||
run_cmd('git clone ' + URL_GIT.format(base_name), print_error=False, cwd=temp_dir)
|
||||
|
||||
clone_path = '{}/{}'.format(temp_dir, base_name)
|
||||
clone_dir = f'{temp_dir}/{base_name}'
|
||||
|
||||
srcinfo_path = '{}/.SRCINFO'.format(clone_path)
|
||||
srcinfo_path = f'{clone_dir}/.SRCINFO'
|
||||
|
||||
if not os.path.exists(srcinfo_path):
|
||||
return PackageHistory.empyt(pkg)
|
||||
|
||||
logs = git.log_shas_and_timestamps(clone_path)
|
||||
logs = git.log_shas_and_timestamps(clone_dir)
|
||||
|
||||
if logs:
|
||||
srcfields = {'epoch', 'pkgver', 'pkgrel'}
|
||||
@@ -1622,7 +1622,7 @@ class ArchManager(SoftwareManager):
|
||||
'3_date': datetime.fromtimestamp(timestamp)}) # the number prefix is to ensure the rendering order
|
||||
|
||||
if idx + 1 < len(logs):
|
||||
if not run_cmd('git reset --hard ' + logs[idx + 1][0], cwd=clone_path):
|
||||
if not run_cmd('git reset --hard ' + logs[idx + 1][0], cwd=clone_dir):
|
||||
break
|
||||
|
||||
return PackageHistory(pkg=pkg, history=history, pkg_status_idx=status_idx)
|
||||
@@ -1738,7 +1738,7 @@ class ArchManager(SoftwareManager):
|
||||
context.restabilish_progress()
|
||||
return res
|
||||
|
||||
def _install_deps(self, context: TransactionContext, deps: List[Tuple[str, str]]) -> Iterable[str]:
|
||||
def _install_deps(self, context: TransactionContext, deps: List[Tuple[str, str]]) -> Optional[Iterable[str]]:
|
||||
progress_increment = int(100 / len(deps))
|
||||
progress = 0
|
||||
self._update_progress(context, 1)
|
||||
@@ -1785,7 +1785,7 @@ class ArchManager(SoftwareManager):
|
||||
pkg_sizes = pacman.map_download_sizes(repo_dep_names)
|
||||
downloaded = self._download_packages(repo_dep_names, context.handler, context.root_password, pkg_sizes, multithreaded=True)
|
||||
except ArchDownloadException:
|
||||
return False
|
||||
return None
|
||||
|
||||
status_handler = TransactionStatusHandler(watcher=context.watcher, i18n=self.i18n, names={*repo_dep_names},
|
||||
logger=self.logger, percentage=len(repo_deps) > 1, downloading=downloaded)
|
||||
@@ -1827,7 +1827,8 @@ class ArchManager(SoftwareManager):
|
||||
return pkg_repos
|
||||
|
||||
def _pre_download_source(self, pkgname: str, project_dir: str, watcher: ProcessWatcher) -> bool:
|
||||
if self.context.file_downloader.is_multithreaded():
|
||||
# TODO: multi-threaded download client cannot be run as another user at the moment
|
||||
if not self.context.root_user and self.context.file_downloader.is_multithreaded():
|
||||
with open('{}/.SRCINFO'.format(project_dir)) as f:
|
||||
srcinfo = aur.map_srcinfo(string=f.read(), pkgname=pkgname)
|
||||
|
||||
@@ -1839,7 +1840,7 @@ class ArchManager(SoftwareManager):
|
||||
continue
|
||||
else:
|
||||
for f in srcinfo[attr]:
|
||||
if RE_PRE_DOWNLOAD_WL_PROTOCOLS.match(f) and not RE_PRE_DOWNLOAD_BL_EXT.match(f):
|
||||
if RE_PRE_DOWNLOAD_WL_PROTOCOLS.match(f) and not RE_PRE_DOWNLOAD_BL_EXT.match(f):
|
||||
pre_download_files.append(f)
|
||||
|
||||
if pre_download_files:
|
||||
@@ -1872,10 +1873,11 @@ class ArchManager(SoftwareManager):
|
||||
deny_button=False)
|
||||
|
||||
if pkgbuild_input.get_value() != pkgbuild:
|
||||
with open(pkgbuild_path, 'w+') as f:
|
||||
f.write(pkgbuild_input.get_value())
|
||||
if not write_as_user(content=pkgbuild_input.get_value(), file_path=pkgbuild_path, user=self.pkgbuilder_user):
|
||||
return False
|
||||
|
||||
return makepkg.update_srcinfo('/'.join(pkgbuild_path.split('/')[0:-1]))
|
||||
return makepkg.update_srcinfo(project_dir='/'.join(pkgbuild_path.split('/')[0:-1]),
|
||||
custom_user=self.pkgbuilder_user)
|
||||
|
||||
return False
|
||||
|
||||
@@ -1897,9 +1899,10 @@ class ArchManager(SoftwareManager):
|
||||
if self._ask_for_pkgbuild_edition(pkgname=context.name,
|
||||
arch_config=context.config,
|
||||
watcher=context.watcher,
|
||||
pkgbuild_path='{}/PKGBUILD'.format(context.project_dir)):
|
||||
pkgbuild_path=f'{context.project_dir}/PKGBUILD'):
|
||||
context.pkgbuild_edited = True
|
||||
srcinfo = aur.map_srcinfo(string=makepkg.gen_srcinfo(context.project_dir), pkgname=context.name)
|
||||
srcinfo = aur.map_srcinfo(string=makepkg.gen_srcinfo(build_dir=context.project_dir, custom_user=self.pkgbuilder_user),
|
||||
pkgname=context.name)
|
||||
|
||||
if srcinfo:
|
||||
context.name = srcinfo['pkgname']
|
||||
@@ -1915,12 +1918,14 @@ class ArchManager(SoftwareManager):
|
||||
setattr(context.pkg, pkgattr, srcinfo.get(srcattr, getattr(context.pkg, pkgattr)))
|
||||
|
||||
def _read_srcinfo(self, context: TransactionContext) -> str:
|
||||
src_path = '{}/.SRCINFO'.format(context.project_dir)
|
||||
if not os.path.exists(src_path):
|
||||
srcinfo = makepkg.gen_srcinfo(context.project_dir, context.custom_pkgbuild_path)
|
||||
src_path = f'{context.project_dir}/.SRCINFO'
|
||||
|
||||
with open(src_path, 'w+') as f:
|
||||
f.write(srcinfo)
|
||||
if not os.path.exists(src_path):
|
||||
srcinfo = makepkg.gen_srcinfo(build_dir=context.project_dir,
|
||||
custom_pkgbuild_path=context.custom_pkgbuild_path,
|
||||
custom_user=self.pkgbuilder_user)
|
||||
|
||||
write_as_user(content=srcinfo, file_path=src_path, user=self.pkgbuilder_user)
|
||||
else:
|
||||
with open(src_path) as f:
|
||||
srcinfo = f.read()
|
||||
@@ -1947,10 +1952,11 @@ class ArchManager(SoftwareManager):
|
||||
cpus_changed, cpu_prev_governors = cpu_manager.set_all_cpus_to('performance', context.root_password, self.logger)
|
||||
|
||||
try:
|
||||
pkgbuilt, output = makepkg.make(pkgdir=context.project_dir,
|
||||
optimize=optimize,
|
||||
handler=context.handler,
|
||||
custom_pkgbuild=context.custom_pkgbuild_path)
|
||||
pkgbuilt, output = makepkg.build(pkgdir=context.project_dir,
|
||||
optimize=optimize,
|
||||
handler=context.handler,
|
||||
custom_pkgbuild=context.custom_pkgbuild_path,
|
||||
custom_user=self.pkgbuilder_user)
|
||||
finally:
|
||||
if cpus_changed and cpu_prev_governors:
|
||||
self.logger.info("Restoring CPU governors")
|
||||
@@ -1998,7 +2004,10 @@ class ArchManager(SoftwareManager):
|
||||
def __fill_aur_output_files(self, context: TransactionContext):
|
||||
self.logger.info("Determining output files of '{}'".format(context.name))
|
||||
context.watcher.change_substatus(self.i18n['arch.aur.build.list_output'])
|
||||
output_files = {f for f in makepkg.list_output_files(context.project_dir, context.custom_pkgbuild_path) if os.path.isfile(f)}
|
||||
|
||||
output_files = {f for f in makepkg.list_output_files(project_dir=context.project_dir,
|
||||
custom_pkgbuild_path=context.custom_pkgbuild_path,
|
||||
custom_user=self.pkgbuilder_user) if os.path.isfile(f)}
|
||||
|
||||
if output_files:
|
||||
context.install_files = output_files
|
||||
@@ -2013,7 +2022,8 @@ class ArchManager(SoftwareManager):
|
||||
file_to_install = gen_file[0]
|
||||
|
||||
if len(gen_file) > 1:
|
||||
srcinfo = aur.map_srcinfo(string=makepkg.gen_srcinfo(context.project_dir), pkgname=context.name)
|
||||
srcinfo = aur.map_srcinfo(string=makepkg.gen_srcinfo(build_dir=context.project_dir, custom_user=self.pkgbuilder_user),
|
||||
pkgname=context.name)
|
||||
pkgver = '-{}'.format(srcinfo['pkgver']) if srcinfo.get('pkgver') else ''
|
||||
pkgrel = '-{}'.format(srcinfo['pkgrel']) if srcinfo.get('pkgrel') else ''
|
||||
arch = '-{}'.format(srcinfo['arch']) if srcinfo.get('arch') else ''
|
||||
@@ -2134,11 +2144,12 @@ class ArchManager(SoftwareManager):
|
||||
if not handled_deps:
|
||||
return False
|
||||
|
||||
check_res = makepkg.check(context.project_dir,
|
||||
check_res = makepkg.check(project_dir=context.project_dir,
|
||||
optimize=bool(context.config['optimize']),
|
||||
missing_deps=False,
|
||||
handler=context.handler,
|
||||
custom_pkgbuild=context.custom_pkgbuild_path)
|
||||
custom_pkgbuild=context.custom_pkgbuild_path,
|
||||
custom_user=self.pkgbuilder_user)
|
||||
|
||||
if check_res:
|
||||
if check_res.get('gpg_key'):
|
||||
@@ -2502,23 +2513,30 @@ class ArchManager(SoftwareManager):
|
||||
return False
|
||||
|
||||
def _install_from_aur(self, context: TransactionContext) -> bool:
|
||||
if not context.dependency and not self.add_package_builder_user(context.handler):
|
||||
return False
|
||||
|
||||
self._optimize_makepkg(context.config, context.watcher)
|
||||
|
||||
context.build_dir = '{}/build_{}'.format(get_build_dir(context.config), int(time.time()))
|
||||
context.build_dir = f'{get_build_dir(context.config, self.pkgbuilder_user)}/build_{int(time.time())}'
|
||||
|
||||
try:
|
||||
if not os.path.exists(context.build_dir):
|
||||
build_dir = context.handler.handle(SystemProcess(new_subprocess(['mkdir', '-p', context.build_dir])))
|
||||
build_dir, build_dir_error = sshell.mkdir(dir_path=context.build_dir, custom_user=self.pkgbuilder_user)
|
||||
self._update_progress(context, 10)
|
||||
|
||||
if build_dir:
|
||||
if not build_dir:
|
||||
context.watcher.print(build_dir_error)
|
||||
else:
|
||||
base_name = context.get_base_name()
|
||||
context.watcher.change_substatus(self.i18n['arch.clone'].format(bold(base_name)))
|
||||
cloned = context.handler.handle_simple(git.clone_as_process(url=URL_GIT.format(base_name), cwd=context.build_dir, depth=1))
|
||||
clone_dir = f'{context.build_dir}/{base_name}'
|
||||
cloned = context.handler.handle_simple(git.clone(url=URL_GIT.format(base_name), target_dir=clone_dir,
|
||||
depth=1, custom_user=self.pkgbuilder_user))
|
||||
|
||||
if cloned:
|
||||
self._update_progress(context, 40)
|
||||
context.project_dir = '{}/{}'.format(context.build_dir, base_name)
|
||||
context.project_dir = clone_dir
|
||||
return self._build(context)
|
||||
finally:
|
||||
if os.path.exists(context.build_dir) and context.config['aur_remove_build_dir']:
|
||||
@@ -2544,10 +2562,10 @@ class ArchManager(SoftwareManager):
|
||||
ArchCompilationOptimizer(i18n=self.i18n, logger=self.context.logger, taskman=TaskManager()).optimize()
|
||||
|
||||
def install(self, pkg: ArchPackage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher, context: TransactionContext = None) -> TransactionResult:
|
||||
self.aur_client.clean_caches()
|
||||
if not self.check_action_allowed(pkg, watcher):
|
||||
return TransactionResult.fail()
|
||||
|
||||
if not self._check_action_allowed(pkg, watcher):
|
||||
return TransactionResult(success=False, installed=[], removed=[])
|
||||
self.aur_client.clean_caches()
|
||||
|
||||
handler = ProcessHandler(watcher) if not context else context.handler
|
||||
|
||||
@@ -2936,7 +2954,7 @@ class ArchManager(SoftwareManager):
|
||||
capitalize_label=False),
|
||||
FileChooserComponent(id_='aur_build_dir',
|
||||
label=self.i18n['arch.config.aur_build_dir'],
|
||||
tooltip=self.i18n['arch.config.aur_build_dir.tip'].format(BUILD_DIR),
|
||||
tooltip=self.i18n['arch.config.aur_build_dir.tip'].format(get_build_dir(arch_config, self.pkgbuilder_user)),
|
||||
max_width=max_width,
|
||||
file_path=arch_config['aur_build_dir'],
|
||||
capitalize_label=False,
|
||||
@@ -3397,7 +3415,8 @@ class ArchManager(SoftwareManager):
|
||||
deny_label=self.i18n['arch.aur.sync.several_names.popup.bt_selected']):
|
||||
context.pkgs_to_build = {context.name, *select.get_selected_values()}
|
||||
|
||||
pkgbuild_path = '{}/PKGBUILD'.format(context.project_dir)
|
||||
pkgbuild_path = f'{context.project_dir}/PKGBUILD'
|
||||
|
||||
with open(pkgbuild_path) as f:
|
||||
current_pkgbuild = f.read()
|
||||
|
||||
@@ -3407,16 +3426,22 @@ class ArchManager(SoftwareManager):
|
||||
names = context.name
|
||||
context.pkgs_to_build = {context.name}
|
||||
|
||||
new_pkgbuild = RE_PKGBUILD_PKGNAME.sub("pkgname={}".format(names), current_pkgbuild)
|
||||
custom_pkgbuild_path = pkgbuild_path + '_CUSTOM'
|
||||
new_pkgbuild = RE_PKGBUILD_PKGNAME.sub(f"pkgname={names}", current_pkgbuild)
|
||||
custom_pkgbuild_path = f'{pkgbuild_path}_CUSTOM'
|
||||
|
||||
with open(custom_pkgbuild_path, 'w+') as f:
|
||||
f.write(new_pkgbuild)
|
||||
if not write_as_user(content=new_pkgbuild,
|
||||
file_path=custom_pkgbuild_path,
|
||||
user=self.pkgbuilder_user):
|
||||
self.logger.error(f"Could not write edited PKGBUILD to '{custom_pkgbuild_path}'")
|
||||
return
|
||||
|
||||
new_srcinfo = makepkg.gen_srcinfo(context.project_dir, custom_pkgbuild_path)
|
||||
new_srcinfo = makepkg.gen_srcinfo(build_dir=context.project_dir,
|
||||
custom_pkgbuild_path=custom_pkgbuild_path,
|
||||
custom_user=self.pkgbuilder_user)
|
||||
|
||||
with open('{}/.SRCINFO'.format(context.project_dir), 'w+') as f:
|
||||
f.write(new_srcinfo)
|
||||
srcinfo_path = f'{context.project_dir}/.SRCINFO'
|
||||
if not write_as_user(content=new_srcinfo, file_path=srcinfo_path, user=self.pkgbuilder_user):
|
||||
self.logger.warning(f"Could not write the updated .SRCINFO content to '{srcinfo_path}'")
|
||||
|
||||
return custom_pkgbuild_path
|
||||
|
||||
@@ -3499,3 +3524,47 @@ class ArchManager(SoftwareManager):
|
||||
|
||||
pkg.update_state()
|
||||
return True
|
||||
|
||||
def check_action_allowed(self, pkg: ArchPackage, watcher: Optional[ProcessWatcher]) -> bool:
|
||||
if self.context.root_user and pkg.repository == 'aur':
|
||||
if not shutil.which('useradd'):
|
||||
if watcher:
|
||||
watcher.show_message(title=self.i18n['error'].capitalize(),
|
||||
type_=MessageType.ERROR,
|
||||
body=self.i18n['arch.aur.error.missing_root_dep'].format(dep=bold('useradd'),
|
||||
aur=bold('AUR'),
|
||||
root=bold('root')))
|
||||
return False
|
||||
|
||||
if not shutil.which('runuser'):
|
||||
if watcher:
|
||||
watcher.show_message(title=self.i18n['error'].capitalize(),
|
||||
type_=MessageType.ERROR,
|
||||
body=self.i18n['arch.aur.error.missing_root_dep'].format(dep=bold('runuser'),
|
||||
aur=bold('AUR'),
|
||||
root=bold('root')))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def add_package_builder_user(self, handler: ProcessHandler) -> bool:
|
||||
if self.context.root_user and self.pkgbuilder_user:
|
||||
try:
|
||||
getpwnam(self.pkgbuilder_user)
|
||||
return True
|
||||
except KeyError:
|
||||
self.logger.warning(f"Package builder user '{self.pkgbuilder_user}' does not exist")
|
||||
self.logger.info(f"Adding the package builder user '{self.pkgbuilder_user}'")
|
||||
added, output = handler.handle_simple(SimpleProcess(cmd=['useradd', self.pkgbuilder_user], shell=True))
|
||||
|
||||
if not added:
|
||||
output_log = "Command output: {}".format(output.replace('\n', ' ') if output else '(no output)')
|
||||
self.logger.error(f"Could not add the package builder user '{self.pkgbuilder_user}'. {output_log}")
|
||||
handler.watcher.show_message(title=self.i18n['error'].capitalize(),
|
||||
type_=MessageType.ERROR,
|
||||
body=self.i18n['arch.aur.error.add_builder_user'].format(user=bold(self.pkgbuilder_user),
|
||||
aur=bold('AUR')))
|
||||
|
||||
return added
|
||||
|
||||
return True
|
||||
|
||||
@@ -53,10 +53,13 @@ def log_shas_and_timestamps(repo_path: str) -> Optional[List[Tuple[str, int]]]:
|
||||
return logs
|
||||
|
||||
|
||||
def clone_as_process(url: str, cwd: Optional[str], depth: int = -1) -> SimpleProcess:
|
||||
def clone(url: str, target_dir: Optional[str], depth: int = -1, custom_user: Optional[str] = None) -> SimpleProcess:
|
||||
cmd = ['git', 'clone', url]
|
||||
|
||||
if depth > 0:
|
||||
cmd.append('--depth={}'.format(depth))
|
||||
cmd.append(f'--depth={depth}')
|
||||
|
||||
return SimpleProcess(cmd=cmd, cwd=cwd)
|
||||
if target_dir:
|
||||
cmd.append(target_dir)
|
||||
|
||||
return SimpleProcess(cmd=cmd, custom_user=custom_user)
|
||||
|
||||
@@ -1,20 +1,61 @@
|
||||
import os
|
||||
import re
|
||||
from typing import Tuple, Optional, Set
|
||||
from typing import Optional, Set, Tuple
|
||||
|
||||
from bauh.commons.system import SimpleProcess, ProcessHandler, run_cmd
|
||||
from bauh.commons import system
|
||||
from bauh.commons.system import ProcessHandler, SimpleProcess
|
||||
from bauh.gems.arch import CUSTOM_MAKEPKG_FILE
|
||||
from bauh.gems.arch.proc_util import write_as_user
|
||||
|
||||
RE_DEPS_PATTERN = re.compile(r'\n?\s+->\s(.+)\n')
|
||||
RE_UNKNOWN_GPG_KEY = re.compile(r'\(unknown public key (\w+)\)')
|
||||
RE_DEPS_PATTERN = re.compile(r'\n?\s+->\s(.+)\n')
|
||||
|
||||
|
||||
def gen_srcinfo(build_dir: str, custom_pkgbuild_path: Optional[str] = None) -> str:
|
||||
return run_cmd('makepkg --printsrcinfo{}'.format(' -p {}'.format(custom_pkgbuild_path) if custom_pkgbuild_path else ''),
|
||||
cwd=build_dir)
|
||||
def gen_srcinfo(build_dir: str, custom_pkgbuild_path: Optional[str] = None, custom_user: Optional[str] = None) -> str:
|
||||
cmd = f"makepkg --printsrcinfo{' -p {}'.format(custom_pkgbuild_path) if custom_pkgbuild_path else ''}"
|
||||
return system.run_cmd(cmd, cwd=build_dir, custom_user=custom_user)
|
||||
|
||||
|
||||
def check(pkgdir: str, optimize: bool, missing_deps: bool, handler: ProcessHandler, custom_pkgbuild: Optional[str] = None) -> dict:
|
||||
def update_srcinfo(project_dir: str, custom_user: Optional[str] = None) -> bool:
|
||||
updated_src = system.run_cmd('makepkg --printsrcinfo', cwd=project_dir, custom_user=custom_user)
|
||||
|
||||
if updated_src:
|
||||
return write_as_user(content=updated_src, file_path=f"{project_dir}/.SRCINFO", user=custom_user)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def list_output_files(project_dir: str, custom_pkgbuild_path: Optional[str] = None,
|
||||
custom_user: Optional[str] = None) -> Set[str]:
|
||||
cmd = f"makepkg --packagelist{' -p {}'.format(custom_pkgbuild_path) if custom_pkgbuild_path else ''}"
|
||||
output = system.run_cmd(cmd=cmd, print_error=False, cwd=project_dir, custom_user=custom_user)
|
||||
|
||||
if output:
|
||||
return {p.strip() for p in output.split('\n') if p}
|
||||
|
||||
return set()
|
||||
|
||||
|
||||
def build(pkgdir: str, optimize: bool, handler: ProcessHandler, custom_pkgbuild: Optional[str] = None,
|
||||
custom_user: Optional[str] = None) -> Tuple[bool, str]:
|
||||
cmd = ['makepkg', '-ALcsmf', '--skipchecksums', '--nodeps']
|
||||
|
||||
if custom_pkgbuild:
|
||||
cmd.append('-p')
|
||||
cmd.append(custom_pkgbuild)
|
||||
|
||||
if optimize:
|
||||
if os.path.exists(CUSTOM_MAKEPKG_FILE):
|
||||
handler.watcher.print(f'Using custom makepkg.conf -> {CUSTOM_MAKEPKG_FILE}')
|
||||
cmd.append(f'--config={CUSTOM_MAKEPKG_FILE}')
|
||||
else:
|
||||
handler.watcher.print(f'Custom optimized makepkg.conf ({CUSTOM_MAKEPKG_FILE}) not found')
|
||||
|
||||
return handler.handle_simple(SimpleProcess(cmd, cwd=pkgdir, shell=True, custom_user=custom_user))
|
||||
|
||||
|
||||
def check(project_dir: str, optimize: bool, missing_deps: bool, handler: ProcessHandler,
|
||||
custom_pkgbuild: Optional[str] = None, custom_user: Optional[str] = None) -> dict:
|
||||
res = {}
|
||||
|
||||
cmd = ['makepkg', '-ALcfm', '--check', '--noarchive', '--nobuild', '--noprepare']
|
||||
@@ -28,12 +69,12 @@ def check(pkgdir: str, optimize: bool, missing_deps: bool, handler: ProcessHandl
|
||||
|
||||
if optimize:
|
||||
if os.path.exists(CUSTOM_MAKEPKG_FILE):
|
||||
handler.watcher.print('Using custom makepkg.conf -> {}'.format(CUSTOM_MAKEPKG_FILE))
|
||||
cmd.append('--config={}'.format(CUSTOM_MAKEPKG_FILE))
|
||||
handler.watcher.print(f'Using custom makepkg.conf -> {CUSTOM_MAKEPKG_FILE}')
|
||||
cmd.append(f'--config={CUSTOM_MAKEPKG_FILE}')
|
||||
else:
|
||||
handler.watcher.print('Custom optimized makepkg.conf ( {} ) not found'.format(CUSTOM_MAKEPKG_FILE))
|
||||
handler.watcher.print(f'Custom optimized makepkg.conf ({CUSTOM_MAKEPKG_FILE}) not found')
|
||||
|
||||
success, output = handler.handle_simple(SimpleProcess(cmd, cwd=pkgdir, shell=True))
|
||||
success, output = handler.handle_simple(SimpleProcess(cmd, cwd=project_dir, shell=True, custom_user=custom_user))
|
||||
|
||||
if missing_deps and 'Missing dependencies' in output:
|
||||
res['missing_deps'] = RE_DEPS_PATTERN.findall(output)
|
||||
@@ -47,42 +88,3 @@ def check(pkgdir: str, optimize: bool, missing_deps: bool, handler: ProcessHandl
|
||||
res['validity_check'] = True
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def make(pkgdir: str, optimize: bool, handler: ProcessHandler, custom_pkgbuild: Optional[str] = None) -> Tuple[bool, str]:
|
||||
cmd = ['makepkg', '-ALcsmf', '--skipchecksums', '--nodeps']
|
||||
|
||||
if custom_pkgbuild:
|
||||
cmd.append('-p')
|
||||
cmd.append(custom_pkgbuild)
|
||||
|
||||
if optimize:
|
||||
if os.path.exists(CUSTOM_MAKEPKG_FILE):
|
||||
handler.watcher.print('Using custom makepkg.conf -> {}'.format(CUSTOM_MAKEPKG_FILE))
|
||||
cmd.append('--config={}'.format(CUSTOM_MAKEPKG_FILE))
|
||||
else:
|
||||
handler.watcher.print('Custom optimized makepkg.conf ( {} ) not found'.format(CUSTOM_MAKEPKG_FILE))
|
||||
|
||||
return handler.handle_simple(SimpleProcess(cmd, cwd=pkgdir, shell=True))
|
||||
|
||||
|
||||
def update_srcinfo(project_dir: str) -> bool:
|
||||
updated_src = run_cmd('makepkg --printsrcinfo', cwd=project_dir)
|
||||
|
||||
if updated_src:
|
||||
with open('{}/.SRCINFO'.format(project_dir), 'w+') as f:
|
||||
f.write(updated_src)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def list_output_files(project_dir: str, custom_pkgbuild_path: Optional[str] = None) -> Set[str]:
|
||||
output = run_cmd(cmd='makepkg --packagelist{}'.format(' -p {}'.format(custom_pkgbuild_path) if custom_pkgbuild_path else ''),
|
||||
print_error=False,
|
||||
cwd=project_dir)
|
||||
|
||||
if output:
|
||||
return {p.strip() for p in output.split('\n') if p}
|
||||
|
||||
return set()
|
||||
|
||||
51
bauh/gems/arch/proc_util.py
Normal file
51
bauh/gems/arch/proc_util.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import multiprocessing
|
||||
import os
|
||||
import traceback
|
||||
from pwd import getpwnam
|
||||
from typing import Callable, Optional, TypeVar
|
||||
|
||||
R = TypeVar('R')
|
||||
|
||||
|
||||
class CallAsUser:
|
||||
|
||||
def __init__(self, target: Callable[[], R], user: str):
|
||||
self._target = target
|
||||
self._user = user
|
||||
|
||||
def __call__(self, *args, **kwargs) -> R:
|
||||
try:
|
||||
os.setuid(getpwnam(self._user).pw_uid)
|
||||
return self._target()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
class WriteToFile:
|
||||
|
||||
def __init__(self, file_path: str, content: str):
|
||||
self._file_path = file_path
|
||||
self._content = content
|
||||
|
||||
def __call__(self, *args, **kwargs) -> bool:
|
||||
try:
|
||||
with open(self._file_path, 'w+') as f:
|
||||
f.write(self._content)
|
||||
|
||||
return True
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def exec_as_user(target: Callable[[], R], user: Optional[str] = None) -> R:
|
||||
if user:
|
||||
with multiprocessing.Pool(1) as pool:
|
||||
return pool.apply(CallAsUser(target, user))
|
||||
else:
|
||||
return target()
|
||||
|
||||
|
||||
def write_as_user(content: str, file_path: str, user: Optional[str] = None) -> bool:
|
||||
return exec_as_user(target=WriteToFile(file_path=file_path, content=content),
|
||||
user=user)
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=S’estan llegint les revisions del dipòsit
|
||||
arch.downgrade.repo_pkg.no_versions=No s'ha trobat cap versió antiga al disc
|
||||
arch.downgrade.searching_stored=Cerqueu versions antigues al disc
|
||||
arch.downgrade.version_found=S’ha trobat la versió actual del paquet
|
||||
arch.aur.error.missing_root_dep={dep} is not installed and is required for installing {aur} packages as the {root} user
|
||||
arch.aur.error.add_builder_user=It was not possible to create the user {user} for building {aur} packages
|
||||
arch.info.00_pkg_build=PKGBUILD
|
||||
arch.info.01_id=identificació
|
||||
arch.info.02_name=nom
|
||||
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=Repository Commits lesen
|
||||
arch.downgrade.repo_pkg.no_versions=No old version found on disk
|
||||
arch.downgrade.searching_stored=Looking for old versions on disk
|
||||
arch.downgrade.version_found=Aktuelle Paketversion gefunden
|
||||
arch.aur.error.missing_root_dep={dep} is not installed and is required for installing {aur} packages as the {root} user
|
||||
arch.aur.error.add_builder_user=It was not possible to create the user {user} for building {aur} packages
|
||||
arch.info.00_pkg_build=pkgbuild
|
||||
arch.info.01_id=Ich würde
|
||||
arch.info.02_name=Name
|
||||
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=Reading the repository commits
|
||||
arch.downgrade.repo_pkg.no_versions=No old version found on disk
|
||||
arch.downgrade.searching_stored=Looking for old versions on disk
|
||||
arch.downgrade.version_found=Current package version found
|
||||
arch.aur.error.missing_root_dep={dep} is not installed and is required for installing {aur} packages as the {root} user
|
||||
arch.aur.error.add_builder_user=It was not possible to create the user {user} for building {aur} packages
|
||||
arch.info.00_pkg_build=pkgbuild
|
||||
arch.info.01_id=id
|
||||
arch.info.02_name=name
|
||||
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=Leyendo los commits del repositorio
|
||||
arch.downgrade.repo_pkg.no_versions=No se encontró una versión anterior en el disco
|
||||
arch.downgrade.searching_stored=Buscando versiones antiguas en el disco
|
||||
arch.downgrade.version_found=Version actual del paquete encontrada
|
||||
arch.aur.error.missing_root_dep={dep} no está instalado y es necesario para la instalación de paquetes del {aur} como el usuario {root}
|
||||
arch.aur.error.add_builder_user=No fue posible crear el usuario {user} para construir paquetes del {aur}
|
||||
arch.info.00_pkg_build=pkgbuild
|
||||
arch.info.01_id=id
|
||||
arch.info.02_name=nombre
|
||||
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=Lecture des commits du dépôt
|
||||
arch.downgrade.repo_pkg.no_versions=Aucune version antérieure trouvée sur le disque
|
||||
arch.downgrade.searching_stored=Recherche de version antérieure sur le disque
|
||||
arch.downgrade.version_found=Version actuelle du paquet trouvée
|
||||
arch.aur.error.missing_root_dep={dep} is not installed and is required for installing {aur} packages as the {root} user
|
||||
arch.aur.error.add_builder_user=It was not possible to create the user {user} for building {aur} packages
|
||||
arch.info.00_pkg_build=pkgbuild
|
||||
arch.info.01_id=id
|
||||
arch.info.02_name=nom
|
||||
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=Reading the repository commits
|
||||
arch.downgrade.repo_pkg.no_versions=Nessuna versione precedente trovata sul disco
|
||||
arch.downgrade.searching_stored=Ricerca di versioni precedenti su disco
|
||||
arch.downgrade.version_found=Trovata la versione del pacchetto corrente
|
||||
arch.aur.error.missing_root_dep={dep} is not installed and is required for installing {aur} packages as the {root} user
|
||||
arch.aur.error.add_builder_user=It was not possible to create the user {user} for building {aur} packages
|
||||
arch.info.00_pkg_build=pkgbuild
|
||||
arch.info.01_id=id
|
||||
arch.info.02_name=nome
|
||||
|
||||
@@ -125,6 +125,8 @@ arch.downgrade.reading_commits=Lendo os commits do repositório
|
||||
arch.downgrade.repo_pkg.no_versions=Nenhuma versão antiga encontrada no disco
|
||||
arch.downgrade.searching_stored=Procurando versões antigas no disco
|
||||
arch.downgrade.version_found=Versão atual do pacote encontrada
|
||||
arch.aur.error.missing_root_dep={dep} não está instalado e é necessário para a instalação de pacotes do {aur} como o usuário {root}
|
||||
arch.aur.error.add_builder_user=Não foi possível criar o usuário {user} para a construção de pacotes do {aur}
|
||||
arch.info.00_pkg_build=pkgbuild
|
||||
arch.info.01_id=id
|
||||
arch.info.02_name=nome
|
||||
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=Чтение коммитов репозитор
|
||||
arch.downgrade.repo_pkg.no_versions=No old version found on disk
|
||||
arch.downgrade.searching_stored=Looking for old versions on disk
|
||||
arch.downgrade.version_found=Найдена текущая версия пакета
|
||||
arch.aur.error.missing_root_dep={dep} is not installed and is required for installing {aur} packages as the {root} user
|
||||
arch.aur.error.add_builder_user=It was not possible to create the user {user} for building {aur} packages
|
||||
arch.info.00_pkg_build=PKGBUILD
|
||||
arch.info.01_id=Идентификатор
|
||||
arch.info.02_name=Имя
|
||||
|
||||
@@ -126,6 +126,8 @@ arch.downgrade.reading_commits=Depo çalışmalarını oku
|
||||
arch.downgrade.repo_pkg.no_versions=Diskte eski sürüm bulunamadı
|
||||
arch.downgrade.searching_stored=Diskteki eski sürümlere bakılıyor
|
||||
arch.downgrade.version_found=Geçerli paket sürümü bulundu
|
||||
arch.aur.error.missing_root_dep={dep} is not installed and is required for installing {aur} packages as the {root} user
|
||||
arch.aur.error.add_builder_user=It was not possible to create the user {user} for building {aur} packages
|
||||
arch.info.00_pkg_build=pkgbuild
|
||||
arch.info.01_id=kimlik
|
||||
arch.info.02_name=isim
|
||||
|
||||
8
bauh/gems/arch/sshell.py
Normal file
8
bauh/gems/arch/sshell.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from bauh.commons.system import execute
|
||||
|
||||
|
||||
def mkdir(dir_path: str, parent: bool = True, custom_user: Optional[str] = None) -> Tuple[bool, Optional[str]]:
|
||||
code, output = execute(f'mkdir {"-p " if parent else ""}"{dir_path}"', shell=True, custom_user=custom_user)
|
||||
return code == 0, output
|
||||
Reference in New Issue
Block a user