From 6800d6f8e54ac6f597874c48d0c1d73f29766a16 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 14 Mar 2022 17:29:21 -0300 Subject: [PATCH 01/37] [arch] fix: not displaying ignored updates (regression) --- CHANGELOG.md | 5 +++++ bauh/__init__.py | 2 +- bauh/gems/arch/controller.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45f391fe..8efc7ddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ 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.10.1] +### Fixes +- Arch + - regression: not displaying ignored updates + ## [0.10.0] 2022-03-14 ### Features diff --git a/bauh/__init__.py b/bauh/__init__.py index f9b95658..91a5a6a5 100644 --- a/bauh/__init__.py +++ b/bauh/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.10.0' +__version__ = '0.10.1' __app_name__ = 'bauh' import os diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index d638d9e8..bfea0815 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -635,7 +635,7 @@ def read_installed(self, disk_loader: Optional[DiskCacheLoader], limit: int = -1 if ignored: for p in pkgs: if p.name in ignored: - p.updates_ignored = True + p.update_ignored = True return SearchResult(pkgs, None, len(pkgs)) From a4ccb08296f77877000037e06bd66776f6c41cf8 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 24 Mar 2022 15:29:56 -0300 Subject: [PATCH 02/37] [commons] refactoring: using empty string for the default LANG --- bauh/commons/system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bauh/commons/system.py b/bauh/commons/system.py index f9513b54..f013febf 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -13,7 +13,7 @@ GLOBAL_PY_LIBS = '/usr/lib/python{}'.format(PY_VERSION) PATH = os.getenv('PATH') -DEFAULT_LANG = 'en' +DEFAULT_LANG = '' GLOBAL_INTERPRETER_PATH = ':'.join(PATH.split(':')[1:]) @@ -23,7 +23,7 @@ USE_GLOBAL_INTERPRETER = bool(os.getenv('VIRTUAL_ENV')) -def gen_env(global_interpreter: bool, lang: str = DEFAULT_LANG, extra_paths: Optional[Set[str]] = None) -> dict: +def gen_env(global_interpreter: bool, lang: Optional[str] = DEFAULT_LANG, extra_paths: Optional[Set[str]] = None) -> dict: custom_env = dict(os.environ) if lang is not None: From 9632b95b9d70f86b3d16cbfc758ea4a47791e0cd Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 24 Mar 2022 16:25:18 -0300 Subject: [PATCH 03/37] [commons] refactoring: using 'en_US.UTF-8' as the the default LANG --- bauh/commons/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bauh/commons/system.py b/bauh/commons/system.py index f013febf..7ea8654b 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -13,7 +13,7 @@ GLOBAL_PY_LIBS = '/usr/lib/python{}'.format(PY_VERSION) PATH = os.getenv('PATH') -DEFAULT_LANG = '' +DEFAULT_LANG = 'en_US.UTF-8' GLOBAL_INTERPRETER_PATH = ':'.join(PATH.split(':')[1:]) From e7701a96c62f268437f69e92fe6b8ea7ff557fa5 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 24 Mar 2022 16:50:28 -0300 Subject: [PATCH 04/37] Revert "[commons] refactoring: using 'en_US.UTF-8' as the the default LANG" This reverts commit 9632b95b9d70f86b3d16cbfc758ea4a47791e0cd. --- bauh/commons/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bauh/commons/system.py b/bauh/commons/system.py index 7ea8654b..f013febf 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -13,7 +13,7 @@ GLOBAL_PY_LIBS = '/usr/lib/python{}'.format(PY_VERSION) PATH = os.getenv('PATH') -DEFAULT_LANG = 'en_US.UTF-8' +DEFAULT_LANG = '' GLOBAL_INTERPRETER_PATH = ':'.join(PATH.split(':')[1:]) From d10255ed3e2b326bb556b34ddc054cb56ce8707d Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 24 Mar 2022 17:34:00 -0300 Subject: [PATCH 05/37] [view] fix: rare crash when updating table items --- CHANGELOG.md | 3 +++ bauh/view/qt/window.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8efc7ddf..dc4d3de2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Arch - regression: not displaying ignored updates +- UI: + - fix: rare crash when updating table items + ## [0.10.0] 2022-03-14 ### Features diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index 3847934b..7141760e 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -659,7 +659,7 @@ def _update_state_when_pkgs_ready(self): self._reorganize() def _update_package_data(self, idx: int): - if self.table_apps.isEnabled(): + if self.table_apps.isEnabled() and self.pkgs is not None and 0 <= idx < len(self.pkgs): pkg = self.pkgs[idx] pkg.status = PackageViewStatus.READY self.table_apps.update_package(pkg) From d55e46c6ecb981033ca4c4ce20a714f731d01bc0 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 24 Mar 2022 17:35:32 -0300 Subject: [PATCH 06/37] [commons] refactoring: typing and kwargs of the 'system' module --- bauh/commons/system.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/bauh/commons/system.py b/bauh/commons/system.py index f013febf..5a47f994 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -63,7 +63,7 @@ def wait(self): class SimpleProcess: def __init__(self, cmd: Iterable[str], cwd: str = '.', expected_code: int = 0, - global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: str = DEFAULT_LANG, root_password: Optional[str] = None, + global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: Optional[str] = DEFAULT_LANG, root_password: Optional[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, custom_user: Optional[str] = None): @@ -79,16 +79,17 @@ def __init__(self, cmd: Iterable[str], cwd: str = '.', expected_code: int = 0, final_cmd.extend(cmd) - self.instance = self._new(final_cmd, cwd, global_interpreter, lang, stdin=pwdin, extra_paths=extra_paths, extra_env=extra_env) + self.instance = self._new(final_cmd, cwd, global_interpreter, lang=lang, stdin=pwdin, + extra_paths=extra_paths, extra_env=extra_env) self.expected_code = expected_code self.error_phrases = error_phrases self.wrong_error_phrases = wrong_error_phrases self.success_phrases = success_phrases - def _new(self, cmd: List[str], cwd: str, global_interpreter: bool, lang: str, stdin = None, + def _new(self, cmd: List[str], cwd: str, global_interpreter: bool, lang: Optional[str], stdin = None, extra_paths: Set[str] = None, extra_env: Optional[Dict[str, str]] = None) -> subprocess.Popen: - env = gen_env(global_interpreter, lang, extra_paths=extra_paths) + env = gen_env(global_interpreter=global_interpreter, lang=lang, extra_paths=extra_paths) if extra_env: for var, val in extra_env.items(): @@ -240,7 +241,7 @@ def handle_simple(self, proc: SimpleProcess, output_handler=None, notify_watcher 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, - custom_user: Optional[str] = None) -> Optional[str]: + custom_user: Optional[str] = None, lang: Optional[str] = DEFAULT_LANG) -> Optional[str]: """ runs a given command and returns its default output :return: @@ -248,7 +249,7 @@ def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False, args = { "shell": True, "stdout": PIPE, - "env": gen_env(global_interpreter, extra_paths=extra_paths), + "env": gen_env(global_interpreter=global_interpreter, lang=lang, extra_paths=extra_paths), 'cwd': cwd } @@ -266,7 +267,7 @@ def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False, def new_subprocess(cmd: List[str], cwd: str = '.', shell: bool = False, stdin = None, - global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: str = DEFAULT_LANG, + global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: Optional[str] = DEFAULT_LANG, extra_paths: Set[str] = None, custom_user: Optional[str] = None) -> subprocess.Popen: args = { "stdout": PIPE, From e56ee01d523f4b714cb0852d6ac9ed5a6b0de05a Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 24 Mar 2022 17:36:59 -0300 Subject: [PATCH 07/37] [CHANGELOG.md] Updating 0.10.1 changes --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4d3de2..9884dcce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ 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.10.1] +### Improvements +- General + - code refactoring + ### Fixes - Arch - regression: not displaying ignored updates From a5ed57177c95347d66c4a13ad25458c570f795df Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 09:06:51 -0300 Subject: [PATCH 08/37] [flatpak] fix: wrong encoded strings displayed (requires Flatpak >= 1.12) --- CHANGELOG.md | 4 ++++ bauh/gems/flatpak/__init__.py | 1 + bauh/gems/flatpak/controller.py | 25 ++++++++++++++--------- bauh/gems/flatpak/flatpak.py | 36 ++++++++++++++++++--------------- bauh/gems/flatpak/worker.py | 4 +++- 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9884dcce..bfa8a59d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Arch - regression: not displaying ignored updates +- Flatpak: + - executed commands are not displayed on the system default language and encoding (requires Flatpak >= 1.12) [#242](https://github.com/vinifmor/bauh/issues/242) + - applications and runtimes descriptions are not displayed on the system default language (when available) [#242](https://github.com/vinifmor/bauh/issues/242) + - UI: - fix: rare crash when updating table items diff --git a/bauh/gems/flatpak/__init__.py b/bauh/gems/flatpak/__init__.py index 0f6ec4c3..a775678b 100644 --- a/bauh/gems/flatpak/__init__.py +++ b/bauh/gems/flatpak/__init__.py @@ -18,6 +18,7 @@ VERSION_1_3 = parse_version('1.3') VERSION_1_4 = parse_version('1.4') VERSION_1_5 = parse_version('1.5') +VERSION_1_12 = parse_version('1.12') def get_icon_path() -> str: diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index 3a11f1a5..7cda8b2e 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -224,10 +224,11 @@ def downgrade(self, pkg: FlatpakApplication, root_password: Optional[str], watch commit = history.history[history.pkg_status_idx + 1]['commit'] watcher.change_substatus(self.i18n['flatpak.downgrade.reverting']) watcher.change_progress(50) - success, _ = ProcessHandler(watcher).handle_simple(flatpak.downgrade(pkg.ref, - commit, - pkg.installation, - root_password)) + success, _ = ProcessHandler(watcher).handle_simple(flatpak.downgrade(app_ref=pkg.ref, + commit=commit, + installation=pkg.installation, + root_password=root_password, + version=flatpak.get_version())) watcher.change_progress(100) return success @@ -254,13 +255,15 @@ def upgrade(self, requirements: UpgradeRequirements, root_password: Optional[str if req.pkg.update_component: res, _ = ProcessHandler(watcher).handle_simple(flatpak.install(app_id=ref, installation=req.pkg.installation, - origin=req.pkg.origin)) + origin=req.pkg.origin, + version=flatpak_version)) else: res, _ = ProcessHandler(watcher).handle_simple(flatpak.update(app_ref=ref, installation=req.pkg.installation, related=related, - deps=deps)) + deps=deps, + version=flatpak_version)) watcher.change_substatus('') if not res: @@ -280,7 +283,9 @@ def uninstall(self, pkg: FlatpakApplication, root_password: Optional[str], watch if not self._make_exports_dir(watcher): return TransactionResult.fail() - uninstalled, _ = ProcessHandler(watcher).handle_simple(flatpak.uninstall(pkg.ref, pkg.installation)) + flatpak_version = flatpak.get_version() + uninstalled, _ = ProcessHandler(watcher).handle_simple(flatpak.uninstall(pkg.ref, pkg.installation, + flatpak_version)) if uninstalled: if self.suggestions_cache: @@ -445,7 +450,8 @@ def install(self, pkg: FlatpakApplication, root_password: Optional[str], disk_lo if not self._make_exports_dir(handler.watcher): return TransactionResult(success=False, installed=[], removed=[]) - installed, output = handler.handle_simple(flatpak.install(str(pkg.id), pkg.origin, pkg.installation)) + installed, output = handler.handle_simple(flatpak.install(str(pkg.id), pkg.origin, pkg.installation, + flatpak_version)) if not installed and 'error: No ref chosen to resolve matches' in output: ref_opts = RE_INSTALL_REFS.findall(output) @@ -459,7 +465,8 @@ def install(self, pkg: FlatpakApplication, root_password: Optional[str], disk_lo confirmation_label=self.i18n['proceed'].capitalize(), deny_label=self.i18n['cancel'].capitalize()): ref = ref_select.get_selected() - installed, output = handler.handle_simple(flatpak.install(ref, pkg.origin, pkg.installation)) + installed, output = handler.handle_simple(flatpak.install(ref, pkg.origin, pkg.installation, + flatpak_version)) pkg.ref = ref pkg.runtime = 'runtime' in ref else: diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index 1882e7f9..7d781cce 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -9,9 +9,9 @@ from packaging.version import parse as parse_version from bauh.api.exception import NoInternetException -from bauh.commons.system import new_subprocess, run_cmd, SimpleProcess, ProcessHandler +from bauh.commons.system import new_subprocess, run_cmd, SimpleProcess, ProcessHandler, DEFAULT_LANG from bauh.commons.util import size_to_byte -from bauh.gems.flatpak import EXPORTS_PATH, VERSION_1_3, VERSION_1_2, VERSION_1_5 +from bauh.gems.flatpak import EXPORTS_PATH, VERSION_1_3, VERSION_1_2, VERSION_1_5, VERSION_1_12 RE_SEVERAL_SPACES = re.compile(r'\s+') RE_COMMIT = re.compile(r'(Latest commit|Commit)\s*:\s*(.+)') @@ -95,7 +95,7 @@ def list_installed(version: Version) -> List[dict]: apps = [] if version < VERSION_1_2: - app_list = new_subprocess(['flatpak', 'list', '-d']) + app_list = new_subprocess(['flatpak', 'list', '-d'], lang=None) for o in app_list.stdout: if o: @@ -118,7 +118,7 @@ def list_installed(version: Version) -> List[dict]: else: cols = 'application,ref,arch,branch,description,origin,options,{}version'.format('' if version < VERSION_1_3 else 'name,') - app_list = new_subprocess(['flatpak', 'list', '--columns=' + cols]) + app_list = new_subprocess(['flatpak', 'list', '--columns=' + cols], lang=None) for o in app_list.stdout: if o: @@ -157,7 +157,7 @@ def list_installed(version: Version) -> List[dict]: return apps -def update(app_ref: str, installation: str, related: bool = False, deps: bool = False) -> SimpleProcess: +def update(app_ref: str, installation: str, version: Version, related: bool = False, deps: bool = False) -> SimpleProcess: cmd = ['flatpak', 'update', '-y', app_ref, '--{}'.format(installation)] if not related: @@ -166,12 +166,14 @@ def update(app_ref: str, installation: str, related: bool = False, deps: bool = if not deps: cmd.append('--no-deps') - return SimpleProcess(cmd=cmd, extra_paths={EXPORTS_PATH}, shell=True) + return SimpleProcess(cmd=cmd, extra_paths={EXPORTS_PATH}, shell=True, + lang=DEFAULT_LANG if version < VERSION_1_12 else None) -def uninstall(app_ref: str, installation: str) -> SimpleProcess: +def uninstall(app_ref: str, installation: str, version: Version) -> SimpleProcess: return SimpleProcess(cmd=['flatpak', 'uninstall', app_ref, '-y', '--{}'.format(installation)], extra_paths={EXPORTS_PATH}, + lang=DEFAULT_LANG if version < VERSION_1_12 else None, shell=True) @@ -229,14 +231,15 @@ def read_updates(version: Version, installation: str) -> Dict[str, set]: return res -def downgrade(app_ref: str, commit: str, installation: str, root_password: Optional[str]) -> SimpleProcess: +def downgrade(app_ref: str, commit: str, installation: str, root_password: Optional[str], version: Version) -> SimpleProcess: cmd = ['flatpak', 'update', '--no-related', '--no-deps', '--commit={}'.format(commit), app_ref, '-y', '--{}'.format(installation)] return SimpleProcess(cmd=cmd, - root_password=root_password if installation=='system' else None, + root_password=root_password if installation == 'system' else None, extra_paths={EXPORTS_PATH}, - success_phrases={'Changes complete.', 'Updates complete.'}, - wrong_error_phrases={'Warning'}) + lang=DEFAULT_LANG if version < VERSION_1_12 else None, + success_phrases={'Changes complete.', 'Updates complete.'} if version < VERSION_1_12 else None, + wrong_error_phrases={'Warning'} if version < VERSION_1_12 else None) def get_app_commits(app_ref: str, origin: str, installation: str, handler: ProcessHandler) -> Optional[List[str]]: @@ -282,13 +285,13 @@ def get_app_commits_data(app_ref: str, origin: str, installation: str, full_str: def search(version: Version, word: str, installation: str, app_id: bool = False) -> List[dict]: - res = run_cmd('{} search {} --{}'.format('flatpak', word, installation)) + res = run_cmd('{} search {} --{}'.format('flatpak', word, installation), lang=None) found = [] - split_res = res.split('\n') + split_res = res.strip().split('\n') - if split_res and split_res[0].lower() != 'no matches found': + if split_res and '\t' in split_res[0]: for info in split_res: if info: info_list = info.split('\t') @@ -359,10 +362,11 @@ def search(version: Version, word: str, installation: str, app_id: bool = False) return found -def install(app_id: str, origin: str, installation: str) -> SimpleProcess: +def install(app_id: str, origin: str, installation: str, version: Version) -> SimpleProcess: return SimpleProcess(cmd=['flatpak', 'install', origin, app_id, '-y', '--{}'.format(installation)], extra_paths={EXPORTS_PATH}, - wrong_error_phrases={'Warning'}, + lang=DEFAULT_LANG if version < VERSION_1_12 else None, + wrong_error_phrases={'Warning'} if version < VERSION_1_12 else None, shell=True) diff --git a/bauh/gems/flatpak/worker.py b/bauh/gems/flatpak/worker.py index 529c4afc..e16df1bb 100644 --- a/bauh/gems/flatpak/worker.py +++ b/bauh/gems/flatpak/worker.py @@ -55,7 +55,9 @@ def run(self): if not self.app.name: self.app.name = data.get('name') - self.app.description = data.get('description', data.get('summary', None)) + if not self.app.description: + self.app.description = data.get('description', data.get('summary', None)) + self.app.icon_url = data.get('iconMobileUrl', None) self.app.latest_version = data.get('currentReleaseVersion', self.app.version) From 9d35e896f84bff82136a4002d1cbc03aba2ee009 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 09:36:37 -0300 Subject: [PATCH 09/37] [flatpak] refactoring: 'flatpak' module String formatting method --- bauh/gems/flatpak/flatpak.py | 50 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index 7d781cce..6247cf3a 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -12,6 +12,7 @@ from bauh.commons.system import new_subprocess, run_cmd, SimpleProcess, ProcessHandler, DEFAULT_LANG from bauh.commons.util import size_to_byte from bauh.gems.flatpak import EXPORTS_PATH, VERSION_1_3, VERSION_1_2, VERSION_1_5, VERSION_1_12 +from bauh.gems.flatpak.constants import FLATHUB_URL RE_SEVERAL_SPACES = re.compile(r'\s+') RE_COMMIT = re.compile(r'(Latest commit|Commit)\s*:\s*(.+)') @@ -70,20 +71,20 @@ def is_installed(): def get_version() -> Optional[Version]: - res = run_cmd('{} --version'.format('flatpak'), print_error=False) + res = run_cmd('flatpak --version', print_error=False) return parse_version(res.split(' ')[1].strip()) if res else None -def get_app_info(app_id: str, branch: str, installation: str): +def get_app_info(app_id: str, branch: str, installation: str) -> Optional[str]: try: - return run_cmd('{} info {} {}'.format('flatpak', app_id, branch, '--{}'.format(installation))) + return run_cmd(f'flatpak info {app_id} {branch} --{installation}') except: traceback.print_exc() return '' def get_commit(app_id: str, branch: str, installation: str) -> Optional[str]: - info = run_cmd('flatpak info {} {} --{}'.format(app_id, branch, installation)) + info = run_cmd(f'flatpak info {app_id} {branch} --{installation}') if info: commits = RE_COMMIT.findall(info) @@ -117,8 +118,9 @@ def list_installed(version: Version) -> List[dict]: }) else: - cols = 'application,ref,arch,branch,description,origin,options,{}version'.format('' if version < VERSION_1_3 else 'name,') - app_list = new_subprocess(['flatpak', 'list', '--columns=' + cols], lang=None) + name_col = '' if version < VERSION_1_3 else 'name,' + cols = f'application,ref,arch,branch,description,origin,options,{name_col}version' + app_list = new_subprocess(['flatpak', 'list', f'--columns={cols}'], lang=None) for o in app_list.stdout: if o: @@ -158,7 +160,7 @@ def list_installed(version: Version) -> List[dict]: def update(app_ref: str, installation: str, version: Version, related: bool = False, deps: bool = False) -> SimpleProcess: - cmd = ['flatpak', 'update', '-y', app_ref, '--{}'.format(installation)] + cmd = ['flatpak', 'update', '-y', app_ref, f'--{installation}'] if not related: cmd.append('--no-related') @@ -171,7 +173,7 @@ def update(app_ref: str, installation: str, version: Version, related: bool = Fa def uninstall(app_ref: str, installation: str, version: Version) -> SimpleProcess: - return SimpleProcess(cmd=['flatpak', 'uninstall', app_ref, '-y', '--{}'.format(installation)], + return SimpleProcess(cmd=['flatpak', 'uninstall', app_ref, '-y', f'--{installation}'], extra_paths={EXPORTS_PATH}, lang=DEFAULT_LANG if version < VERSION_1_12 else None, shell=True) @@ -191,16 +193,16 @@ def read_updates(version: Version, installation: str) -> Dict[str, set]: res = {'partial': set(), 'full': set()} if version < VERSION_1_2: try: - output = run_cmd('{} update --no-related --no-deps --{}'.format('flatpak', installation), ignore_return_code=True) + output = run_cmd(f'flatpak update --no-related --no-deps --{installation}', ignore_return_code=True) - if 'Updating in {}'.format(installation) in output: - for line in output.split('Updating in {}:\n'.format(installation))[1].split('\n'): + if f'Updating in {installation}' in output: + for line in output.split(f'Updating in {installation}:\n')[1].split('\n'): if not line.startswith('Is this ok'): res['full'].add('{}/{}'.format(installation, line.split('\t')[0].strip())) except: traceback.print_exc() else: - updates = new_subprocess(['flatpak', 'update', '--{}'.format(installation)]).stdout + updates = new_subprocess(['flatpak', 'update', f'--{installation}']).stdout reg = r'[0-9]+\.\s+.+' @@ -215,7 +217,7 @@ def read_updates(version: Version, installation: str) -> Dict[str, set]: elif version >= VERSION_1_2: update_id = f'{line_split[2]}/{line_split[4]}/{installation}/{line_split[5]}' else: - update_id = '{}/{}/{}'.format(line_split[2], line_split[4], installation) + update_id = f'{line_split[2]}/{line_split[4]}/{installation}' if version >= VERSION_1_3 and len(line_split) >= 6: if line_split[4].strip().lower() in OPERATION_UPDATE_SYMBOLS: @@ -232,7 +234,7 @@ def read_updates(version: Version, installation: str) -> Dict[str, set]: def downgrade(app_ref: str, commit: str, installation: str, root_password: Optional[str], version: Version) -> SimpleProcess: - cmd = ['flatpak', 'update', '--no-related', '--no-deps', '--commit={}'.format(commit), app_ref, '-y', '--{}'.format(installation)] + cmd = ['flatpak', 'update', '--no-related', '--no-deps', f'--commit={commit}', app_ref, '-y', f'--{installation}'] return SimpleProcess(cmd=cmd, root_password=root_password if installation == 'system' else None, @@ -244,7 +246,7 @@ def downgrade(app_ref: str, commit: str, installation: str, root_password: Optio def get_app_commits(app_ref: str, origin: str, installation: str, handler: ProcessHandler) -> Optional[List[str]]: try: - p = SimpleProcess(['flatpak', 'remote-info', '--log', origin, app_ref, '--{}'.format(installation)]) + p = SimpleProcess(['flatpak', 'remote-info', '--log', origin, app_ref, f'--{installation}']) success, output = handler.handle_simple(p) if output.startswith('error:'): return @@ -255,7 +257,7 @@ def get_app_commits(app_ref: str, origin: str, installation: str, handler: Proce def get_app_commits_data(app_ref: str, origin: str, installation: str, full_str: bool = True) -> List[dict]: - log = run_cmd('{} remote-info --log {} {} --{}'.format('flatpak', origin, app_ref, installation)) + log = run_cmd(f'flatpak remote-info --log {origin} {app_ref} --{installation}') if not log: raise NoInternetException() @@ -285,7 +287,7 @@ def get_app_commits_data(app_ref: str, origin: str, installation: str, full_str: def search(version: Version, word: str, installation: str, app_id: bool = False) -> List[dict]: - res = run_cmd('{} search {} --{}'.format('flatpak', word, installation), lang=None) + res = run_cmd(f'flatpak search {word} --{installation}', lang=None) found = [] @@ -363,7 +365,7 @@ def search(version: Version, word: str, installation: str, app_id: bool = False) def install(app_id: str, origin: str, installation: str, version: Version) -> SimpleProcess: - return SimpleProcess(cmd=['flatpak', 'install', origin, app_id, '-y', '--{}'.format(installation)], + return SimpleProcess(cmd=['flatpak', 'install', origin, app_id, '-y', f'--{installation}'], extra_paths={EXPORTS_PATH}, lang=DEFAULT_LANG if version < VERSION_1_12 else None, wrong_error_phrases={'Warning'} if version < VERSION_1_12 else None, @@ -371,17 +373,19 @@ def install(app_id: str, origin: str, installation: str, version: Version) -> Si def set_default_remotes(installation: str, root_password: Optional[str] = None) -> SimpleProcess: - cmd = ['flatpak', 'remote-add', '--if-not-exists', 'flathub', 'https://flathub.org/repo/flathub.flatpakrepo', '--{}'.format(installation)] + cmd = ['flatpak', 'remote-add', '--if-not-exists', 'flathub', f'{FLATHUB_URL}/repo/flathub.flatpakrepo', + f'--{installation}'] + return SimpleProcess(cmd, root_password=root_password) def has_remotes_set() -> bool: - return bool(run_cmd('{} remotes'.format('flatpak')).strip()) + return bool(run_cmd('flatpak remotes').strip()) def list_remotes() -> Dict[str, Set[str]]: res = {'system': set(), 'user': set()} - output = run_cmd('{} remotes'.format('flatpak')).strip() + output = run_cmd('flatpak remotes').strip() if output: lines = output.split('\n') @@ -398,11 +402,11 @@ def list_remotes() -> Dict[str, Set[str]]: def run(app_id: str): - subprocess.Popen(['flatpak run {}'.format(app_id)], shell=True, env={**os.environ}) + subprocess.Popen([f'flatpak run {app_id}'], shell=True, env={**os.environ}) def map_update_download_size(app_ids: Iterable[str], installation: str, version: Version) -> Dict[str, int]: - success, output = ProcessHandler().handle_simple(SimpleProcess(['flatpak', 'update', '--{}'.format(installation)])) + success, output = ProcessHandler().handle_simple(SimpleProcess(['flatpak', 'update', f'--{installation}'])) if version >= VERSION_1_2: res = {} p = re.compile(r'^\d+.\t') From cbfd1c491afa2f0d4740e82e8525baa91b4cb4d0 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 09:41:58 -0300 Subject: [PATCH 10/37] [commons.system] refactoring: 'new_subprocess' parameters tyiping --- bauh/commons/system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bauh/commons/system.py b/bauh/commons/system.py index 5a47f994..6be7f1eb 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -4,7 +4,7 @@ import time from io import StringIO from subprocess import PIPE -from typing import List, Tuple, Set, Dict, Optional, Iterable +from typing import List, Tuple, Set, Dict, Optional, Iterable, Union, IO, Any # default environment variables for subprocesses. from bauh.api.abstract.handler import ProcessWatcher @@ -266,7 +266,7 @@ def run_cmd(cmd: str, expected_code: int = 0, ignore_return_code: bool = False, pass -def new_subprocess(cmd: List[str], cwd: str = '.', shell: bool = False, stdin = None, +def new_subprocess(cmd: Iterable[str], cwd: str = '.', shell: bool = False, stdin: Optional[Union[None, int, IO[Any]]] = None, global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: Optional[str] = DEFAULT_LANG, extra_paths: Set[str] = None, custom_user: Optional[str] = None) -> subprocess.Popen: args = { From 54dc8ac711f67417cb08e8a0c355f910fca71f5e Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 09:46:39 -0300 Subject: [PATCH 11/37] [flatpak.flatpak] refactoring: using tuples when possible --- bauh/gems/flatpak/flatpak.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index 6247cf3a..c8cbbc64 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -58,7 +58,7 @@ def get_fields(app_id: str, branch: str, fields: List[str]) -> List[str]: info = new_subprocess(cmd).stdout res = [] - for o in new_subprocess(['grep', '-E', '({}):.+'.format('|'.join(fields)), '-o'], stdin=info).stdout: + for o in new_subprocess(('grep', '-E', '({}):.+'.format('|'.join(fields)), '-o'), stdin=info).stdout: if o: res.append(o.decode().split(':')[-1].strip()) @@ -96,7 +96,7 @@ def list_installed(version: Version) -> List[dict]: apps = [] if version < VERSION_1_2: - app_list = new_subprocess(['flatpak', 'list', '-d'], lang=None) + app_list = new_subprocess(('flatpak', 'list', '-d'), lang=None) for o in app_list.stdout: if o: @@ -120,7 +120,7 @@ def list_installed(version: Version) -> List[dict]: else: name_col = '' if version < VERSION_1_3 else 'name,' cols = f'application,ref,arch,branch,description,origin,options,{name_col}version' - app_list = new_subprocess(['flatpak', 'list', f'--columns={cols}'], lang=None) + app_list = new_subprocess(('flatpak', 'list', f'--columns={cols}'), lang=None) for o in app_list.stdout: if o: @@ -173,7 +173,7 @@ def update(app_ref: str, installation: str, version: Version, related: bool = Fa def uninstall(app_ref: str, installation: str, version: Version) -> SimpleProcess: - return SimpleProcess(cmd=['flatpak', 'uninstall', app_ref, '-y', f'--{installation}'], + return SimpleProcess(cmd=('flatpak', 'uninstall', app_ref, '-y', f'--{installation}'), extra_paths={EXPORTS_PATH}, lang=DEFAULT_LANG if version < VERSION_1_12 else None, shell=True) @@ -202,12 +202,12 @@ def read_updates(version: Version, installation: str) -> Dict[str, set]: except: traceback.print_exc() else: - updates = new_subprocess(['flatpak', 'update', f'--{installation}']).stdout + updates = new_subprocess(('flatpak', 'update', f'--{installation}')).stdout reg = r'[0-9]+\.\s+.+' try: - for o in new_subprocess(['grep', '-E', reg, '-o', '--color=never'], stdin=updates).stdout: + for o in new_subprocess(('grep', '-E', reg, '-o', '--color=never'), stdin=updates).stdout: if o: line_split = o.decode().strip().split('\t') @@ -234,7 +234,7 @@ def read_updates(version: Version, installation: str) -> Dict[str, set]: def downgrade(app_ref: str, commit: str, installation: str, root_password: Optional[str], version: Version) -> SimpleProcess: - cmd = ['flatpak', 'update', '--no-related', '--no-deps', f'--commit={commit}', app_ref, '-y', f'--{installation}'] + cmd = ('flatpak', 'update', '--no-related', '--no-deps', f'--commit={commit}', app_ref, '-y', f'--{installation}') return SimpleProcess(cmd=cmd, root_password=root_password if installation == 'system' else None, @@ -246,7 +246,7 @@ def downgrade(app_ref: str, commit: str, installation: str, root_password: Optio def get_app_commits(app_ref: str, origin: str, installation: str, handler: ProcessHandler) -> Optional[List[str]]: try: - p = SimpleProcess(['flatpak', 'remote-info', '--log', origin, app_ref, f'--{installation}']) + p = SimpleProcess(('flatpak', 'remote-info', '--log', origin, app_ref, f'--{installation}')) success, output = handler.handle_simple(p) if output.startswith('error:'): return @@ -365,7 +365,7 @@ def search(version: Version, word: str, installation: str, app_id: bool = False) def install(app_id: str, origin: str, installation: str, version: Version) -> SimpleProcess: - return SimpleProcess(cmd=['flatpak', 'install', origin, app_id, '-y', f'--{installation}'], + return SimpleProcess(cmd=('flatpak', 'install', origin, app_id, '-y', f'--{installation}'), extra_paths={EXPORTS_PATH}, lang=DEFAULT_LANG if version < VERSION_1_12 else None, wrong_error_phrases={'Warning'} if version < VERSION_1_12 else None, @@ -373,8 +373,8 @@ def install(app_id: str, origin: str, installation: str, version: Version) -> Si def set_default_remotes(installation: str, root_password: Optional[str] = None) -> SimpleProcess: - cmd = ['flatpak', 'remote-add', '--if-not-exists', 'flathub', f'{FLATHUB_URL}/repo/flathub.flatpakrepo', - f'--{installation}'] + cmd = ('flatpak', 'remote-add', '--if-not-exists', 'flathub', f'{FLATHUB_URL}/repo/flathub.flatpakrepo', + f'--{installation}') return SimpleProcess(cmd, root_password=root_password) @@ -402,11 +402,11 @@ def list_remotes() -> Dict[str, Set[str]]: def run(app_id: str): - subprocess.Popen([f'flatpak run {app_id}'], shell=True, env={**os.environ}) + subprocess.Popen((f'flatpak run {app_id}',), shell=True, env={**os.environ}) def map_update_download_size(app_ids: Iterable[str], installation: str, version: Version) -> Dict[str, int]: - success, output = ProcessHandler().handle_simple(SimpleProcess(['flatpak', 'update', f'--{installation}'])) + success, output = ProcessHandler().handle_simple(SimpleProcess(('flatpak', 'update', f'--{installation}'))) if version >= VERSION_1_2: res = {} p = re.compile(r'^\d+.\t') From 6c8adfe1edbc243390c2491dcef125885f84a5f5 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 11:46:46 -0300 Subject: [PATCH 12/37] [flatpak] feature: 'full update' custom action --- CHANGELOG.md | 9 +++++++++ README.md | 2 ++ bauh/gems/flatpak/controller.py | 25 ++++++++++++++++++++++++- bauh/gems/flatpak/flatpak.py | 5 +++++ bauh/gems/flatpak/resources/locale/ca | 3 +++ bauh/gems/flatpak/resources/locale/de | 3 +++ bauh/gems/flatpak/resources/locale/en | 3 +++ bauh/gems/flatpak/resources/locale/es | 3 +++ bauh/gems/flatpak/resources/locale/fr | 3 +++ bauh/gems/flatpak/resources/locale/it | 3 +++ bauh/gems/flatpak/resources/locale/pt | 3 +++ bauh/gems/flatpak/resources/locale/ru | 3 +++ bauh/gems/flatpak/resources/locale/tr | 3 +++ 13 files changed, 67 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfa8a59d..f32ca33c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ 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.10.1] +### Features +- Flatpak + - new custom action "Full update": fully updates all installed Flatpak apps and components (useful if you are having issues with runtime updates) + +

