Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Consider foreign provided packages when installing foreign packages #46
  • Loading branch information
kiviktnm committed Feb 7, 2026
commit c5898d82a35e8d08a2c0109f7b7b639b578f74f7
6 changes: 6 additions & 0 deletions plugins/decman-pacman/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ dependencies = [
"requests",
]

[dependency-groups]
dev = [
"pytest>=8.4.2",
"pytest-mock>=3.15.1",
]

[project.entry-points."decman.plugins"]
pacman = "decman.plugins.pacman:Pacman"
aur = "decman.plugins.aur:AUR"
Expand Down
3 changes: 2 additions & 1 deletion plugins/decman-pacman/src/decman/plugins/aur/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ def apply(

output.print_summary("Upgrading foreign packages.")
if not dry_run:
fpm.upgrade(upgrade_devel, force, self.ignored_packages)
# don't try to upgrade removed packages
fpm.upgrade(upgrade_devel, force, self.ignored_packages | actually_to_remove)

to_install = (
(self.packages | custom_package_names)
Expand Down
10 changes: 10 additions & 0 deletions plugins/decman-pacman/src/decman/plugins/aur/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@ def get_foreign_orphans(self) -> set[str]:
"""
return self._get_orphans(pacman.PacmanInterface._is_foreign)

def is_provided_by_installed(self, dependency: str) -> bool:
return pacman.strip_dependency(dependency) in self._local_provides_index

def filter_installed_packages(self, deps: set[str]) -> set[str]:
out = set()
for d in deps:
if not self.is_provided_by_installed(d) and d not in self.get_all_packages():
out.add(d)
return out

def is_installable(self, pkg: str) -> bool:
"""
Returns True if a package can be installed using pacman.
Expand Down
15 changes: 13 additions & 2 deletions plugins/decman-pacman/src/decman/plugins/aur/fpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ def __init__(self) -> None:
self.foreign_build_dep_pkgs: set[str] = set()
self.build_order: list[str] = []
self.packages: dict[str, ForeignPackage] = {}
# maps dependency names to package names
self.providers: dict[str, list[str]] = {}
self.all_provided: set[str] = set()
self._pkgbases_to_pkgs: dict[str, set[str]] = {}
self._pkgs_to_pkgbases: dict[str, str] = {}

Expand Down Expand Up @@ -277,8 +280,11 @@ def install(
if not output.prompt_confirm("Proceed?", default=True):
raise ForeignPackageManagerError("Installing aborted by the user.")

needed_pacman_deps = self._pacman.filter_installed_packages(
resolved_dependencies.pacman_deps - resolved_dependencies.all_provided
)
output.print_summary("Installing foreign package dependencies from pacman.")
self._pacman.install_dependencies(resolved_dependencies.pacman_deps)
self._pacman.install_dependencies(needed_pacman_deps)

try:
with PackageBuilder(
Expand Down Expand Up @@ -319,7 +325,8 @@ def install(
output.print_summary("Installing foreign packages.")
self._pacman.install_files(
package_files_to_install,
as_explicit=resolved_dependencies.foreign_pkgs,
as_explicit=resolved_dependencies.foreign_pkgs
- resolved_dependencies.foreign_dep_pkgs,
)
else:
output.print_summary("No packages to install.")
Expand Down Expand Up @@ -379,6 +386,10 @@ def process_dep(pkgname: str, depname: str, add_to: set[str]):
f"Failed to find '{pkgname}' from AUR or user provided packages."
)

for provided in info.provides:
result.providers.setdefault(provided, []).append(pkgname)
result.all_provided.add(provided)

result.pacman_deps.update(info.native_dependencies(self._pacman))
result.add_pkgbase_info(pkgname, info.pkgbase)

Expand Down
15 changes: 15 additions & 0 deletions plugins/decman-pacman/src/decman/plugins/pacman.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ def __init__(
self._dbpath = dbpath
self._handle = self._create_pyalpm_handle()
self._name_index = self._create_name_index()
self._local_provides_index = self._create_local_provides_index()
self._provides_index = self._create_provides_index()
self._requiredby_index = self._create_requiredby_index()

Expand All @@ -231,6 +232,14 @@ def _create_pyalpm_handle(self):
def _create_name_index(self) -> dict[str, pyalpm.Package]:
return {pkg.name: pkg for db in self._handle.get_syncdbs() for pkg in db.pkgcache}

def _create_local_provides_index(self) -> dict[str, set[str]]:
out: dict[str, set[str]] = {}
for pkg in self._handle.get_localdb().pkgcache:
for p in pkg.provides:
out.setdefault(strip_dependency(p), set()).add(pkg.name)
out.setdefault(p, set()).add(pkg.name)
return out

def _create_provides_index(self) -> dict[str, set[str]]:
out: dict[str, set[str]] = {}
for db in self._handle.get_syncdbs():
Expand All @@ -249,6 +258,12 @@ def _is_native(self, package: str) -> bool:
def _is_foreign(self, package: str) -> bool:
return not self._is_native(package)

def get_all_packages(self) -> set[str]:
"""
Returns a set of all installed packages.
"""
return {pkg for pkg in self._handle.get_localdb().pkgcache}

def get_native_explicit(self) -> set[str]:
"""
Returns a set of explicitly installed native packages.
Expand Down
Loading