diff --git a/SOURCES/0001-dir2module-generate-also-profiles-and-modulemd-defau.patch b/SOURCES/0001-dir2module-generate-also-profiles-and-modulemd-defau.patch new file mode 100644 index 0000000..1a6a343 --- /dev/null +++ b/SOURCES/0001-dir2module-generate-also-profiles-and-modulemd-defau.patch @@ -0,0 +1,193 @@ +From 908b48d28cf68946dfe2f309d73f3f2a12efe021 Mon Sep 17 00:00:00 2001 +From: Jakub Kadlcik +Date: Thu, 19 Aug 2021 00:33:59 +0200 +Subject: [PATCH] dir2module: generate also profiles and modulemd-defaults file + +--- + dir2module/dir2module/dir2module.py | 102 ++++++++++++++++++++++++---- + dir2module/tests/conftest.py | 2 +- + 2 files changed, 90 insertions(+), 14 deletions(-) + +diff --git a/dir2module/dir2module/dir2module.py b/dir2module/dir2module/dir2module.py +index 9902aec..6643a83 100755 +--- a/dir2module/dir2module/dir2module.py ++++ b/dir2module/dir2module/dir2module.py +@@ -20,13 +20,13 @@ gi.require_version("Modulemd", "2.0") + from gi.repository import Modulemd # noqa: E402 + + +-class Module(object): ++class ModuleBase: + """ +- Provide a high-level interface for representing modules and yaml generation +- based on their values. ++ Base class for modulemd things + """ ++ + def __init__(self, name, stream, version, context, arch, summary, +- description, module_license, licenses, package_nevras, requires): ++ description, module_license, licenses, packages, requires): + self.name = name + self.stream = stream + self.version = version +@@ -36,18 +36,69 @@ class Module(object): + self.description = description + self.module_license = module_license + self.licenses = licenses +- self.package_nevras = package_nevras ++ self.packages = packages + self.requires = requires + ++ @property ++ def filename_format(self): ++ """ ++ String format for the modulemd filename. It can contain the following ++ variables: ++ {N} - Module name ++ {S} - Module stream name ++ {V} - Module version ++ {C} - Module context ++ {A} - Module architecture ++ """ ++ raise NotImplementedError ++ ++ def dumps(self): ++ """ ++ Generate YAML based on input parameters and return it as a string ++ """ ++ raise NotImplementedError ++ + @property + def filename(self): + """ + Generate filename for a module yaml + """ +- return "{N}:{S}:{V}:{C}:{A}.modulemd.yaml".format( ++ return self.filename_format.format( + N=self.name, S=self.stream, V=self.version, + C=self.context, A=self.arch) + ++ def dump(self): ++ """ ++ Generate modulemd yaml based on input parameters write it into file ++ """ ++ with open(self.filename, "w") as moduleyaml: ++ moduleyaml.write(self.dumps()) ++ ++ @property ++ def package_names(self): ++ """ ++ Return the list of unique package names within this module ++ """ ++ return {package.header.name for package in self.packages} ++ ++ @property ++ def package_nevras(self): ++ """ ++ Return the list of unique package NEVRAs within this module ++ """ ++ return {package.nevra for package in self.packages} ++ ++ ++class Module(ModuleBase): ++ """ ++ Provide a high-level interface for representing modules and yaml generation ++ based on their values. ++ """ ++ ++ @property ++ def filename_format(self): ++ return "{N}:{S}:{V}:{C}:{A}.modulemd.yaml" ++ + def dumps(self): + """ + Generate modulemd yaml based on input parameters and return it as a string +@@ -70,16 +121,37 @@ class Module(object): + dependencies.add_runtime_stream(depname, depstream) + mod_stream.add_dependencies(dependencies) + ++ profile = Modulemd.Profile.new("common") ++ for pkgname in self.package_names: ++ profile.add_rpm(pkgname) ++ mod_stream.add_profile(profile) ++ + index = Modulemd.ModuleIndex.new() + index.add_module_stream(mod_stream) + return index.dump_to_string() + +- def dump(self): ++ ++class ModuleDefaults(ModuleBase): ++ """ ++ Provide a high-level interface for representing modulemd defaults files ++ """ ++ ++ @property ++ def filename_format(self): ++ return "{N}:{S}:{V}:{C}:{A}.modulemd-defaults.yaml" ++ ++ def dumps(self): + """ +- Generate modulemd yaml based on input parameters write it into file ++ Generate modulemd_defaults yaml based on input parameters and return it ++ as a string + """ +- with open(self.filename, "w") as moduleyaml: +- moduleyaml.write(self.dumps()) ++ mod_defaults = Modulemd.DefaultsV1.new(self.name) ++ mod_defaults.set_default_stream(self.stream) ++ mod_defaults.add_default_profile_for_stream(self.stream, "common") ++ ++ index = Modulemd.ModuleIndex.new() ++ index.add_defaults(mod_defaults) ++ return index.dump_to_string() + + + class Package(object): +@@ -220,7 +292,6 @@ def main(): + + packages = [Package(package) for package in packages] + licenses = {package.license for package in packages} +- nevras = {package.nevra for package in packages} + + requires = parse_dependencies(args.requires) + description = args.description \ +@@ -238,8 +309,10 @@ def main(): + raise RuntimeError("All packages need to contain the `modularitylabel` header. " + "To suppress this constraint, use `--force` parameter") + +- module = Module(name, stream, version, context, arch, args.summary, +- description, args.license, licenses, nevras, requires) ++ modargs = [name, stream, version, context, arch, args.summary, description, ++ args.license, licenses, packages, requires] ++ module = Module(*modargs) ++ module_defaults = ModuleDefaults(*modargs) + + if args.stdout: + print(module.dumps()) +@@ -247,6 +320,9 @@ def main(): + module.dump() + print("Created {0}".format(module.filename)) + ++ module_defaults.dump() ++ print("Created {0}".format(module_defaults.filename)) ++ + + if __name__ == "__main__": + try: +diff --git a/dir2module/tests/conftest.py b/dir2module/tests/conftest.py +index 9309b44..c6956cb 100644 +--- a/dir2module/tests/conftest.py ++++ b/dir2module/tests/conftest.py +@@ -15,7 +15,7 @@ def dummy_module(): + 'description': 'One dummy module for your tests', + 'module_license': 'No License', + 'licenses': [], +- 'package_nevras': [], ++ 'packages': [], + 'requires': {} + } + +-- +2.41.0 + diff --git a/SOURCES/0002-repo2module-don-t-traceback-because-of-a-modular-SRP.patch b/SOURCES/0002-repo2module-don-t-traceback-because-of-a-modular-SRP.patch new file mode 100644 index 0000000..ee921ee --- /dev/null +++ b/SOURCES/0002-repo2module-don-t-traceback-because-of-a-modular-SRP.patch @@ -0,0 +1,30 @@ +From 1188c8215955c14fadb1f17c2b55f4135db2a224 Mon Sep 17 00:00:00 2001 +From: Jakub Kadlcik +Date: Tue, 2 May 2023 00:12:39 +0200 +Subject: [PATCH 2/4] repo2module: don't traceback because of a modular SRPM in + the repo + +Fix RHBZ 2186223 +--- + repo2module/repo2module/cli.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/repo2module/repo2module/cli.py b/repo2module/repo2module/cli.py +index 1d4ce27..44dd24c 100644 +--- a/repo2module/repo2module/cli.py ++++ b/repo2module/repo2module/cli.py +@@ -61,6 +61,11 @@ def get_source_packages(packages): + """ + source_packages = set() + for pkg in packages: ++ # In this case, the `pkg` is a SRPM file ++ if not pkg.rpm_sourcerpm: ++ source_packages.add(pkg.name) ++ continue ++ + # Get the source RPM NEVRA without the trailing ".rpm" + subject = Subject(pkg.rpm_sourcerpm[:-4]) + +-- +2.41.0 + diff --git a/SOURCES/0003-createrepo-replace-deprecated-LooseVersion.patch b/SOURCES/0003-createrepo-replace-deprecated-LooseVersion.patch new file mode 100644 index 0000000..8f40efa --- /dev/null +++ b/SOURCES/0003-createrepo-replace-deprecated-LooseVersion.patch @@ -0,0 +1,133 @@ +From f3435b9a9f5ddff0d4593dbda7c5da592ea31f61 Mon Sep 17 00:00:00 2001 +From: Marek Kulik +Date: Wed, 20 Apr 2022 09:38:51 +0200 +Subject: [PATCH 3/4] createrepo: replace deprecated LooseVersion + +DeprecationWarning: The distutils package is +deprecated and slated for removal in Python 3.12 +--- + .../createrepo_mod/createrepo_mod.py | 13 +++++-- + modulemd_tools/modulemd_tools/yaml.py | 39 +++++++++++++++++++ + modulemd_tools/tests/test_yaml.py | 9 ++++- + 3 files changed, 55 insertions(+), 6 deletions(-) + +diff --git a/createrepo_mod/createrepo_mod/createrepo_mod.py b/createrepo_mod/createrepo_mod/createrepo_mod.py +index ee078f7..4e0eb71 100755 +--- a/createrepo_mod/createrepo_mod/createrepo_mod.py ++++ b/createrepo_mod/createrepo_mod/createrepo_mod.py +@@ -16,11 +16,16 @@ https://docs.fedoraproject.org/en-US/modularity/hosting-modules/ + """ + + ++import argparse + import os +-import sys + import subprocess +-import argparse +-from distutils.version import LooseVersion ++import sys ++ ++# python3-packaging in not available in RHEL 8.x ++try: ++ from packaging.version import Version ++except ModuleNotFoundError: ++ from distutils.version import LooseVersion as Version + + import gi + gi.require_version("Modulemd", "2.0") +@@ -99,7 +104,7 @@ def createrepo_c_with_builtin_module_support(): + """ + cmd = ["rpm", "-q", "createrepo_c", "--queryformat", "%{VERSION}"] + createrepo_c_version = subprocess.check_output(cmd).decode("utf-8") +- return LooseVersion(createrepo_c_version) >= LooseVersion("0.16.1") ++ return Version(createrepo_c_version) >= Version("0.16.1") + + + def main(): +diff --git a/modulemd_tools/modulemd_tools/yaml.py b/modulemd_tools/modulemd_tools/yaml.py +index 43f314f..ac644b6 100644 +--- a/modulemd_tools/modulemd_tools/yaml.py ++++ b/modulemd_tools/modulemd_tools/yaml.py +@@ -8,6 +8,12 @@ import os + import gi + import yaml + ++# python3-packaging in not available in RHEL 8.x ++try: ++ from packaging.version import Version ++except ModuleNotFoundError: ++ from distutils.version import StrictVersion as Version ++ + gi.require_version("Modulemd", "2.0") + from gi.repository import Modulemd # noqa: E402 + +@@ -307,3 +313,36 @@ def _stream2yaml(mod_stream): + return idx.dump_to_string() + except gi.repository.GLib.GError as ex: + raise RuntimeError(ex.message) ++ ++ ++def _modulemd_read_packager_string(mod_yaml, name=None, stream=None): ++ """ ++ For the time being we happen to be in a transition state when ++ `Modulemd.ModuleStream.read_string` is deprecated and throws warnings on ++ Fedora but we still use old libmodulemd (2.9.4) on RHEL8, which doesn't ++ provide its replacement in the form of `Modulemd.read_packager_string`. ++ """ ++ if Version(Modulemd.get_version()) < Version("2.11"): ++ mod_stream = Modulemd.ModuleStreamV2.new(name, stream) ++ mod_stream = mod_stream.read_string(mod_yaml, True, name, stream) ++ return mod_stream ++ ++ return Modulemd.read_packager_string(mod_yaml, name, stream) ++ ++ ++def _modulestream_upgrade_ext(mod_stream, version): ++ """ ++ For the time being we happen to be in a transition state when ++ `Modulemd.ModuleStream.upgrade` is deprecated and throws warnings on ++ Fedora but we still use old libmodulemd (2.9.4) on RHEL8, which doesn't ++ provide its replacement in the form of `Modulemd.ModuleStream.upgrade_ext`. ++ """ ++ if Version(Modulemd.get_version()) < Version("2.10"): ++ return mod_stream.upgrade(version) ++ ++ mod_upgraded = mod_stream.upgrade_ext(version) ++ return mod_upgraded.get_stream_by_NSVCA( ++ mod_stream.get_stream_name(), ++ mod_stream.get_version(), ++ mod_stream.get_context(), ++ mod_stream.get_arch()) +diff --git a/modulemd_tools/tests/test_yaml.py b/modulemd_tools/tests/test_yaml.py +index 8090a2b..f51a330 100644 +--- a/modulemd_tools/tests/test_yaml.py ++++ b/modulemd_tools/tests/test_yaml.py +@@ -2,10 +2,15 @@ import os + import unittest + from unittest import mock + import yaml +-from distutils.version import LooseVersion + from modulemd_tools.yaml import (is_valid, validate, create, update, dump, + upgrade, _yaml2stream, _stream2yaml) + ++# python3-packaging in not available in RHEL 8.x ++try: ++ from packaging.version import Version ++except ModuleNotFoundError: ++ from distutils.version import LooseVersion as Version ++ + import gi + gi.require_version("Modulemd", "2.0") + from gi.repository import Modulemd # noqa: E402 +@@ -19,7 +24,7 @@ def old_libmodulemd(): + skip those few test on EPEL8 until it receives an update. + See also `080e2bb` + """ +- return LooseVersion(Modulemd.get_version()) < LooseVersion("2.11.1") ++ return Version(Modulemd.get_version()) < Version("2.11.1") + + + class TestYaml(unittest.TestCase): +-- +2.41.0 + diff --git a/SOURCES/0004-modulemd_tools-fix-tests-for-new-libmodulemd-version.patch b/SOURCES/0004-modulemd_tools-fix-tests-for-new-libmodulemd-version.patch new file mode 100644 index 0000000..1c64afd --- /dev/null +++ b/SOURCES/0004-modulemd_tools-fix-tests-for-new-libmodulemd-version.patch @@ -0,0 +1,47 @@ +From 0407a6af2f0c59e9619418a606d5d00183d40693 Mon Sep 17 00:00:00 2001 +From: Jakub Kadlcik +Date: Tue, 13 Jun 2023 18:19:15 +0200 +Subject: [PATCH 4/4] modulemd_tools: fix tests for new libmodulemd version + 2.15.0 + +--- + modulemd_tools/tests/test_yaml.py | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +diff --git a/modulemd_tools/tests/test_yaml.py b/modulemd_tools/tests/test_yaml.py +index f51a330..2f8b4d2 100644 +--- a/modulemd_tools/tests/test_yaml.py ++++ b/modulemd_tools/tests/test_yaml.py +@@ -27,6 +27,10 @@ def old_libmodulemd(): + return Version(Modulemd.get_version()) < Version("2.11.1") + + ++def min_libmodulemd_version(version): ++ return Version(Modulemd.get_version()) >= Version(version) ++ ++ + class TestYaml(unittest.TestCase): + + def test_is_valid(self): +@@ -57,9 +61,16 @@ class TestYaml(unittest.TestCase): + self.assertEqual(mod1["version"], 2) + self.assertEqual(mod1["data"]["name"], "foo") + self.assertEqual(mod1["data"]["stream"], "stable") +- self.assertEqual(mod1["data"]["summary"], None) + self.assertEqual(mod1["data"]["description"], "") +- self.assertEqual(mod1["data"]["license"]["module"], [None]) ++ ++ # Between libmodulemd version 2.14.0 and 2.15.0 a change in `None` ++ # vs empty string happened ++ if min_libmodulemd_version("2.15.0"): ++ self.assertEqual(mod1["data"]["summary"], "") ++ self.assertEqual(mod1["data"]["license"]["module"], [""]) ++ else: ++ self.assertEqual(mod1["data"]["summary"], None) ++ self.assertEqual(mod1["data"]["license"]["module"], [None]) + + def test_update_after_build(self): + """ +-- +2.41.0 + diff --git a/SPECS/modulemd-tools.spec b/SPECS/modulemd-tools.spec index 0c9249f..4254991 100644 --- a/SPECS/modulemd-tools.spec +++ b/SPECS/modulemd-tools.spec @@ -1,6 +1,6 @@ Name: modulemd-tools Version: 0.9 -Release: 3%{?dist} +Release: 5%{?dist} Summary: Collection of tools for parsing and generating modulemd YAML files License: MIT BuildArch: noarch @@ -8,6 +8,18 @@ BuildArch: noarch URL: https://github.com/rpm-software-management/modulemd-tools Source0: https://github.com/rpm-software-management/modulemd-tools/archive/%{version}/%{name}-%{version}.tar.gz +# https://github.com/rpm-software-management/modulemd-tools/commit/195df77 +Patch0: 0001-dir2module-generate-also-profiles-and-modulemd-defau.patch + +# https://github.com/rpm-software-management/modulemd-tools/commit/0d718ca +Patch1: 0002-repo2module-don-t-traceback-because-of-a-modular-SRP.patch + +# https://github.com/rpm-software-management/modulemd-tools/commit/cd04198 +Patch2: 0003-createrepo-replace-deprecated-LooseVersion.patch + +# https://github.com/rpm-software-management/modulemd-tools/commit/ac3b173 +Patch3: 0004-modulemd_tools-fix-tests-for-new-libmodulemd-version.patch + BuildRequires: createrepo_c BuildRequires: argparse-manpage BuildRequires: python3-devel @@ -50,7 +62,7 @@ modulemd-generate-macros - Generate module-build-macros SRPM package, which is %prep -%setup -q +%autosetup -p1 %build @@ -156,6 +168,17 @@ cd .. %changelog +* Sun Jul 30 2023 Jakub Kadlcik - 0.9-5 +- Don't traceback because of a modular SRPM in the repo + Related: rhbz#2227436 +- Replace deprecated LooseVersion +- Fix tests for new libmodulemd version 2.15.0 + +* Fri Jul 21 2023 Jakub Kadlcik - 0.9-4 +- Generate profiles section and modulemd-defaults file + Related: rhbz#1801747 +- Use autosetup to automatically apply patches + * Mon Aug 09 2021 Mohan Boddu - 0.9-3 - Rebuilt for IMA sigs, glibc 2.34, aarch64 flags Related: rhbz#1991688 @@ -181,7 +204,7 @@ cd .. - Drop libmodulemd dependency in favor of python3-libmodulemd * Sun Nov 22 2020 Jakub Kadlcik 0.6-1 -- Generate manpages for all tools in this repository +- Generate manpages for all tools in this repository - modulemd-generate-macros: add a tool for generating module-build-macros - modulemd_tools: add the first pieces of a python library (for internal usage only)