+ +

+ + ### Improvements - General - code refactoring diff --git a/README.md b/README.md index 9b554c81..bb1d78ba 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,8 @@ prefer_repository_provider: true # when there is just one repository provider f ``` installation_level: null # defines a default installation level: "user" or "system". (null will display a popup asking the level) ``` +- Custom actions supported: + - **Full update**: it completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. #### Snap diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index 7cda8b2e..eae5c8d5 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -15,7 +15,7 @@ from bauh.api.abstract.disk import DiskCacheLoader from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePackage, PackageSuggestion, \ - SuggestionPriority, PackageStatus + SuggestionPriority, PackageStatus, CustomSoftwareAction from bauh.api.abstract.view import MessageType, FormComponent, SingleSelectComponent, InputOption, SelectViewType, \ ViewComponent, PanelComponent from bauh.commons.boot import CreateConfigFile @@ -46,6 +46,7 @@ def __init__(self, context: ApplicationContext): self.suggestions_cache = context.cache_factory.new(None) self.logger = context.logger self.configman = FlatpakConfigManager() + self._action_full_update: Optional[CustomSoftwareAction] = None def get_managed_types(self) -> Set["type"]: return {FlatpakApplication} @@ -734,3 +735,25 @@ def revert_ignored_update(self, pkg: FlatpakApplication): self._write_ignored_updates(ignored_keys) pkg.updates_ignored = False + + def gen_custom_actions(self) -> Generator[CustomSoftwareAction, None, None]: + yield self.action_full_update + + def full_update(self, root_password: Optional[str], watcher: ProcessWatcher) -> bool: + handler = ProcessHandler(watcher) + return handler.handle_simple(flatpak.full_update(flatpak.get_version()))[0] + + @property + def action_full_update(self) -> CustomSoftwareAction: + if self._action_full_update is None: + self._action_full_update = CustomSoftwareAction(i18n_label_key='flatpak.action.full_update', + i18n_description_key='flatpak.action.full_update.description', + i18n_status_key='flatpak.action.full_update.status', + backup=True, + manager=self, + requires_internet=True, + icon_path=get_icon_path(), + manager_method='full_update', + requires_root=False) + + return self._action_full_update diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index c8cbbc64..04e2d009 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -172,6 +172,11 @@ def update(app_ref: str, installation: str, version: Version, related: bool = Fa lang=DEFAULT_LANG if version < VERSION_1_12 else None) +def full_update(version: VERSION_1_12) -> SimpleProcess: + return SimpleProcess(cmd=('flatpak', 'update', '-y'), extra_paths={EXPORTS_PATH}, shell=True, + lang=DEFAULT_LANG if version < VERSION_1_12 else None) + + def uninstall(app_ref: str, installation: str, version: Version) -> SimpleProcess: return SimpleProcess(cmd=('flatpak', 'uninstall', app_ref, '-y', f'--{installation}'), extra_paths={EXPORTS_PATH}, diff --git a/bauh/gems/flatpak/resources/locale/ca b/bauh/gems/flatpak/resources/locale/ca index 41c10420..8a66e731 100644 --- a/bauh/gems/flatpak/resources/locale/ca +++ b/bauh/gems/flatpak/resources/locale/ca @@ -1,3 +1,6 @@ +flatpak.action.full_update=Full update +flatpak.action.full_update.description=It completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. +flatpak.action.full_update.status=Updating Flatpak apps and components flatpak.config.install_level=level flatpak.config.install_level.ask.tip={app} will ask the level that should be applied during the app installation flatpak.config.install_level.system=system diff --git a/bauh/gems/flatpak/resources/locale/de b/bauh/gems/flatpak/resources/locale/de index afd6ce47..46ae4945 100644 --- a/bauh/gems/flatpak/resources/locale/de +++ b/bauh/gems/flatpak/resources/locale/de @@ -1,3 +1,6 @@ +flatpak.action.full_update=Full update +flatpak.action.full_update.description=It completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. +flatpak.action.full_update.status=Updating Flatpak apps and components flatpak.config.install_level=level flatpak.config.install_level.ask.tip={app} will ask the level that should be applied during the app installation flatpak.config.install_level.system=system diff --git a/bauh/gems/flatpak/resources/locale/en b/bauh/gems/flatpak/resources/locale/en index 726d6214..aafc924d 100644 --- a/bauh/gems/flatpak/resources/locale/en +++ b/bauh/gems/flatpak/resources/locale/en @@ -1,3 +1,6 @@ +flatpak.action.full_update=Full update +flatpak.action.full_update.description=It completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. +flatpak.action.full_update.status=Updating Flatpak apps and components flatpak.config.install_level=level flatpak.config.install_level.ask.tip={app} will ask the level that should be applied during the app installation flatpak.config.install_level.system=system diff --git a/bauh/gems/flatpak/resources/locale/es b/bauh/gems/flatpak/resources/locale/es index 24e88dff..16d1d37f 100644 --- a/bauh/gems/flatpak/resources/locale/es +++ b/bauh/gems/flatpak/resources/locale/es @@ -1,3 +1,6 @@ +flatpak.action.full_update=Actualización completa +flatpak.action.full_update.description=Actualiza completamente las aplicaciones y componentes Flatpak. Útil si hay problemas con las actualizaciones de runtimes. +flatpak.action.full_update.status=Actualizando las aplicaciones y componentes Flatpak flatpak.config.install_level=nivel flatpak.config.install_level.ask.tip={app} preguntará el nivel que debe aplicarse durante la instalación de la aplicación flatpak.config.install_level.system=sistema diff --git a/bauh/gems/flatpak/resources/locale/fr b/bauh/gems/flatpak/resources/locale/fr index df08b4c0..549ddcb5 100644 --- a/bauh/gems/flatpak/resources/locale/fr +++ b/bauh/gems/flatpak/resources/locale/fr @@ -1,3 +1,6 @@ +flatpak.action.full_update=Full update +flatpak.action.full_update.description=It completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. +flatpak.action.full_update.status=Updating Flatpak apps and components flatpak.config.install_level=niveau flatpak.config.install_level.ask.tip={app} va demander le niveau à appliquer durant l'installation flatpak.config.install_level.system=système diff --git a/bauh/gems/flatpak/resources/locale/it b/bauh/gems/flatpak/resources/locale/it index 76ae88d7..54012fc5 100644 --- a/bauh/gems/flatpak/resources/locale/it +++ b/bauh/gems/flatpak/resources/locale/it @@ -1,3 +1,6 @@ +flatpak.action.full_update=Full update +flatpak.action.full_update.description=It completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. +flatpak.action.full_update.status=Updating Flatpak apps and components flatpak.config.install_level=level flatpak.config.install_level.ask.tip={app} will ask the level that should be applied during the app installation flatpak.config.install_level.system=system diff --git a/bauh/gems/flatpak/resources/locale/pt b/bauh/gems/flatpak/resources/locale/pt index 724bcb8a..f4af8a3a 100644 --- a/bauh/gems/flatpak/resources/locale/pt +++ b/bauh/gems/flatpak/resources/locale/pt @@ -1,3 +1,6 @@ +flatpak.action.full_update=Atualização completa +flatpak.action.full_update.description=Atualiza completamente aplicações e componentes Flatpak instalados. Útil se você estiver com problemas de atualização dos runtimes. +flatpak.action.full_update.status=Atualizando aplicações e componentes Flatpak flatpak.config.install_level=nível flatpak.config.install_level.ask.tip=O {app} perguntará qual o nível deverá ser aplicado durante a instalação do aplicativo flatpak.config.install_level.system=sistema diff --git a/bauh/gems/flatpak/resources/locale/ru b/bauh/gems/flatpak/resources/locale/ru index 80016c54..6e4e7bca 100644 --- a/bauh/gems/flatpak/resources/locale/ru +++ b/bauh/gems/flatpak/resources/locale/ru @@ -1,3 +1,6 @@ +flatpak.action.full_update=Full update +flatpak.action.full_update.description=It completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. +flatpak.action.full_update.status=Updating Flatpak apps and components flatpak.config.install_level=Уровень flatpak.config.install_level.ask.tip={app} запросит уровень, который должен быть применен во время установки приложения flatpak.config.install_level.system=Система diff --git a/bauh/gems/flatpak/resources/locale/tr b/bauh/gems/flatpak/resources/locale/tr index 1901e447..95c170bb 100644 --- a/bauh/gems/flatpak/resources/locale/tr +++ b/bauh/gems/flatpak/resources/locale/tr @@ -1,3 +1,6 @@ +flatpak.action.full_update=Full update +flatpak.action.full_update.description=It completely updates the Flatpak apps and components. Useful if you are having issues with runtime updates. +flatpak.action.full_update.status=Updating Flatpak apps and components flatpak.config.install_level=seviye flatpak.config.install_level.ask.tip={app} uygulama kurulumu sırasında uygulanması gereken seviyeyi soracak flatpak.config.install_level.system=sistem From e654ae06a576ab43dafce7e297fad7d23c7f58cb Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 12:53:14 -0300 Subject: [PATCH 13/37] [web] fix: using the wrong locale format for the Accept-Language header --- CHANGELOG.md | 3 ++ bauh/gems/web/controller.py | 28 ++++++++++++++---- tests/gems/web/__init__.py | 0 tests/gems/web/test_controller.py | 47 +++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 tests/gems/web/__init__.py create mode 100644 tests/gems/web/test_controller.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f32ca33c..9b20e84c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - executed commands are not displayed on the system default language and encoding (requires Flatpak >= 1.12) [#242](https://github.com/vinifmor/bauh/issues/242) - applications and runtimes descriptions are not displayed on the system default language (when available) [#242](https://github.com/vinifmor/bauh/issues/242) +- Web + - using the wrong locale format for the Accept-Language header + - UI: - fix: rare crash when updating table items diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index 615359a6..629b9916 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -59,6 +59,8 @@ RE_SEVERAL_SPACES = re.compile(r'\s+') RE_SYMBOLS_SPLIT = re.compile(r'[\-|_\s:.]') +DEFAULT_LANGUAGE_HEADER = 'en-US, en' + class WebApplicationManager(SoftwareManager): @@ -77,12 +79,28 @@ def __init__(self, context: ApplicationContext, suggestions_loader: Optional[Sug self.idxman = SearchIndexManager(logger=context.logger) self._custom_actions: Optional[Iterable[CustomSoftwareAction]] = None - def _get_lang_header(self) -> str: + def get_accept_language_header(self) -> str: try: - system_locale = locale.getdefaultlocale() - return system_locale[0] if system_locale else 'en_US' + syslocale = locale.getdefaultlocale() + + if syslocale: + locale_split = syslocale[0].split('_') + + if len(locale_split) == 2: + sys_lang = f'{locale_split[0]}-{locale_split[1]}, {locale_split[0]}' + + if DEFAULT_LANGUAGE_HEADER.split(',')[1].strip() not in sys_lang: + return f'{sys_lang}, {DEFAULT_LANGUAGE_HEADER}' + + return sys_lang + + else: + return f'{syslocale[0]}, {DEFAULT_LANGUAGE_HEADER}' + else: + return DEFAULT_LANGUAGE_HEADER + except: - return 'en_US' + return DEFAULT_LANGUAGE_HEADER def clean_environment(self, root_password: Optional[str], watcher: ProcessWatcher) -> bool: handler = ProcessHandler(watcher) @@ -221,7 +239,7 @@ def serialize_to_disk(self, pkg: SoftwarePackage, icon_bytes: Optional[bytes], o super(WebApplicationManager, self).serialize_to_disk(pkg=pkg, icon_bytes=None, only_icon=False) def _request_url(self, url: str) -> Optional[Response]: - headers = {'Accept-language': self._get_lang_header(), 'User-Agent': UA_CHROME} + headers = {'Accept-language': self.get_accept_language_header(), 'User-Agent': UA_CHROME} try: return self.http_client.get(url, headers=headers, ignore_ssl=True, single_call=True, session=False, allow_redirects=True) diff --git a/tests/gems/web/__init__.py b/tests/gems/web/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/gems/web/test_controller.py b/tests/gems/web/test_controller.py new file mode 100644 index 00000000..442ffe24 --- /dev/null +++ b/tests/gems/web/test_controller.py @@ -0,0 +1,47 @@ +from unittest import TestCase +from unittest.mock import Mock, patch + +from bauh.gems.web.controller import DEFAULT_LANGUAGE_HEADER +from bauh.gems.web.controller import WebApplicationManager + + +class ControllerTest(TestCase): + + def test_DEFAULT_LANGUAGE_HEADER(self): + self.assertEqual('en-US, en', DEFAULT_LANGUAGE_HEADER) + + +class WebApplicationManagerTest(TestCase): + + def setUp(self): + self.manager = WebApplicationManager(context=Mock()) + + @patch('locale.getdefaultlocale', side_effect=Exception) + def test_get_accept_language_header__must_return_default_locale_when_exception_raised(self, getdefaultlocale: Mock): + returned = self.manager.get_accept_language_header() + self.assertEqual(DEFAULT_LANGUAGE_HEADER, returned) + getdefaultlocale.assert_called_once() + + @patch('locale.getdefaultlocale', return_value=None) + def test_get_accept_language_header__must_return_default_locale_when_no_locale_is_returned(self, getdefaultlocale: Mock): + returned = self.manager.get_accept_language_header() + self.assertEqual(DEFAULT_LANGUAGE_HEADER, returned) + getdefaultlocale.assert_called_once() + + @patch('locale.getdefaultlocale', return_value=['es_AR']) + def test_get_accept_language_header__must_return_the_system_locale_without_underscore_plus_default_locale(self, getdefaultlocale: Mock): + returned = self.manager.get_accept_language_header() + self.assertEqual(f'es-AR, es, {DEFAULT_LANGUAGE_HEADER}', returned) + getdefaultlocale.assert_called_once() + + @patch('locale.getdefaultlocale', return_value=['es']) + def test_get_accept_language_header__must_return_the_simple_system_locale_plus_default_locale(self, getdefaultlocale: Mock): + returned = self.manager.get_accept_language_header() + self.assertEqual(f'es, {DEFAULT_LANGUAGE_HEADER}', returned) + getdefaultlocale.assert_called_once() + + @patch('locale.getdefaultlocale', return_value=['en_IN']) + def test_get_accept_language_header__must_not_concatenate_default_locale_if_system_locale_has_it(self, getdefaultlocale: Mock): + returned = self.manager.get_accept_language_header() + self.assertEqual(f'en-IN, en', returned) + getdefaultlocale.assert_called_once() From ea9864702730c3eab6879e975cf432d6d965707f Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 13:16:55 -0300 Subject: [PATCH 14/37] [snap.snap] refactoring: 'BASE_CMD' constant removed --- bauh/gems/snap/snap.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/bauh/gems/snap/snap.py b/bauh/gems/snap/snap.py index 002383db..331fa52c 100644 --- a/bauh/gems/snap/snap.py +++ b/bauh/gems/snap/snap.py @@ -6,22 +6,20 @@ from bauh.commons.system import SimpleProcess -BASE_CMD = 'snap' - def is_installed() -> bool: - return bool(shutil.which(BASE_CMD)) + return bool(shutil.which('snap')) def uninstall_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess: - return SimpleProcess(cmd=[BASE_CMD, 'remove', app_name], + return SimpleProcess(cmd=['snap', 'remove', app_name], root_password=root_password, shell=True) def install_and_stream(app_name: str, confinement: str, root_password: Optional[str], channel: Optional[str] = None) -> SimpleProcess: - install_cmd = [BASE_CMD, 'install', app_name] # default + install_cmd = ['snap', 'install', app_name] # default if confinement == 'classic': install_cmd.append('--classic') @@ -33,13 +31,13 @@ def install_and_stream(app_name: str, confinement: str, root_password: Optional[ def downgrade_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess: - return SimpleProcess(cmd=[BASE_CMD, 'revert', app_name], + return SimpleProcess(cmd=['snap', 'revert', app_name], root_password=root_password, shell=True) def refresh_and_stream(app_name: str, root_password: Optional[str], channel: Optional[str] = None) -> SimpleProcess: - cmd = [BASE_CMD, 'refresh', app_name] + cmd = ['snap', 'refresh', app_name] if channel: cmd.append(f'--channel={channel}') @@ -51,12 +49,12 @@ def refresh_and_stream(app_name: str, root_password: Optional[str], channel: Opt def run(cmd: str): - subprocess.Popen([f'{BASE_CMD} run {cmd}'], shell=True, env={**os.environ}) + subprocess.Popen([f'snap run {cmd}'], shell=True, env={**os.environ}) def is_api_available() -> Tuple[bool, str]: output = StringIO() - for o in SimpleProcess([BASE_CMD, 'search']).instance.stdout: + for o in SimpleProcess(['snap', 'search']).instance.stdout: if o: output.write(o.decode()) From 8307864ed1104dc02025a5c8c907233a391bb8b0 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 13:21:19 -0300 Subject: [PATCH 15/37] [snap.snap] refactoring: using tuples when possible --- bauh/gems/snap/snap.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bauh/gems/snap/snap.py b/bauh/gems/snap/snap.py index 331fa52c..2e2eb5b7 100644 --- a/bauh/gems/snap/snap.py +++ b/bauh/gems/snap/snap.py @@ -12,7 +12,7 @@ def is_installed() -> bool: def uninstall_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess: - return SimpleProcess(cmd=['snap', 'remove', app_name], + return SimpleProcess(cmd=('snap', 'remove', app_name), root_password=root_password, shell=True) @@ -31,7 +31,7 @@ def install_and_stream(app_name: str, confinement: str, root_password: Optional[ def downgrade_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess: - return SimpleProcess(cmd=['snap', 'revert', app_name], + return SimpleProcess(cmd=('snap', 'revert', app_name), root_password=root_password, shell=True) @@ -49,12 +49,12 @@ def refresh_and_stream(app_name: str, root_password: Optional[str], channel: Opt def run(cmd: str): - subprocess.Popen([f'snap run {cmd}'], shell=True, env={**os.environ}) + subprocess.Popen((f'snap run {cmd}',), shell=True, env={**os.environ}) def is_api_available() -> Tuple[bool, str]: output = StringIO() - for o in SimpleProcess(['snap', 'search']).instance.stdout: + for o in SimpleProcess(('snap', 'search')).instance.stdout: if o: output.write(o.decode()) From 330613f2c59e16ff846f57e92c32b68ded37a013 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 15:50:01 -0300 Subject: [PATCH 16/37] [commons.system] fix: some action errors not being displayed on the details component when they are concatenated with sudo output --- CHANGELOG.md | 1 + bauh/commons/system.py | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b20e84c..20268134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - UI: - fix: rare crash when updating table items + - some action errors not being displayed on the details component when they are concatenated with sudo output ## [0.10.0] 2022-03-14 diff --git a/bauh/commons/system.py b/bauh/commons/system.py index 6be7f1eb..83678455 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -1,4 +1,5 @@ import os +import re import subprocess import sys import time @@ -22,6 +23,8 @@ USE_GLOBAL_INTERPRETER = bool(os.getenv('VIRTUAL_ENV')) +RE_SUDO_OUTPUT = re.compile(r'[sudo]\s*[\w\s]+:\s*') + def gen_env(global_interpreter: bool, lang: Optional[str] = DEFAULT_LANG, extra_paths: Optional[Set[str]] = None) -> dict: custom_env = dict(os.environ) @@ -198,8 +201,8 @@ def handle_simple(self, proc: SimpleProcess, output_handler=None, notify_watcher except UnicodeDecodeError: continue - if line.startswith('[sudo] password'): - continue + if line.startswith('[sudo]'): + line = RE_SUDO_OUTPUT.split(line)[1] output.write(line) From 1961521a5ece47aa964410cac19eb11e2a4b8eea Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 15:50:42 -0300 Subject: [PATCH 17/37] [CHANGELOG.md] fix 0.10.1 changes --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20268134..c3a1d313 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - using the wrong locale format for the Accept-Language header - UI: - - fix: rare crash when updating table items + - rare crash when updating table items - some action errors not being displayed on the details component when they are concatenated with sudo output ## [0.10.0] 2022-03-14 From 4e1bffa72f24ad8f470016f82df661efc18c7027 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Fri, 25 Mar 2022 15:56:47 -0300 Subject: [PATCH 18/37] [snap] improvement: allowing the actions output locale to be decided by the Snap client --- CHANGELOG.md | 3 +++ bauh/gems/snap/snap.py | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3a1d313..da2c6d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - General - code refactoring +- Snap + - allowing the actions output locale to be decided by the Snap client + ### Fixes - Arch - regression: not displaying ignored updates diff --git a/bauh/gems/snap/snap.py b/bauh/gems/snap/snap.py index 2e2eb5b7..f3d4f2ed 100644 --- a/bauh/gems/snap/snap.py +++ b/bauh/gems/snap/snap.py @@ -14,6 +14,7 @@ def is_installed() -> bool: def uninstall_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess: return SimpleProcess(cmd=('snap', 'remove', app_name), root_password=root_password, + lang=None, shell=True) @@ -27,13 +28,14 @@ def install_and_stream(app_name: str, confinement: str, root_password: Optional[ if channel: install_cmd.append(f'--channel={channel}') - return SimpleProcess(install_cmd, root_password=root_password, shell=True) + return SimpleProcess(install_cmd, root_password=root_password, shell=True, lang=None) def downgrade_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess: return SimpleProcess(cmd=('snap', 'revert', app_name), root_password=root_password, - shell=True) + shell=True, + lang=None) def refresh_and_stream(app_name: str, root_password: Optional[str], channel: Optional[str] = None) -> SimpleProcess: @@ -44,7 +46,7 @@ def refresh_and_stream(app_name: str, root_password: Optional[str], channel: Opt return SimpleProcess(cmd=cmd, root_password=root_password, - error_phrases={'no updates available'}, + lang=None, shell=True) @@ -54,7 +56,7 @@ def run(cmd: str): def is_api_available() -> Tuple[bool, str]: output = StringIO() - for o in SimpleProcess(('snap', 'search')).instance.stdout: + for o in SimpleProcess(('snap', 'search'), lang=None).instance.stdout: if o: output.write(o.decode()) From f44097ad7e4375c11da8eb8ba82119440d4aa073 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 28 Mar 2022 10:56:14 -0300 Subject: [PATCH 19/37] [arch] fix: package sizes float conversion error --- bauh/gems/arch/pacman.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bauh/gems/arch/pacman.py b/bauh/gems/arch/pacman.py index f7382f4a..e9a35a0c 100644 --- a/bauh/gems/arch/pacman.py +++ b/bauh/gems/arch/pacman.py @@ -508,7 +508,7 @@ def map_update_sizes(pkgs: List[str]) -> Dict[str, int]: # bytes: output = run_cmd('pacman -Si {}'.format(' '.join(pkgs))) if output: - return {pkgs[idx]: size_to_byte(float(size[0]), size[1]) for idx, size in enumerate(RE_INSTALLED_SIZE.findall(output))} + return {pkgs[idx]: size_to_byte(float(size[0].replace(',', '.')), size[1]) for idx, size in enumerate(RE_INSTALLED_SIZE.findall(output))} return {} @@ -517,7 +517,7 @@ def map_download_sizes(pkgs: List[str]) -> Dict[str, int]: # bytes: output = run_cmd('pacman -Si {}'.format(' '.join(pkgs))) if output: - return {pkgs[idx]: size_to_byte(float(size[0]), size[1]) for idx, size in enumerate(RE_DOWNLOAD_SIZE.findall(output))} + return {pkgs[idx]: size_to_byte(float(size[0].replace(',', '.')), size[1]) for idx, size in enumerate(RE_DOWNLOAD_SIZE.findall(output))} return {} @@ -526,7 +526,7 @@ def get_installed_size(pkgs: List[str]) -> Dict[str, int]: # bytes output = run_cmd('pacman -Qi {}'.format(' '.join(pkgs))) if output: - return {pkgs[idx]: size_to_byte(float(size[0]), size[1]) for idx, size in enumerate(RE_INSTALLED_SIZE.findall(output))} + return {pkgs[idx]: size_to_byte(float(size[0].replace(',', '.')), size[1]) for idx, size in enumerate(RE_INSTALLED_SIZE.findall(output))} return {} @@ -688,11 +688,11 @@ def map_updates_data(pkgs: Iterable[str], files: bool = False) -> dict: latest_field = 'c' elif field == 'Download Size': size = val.split(' ') - data['ds'] = size_to_byte(float(size[0]), size[1]) + data['ds'] = size_to_byte(float(size[0].replace(',', '.')), size[1]) latest_field = 'ds' elif field == 'Installed Size': size = val.split(' ') - data['s'] = size_to_byte(float(size[0]), size[1]) + data['s'] = size_to_byte(float(size[0].replace(',', '.')), size[1]) latest_field = 's' elif latest_name and latest_field == 's': res[latest_name] = data From 84645b58facee3840a39a52737fa3a3c4dfe0ff5 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 28 Mar 2022 10:58:00 -0300 Subject: [PATCH 20/37] [flatpak] fix: package sizes float conversion error --- bauh/gems/flatpak/flatpak.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bauh/gems/flatpak/flatpak.py b/bauh/gems/flatpak/flatpak.py index 04e2d009..bd274138 100755 --- a/bauh/gems/flatpak/flatpak.py +++ b/bauh/gems/flatpak/flatpak.py @@ -438,12 +438,12 @@ def map_update_download_size(app_ids: Iterable[str], installation: str, version: if size and len(size) > 1: try: - res[related_id[0].strip()] = size_to_byte(float(size[0]), size[1].strip()) + res[related_id[0].strip()] = size_to_byte(float(size[0].replace(',', '.')), size[1].strip()) except: traceback.print_exc() else: try: - res[related_id[0].strip()] = size_to_byte(float(size_tuple[0]), size_tuple[1].strip()) + res[related_id[0].strip()] = size_to_byte(float(size_tuple[0].replace(',', '.')), size_tuple[1].strip()) except: traceback.print_exc() return res From eb7347dbea97cb33b613e2c36842124314f5d534 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 28 Mar 2022 15:24:42 -0300 Subject: [PATCH 21/37] [debian] fix: packages descriptions are not displayed on the system's default language --- CHANGELOG.md | 3 +++ bauh/gems/debian/aptitude.py | 28 ++++++++++++++++------------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da2c6d8c..592e1ff9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Arch - regression: not displaying ignored updates +- Debian + - packages descriptions are not displayed on the system's default language (when available) + - Flatpak: - executed commands are not displayed on the system default language and encoding (requires Flatpak >= 1.12) [#242](https://github.com/vinifmor/bauh/issues/242) - applications and runtimes descriptions are not displayed on the system default language (when available) [#242](https://github.com/vinifmor/bauh/issues/242) diff --git a/bauh/gems/debian/aptitude.py b/bauh/gems/debian/aptitude.py index 76b82cf8..3a7d18e3 100644 --- a/bauh/gems/debian/aptitude.py +++ b/bauh/gems/debian/aptitude.py @@ -46,6 +46,7 @@ def __init__(self, logger: Logger): self._size_attrs: Optional[Tuple[str]] = None self._default_lang = '' self._ignored_fields: Optional[Set[str]] = None + self._re_none: Optional[Pattern] = None def show(self, pkgs: Iterable[str], attrs: Optional[Collection[str]] = None, verbose: bool = False) \ -> Optional[Dict[str, Dict[str, object]]]: @@ -135,11 +136,10 @@ def simulate_upgrade(self, packages: Iterable[str]) -> DebianTransaction: def upgrade(self, packages: Iterable[str], root_password: Optional[str]) -> SimpleProcess: cmd = self.gen_transaction_cmd('upgrade', packages).split(' ') - return SimpleProcess(cmd=cmd, shell=True, lang=self._default_lang, - root_password=root_password) + return SimpleProcess(cmd=cmd, shell=True, root_password=root_password) def update(self, root_password: Optional[str]) -> SimpleProcess: - return SimpleProcess(('aptitude', 'update'), root_password=root_password, shell=True, lang=self._default_lang) + return SimpleProcess(('aptitude', 'update'), root_password=root_password, shell=True) def simulate_installation(self, packages: Iterable[str]) -> Optional[DebianTransaction]: code, output = system.execute(self.gen_transaction_cmd('install', packages, simulate=True), @@ -150,7 +150,7 @@ def simulate_installation(self, packages: Iterable[str]) -> Optional[DebianTrans def install(self, packages: Iterable[str], root_password: Optional[str]) -> SimpleProcess: cmd = self.gen_transaction_cmd('install', packages).split(' ') - return SimpleProcess(cmd=cmd, shell=True, lang=self._default_lang, root_password=root_password) + return SimpleProcess(cmd=cmd, shell=True, root_password=root_password) def read_installed(self) -> Generator[DebianPackage, None, None]: yield from self.search(query='~i') @@ -169,9 +169,7 @@ def read_updates(self) -> Generator[Tuple[str, str], None, None]: def search(self, query: str, fill_size: bool = False) -> Generator[DebianPackage, None, None]: attrs = f"%p^%v^%V^%m^%s^{'%I^' if fill_size else ''}%d" - _, output = system.execute(f"aptitude search {query} -q -F '{attrs}' --disable-columns", - shell=True, - custom_env=self.env) + _, output = system.execute(f"aptitude search {query} -q -F '{attrs}' --disable-columns", shell=True) if output: no_attrs = 7 if fill_size else 6 @@ -180,7 +178,7 @@ def search(self, query: str, fill_size: bool = False) -> Generator[DebianPackage line_split = line.strip().split('^', maxsplit=no_attrs - 1) if len(line_split) == no_attrs: - latest_version = line_split[2] if line_split[2] != '' else None + latest_version = line_split[2] if not self.re_none.match(line_split[2]) else None size = None @@ -195,7 +193,7 @@ def search(self, query: str, fill_size: bool = False) -> Generator[DebianPackage traceback.print_exc() if latest_version is not None: - installed_version = line_split[1] if line_split[1] != '' else None + installed_version = line_split[1] if not self.re_none.match(line_split[1]) else None section = strip_section(line_split[4]) yield DebianPackage(name=line_split[0], @@ -214,7 +212,7 @@ def search_by_name(self, names: Iterable[str], fill_size: bool = False) -> Gener def remove(self, packages: Iterable[str], root_password: Optional[str], purge: bool = False) -> SimpleProcess: return SimpleProcess(cmd=self.gen_remove_cmd(packages, purge).split(' '), shell=True, - lang=self._default_lang, root_password=root_password) + root_password=root_password) def read_installed_names(self) -> Generator[str, None, None]: code, output = system.execute("aptitude search ~i -q -F '%p' --disable-columns", @@ -236,8 +234,7 @@ def re_show_attr(self) -> Pattern: @property def env(self) -> Dict[str, str]: if self._env is None: - self._env = system.gen_env(global_interpreter=system.USE_GLOBAL_INTERPRETER, - lang=self._default_lang) + self._env = system.gen_env(global_interpreter=system.USE_GLOBAL_INTERPRETER) return self._env @@ -282,6 +279,13 @@ def gen_transaction_cmd(type_: str, packages: Iterable[str], simulate: bool = Fa f" -o Aptitude::ProblemResolver::EssentialRemoveScore=9999999" \ f"{' -V -s -Z' if simulate else ''}" + @property + def re_none(self) -> Pattern: + if self._re_none is None: + self._re_none = re.compile(r'^<\w+>$') + + return self._re_none + class AptitudeOutputHandler(Thread): From ba9c61db4e572793057ee584bdd3bbf0f227d032 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 28 Mar 2022 15:53:36 -0300 Subject: [PATCH 22/37] [arch] fix: displaying '?' instead of '0' for dependency sizes --- CHANGELOG.md | 1 + bauh/gems/arch/confirmation.py | 4 ++-- bauh/gems/arch/updates.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 592e1ff9..fff4fb16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixes - Arch - regression: not displaying ignored updates + - dependency size: display a '?' instead of '0' ('?' should only be displayed when the size is unknown) - Debian - packages descriptions are not displayed on the system's default language (when available) diff --git a/bauh/gems/arch/confirmation.py b/bauh/gems/arch/confirmation.py index b461f999..ec716cc2 100644 --- a/bauh/gems/arch/confirmation.py +++ b/bauh/gems/arch/confirmation.py @@ -26,7 +26,7 @@ def request_optional_deps(pkgname: str, pkg_repos: dict, watcher: ProcessWatcher i18n['repository'], d['repository'].lower(), i18n['size'].capitalize(), - get_human_size_str(size) if size else '?'), p) + get_human_size_str(size) if size is not None else '?'), p) op.icon_path = _get_repo_icon(d['repository']) opts.append(op) @@ -58,7 +58,7 @@ def request_install_missing_deps(pkgname: Optional[str], deps: List[Tuple[str, s i18n['repository'], dep[1].lower(), i18n['size'].capitalize(), - get_human_size_str(size) if size else '?'), dep[0]) + get_human_size_str(size) if size is not None else '?'), dep[0]) op.read_only = True op.icon_path = _get_repo_icon(dep[1]) opts.append(op) diff --git a/bauh/gems/arch/updates.py b/bauh/gems/arch/updates.py index 888e3833..e4c55844 100644 --- a/bauh/gems/arch/updates.py +++ b/bauh/gems/arch/updates.py @@ -335,7 +335,7 @@ def _map_requirement(self, pkg: ArchPackage, context: UpdateRequirementsContext, current_size = installed_sizes.get(pkg.name) if installed_sizes else None - if current_size is not None and pkgdata['s']: + if current_size is not None and pkgdata['s'] is not None: requirement.extra_size = pkgdata['s'] - current_size required_by = set() From d2513cc94f231f9d873de24f629fc573e837311a Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 28 Mar 2022 16:00:49 -0300 Subject: [PATCH 23/37] [arch] improvement: increasing the dependencies dialog width --- bauh/gems/arch/confirmation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bauh/gems/arch/confirmation.py b/bauh/gems/arch/confirmation.py index ec716cc2..75728a26 100644 --- a/bauh/gems/arch/confirmation.py +++ b/bauh/gems/arch/confirmation.py @@ -64,7 +64,8 @@ def request_install_missing_deps(pkgname: Optional[str], deps: List[Tuple[str, s opts.append(op) comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts)) - return watcher.request_confirmation(i18n['arch.missing_deps.title'], msg, [comp], confirmation_label=i18n['continue'].capitalize(), deny_label=i18n['cancel'].capitalize()) + return watcher.request_confirmation(i18n['arch.missing_deps.title'], msg, [comp], confirmation_label=i18n['continue'].capitalize(), deny_label=i18n['cancel'].capitalize(), + min_width=672) def request_providers(providers_map: Dict[str, Set[str]], repo_map: Dict[str, str], watcher: ProcessWatcher, i18n: I18n) -> Set[str]: From 9e8ad38e033f76329a27947403d2ce2583fef8ff Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 28 Mar 2022 16:58:22 -0300 Subject: [PATCH 24/37] [arch] improvement: text length of the 'dependencies installation' popup reduced --- CHANGELOG.md | 3 +++ bauh/gems/arch/confirmation.py | 8 ++++---- bauh/gems/arch/controller.py | 5 +++-- bauh/gems/arch/resources/locale/ca | 2 +- bauh/gems/arch/resources/locale/de | 2 +- bauh/gems/arch/resources/locale/en | 2 +- bauh/gems/arch/resources/locale/es | 2 +- bauh/gems/arch/resources/locale/fr | 2 +- bauh/gems/arch/resources/locale/it | 2 +- bauh/gems/arch/resources/locale/pt | 2 +- bauh/gems/arch/resources/locale/ru | 2 +- bauh/gems/arch/resources/locale/tr | 2 +- 12 files changed, 19 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fff4fb16..143cdc71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - General - code refactoring +- Arch + - text length of some popups reduced + - Snap - allowing the actions output locale to be decided by the Snap client diff --git a/bauh/gems/arch/confirmation.py b/bauh/gems/arch/confirmation.py index 75728a26..616be095 100644 --- a/bauh/gems/arch/confirmation.py +++ b/bauh/gems/arch/confirmation.py @@ -1,4 +1,4 @@ -from typing import Set, List, Tuple, Dict, Optional +from typing import Set, Tuple, Dict, Collection from bauh.api.abstract.handler import ProcessWatcher from bauh.api.abstract.view import MultipleSelectComponent, InputOption, FormComponent, SingleSelectComponent, \ @@ -44,8 +44,8 @@ def request_optional_deps(pkgname: str, pkg_repos: dict, watcher: ProcessWatcher return {o.value for o in view_opts.values} -def request_install_missing_deps(pkgname: Optional[str], deps: List[Tuple[str, str]], watcher: ProcessWatcher, i18n: I18n) -> bool: - msg = '

