diff --git a/osc-staging.py b/osc-staging.py index 641f6c99..1fda6242 100644 --- a/osc-staging.py +++ b/osc-staging.py @@ -118,6 +118,7 @@ def clean_args(args): @cmdln.option('--try-strategies', action='store_true', default=False, help='apply strategies and keep any with desireable outcome') @cmdln.option('--strategy', help='apply a specific strategy') @cmdln.option('--no-color', action='store_true', help='strip colors from output (or add staging.color = 0 to the .oscrc general section') +@cmdln.option('--remove-exclusion', action='store_true', help='unignore selected requests automatically', default=False) @cmdln.option('--save', action='store_true', help='save the result to the pseudometa package') def do_staging(self, subcmd, opts, *args): """${cmd_name}: Commands to work with staging projects @@ -266,6 +267,9 @@ def do_staging(self, subcmd, opts, *args): select --move --filter-from A B $(< package.list) + select --remove-exclusion will unignore the requests selected (ignored requests + are called excluded in the OBS API) + "unselect" will remove from the project - pushing them back to the backlog If a message is included the requests will be ignored first. @@ -304,7 +308,7 @@ def do_staging(self, subcmd, opts, *args): osc staging unignore [--cleanup] [REQUEST...|all] osc staging list [--supersede] osc staging lock [-m MESSAGE] - osc staging select [--no-freeze] [--move [--filter-from STAGING]] + osc staging select [--no-freeze] [--remove-exclusion] [--move [--filter-from STAGING]] STAGING REQUEST... osc staging select [--no-freeze] [--interactive|--non-interactive] [--filter-by...] [--group-by...] @@ -559,13 +563,13 @@ def do_staging(self, subcmd, opts, *args): # api.set_splitter_info_in_prj_pseudometa(target_project, info['group'], info['strategy']) SelectCommand(api, target_project) \ - .perform(request_ids, no_freeze=opts.no_freeze) + .perform(request_ids, no_freeze=opts.no_freeze, remove_exclusion=opts.remove_exclusion) else: target_project = api.prj_from_short(stagings[0]) filter_from = api.prj_from_short(opts.filter_from) if opts.filter_from else None SelectCommand(api, target_project) \ .perform(requests, opts.move, - filter_from, opts.no_freeze) + filter_from, no_freeze=opts.no_freeze, remove_exclusion=opts.remove_exclusion) elif cmd == 'cleanup_rings': CleanupRings(api).perform() elif cmd == 'ignore': diff --git a/osclib/select_command.py b/osclib/select_command.py index 56e2d394..7bbc6414 100644 --- a/osclib/select_command.py +++ b/osclib/select_command.py @@ -55,7 +55,7 @@ class SelectCommand(object): return candidates[0] if candidates else None - def select_request(self, request, move, filter_from): + def select_request(self, request, move, filter_from, remove_exclusion=False): supersede = False staged_requests = { @@ -68,7 +68,7 @@ class SelectCommand(object): # Normal 'select' command print('Adding request "{}" to project "{}"'.format(request, self.target_project)) - return self.api.rq_to_prj(request, self.target_project) + return self.api.rq_to_prj(request, self.target_project, remove_exclusion) elif request in staged_requests and (move or supersede): # 'select' command becomes a 'move' # supersede = (new_rq, package, project) @@ -106,7 +106,7 @@ class SelectCommand(object): raise oscerr.WrongArgs('Arguments for select are not correct.') def perform(self, requests, move=False, - filter_from=None, no_freeze=False): + filter_from=None, no_freeze=False, remove_exclusion=False): """ Select package and move it accordingly by arguments :param target_project: project we want to target @@ -132,7 +132,7 @@ class SelectCommand(object): requests_count = len(requests) for index, request in enumerate(requests, start=1): print('({}/{}) '.format(index, requests_count), end='') - if not self.select_request(request, move, filter_from): + if not self.select_request(request, move, filter_from, remove_exclusion=remove_exclusion): return False # Notify everybody about the changes diff --git a/osclib/stagingapi.py b/osclib/stagingapi.py index 5291d475..17b1f9fd 100644 --- a/osclib/stagingapi.py +++ b/osclib/stagingapi.py @@ -895,7 +895,7 @@ class StagingAPI(object): return (time.time() - float(entry.get('mtime'))) / 3600 / 24 return 100000 # quite some! - def rq_to_prj(self, request_id, project): + def rq_to_prj(self, request_id, project, remove_exclusion=False): """ Links request to project - delete or submit :param request_id: request to link @@ -923,7 +923,10 @@ class StagingAPI(object): raise oscerr.WrongArgs(msg) requestxml = f"" - u = makeurl(self.apiurl, ['staging', self.project, 'staging_projects', project, 'staged_requests']) + opts = {} + if remove_exclusion: + opts['remove_exclusion'] = 1 + u = makeurl(self.apiurl, ['staging', self.project, 'staging_projects', project, 'staged_requests'], opts) f = http_POST(u, data=requestxml) if act_type == 'delete': diff --git a/tests/select_tests.py b/tests/select_tests.py index 4fd48162..06736039 100644 --- a/tests/select_tests.py +++ b/tests/select_tests.py @@ -1,22 +1,13 @@ -import unittest -import os.path from osc import oscerr -import osc.conf -from osc.core import http_GET, http_POST, makeurl -from lxml import etree as ET -from osclib.cache import Cache -from osclib.cache_manager import CacheManager -from osclib.comments import CommentAPI from osclib.conf import Config from osclib.core import package_list from osclib.select_command import SelectCommand from osclib.unselect_command import UnselectCommand from osclib.supersede_command import SupersedeCommand -from osclib.stagingapi import StagingAPI -from osclib.memoize import memoize_session_reset +from osclib.ignore_command import IgnoreCommand from osclib.core import source_file_load -import logging - +from urllib.error import HTTPError +from lxml import etree as ET from mock import MagicMock from . import OBSLocal @@ -136,3 +127,27 @@ class TestSelect(OBSLocal.TestCase): # TODO: record which URLs were called so we can verify them # but we wont' be able to test the actual wipe unless we really build something # which is too expensive + + def test_select_excluded_package(self): + self.wf.setup_rings() + staging = self.wf.create_staging('A', freeze=True, with_repo=True) + + self.wf.create_submit_request('devel:wine', 'wine') + IgnoreCommand(self.wf.api).perform(['wine']) + + with self.assertRaises(HTTPError) as context: + SelectCommand(self.wf.api, staging.name).perform(['wine']) + + root = ET.fromstring(context.exception.fp.read()) + self.assertRegex(root.find('summary').text, r'Use --remove-exclusion') + + def test_excluded_package(self): + self.wf.setup_rings() + staging = self.wf.create_staging('A', freeze=True, with_repo=True) + + self.wf.create_submit_request('devel:wine', 'wine') + IgnoreCommand(self.wf.api).perform(['wine']) + + ret = SelectCommand(self.wf.api, staging.name).perform(['wine'], remove_exclusion=True) + self.assertEqual(True, ret) + self.assertEqual(0, len(self.wf.api.get_ignored_requests()))