mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-09 14:24:16 +02:00
[arch] fix -> not able to replace an installed package for a new one that replaces it during conflict resolutions
This commit is contained in:
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
### Fixes
|
### Fixes
|
||||||
- Arch:
|
- Arch:
|
||||||
- search: not able to find installed packages that were renamed on the repositories (e.g: xapps -> xapp)
|
- search: not able to find installed packages that were renamed on the repositories (e.g: xapps -> xapp)
|
||||||
|
- not able to replace an installed package for a new one that replaces it during conflict resolutions (e.g: xapp replaces xapps)
|
||||||
- AUR: not able to find some repository dependencies when their names are not an exact match (e.g: sc-controller [0.4.7-1] relies on "pylibacl". This dependency now is called "python-pylibacl")
|
- AUR: not able to find some repository dependencies when their names are not an exact match (e.g: sc-controller [0.4.7-1] relies on "pylibacl". This dependency now is called "python-pylibacl")
|
||||||
|
|
||||||
## [0.9.8] 2020-10-02
|
## [0.9.8] 2020-10-02
|
||||||
|
|||||||
@@ -1095,15 +1095,20 @@ class ArchManager(SoftwareManager):
|
|||||||
watcher.change_substatus('')
|
watcher.change_substatus('')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _uninstall_pkgs(self, pkgs: Iterable[str], root_password: str, handler: ProcessHandler) -> bool:
|
def _uninstall_pkgs(self, pkgs: Iterable[str], root_password: str, handler: ProcessHandler, ignore_dependencies: bool = False) -> bool:
|
||||||
status_handler = TransactionStatusHandler(watcher=handler.watcher,
|
status_handler = TransactionStatusHandler(watcher=handler.watcher,
|
||||||
i18n=self.i18n,
|
i18n=self.i18n,
|
||||||
names={*pkgs},
|
names={*pkgs},
|
||||||
logger=self.logger,
|
logger=self.logger,
|
||||||
pkgs_to_remove=len(pkgs))
|
pkgs_to_remove=len(pkgs))
|
||||||
|
|
||||||
|
cmd = ['pacman', '-R', *pkgs, '--noconfirm']
|
||||||
|
|
||||||
|
if ignore_dependencies:
|
||||||
|
cmd.append('-dd')
|
||||||
|
|
||||||
status_handler.start()
|
status_handler.start()
|
||||||
all_uninstalled, _ = handler.handle_simple(SimpleProcess(cmd=['pacman', '-R', *pkgs, '--noconfirm'],
|
all_uninstalled, _ = handler.handle_simple(SimpleProcess(cmd=cmd,
|
||||||
root_password=root_password,
|
root_password=root_password,
|
||||||
error_phrases={'error: failed to prepare transaction',
|
error_phrases={'error: failed to prepare transaction',
|
||||||
'error: failed to commit transaction'},
|
'error: failed to commit transaction'},
|
||||||
@@ -1167,24 +1172,25 @@ class ArchManager(SoftwareManager):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _uninstall(self, context: TransactionContext, names: Set[str], remove_unneeded: bool = False, disk_loader: DiskCacheLoader = None):
|
def _uninstall(self, context: TransactionContext, names: Set[str], remove_unneeded: bool = False, disk_loader: Optional[DiskCacheLoader] = None, skip_requirements: bool = False):
|
||||||
self._update_progress(context, 10)
|
self._update_progress(context, 10)
|
||||||
|
|
||||||
net_available = internet.is_available() if disk_loader else True
|
net_available = internet.is_available() if disk_loader else True
|
||||||
|
|
||||||
hard_requirements = set()
|
hard_requirements = set()
|
||||||
|
|
||||||
for n in names:
|
if not skip_requirements:
|
||||||
try:
|
for n in names:
|
||||||
pkg_reqs = pacman.list_hard_requirements(n, self.logger)
|
try:
|
||||||
|
pkg_reqs = pacman.list_hard_requirements(n, self.logger)
|
||||||
|
|
||||||
if pkg_reqs:
|
if pkg_reqs:
|
||||||
hard_requirements.update(pkg_reqs)
|
hard_requirements.update(pkg_reqs)
|
||||||
except PackageInHoldException:
|
except PackageInHoldException:
|
||||||
context.watcher.show_message(title=self.i18n['error'].capitalize(),
|
context.watcher.show_message(title=self.i18n['error'].capitalize(),
|
||||||
body=self.i18n['arch.uninstall.error.hard_dep_in_hold'].format(bold(n)),
|
body=self.i18n['arch.uninstall.error.hard_dep_in_hold'].format(bold(n)),
|
||||||
type_=MessageType.ERROR)
|
type_=MessageType.ERROR)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self._update_progress(context, 25)
|
self._update_progress(context, 25)
|
||||||
|
|
||||||
@@ -1199,7 +1205,7 @@ class ArchManager(SoftwareManager):
|
|||||||
watcher=context.watcher):
|
watcher=context.watcher):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if remove_unneeded:
|
if not skip_requirements and remove_unneeded:
|
||||||
unnecessary_packages = pacman.list_post_uninstall_unneeded_packages(to_uninstall)
|
unnecessary_packages = pacman.list_post_uninstall_unneeded_packages(to_uninstall)
|
||||||
self.logger.info("Checking unnecessary optdeps")
|
self.logger.info("Checking unnecessary optdeps")
|
||||||
|
|
||||||
@@ -1222,7 +1228,7 @@ class ArchManager(SoftwareManager):
|
|||||||
else:
|
else:
|
||||||
instances = None
|
instances = None
|
||||||
|
|
||||||
uninstalled = self._uninstall_pkgs(to_uninstall, context.root_password, context.handler)
|
uninstalled = self._uninstall_pkgs(to_uninstall, context.root_password, context.handler, ignore_dependencies=skip_requirements)
|
||||||
|
|
||||||
if uninstalled:
|
if uninstalled:
|
||||||
if disk_loader: # loading package instances in case the uninstall succeeds
|
if disk_loader: # loading package instances in case the uninstall succeeds
|
||||||
@@ -1521,7 +1527,7 @@ class ArchManager(SoftwareManager):
|
|||||||
else:
|
else:
|
||||||
return self._get_history_repo_pkg(pkg)
|
return self._get_history_repo_pkg(pkg)
|
||||||
|
|
||||||
def _request_conflict_resolution(self, pkg: str, conflicting_pkg: str, context: TransactionContext) -> bool:
|
def _request_conflict_resolution(self, pkg: str, conflicting_pkg: str, context: TransactionContext, skip_requirements: bool = False) -> bool:
|
||||||
conflict_msg = '{} {} {}'.format(bold(pkg), self.i18n['and'], bold(conflicting_pkg))
|
conflict_msg = '{} {} {}'.format(bold(pkg), self.i18n['and'], bold(conflicting_pkg))
|
||||||
if not context.watcher.request_confirmation(title=self.i18n['arch.install.conflict.popup.title'],
|
if not context.watcher.request_confirmation(title=self.i18n['arch.install.conflict.popup.title'],
|
||||||
body=self.i18n['arch.install.conflict.popup.body'].format(conflict_msg)):
|
body=self.i18n['arch.install.conflict.popup.body'].format(conflict_msg)):
|
||||||
@@ -1534,7 +1540,8 @@ class ArchManager(SoftwareManager):
|
|||||||
if context.removed is None:
|
if context.removed is None:
|
||||||
context.removed = {}
|
context.removed = {}
|
||||||
|
|
||||||
res = self._uninstall(context=context, names={conflicting_pkg}, disk_loader=context.disk_loader, remove_unneeded=False)
|
res = self._uninstall(context=context, names={conflicting_pkg}, disk_loader=context.disk_loader,
|
||||||
|
remove_unneeded=False, skip_requirements=skip_requirements)
|
||||||
context.restabilish_progress()
|
context.restabilish_progress()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -1572,16 +1579,17 @@ class ArchManager(SoftwareManager):
|
|||||||
|
|
||||||
all_provided = context.get_provided_map()
|
all_provided = context.get_provided_map()
|
||||||
|
|
||||||
for dep, conflicts in pacman.map_conflicts_with(repo_dep_names, remote=True).items():
|
for dep, data in pacman.map_conflicts_with(repo_dep_names, remote=True).items():
|
||||||
if conflicts:
|
if data and data['c']:
|
||||||
for c in conflicts:
|
for c in data['c']:
|
||||||
source_conflict = all_provided.get(c)
|
source_conflict = all_provided.get(c)
|
||||||
|
|
||||||
if source_conflict:
|
if source_conflict:
|
||||||
conflict_pkg = [*source_conflict][0]
|
conflict_pkg = [*source_conflict][0]
|
||||||
|
|
||||||
if dep != conflict_pkg:
|
if dep != conflict_pkg:
|
||||||
if not self._request_conflict_resolution(dep, conflict_pkg , context):
|
if not self._request_conflict_resolution(dep, conflict_pkg, context,
|
||||||
|
skip_requirements=data['r'] and conflict_pkg in data['r']):
|
||||||
return {dep}
|
return {dep}
|
||||||
|
|
||||||
downloaded = 0
|
downloaded = 0
|
||||||
@@ -1592,7 +1600,7 @@ class ArchManager(SoftwareManager):
|
|||||||
except ArchDownloadException:
|
except ArchDownloadException:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
status_handler = TransactionStatusHandler(watcher=context.watcher, i18n=self.i18n, names=repo_dep_names,
|
status_handler = TransactionStatusHandler(watcher=context.watcher, i18n=self.i18n, names={*repo_dep_names},
|
||||||
logger=self.logger, percentage=len(repo_deps) > 1, downloading=downloaded)
|
logger=self.logger, percentage=len(repo_deps) > 1, downloading=downloaded)
|
||||||
status_handler.start()
|
status_handler.start()
|
||||||
installed, _ = context.handler.handle_simple(pacman.install_as_process(pkgpaths=repo_dep_names,
|
installed, _ = context.handler.handle_simple(pacman.install_as_process(pkgpaths=repo_dep_names,
|
||||||
@@ -2106,8 +2114,25 @@ class ArchManager(SoftwareManager):
|
|||||||
if context.removed is None:
|
if context.removed is None:
|
||||||
context.removed = {}
|
context.removed = {}
|
||||||
|
|
||||||
|
to_install_replacements = pacman.map_replaces(names_to_install)
|
||||||
|
|
||||||
|
skip_requirement_checking = False
|
||||||
|
if to_install_replacements: # checking if the packages to be installed replace the installed packages
|
||||||
|
all_replacements = set()
|
||||||
|
|
||||||
|
for replacements in to_install_replacements:
|
||||||
|
all_replacements.update(replacements)
|
||||||
|
|
||||||
|
if all_replacements:
|
||||||
|
for pkg in to_uninstall:
|
||||||
|
if pkg not in all_replacements:
|
||||||
|
break
|
||||||
|
|
||||||
|
skip_requirement_checking = True
|
||||||
|
|
||||||
context.disable_progress_if_changing()
|
context.disable_progress_if_changing()
|
||||||
if not self._uninstall(names=to_uninstall, context=context, remove_unneeded=False, disk_loader=context.disk_loader):
|
if not self._uninstall(names=to_uninstall, context=context, remove_unneeded=False,
|
||||||
|
disk_loader=context.disk_loader, skip_requirements=skip_requirement_checking):
|
||||||
context.watcher.show_message(title=self.i18n['error'],
|
context.watcher.show_message(title=self.i18n['error'],
|
||||||
body=self.i18n['arch.uninstalling.conflict.fail'].format(', '.join((bold(p) for p in to_uninstall))),
|
body=self.i18n['arch.uninstalling.conflict.fail'].format(', '.join((bold(p) for p in to_uninstall))),
|
||||||
type_=MessageType.ERROR)
|
type_=MessageType.ERROR)
|
||||||
|
|||||||
@@ -960,12 +960,57 @@ def map_required_by(names: Iterable[str] = None, remote: bool = False) -> Dict[s
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def map_conflicts_with(names: Iterable[str], remote: bool) -> Dict[str, Set[str]]:
|
def map_conflicts_with(names: Iterable[str], remote: bool) -> Dict[str, Dict[str, Set[str]]]:
|
||||||
output = run_cmd('pacman -{}i {}'.format('S' if remote else 'Q', ' '.join(names)))
|
output = run_cmd('pacman -{}i {}'.format('S' if remote else 'Q', ' '.join(names)))
|
||||||
|
|
||||||
if output:
|
if output:
|
||||||
res = {}
|
res = {}
|
||||||
latest_name, conflicts = None, None
|
latest_name, conflicts, replaces, field = None, 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':
|
||||||
|
field = 'n'
|
||||||
|
val = line[field_sep_idx + 1:].strip()
|
||||||
|
latest_name = val
|
||||||
|
elif field == 'Conflicts With':
|
||||||
|
field = 'c'
|
||||||
|
val = line[field_sep_idx + 1:].strip()
|
||||||
|
conflicts = set()
|
||||||
|
if val != 'None':
|
||||||
|
conflicts.update((d for d in val.split(' ') if d))
|
||||||
|
elif field == 'Replaces':
|
||||||
|
field = 'r'
|
||||||
|
val = line[field_sep_idx + 1:].strip()
|
||||||
|
replaces = set()
|
||||||
|
if val != 'None':
|
||||||
|
replaces.update((d for d in val.split(' ') if d))
|
||||||
|
|
||||||
|
elif latest_name and conflicts is not None and replaces is not None:
|
||||||
|
field = None
|
||||||
|
res[latest_name] = {'c': conflicts, 'r': replaces}
|
||||||
|
latest_name, conflicts, replaces = None, None, None
|
||||||
|
|
||||||
|
elif latest_name and field:
|
||||||
|
if field == 'c':
|
||||||
|
conflicts.update((d for d in l.strip().split(' ') if d))
|
||||||
|
elif field == 'r':
|
||||||
|
replaces.update((d for d in l.strip().split(' ') if d))
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def map_replaces(names: Iterable[str], remote: bool = False) -> Dict[str, Set[str]]:
|
||||||
|
output = run_cmd('pacman -{}i {}'.format('S' if remote else 'Q', ' '.join(names)))
|
||||||
|
|
||||||
|
if output:
|
||||||
|
res = {}
|
||||||
|
latest_name, replaces = None, None
|
||||||
|
|
||||||
for l in output.split('\n'):
|
for l in output.split('\n'):
|
||||||
if l:
|
if l:
|
||||||
@@ -977,18 +1022,18 @@ def map_conflicts_with(names: Iterable[str], remote: bool) -> Dict[str, Set[str]
|
|||||||
if field == 'Name':
|
if field == 'Name':
|
||||||
val = line[field_sep_idx + 1:].strip()
|
val = line[field_sep_idx + 1:].strip()
|
||||||
latest_name = val
|
latest_name = val
|
||||||
elif field == 'Conflicts With':
|
elif field == 'Replaces':
|
||||||
val = line[field_sep_idx + 1:].strip()
|
val = line[field_sep_idx + 1:].strip()
|
||||||
conflicts = set()
|
replaces = set()
|
||||||
if val != 'None':
|
if val != 'None':
|
||||||
conflicts.update((d for d in val.split(' ') if d))
|
replaces.update((d for d in val.split(' ') if d))
|
||||||
|
|
||||||
elif latest_name and conflicts is not None:
|
elif latest_name and replaces is not None:
|
||||||
res[latest_name] = conflicts
|
res[latest_name] = replaces
|
||||||
latest_name, conflicts = None, None
|
latest_name, replaces = None, None, None
|
||||||
|
|
||||||
elif latest_name and conflicts is not None:
|
elif latest_name and replaces is not None:
|
||||||
conflicts.update(conflicts.update((d for d in l.strip().split(' ') if d)))
|
replaces.update((d for d in l.strip().split(' ') if d))
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user