mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-07 17:34:14 +02:00
0.9.23
This commit is contained in:
18
CHANGELOG.md
18
CHANGELOG.md
@@ -4,6 +4,24 @@ 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] 2021-12-10
|
||||
### Features
|
||||
- General
|
||||
- new configuration file `/etc/bauh/gems.forbidden` can be used by system administrators/distribution package managers to disable the management of supported packaging formats globally (more on that [here](https://github.com/vinifmor/bauh#forbidden_gems))
|
||||
|
||||
- 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)
|
||||
|
||||
### Improvements
|
||||
- code refactoring (String formatting method)
|
||||
|
||||
### Fixes
|
||||
- Arch
|
||||
- not updating the view (GUI) status correctly after uninstalling a package whose PKGBUILD file was edited (specifically the **name**)
|
||||
- missing error handling when hard requirements for optional dependencies cannot be found by pacman
|
||||
|
||||
|
||||
## [0.9.22] 2021-11-30
|
||||
### Improvements
|
||||
- General
|
||||
|
||||
32
README.md
32
README.md
@@ -15,22 +15,23 @@ Key features
|
||||
|
||||
|
||||
## Index
|
||||
1. [Installation](#installation)
|
||||
1. [Installation](#installation)
|
||||
- [Ubuntu-based distros (20.04)](#inst_ubuntu)
|
||||
- [Arch-based distros](#inst_arch)
|
||||
2. [Isolated installation](#inst_iso)
|
||||
3. [Desktop entry / menu shortcut](#desk_entry)
|
||||
4. [Autostart: tray mode](#autostart)
|
||||
5. [Distribution](#dist)
|
||||
6. [Supported types](#types)
|
||||
2. [Isolated installation](#inst_iso)
|
||||
3. [Desktop entry / menu shortcut](#desk_entry)
|
||||
4. [Autostart: tray mode](#autostart)
|
||||
5. [Distribution](#dist)
|
||||
6. [Supported types](#types)
|
||||
- [AppImage](#type_appimage)
|
||||
- [Arch packages/AUR](#type_arch)
|
||||
- [Flatpak](#type_flatpak)
|
||||
- [Snap](#type_snap)
|
||||
- [Native Web applications](#type_web)
|
||||
7. [General settings](#settings)
|
||||
8. [Directory structure, caching and logs](#dirs)
|
||||
9. [Custom themes](#custom_themes)
|
||||
7. [General settings](#settings)
|
||||
- [Forbidden packaging formats](#forbidden_gems)
|
||||
8. [Directory structure, caching and logs](#dirs)
|
||||
9. [Custom themes](#custom_themes)
|
||||
10. [Tray icons](#tray_icons)
|
||||
11. [CLI (Command Line Interface)](#cli)
|
||||
12. [Improving performance](#performance)
|
||||
@@ -240,6 +241,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`
|
||||
@@ -410,6 +414,16 @@ boot:
|
||||
load_apps: true # if the installed applications or suggestions should be loaded on the management panel after the initialization process. Default: true.
|
||||
```
|
||||
|
||||
##### <a name="forbidden_gems">Forbidden packaging formats</a>
|
||||
- System administrators and package managers of Linux distributions can disable the usage/management of supported packaging formats
|
||||
by adding their ids to the file `/etc/bauh/gems.forbidden`. This will prevent their management code to be loaded.
|
||||
- Example (one id per line):
|
||||
```
|
||||
arch
|
||||
appimage
|
||||
# flatpak # 'sharps' can be used to ignore a given line (comment)
|
||||
```
|
||||
|
||||
#### <a name="dirs">Directory structure, caching and logs</a>
|
||||
- `~/.config/bauh` (or `/etc/bauh` for **root**): stores configuration files
|
||||
- `~/.cache/bauh` (or `/var/cache/bauh` for **root**): stores data about your installed applications, databases, indexes, etc. Files are stored here to provide a faster initialization and data recovery.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '0.9.22'
|
||||
__version__ = '0.9.23'
|
||||
__app_name__ = 'bauh'
|
||||
|
||||
import os
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -49,7 +49,8 @@ def main():
|
||||
internet_checker=InternetChecker(offline=False),
|
||||
root_user=user.is_root())
|
||||
|
||||
managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config, default_locale=DEFAULT_I18N_KEY)
|
||||
managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config,
|
||||
default_locale=DEFAULT_I18N_KEY, logger=logger)
|
||||
|
||||
cli = CLIManager(GenericSoftwareManager(managers, context=context, config=app_config))
|
||||
|
||||
|
||||
@@ -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")
|
||||
@@ -1457,7 +1457,16 @@ class ArchManager(SoftwareManager):
|
||||
names={pkg.name},
|
||||
disk_loader=disk_loader) # to be able to return all uninstalled packages
|
||||
if success:
|
||||
return TransactionResult(success=True, installed=None, removed=[*removed.values()] if removed else [])
|
||||
removed_list = []
|
||||
|
||||
main_removed = removed.get(pkg.name)
|
||||
if main_removed:
|
||||
pkg.installed = False
|
||||
pkg.url_download = main_removed.url_download # otherwise uninstalled AUR packages cannot be reinstalled on the same view
|
||||
removed_list.append(pkg)
|
||||
|
||||
removed_list.extend((inst for name, inst in removed.items() if name != pkg.name))
|
||||
return TransactionResult(success=not pkg.installed, installed=None, removed=removed_list)
|
||||
else:
|
||||
return TransactionResult.fail()
|
||||
|
||||
@@ -1581,21 +1590,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 +1631,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 +1747,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 +1794,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 +1836,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 +1849,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 +1882,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 +1908,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 +1927,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 +1961,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 +2013,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 +2031,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 +2153,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 +2522,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 +2571,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
|
||||
|
||||
@@ -2567,11 +2594,11 @@ class ArchManager(SoftwareManager):
|
||||
root_password=root_password, handler=handler)
|
||||
|
||||
if pkg.repository == 'aur':
|
||||
res = self._install_from_aur(install_context)
|
||||
pkg_installed = self._install_from_aur(install_context)
|
||||
else:
|
||||
res = self._install_from_repository(install_context)
|
||||
pkg_installed = self._install_from_repository(install_context)
|
||||
|
||||
if res:
|
||||
if pkg_installed:
|
||||
pkg.name = install_context.name # changes the package name in case the PKGBUILD was edited
|
||||
|
||||
if os.path.exists(pkg.get_disk_data_path()):
|
||||
@@ -2589,7 +2616,7 @@ class ArchManager(SoftwareManager):
|
||||
|
||||
installed = []
|
||||
|
||||
if res and disk_loader and install_context.installed:
|
||||
if pkg_installed and disk_loader and install_context.installed:
|
||||
installed.append(pkg)
|
||||
|
||||
installed_to_load = []
|
||||
@@ -2610,7 +2637,7 @@ class ArchManager(SoftwareManager):
|
||||
self.logger.warning("Could not load all installed packages. Missing: {}".format(missing))
|
||||
|
||||
removed = [*install_context.removed.values()] if install_context.removed else []
|
||||
return TransactionResult(success=res, installed=installed, removed=removed)
|
||||
return TransactionResult(success=pkg_installed, installed=installed, removed=removed)
|
||||
|
||||
def _install_from_repository(self, context: TransactionContext) -> bool:
|
||||
try:
|
||||
@@ -2936,7 +2963,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 +3424,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 +3435,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
|
||||
|
||||
@@ -3449,6 +3483,8 @@ class ArchManager(SoftwareManager):
|
||||
except PackageInHoldException:
|
||||
self.logger.warning("There is a requirement in hold for opt dep '{}'".format(p))
|
||||
continue
|
||||
except PackageNotFoundException:
|
||||
self.logger.warning(f"No hard requirements found for optional {p}. Reason: package not found")
|
||||
|
||||
return res
|
||||
|
||||
@@ -3499,3 +3535,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
|
||||
@@ -124,7 +124,7 @@ class SnapManager(SoftwareManager):
|
||||
return ProcessHandler(watcher).handle_simple(snap.downgrade_and_stream(pkg.name, root_password))[0]
|
||||
|
||||
def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> SystemProcess:
|
||||
raise Exception("'upgrade' is not supported by {}".format(SnapManager.__class__.__name__))
|
||||
raise Exception(f"'upgrade' is not supported by {SnapManager.__class__.__name__}")
|
||||
|
||||
def uninstall(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:
|
||||
if snap.is_installed() and snapd.is_running():
|
||||
@@ -173,7 +173,7 @@ class SnapManager(SoftwareManager):
|
||||
return info
|
||||
|
||||
def get_history(self, pkg: SnapApplication) -> PackageHistory:
|
||||
raise Exception("'get_history' is not supported by {}".format(pkg.__class__.__name__))
|
||||
raise Exception(f"'get_history' is not supported by {pkg.__class__.__name__}")
|
||||
|
||||
def install(self, pkg: SnapApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
|
||||
# retrieving all installed so it will be possible to know the additional installed runtimes after the operation succeeds
|
||||
@@ -210,20 +210,20 @@ class SnapManager(SoftwareManager):
|
||||
if channels:
|
||||
opts = [InputOption(label=c[0], value=c[1]) for c in channels]
|
||||
channel_select = SingleSelectComponent(type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0])
|
||||
body = '<p>{}.</p>'.format(self.i18n['snap.install.available_channels.message'].format(bold(self.i18n['stable']), bold(pkg.name)))
|
||||
body += '<p>{}:</p>'.format(self.i18n['snap.install.available_channels.help'])
|
||||
body = f"<p>{self.i18n['snap.install.available_channels.message'].format(bold(self.i18n['stable']), bold(pkg.name))}.</p>"
|
||||
body += f"<p>{self.i18n['snap.install.available_channels.help']}:</p>"
|
||||
|
||||
if watcher.request_confirmation(title=self.i18n['snap.install.available_channels.title'],
|
||||
body=body,
|
||||
components=[channel_select],
|
||||
confirmation_label=self.i18n['continue'],
|
||||
deny_label=self.i18n['cancel']):
|
||||
self.logger.info("Installing '{}' with the custom command '{}'".format(pkg.name, channel_select.value))
|
||||
self.logger.info(f"Installing '{pkg.name}' with the custom command '{channel_select.value}'")
|
||||
res = ProcessHandler(watcher).handle(SystemProcess(new_root_subprocess(channel_select.value.value.split(' '), root_password=root_password)))
|
||||
return self._gen_installation_response(success=res, pkg=pkg,
|
||||
installed=installed_names, disk_loader=disk_loader)
|
||||
else:
|
||||
self.logger.error("Could not find available channels in the installation output: {}".format(output))
|
||||
self.logger.error(f"Could not find available channels in the installation output: {output}")
|
||||
|
||||
return self._gen_installation_response(success=res, pkg=pkg, installed=installed_names, disk_loader=disk_loader)
|
||||
|
||||
@@ -321,14 +321,13 @@ class SnapManager(SoftwareManager):
|
||||
if not snapd.is_running():
|
||||
snap_bold = bold('Snap')
|
||||
return [self.i18n['snap.notification.snapd_unavailable'].format(bold('snapd'), snap_bold),
|
||||
self.i18n['snap.notification.snap.disable'].format(snap_bold, bold(
|
||||
'{} > {}'.format(self.i18n['settings'].capitalize(),
|
||||
self.i18n['core.config.tab.types'])))]
|
||||
self.i18n['snap.notification.snap.disable'].format(snap_bold,
|
||||
bold(f"{self.i18n['settings'].capitalize()} > {self.i18n['core.config.tab.types']}"))]
|
||||
elif internet_available:
|
||||
available, output = snap.is_api_available()
|
||||
|
||||
if not available:
|
||||
self.logger.warning('It seems Snap API is not available. Search output: {}'.format(output))
|
||||
self.logger.warning(f'It seems Snap API is not available. Search output: {output}')
|
||||
return [self.i18n['snap.notifications.api.unavailable'].format(bold('Snaps'), bold('Snap'))]
|
||||
|
||||
def _fill_suggestion(self, name: str, priority: SuggestionPriority, snapd_client: SnapdClient, out: List[PackageSuggestion]):
|
||||
@@ -347,7 +346,7 @@ class SnapManager(SoftwareManager):
|
||||
out.append(sug)
|
||||
return
|
||||
|
||||
self.logger.warning("Could not retrieve suggestion '{}'".format(name))
|
||||
self.logger.warning(f"Could not retrieve suggestion '{name}'")
|
||||
|
||||
def _map_to_app(self, app_json: dict, installed: bool, disk_loader: Optional[DiskCacheLoader] = None, is_application: bool = False) -> SnapApplication:
|
||||
app = SnapApplication(id=app_json.get('id'),
|
||||
@@ -382,11 +381,11 @@ class SnapManager(SoftwareManager):
|
||||
res = []
|
||||
|
||||
if snapd.is_running():
|
||||
self.logger.info('Downloading suggestions file {}'.format(SUGGESTIONS_FILE))
|
||||
self.logger.info(f'Downloading suggestions file {SUGGESTIONS_FILE}')
|
||||
file = self.http_client.get(SUGGESTIONS_FILE)
|
||||
|
||||
if not file or not file.text:
|
||||
self.logger.warning("No suggestion found in {}".format(SUGGESTIONS_FILE))
|
||||
self.logger.warning(f"No suggestion found in {SUGGESTIONS_FILE}")
|
||||
return res
|
||||
else:
|
||||
self.logger.info('Mapping suggestions')
|
||||
@@ -437,7 +436,7 @@ class SnapManager(SoftwareManager):
|
||||
else:
|
||||
cmd = commands[0]['name']
|
||||
|
||||
self.logger.info("Running '{}': {}".format(pkg.name, cmd))
|
||||
self.logger.info(f"Running '{pkg.name}': {cmd}")
|
||||
snap.run(cmd)
|
||||
|
||||
def get_screenshots(self, pkg: SnapApplication) -> List[str]:
|
||||
@@ -482,19 +481,19 @@ class SnapManager(SoftwareManager):
|
||||
try:
|
||||
data = [r for r in snapd_client.find_by_name(pkg.name) if r['name'] == pkg.name]
|
||||
except:
|
||||
self.logger.warning("snapd client could not retrieve channels for '{}'".format(pkg.name))
|
||||
self.logger.warning(f"snapd client could not retrieve channels for '{pkg.name}'")
|
||||
return
|
||||
|
||||
if not data:
|
||||
self.logger.warning("snapd client could find a match for name '{}' when retrieving its channels".format(pkg.name))
|
||||
self.logger.warning(f"snapd client could find a match for name '{pkg.name}' when retrieving its channels")
|
||||
else:
|
||||
if not data[0].get('channels'):
|
||||
self.logger.info("No channel available for '{}'. Skipping selection.".format(pkg.name))
|
||||
self.logger.info(f"No channel available for '{pkg.name}'. Skipping selection.")
|
||||
else:
|
||||
if pkg.channel:
|
||||
current_channel = pkg.channel if '/' in pkg.channel else 'latest/{}'.format(pkg.channel)
|
||||
current_channel = pkg.channel if '/' in pkg.channel else f'latest/{pkg.channel}'
|
||||
else:
|
||||
current_channel = 'latest/{}'.format(data[0].get('channel', 'stable'))
|
||||
current_channel = f"latest/{data[0].get('channel', 'stable')}"
|
||||
|
||||
opts = []
|
||||
def_opt = None
|
||||
@@ -510,7 +509,7 @@ class SnapManager(SoftwareManager):
|
||||
def_opt = op
|
||||
|
||||
if not opts:
|
||||
self.logger.info("No different channel available for '{}'. Skipping selection.".format(pkg.name))
|
||||
self.logger.info(f"No different channel available for '{pkg.name}'. Skipping selection.")
|
||||
return
|
||||
|
||||
select = SingleSelectComponent(label='',
|
||||
|
||||
@@ -70,7 +70,7 @@ class SnapApplication(SoftwarePackage):
|
||||
return self.type == 'app'
|
||||
|
||||
def get_disk_cache_path(self):
|
||||
return super(SnapApplication, self).get_disk_cache_path() + '/installed/' + self.name
|
||||
return f'{super(SnapApplication, self).get_disk_cache_path()}/installed/{self.name}'
|
||||
|
||||
def is_trustable(self) -> bool:
|
||||
return self.verified_publisher
|
||||
|
||||
@@ -27,7 +27,7 @@ def install_and_stream(app_name: str, confinement: str, root_password: str, chan
|
||||
install_cmd.append('--classic')
|
||||
|
||||
if channel:
|
||||
install_cmd.append('--channel={}'.format(channel))
|
||||
install_cmd.append(f'--channel={channel}')
|
||||
|
||||
return SimpleProcess(install_cmd, root_password=root_password, shell=True)
|
||||
|
||||
@@ -42,7 +42,7 @@ def refresh_and_stream(app_name: str, root_password: str, channel: Optional[str]
|
||||
cmd = [BASE_CMD, 'refresh', app_name]
|
||||
|
||||
if channel:
|
||||
cmd.append('--channel={}'.format(channel))
|
||||
cmd.append(f'--channel={channel}')
|
||||
|
||||
return SimpleProcess(cmd=cmd,
|
||||
root_password=root_password,
|
||||
@@ -51,7 +51,7 @@ def refresh_and_stream(app_name: str, root_password: str, channel: Optional[str]
|
||||
|
||||
|
||||
def run(cmd: str):
|
||||
subprocess.Popen(['snap run {}'.format(cmd)], shell=True, env={**os.environ})
|
||||
subprocess.Popen([f'{BASE_CMD} run {cmd}'], shell=True, env={**os.environ})
|
||||
|
||||
|
||||
def is_api_available() -> Tuple[bool, str]:
|
||||
|
||||
@@ -55,7 +55,7 @@ class SnapdClient:
|
||||
final_query = query.strip()
|
||||
|
||||
if final_query and self.session:
|
||||
res = self.session.get(url='{}/find'.format(URL_BASE), params={'q': final_query})
|
||||
res = self.session.get(url=f'{URL_BASE}/find', params={'q': final_query})
|
||||
|
||||
if res.status_code == 200:
|
||||
json_res = res.json()
|
||||
@@ -65,7 +65,7 @@ class SnapdClient:
|
||||
|
||||
def find_by_name(self, name: str) -> Optional[List[dict]]:
|
||||
if name and self.session:
|
||||
res = self.session.get('{}/find?name={}'.format(URL_BASE, name))
|
||||
res = self.session.get(f'{URL_BASE}/find?name={name}')
|
||||
|
||||
if res.status_code == 200:
|
||||
json_res = res.json()
|
||||
@@ -75,7 +75,7 @@ class SnapdClient:
|
||||
|
||||
def list_all_snaps(self) -> List[dict]:
|
||||
if self.session:
|
||||
res = self.session.get('{}/snaps'.format(URL_BASE))
|
||||
res = self.session.get(f'{URL_BASE}/snaps')
|
||||
|
||||
if res.status_code == 200:
|
||||
json_res = res.json()
|
||||
@@ -87,7 +87,7 @@ class SnapdClient:
|
||||
|
||||
def list_only_apps(self) -> List[dict]:
|
||||
if self.session:
|
||||
res = self.session.get('{}/apps'.format(URL_BASE))
|
||||
res = self.session.get(f'{URL_BASE}/apps')
|
||||
|
||||
if res.status_code == 200:
|
||||
json_res = res.json()
|
||||
@@ -98,7 +98,7 @@ class SnapdClient:
|
||||
|
||||
def list_commands(self, name: str) -> List[dict]:
|
||||
if self.session:
|
||||
res = self.session.get('{}/apps?names={}'.format(URL_BASE, name))
|
||||
res = self.session.get(f'{URL_BASE}/apps?names={name}')
|
||||
|
||||
if res.status_code == 200:
|
||||
json_res = res.json()
|
||||
|
||||
@@ -45,7 +45,8 @@ def new_manage_panel(app_args: Namespace, app_config: dict, logger: logging.Logg
|
||||
internet_checker=InternetChecker(offline=app_args.offline),
|
||||
root_user=user.is_root())
|
||||
|
||||
managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config, default_locale=DEFAULT_I18N_KEY)
|
||||
managers = gems.load_managers(context=context, locale=i18n.current_key, config=app_config,
|
||||
default_locale=DEFAULT_I18N_KEY, logger=logger)
|
||||
|
||||
if app_args.reset:
|
||||
util.clean_app_files(managers)
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import inspect
|
||||
import os
|
||||
import pkgutil
|
||||
from typing import List
|
||||
from logging import Logger
|
||||
from typing import List, Generator
|
||||
|
||||
from bauh import ROOT_DIR
|
||||
from bauh import __app_name__, ROOT_DIR
|
||||
from bauh.api.abstract.controller import SoftwareManager, ApplicationContext
|
||||
from bauh.view.util import translation
|
||||
|
||||
FORBIDDEN_GEMS_FILE = f'/etc/{__app_name__}/gems.forbidden'
|
||||
|
||||
|
||||
def find_manager(member):
|
||||
if not isinstance(member, str):
|
||||
@@ -19,12 +22,34 @@ def find_manager(member):
|
||||
return manager_found
|
||||
|
||||
|
||||
def load_managers(locale: str, context: ApplicationContext, config: dict, default_locale: str) -> List[SoftwareManager]:
|
||||
def read_forbidden_gems() -> Generator[str, None, None]:
|
||||
try:
|
||||
with open(FORBIDDEN_GEMS_FILE) as f:
|
||||
forbidden_lines = f.readlines()
|
||||
|
||||
for line in forbidden_lines:
|
||||
clean_line = line.strip()
|
||||
|
||||
if clean_line and not clean_line.startswith('#'):
|
||||
yield clean_line
|
||||
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
|
||||
def load_managers(locale: str, context: ApplicationContext, config: dict, default_locale: str, logger: Logger) -> List[SoftwareManager]:
|
||||
managers = []
|
||||
|
||||
for f in os.scandir(ROOT_DIR + '/gems'):
|
||||
forbidden_gems = {gem for gem in read_forbidden_gems()}
|
||||
|
||||
for f in os.scandir(f'{ROOT_DIR}/gems'):
|
||||
if f.is_dir() and f.name != '__pycache__':
|
||||
loader = pkgutil.find_loader('bauh.gems.{}.controller'.format(f.name))
|
||||
|
||||
if f.name in forbidden_gems:
|
||||
logger.warning(f"gem '{f.name}' could not be loaded because it was marked as forbidden in '{FORBIDDEN_GEMS_FILE}'")
|
||||
continue
|
||||
|
||||
loader = pkgutil.find_loader(f'bauh.gems.{f.name}.controller')
|
||||
|
||||
if loader:
|
||||
module = loader.load_module()
|
||||
@@ -33,7 +58,7 @@ def load_managers(locale: str, context: ApplicationContext, config: dict, defaul
|
||||
|
||||
if manager_class:
|
||||
if locale:
|
||||
locale_path = '{}/resources/locale'.format(f.path)
|
||||
locale_path = f'{f.path}/resources/locale'
|
||||
|
||||
if os.path.exists(locale_path):
|
||||
context.i18n.current.update(translation.get_locale_keys(locale, locale_path)[1])
|
||||
@@ -51,4 +76,3 @@ def load_managers(locale: str, context: ApplicationContext, config: dict, defaul
|
||||
managers.append(man)
|
||||
|
||||
return managers
|
||||
|
||||
|
||||
Reference in New Issue
Block a user