From 95be11130ef8789db8c5bbb73bb4fdf9128b0320 Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Mon, 13 Feb 2023 13:52:00 +0100 Subject: [PATCH] core.Action: Add src_pkg_object and tgt_pkg_object properties providing object wrappers to file lists --- osc/_private/__init__.py | 1 + osc/_private/package.py | 23 +++++++++++++++++++++++ osc/core.py | 36 ++++++++++++++++++++++++++++++++++++ osc/store.py | 10 ++++++++++ tests/test_store.py | 23 +++++++++++++++++++++++ 5 files changed, 93 insertions(+) diff --git a/osc/_private/__init__.py b/osc/_private/__init__.py index 868c741e..e26b045c 100644 --- a/osc/_private/__init__.py +++ b/osc/_private/__init__.py @@ -13,4 +13,5 @@ from .api_source import release from .common import print_msg from .common import format_msg_project_package_options from .package import ApiPackage +from .package import LocalPackage from .request import forward_request diff --git a/osc/_private/package.py b/osc/_private/package.py index c1fca72e..3f767155 100644 --- a/osc/_private/package.py +++ b/osc/_private/package.py @@ -1,5 +1,6 @@ import functools +from .. import oscerr from . import api @@ -18,6 +19,7 @@ class PackageBase: self.files = [] directory_node = self._get_directory_node() self._load_from_directory_node(directory_node) + self._meta_node = None def __str__(self): return f"{self.project}/{self.name}" @@ -60,6 +62,19 @@ class PackageBase: # the name is omitted and we want it present for overall sanity self.linkinfo.package = self.name + def _get_meta_node(self): + raise NotImplementedError() + + def get_meta_value(self, option): + if not self._meta_node: + self._meta_node = self._get_meta_node() + if not self._meta_node: + return None + node = api.find_node(self._meta_node, "package", option) + if node is None or not node.text: + raise oscerr.APIError(f"Couldn't get '{option}' from package _meta") + return node.text + class ApiPackage(PackageBase): def __init__(self, apiurl, project, package, rev=None): @@ -75,6 +90,11 @@ class ApiPackage(PackageBase): url_query["rev"] = self.__rev return api.get(self.apiurl, url_path, url_query) + def _get_meta_node(self): + url_path = ["source", self.project, self.name, "_meta"] + url_query = {} + return api.get(self.apiurl, url_path, url_query) + class LocalPackage(PackageBase): def __init__(self, path): @@ -86,3 +106,6 @@ class LocalPackage(PackageBase): def _get_directory_node(self): return self.store.read_xml_node("_files", "directory").getroot() + + def _get_meta_node(self): + return self.store._meta_node diff --git a/osc/core.py b/osc/core.py index 5174f2c9..ab1bc305 100644 --- a/osc/core.py +++ b/osc/core.py @@ -2880,6 +2880,8 @@ class Action: def __init__(self, type, **kwargs): self.apiurl = kwargs.pop("apiurl", None) + self._src_pkg_object = None + self._tgt_pkg_object = None if type not in Action.type_args.keys(): raise oscerr.WrongArgs('invalid action type: \'%s\'' % type) self.type = type @@ -2890,6 +2892,40 @@ class Action: for i in Action.type_args[type]: setattr(self, i, kwargs.get(i)) + @property + def src_pkg_object(self): + if not getattr(self, "src_project", None) or not getattr(self, "src_package", None): + return None + if not self._src_pkg_object: + src_rev = getattr(self, "src_rev", None) + self._src_pkg_object = _private.ApiPackage(self.apiurl, self.src_project, self.src_package, src_rev) + return self._src_pkg_object + + @property + def tgt_pkg_object(self): + if not self._tgt_pkg_object: + if self.type == "maintenance_incident": + # the target project for maintenance incidents is virtual and cannot be queried + # the actual target project is in the "releaseproject" attribute + # + # tgt_releaseproject is always set for a maintenance_incident + # pylint: disable=no-member + tgt_project = self.tgt_releaseproject + + # the target package is not specified + # we need to extract it from source package's _meta + src_package_meta_releasename = self.src_pkg_object.get_meta_value("releasename") + tgt_package = src_package_meta_releasename.split(".")[0] + else: + if not getattr(self, "tgt_project", None) or not getattr(self, "tgt_package", None): + return None + # tgt_project and tgt_package are checked above + # pylint: disable=no-member + tgt_project = self.tgt_project + tgt_package = self.tgt_package + self._tgt_pkg_object = _private.ApiPackage(self.apiurl, tgt_project, tgt_package) + return self._tgt_pkg_object + def to_xml(self): """ Serialize object to XML. diff --git a/osc/store.py b/osc/store.py index fd6d306f..a4f8f4fa 100644 --- a/osc/store.py +++ b/osc/store.py @@ -299,3 +299,13 @@ class Store: if len(value) != 3: raise ValueError("A list with exactly 3 items is expected: [repo, arch, vm_type]") self.write_list("_last_buildroot", value) + + @property + def _meta_node(self): + if not self.exists("_meta"): + return None + if self.is_package: + root = self.read_xml_node("_meta", "package").getroot() + else: + root = self.read_xml_node("_meta", "project").getroot() + return root diff --git a/tests/test_store.py b/tests/test_store.py index 58a1e751..f77cf613 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -203,6 +203,29 @@ class TestStore(unittest.TestCase): store2 = Store(self.tmpdir) self.assertEqual(store2.last_buildroot, ["repo", "arch", "vm_type"]) + def test_meta_node(self): + self.store.write_string( + "_meta", + """ + title + desc + name + + + + +""", + ) + node = self.store._meta_node + self.assertNotEqual(node, None) + + # try to read the _meta via a package class + from osc._private import LocalPackage + + self.store.files = [] + pkg = LocalPackage(self.tmpdir) + self.assertEqual(pkg.get_meta_value("releasename"), "name") + if __name__ == "__main__": unittest.main()