From f3dbcac41a547ee75ad52af82a8ba56f0c83eb7e Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Oct 03 2016 14:52:58 +0000 Subject: [PATCH 1/4] getRPMDeps exposes additional deps Adding Conflicts, Suggests, Enhances, Supplements and Recommend header fields in addition to original Requires, Conflicts, Obsoletes and Provides relates: https://pagure.io/koji/issue/142 --- diff --git a/hub/kojihub.py b/hub/kojihub.py index be21e1c..f1b4cec 100644 --- a/hub/kojihub.py +++ b/hub/kojihub.py @@ -9520,7 +9520,7 @@ class RootExports(object): results = [] - for dep_name in ['REQUIRE', 'PROVIDE', 'CONFLICT', 'OBSOLETE']: + for dep_name in ['REQUIRE', 'PROVIDE', 'CONFLICT', 'OBSOLETE', 'SUGGEST', 'ENHANCE', 'SUPPLEMENT', 'RECOMMEND']: dep_id = getattr(koji, 'DEP_' + dep_name) if depType is None or depType == dep_id: fields = koji.get_header_fields(rpm_path, [dep_name + 'NAME', diff --git a/koji/__init__.py b/koji/__init__.py index 2252e4f..64435a7 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -182,6 +182,10 @@ DEP_REQUIRE = 0 DEP_PROVIDE = 1 DEP_OBSOLETE = 2 DEP_CONFLICT = 3 +DEP_SUGGEST = 4 +DEP_ENHANCE = 5 +DEP_SUPPLEMENT = 6 +DEP_RECOMMEND = 7 #dependency flags RPMSENSE_LESS = 2 diff --git a/tests/data/rpms/test-deps-1-1.fc24.x86_64.rpm b/tests/data/rpms/test-deps-1-1.fc24.x86_64.rpm new file mode 100644 index 0000000..975b2ea Binary files /dev/null and b/tests/data/rpms/test-deps-1-1.fc24.x86_64.rpm differ diff --git a/tests/data/specs/test-deps.spec b/tests/data/specs/test-deps.spec new file mode 100644 index 0000000..d31312d --- /dev/null +++ b/tests/data/specs/test-deps.spec @@ -0,0 +1,28 @@ +Name: test-deps +Version: 1 +Release: 1%{?dist} +Summary: Testing dependency header fields + +License: none + +Requires: require1 +Requires: require2 +Provides: provide1 +Provides: provide2 +Obsoletes: obsoletes1 +Obsoletes: obsoletes2 +Conflicts: conflicts1 +Conflicts: conflicts2 +Suggests: suggests1 +Suggests: suggests2 +Enhances: enhances1 +Enhances: enhances2 +Supplements: supplements1 +Supplements: supplements2 +Recommends: recommends1 +Recommends: recommends2 + +%description +Testing dependency header fields + +%files diff --git a/tests/test_hub/test_getRPMDeps.py b/tests/test_hub/test_getRPMDeps.py new file mode 100644 index 0000000..c87c59f --- /dev/null +++ b/tests/test_hub/test_getRPMDeps.py @@ -0,0 +1,41 @@ +import mock +import os +import unittest + +import koji +import kojihub + + +class TestGetRPMDeps(unittest.TestCase): + @mock.patch('kojihub.get_rpm') + @mock.patch('kojihub.get_build') + @mock.patch('koji.pathinfo') + def test_getRPMDeps(self, pi, build, rpm): + pi.build.return_value = os.path.join(os.path.dirname(__file__), '../data/rpms') + pi.rpm.return_value = 'test-deps-1-1.fc24.x86_64.rpm' + getRPMDeps = kojihub.RootExports().getRPMDeps + res = getRPMDeps('') + self.assertEqual(len(res), 22) + types = set([x['type'] for x in res]) + self.assertEqual(set([koji.DEP_REQUIRE, + koji.DEP_PROVIDE, + koji.DEP_OBSOLETE, + koji.DEP_CONFLICT, + koji.DEP_SUGGEST, + koji.DEP_ENHANCE, + koji.DEP_SUPPLEMENT, + koji.DEP_RECOMMEND, + ]), types) + + # test correct mapping of names + result = {} + for r in res: + result.setdefault(r['type'], set()).add(r['name']) + self.assertIn('require1', result[koji.DEP_REQUIRE]) + self.assertIn('provide1', result[koji.DEP_PROVIDE]) + self.assertIn('obsoletes1', result[koji.DEP_OBSOLETE]) + self.assertIn('conflicts1', result[koji.DEP_CONFLICT]) + self.assertIn('suggests1', result[koji.DEP_SUGGEST]) + self.assertIn('enhances1', result[koji.DEP_ENHANCE]) + self.assertIn('supplements1', result[koji.DEP_SUPPLEMENT]) + self.assertIn('recommends1', result[koji.DEP_RECOMMEND]) diff --git a/tests/test_parsers.py b/tests/test_parsers.py index 93de88a..c41fbd1 100644 --- a/tests/test_parsers.py +++ b/tests/test_parsers.py @@ -2,9 +2,12 @@ """Test the __init__.py module""" -import koji +import os +import rpm import unittest +import koji + class INITTestCase(unittest.TestCase): """Main test case container""" @@ -63,5 +66,35 @@ class INITTestCase(unittest.TestCase): self.assertEqual(ret['arch'], "src") self.assertEqual(ret['src'], True) + +class HeaderTestCase(unittest.TestCase): + rpm_path = os.path.join(os.path.dirname(__file__), 'data/rpms/test-deps-1-1.fc24.x86_64.rpm') + + def setUp(self): + self.fd = open(self.rpm_path) + + def tearDown(self): + self.fd.close() + + def test_get_rpm_header(self): + self.assertRaises(IOError, koji.get_rpm_header, 'nonexistent_path') + self.assertRaises(AttributeError, koji.get_rpm_header, None) + self.assertIsInstance(koji.get_rpm_header(self.rpm_path), rpm.hdr) + self.assertIsInstance(koji.get_rpm_header(self.fd), rpm.hdr) + # TODO: + # test ts + + def test_get_header_fields(self): + # incorrect + self.assertRaises(IOError, koji.get_header_fields, 'nonexistent_path', []) + self.assertRaises(koji.GenericError, koji.get_header_fields, self.rpm_path, 'nonexistent_header') + self.assertEqual(koji.get_header_fields(self.rpm_path, []), {}) + + # correct + self.assertEqual(['REQUIRES'], koji.get_header_fields(self.rpm_path, ['REQUIRES']).keys()) + self.assertEqual(['PROVIDES', 'REQUIRES'], sorted(koji.get_header_fields(self.rpm_path, ['REQUIRES', 'PROVIDES']))) + hdr = koji.get_rpm_header(self.rpm_path) + self.assertEqual(['REQUIRES'], koji.get_header_fields(hdr, ['REQUIRES']).keys()) + if __name__ == '__main__': unittest.main() From 54227239992e5221163b77e1ccc1f4f88f208f67 Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Oct 06 2016 11:29:51 +0000 Subject: [PATCH 2/4] show optional deps in web ui --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 63c6be6..d789305 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -1363,14 +1363,22 @@ def rpminfo(environ, rpmID, fileOrder='name', fileStart=None, buildrootOrder='-i if rpm['buildroot_id'] != None: builtInRoot = server.getBuildroot(rpm['buildroot_id']) if rpm['external_repo_id'] == 0: - values['requires'] = server.getRPMDeps(rpm['id'], koji.DEP_REQUIRE) - values['requires'].sort(_sortbyname) values['provides'] = server.getRPMDeps(rpm['id'], koji.DEP_PROVIDE) values['provides'].sort(_sortbyname) values['obsoletes'] = server.getRPMDeps(rpm['id'], koji.DEP_OBSOLETE) values['obsoletes'].sort(_sortbyname) values['conflicts'] = server.getRPMDeps(rpm['id'], koji.DEP_CONFLICT) values['conflicts'].sort(_sortbyname) + values['requires'] = server.getRPMDeps(rpm['id'], koji.DEP_REQUIRE) + values['requires'].sort(_sortbyname) + values['recommends'] = server.getRPMDeps(rpm['id'], koji.DEP_RECOMMEND) + values['recommends'].sort(_sortbyname) + values['suggests'] = server.getRPMDeps(rpm['id'], koji.DEP_SUGGEST) + values['suggests'].sort(_sortbyname) + values['supplements'] = server.getRPMDeps(rpm['id'], koji.DEP_SUPPLEMENT) + values['supplements'].sort(_sortbyname) + values['enhances'] = server.getRPMDeps(rpm['id'], koji.DEP_ENHANCE) + values['enhances'].sort(_sortbyname) headers = server.getRPMHeaders(rpm['id'], headers=['summary', 'description']) values['summary'] = koji.fixEncoding(headers.get('summary')) values['description'] = koji.fixEncoding(headers.get('description')) diff --git a/www/kojiweb/rpminfo.chtml b/www/kojiweb/rpminfo.chtml index 586279e..3f862db 100644 --- a/www/kojiweb/rpminfo.chtml +++ b/www/kojiweb/rpminfo.chtml @@ -92,6 +92,38 @@ + Obsoletes + + #if $len($obsoletes) > 0 + + #for $dep in $obsoletes + + + + #end for +
$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
+ #else + No Obsoletes + #end if + + + + Conflicts + + #if $len($conflicts) > 0 + + #for $dep in $conflicts + + + + #end for +
$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
+ #else + No Conflicts + #end if + + + Requires #if $len($requires) > 0 @@ -108,34 +140,66 @@ - Obsoletes + Recommends - #if $len($obsoletes) > 0 + #if $len($recommends) > 0 - #for $dep in $obsoletes + #for $dep in $recommends #end for
$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
#else - No Obsoletes + No Recommends #end if - Conflicts + Suggests - #if $len($conflicts) > 0 + #if $len($suggests) > 0 - #for $dep in $conflicts + #for $dep in $suggests #end for
$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
#else - No Conflicts + No Suggests + #end if + + + + Supplements + + #if $len($supplements) > 0 + + #for $dep in $supplements + + + + #end for +
$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
+ #else + No Supplements + #end if + + + + Enhances + + #if $len($enhances) > 0 + + #for $dep in $enhances + + + + #end for +
$util.escapeHTML($util.formatDep($dep.name, $dep.version, $dep.flags))
+ #else + No Enhances #end if From e1bbce63419c535d5540acaaa8afffcc71ac567c Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Oct 06 2016 12:08:01 +0000 Subject: [PATCH 3/4] ignore optional deps for rpm < 4.12 --- diff --git a/koji/__init__.py b/koji/__init__.py index 64435a7..a81a183 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -95,6 +95,14 @@ RPM_FILEDIGESTALGO_IDS = { 11: 'SHA224' } +# rpm 4.12 introduces optional deps +try: + RPM_SUPPORTS_OPTIONAL_DEPS = int(rpm.__version_info__[0]) > 4 or \ + (int(rpm.__version_info__[0]) == 4 and int(rpm.__version_info__[1]) >= 12) +except AttributeError: + # older versions don't even have __version_info__ + RPM_SUPPORTS_OPTIONAL_DEPS = False + class Enum(dict): """A simple class to track our enumerated constants @@ -858,6 +866,11 @@ def get_rpm_header(f, ts=None): def get_header_field(hdr, name): """Extract named field from an rpm header""" + if not RPM_SUPPORTS_OPTIONAL_DEPS and name in ('SUGGESTNAME', 'SUGGESTVERSION', 'SUGGESTFLAGS', + 'ENHANCENAME', 'ENHANCEVERSION', 'ENHANCEFLAGS', + 'SUPPLEMENTNAME', 'SUPPLEMENTVERSION', 'SUPPLEMENTFLAGS', + 'RECOMMENDNAME', 'RECOMMENDVERSION', 'RECOMMENDFLAGS'): + return [] idx = getattr(rpm, "RPMTAG_%s" % name.upper(), None) if idx is None: raise GenericError, "No such rpm header field: %s" % name diff --git a/tests/test_hub/test_getRPMDeps.py b/tests/test_hub/test_getRPMDeps.py index c87c59f..0d3149e 100644 --- a/tests/test_hub/test_getRPMDeps.py +++ b/tests/test_hub/test_getRPMDeps.py @@ -15,27 +15,46 @@ class TestGetRPMDeps(unittest.TestCase): pi.rpm.return_value = 'test-deps-1-1.fc24.x86_64.rpm' getRPMDeps = kojihub.RootExports().getRPMDeps res = getRPMDeps('') - self.assertEqual(len(res), 22) - types = set([x['type'] for x in res]) - self.assertEqual(set([koji.DEP_REQUIRE, - koji.DEP_PROVIDE, - koji.DEP_OBSOLETE, - koji.DEP_CONFLICT, - koji.DEP_SUGGEST, - koji.DEP_ENHANCE, - koji.DEP_SUPPLEMENT, - koji.DEP_RECOMMEND, - ]), types) + # limit test for rpm < 4.12 + if koji.RPM_SUPPORTS_OPTIONAL_DEPS: + self.assertEqual(len(res), 22) + types = set([x['type'] for x in res]) + self.assertEqual(set([koji.DEP_REQUIRE, + koji.DEP_PROVIDE, + koji.DEP_OBSOLETE, + koji.DEP_CONFLICT, + koji.DEP_SUGGEST, + koji.DEP_ENHANCE, + koji.DEP_SUPPLEMENT, + koji.DEP_RECOMMEND, + ]), types) - # test correct mapping of names - result = {} - for r in res: - result.setdefault(r['type'], set()).add(r['name']) - self.assertIn('require1', result[koji.DEP_REQUIRE]) - self.assertIn('provide1', result[koji.DEP_PROVIDE]) - self.assertIn('obsoletes1', result[koji.DEP_OBSOLETE]) - self.assertIn('conflicts1', result[koji.DEP_CONFLICT]) - self.assertIn('suggests1', result[koji.DEP_SUGGEST]) - self.assertIn('enhances1', result[koji.DEP_ENHANCE]) - self.assertIn('supplements1', result[koji.DEP_SUPPLEMENT]) - self.assertIn('recommends1', result[koji.DEP_RECOMMEND]) + # test correct mapping of names + result = {} + for r in res: + result.setdefault(r['type'], set()).add(r['name']) + self.assertTrue('require1' in result[koji.DEP_REQUIRE]) + self.assertTrue('provide1' in result[koji.DEP_PROVIDE]) + self.assertTrue('obsoletes1' in result[koji.DEP_OBSOLETE]) + self.assertTrue('conflicts1' in result[koji.DEP_CONFLICT]) + self.assertTrue('suggests1' in result[koji.DEP_SUGGEST]) + self.assertTrue('enhances1' in result[koji.DEP_ENHANCE]) + self.assertTrue('supplements1' in result[koji.DEP_SUPPLEMENT]) + self.assertTrue('recommends1' in result[koji.DEP_RECOMMEND]) + else: + self.assertEqual(len(res), 14) + types = set([x['type'] for x in res]) + self.assertEqual(set([koji.DEP_REQUIRE, + koji.DEP_PROVIDE, + koji.DEP_OBSOLETE, + koji.DEP_CONFLICT, + ]), types) + + # test correct mapping of names + result = {} + for r in res: + result.setdefault(r['type'], set()).add(r['name']) + self.assertTrue('require1' in result[koji.DEP_REQUIRE]) + self.assertTrue('provide1' in result[koji.DEP_PROVIDE]) + self.assertTrue('obsoletes1' in result[koji.DEP_OBSOLETE]) + self.assertTrue('conflicts1' in result[koji.DEP_CONFLICT]) From 2e8d82ca6d432cf8ed3519a3c0301447c65dbe7f Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Oct 06 2016 12:12:45 +0000 Subject: [PATCH 4/4] don't show optional deps if they are not supported by rpm --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index d789305..fa9019d 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -1371,14 +1371,18 @@ def rpminfo(environ, rpmID, fileOrder='name', fileStart=None, buildrootOrder='-i values['conflicts'].sort(_sortbyname) values['requires'] = server.getRPMDeps(rpm['id'], koji.DEP_REQUIRE) values['requires'].sort(_sortbyname) - values['recommends'] = server.getRPMDeps(rpm['id'], koji.DEP_RECOMMEND) - values['recommends'].sort(_sortbyname) - values['suggests'] = server.getRPMDeps(rpm['id'], koji.DEP_SUGGEST) - values['suggests'].sort(_sortbyname) - values['supplements'] = server.getRPMDeps(rpm['id'], koji.DEP_SUPPLEMENT) - values['supplements'].sort(_sortbyname) - values['enhances'] = server.getRPMDeps(rpm['id'], koji.DEP_ENHANCE) - values['enhances'].sort(_sortbyname) + if koji.RPM_SUPPORTS_OPTIONAL_DEPS: + values['optional_deps'] = True + values['recommends'] = server.getRPMDeps(rpm['id'], koji.DEP_RECOMMEND) + values['recommends'].sort(_sortbyname) + values['suggests'] = server.getRPMDeps(rpm['id'], koji.DEP_SUGGEST) + values['suggests'].sort(_sortbyname) + values['supplements'] = server.getRPMDeps(rpm['id'], koji.DEP_SUPPLEMENT) + values['supplements'].sort(_sortbyname) + values['enhances'] = server.getRPMDeps(rpm['id'], koji.DEP_ENHANCE) + values['enhances'].sort(_sortbyname) + else: + values['optional_deps'] = False headers = server.getRPMHeaders(rpm['id'], headers=['summary', 'description']) values['summary'] = koji.fixEncoding(headers.get('summary')) values['description'] = koji.fixEncoding(headers.get('description')) diff --git a/www/kojiweb/rpminfo.chtml b/www/kojiweb/rpminfo.chtml index 3f862db..e12db01 100644 --- a/www/kojiweb/rpminfo.chtml +++ b/www/kojiweb/rpminfo.chtml @@ -139,6 +139,7 @@ #end if + #if $optional_deps Recommends @@ -203,6 +204,7 @@ #end if + #end if Files