{}

'.format(i18n['arch.missing_deps.body'].format(name=bold(pkgname) if pkgname else '', deps=bold(str(len(deps))))) +def request_install_missing_deps(deps: Collection[Tuple[str, str]], watcher: ProcessWatcher, i18n: I18n) -> bool: + msg = f"

{i18n['arch.missing_deps.body'].format(deps=bold(str(len(deps))))}:

" opts = [] @@ -65,7 +65,7 @@ def request_install_missing_deps(pkgname: Optional[str], deps: List[Tuple[str, s comp = MultipleSelectComponent(label='', options=opts, default_options=set(opts)) return watcher.request_confirmation(i18n['arch.missing_deps.title'], msg, [comp], confirmation_label=i18n['continue'].capitalize(), deny_label=i18n['cancel'].capitalize(), - min_width=672) + min_width=600) def request_providers(providers_map: Dict[str, Set[str]], repo_map: Dict[str, str], watcher: ProcessWatcher, i18n: I18n) -> Set[str]: diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index bfea0815..76cfea3d 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -2075,7 +2075,7 @@ def _save_pkgbuild(self, context: TransactionContext): def _ask_and_install_missing_deps(self, context: TransactionContext, missing_deps: List[Tuple[str, str]]) -> bool: context.watcher.change_substatus(self.i18n['arch.missing_deps_found'].format(bold(context.name))) - if not confirmation.request_install_missing_deps(context.name, missing_deps, context.watcher, self.i18n): + if not confirmation.request_install_missing_deps(missing_deps, context.watcher, self.i18n): context.watcher.print(self.i18n['action.cancelled']) return False @@ -2265,7 +2265,8 @@ def _install_optdeps(self, context: TransactionContext) -> bool: sorted_deps = sorting.sort(to_sort, {**deps_data, **subdeps_data}, provided_map) - if display_deps_dialog and not confirmation.request_install_missing_deps(None, sorted_deps, context.watcher, self.i18n): + if display_deps_dialog and not confirmation.request_install_missing_deps(sorted_deps, context.watcher, + self.i18n): context.watcher.print(self.i18n['action.cancelled']) return True # because the main package installation was successful diff --git a/bauh/gems/arch/resources/locale/ca b/bauh/gems/arch/resources/locale/ca index 4a90cbb2..c551cb66 100644 --- a/bauh/gems/arch/resources/locale/ca +++ b/bauh/gems/arch/resources/locale/ca @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=Dependències opcionals arch.installing.package=S’està instal·lant el paquet {} arch.checking_unnecessary_deps=Checking if there are packages no longer needed arch.makepkg.optimizing=Optimitzant la recopilació -arch.missing_deps.body=S’han d’instal·lar les {deps} dependències següents abans de continuar amb la instal·lació de {name} +arch.missing_deps.body=The following dependencies ({deps}) will be installed arch.missing_deps.title=Dependències mancants arch.missing_deps_found=Dependències mancants per a {} arch.mthread_downloaded.error.cache_dir=It was not possible to create the cache directory {} diff --git a/bauh/gems/arch/resources/locale/de b/bauh/gems/arch/resources/locale/de index 37e579bd..19ada1a5 100644 --- a/bauh/gems/arch/resources/locale/de +++ b/bauh/gems/arch/resources/locale/de @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=Optionale Abhängigkeiten arch.installing.package=Paket {} installieren arch.checking_unnecessary_deps=Checking if there are packages no longer needed arch.makepkg.optimizing=Optimiert die Zusammenstellung -arch.missing_deps.body=Die folgenden {deps} Abhängigkeiten müssten installiert sein, bevor mit der {name} Installation fortgefahren werden kann +arch.missing_deps.body=The following dependencies ({deps}) will be installed arch.missing_deps.title=Fehlende Abhängigkeiten arch.missing_deps_found=Fehlende Abhängigkeiten für {} arch.mthread_downloaded.error.cache_dir=It was not possible to create the cache directory {} diff --git a/bauh/gems/arch/resources/locale/en b/bauh/gems/arch/resources/locale/en index 9a139db8..0a7ad9c6 100644 --- a/bauh/gems/arch/resources/locale/en +++ b/bauh/gems/arch/resources/locale/en @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=Optional dependencies arch.installing.package=Installing {} package arch.checking_unnecessary_deps=Checking if there are packages no longer needed arch.makepkg.optimizing=Optimizing the compilation -arch.missing_deps.body=The following {deps} dependencies must be installed so the {name} installation can continue +arch.missing_deps.body=The following dependencies ({deps}) will be installed arch.missing_deps.title=Missing dependencies arch.missing_deps_found=Missing dependencies for {} arch.mthread_downloaded.error.cache_dir=It was not possible to create the cache directory {} diff --git a/bauh/gems/arch/resources/locale/es b/bauh/gems/arch/resources/locale/es index 7b794f1a..9035ab68 100644 --- a/bauh/gems/arch/resources/locale/es +++ b/bauh/gems/arch/resources/locale/es @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=Dependencias opcionales arch.installing.package=Instalando el paquete {} arch.checking_unnecessary_deps=Verificando se hay paquetes innecesarios arch.makepkg.optimizing=Optimizing the compilation -arch.missing_deps.body=Deben instalarse las siguientes {deps} dependencias para que la instalación de {name} pueda continuar +arch.missing_deps.body=Las siguientes dependencias ({deps}) serán instaladas arch.missing_deps.title=Dependencias faltantes arch.missing_deps_found=Dependencias faltantes para {} arch.mthread_downloaded.error.cache_dir=No fue posible crear el directorio de caché {} diff --git a/bauh/gems/arch/resources/locale/fr b/bauh/gems/arch/resources/locale/fr index e7608733..ebdbd009 100644 --- a/bauh/gems/arch/resources/locale/fr +++ b/bauh/gems/arch/resources/locale/fr @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=Dépendances optionnelles arch.installing.package=Installation du paquet {} arch.checking_unnecessary_deps=Calcul des paquets devenus inutiles arch.makepkg.optimizing=Optimisation de la compilation -arch.missing_deps.body=Les dependances {deps} doivent être installées pour que l'installation de {name} se poursuive +arch.missing_deps.body=The following dependencies ({deps}) will be installed arch.missing_deps.title=Dépendances manquantes arch.missing_deps_found=Dépendances manquantes pour {} arch.mthread_downloaded.error.cache_dir=Impossible de créer le dossier de cache {} diff --git a/bauh/gems/arch/resources/locale/it b/bauh/gems/arch/resources/locale/it index f842df16..1d4fd5a0 100644 --- a/bauh/gems/arch/resources/locale/it +++ b/bauh/gems/arch/resources/locale/it @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=Dipendenze opzionali arch.installing.package=Installazione del pacchetto {} arch.checking_unnecessary_deps=Checking if there are packages no longer needed arch.makepkg.optimizing=Ottimizzando la compilazione -arch.missing_deps.body=Le seguenti {deps} dipendenze devono essere installate prima che l'installazione di {name} continui +arch.missing_deps.body=The following dependencies ({deps}) will be installed arch.missing_deps.title=Dipendenze mancanti arch.missing_deps_found=Dipendenze mancanti per {} arch.mthread_downloaded.error.cache_dir=It was not possible to create the cache directory {} diff --git a/bauh/gems/arch/resources/locale/pt b/bauh/gems/arch/resources/locale/pt index 434eb9f9..899415e2 100644 --- a/bauh/gems/arch/resources/locale/pt +++ b/bauh/gems/arch/resources/locale/pt @@ -229,7 +229,7 @@ arch.install.optdeps.request.title=Dependências opcionais arch.installing.package=Instalando o pacote {} arch.checking_unnecessary_deps=Verificando se há pacotes não mais necessários arch.makepkg.optimizing=Otimizando a compilação -arch.missing_deps.body=As seguintes {deps} dependências devem ser instaladas para que a instalação de {name} continue +arch.missing_deps.body=As seguintes dependências ({deps}) serão instaladas arch.missing_deps.title=Dependências ausentes arch.missing_deps_found=Dependencias ausentes para {} arch.mthread_downloaded.error.cache_dir=Não foi possível criar o diretório para cache {} diff --git a/bauh/gems/arch/resources/locale/ru b/bauh/gems/arch/resources/locale/ru index a3f4c91e..e755aa40 100644 --- a/bauh/gems/arch/resources/locale/ru +++ b/bauh/gems/arch/resources/locale/ru @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=Необязательные зависимо arch.installing.package=Установка пакета {} arch.checking_unnecessary_deps=Checking if there are packages no longer needed arch.makepkg.optimizing=Оптимизация компиляции -arch.missing_deps.body=Необходимо установить следующие зависимости , чтобы продолжить установку {name} +arch.missing_deps.body=The following dependencies ({deps}) will be installed arch.missing_deps.title=Отсутствующие зависимости arch.missing_deps_found=Отсутствуют зависимости для {} arch.mthread_downloaded.error.cache_dir=It was not possible to create the cache directory {} diff --git a/bauh/gems/arch/resources/locale/tr b/bauh/gems/arch/resources/locale/tr index 116b6aa5..8d4731bf 100644 --- a/bauh/gems/arch/resources/locale/tr +++ b/bauh/gems/arch/resources/locale/tr @@ -230,7 +230,7 @@ arch.install.optdeps.request.title=İsteğe bağlı bağımlılıklar arch.installing.package={} Paketi yükleniyor arch.checking_unnecessary_deps=Artık gerekli olmayan paketler olup olmadığını kontrol et arch.makepkg.optimizing=Derlemeyi optimize et -arch.missing_deps.body={name} kurulumunun devam edebilmesi için aşağıdaki {deps} bağımlılık kurulmalıdır +arch.missing_deps.body=The following dependencies ({deps}) will be installed arch.missing_deps.title=Eksik bağımlılıklar arch.missing_deps_found={} için eksik bağımlılıklar arch.mthread_downloaded.error.cache_dir=It was not possible to create the cache directory {} From e8f09f5ad430017eaced8ec6d8eaf8bb4e41bd92 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Mon, 28 Mar 2022 17:55:29 -0300 Subject: [PATCH 25/37] [arch] refactoring: removing unused methods from the 'pacman' module --- bauh/gems/arch/pacman.py | 199 --------------------------------------- 1 file changed, 199 deletions(-) diff --git a/bauh/gems/arch/pacman.py b/bauh/gems/arch/pacman.py index e9a35a0c..24200df7 100644 --- a/bauh/gems/arch/pacman.py +++ b/bauh/gems/arch/pacman.py @@ -55,10 +55,6 @@ def get_repositories(pkgs: Iterable[str]) -> dict: return repositories -def is_available_in_repositories(pkg_name: str) -> bool: - return bool(run_cmd('pacman -Ss ' + pkg_name)) - - def get_info(pkg_name, remote: bool = False) -> str: return run_cmd('pacman -{}i {}'.format('Q' if not remote else 'S', pkg_name), print_error=False) @@ -397,32 +393,6 @@ def list_repository_updates() -> Dict[str, str]: return res -def map_sorting_data(pkgnames: List[str]) -> Dict[str, dict]: - allinfo = new_subprocess(['pacman', '-Qi', *pkgnames]).stdout - - pkgs, current_pkg = {}, {} - mapped_attrs = 0 - for out in new_subprocess(["grep", "-Po", "(Name|Provides|Depends On)\s*:\s*\K(.+)"], stdin=allinfo).stdout: - if out: - line = out.decode().strip() - - if line: - if mapped_attrs == 0: - current_pkg['name'] = line - elif mapped_attrs == 1: - provides = set() if line == 'None' else set(line.split(' ')) - provides.add(current_pkg['name']) - current_pkg['provides'] = provides - elif mapped_attrs == 2: - current_pkg['depends'] = line.split(':')[1].strip() - pkgs[current_pkg['name']] = current_pkg - del current_pkg['name'] - - mapped_attrs = 0 - current_pkg = {} - return pkgs - - def get_build_date(pkgname: str) -> str: output = run_cmd('pacman -Qi {}'.format(pkgname)) @@ -801,67 +771,6 @@ def map_optional_deps(names: Iterable[str], remote: bool, not_installed: bool = return res -def map_all_deps(names: Iterable[str], only_installed: bool = False) -> Dict[str, Set[str]]: - output = run_cmd('pacman -Qi {}'.format(' '.join(names))) - - if output: - res = {} - deps_fields = {'Depends On', 'Optional Deps'} - latest_name, deps, latest_field = None, None, None - - for l in output.split('\n'): - if l: - if l[0] != ' ': - line = l.strip() - field_sep_idx = line.index(':') - field = line[0:field_sep_idx].strip() - - if field == 'Name': - latest_field = field - val = line[field_sep_idx + 1:].strip() - latest_name = val - deps = None - elif field in deps_fields: - latest_field = field - val = line[field_sep_idx + 1:].strip() - opt_deps = latest_field == 'Optional Deps' - - if deps is None: - deps = set() - - if val != 'None': - if ':' in val: - dep_info = val.split(':') - desc = dep_info[1].strip() - - if desc and opt_deps and only_installed and '[installed]' not in desc: - continue - - deps.add(dep_info[0].strip()) - else: - deps.update({dep.strip() for dep in val.split(' ') if dep}) - - elif latest_name and deps is not None: - res[latest_name] = deps - latest_name, deps, latest_field = None, None, None - - elif latest_name and deps is not None: - opt_deps = latest_field == 'Optional Deps' - - if ':' in l: - dep_info = l.split(':') - desc = dep_info[1].strip() - - if desc and opt_deps and only_installed and '[installed]' not in desc: - continue - - deps.add(dep_info[0].strip()) - else: - deps.update({dep.strip() for dep in l.split(' ') if dep}) - - return res - - def map_required_dependencies(*names: str) -> Dict[str, Set[str]]: output = run_cmd('pacman -Qi {}'.format(' '.join(names) if names else '')) @@ -1035,114 +944,6 @@ def map_replaces(names: Iterable[str], remote: bool = False) -> Dict[str, Set[st return res -def _list_unnecessary_deps(pkgs: Iterable[str], already_checked: Set[str], all_provided: Dict[str, Set[str]], recursive: bool = False) -> Set[str]: - output = run_cmd('pacman -Qi {}'.format(' '.join(pkgs))) - - if output: - res = set() - deps_field = False - - for l in output.split('\n'): - if l: - if l[0] != ' ': - line = l.strip() - field_sep_idx = line.index(':') - field = line[0:field_sep_idx].strip() - - if field == 'Depends On': - deps_field = True - val = line[field_sep_idx + 1:].strip() - - if val != 'None': - if ':' in val: - dep_info = val.split(':') - - real_deps = all_provided.get(dep_info[0].strip()) - - if real_deps: - res.update(real_deps) - else: - for dep in val.split(' '): - if dep: - real_deps = all_provided.get(dep.strip()) - - if real_deps: - res.update(real_deps) - - elif deps_field: - latest_field = False - - elif deps_field: - if ':' in l: - dep_info = l.split(':') - - real_deps = all_provided.get(dep_info[0].strip()) - - if real_deps: - res.update(real_deps) - else: - for dep in l.split(' '): - if dep: - real_deps = all_provided.get(dep.strip()) - - if real_deps: - res.update(real_deps) - - if res: - res = {dep for dep in res if dep not in already_checked} - already_checked.update(res) - - if recursive and res: - subdeps = _list_unnecessary_deps(res, already_checked, all_provided) - - if subdeps: - res.update(subdeps) - - return res - - -def list_unnecessary_deps(pkgs: Iterable[str], all_provided: Dict[str, Set[str]] = None) -> Set[str]: - all_checked = set(pkgs) - all_deps = _list_unnecessary_deps(pkgs, all_checked, map_provided(remote=False) if not all_provided else all_provided, recursive=True) - - unnecessary = set(pkgs) - if all_deps: - requirements_map = map_required_by(all_deps) - - to_clean = set() - for dep, required_by in requirements_map.items(): - if not required_by or not required_by.difference(unnecessary): - unnecessary.add(dep) - to_clean.add(dep) - elif required_by.difference(all_checked): # checking if there are requirements outside the context - to_clean.add(dep) - - if to_clean: - for dep in to_clean: - del requirements_map[dep] - - if requirements_map: - while True: - to_clean = set() - for dep, required_by in requirements_map.items(): - if not required_by.difference(unnecessary): - unnecessary.add(dep) - to_clean.add(dep) - - if to_clean: - for dep in to_clean: - del requirements_map[dep] - else: - break - - if requirements_map: # if it reaches this points it is possible to exist mutual dependent packages - for dep, required_by in requirements_map.items(): - if not required_by.difference({*requirements_map.keys(), *unnecessary}): - unnecessary.add(dep) - - return unnecessary.difference(pkgs) - - def list_installed_names() -> Set[str]: output = run_cmd('pacman -Qq', print_error=False) return {name.strip() for name in output.split('\n') if name} if output else set() From 4aef256c8921cef93a9860b6a9a10e2cba0ed6da Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Tue, 29 Mar 2022 12:15:32 -0300 Subject: [PATCH 26/37] [view] improvement: only displaying the 'Installed' filter when installed packages are available on the table --- CHANGELOG.md | 3 +++ bauh/view/qt/commons.py | 7 ++++++- bauh/view/qt/window.py | 31 +++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 143cdc71..771c1c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Snap - allowing the actions output locale to be decided by the Snap client +- UI + - only displaying the "Installed" filter when installed packages are available on the table + ### Fixes - Arch - regression: not displaying ignored updates diff --git a/bauh/view/qt/commons.py b/bauh/view/qt/commons.py index 8ba0a221..e5384a13 100644 --- a/bauh/view/qt/commons.py +++ b/bauh/view/qt/commons.py @@ -13,6 +13,7 @@ def new_pkgs_info() -> dict: 'napp_updates': 0, 'pkgs_displayed': [], 'not_installed': 0, + 'installed': 0, 'categories': set(), 'pkgs': []} # total packages @@ -41,7 +42,11 @@ def update_info(pkgv: PackageView, pkgs_info: dict): pkgs_info['categories'].add(cat) pkgs_info['pkgs'].append(pkgv) - pkgs_info['not_installed'] += 1 if not pkgv.model.installed else 0 + + if pkgv.model.installed: + pkgs_info['installed'] += 1 + else: + pkgs_info['not_installed'] += 1 def apply_filters(pkg: PackageView, filters: dict, info: dict, limit: bool = True): diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index 7141760e..6cf37bf3 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -469,7 +469,7 @@ def _register_groups(self): *common_filters) self.comp_manager.register_group(GROUP_UPPER_BAR, False, - CHECK_APPS, CHECK_UPDATES, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME, + CHECK_APPS, CHECK_UPDATES, CHECK_INSTALLED, COMBO_CATEGORIES, COMBO_TYPES, INP_NAME, BT_INSTALLED, BT_SUGGESTIONS, BT_REFRESH, BT_UPGRADE) self.comp_manager.register_group(GROUP_LOWER_BTS, False, BT_SUGGESTIONS, BT_THEMES, BT_CUSTOM_ACTIONS, BT_SETTINGS, BT_ABOUT) @@ -805,6 +805,7 @@ def _finish_uninstall(self, res: dict): self.update_custom_actions() self._show_console_checkbox_if_output() + self._update_installed_filter() self.begin_apply_filters() notify_tray() else: @@ -932,9 +933,6 @@ def update_pkgs(self, new_pkgs: Optional[List[SoftwarePackage]], as_installed: b pkgs_info = commons.new_pkgs_info() filters = self._gen_filters(ignore_updates=ignore_updates) - if not keep_filters: - self._change_checkbox(self.check_installed, False, 'filter_installed', trigger=False) - if new_pkgs is not None: old_installed = None @@ -988,6 +986,9 @@ def update_pkgs(self, new_pkgs: Optional[List[SoftwarePackage]], as_installed: b self.pkgs_installed = pkgs_info['pkgs'] self.pkgs = pkgs_info['pkgs_displayed'] + self._update_installed_filter(installed_available=pkgs_info['installed'] > 0, + keep_state=keep_filters, + hide=as_installed) self._update_table(pkgs_info=pkgs_info) if new_pkgs: @@ -1007,6 +1008,27 @@ def update_pkgs(self, new_pkgs: Optional[List[SoftwarePackage]], as_installed: b return True + def _update_installed_filter(self, keep_state: bool = True, hide: bool = False, installed_available: Optional[bool] = None): + if installed_available is not None: + has_installed = installed_available + elif self.pkgs_available == self.pkgs_installed: # it means the "installed" view is loaded + has_installed = False + else: + has_installed = False + if self.pkgs_available: + for p in self.pkgs_available: + if p.model.installed: + has_installed = True + break + + if not keep_state or not has_installed: + self._change_checkbox(self.check_installed, False, 'filter_installed', trigger=False) + + if hide: + self.comp_manager.set_component_visible(CHECK_INSTALLED, False) + else: + self.comp_manager.set_component_visible(CHECK_INSTALLED, has_installed) + def _apply_filters(self, pkgs_info: dict, ignore_updates: bool): pkgs_info['pkgs_displayed'] = [] filters = self._gen_filters(ignore_updates=ignore_updates) @@ -1438,6 +1460,7 @@ def _finish_install(self, res: dict): self.pkgs_installed.insert(idx, PackageView(model, self.i18n)) self.update_custom_actions() + self._update_installed_filter(installed_available=True, keep_state=True) self.table_apps.change_headers_policy(policy=QHeaderView.Stretch, maximized=self._maximized) self.table_apps.change_headers_policy(policy=QHeaderView.ResizeToContents, maximized=self._maximized) self._resize(accept_lower_width=False) From bb9a52f34fd80a706c6565d201ef4eec6902e8e9 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Tue, 29 Mar 2022 16:36:27 -0300 Subject: [PATCH 27/37] [commons] improvement: accepting 'shell' as an argument for 'new_root_subprocess' --- bauh/commons/system.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bauh/commons/system.py b/bauh/commons/system.py index 83678455..795d85a0 100644 --- a/bauh/commons/system.py +++ b/bauh/commons/system.py @@ -285,10 +285,10 @@ def new_subprocess(cmd: Iterable[str], cwd: str = '.', shell: bool = False, stdi return subprocess.Popen(final_cmd, **args) -def new_root_subprocess(cmd: List[str], root_password: Optional[str], cwd: str = '.', +def new_root_subprocess(cmd: Iterable[str], root_password: Optional[str], cwd: str = '.', global_interpreter: bool = USE_GLOBAL_INTERPRETER, lang: str = DEFAULT_LANG, - extra_paths: Set[str] = None) -> subprocess.Popen: - pwdin, final_cmd = None, [] + extra_paths: Set[str] = None, shell: bool = False) -> subprocess.Popen: + pwdin, final_cmd = subprocess.DEVNULL, [] if isinstance(root_password, str): final_cmd.extend(['sudo', '-S']) @@ -296,7 +296,11 @@ def new_root_subprocess(cmd: List[str], root_password: Optional[str], cwd: str = final_cmd.extend(cmd) - return subprocess.Popen(final_cmd, stdin=pwdin, stdout=PIPE, stderr=PIPE, cwd=cwd, env=gen_env(global_interpreter, lang, extra_paths)) + if shell: + final_cmd = ' '.join(final_cmd) + + return subprocess.Popen(final_cmd, stdin=pwdin, stdout=PIPE, stderr=PIPE, cwd=cwd, + env=gen_env(global_interpreter, lang, extra_paths), shell=shell) def notify_user(msg: str, app_name: str, icon_path: str): From 075559a4376a5c47b81de8b691e9703d53d49df6 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Tue, 29 Mar 2022 17:00:02 -0300 Subject: [PATCH 28/37] [view] fix: ancestor reference to 18n --- bauh/view/qt/thread.py | 102 +++++++++++++++++++---------------------- bauh/view/qt/window.py | 16 +++---- 2 files changed, 54 insertions(+), 64 deletions(-) diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index a692d517..27f59ad1 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -47,8 +47,9 @@ class AsyncAction(QThread, ProcessWatcher): signal_root_password = pyqtSignal() signal_progress_control = pyqtSignal(bool) - def __init__(self, root_password: Optional[Tuple[bool, str]] = None): + def __init__(self, i18n: I18n, root_password: Optional[Tuple[bool, str]] = None): super(AsyncAction, self).__init__() + self.i18n = i18n self.wait_confirmation = False self.confirmation_res = None self.root_password = root_password @@ -129,7 +130,7 @@ def request_reboot(self, msg: str) -> bool: return False - def _generate_backup(self, app_config: dict, i18n: I18n, root_password: Optional[str]) -> bool: + def _generate_backup(self, app_config: dict, root_password: Optional[str]) -> bool: if app_config['backup']['mode'] not in ('only_one', 'incremental'): self.show_message(title=self.i18n['error'].capitalize(), body='{}: {}'.format(self.i18n['action.backup.invalid_mode'],bold(app_config['backup']['mode'])), @@ -139,25 +140,25 @@ def _generate_backup(self, app_config: dict, i18n: I18n, root_password: Optional handler = ProcessHandler(self) if app_config['backup']['mode'] == 'only_one': - self.change_substatus('[{}] {}'.format(i18n['core.config.tab.backup'].lower(), i18n['action.backup.substatus.delete'])) + self.change_substatus('[{}] {}'.format(self.i18n['core.config.tab.backup'].lower(), self.i18n['action.backup.substatus.delete'])) deleted, _ = handler.handle_simple(timeshift.delete_all_snapshots(root_password)) - if not deleted and not self.request_confirmation(title=i18n['core.config.tab.backup'], - body='{}. {}'.format(i18n['action.backup.error.delete'], - i18n['action.backup.error.proceed']), - confirmation_label=i18n['yes'].capitalize(), - deny_label=i18n['no'].capitalize()): + if not deleted and not self.request_confirmation(title=self.i18n['core.config.tab.backup'], + body='{}. {}'.format(self.i18n['action.backup.error.delete'], + self.i18n['action.backup.error.proceed']), + confirmation_label=self.i18n['yes'].capitalize(), + deny_label=self.i18n['no'].capitalize()): self.change_substatus('') return False - self.change_substatus('[{}] {}'.format(i18n['core.config.tab.backup'].lower(), i18n['action.backup.substatus.create'])) + self.change_substatus('[{}] {}'.format(self.i18n['core.config.tab.backup'].lower(), self.i18n['action.backup.substatus.create'])) created, _ = handler.handle_simple(timeshift.create_snapshot(root_password, app_config['backup']['type'])) - if not created and not self.request_confirmation(title=i18n['core.config.tab.backup'], - body='{}. {}'.format(i18n['action.backup.error.create'], - i18n['action.backup.error.proceed']), - confirmation_label=i18n['yes'].capitalize(), - deny_label=i18n['no'].capitalize()): + if not created and not self.request_confirmation(title=self.i18n['core.config.tab.backup'], + body='{}. {}'.format(self.i18n['action.backup.error.create'], + self.i18n['action.backup.error.proceed']), + confirmation_label=self.i18n['yes'].capitalize(), + deny_label=self.i18n['no'].capitalize()): self.change_substatus('') return False @@ -173,24 +174,24 @@ def _check_backup_requirements(self, app_config: dict, pkg: Optional[PackageView return bool(app_config['backup']['enabled']) and timeshift.is_available() - def _should_backup(self, action_key: str, app_config: dict, i18n: I18n) -> bool: + def _should_backup(self, action_key: str, app_config: dict) -> bool: # backup -> true: do not ask, only execute | false: do not ask or execute | None: ask backup = app_config['backup'][action_key] if action_key else None if backup is None: - return self.request_confirmation(title=i18n['core.config.tab.backup'], - body=i18n['action.backup.msg'], - confirmation_label=i18n['yes'].capitalize(), - deny_label=i18n['no'].capitalize()) + return self.request_confirmation(title=self.i18n['core.config.tab.backup'], + body=self.i18n['action.backup.msg'], + confirmation_label=self.i18n['yes'].capitalize(), + deny_label=self.i18n['no'].capitalize()) else: return backup - def request_backup(self, action_key: Optional[str], pkg: Optional[PackageView], i18n: I18n, app_config: dict, root_password: Optional[str], backup_only: bool = False) -> Tuple[bool, Optional[str]]: + def request_backup(self, action_key: Optional[str], pkg: Optional[PackageView], app_config: dict, root_password: Optional[str], backup_only: bool = False) -> Tuple[bool, Optional[str]]: if not backup_only: if not self._check_backup_requirements(app_config=app_config, pkg=pkg, action_key=action_key): return True, root_password - if not self._should_backup(action_key=action_key, app_config=app_config, i18n=i18n): + if not self._should_backup(action_key=action_key, app_config=app_config): return True, root_password pwd = root_password @@ -200,7 +201,7 @@ def request_backup(self, action_key: Optional[str], pkg: Optional[PackageView], if not valid_password: return False, None - return self._generate_backup(app_config=app_config, i18n=i18n, root_password=pwd), pwd + return self._generate_backup(app_config=app_config, root_password=pwd), pwd class UpgradeSelected(AsyncAction): @@ -210,10 +211,9 @@ class UpgradeSelected(AsyncAction): def __init__(self, manager: SoftwareManager, internet_checker: InternetChecker, i18n: I18n, screen_width: int, pkgs: List[PackageView] = None): - super(UpgradeSelected, self).__init__() + super(UpgradeSelected, self).__init__(i18n=i18n) self.pkgs = pkgs self.manager = manager - self.i18n = i18n self.internet_checker = internet_checker self.screen_width = screen_width @@ -491,7 +491,7 @@ def run(self): # backup dialog ( if enabled, supported and accepted ) should_backup = bkp_supported should_backup = should_backup and self._check_backup_requirements(app_config=app_config, pkg=None, action_key='upgrade') - should_backup = should_backup and self._should_backup(action_key='upgrade', app_config=app_config, i18n=self.i18n) + should_backup = should_backup and self._should_backup(action_key='upgrade', app_config=app_config) # trim dialog ( if enabled and accepted ) if app_config['disk']['trim']['after_upgrade'] is not False: @@ -511,7 +511,6 @@ def run(self): if should_backup: proceed, root_password = self.request_backup(action_key='upgrade', app_config=app_config, - i18n=self.i18n, root_password=root_password, pkg=None, backup_only=True) @@ -550,8 +549,8 @@ def run(self): class RefreshApps(AsyncAction): - def __init__(self, manager: SoftwareManager, pkg_types: Set[Type[SoftwarePackage]] = None): - super(RefreshApps, self).__init__() + def __init__(self, i18n: I18n, manager: SoftwareManager, pkg_types: Set[Type[SoftwarePackage]] = None): + super(RefreshApps, self).__init__(i18n=i18n) self.manager = manager self.pkg_types = pkg_types @@ -580,18 +579,16 @@ def run(self): class UninstallPackage(AsyncAction): def __init__(self, manager: SoftwareManager, icon_cache: MemoryCache, i18n: I18n, pkg: PackageView = None): - super(UninstallPackage, self).__init__() + super(UninstallPackage, self).__init__(i18n=i18n) self.pkg = pkg self.manager = manager self.icon_cache = icon_cache self.root_pwd = None - self.i18n = i18n def run(self): if self.pkg: proceed, _ = self.request_backup(action_key='uninstall', pkg=self.pkg, - i18n=self.i18n, root_password=self.root_pwd, app_config=CoreConfigManager().get_config()) if not proceed: @@ -622,10 +619,9 @@ def run(self): class DowngradePackage(AsyncAction): def __init__(self, manager: SoftwareManager, i18n: I18n, pkg: PackageView = None): - super(DowngradePackage, self).__init__() + super(DowngradePackage, self).__init__(i18n=i18n) self.manager = manager self.pkg = pkg - self.i18n = i18n self.root_pwd = None def run(self): @@ -634,7 +630,6 @@ def run(self): proceed, _ = self.request_backup(action_key='downgrade', pkg=self.pkg, - i18n=self.i18n, root_password=self.root_pwd, app_config=CoreConfigManager().get_config()) @@ -657,8 +652,8 @@ def run(self): class ShowPackageInfo(AsyncAction): - def __init__(self, manager: SoftwareManager, pkg: PackageView = None): - super(ShowPackageInfo, self).__init__() + def __init__(self, i18n: I18n, manager: SoftwareManager, pkg: PackageView = None): + super(ShowPackageInfo, self).__init__(i18n=i18n) self.pkg = pkg self.manager = manager @@ -678,10 +673,9 @@ def run(self): class ShowPackageHistory(AsyncAction): def __init__(self, manager: SoftwareManager, i18n: I18n, pkg: PackageView = None): - super(ShowPackageHistory, self).__init__() + super(ShowPackageHistory, self).__init__(i18n=i18n) self.pkg = pkg self.manager = manager - self.i18n = i18n def run(self): if self.pkg: @@ -695,8 +689,8 @@ def run(self): class SearchPackages(AsyncAction): - def __init__(self, manager: SoftwareManager): - super(SearchPackages, self).__init__() + def __init__(self, i18n: I18n, manager: SoftwareManager): + super(SearchPackages, self).__init__(i18n=i18n) self.word = None self.manager = manager @@ -717,11 +711,10 @@ def run(self): class InstallPackage(AsyncAction): def __init__(self, manager: SoftwareManager, icon_cache: MemoryCache, i18n: I18n, pkg: PackageView = None): - super(InstallPackage, self).__init__() + super(InstallPackage, self).__init__(i18n=i18n) self.pkg = pkg self.manager = manager self.icon_cache = icon_cache - self.i18n = i18n self.root_pwd = None def run(self): @@ -730,7 +723,6 @@ def run(self): proceed, _ = self.request_backup(action_key='install', pkg=self.pkg, - i18n=self.i18n, root_password=self.root_pwd, app_config=CoreConfigManager().get_config()) @@ -895,8 +887,8 @@ def run(self): class FindSuggestions(AsyncAction): - def __init__(self, man: SoftwareManager): - super(FindSuggestions, self).__init__() + def __init__(self, i18n: I18n, man: SoftwareManager): + super(FindSuggestions, self).__init__(i18n=i18n) self.man = man self.filter_installed = False @@ -922,8 +914,8 @@ def run(self): class LaunchPackage(AsyncAction): - def __init__(self, manager: SoftwareManager, pkg: PackageView = None): - super(LaunchPackage, self).__init__() + def __init__(self, i18n: I18n, manager: SoftwareManager, pkg: PackageView = None): + super(LaunchPackage, self).__init__(i18n=i18n) self.pkg = pkg self.manager = manager @@ -943,8 +935,8 @@ class ApplyFilters(AsyncAction): signal_table = pyqtSignal(object) - def __init__(self, filters: dict = None, pkgs: List[PackageView] = None): - super(ApplyFilters, self).__init__() + def __init__(self, i18n: I18n, filters: dict = None, pkgs: List[PackageView] = None): + super(ApplyFilters, self).__init__(i18n=i18n) self.pkgs = pkgs self.filters = filters self.wait_table_update = False @@ -979,12 +971,11 @@ def run(self): class CustomAction(AsyncAction): def __init__(self, manager: SoftwareManager, i18n: I18n, custom_action: CustomSoftwareAction = None, pkg: PackageView = None, root_password: Optional[str] = None): - super(CustomAction, self).__init__() + super(CustomAction, self).__init__(i18n=i18n) self.manager = manager self.pkg = pkg self.custom_action = custom_action self.root_pwd = root_password - self.i18n = i18n def run(self): res = {'success': False, 'pkg': self.pkg, 'action': self.custom_action, 'error': None, 'error_type': MessageType.ERROR} @@ -992,7 +983,6 @@ def run(self): if self.custom_action.backup: proceed, _ = self.request_backup(app_config=CoreConfigManager().get_config(), action_key=None, - i18n=self.i18n, root_password=self.root_pwd, pkg=self.pkg) if not proceed: @@ -1021,8 +1011,8 @@ def run(self): class ShowScreenshots(AsyncAction): - def __init__(self, manager: SoftwareManager, pkg: PackageView = None): - super(ShowScreenshots, self).__init__() + def __init__(self, i18n: I18n, manager: SoftwareManager, pkg: PackageView = None): + super(ShowScreenshots, self).__init__(i18n=i18n) self.pkg = pkg self.manager = manager @@ -1035,8 +1025,8 @@ def run(self): class IgnorePackageUpdates(AsyncAction): - def __init__(self, manager: SoftwareManager, pkg: PackageView = None): - super(IgnorePackageUpdates, self).__init__() + def __init__(self, i18n: I18n, manager: SoftwareManager, pkg: PackageView = None): + super(IgnorePackageUpdates, self).__init__(i18n=i18n) self.pkg = pkg self.manager = manager diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index 6cf37bf3..23d6cbc3 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -331,18 +331,18 @@ def __init__(self, i18n: I18n, icon_cache: MemoryCache, manager: SoftwareManager internet_checker=context.internet_checker, screen_width=screen_size.width()), finished_call=self._finish_upgrade_selected) - self.thread_refresh = self._bind_async_action(RefreshApps(self.manager), finished_call=self._finish_refresh_packages, only_finished=True) + self.thread_refresh = self._bind_async_action(RefreshApps(i18n, self.manager), finished_call=self._finish_refresh_packages, only_finished=True) self.thread_uninstall = self._bind_async_action(UninstallPackage(self.manager, self.icon_cache, self.i18n), finished_call=self._finish_uninstall) - self.thread_show_info = self._bind_async_action(ShowPackageInfo(self.manager), finished_call=self._finish_show_info) + self.thread_show_info = self._bind_async_action(ShowPackageInfo(i18n, self.manager), finished_call=self._finish_show_info) self.thread_show_history = self._bind_async_action(ShowPackageHistory(self.manager, self.i18n), finished_call=self._finish_show_history) - self.thread_search = self._bind_async_action(SearchPackages(self.manager), finished_call=self._finish_search, only_finished=True) + self.thread_search = self._bind_async_action(SearchPackages(i18n, self.manager), finished_call=self._finish_search, only_finished=True) self.thread_downgrade = self._bind_async_action(DowngradePackage(self.manager, self.i18n), finished_call=self._finish_downgrade) - self.thread_suggestions = self._bind_async_action(FindSuggestions(man=self.manager), finished_call=self._finish_load_suggestions, only_finished=True) - self.thread_launch = self._bind_async_action(LaunchPackage(self.manager), finished_call=self._finish_launch_package, only_finished=False) + self.thread_suggestions = self._bind_async_action(FindSuggestions(i18n=i18n, man=self.manager), finished_call=self._finish_load_suggestions, only_finished=True) + self.thread_launch = self._bind_async_action(LaunchPackage(i18n, self.manager), finished_call=self._finish_launch_package, only_finished=False) self.thread_custom_action = self._bind_async_action(CustomAction(manager=self.manager, i18n=self.i18n), finished_call=self._finish_execute_custom_action) - self.thread_screenshots = self._bind_async_action(ShowScreenshots(self.manager), finished_call=self._finish_show_screenshots) + self.thread_screenshots = self._bind_async_action(ShowScreenshots(i18n, self.manager), finished_call=self._finish_show_screenshots) - self.thread_apply_filters = ApplyFilters() + self.thread_apply_filters = ApplyFilters(i18n) self.thread_apply_filters.signal_finished.connect(self._finish_apply_filters) self.thread_apply_filters.signal_table.connect(self._update_table_and_upgrades) self.signal_table_update.connect(self.thread_apply_filters.stop_waiting) @@ -358,7 +358,7 @@ def __init__(self, i18n: I18n, icon_cache: MemoryCache, manager: SoftwareManager self.thread_notify_pkgs_ready.signal_finished.connect(self._update_state_when_pkgs_ready) self.signal_stop_notifying.connect(self.thread_notify_pkgs_ready.stop_working) - self.thread_ignore_updates = IgnorePackageUpdates(manager=self.manager) + self.thread_ignore_updates = IgnorePackageUpdates(i18n=i18n, manager=self.manager) self._bind_async_action(self.thread_ignore_updates, finished_call=self.finish_ignore_updates) self.thread_reload = StartAsyncAction(delay_in_milis=5) From da8a4e6547a557f52a0c161a776fe5532fb3ddbd Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Tue, 29 Mar 2022 17:44:00 -0300 Subject: [PATCH 29/37] [backup] improvement: 'single mode' should only remove self generated snapshots --- CHANGELOG.md | 2 ++ README.md | 2 +- bauh/view/core/timeshift.py | 28 +++++++++++++++++++++++++--- bauh/view/qt/thread.py | 32 +++++++++++++++++++++----------- bauh/view/resources/locale/ca | 2 +- bauh/view/resources/locale/de | 2 +- bauh/view/resources/locale/en | 2 +- bauh/view/resources/locale/es | 2 +- bauh/view/resources/locale/it | 2 +- bauh/view/resources/locale/pt | 2 +- bauh/view/resources/locale/ru | 2 +- 11 files changed, 56 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 771c1c00..498c05bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Improvements - General - code refactoring + - backup: + - single mode: only removing self generated snapshots (now the snapshots are associated with the comment "") [#244](https://github.com/vinifmor/bauh/issues/244) - Arch - text length of some popups reduced diff --git a/README.md b/README.md index bb1d78ba..778acb8d 100644 --- a/README.md +++ b/README.md @@ -438,7 +438,7 @@ disk: after_upgrade: false # it trims the disk after a successful packages upgrade (`fstrim -a -v`). 'true' will automatically perform the trim and 'null' will display a confirmation dialog backup: enabled: true # generate timeshift snapshots before an action (if timeshift is installed on the system) - mode: 'incremental' # incremental=generates a new snapshot based on another pre-exising one. 'only_one'=deletes all pre-existing snapshots and generates a fresh one. + mode: 'incremental' # incremental=generates a new snapshot based on another pre-exising one. 'only_one'=deletes all pre-existing self created snapshots and generates a fresh one. install: null # defines if the backup should be performed before installing a package. Allowed values: null (a dialog will be displayed asking if a snapshot should be generated), true: generates the backup without asking. false: disables the backup for this operation uninstall: null # defines if the backup should be performed before uninstalling a package. Allowed values: null (a dialog will be displayed asking if a snapshot should be generated), true: generates the backup without asking. false: disables the backup for this operation upgrade: null # defines if the backup should be performed before upgrading a package. Allowed values: null (a dialog will be displayed asking if a snapshot should be generated), true: generates the backup without asking. false: disables the backup for this operation diff --git a/bauh/view/core/timeshift.py b/bauh/view/core/timeshift.py index e2d4ad73..e0e551b8 100644 --- a/bauh/view/core/timeshift.py +++ b/bauh/view/core/timeshift.py @@ -1,7 +1,11 @@ +import re import shutil -from typing import Optional +from typing import Optional, Generator -from bauh.commons.system import SimpleProcess +from bauh import __app_name__ +from bauh.commons.system import SimpleProcess, new_root_subprocess + +RE_SNAPSHOTS = re.compile(r'\d+\s+>\s+([\w\-_]+)\s+.+<{}>'.format(__app_name__)) def is_available() -> bool: @@ -12,5 +16,23 @@ def delete_all_snapshots(root_password: Optional[str]) -> SimpleProcess: return SimpleProcess(['timeshift', '--delete-all', '--scripted'], root_password=root_password) +def delete(snapshot_name: str, root_password: Optional[str]) -> SimpleProcess: + return SimpleProcess(('timeshift', '--delete', '--snapshot', snapshot_name), + shell=True, root_password=root_password) + + def create_snapshot(root_password: Optional[str], mode: str) -> SimpleProcess: - return SimpleProcess(['timeshift', '--create', '--scripted', '--{}'.format(mode)], root_password=root_password) + return SimpleProcess(('timeshift', '--create', '--scripted', f'--{mode}', '--comments', f'<{__app_name__}>'), + root_password=root_password) + + +def read_created_snapshots(root_password: Optional[str]) -> Generator[str, None, None]: + proc = new_root_subprocess(cmd=('timeshift', '--list'), root_password=root_password, shell=True) + proc.wait() + + if proc.returncode == 0: + output = '\n'.join((o.decode() for o in proc.stdout)) + + if output: + for name in RE_SNAPSHOTS.findall(output): + yield name diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index 27f59ad1..495ddf55 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta from io import StringIO from pathlib import Path -from typing import List, Type, Set, Tuple, Optional, Iterable +from typing import List, Type, Set, Tuple, Optional import requests from PyQt5.QtCore import QThread, pyqtSignal, QObject @@ -140,16 +140,26 @@ def _generate_backup(self, app_config: dict, root_password: Optional[str]) -> bo handler = ProcessHandler(self) if app_config['backup']['mode'] == 'only_one': - self.change_substatus('[{}] {}'.format(self.i18n['core.config.tab.backup'].lower(), self.i18n['action.backup.substatus.delete'])) - deleted, _ = handler.handle_simple(timeshift.delete_all_snapshots(root_password)) - - if not deleted and not self.request_confirmation(title=self.i18n['core.config.tab.backup'], - body='{}. {}'.format(self.i18n['action.backup.error.delete'], - self.i18n['action.backup.error.proceed']), - confirmation_label=self.i18n['yes'].capitalize(), - deny_label=self.i18n['no'].capitalize()): - self.change_substatus('') - return False + previous_snapshots = tuple(timeshift.read_created_snapshots(root_password)) + + if previous_snapshots: + substatus = f"[{self.i18n['core.config.tab.backup'].lower()}] {self.i18n['action.backup.substatus.delete']}" + self.change_substatus(substatus) + + delete_failed = False + for snapshot in reversed(previous_snapshots): + deleted, _ = handler.handle_simple(timeshift.delete(snapshot, root_password)) + + if not deleted: + delete_failed = True + + if delete_failed and not self.request_confirmation(title=self.i18n['core.config.tab.backup'], + body=f"{self.i18n['action.backup.error.delete']}. " + f"{self.i18n['action.backup.error.proceed']}", + confirmation_label=self.i18n['yes'].capitalize(), + deny_label=self.i18n['no'].capitalize()): + self.change_substatus('') + return False self.change_substatus('[{}] {}'.format(self.i18n['core.config.tab.backup'].lower(), self.i18n['action.backup.substatus.create'])) created, _ = handler.handle_simple(timeshift.create_snapshot(root_password, app_config['backup']['type'])) diff --git a/bauh/view/resources/locale/ca b/bauh/view/resources/locale/ca index 87b6267e..854eebf2 100644 --- a/bauh/view/resources/locale/ca +++ b/bauh/view/resources/locale/ca @@ -45,7 +45,7 @@ Ukraine=Ukraine United Kingdom=United Kingdom United States=United States action.backup.error.create=It was not possible to generate a new system copy -action.backup.error.delete=It was not possible to delete the old system copies +action.backup.error.delete=It was not possible to delete all the old system copies action.backup.error.proceed=Proceed anyway ? action.backup.invalid_mode=Invalid backup mode action.backup.msg=Do you want to generate a system copy before proceeding ? diff --git a/bauh/view/resources/locale/de b/bauh/view/resources/locale/de index aac43a58..551a2385 100644 --- a/bauh/view/resources/locale/de +++ b/bauh/view/resources/locale/de @@ -45,7 +45,7 @@ Ukraine=Ukraine United Kingdom=United Kingdom United States=United States action.backup.error.create=It was not possible to generate a new system copy -action.backup.error.delete=It was not possible to delete the old system copies +action.backup.error.delete=It was not possible to delete all the old system copies action.backup.error.proceed=Proceed anyway ? action.backup.invalid_mode=Invalid backup mode action.backup.msg=Do you want to generate a system copy before proceeding ? diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index bb4e9893..537d7aa4 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -45,7 +45,7 @@ Ukraine=Ukraine United Kingdom=United Kingdom United States=United States action.backup.error.create=It was not possible to generate a new system copy -action.backup.error.delete=It was not possible to delete the old system copies +action.backup.error.delete=It was not possible to delete all the old system copies action.backup.error.proceed=Proceed anyway ? action.backup.invalid_mode=Invalid backup mode action.backup.msg=Do you want to generate a system copy before proceeding ? diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index 6278a542..2312ba60 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -45,7 +45,7 @@ Ukraine=Ucrania United Kingdom=Reino Unido United States=Estados Unidos action.backup.error.create=No fue posible generar una nueva copia de seguridad -action.backup.error.delete=No fue posible eliminar las copias de seguridad antiguas del sistema +action.backup.error.delete=No fue posible eliminar todas las copias de seguridad antiguas del sistema action.backup.error.proceed=¿Continuar de todos modos? action.backup.invalid_mode=Modo de copia de seguridad inválido action.backup.msg=¿Desea generar una copia de seguridad del sistema antes de continuar? diff --git a/bauh/view/resources/locale/it b/bauh/view/resources/locale/it index be84a0d1..4c28603f 100644 --- a/bauh/view/resources/locale/it +++ b/bauh/view/resources/locale/it @@ -45,7 +45,7 @@ Ukraine=Ukraine United Kingdom=United Kingdom United States=United States action.backup.error.create=It was not possible to generate a new system copy -action.backup.error.delete=It was not possible to delete the old system copies +action.backup.error.delete=It was not possible to delete all the old system copies action.backup.error.proceed=Proceed anyway ? action.backup.invalid_mode=Invalid backup mode action.backup.msg=Do you want to generate a system copy before proceeding ? diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index 2ff8566a..25a6276f 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -45,7 +45,7 @@ Ukraine=Ucrânia United Kingdom=Reino Unido United States=Estados Unidos action.backup.error.create=Não foi possível gerar uma nova cópia de segurança do sistema -action.backup.error.delete=Não foi possível remover as cópias de segurança antigas do sistema +action.backup.error.delete=Não foi possível remover todas as cópias de segurança antigas do sistema action.backup.error.proceed=Continuar mesmo assim ? action.backup.invalid_mode=Modo de cópia de segurança inválido action.backup.msg=Você deseja gerar uma cópia de segurança do sistema antes de proceder ? diff --git a/bauh/view/resources/locale/ru b/bauh/view/resources/locale/ru index d9a834e7..b0234230 100644 --- a/bauh/view/resources/locale/ru +++ b/bauh/view/resources/locale/ru @@ -45,7 +45,7 @@ Ukraine=Украина United Kingdom=Великобритания United States=Соединённые Штаты Америки action.backup.error.create=It was not possible to generate a new system copy -action.backup.error.delete=It was not possible to delete the old system copies +action.backup.error.delete=It was not possible to delete all the old system copies action.backup.error.proceed=Proceed anyway ? action.backup.invalid_mode=Invalid backup mode action.backup.msg=Do you want to generate a system copy before proceeding ? From a904c73b2d79d11def80989c7d1f78f6d5f2fd34 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Tue, 29 Mar 2022 19:01:31 -0300 Subject: [PATCH 30/37] [backup] improvement: remove_method option --- CHANGELOG.md | 8 +++++- README.md | 1 + bauh/commons/view_utils.py | 5 ++-- bauh/view/core/config.py | 6 +++- bauh/view/core/settings.py | 27 ++++++++++++++++-- bauh/view/qt/thread.py | 52 +++++++++++++++++++++-------------- bauh/view/resources/locale/ca | 5 ++++ bauh/view/resources/locale/de | 5 ++++ bauh/view/resources/locale/en | 5 ++++ bauh/view/resources/locale/es | 5 ++++ bauh/view/resources/locale/fr | 5 ++++ bauh/view/resources/locale/it | 5 ++++ bauh/view/resources/locale/pt | 5 ++++ bauh/view/resources/locale/ru | 5 ++++ bauh/view/resources/locale/tr | 5 ++++ 15 files changed, 115 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 498c05bb..fa3a6a27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - General - code refactoring - backup: - - single mode: only removing self generated snapshots (now the snapshots are associated with the comment "") [#244](https://github.com/vinifmor/bauh/issues/244) + - single mode: now supports two remove methods [#244](https://github.com/vinifmor/bauh/issues/244) + - self: it removes only self generated backups/snapshots (default) + - all: it removes all existing backup/snapshots on the disc + +

