From 017065bd889562139336abf7f3f8d06392d726f0 Mon Sep 17 00:00:00 2001 From: Lubomír Sedlář Date: Jun 18 2018 10:54:21 +0000 Subject: Add script to check if build is in compose The result is indicated in the exit code and a message on stdout (unless suppressed with `-q` option). The build can be specified as full NEVR or just NVR (in which case we assume epoch 0). It works with local path to compose or with a URL to the compose. Fixes: https://pagure.io/compose-utils/issue/73 --- diff --git a/README.rst b/README.rst index a0ec583..a9df930 100644 --- a/README.rst +++ b/README.rst @@ -13,6 +13,9 @@ Contents **compose-create-legacy-composeinfo** create ``.composeinfo`` file in legacy INI-style format +**compose-has-build** + check if a build with a given NVR is present in the compose + **compose-list** lists composes in a directory and computes next/previous compose ID diff --git a/bin/compose-has-build b/bin/compose-has-build new file mode 100755 index 0000000..51e5f69 --- /dev/null +++ b/bin/compose-has-build @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +from __future__ import print_function + +import os +import sys + +here = sys.path[0] +if here != "/usr/bin": + sys.path[0] = os.path.dirname(here) + +from compose_utils import has_build + +if __name__ == "__main__": + has_build() diff --git a/compose_utils/__init__.py b/compose_utils/__init__.py index a2ad8fd..8d8946d 100644 --- a/compose_utils/__init__.py +++ b/compose_utils/__init__.py @@ -1,4 +1,47 @@ # -*- encoding: utf-8 -*- +from __future__ import print_function + +import argparse +import sys + +import productmd.compose +import kobo.rpmlib + from .copy_compose import copy_compose # noqa + + +def look_for_build(compose, nvr): + parts = kobo.rpmlib.parse_nvr(nvr) + parts["epoch"] = parts["epoch"] or "0" + srpm_name = "{name}-{epoch}:{version}-{release}.src".format(**parts) + + for variant in compose.rpms.rpms: + for arch in compose.rpms.rpms[variant]: + if srpm_name in compose.rpms.rpms[variant][arch]: + return True + + return False + + +def has_build(args=None): + parser = argparse.ArgumentParser() + parser.add_argument( + "-q", "--quiet", help="do not print any output", action="store_true" + ) + parser.add_argument("COMPOSE", help="compose to check") + parser.add_argument("NVR", help="build to look for") + + opts = parser.parse_args(args) + + compose = productmd.compose.Compose(opts.COMPOSE) + compose_id = compose.info.compose.id + + if look_for_build(compose, opts.NVR): + if not opts.quiet: + print("Compose %s has build %s" % (compose_id, opts.NVR)) + else: + if not opts.quiet: + print("Compose %s does not have build %s" % (compose_id, opts.NVR)) + sys.exit(1) diff --git a/doc/compose-has-build.1 b/doc/compose-has-build.1 new file mode 100644 index 0000000..11ae14c --- /dev/null +++ b/doc/compose-has-build.1 @@ -0,0 +1,27 @@ +.TH compose-has-build 1 +.SH NAME +compose-has-build \- check for existence of a build in a compose +.SH SYNOPSIS +.B compose-has-build +[\fIOPTIONS\fR...] +\fICOMPOSE_PATH\fR +\fINVR\fR +.SH DESCRIPTION +.B compose-has-build +checks metadata in the compose and checks if the given build is present. The +build can be specified either as NVR or as full NEVR. If there is no epoch, 0 +is assumed. +.SH OPTIONS +.TP +.BR \-h ", " \-\-help +Print help and exit. +.TP +.BR \-q ", " \-\-quiet +Do not print any output, just indicate result in exit code. +.SH EXIT CODE +When build is present, this tool exits with status code of \fB0\fR. +When build is missing, the exit code will be \fB1\fR. +.SH BUGS +Please report bugs at +.br +https://pagure.io/compose-utils/issues diff --git a/setup.py b/setup.py index bf168ae..1e24b5e 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ setup( scripts=[ 'bin/compose-changelog', 'bin/compose-create-legacy-composeinfo', + 'bin/compose-has-build', 'bin/compose-latest-symlink', 'bin/compose-list', 'bin/compose-partial-copy', @@ -31,6 +32,7 @@ setup( ('share/man/man1', [ 'doc/compose-changelog.1', 'doc/compose-create-legacy-composeinfo.1', + 'doc/compose-has-build.1', 'doc/compose-list.1', 'doc/compose-partial-copy.1', 'doc/compose-report-package-moves.1', diff --git a/tests/test_has_build.py b/tests/test_has_build.py new file mode 100644 index 0000000..2658b83 --- /dev/null +++ b/tests/test_has_build.py @@ -0,0 +1,72 @@ +# -*- encoding: utf-8 -*- + +import mock + +try: + import unittest2 as unittest +except ImportError: + import unittest +from six import StringIO + +from .helpers import get_compose, get_compose_path + +import compose_utils + + +class LookForBuildTest(unittest.TestCase): + def test_present_nvr(self): + compose = get_compose("DP-1.0-20160315.t.0") + self.assertTrue(compose_utils.look_for_build(compose, "dummy-bash-4.2.37-6")) + + def test_present_nevr(self): + compose = get_compose("DP-1.0-20160315.t.0") + self.assertTrue(compose_utils.look_for_build(compose, "dummy-bash-0:4.2.37-6")) + + def test_missing_nvr(self): + compose = get_compose("DP-1.0-20160315.t.0") + self.assertFalse(compose_utils.look_for_build(compose, "dummy-bash-4.2.37-1")) + + def test_missing_nevr(self): + compose = get_compose("DP-1.0-20160315.t.0") + self.assertFalse(compose_utils.look_for_build(compose, "dummy-bash-0:4.2.37-1")) + + def test_missing_nevr_bad_epoch(self): + compose = get_compose("DP-1.0-20160315.t.0") + self.assertFalse(compose_utils.look_for_build(compose, "dummy-bash-1:4.2.37-6")) + + +class HasBuildTest(unittest.TestCase): + def setUp(self): + self.path = get_compose_path("DP-1.0-20160315.t.0") + + def test_present(self): + with mock.patch("sys.stdout", new_callable=StringIO) as mock_out: + compose_utils.has_build([self.path, "dummy-bash-4.2.37-6"]) + + self.assertEqual( + "Compose DP-1.0-20160315.t.0 has build dummy-bash-4.2.37-6\n", + mock_out.getvalue(), + ) + + def test_missing(self): + with mock.patch("sys.stdout", new_callable=StringIO) as mock_out: + with self.assertRaises(SystemExit): + compose_utils.has_build([self.path, "dummy-bash-4.2.37-1"]) + + self.assertEqual( + "Compose DP-1.0-20160315.t.0 does not have build dummy-bash-4.2.37-1\n", + mock_out.getvalue(), + ) + + def test_present_quiet(self): + with mock.patch("sys.stdout", new_callable=StringIO) as mock_out: + compose_utils.has_build(["-q", self.path, "dummy-bash-4.2.37-6"]) + + self.assertEqual("", mock_out.getvalue()) + + def test_missing_quiet(self): + with mock.patch("sys.stdout", new_callable=StringIO) as mock_out: + with self.assertRaises(SystemExit): + compose_utils.has_build(["-q", self.path, "dummy-bash-4.2.37-1"]) + + self.assertEqual("", mock_out.getvalue())