Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
31 changes: 31 additions & 0 deletions selftests/install/test_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ def test_install_plan_debian_uses_apt(tmp_path: Path):
assert plan.system_step is not None
assert plan.system_step.manager == "apt"
assert "libnss3" in plan.system_step.packages
# Ubuntu 24.04 should use libasound2t64, not libasound2
assert "libasound2t64" in plan.system_step.packages
assert "libasound2" not in plan.system_step.packages


def test_install_plan_suse_uses_zypper(tmp_path: Path):
Expand Down Expand Up @@ -234,6 +237,34 @@ def test_install_plan_pending_packages_now_present_get_claimed(tmp_path: Path):
assert "libdrm" in plan.system_step.packages


def test_install_plan_normalizes_legacy_pending_libasound2(tmp_path: Path):
"""Old manifest with pending 'libasound2' gets claimed as 'libasound2t64' on 24.04."""
info = PlatformInfo(family="debian-family", id="ubuntu", version="24.04")
m = Manifest(
version=SCHEMA_VERSION,
vip_version="0.0.0",
created_at="",
updated_at="",
host="h",
platform="debian-family",
platform_id="ubuntu",
platform_version="24.04",
items=[],
pending_system_packages=["libasound2"],
)
plan = pl.build_install_plan(
platform_info=info,
manifest=m,
rpm_installed=lambda names: set(),
dpkg_installed=lambda names: {"libasound2t64"},
chromium_present=False,
playwright_cache_dir=tmp_path / "cache",
skip_system=False,
)
# The legacy name should be normalized and claimed.
assert "libasound2t64" in plan.claim_pending


def _full_manifest() -> Manifest:
m = _empty_manifest()
m.items = [
Expand Down
34 changes: 34 additions & 0 deletions selftests/install/test_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,40 @@ def test_debian_packages_is_tuple_of_strings():
assert "libnss3" in plat.DEBIAN_PACKAGES


def test_debian_packages_ubuntu_2404_uses_libasound2t64():
info = plat.PlatformInfo(family="debian-family", id="ubuntu", version="24.04")
pkgs = plat.debian_packages(info)
assert "libasound2t64" in pkgs
assert "libasound2" not in pkgs


def test_debian_packages_ubuntu_2204_uses_libasound2():
info = plat.PlatformInfo(family="debian-family", id="ubuntu", version="22.04")
pkgs = plat.debian_packages(info)
assert "libasound2" in pkgs
assert "libasound2t64" not in pkgs


def test_debian_packages_debian_uses_libasound2():
info = plat.PlatformInfo(family="debian-family", id="debian", version="12")
pkgs = plat.debian_packages(info)
assert "libasound2" in pkgs
assert "libasound2t64" not in pkgs


def test_debian_packages_ubuntu_derivative_uses_libasound2t64():
"""Ubuntu derivatives (e.g. Pop!_OS) that report ID_LIKE=ubuntu also need t64."""
info = plat.PlatformInfo(
family="debian-family",
id="pop",
version="24.04",
raw={"ID": "pop", "ID_LIKE": "ubuntu debian", "VERSION_ID": "24.04"},
)
pkgs = plat.debian_packages(info)
assert "libasound2t64" in pkgs
assert "libasound2" not in pkgs


def test_suse_packages_is_tuple_of_strings():
assert isinstance(plat.SUSE_PACKAGES, tuple)
assert all(isinstance(p, str) and p for p in plat.SUSE_PACKAGES)
Expand Down
33 changes: 30 additions & 3 deletions src/vip/install/plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,28 @@ def is_empty(self) -> bool:
return True


# Maps old Debian package names to their t64 replacements.
# Used to reconcile manifest entries recorded under the pre-24.04 name.
_DEBIAN_RENAME_MAP: dict[str, str] = {"libasound2": "libasound2t64"}


def _normalize_pending_debian(pending: set[str], current_packages: tuple[str, ...]) -> set[str]:
"""Map legacy pending names to the current package list.

If the manifest recorded "libasound2" but we now install "libasound2t64",
rewrite the pending entry so ``claim_pending`` can match.
"""
current = set(current_packages)
out: set[str] = set()
for name in pending:
new_name = _DEBIAN_RENAME_MAP.get(name)
if new_name and new_name in current and name not in current:
out.add(new_name)
else:
out.add(name)
return out


def build_install_plan(
*,
platform_info: plat.PlatformInfo,
Expand All @@ -66,9 +88,14 @@ def build_install_plan(
missing = tuple(p for p in plat.RHEL_PACKAGES if p not in present)
system_step = SystemPackagesStep(manager="dnf", packages=missing)
elif family == "debian-family":
present = dpkg_installed(plat.DEBIAN_PACKAGES)
claim_pending = tuple(sorted(pending & present))
missing = tuple(p for p in plat.DEBIAN_PACKAGES if p not in present)
packages = plat.debian_packages(platform_info)
present = dpkg_installed(packages)
# Normalize legacy pending names: if the manifest recorded
# "libasound2" but we now install "libasound2t64", treat the old
# name as claimable when the new name is present.
normalized_pending = _normalize_pending_debian(pending, packages)
claim_pending = tuple(sorted(normalized_pending & present))
missing = tuple(p for p in packages if p not in present)
Comment on lines +91 to +98
system_step = SystemPackagesStep(manager="apt", packages=missing)
elif family == "suse-family":
present = rpm_installed(plat.SUSE_PACKAGES)
Expand Down
23 changes: 23 additions & 0 deletions src/vip/install/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,29 @@ def _read_os_release() -> dict[str, str]:
"libglib2.0-0",
)

# Ubuntu 24.04+ renamed libasound2 → libasound2t64 as part of the 64-bit time_t
# transition. Unlike other t64 renames (libcups2 → libcups2t64) that apt resolves
# automatically, libasound2 became a virtual package with multiple providers, so
# apt refuses to install it directly. We swap it for the concrete name on >= 24.04.
_T64_LIBASOUND_MIN_VERSION = "24.04"


def _is_ubuntu_like(platform_info: PlatformInfo) -> bool:
"""True when the distro is Ubuntu or an Ubuntu derivative (e.g. Pop!_OS)."""
if platform_info.id == "ubuntu":
return True
raw = platform_info.raw or {}
return "ubuntu" in raw.get("ID_LIKE", "").lower().split()


def debian_packages(platform_info: PlatformInfo) -> tuple[str, ...]:
"""Return DEBIAN_PACKAGES with version-appropriate substitutions."""
version = platform_info.version or ""
if _is_ubuntu_like(platform_info) and version >= _T64_LIBASOUND_MIN_VERSION:
return tuple("libasound2t64" if p == "libasound2" else p for p in DEBIAN_PACKAGES)
return DEBIAN_PACKAGES


# When updating the playwright pin in pyproject.toml, review DEBIAN_PACKAGES against
# https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/registry/nativeDeps.ts
# and update this value. The selftest enforces that they match.
Expand Down
Loading