+ +

- Arch - text length of some popups reduced diff --git a/README.md b/README.md index 778acb8d..faa24bac 100644 --- a/README.md +++ b/README.md @@ -444,6 +444,7 @@ backup: upgrade: null # defines if the backup should be performed before upgrading a package. Allowed values: null (a dialog will be displayed asking if a snapshot should be generated), true: generates the backup without asking. false: disables the backup for this operation downgrade: null # defines if the backup should be performed before downgrading a package. Allowed values: null (a dialog will be displayed asking if a snapshot should be generated), true: generates the backup without asking. false: disables the backup for this operation type: rsync # defines the Timeshift backup mode -> 'rsync' (default) or 'btrfs' + remove_method: self # define which backups should be removed in the 'only_one' mode. 'self': only self generated copies. 'all': all existing backups on the disc. boot: load_apps: true # if the installed applications or suggestions should be loaded on the management panel after the initialization process. Default: true. ``` diff --git a/bauh/commons/view_utils.py b/bauh/commons/view_utils.py index 1ade21ae..3d0791f0 100644 --- a/bauh/commons/view_utils.py +++ b/bauh/commons/view_utils.py @@ -1,13 +1,12 @@ -from typing import List, Tuple, Optional +from typing import Tuple, Optional, Iterable from bauh.api.abstract.view import SelectViewType, InputOption, SingleSelectComponent - SIZE_UNITS = ((1, 'B'), (1024, 'Kb'), (1048576, 'Mb'), (1073741824, 'Gb'), (1099511627776, 'Tb'), (1125899906842624, 'Pb')) -def new_select(label: str, tip: str, id_: str, opts: List[Tuple[Optional[str], object, Optional[str]]], value: object, max_width: int, +def new_select(label: str, tip: Optional[str], id_: str, opts: Iterable[Tuple[Optional[str], object, Optional[str]]], value: object, max_width: int, type_: SelectViewType = SelectViewType.RADIO, capitalize_label: bool = True): inp_opts = [InputOption(label=o[0].capitalize(), value=o[1], tooltip=o[2]) for o in opts] def_opt = [o for o in inp_opts if o.value == value] diff --git a/bauh/view/core/config.py b/bauh/view/core/config.py index a9fa7e91..e9c7c5d8 100644 --- a/bauh/view/core/config.py +++ b/bauh/view/core/config.py @@ -3,6 +3,9 @@ FILE_PATH = f'{CONFIG_DIR}/config.yml' +BACKUP_DEFAULT_REMOVE_METHOD = 'self' +BACKUP_REMOVE_METHODS = {BACKUP_DEFAULT_REMOVE_METHOD, 'all'} + class CoreConfigManager(YAMLConfigManager): @@ -63,7 +66,8 @@ def get_default_config(self) -> dict: 'downgrade': None, 'upgrade': None, 'mode': 'incremental', - 'type': 'rsync' + 'type': 'rsync', + 'remove_method': 'self' }, 'boot': { 'load_apps': True diff --git a/bauh/view/core/settings.py b/bauh/view/core/settings.py index 88820a55..561bf12c 100644 --- a/bauh/view/core/settings.py +++ b/bauh/view/core/settings.py @@ -16,7 +16,7 @@ FileChooserComponent, RangeInputComponent from bauh.commons.view_utils import new_select from bauh.view.core import timeshift -from bauh.view.core.config import CoreConfigManager +from bauh.view.core.config import CoreConfigManager, BACKUP_REMOVE_METHODS, BACKUP_DEFAULT_REMOVE_METHOD from bauh.view.core.downloader import AdaptableFileDownloader from bauh.view.util import translation from bauh.view.util.translation import I18n @@ -327,7 +327,7 @@ def _gen_general_settings(self, core_config: dict, screen_width: int, screen_hei sub_comps = [FormComponent([select_locale, select_store_pwd, select_sysnotify, select_load_apps, select_sugs, inp_sugs, inp_reboot], spaces=False)] return TabComponent(self.i18n['core.config.tab.general'].capitalize(), PanelComponent(sub_comps), None, 'core.gen') - def _gen_bool_component(self, label: str, tooltip: str, value: bool, id_: str, max_width: int = 200) -> SingleSelectComponent: + def _gen_bool_component(self, label: str, tooltip: Optional[str], value: bool, id_: str, max_width: int = 200) -> SingleSelectComponent: opts = [InputOption(label=self.i18n['yes'].capitalize(), value=True), InputOption(label=self.i18n['no'].capitalize(), value=False)] @@ -397,6 +397,7 @@ def _save_settings(self, general: PanelComponent, core_config['backup']['enabled'] = bkp_form.get_component('enabled').get_selected() core_config['backup']['mode'] = bkp_form.get_component('mode').get_selected() core_config['backup']['type'] = bkp_form.get_component('type').get_selected() + core_config['backup']['remove_method'] = bkp_form.get_component('remove_method').get_selected() core_config['backup']['install'] = bkp_form.get_component('install').get_selected() core_config['backup']['uninstall'] = bkp_form.get_component('uninstall').get_selected() core_config['backup']['upgrade'] = bkp_form.get_component('upgrade').get_selected() @@ -580,5 +581,25 @@ def _gen_backup_settings(self, core_config: dict, screen_width: int, screen_heig max_width=default_width, id_='type') - sub_comps = [FormComponent([enabled_opt, mode, type_, install_mode, uninstall_mode, upgrade_mode, downgrade_mode], spaces=False)] + remove_method = core_config['backup']['remove_method'] + + if not remove_method or remove_method not in BACKUP_REMOVE_METHODS: + remove_method = BACKUP_DEFAULT_REMOVE_METHOD + + remove_i18n = 'core.config.backup.remove_method' + remove_opts = ((self.i18n[f'{remove_i18n}.{m}'], m, self.i18n[f'{remove_i18n}.{m}.tip']) + for m in sorted(BACKUP_REMOVE_METHODS)) + + remove_label = f'{self.i18n[remove_i18n]} ({self.i18n["core.config.backup.mode"]} ' \ + f'"{self.i18n["core.config.backup.mode.only_one"].capitalize()}")' + + sel_remove = new_select(label=remove_label, + tip=None, + value=remove_method, + opts=remove_opts, + max_width=default_width, + capitalize_label=False, + id_='remove_method') + + sub_comps = [FormComponent([enabled_opt, type_, mode, sel_remove, install_mode, uninstall_mode, upgrade_mode, downgrade_mode], spaces=False)] return TabComponent(self.i18n['core.config.tab.backup'].capitalize(), PanelComponent(sub_comps), None, 'core.bkp') diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index 495ddf55..42a65d8b 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -26,7 +26,7 @@ from bauh.commons.system import ProcessHandler, SimpleProcess from bauh.commons.view_utils import get_human_size_str from bauh.view.core import timeshift -from bauh.view.core.config import CoreConfigManager +from bauh.view.core.config import CoreConfigManager, BACKUP_REMOVE_METHODS, BACKUP_DEFAULT_REMOVE_METHOD from bauh.view.qt import commons from bauh.view.qt.commons import sort_packages from bauh.view.qt.view_model import PackageView, PackageViewStatus @@ -140,26 +140,36 @@ def _generate_backup(self, app_config: dict, root_password: Optional[str]) -> bo handler = ProcessHandler(self) if app_config['backup']['mode'] == 'only_one': - previous_snapshots = tuple(timeshift.read_created_snapshots(root_password)) - - if previous_snapshots: - substatus = f"[{self.i18n['core.config.tab.backup'].lower()}] {self.i18n['action.backup.substatus.delete']}" - self.change_substatus(substatus) - - delete_failed = False - for snapshot in reversed(previous_snapshots): - deleted, _ = handler.handle_simple(timeshift.delete(snapshot, root_password)) - - if not deleted: - delete_failed = True - - if delete_failed and not self.request_confirmation(title=self.i18n['core.config.tab.backup'], - body=f"{self.i18n['action.backup.error.delete']}. " - f"{self.i18n['action.backup.error.proceed']}", - confirmation_label=self.i18n['yes'].capitalize(), - deny_label=self.i18n['no'].capitalize()): - self.change_substatus('') - return False + remove_method = app_config['backup']['remove_method'] + + if remove_method not in BACKUP_REMOVE_METHODS: + remove_method = BACKUP_DEFAULT_REMOVE_METHOD + + delete_failed = False + + if remove_method == 'self': + previous_snapshots = tuple(timeshift.read_created_snapshots(root_password)) + + if previous_snapshots: + substatus = f"[{self.i18n['core.config.tab.backup'].lower()}] {self.i18n['action.backup.substatus.delete']}" + self.change_substatus(substatus) + + for snapshot in reversed(previous_snapshots): + deleted, _ = handler.handle_simple(timeshift.delete(snapshot, root_password)) + + if not deleted: + delete_failed = True + else: + deleted, _ = handler.handle_simple(timeshift.delete_all_snapshots(root_password)) + delete_failed = not deleted + + if delete_failed and not self.request_confirmation(title=self.i18n['core.config.tab.backup'], + body=f"{self.i18n['action.backup.error.delete']}. " + f"{self.i18n['action.backup.error.proceed']}", + confirmation_label=self.i18n['yes'].capitalize(), + deny_label=self.i18n['no'].capitalize()): + self.change_substatus('') + return False self.change_substatus('[{}] {}'.format(self.i18n['core.config.tab.backup'].lower(), self.i18n['action.backup.substatus.create'])) created, _ = handler.handle_simple(timeshift.create_snapshot(root_password, app_config['backup']['type'])) diff --git a/bauh/view/resources/locale/ca b/bauh/view/resources/locale/ca index 854eebf2..59944115 100644 --- a/bauh/view/resources/locale/ca +++ b/bauh/view/resources/locale/ca @@ -211,6 +211,11 @@ core.config.backup.mode.incremental=Incremental core.config.backup.mode.incremental.tip=A new system backup will be generated containing only the changed files since the latest copy. core.config.backup.mode.only_one=Single core.config.backup.mode.only_one.tip=Only one system backup will be kept. Pre-existing backups will be erased. +core.config.backup.remove_method=Remove +core.config.backup.remove_method.self=Only generated +core.config.backup.remove_method.self.tip=It removes only the self generated backups +core.config.backup.remove_method.all=All +core.config.backup.remove_method.all.tip=It removes all existing backups on the disc core.config.backup.uninstall=Before uninstalling core.config.backup.upgrade=Before upgrading core.config.boot.load_apps=Load apps after startup diff --git a/bauh/view/resources/locale/de b/bauh/view/resources/locale/de index 551a2385..b08ec3bf 100644 --- a/bauh/view/resources/locale/de +++ b/bauh/view/resources/locale/de @@ -210,6 +210,11 @@ core.config.backup.mode.incremental=Incremental core.config.backup.mode.incremental.tip=A new system backup will be generated containing only the changed files since the latest copy. core.config.backup.mode.only_one=Single core.config.backup.mode.only_one.tip=Only one system backup will be kept. Pre-existing backups will be erased. +core.config.backup.remove_method=Remove +core.config.backup.remove_method.self=Only generated +core.config.backup.remove_method.self.tip=It removes only the self generated backups +core.config.backup.remove_method.all=All +core.config.backup.remove_method.all.tip=It removes all existing backups on the disc core.config.backup.uninstall=Before uninstalling core.config.backup.upgrade=Before upgrading core.config.boot.load_apps=Load apps after startup diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index 537d7aa4..f68805bb 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -211,6 +211,11 @@ core.config.backup.mode.incremental=Incremental core.config.backup.mode.only_one.tip=Only one system backup will be kept. Pre-existing backups will be erased. core.config.backup.mode.only_one=Single core.config.backup.mode=Mode +core.config.backup.remove_method=Remove +core.config.backup.remove_method.self=Only generated +core.config.backup.remove_method.self.tip=It removes only the self generated backups +core.config.backup.remove_method.all=All +core.config.backup.remove_method.all.tip=It removes all existing backups on the disc core.config.backup.uninstall=Before uninstalling core.config.backup.upgrade=Before upgrading core.config.backup=Enabled diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index 2312ba60..73d9e6c1 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -211,6 +211,11 @@ core.config.backup.mode.incremental=Incremental core.config.backup.mode.incremental.tip=Se generará una nueva copia de seguridad del sistema que contenga solo los archivos modificados desde la última copia. core.config.backup.mode.only_one=Única core.config.backup.mode.only_one.tip=Solo se guardará una copia de seguridad del sistema. Las copias preexistentes serán borradas. +core.config.backup.remove_method=Eliminar +core.config.backup.remove_method.self=Solo generadas +core.config.backup.remove_method.self.tip=Elimina solo las copias generadas por la aplicación +core.config.backup.remove_method.all=Todas +core.config.backup.remove_method.all.tip=Elimina todas las copias de seguridad existentes en el disco core.config.backup.uninstall=Antes de desinstalar core.config.backup.upgrade=Antes de actualizar core.config.boot.load_apps=Cargar aplicaciones al inicio diff --git a/bauh/view/resources/locale/fr b/bauh/view/resources/locale/fr index 1aa701fe..3065098f 100644 --- a/bauh/view/resources/locale/fr +++ b/bauh/view/resources/locale/fr @@ -209,6 +209,11 @@ core.config.backup.mode.incremental=Incrémental core.config.backup.mode.only_one.tip=Une seule sauvegarde sera conservée. Les précédantes seront écrasées core.config.backup.mode.only_one=Seul core.config.backup.mode=Mode +core.config.backup.remove_method=Remove +core.config.backup.remove_method.self=Only generated +core.config.backup.remove_method.self.tip=It removes only the self generated backups +core.config.backup.remove_method.all=All +core.config.backup.remove_method.all.tip=It removes all existing backups on the disc core.config.backup.uninstall=Avant de désinstaller core.config.backup.upgrade=Avant de mettre à jour core.config.backup=Activé diff --git a/bauh/view/resources/locale/it b/bauh/view/resources/locale/it index 4c28603f..c629bbd7 100644 --- a/bauh/view/resources/locale/it +++ b/bauh/view/resources/locale/it @@ -210,6 +210,11 @@ core.config.backup.mode.incremental=Incremental core.config.backup.mode.incremental.tip=A new system backup will be generated containing only the changed files since the latest copy. core.config.backup.mode.only_one=Single core.config.backup.mode.only_one.tip=Only one system backup will be kept. Pre-existing backups will be erased. +core.config.backup.remove_method=Remove +core.config.backup.remove_method.self=Only generated +core.config.backup.remove_method.self.tip=It removes only the self generated backups +core.config.backup.remove_method.all=All +core.config.backup.remove_method.all.tip=It removes all existing backups on the disc core.config.backup.uninstall=Before uninstalling core.config.backup.upgrade=Before upgrading core.config.boot.load_apps=Load apps after startup diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index 25a6276f..aa011ea6 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -209,6 +209,11 @@ core.config.backup.mode.incremental=Incremental core.config.backup.mode.only_one.tip=Somente uma cópia de segurança do sistema será mantida. Pré-existentes serão apagadas core.config.backup.mode.only_one=Única core.config.backup.mode=Modo +core.config.backup.remove_method=Remover +core.config.backup.remove_method.self=Somente geradas +core.config.backup.remove_method.self.tip=Remove somente as cópia geradas pela aplicação +core.config.backup.remove_method.all=Todas +core.config.backup.remove_method.all.tip=Remove todas as cópias existentes no disco core.config.backup.uninstall=Antes de desinstalar core.config.backup.upgrade=Antes de atualizar core.config.backup=Habilitada diff --git a/bauh/view/resources/locale/ru b/bauh/view/resources/locale/ru index b0234230..89c13d63 100644 --- a/bauh/view/resources/locale/ru +++ b/bauh/view/resources/locale/ru @@ -210,6 +210,11 @@ core.config.backup.mode.incremental=Incremental core.config.backup.mode.incremental.tip=A new system backup will be generated containing only the changed files since the latest copy. core.config.backup.mode.only_one=Single core.config.backup.mode.only_one.tip=Only one system backup will be kept. Pre-existing backups will be erased. +core.config.backup.remove_method=Remove +core.config.backup.remove_method.self=Only generated +core.config.backup.remove_method.self.tip=It removes only the self generated backups +core.config.backup.remove_method.all=All +core.config.backup.remove_method.all.tip=It removes all existing backups on the disc core.config.backup.uninstall=Before uninstalling core.config.backup.upgrade=Before upgrading core.config.boot.load_apps=Load apps after startup diff --git a/bauh/view/resources/locale/tr b/bauh/view/resources/locale/tr index f8e92e3a..e09c5703 100644 --- a/bauh/view/resources/locale/tr +++ b/bauh/view/resources/locale/tr @@ -209,6 +209,11 @@ core.config.backup.mode.incremental=Artan core.config.backup.mode.only_one.tip=Yalnızca bir sistem yedeklemesi tutulur. Önceden var olan yedeklemeler silinecek. core.config.backup.mode.only_one=Tekil core.config.backup.mode=Mod +core.config.backup.remove_method=Remove +core.config.backup.remove_method.self=Only generated +core.config.backup.remove_method.self.tip=It removes only the self generated backups +core.config.backup.remove_method.all=All +core.config.backup.remove_method.all.tip=It removes all existing backups on the disc core.config.backup.uninstall=Önce kaldırılıyor core.config.backup.upgrade=Önce yükseltiliyor core.config.backup=Etkin From cd32286cc3ebb868e662d9ed89321868a0d4bbec Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 30 Mar 2022 14:07:00 -0300 Subject: [PATCH 31/37] [tests] fix: broken test case --- tests/gems/debian/test_aptitude.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/gems/debian/test_aptitude.py b/tests/gems/debian/test_aptitude.py index 37ba5385..a8a5a93a 100644 --- a/tests/gems/debian/test_aptitude.py +++ b/tests/gems/debian/test_aptitude.py @@ -35,8 +35,7 @@ def test_search__must_return_installed_and_not_installed_packages_with_updates(s query = 'gimp' res = [p for p in self.aptitude.search(query=query)] - execute.assert_called_once_with(f"aptitude search {query} -q -F '%p^%v^%V^%m^%s^%d' --disable-columns", - shell=True, custom_env=system.gen_env(USE_GLOBAL_INTERPRETER, lang='')) + execute.assert_called_once_with(f"aptitude search {query} -q -F '%p^%v^%V^%m^%s^%d' --disable-columns", shell=True) exp = [ DebianPackage(name='gimp-cbmplugs', version='1.2.2-1build1', latest_version='1.2.2-1build1', From 6d23916177cb81eee1b87208b9e494c4513dae92 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 30 Mar 2022 14:08:20 -0300 Subject: [PATCH 32/37] [timeshift] refactoring: using tuple instead of list --- bauh/view/core/timeshift.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bauh/view/core/timeshift.py b/bauh/view/core/timeshift.py index e0e551b8..2ed1f99a 100644 --- a/bauh/view/core/timeshift.py +++ b/bauh/view/core/timeshift.py @@ -13,7 +13,7 @@ def is_available() -> bool: def delete_all_snapshots(root_password: Optional[str]) -> SimpleProcess: - return SimpleProcess(['timeshift', '--delete-all', '--scripted'], root_password=root_password) + return SimpleProcess(('timeshift', '--delete-all', '--scripted'), root_password=root_password) def delete(snapshot_name: str, root_password: Optional[str]) -> SimpleProcess: From 889a30e2e81ee6dd1614fcf01ac03d18713c6b43 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 30 Mar 2022 16:20:24 -0300 Subject: [PATCH 33/37] [view] improvement: no margin for wrapped subcomponents of FormComponent --- CHANGELOG.md | 1 + bauh/view/qt/components.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa3a6a27..514c86e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - UI - only displaying the "Installed" filter when installed packages are available on the table + - settings: margin between components reduced [#241](https://github.com/vinifmor/bauh/issues/241) ### Fixes - Arch diff --git a/bauh/view/qt/components.py b/bauh/view/qt/components.py index a3fbdd23..8170f559 100644 --- a/bauh/view/qt/components.py +++ b/bauh/view/qt/components.py @@ -865,6 +865,7 @@ def _update_value(): def _wrap(self, comp: QWidget, model: ViewComponent) -> QWidget: field_container = QWidget() field_container.setLayout(QHBoxLayout()) + field_container.layout().setContentsMargins(0, 0, 0, 0) if model.max_width > 0: field_container.setMaximumWidth(int(model.max_width)) From 45c586ed4bba29ec01f628248d8e16ab6fbb58ab Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 30 Mar 2022 17:14:46 -0300 Subject: [PATCH 34/37] [view] improvement: allowing max_width to be defined for the confirmation dialogs --- bauh/api/abstract/handler.py | 4 +++- bauh/view/qt/dialog.py | 5 ++++- bauh/view/qt/thread.py | 5 +++-- bauh/view/qt/window.py | 3 ++- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/bauh/api/abstract/handler.py b/bauh/api/abstract/handler.py index ef766d42..393ede9c 100644 --- a/bauh/api/abstract/handler.py +++ b/bauh/api/abstract/handler.py @@ -21,7 +21,8 @@ def request_confirmation(self, title: str, body: Optional[str], components: List confirmation_label: str = None, deny_label: str = None, deny_button: bool = True, window_cancel: bool = False, confirmation_button: bool = True, min_width: Optional[int] = None, - min_height: Optional[int] = None) -> bool: + min_height: Optional[int] = None, + max_width: Optional[int] = None) -> bool: """ request a user confirmation. In the current GUI implementation, it shows a popup to the user. :param title: popup title @@ -34,6 +35,7 @@ def request_confirmation(self, title: str, body: Optional[str], components: List :param confirmation_button: if the confirmation button should be displayed :param min_width: minimum width for the confirmation dialog :param min_height: minimum height for the confirmation dialog + :param max_width: maximum width for the confirmation dialog :return: if the request was confirmed by the user """ pass diff --git a/bauh/view/qt/dialog.py b/bauh/view/qt/dialog.py index ae867a0f..13ffc0f5 100644 --- a/bauh/view/qt/dialog.py +++ b/bauh/view/qt/dialog.py @@ -35,7 +35,7 @@ def __init__(self, title: str, body: Optional[str], i18n: I18n, icon: QIcon = QI widgets: Optional[List[QWidget]] = None, confirmation_button: bool = True, deny_button: bool = True, window_cancel: bool = False, confirmation_label: Optional[str] = None, deny_label: Optional[str] = None, confirmation_icon: bool = True, min_width: Optional[int] = None, - min_height: Optional[int] = None): + min_height: Optional[int] = None, max_width: Optional[int] = None): super(ConfirmationDialog, self).__init__() if not window_cancel: @@ -46,6 +46,9 @@ def __init__(self, title: str, body: Optional[str], i18n: I18n, icon: QIcon = QI self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred) self.setMinimumWidth(min_width if min_width and min_width > 0 else 250) + if max_width is not None and max_width > 0: + self.setMaximumWidth(max_width) + if isinstance(min_height, int) and min_height > 0: self.setMinimumHeight(min_height) diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index 42a65d8b..adece32f 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -60,13 +60,14 @@ def request_confirmation(self, title: str, body: str, components: List[ViewCompo window_cancel: bool = False, confirmation_button: bool = True, min_width: Optional[int] = None, - min_height: Optional[int] = None) -> bool: + min_height: Optional[int] = None, + max_width: Optional[int] = None) -> bool: self.wait_confirmation = True self.signal_confirmation.emit({'title': title, 'body': body, 'components': components, 'confirmation_label': confirmation_label, 'deny_label': deny_label, 'deny_button': deny_button, 'window_cancel': window_cancel, 'confirmation_button': confirmation_button, 'min_width': min_width, - 'min_height': min_height}) + 'min_height': min_height, 'max_width': max_width}) self.wait_user() return self.confirmation_res diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index 23d6cbc3..1b3273e3 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -559,7 +559,8 @@ def _ask_confirmation(self, msg: dict): window_cancel=msg['window_cancel'], confirmation_button=msg.get('confirmation_button', True), min_width=msg.get('min_width'), - min_height=msg.get('min_height')) + min_height=msg.get('min_height'), + max_width=msg.get('max_width')) diag.ask() res = diag.confirmed self.thread_animate_progress.animate() From bac69d9f82b9c6287fadf88f47759d202142f625 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 30 Mar 2022 17:15:17 -0300 Subject: [PATCH 35/37] [appimage] improvement: limiting the UI components width of the file installation and upgrade windows --- CHANGELOG.md | 3 +++ bauh/gems/appimage/controller.py | 27 +++++++++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 514c86e3..8807b49a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

