pmb.chroot.apk.install: let apk figure out depends (MR 2185)

Previously to this patch, pmbootstrap would pass a full dependency tree
to "apk add". It would use a virtual package to ensure only the right
packages get added to /etc/apk/world. For example:
  apk add -u --virtual .pmbootstrap postmarketos-base device-asus-me176c \
  postmarketos-ui-sxmo-de-sway device-asus-me176c-nonfree-firmware w3m \
  sfeed clickclack firefox-esr font-noto font-noto-emoji gnome-icon-theme \
  imv megapixels mobile-config-firefox ttyescape postmarketos-base-nofde \
  eudev openssh postmarketos-mkinitfs postmarketos-mvcfg postmarketos-keys \
  ...

Instead of doing that, only pass the packages we want to install and let
apk figure out the dependencies. Most of the time we can even avoid
using the virtual package now.

== Remaining edge case: locally built packages
apk will only upgrade a package with the same pkgver + pkgrel but a
different build date if the full path to an apk file gets passed as
argument. So if the user built a package locally that will be installed,
or one of its dependencies then we still need to use a virtual package
and possibly pass a dependency to apk. Replace
replace_aports_packages_with_path() with packages_get_locally_built_apks()
to get a list of such packages and adjust install() and
install_run_apk() to use it.
This commit is contained in:
Oliver Smith 2022-05-29 10:51:00 +02:00
parent 903ed4ee30
commit 6a74109154
No known key found for this signature in database
GPG Key ID: 5AE7F5513E0885CB
2 changed files with 121 additions and 39 deletions

View File

@ -134,53 +134,57 @@ def packages_split_to_add_del(packages):
return (to_add, to_del)
def replace_aports_packages_with_path(args, packages, suffix, arch):
def packages_get_locally_built_apks(args, packages, arch):
"""
apk will only re-install packages with the same pkgname,
pkgver and pkgrel, when you give it the absolute path to the package.
This function replaces all packages that were built locally,
with the absolute path to the package.
Iterate over packages and if existing, get paths to locally built packages.
This is used to force apk to upgrade packages to newer local versions, even
if the pkgver and pkgrel did not change.
:param packages: list of pkgnames
:param arch: architecture that the locally built packages should have
:returns: list of apk file paths that are valid inside the chroots, e.g.
["/mnt/pmbootstrap-packages/x86_64/hello-world-1-r6.apk", ...]
"""
channel = pmb.config.pmaports.read_config(args)["channel"]
ret = []
for package in packages:
aport = pmb.helpers.pmaports.find(args, package, False)
if aport:
data_repo = pmb.parse.apkindex.package(args, package, arch, False)
if not data_repo:
raise RuntimeError(f"{package}: could not find binary"
" package, although it should exist for"
" sure at this point in the code."
" Probably an APKBUILD subpackage parsing"
" bug. Related: https://gitlab.com/"
"postmarketOS/build.postmarketos.org/"
"issues/61")
apk_path = (f"/mnt/pmbootstrap-packages/{arch}/"
f"{package}-{data_repo['version']}.apk")
if os.path.exists(f"{args.work}/chroot_{suffix}{apk_path}"):
package = apk_path
ret.append(package)
data_repo = pmb.parse.apkindex.package(args, package, arch, False)
if not data_repo:
continue
apk_file = f"{package}-{data_repo['version']}.apk"
if not os.path.exists(f"{args.work}/packages/{channel}/{arch}/{apk_file}"):
continue
ret.append(f"/mnt/pmbootstrap-packages/{arch}/{apk_file}")
return ret
def install_run_apk(args, packages, to_add, to_del, suffix):
def install_run_apk(args, to_add, to_add_local, to_del, suffix):
"""
Run apk to add packages, and ensure only the desired packages get
explicitly marked as installed.
:param to_add: list of pkgnames to install, without their dependencies
:param to_add_local: return of packages_get_locally_built_apks()
:param to_del: list of pkgnames to be deleted, this should be set to
conflicting dependencies in any of the packages to be
installed or their dependencies (e.g. ["osk-sdl"])
:param suffix: the chroot suffix, e.g. "native" or "rootfs_qemu-amd64"
"""
# Split off conflicts
packages_without_conflicts = list(
filter(lambda p: not p.startswith("!"), packages))
commands = [["add"] + to_add]
# Use a virtual package to mark only the explicitly requested packages as
# explicitly installed, not their dependencies or specific paths (#1212)
commands = [["add"] + packages_without_conflicts]
if len(to_add) and packages_without_conflicts != to_add:
commands = [["add", "-u", "--virtual", ".pmbootstrap"] +
to_add,
["add"] + packages_without_conflicts,
["del", ".pmbootstrap"]]
if len(to_del):
commands.append(["del"] + to_del)
# explicitly installed, not the ones in to_add_local
if to_add_local:
commands += [["add", "-u", "--virtual", ".pmbootstrap"] + to_add_local,
["del", ".pmbootstrap"]]
if to_del:
commands += [["del"] + to_del]
for (i, command) in enumerate(commands):
if args.offline:
command = ["--no-network"] + command
@ -240,12 +244,10 @@ def install(args, packages, suffix="native", build=True):
message += f" {pkgname}"
logging.info(message)
# Local packages: Using the path instead of pkgname makes apk update
# packages of the same version if the build date is different
to_add = replace_aports_packages_with_path(args, to_add,
suffix, arch)
to_add_local = packages_get_locally_built_apks(args, to_add, arch)
to_add_no_deps, _ = packages_split_to_add_del(packages)
install_run_apk(args, packages, to_add, to_del, suffix)
install_run_apk(args, to_add_no_deps, to_add_local, to_del, suffix)
def installed(args, suffix="native"):

