From af4b817c217ef0ffee91b40afa48e68a315ef03a Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Sat, 10 Mar 2018 13:15:30 +0000 Subject: [PATCH] Skip virtual packages when parsing APKINDEX (#1278) Since PR #1247 we are using the virtual package `.pmbootstrap` to mark packages as not explcitly installed. In case `pmbootstrap` doesn't finish the installation because of a bug, the `.pmbootstrap` virtual package may not get uninstalled. As virtual packages don't have the "timestamp" attribute set in the package database (which indicates when the package was built), the APKINDEX parser fails to parse them. Changes: * pmb.parse.apkindex.parse_next_block(): don't require the "timestamp" attribute to be set (but the arch attribute instead, which is always present) * pmb.parse.apkindex.parse(): when a block does not have a `timestamp` attribute, skip it, because it must be a virtual package. * add test cases for both functions with a package database that contains a virtual package. --- pmb/parse/apkindex.py | 15 ++++++- test/test_parse_apkindex.py | 54 ++++++++++++++++++++++++++ test/testdata/apkindex/virtual_package | 31 +++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 test/testdata/apkindex/virtual_package diff --git a/pmb/parse/apkindex.py b/pmb/parse/apkindex.py index 38ac5531..a1c64d1e 100644 --- a/pmb/parse/apkindex.py +++ b/pmb/parse/apkindex.py @@ -37,9 +37,16 @@ def parse_next_block(args, path, lines, start): :returns: a dictionary with the following structure: { "arch": "noarch", "depends": ["busybox-extras", "lddtree", ... ], + "origin": "postmarketos-mkinitfs", "pkgname": "postmarketos-mkinitfs", "provides": ["mkinitfs=0.0.1"], + "timestamp": "1500000000", "version": "0.0.4-r10" } + NOTE: "depends" is not set for packages without any dependencies, + e.g. musl. + NOTE: "timestamp" and "origin" are not set for virtual packages + (#1273). We use that information to skip these virtual + packages in parse(). :returns: None, when there are no more blocks """ @@ -77,7 +84,7 @@ def parse_next_block(args, path, lines, start): # Format and return the block if end_of_block_found: # Check for required keys - for key in ["pkgname", "version", "timestamp"]: + for key in ["arch", "pkgname", "version"]: if key not in ret: raise RuntimeError("Missing required key '" + key + "' in block " + str(ret) + ", file: " + path) @@ -209,6 +216,12 @@ def parse(args, path, multiple_providers=True): if not block: break + # Skip virtual packages + if "timestamp" not in block: + logging.verbose("Skipped virtual package " + str(block) + " in" + " file: " + path) + continue + # Add the next package and all aliases parse_add_block(ret, block, None, multiple_providers) if "provides" in block: diff --git a/test/test_parse_apkindex.py b/test/test_parse_apkindex.py index f964aa31..7424e2b2 100644 --- a/test/test_parse_apkindex.py +++ b/test/test_parse_apkindex.py @@ -101,6 +101,42 @@ def test_parse_next_block_no_error(args): assert start == [45] +def test_parse_next_block_virtual(args): + """ + Test parsing a virtual package from an APKINDEX. + """ + # Read the file + func = pmb.parse.apkindex.parse_next_block + path = pmb.config.pmb_src + "/test/testdata/apkindex/virtual_package" + with open(path, "r", encoding="utf-8") as handle: + lines = handle.readlines() + + # First block + start = [0] + block = {'arch': 'x86_64', + 'depends': ['so:libc.musl-x86_64.so.1'], + 'origin': 'hello-world', + 'pkgname': 'hello-world', + 'provides': ['cmd:hello-world'], + 'timestamp': '1500000000', + 'version': '2-r0'} + assert func(args, path, lines, start) == block + assert start == [20] + + # Second block: virtual package + block = {'arch': 'noarch', + 'depends': ['hello-world'], + 'pkgname': '.pmbootstrap', + 'provides': [], + 'version': '0'} + assert func(args, path, lines, start) == block + assert start == [31] + + # No more blocks + assert func(args, path, lines, start) is None + assert start == [31] + + def test_parse_add_block(args): func = pmb.parse.apkindex.parse_add_block multiple_providers = False @@ -231,6 +267,24 @@ def test_parse(args): assert args.cache["apkindex"][path]["multiple"] == ret_multiple +def test_parse_virtual(args): + """ + This APKINDEX contains a virtual package .pbmootstrap. It must not be part + of the output. + """ + path = pmb.config.pmb_src + "/test/testdata/apkindex/virtual_package" + block = {'arch': 'x86_64', + 'depends': ['so:libc.musl-x86_64.so.1'], + 'origin': 'hello-world', + 'pkgname': 'hello-world', + 'provides': ['cmd:hello-world'], + 'timestamp': '1500000000', + 'version': '2-r0'} + ret = {"hello-world": block, "cmd:hello-world": block} + assert pmb.parse.apkindex.parse(args, path, False) == ret + assert args.cache["apkindex"][path]["single"] == ret + + def test_providers_invalid_package(args, tmpdir): # Create empty APKINDEX path = str(tmpdir) + "/APKINDEX" diff --git a/test/testdata/apkindex/virtual_package b/test/testdata/apkindex/virtual_package new file mode 100644 index 00000000..fa2f24d1 --- /dev/null +++ b/test/testdata/apkindex/virtual_package @@ -0,0 +1,31 @@ +C:Q1XaZzCVZ9mvH8djPyEb5aUYhG3r4= +P:hello-world +V:2-r0 +A:x86_64 +S:2897 +I:20480 +T:hello world program to be built in the testsuite +U:https://en.wikipedia.org/wiki/%22Hello,_World!%22_program +L:MIT +o:hello-world +t:1500000000 +c: +D:so:libc.musl-x86_64.so.1 +p:cmd:hello-world +F:usr +F:usr/bin +R:hello-world +a:0:0:755 +Z:Q1ZjTpsnMchSsSwEPB1cTjihYuJvo= + +C:Q127l1Ui9vzedbeR3BMelZnSa4pwY= +P:.pmbootstrap +V:0 +A:noarch +S:0 +I:0 +T:virtual meta package +U: +L: +D:hello-world +