+ +- AppImage + - Limiting the UI components width of the file installation and upgrade windows - Arch - text length of some popups reduced diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py index aa946ca3..7fe48172 100644 --- a/bauh/gems/appimage/controller.py +++ b/bauh/gems/appimage/controller.py @@ -85,27 +85,31 @@ def __init__(self, context: ApplicationContext): self._search_unfilled_attrs: Optional[Tuple[str, ...]] = None def install_file(self, root_password: Optional[str], watcher: ProcessWatcher) -> bool: + max_width = 350 file_chooser = FileChooserComponent(label=self.i18n['file'].capitalize(), allowed_extensions={'AppImage', '*'}, - search_path=get_default_manual_installation_file_dir()) - input_name = TextInputComponent(label=self.i18n['name'].capitalize()) - input_version = TextInputComponent(label=self.i18n['version'].capitalize()) + search_path=get_default_manual_installation_file_dir(), + max_width=max_width) + input_name = TextInputComponent(label=self.i18n['name'].capitalize(), max_width=max_width) + input_version = TextInputComponent(label=self.i18n['version'].capitalize(), max_width=max_width) file_chooser.observers.append(ManualInstallationFileObserver(input_name, input_version)) - input_description = TextInputComponent(label=self.i18n['description'].capitalize()) + input_description = TextInputComponent(label=self.i18n['description'].capitalize(), max_width=max_width) cat_ops = [InputOption(label=self.i18n['category.none'].capitalize(), value=0)] cat_ops.extend([InputOption(label=self.i18n.get(f'category.{c.lower()}', c.lower()).capitalize(), value=c) for c in self.context.default_categories]) inp_cat = SingleSelectComponent(label=self.i18n['category'], type_=SelectViewType.COMBO, options=cat_ops, - default_option=cat_ops[0]) + default_option=cat_ops[0], max_width=max_width) - form = FormComponent(label='', components=[file_chooser, input_name, input_version, input_description, inp_cat], spaces=False) + form = FormComponent(label='', components=[file_chooser, input_name, input_version, input_description, inp_cat], + spaces=False) while True: if watcher.request_confirmation(title=self.i18n['appimage.custom_action.install_file.details'], body=None, components=[form], confirmation_label=self.i18n['proceed'].capitalize(), - deny_label=self.i18n['cancel'].capitalize()): + deny_label=self.i18n['cancel'].capitalize(), + min_height=100, max_width=max_width + 150): if not file_chooser.file_path or not os.path.isfile(file_chooser.file_path) or not file_chooser.file_path.lower().strip().endswith('.appimage'): watcher.request_confirmation(title=self.i18n['error'].capitalize(), body=self.i18n['appimage.custom_action.install_file.invalid_file'], @@ -139,17 +143,20 @@ def install_file(self, root_password: Optional[str], watcher: ProcessWatcher) -> return res def update_file(self, pkg: AppImage, root_password: Optional[str], watcher: ProcessWatcher): + max_width = 350 file_chooser = FileChooserComponent(label=self.i18n['file'].capitalize(), allowed_extensions={'AppImage', '*'}, - search_path=get_default_manual_installation_file_dir()) - input_version = TextInputComponent(label=self.i18n['version'].capitalize()) + search_path=get_default_manual_installation_file_dir(), + max_width=max_width) + input_version = TextInputComponent(label=self.i18n['version'].capitalize(), max_width=max_width) file_chooser.observers.append(ManualInstallationFileObserver(None, input_version)) while True: if watcher.request_confirmation(title=self.i18n['appimage.custom_action.manual_update.details'], body=None, components=[FormComponent(label='', components=[file_chooser, input_version], spaces=False)], confirmation_label=self.i18n['proceed'].capitalize(), - deny_label=self.i18n['cancel'].capitalize()): + deny_label=self.i18n['cancel'].capitalize(), + min_height=100, max_width=max_width + 150): if not file_chooser.file_path or not os.path.isfile(file_chooser.file_path) or not file_chooser.file_path.lower().strip().endswith('.appimage'): watcher.request_confirmation(title=self.i18n['error'].capitalize(), From c66cd464af0e3c57258c46b33edbf8debb9fce95 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 30 Mar 2022 18:17:35 -0300 Subject: [PATCH 36/37] [view] improvement: 'close' button added to the screenshots window --- CHANGELOG.md | 1 + bauh/view/qt/screenshots.py | 15 +++++++++++++++ bauh/view/resources/locale/ca | 1 + bauh/view/resources/locale/de | 1 + bauh/view/resources/locale/en | 1 + bauh/view/resources/locale/es | 1 + bauh/view/resources/locale/fr | 1 + bauh/view/resources/locale/it | 1 + bauh/view/resources/locale/pt | 1 + bauh/view/resources/locale/ru | 1 + bauh/view/resources/locale/tr | 1 + bauh/view/resources/style/default/default.qss | 4 ++++ 12 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8807b49a..83d7b5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - UI - only displaying the "Installed" filter when installed packages are available on the table - settings: margin between components reduced [#241](https://github.com/vinifmor/bauh/issues/241) + - "close" button added to the screenshots window (some distributions hide the default "x" on the dialog frame) [#246](https://github.com/vinifmor/bauh/issues/246) ### Fixes - Arch diff --git a/bauh/view/qt/screenshots.py b/bauh/view/qt/screenshots.py index cc3b719e..5d52aaf7 100644 --- a/bauh/view/qt/screenshots.py +++ b/bauh/view/qt/screenshots.py @@ -47,7 +47,22 @@ def __init__(self, pkg: PackageView, http_client: HttpClient, icon_cache: Memory self.setWindowIcon(QIcon(pkg.model.get_type_icon_path())) self.setLayout(QVBoxLayout()) + self.bt_close = QPushButton(self.i18n['screenshots.bt_close']) + self.bt_close.setObjectName('close') + self.bt_close.clicked.connect(self.close) + self.bt_close.setCursor(QCursor(Qt.PointingHandCursor)) + + self.upper_buttons = QWidget() + self.upper_buttons.setObjectName('upper_buttons') + self.upper_buttons.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) + self.upper_buttons.setContentsMargins(0, 0, 0, 0) + self.upper_buttons.setLayout(QHBoxLayout()) + self.upper_buttons.layout().setAlignment(Qt.AlignRight) + self.upper_buttons.layout().addWidget(self.bt_close) + self.layout().addWidget(self.upper_buttons) + self.layout().addWidget(new_spacer()) + self.img = QLabel() self.img.setObjectName('image') self.layout().addWidget(self.img) diff --git a/bauh/view/resources/locale/ca b/bauh/view/resources/locale/ca index 59944115..53ce2f99 100644 --- a/bauh/view/resources/locale/ca +++ b/bauh/view/resources/locale/ca @@ -411,6 +411,7 @@ publisher=proveïdor publisher.verified=verificat repository=dipòsit screenshots.bt_back.label=anterior +screenshots.bt_close=Tancar screenshots.bt_next.label=següent screenshots.download.no_content=No hi ha contingut a mostrar screenshots.image.loading=Loading diff --git a/bauh/view/resources/locale/de b/bauh/view/resources/locale/de index b08ec3bf..2b414ca2 100644 --- a/bauh/view/resources/locale/de +++ b/bauh/view/resources/locale/de @@ -410,6 +410,7 @@ publisher=Herausgeber publisher.verified=Geprüft repository=Repository screenshots.bt_back.label=Zurück +screenshots.bt_close=Schließen screenshots.bt_next.label=Fortfahren screenshots.download.no_content=Kein Inhalt screenshots.download.no_response=Screenshot nicht gefunden diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index f68805bb..5c04ac3d 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -413,6 +413,7 @@ publisher.verified=verified publisher=publisher repository=repository screenshots.bt_back.label=previous +screenshots.bt_close=Close screenshots.bt_next.label=next screenshots.download.no_content=No content to display screenshots.download.no_response=Image not found diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index 73d9e6c1..87ddf223 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -413,6 +413,7 @@ publisher.verified=verificado removing=removing repository=repositorio screenshots.bt_back.label=anterior +screenshots.bt_close=Cerrar screenshots.bt_next.label=siguiente screenshots.download.no_content=No hay contenido para mostrar screenshots.download.no_response=No se encontró la imagen diff --git a/bauh/view/resources/locale/fr b/bauh/view/resources/locale/fr index 3065098f..9525573f 100644 --- a/bauh/view/resources/locale/fr +++ b/bauh/view/resources/locale/fr @@ -407,6 +407,7 @@ publisher.verified=vérifié publisher=publier repository=dépôt screenshots.bt_back.label=précédant +screenshots.bt_close=Fermer screenshots.bt_next.label=suivant screenshots.download.no_content=Rien à afficher screenshots.download.no_response=Pas d'image diff --git a/bauh/view/resources/locale/it b/bauh/view/resources/locale/it index c629bbd7..db6a75c8 100644 --- a/bauh/view/resources/locale/it +++ b/bauh/view/resources/locale/it @@ -413,6 +413,7 @@ publisher=editore publisher.verified=verificato repository=deposito screenshots.bt_back.label=precedente +screenshots.bt_close=Chiudere screenshots.bt_next.label=prossimo screenshots.download.no_content=Nessun contenuto da visualizzare screenshots.download.no_response=Immagine non trovata diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index aa011ea6..d48a9403 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -411,6 +411,7 @@ publisher.verified=verificado publisher=publicador repository=repositório screenshots.bt_back.label=anterior +screenshots.bt_close=Fechar screenshots.bt_next.label=próxima screenshots.download.no_content=Sem conteúdo para exibir screenshots.download.no_response=Imagem não encontrada diff --git a/bauh/view/resources/locale/ru b/bauh/view/resources/locale/ru index 89c13d63..b975dfc4 100644 --- a/bauh/view/resources/locale/ru +++ b/bauh/view/resources/locale/ru @@ -410,6 +410,7 @@ publisher=издатель publisher.verified=Проверенный repository=Репозиторий screenshots.bt_back.label=Предыдущий +screenshots.bt_close=закрыть screenshots.bt_next.label=Следующий screenshots.download.no_content=Нет материалов для отображени screenshots.download.no_response=Изображение не найдено diff --git a/bauh/view/resources/locale/tr b/bauh/view/resources/locale/tr index e09c5703..1dce6f53 100644 --- a/bauh/view/resources/locale/tr +++ b/bauh/view/resources/locale/tr @@ -410,6 +410,7 @@ publisher.verified=doğrula publisher=yayımcı repository=depo screenshots.bt_back.label=önceki +screenshots.bt_close=Kapatmak screenshots.bt_next.label=sonraki screenshots.download.no_content=Görüntülenecek içerik yok screenshots.download.no_response=İmaj bulunamadı diff --git a/bauh/view/resources/style/default/default.qss b/bauh/view/resources/style/default/default.qss index 0f2cb313..6c71d578 100644 --- a/bauh/view/resources/style/default/default.qss +++ b/bauh/view/resources/style/default/default.qss @@ -391,6 +391,10 @@ ScreenshotsDialog QAbstractButton[control = "true"] { min-width: 100px; } +ScreenshotsDialog QPushButton#close { + min-width: 25px; +} + SettingsWindow TabGroupQt#settings { min-width: 400px; } From c7fc6be20ce006423e38f6ab4a2702235dbebeb0 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Thu, 31 Mar 2022 18:10:32 -0300 Subject: [PATCH 37/37] [CHANGELOG.md] Updating changes --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d7b5fc..be3f9a9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ 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.10.1] +## [0.10.1] 2022-03-31 + ### Features - Flatpak - new custom action "Full update": fully updates all installed Flatpak apps and components (useful if you are having issues with runtime updates)