From bcbb337e5076f8edad332067a64b7d4e6da279b6 Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Jul 30 2018 05:58:13 +0000 Subject: Submit builds from stream branch This patch introduces the ability to submit multiple builds to Koji from stream branch based on configured build targets in local package config file. This applies to `fedpkg build` and chain build and scratch build still work as normal. The config file is optional. Without the config, packages could build from stream branch with global option --release and `fedpkg build` works as normal if there is no config file. If config file is created and build targets is set properly, fedpkg is able to read those targets and submit corresponding builds to Koji at once. Signed-off-by: Chenxiong Qi --- diff --git a/fedpkg/__init__.py b/fedpkg/__init__.py index f3b2efe..27a6021 100644 --- a/fedpkg/__init__.py +++ b/fedpkg/__init__.py @@ -162,13 +162,11 @@ class Commands(pyrpkg.Commands): self._rpmdefines.append("--eval '%%undefine %s'" % self._runtime_disttag) - def load_target(self): - """This creates the target attribute based on branch merge""" - - if self.branch_merge == 'master': - self._target = 'rawhide' + def build_target(self, release): + if release == 'master': + return 'rawhide' else: - self._target = '%s-candidate' % self.branch_merge + return '%s-candidate' % release def load_container_build_target(self): if self.branch_merge == 'master': diff --git a/fedpkg/cli.py b/fedpkg/cli.py index e0c0f25..24e70be 100644 --- a/fedpkg/cli.py +++ b/fedpkg/cli.py @@ -21,9 +21,11 @@ import json import pkg_resources import six import textwrap +import itertools from datetime import datetime +from six.moves import configparser from six.moves.configparser import NoSectionError from six.moves.configparser import NoOptionError from six.moves.urllib_parse import urlparse @@ -32,9 +34,11 @@ from fedpkg.bugzilla import BugzillaClient from fedpkg.utils import ( get_release_branches, sl_list_to_dict, verify_sls, new_pagure_issue, get_pagure_token, is_epel, assert_valid_epel_package, - assert_new_tests_repo, get_dist_git_url) + assert_new_tests_repo, get_dist_git_url, get_stream_branches, + expand_release) RELEASE_BRANCH_REGEX = r'^(f\d+|el\d+|epel\d+)$' +LOCAL_PACKAGE_CONFIG = 'package.cfg' def check_bodhi_version(): @@ -383,6 +387,32 @@ Examples: 'release branch.') extend_parser.set_defaults(command=self.extend_buildroot_override) + def register_build(self): + super(fedpkgClient, self).register_build() + + build_parser = self.subparsers.choices['build'] + build_parser.formatter_class = argparse.RawDescriptionHelpFormatter + build_parser.description = '''{0} + +fedpkg is also able to submit multiple builds to Koji at once from stream +branch based on a local config, which is inside the repository. The config file +is named package.cfg in INI format. For example, + + [koji] + targets = master fedora epel7 + +You only need to put Fedora releases and EPEL in option targets and fedpkg will +convert it to proper Koji build target for submitting builds. Beside regular +release names, option targets accepts two shortcut names as well, fedora and +epel, as you can see in the above example. Name fedora stands for current +active Fedora releases, and epel stands for the active EPEL releases, which are +el6 and epel7 currently. + +Note that the config file is a branch specific file. That means you could +create package.cfg for each stream branch separately to indicate on which +targets to build the package for a particular stream. +'''.format('\n'.join(textwrap.wrap(build_parser.description))) + # Target functions go here def retire(self): # Skip if package is already retired... @@ -708,7 +738,8 @@ suggest_reboot=False 'Only characters, numbers, periods, dashes, ' 'underscores, and pluses are allowed in module branch ' 'names') - release_branches = get_release_branches(pdc_url) + release_branches = list(itertools.chain( + *list(get_release_branches(pdc_url).values()))) if branch in release_branches: if service_levels: raise rpkgError( @@ -729,7 +760,8 @@ suggest_reboot=False pagure_url = config.get('{0}.pagure'.format(name), 'url') pagure_token = get_pagure_token(config, name) if all_releases: - release_branches = get_release_branches(pdc_url) + release_branches = list(itertools.chain( + *list(get_release_branches(pdc_url).values()))) branches = [b for b in release_branches if re.match(r'^(f\d+)$', b)] else: @@ -815,3 +847,87 @@ suggest_reboot=False bodhi_config, build=self.args.NVR or self.cmd.nvr, duration=self.args.duration) + + def read_releases_from_local_config(self, active_releases): + """Read configured releases from build config from repo""" + config_file = os.path.join(self.cmd.path, LOCAL_PACKAGE_CONFIG) + if not os.path.exists(config_file): + self.log.warning('No local config file exists.') + self.log.warning( + 'Create %s to specify build targets to build.', + LOCAL_PACKAGE_CONFIG) + return None + config = configparser.ConfigParser() + if not config.read([config_file]): + raise rpkgError('Package config {0} is not accessible.'.format( + LOCAL_PACKAGE_CONFIG)) + if not config.has_option('koji', 'targets'): + self.log.warning( + 'Build target is not configured. Continue to build as normal.') + return None + target_releases = config.get('koji', 'targets', raw=True).split() + expanded_releases = [] + for rel in target_releases: + expanded = expand_release(rel, active_releases) + if expanded: + expanded_releases += expanded + else: + self.log.error('Target %s is unknown. Skip.', rel) + return sorted(set(expanded_releases)) + + @staticmethod + def is_stream_branch(stream_branches, name): + """Determine if a branch is stream branch + + :param stream_branches: list of stream branches of a package. Each of + them is a mapping containing name and active status, which are + minimum set of properties to be included. For example, ``[{'name': + '8', 'active': true}, {'name': '10', 'active': true}]``. + :type stream_branches: list[dict] + :param str name: branch name to check if it is a stream branch. + :return: True if branch is a stream branch, False otherwise. + :raises rpkgError: if branch is a stream branch but it is inactive. + """ + for branch_info in stream_branches: + if branch_info['name'] != name: + continue + if branch_info['active']: + return True + else: + raise rpkgError('Cannot build from stream branch {0} as it is ' + 'inactive.'.format(name)) + return False + + def _build(self, sets=None): + if hasattr(self.args, 'chain') or self.args.scratch: + return super(fedpkgClient, self)._build(sets) + + server_url = self.config.get('{0}.pdc'.format(self.name), 'url') + + stream_branches = get_stream_branches(server_url, self.cmd.repo_name) + self.log.debug( + 'Package %s has stream branches: %r', + self.cmd.repo_name, [item['name'] for item in stream_branches]) + + if not self.is_stream_branch(stream_branches, self.cmd.branch_merge): + return super(fedpkgClient, self)._build(sets) + + self.log.debug('Current branch %s is a stream branch.', + self.cmd.branch_merge) + + releases = self.read_releases_from_local_config( + get_release_branches(server_url)) + + if not releases: + # If local config file is not created yet, or no build targets + # are not configured, let's build as normal. + return super(fedpkgClient, self)._build(sets) + + self.log.debug('Build on release targets: %r', releases) + task_ids = [] + for release in releases: + self.cmd.branch_merge = release + self.cmd.target = self.cmd.build_target(release) + task_id = super(fedpkgClient, self)._build(sets) + task_ids.append(task_id) + return task_ids diff --git a/fedpkg/utils.py b/fedpkg/utils.py index 43ca0b6..7a9ba20 100644 --- a/fedpkg/utils.py +++ b/fedpkg/utils.py @@ -21,6 +21,38 @@ from requests.exceptions import ConnectionError from pyrpkg import rpkgError +def query_pdc(server_url, endpoint, params, timeout=60): + api_url = '{0}/rest_api/v1/{1}/'.format( + server_url.rstrip('/'), endpoint.strip('/')) + query_args = params + while True: + try: + rv = requests.get(api_url, params=query_args, timeout=60) + except ConnectionError as error: + error_msg = ('The connection to PDC failed while trying to get ' + 'the active release branches. The error was: {0}' + .format(str(error))) + raise rpkgError(error_msg) + + if not rv.ok: + base_error_msg = ('The following error occurred while trying to ' + 'get the active release branches in PDC: {0}') + raise rpkgError(base_error_msg.format(rv.text)) + + rv_json = rv.json() + for item in rv_json['results']: + yield item + + if rv_json['next']: + # Clear the query_args because they are baked into the "next" URL + query_args = {} + api_url = rv_json['next'] + else: + # We've gone through every page, so we can return the found + # branches + break + + def get_sl_type(url, sl_name): """ Gets the service level (SL) type from PDC @@ -96,55 +128,38 @@ def new_pagure_issue(url, token, title, body): url.rstrip('/'), rv.json()['issue']['id']) -def get_release_branches(url): +def get_release_branches(server_url): """ Get the active Fedora release branches from PDC - :param url: a string of the URL to PDC - :return: a set containing the active Fedora release branches + + :param str url: a string of the URL to PDC + :return: a mapping containing the active Fedora releases and EPEL branches. + :rtype: dict """ - branches = set() - api_url = '{0}/rest_api/v1/product-versions/'.format(url.rstrip('/')) query_args = { 'fields': ['short', 'version'], 'active': True } - while True: - try: - rv = requests.get(api_url, params=query_args, timeout=60) - except ConnectionError as error: - error_msg = ('The connection to PDC failed while trying to get ' - 'the active release branches. The error was: {0}' - .format(str(error))) - raise rpkgError(error_msg) + releases = {} - if not rv.ok: - base_error_msg = ('The following error occurred while trying to ' - 'get the active release branches in PDC: {0}') - raise rpkgError(base_error_msg.format(rv.text)) + for product_version in query_pdc( + server_url, 'product-versions', params=query_args): + short_name = product_version['short'] + version = product_version['version'] - rv_json = rv.json() - for product_version in rv_json['results']: - # If the version is not a digit we can ignore it (e.g. rawhide) - if not product_version['version'].isdigit(): - continue - - if product_version['short'] == 'epel': - prefix = 'epel' - if product_version['version'] == '6': - prefix = 'el' - branches.add('{0}{1}'.format( - prefix, product_version['version'])) - elif product_version['short'] == 'fedora': - branches.add('f{0}'.format(product_version['version'])) + # If the version is not a digit we can ignore it (e.g. rawhide) + if not version.isdigit(): + continue - if rv_json['next']: - # Clear the query_args because they are baked into the "next" URL - query_args = {} - api_url = rv_json['next'] - else: - # We've gone through every page, so we can return the found - # branches - return branches + if short_name == 'epel': + prefix = 'el' if version == '6' else 'epel' + elif short_name == 'fedora': + prefix = 'f' + + release = '{0}{1}'.format(prefix, version) + releases.setdefault(short_name, []).append(release) + + return releases def sl_list_to_dict(sls): @@ -306,3 +321,57 @@ def get_dist_git_url(anongiturl): """ parsed_url = urlparse(anongiturl) return '{0}://{1}'.format(parsed_url.scheme, parsed_url.netloc) + + +def get_stream_branches(server_url, package_name): + """Get a package's stream branches + + :param str server_url: PDC server URL. + :param str package_name: package name. Generally for RPM packages, this is + the repository name without namespace. + :return: a list of stream branches. Each element in the list is a dict + containing branch property name and active. + :rtype: list[dict] + """ + query_args = { + 'global_component': package_name, + 'fields': ['name', 'active'], + } + branches = query_pdc( + server_url, 'component-branches', params=query_args) + # When write this method, endpoint component-branches contains not only + # stream branches, but also regular release branches, e.g. master, f28. + # Please remember to review the data regularly, there are only stream + # branches, or some new replacement of PDC fixes the issue as well, it + # should be ok to remove if from this list. + return [ + item for item in branches + if not re.match(r'^(f|el|epel)\d+$', item['name']) and + item['name'] != 'master' + ] + + +def expand_release(rel, active_releases): + """Expand special release to real release name + + Special releases include fedora and epel. Each of them will be expanded to + real release name. + + :param str rel: a release name to be expanded. It could be special names + fedora and epel, or concrete release names, e.g. f28, el6. + :param dict active_releases: a mapping from release category to concrete + release names. Fow now, it has two mappings, from name fedora to f\d\+, + and from epel to el6 and epel7. Value of this parameter should be + returned from `get_release_branches`. + :return: list of releases, for example ``[f28]``, or ``[el6, epel7]``. + """ + if rel == 'master': + return ['master'] + elif rel == 'fedora': + return active_releases['fedora'] + elif rel == 'epel': + return active_releases['epel'] + elif rel in active_releases['fedora'] or rel in active_releases['epel']: + return [rel] + else: + return None diff --git a/test/test_cli.py b/test/test_cli.py index 69f657a..c2b55d8 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -10,6 +10,7 @@ # option) any later version. See http://www.gnu.org/copyleft/gpl.html for # the full text of the license. +import git import io import json import os @@ -27,6 +28,8 @@ try: except ImportError: bodhi = None +import fedpkg.cli + from datetime import datetime, timedelta from fedpkg.bugzilla import BugzillaClient from fedpkg.cli import check_bodhi_version @@ -34,9 +37,9 @@ from freezegun import freeze_time from mock import call, patch, PropertyMock, Mock from os import rmdir from pyrpkg.errors import rpkgError -from six.moves import StringIO from six.moves.configparser import NoOptionError from six.moves.configparser import NoSectionError +from six.moves import StringIO from tempfile import mkdtemp from utils import CliTestCase @@ -656,7 +659,8 @@ class TestRequestBranch(CliTestCase): @patch('sys.stdout', new=StringIO()) def test_request_branch(self, mock_grb, mock_request_post): """Tests request-branch""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} mock_rv = Mock() mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} @@ -691,7 +695,8 @@ class TestRequestBranch(CliTestCase): @patch('sys.stdout', new=StringIO()) def test_request_branch_override(self, mock_grb, mock_request_post): """Tests request-branch with an overriden package and branch name""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} mock_rv = Mock() mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} @@ -724,7 +729,8 @@ class TestRequestBranch(CliTestCase): @patch('sys.stdout', new=StringIO()) def test_request_branch_module(self, mock_grb, mock_request_post): """Tests request-branch for a new module branch""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} mock_rv = Mock() mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} @@ -757,7 +763,8 @@ class TestRequestBranch(CliTestCase): @patch('fedpkg.cli.get_release_branches') def assert_request_branch_container(self, cli_cmd, mock_grb, mock_request_post): """Tests request-branch for a new container branch""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} mock_rv = Mock() mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} @@ -804,7 +811,8 @@ class TestRequestBranch(CliTestCase): def test_request_branch_sls(self, mock_verify_sls, mock_grb, mock_request_post): """Tests request-branch with service levels""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} responses = [] for idx in range(2, 5): mock_rv_post = Mock() @@ -891,7 +899,8 @@ class TestRequestBranch(CliTestCase): @patch('sys.stdout', new=StringIO()) def test_request_branch_all_releases(self, mock_grb, mock_request_post): """Tests request-branch with the '--all-releases' option """ - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} post_side_effect = [] for i in range(1, 4): mock_rv = Mock() @@ -959,7 +968,8 @@ https://pagure.stg.example.com/releng/fedora-scm-requests/issue/3""" @patch('fedpkg.cli.get_release_branches') def test_request_branch_invalid_sls(self, mock_grb): """Tests request-branch with invalid service levels""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} cli_cmd = ['fedpkg-stage', '--path', self.cloned_repo_path, '--name', 'nethack', 'request-branch', '9', '--sl', @@ -976,7 +986,8 @@ https://pagure.stg.example.com/releng/fedora-scm-requests/issue/3""" @patch('fedpkg.cli.get_release_branches') def test_request_branch_sls_on_release_branch_error(self, mock_grb): """Tests request-branch with a release branch and service levels""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} cli_cmd = ['fedpkg-stage', '--path', self.cloned_repo_path, '--name', 'nethack', 'request-branch', 'f27', '--sl', @@ -1056,7 +1067,8 @@ https://pagure.stg.example.com/releng/fedora-scm-requests/issue/3""" @patch('sys.stdout', new=StringIO()) def test_request_with_repo_option(self, mock_grb, mock_request_post): """Test request branch with option --repo""" - mock_grb.return_value = set(['el6', 'epel7', 'f25', 'f26', 'f27']) + mock_grb.return_value = {'fedora': ['f25', 'f26', 'f27'], + 'epel': ['el6', 'epel7']} mock_rv = Mock() mock_rv.ok = True mock_rv.json.return_value = {'issue': {'id': 2}} @@ -1767,3 +1779,186 @@ class TestBodhiOverrideExtend(CliTestCase): self.new_cli() output = sys.stderr.getvalue() self.assertIn('Invalid expiration date', output) + + +class TestReadReleasesFromLocalConfig(CliTestCase): + """Test read releases from local config file""" + + def setUp(self): + super(TestReadReleasesFromLocalConfig, self).setUp() + self.active_releases = { + 'fedora': ['f28', 'f27'], + 'epel': ['el6', 'epel7'], + } + self.fake_cmd = ['fedpkg', '--path', self.cloned_repo_path, 'build'] + + self.package_cfg = os.path.join(self.cloned_repo_path, + fedpkg.cli.LOCAL_PACKAGE_CONFIG) + self.write_file(self.package_cfg, + content='[koji]\ntargets=master f28 fedora epel') + + @patch('os.path.exists', return_value=False) + def test_no_config_file_is_create(self, exists): + with patch('sys.argv', new=self.fake_cmd): + cli = self.new_cli() + + rels = cli.read_releases_from_local_config(self.active_releases) + self.assertIsNone(rels) + + def test_no_build_target_is_configured(self): + with patch('sys.argv', new=self.fake_cmd): + cli = self.new_cli() + + self.write_file(self.package_cfg, content='[koji]') + + rels = cli.read_releases_from_local_config(self.active_releases) + self.assertIsNone(rels) + + def test_config_file_is_not_accessible(self): + with patch('sys.argv', new=self.fake_cmd): + cli = self.new_cli() + + with patch('fedpkg.cli.configparser.ConfigParser.read') as read: + read.return_value = [] + + six.assertRaisesRegex( + self, rpkgError, '.+ not accessible', + cli.read_releases_from_local_config, self.active_releases) + + def test_get_expanded_releases(self): + with patch('sys.argv', new=self.fake_cmd): + cli = self.new_cli() + + rels = cli.read_releases_from_local_config(self.active_releases) + rels = sorted(rels) + self.assertEqual(['el6', 'epel7', 'f27', 'f28', 'master'], rels) + + +class TestIsStreamBranch(CliTestCase): + """Test fedpkgClient.is_stream_branch""" + + def setUp(self): + super(TestIsStreamBranch, self).setUp() + self.fake_cmd = ['fedpkg', '--path', self.cloned_repo_path, 'build'] + + def test_not_a_stream_branch(self): + with patch('sys.argv', new=self.fake_cmd): + cli = self.new_cli() + + result = cli.is_stream_branch( + [{'name': '8', 'active': True}, {'name': '10', 'active': True}], + 'f28') + self.assertFalse(result) + + def test_stream_branch_is_inactive(self): + with patch('sys.argv', new=self.fake_cmd): + cli = self.new_cli() + + six.assertRaisesRegex( + self, rpkgError, 'Cannot build from stream branch', + cli.is_stream_branch, [{'name': '10', 'active': False}], '10') + + def test_branch_is_stream_branch(self): + with patch('sys.argv', new=self.fake_cmd): + cli = self.new_cli() + + result = cli.is_stream_branch( + [{'name': '8', 'active': True}, {'name': '10', 'active': True}], + '8') + self.assertTrue(result) + + +class TestBuildFromStreamBranch(CliTestCase): + """Test build command to build from stream branch""" + + @patch('pyrpkg.cli.cliClient._build') + @patch('fedpkg.cli.get_stream_branches') + def test_build_as_normal_if_branch_is_not_stream_branch( + self, get_stream_branches, _build): + get_stream_branches.return_value = [{'name': '8', 'active': True}] + + self.checkout_branch(git.Repo(self.cloned_repo_path), 'f27') + + cli_cmd = ['fedpkg', '--path', self.cloned_repo_path, 'build'] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli._build() + + _build.assert_called_once_with(None) + + @patch('pyrpkg.cli.cliClient._build') + @patch('fedpkg.cli.get_stream_branches') + @patch('fedpkg.cli.get_release_branches') + def test_build_as_normal_if_no_config_file_is_create( + self, get_release_branches, get_stream_branches, _build): + get_release_branches.return_value = { + 'fedora': ['f28', 'f27'], + 'epel': ['el6', 'epel7'], + } + get_stream_branches.return_value = [{'name': '8', 'active': True}] + self.checkout_branch(git.Repo(self.cloned_repo_path), '8') + + # There is no config file created originally. So, nothing to do here + # to run this test. + + cli_cmd = ['fedpkg', '--path', self.cloned_repo_path, 'build'] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli._build() + + _build.assert_called_once_with(None) + + @patch('pyrpkg.cli.cliClient._build') + @patch('fedpkg.cli.get_stream_branches') + @patch('fedpkg.cli.get_release_branches') + def test_build_as_normal_if_no_build_target_is_configured( + self, get_release_branches, get_stream_branches, _build): + get_release_branches.return_value = { + 'fedora': ['f28', 'f27'], + 'epel': ['el6', 'epel7'], + } + get_stream_branches.return_value = [{'name': '8', 'active': True}] + self.checkout_branch(git.Repo(self.cloned_repo_path), '8') + + # Create local config file without option targets for this test + self.write_file( + os.path.join(self.cloned_repo_path, + fedpkg.cli.LOCAL_PACKAGE_CONFIG), + content='[koji]') + + cli_cmd = ['fedpkg', '--path', self.cloned_repo_path, 'build'] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli._build() + + _build.assert_called_once_with(None) + + @patch('pyrpkg.cli.cliClient._build') + @patch('fedpkg.Commands.build_target') + @patch('fedpkg.cli.get_stream_branches') + @patch('fedpkg.cli.get_release_branches') + def test_submit_builds( + self, get_release_branches, get_stream_branches, build_target, + _build): + get_release_branches.return_value = { + 'fedora': ['f28', 'f27'], + 'epel': ['el6', 'epel7'], + } + get_stream_branches.return_value = [{'name': '8', 'active': True}] + _build.side_effect = [1, 2] + self.checkout_branch(git.Repo(self.cloned_repo_path), '8') + + # Create local config file without option targets for this test + self.write_file( + os.path.join(self.cloned_repo_path, + fedpkg.cli.LOCAL_PACKAGE_CONFIG), + content='[koji]\ntargets = fedora') + + cli_cmd = ['fedpkg', '--path', self.cloned_repo_path, 'build'] + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + task_ids = cli._build() + + build_target.assert_has_calls([call('f27'), call('f28')]) + _build.assert_has_calls([call(None), call(None)]) + self.assertEqual([1, 2], task_ids) diff --git a/test/test_utils.py b/test/test_utils.py index 3fbb904..07ca128 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -146,6 +146,10 @@ class TestUtils(unittest.TestCase): } mock_request_get.return_value = mock_rv expected = set(['el6', 'epel7', 'f25', 'f26', 'f27', 'f28']) + expected = { + 'epel': ['el6', 'epel7'], + 'fedora': ['f25', 'f26', 'f27', 'f28'], + } actual = utils.get_release_branches('http://pdc.local') self.assertEqual(expected, actual) diff --git a/test/utils.py b/test/utils.py index 52e72f5..3f9db64 100644 --- a/test/utils.py +++ b/test/utils.py @@ -159,6 +159,8 @@ rm -rf $$RPM_BUILD_ROOT ['git', 'branch', 'rhel-7'], ['git', 'branch', 'f26'], ['git', 'branch', 'f27'], + # Representing a stream branch + ['git', 'branch', '8'], ] for cmd in git_cmds: self.run_cmd(cmd, cwd=self.repo_path) @@ -173,6 +175,7 @@ rm -rf $$RPM_BUILD_ROOT ['git', 'branch', '--track', 'rhel-7', 'origin/rhel-7'], ['git', 'branch', '--track', 'f26', 'origin/f26'], ['git', 'branch', '--track', 'f27', 'origin/f27'], + ['git', 'branch', '--track', '8', 'origin/8'], ] for cmd in git_cmds: self.run_cmd(cmd, cwd=self.cloned_repo_path)