View File

@ -1,9 +1,11 @@
# Copyright 2022 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import fnmatch
import pytest
import sys
import pmb_test # noqa
import pmb.build
import pmb.chroot.apk
@ -63,3 +65,81 @@ def test_packages_split_to_add_del():
to_add, to_del = pmb.chroot.apk.packages_split_to_add_del(packages)
assert to_add == ["hello", "hello2", "test2"]
assert to_del == ["test", "test3"]
def test_packages_get_locally_built_apks(monkeypatch, args):
args.assume_yes = True
arch = pmb.config.arch_native
packages = ["hello-world", # will exist in repo and locally
"postmarketos-base", # will exist in repo only
"package-that-does-not-exist"] # will not exist at all
pmb.chroot.zap(args, pkgs_local=True)
pmb.build.package(args, "hello-world", force=True)
ret = pmb.chroot.apk.packages_get_locally_built_apks(args, packages, arch)
assert len(ret) == 1
assert fnmatch.fnmatch(ret[0], "*/hello-world-*.apk")
def test_install_run_apk(monkeypatch, args):
global cmds_progress
global cmds
func = pmb.chroot.apk.install_run_apk
suffix = "chroot_native"
def fake_chroot_root(args, command, suffix):
global cmds
cmds += [command]
monkeypatch.setattr(pmb.chroot, "root", fake_chroot_root)
def fake_apk_progress(args, command, chroot, suffix):
global cmds_progress
cmds_progress += [command]
monkeypatch.setattr(pmb.helpers.apk, "apk_with_progress", fake_apk_progress)
def reset_cmds():
global cmds_progress, cmds
cmds = []
cmds_progress = []
# Simple add
reset_cmds()
to_add = ["postmarketos-base", "device-ppp"]
to_add_local = []
to_del = []
func(args, to_add, to_add_local, to_del, suffix)
assert cmds_progress == [["apk", "add", "postmarketos-base", "device-ppp"]]
assert cmds == []
# Add and delete
reset_cmds()
to_add = ["postmarketos-base", "device-ppp"]
to_add_local = []
to_del = ["osk-sdl"]
func(args, to_add, to_add_local, to_del, suffix)
assert cmds_progress == [["apk", "add", "postmarketos-base", "device-ppp"]]
assert cmds == [["apk", "--no-progress", "del", "osk-sdl"]]
# Add with local package
reset_cmds()
to_add = ["postmarketos-base", "device-ppp"]
to_add_local = ["/tmp/device-ppp.apk"]
to_del = []
func(args, to_add, to_add_local, to_del, suffix)
assert cmds_progress == [["apk", "add", "postmarketos-base", "device-ppp"]]
assert cmds == [["apk", "--no-progress", "add", "-u", "--virtual",
".pmbootstrap", "/tmp/device-ppp.apk"],
["apk", "--no-progress", "del", ".pmbootstrap"]]
# Add with --no-network
reset_cmds()
args.offline = True
to_add = ["hello-world"]
to_add_local = []
to_del = []
func(args, to_add, to_add_local, to_del, suffix)
assert cmds_progress == [["apk", "--no-network", "add", "hello-world"]]
assert cmds == []