diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e9210db..8ff4f668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - the dialog is now displayed before the upgrading process (but the operation is only executed after a successful upgrade) - Settings - new option to disable the reboot dialog after a successful upgrade (`updates.ask_for_reboot`) +- Arch + - able to handle upgrade scenarios when a package wants to overwrite files of another installed package ### Fixes - Arch diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index 00928b8c..b30484a0 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -21,7 +21,7 @@ from bauh.api.abstract.handler import ProcessWatcher, TaskManager from bauh.api.abstract.model import PackageUpdate, PackageHistory, SoftwarePackage, PackageSuggestion, PackageStatus, \ SuggestionPriority, CustomSoftwareAction from bauh.api.abstract.view import MessageType, FormComponent, InputOption, SingleSelectComponent, SelectViewType, \ - ViewComponent, PanelComponent, MultipleSelectComponent, TextInputComponent + ViewComponent, PanelComponent, MultipleSelectComponent, TextInputComponent, TextComponent from bauh.api.constants import TEMP_DIR from bauh.commons import user from bauh.commons.category import CategoriesDownloader @@ -189,6 +189,7 @@ class ArchManager(SoftwareManager): manager=self) } self.index_aur = None + self.re_file_conflict = re.compile(r'[\w\d\-_.]+:') @staticmethod def get_semantic_search_map() -> Dict[str, str]: @@ -690,6 +691,66 @@ class ArchManager(SoftwareManager): return False + def _map_conflicting_file(self, output: str) -> List[TextComponent]: + error_idx = None + lines = output.split('\n') + for idx, l in enumerate(lines): + if l and l.strip().lower().startswith('error: failed to commit transaction (conflicting files)'): + error_idx = idx + break + + files = [] + + if error_idx and error_idx + 1 < len(lines): + for idx in range(error_idx + 1, len(lines)): + line = lines[idx].strip() + + if line and self.re_file_conflict.match(line): + files.append(TextComponent(' - {}'.format(line))) + + return files + + def _upgrade_repo_pkgs(self, pkgs: List[str], handler: ProcessHandler, root_password: str, overwrite_files: bool = False) -> bool: + try: + output_handler = TransactionStatusHandler(handler.watcher, self.i18n, len(pkgs), self.logger) + output_handler.start() + success, upgrade_output = handler.handle_simple(pacman.upgrade_several(pkgnames=pkgs, + root_password=root_password, + overwrite_conflicting_files=overwrite_files), + output_handler=output_handler.handle,) + output_handler.stop_working() + output_handler.join() + + handler.watcher.change_substatus('') + + if success: + handler.watcher.print("Repository packages successfully upgraded") + handler.watcher.change_substatus(self.i18n['arch.upgrade.caching_pkgs_data']) + repo_map = pacman.map_repositories(pkgs) + disk.save_several(pkgs, repo_map=repo_map, overwrite=True, maintainer=None) + return True + elif 'conflicting files' in upgrade_output: + files = self._map_conflicting_file(upgrade_output) + if not handler.watcher.request_confirmation(title=self.i18n['warning'].capitalize(), + body=self.i18n['arch.upgrade.error.conflicting_files'] + ':', + deny_label=self.i18n['arch.upgrade.conflicting_files.proceed'], + confirmation_label=self.i18n['arch.upgrade.conflicting_files.stop'], + components=files): + + return self._upgrade_repo_pkgs(pkgs=pkgs, handler=handler, root_password=root_password, overwrite_files=True) + else: + handler.watcher.print("Aborted by the user") + return False + else: + self.logger.error("'pacman' returned an unexpected response or error phrase after upgrading the repository packages") + return False + except: + handler.watcher.change_substatus('') + handler.watcher.print("An error occurred while upgrading repository packages") + self.logger.error("An error occurred while upgrading repository packages") + traceback.print_exc() + return False + def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> bool: self.aur_client.clean_caches() watcher.change_status("{}...".format(self.i18n['manage_window.status.upgrading'])) @@ -733,29 +794,7 @@ class ArchManager(SoftwareManager): self.logger.info("Upgrading {} repository packages: {}".format(len(repo_pkgs_names), ', '.join(repo_pkgs_names))) - try: - output_handler = TransactionStatusHandler(watcher, self.i18n, len(repo_pkgs_names), self.logger) - output_handler.start() - success, _ = handler.handle_simple(pacman.upgrade_several(repo_pkgs_names, root_password), output_handler=output_handler.handle) - output_handler.stop_working() - output_handler.join() - - watcher.change_substatus('') - - if success: - watcher.print("Repository packages successfully upgraded") - watcher.change_substatus(self.i18n['arch.upgrade.caching_pkgs_data']) - repo_map = pacman.map_repositories(repo_pkgs_names) - disk.save_several(repo_pkgs_names, repo_map=repo_map, overwrite=True, maintainer=None) - - else: - self.logger.error("'pacman' returned an unexpected response or error phrase after upgrading the repository packages") - return False - except: - watcher.change_substatus('') - watcher.print("An error occurred while upgrading repository packages") - self.logger.error("An error occurred while upgrading repository packages") - traceback.print_exc() + if not self._upgrade_repo_pkgs(pkgs=repo_pkgs_names, handler=handler, root_password=root_password): return False watcher.change_status('{}...'.format(self.i18n['arch.upgrade.upgrade_aur_pkgs'])) diff --git a/bauh/gems/arch/pacman.py b/bauh/gems/arch/pacman.py index e253e340..75c1c425 100644 --- a/bauh/gems/arch/pacman.py +++ b/bauh/gems/arch/pacman.py @@ -144,7 +144,7 @@ def install_as_process(pkgpaths: Iterable[str], root_password: str, file: bool, return SimpleProcess(cmd=cmd, root_password=root_password, cwd=pkgdir, - error_phrases={"error: failed to prepare transaction", 'error: failed to commit transaction'}) + error_phrases={"error: failed to prepare transaction", 'error: failed to commit transaction', 'error: target not found'}) def list_desktop_entries(pkgnames: Set[str]) -> List[str]: @@ -693,10 +693,15 @@ def list_installed_names() -> Set[str]: return {p for p in run_cmd('pacman -Qq').split('\n') if p} -def upgrade_several(pkgnames: Iterable[str], root_password: str) -> SimpleProcess: - return SimpleProcess(cmd=['pacman', '-S', *pkgnames, '--noconfirm'], +def upgrade_several(pkgnames: Iterable[str], root_password: str, overwrite_conflicting_files: bool = False) -> SimpleProcess: + cmd = ['pacman', '-S', *pkgnames, '--noconfirm'] + + if overwrite_conflicting_files: + cmd.append('--overwrite=*') + + return SimpleProcess(cmd=cmd, root_password=root_password, - error_phrases={'error: failed to prepare transaction', 'error: failed to commit transaction'}) + error_phrases={'error: failed to prepare transaction', 'error: failed to commit transaction', 'error: target not found'}) def remove_several(pkgnames: Iterable[str], root_password: str) -> SystemProcess: diff --git a/bauh/gems/arch/resources/locale/ca b/bauh/gems/arch/resources/locale/ca index 57b78ef1..8c69b5ca 100644 --- a/bauh/gems/arch/resources/locale/ca +++ b/bauh/gems/arch/resources/locale/ca @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict=Conflict between the dependencies {} arch.update_summary.to_update.conflicts_dep=Conflicts with the dependency {} of {} arch.update_summary.to_update.dep_conflicts=Dependency {} conflicts with {} arch.upgrade.caching_pkgs_data=Caching upgrades data +arch.upgrade.error.conflicting_files=Some of the packages being upgraded want to overwrite files of another installed packages +arch.upgrade.conflicting_files.proceed=Allow and continue +arch.upgrade.conflicting_files.stop=Cancel upgrading arch.upgrade.fail=Package {} upgrade failed arch.upgrade.success=Package {} successfully upgraded arch.upgrade.upgrade_aur_pkgs=Upgrading AUR packages diff --git a/bauh/gems/arch/resources/locale/de b/bauh/gems/arch/resources/locale/de index fbfde2f1..22b8e8b0 100644 --- a/bauh/gems/arch/resources/locale/de +++ b/bauh/gems/arch/resources/locale/de @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict=Conflict between the dependencies {} arch.update_summary.to_update.conflicts_dep=Conflicts with the dependency {} of {} arch.update_summary.to_update.dep_conflicts=Dependency {} conflicts with {} arch.upgrade.caching_pkgs_data=Caching upgrades data +arch.upgrade.error.conflicting_files=Some of the packages being upgraded want to overwrite files of another installed packages +arch.upgrade.conflicting_files.proceed=Allow and continue +arch.upgrade.conflicting_files.stop=Cancel upgrading arch.upgrade.fail=Package {} upgrade failed arch.upgrade.success=Package {} successfully upgraded arch.upgrade.upgrade_aur_pkgs=Upgrading AUR packages diff --git a/bauh/gems/arch/resources/locale/en b/bauh/gems/arch/resources/locale/en index 00a28ba4..c483c937 100644 --- a/bauh/gems/arch/resources/locale/en +++ b/bauh/gems/arch/resources/locale/en @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict=Conflict between the dependencies {} arch.update_summary.to_update.conflicts_dep=Conflicts with the dependency {} of {} arch.update_summary.to_update.dep_conflicts=Dependency {} conflicts with {} arch.upgrade.caching_pkgs_data=Caching upgrades data +arch.upgrade.error.conflicting_files=Some of the packages being upgraded want to overwrite files of another installed packages +arch.upgrade.conflicting_files.proceed=Allow and continue +arch.upgrade.conflicting_files.stop=Cancel upgrading arch.upgrade.fail=Package {} upgrade failed arch.upgrade.success=Package {} successfully upgraded arch.upgrade.upgrade_aur_pkgs=Upgrading AUR packages diff --git a/bauh/gems/arch/resources/locale/es b/bauh/gems/arch/resources/locale/es index d0d47b55..c8850bf7 100644 --- a/bauh/gems/arch/resources/locale/es +++ b/bauh/gems/arch/resources/locale/es @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict=Conflicto entre las dependencias {} arch.update_summary.to_update.conflicts_dep=Conflicta con la dependencia {} de {} arch.update_summary.to_update.dep_conflicts=La dependencia {} conflicta con {} arch.upgrade.caching_pkgs_data=Caching upgrades data +arch.upgrade.error.conflicting_files=Algunos de los paquetes que se están actualizando desean sobrescribir archivos de otros paquetes instalados +arch.upgrade.conflicting_files.proceed=Permitir y continuar +arch.upgrade.conflicting_files.stop=Cancelar la actualización arch.upgrade.fail=Falló la actualización del paquete {} arch.upgrade.success=Paquete {} actualizado con éxito arch.upgrade.upgrade_aur_pkgs=Actualizando paquetes de AUR diff --git a/bauh/gems/arch/resources/locale/it b/bauh/gems/arch/resources/locale/it index fdcba133..c66e8ac4 100644 --- a/bauh/gems/arch/resources/locale/it +++ b/bauh/gems/arch/resources/locale/it @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict=Conflict between the dependencies {} arch.update_summary.to_update.conflicts_dep=Conflicts with the dependency {} of {} arch.update_summary.to_update.dep_conflicts=Dependency {} conflicts with {} arch.upgrade.caching_pkgs_data=Caching upgrades data +arch.upgrade.error.conflicting_files=Some of the packages being upgraded want to overwrite files of another installed packages +arch.upgrade.conflicting_files.proceed=Allow and continue +arch.upgrade.conflicting_files.stop=Cancel upgrading arch.upgrade.fail=Package {} upgrade failed arch.upgrade.success=Package {} successfully upgraded arch.upgrade.upgrade_aur_pkgs=Upgrading AUR packages diff --git a/bauh/gems/arch/resources/locale/pt b/bauh/gems/arch/resources/locale/pt index cee36abe..fbbfc524 100644 --- a/bauh/gems/arch/resources/locale/pt +++ b/bauh/gems/arch/resources/locale/pt @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict=Conflito entre as dependências {} e arch.update_summary.to_update.conflicts_dep=Conflita com a dependência {} de {} arch.update_summary.to_update.dep_conflicts=A dependência {} conflita com {} arch.upgrade.caching_pkgs_data=Armazenando dados das atualizações +arch.upgrade.error.conflicting_files=Alguns dos pacotes que estão sendo atualizados querem sobrepor arquivos de outros pacotes instalados +arch.upgrade.conflicting_files.proceed=Permitir e continuar +arch.upgrade.conflicting_files.stop=Cancelar atualização arch.upgrade.fail=Atualização do pacote {} falhou arch.upgrade.success=Pacote {} atualizado com sucesso arch.upgrade.upgrade_aur_pkgs=Atualizando pacotes do AUR diff --git a/bauh/gems/arch/resources/locale/ru b/bauh/gems/arch/resources/locale/ru index 0b51d6e5..397c8a88 100644 --- a/bauh/gems/arch/resources/locale/ru +++ b/bauh/gems/arch/resources/locale/ru @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict=Конфликт между зави arch.update_summary.to_update.conflicts_dep=Конфликты с зависимостью {} от {} arch.update_summary.to_update.dep_conflicts=Зависимость {} конфликтует с {} arch.upgrade.caching_pkgs_data=Caching upgrades data +arch.upgrade.error.conflicting_files=Some of the packages being upgraded want to overwrite files of another installed packages +arch.upgrade.conflicting_files.proceed=Allow and continue +arch.upgrade.conflicting_files.stop=Cancel upgrading arch.upgrade.fail=Package {} upgrade failed arch.upgrade.success=Package {} successfully upgraded arch.upgrade.upgrade_aur_pkgs=Upgrading AUR packages diff --git a/bauh/gems/arch/resources/locale/tr b/bauh/gems/arch/resources/locale/tr index d2a8f2bd..f17454aa 100644 --- a/bauh/gems/arch/resources/locale/tr +++ b/bauh/gems/arch/resources/locale/tr @@ -188,6 +188,9 @@ arch.update_summary.to_install.dep_conflict={} ve {} bağımlılıkları arasın arch.update_summary.to_update.conflicts_dep={} / {} bağımlılığıyla ilgili çakışmalar arch.update_summary.to_update.dep_conflicts=Bağımlılık {} ile {} çakışıyor arch.upgrade.caching_pkgs_data=Yükseltme verileri önbelleğe alınıyor +arch.upgrade.error.conflicting_files=Some of the packages being upgraded want to overwrite files of another installed packages +arch.upgrade.conflicting_files.proceed=Allow and continue +arch.upgrade.conflicting_files.stop=Cancel upgrading arch.upgrade.fail={} paketi yükseltilemedi arch.upgrade.success={} paketi başarıyla yükseltildi arch.upgrade.upgrade_aur_pkgs=AUR paketleri yükseltiliyor