pull/85960/merge
Jason K Hall 23 hours ago committed by GitHub
commit 5bf1f609ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,3 @@
---
minor_changes:
- apt - add support for installing ``.deb`` files whose dependencies include virtual packages (e.g. ``libglib2.0-0`` on Ubuntu 24.04) by recognizing installed providers such as ``libglib2.0-0t64``. This resolves errors like ``Dependency is not satisfiable`` when installing VS Code and other packages (https://github.com/ansible/ansible/issues/85807).

@ -877,34 +877,48 @@ def install_deb(
installed_pkg = apt.Cache()[pkg_key]
installed_version = installed_pkg.installed.version
if package_version_compare(pkg_version, installed_version) == 0:
# Does not need to down-/upgrade, move on to next package
continue
except Exception:
# Must not be installed, continue with installation
pass
# Check if package is installable
if not pkg.check():
if force or ("later version" in pkg._failure_string and allow_downgrade):
missing = getattr(pkg, "missing_deps", [])
all_virtual = all(cache.is_virtual_package(dep) for dep in missing)
if all_virtual:
pass
elif force or ("later version" in pkg._failure_string and allow_downgrade):
pass
else:
m.fail_json(msg=pkg._failure_string)
# add any missing deps to the list of deps we need
# to install so they're all done in one shot
deps_to_install.extend(pkg.missing_deps)
# handle virtual package dependencies properly
missing_deps = []
for dep in pkg.missing_deps:
try:
if cache.is_virtual_package(dep):
providers = cache.get_providing_packages(dep)
if providers:
if any(cache[p].installed for p in providers if p in cache):
continue
else:
missing_deps.append(dep)
else:
missing_deps.append(dep)
else:
missing_deps.append(dep)
except Exception as e:
missing_deps.append(dep)
deps_to_install.extend(missing_deps)
except Exception as e:
m.fail_json(msg="Unable to install package: %s" % to_native(e))
# Install 'Recommends' of this deb file
if install_recommends:
pkg_recommends = get_field_of_deb(m, deb_file, "Recommends")
deps_to_install.extend([pkg_name.strip() for pkg_name in pkg_recommends.split()])
# and add this deb to the list of packages to install
pkgs_to_install.append(deb_file)
# install the deps through apt
retvals = {}
if deps_to_install:
install_dpkg_options = f"{expand_dpkg_options(dpkg_options)} -o DPkg::Lock::Timeout={lock_timeout}"
@ -934,6 +948,44 @@ def install_deb(
with PolicyRcD(m):
rc, out, err = m.run_command(cmd)
# handle missing virtual dependencies
if rc != 0 and "dependency problems" in err:
missing_deps = []
for line in err.splitlines():
if "depends on" in line and "however" in line:
dep = line.split("depends on", 1)[1].split(";", 1)[0].strip()
if dep:
missing_deps.append(dep)
resolved = []
for dep in missing_deps:
try:
if cache.is_virtual_package(dep):
providers = cache.get_providing_packages(dep)
if providers:
provider = providers[0]
resolved.append(provider.name)
except Exception:
continue
if resolved:
m.warn("dpkg dependency error — virtual deps: %s → installing providers: %s"
% (', '.join(missing_deps), ', '.join(resolved)))
(success, retvals) = install(
m=m, pkgspec=resolved, cache=cache,
install_recommends=True,
fail_on_autoremove=fail_on_autoremove,
allow_unauthenticated=allow_unauthenticated,
allow_downgrade=allow_downgrade,
allow_change_held_packages=allow_change_held_packages,
dpkg_options=expand_dpkg_options(dpkg_options),
)
if not success:
m.warn("Provider install failed, attempting apt --fix-broken install")
m.run_command("/usr/bin/apt-get -y --fix-broken install")
rc, out, err = m.run_command(cmd)
if "stdout" in retvals:
stdout = retvals["stdout"] + out
else:

@ -0,0 +1,60 @@
---
- block:
- name: Create directory for test .deb
ansible.builtin.file:
path: /tmp/virtual-test/DEBIAN
state: directory
- name: Create a test .deb file that depends on a virtual package
ansible.builtin.shell: |
cat > /tmp/virtual-test/DEBIAN/control << EOF
Package: virtual-test-pkg
Version: 1.0
Architecture: all
Description: Test package depending on virtual package
Depends: mail-transport-agent
EOF
dpkg-deb --build /tmp/virtual-test /tmp/virtual-test.deb
args:
creates: /tmp/virtual-test.deb
- name: Remove any existing MTA providers
ansible.builtin.apt:
name:
- exim4
- postfix
- sendmail
state: absent
- name: Test the patch - install .deb with virtual package dependency
ansible.builtin.apt:
deb: /tmp/virtual-test.deb
state: present
register: patch_test_result
- name: Debug - Show patch test result
ansible.builtin.debug:
var: patch_test_result
- name: Check if a virtual package provider was automatically installed
ansible.builtin.shell: |
dpkg -l | grep -E "^ii" | grep -E "(exim|postfix|sendmail)" && echo "SUCCESS: Virtual package provider was installed" || echo "FAIL: No provider installed"
register: provider_check
changed_when: false
- name: Show provider check result
ansible.builtin.debug:
var: provider_check.stdout
- name: Assert the patch worked
ansible.builtin.assert:
that:
- patch_test_result is succeeded
- "'SUCCESS' in provider_check.stdout"
fail_msg: "The patch failed to properly resolve virtual package dependencies when installing .deb files"
always:
- name: Clean up test package
ansible.builtin.file:
path: /tmp/virtual-test.deb
state: absent

@ -30,6 +30,8 @@
- import_tasks: 'apt-builddep.yml'
- import_tasks: apt_virtual_pkg_fix.yml
- block:
- import_tasks: 'repo.yml'
always:

@ -4,8 +4,9 @@ from __future__ import annotations
import collections
from ansible.modules.apt import expand_pkgspec_from_fnmatches
import pytest
from ansible.modules.apt import expand_pkgspec_from_fnmatches
FakePackage = collections.namedtuple("Package", ("name",))
fake_cache = [

Loading…
Cancel
Save