Provide ignore/unignore request staging commands.

It is not uncommon for a request to be in a pending state which requires
action beyond the scope of the staging workflow, but does not make sense
to deny the request. For lack of a "postponed" or "pending" state on OBS
a sudo-state of "ignore" is provided for the staging workflow. The ignore
state will remove a request from the "list" and "adi" commands so as not
to be accidentally staged. This avoids the need for keeping a context of
what requests should be ignored in one's memory.

It is expected that an ignored request will have comments reflecting what
is to be done or one should be added via the -m option of ignore command.
This commit is contained in:
Jimmy Berry 2017-01-10 01:27:40 -06:00
parent d21dbfb8c4
commit b25fda9b47
6 changed files with 123 additions and 1 deletions

View File

@ -31,6 +31,8 @@ from osclib.check_command import CheckCommand
from osclib.cleanup_rings import CleanupRings
from osclib.conf import Config
from osclib.freeze_command import FreezeCommand
from osclib.ignore_command import IgnoreCommand
from osclib.unignore_command import UnignoreCommand
from osclib.list_command import ListCommand
from osclib.obslock import OBSLock
from osclib.select_command import SelectCommand
@ -92,6 +94,7 @@ def _full_project_name(self, project):
help='do not update bootstrap-copy when freezing')
@cmdln.option('--wipe-cache', dest='wipe_cache', action='store_true', default=False,
help='wipe GET request cache before executing')
@cmdln.option('-m', '--message', help='message used by ignore command')
def do_staging(self, subcmd, opts, *args):
"""${cmd_name}: Commands to work with staging projects
@ -115,6 +118,10 @@ def do_staging(self, subcmd, opts, *args):
"frozenage" will show when the respective staging project was last frozen
"ignore" will ignore a request from "list" and "adi" commands until unignored
"unignore" will remove from ignore list
"list" will pick the requests not in rings
"select" will add requests to the project
@ -127,6 +134,8 @@ def do_staging(self, subcmd, opts, *args):
osc staging cleanup_rings
osc staging freeze [--no-boostrap] PROJECT...
osc staging frozenage PROJECT...
osc staging ignore [-m MESSAGE] REQUEST...
osc staging unignore REQUEST...|all
osc staging list [--supersede]
osc staging select [--no-freeze] [--move [--from PROJECT]] LETTER REQUEST...
osc staging unselect REQUEST...
@ -151,6 +160,8 @@ def do_staging(self, subcmd, opts, *args):
min_args, max_args = 1, None
elif cmd == 'adi':
min_args, max_args = None, None
elif cmd in ('ignore', 'unignore'):
min_args, max_args = 1, None
elif cmd in ('list', 'accept'):
min_args, max_args = 0, None
elif cmd in ('cleanup_rings', 'acheck'):
@ -233,6 +244,10 @@ def do_staging(self, subcmd, opts, *args):
opts.from_, opts.no_freeze)
elif cmd == 'cleanup_rings':
CleanupRings(api).perform()
elif cmd == 'ignore':
IgnoreCommand(api).perform(args[1:], opts.message)
elif cmd == 'unignore':
UnignoreCommand(api).perform(args[1:])
elif cmd == 'list':
ListCommand(api).perform(args[1:], supersede=opts.supersede)
elif cmd == 'adi':

View File

@ -51,6 +51,7 @@ class AdiCommand:
non_ring_packages = []
non_ring_requests = dict()
non_ring_requests_ignored = []
for request in all_requests:
# Consolidate all data from request
@ -86,6 +87,10 @@ class AdiCommand:
if self.api.update_superseded_request(request):
continue
if not len(wanted_requests) and request_id in self.requests_ignored:
non_ring_requests_ignored.append(request_id)
continue
non_ring_packages.append(target_package)
if split:
# request_id pretended to be index of non_ring_requests
@ -105,6 +110,10 @@ class AdiCommand:
non_ring_requests[source_project] = []
non_ring_requests[source_project].append(request_id)
if len(non_ring_requests_ignored):
print "Not in a ring, but ignored:"
for request_id in non_ring_requests_ignored:
print "- sr#{}: {}".format(request_id, requests_ignored[request_id])
if len(non_ring_packages):
print "Not in a ring:", ' '.join(sorted(non_ring_packages))
else:
@ -124,6 +133,7 @@ class AdiCommand:
"""
Perform the list command
"""
self.requests_ignored = self.api.get_ignored_requests()
if len(packages):
requests = set()
if move:

44
osclib/ignore_command.py Normal file
View File

@ -0,0 +1,44 @@
from osc.core import get_request
from osclib.comments import CommentAPI
class IgnoreCommand(object):
def __init__(self, api):
self.api = api
self.comment = CommentAPI(self.api.apiurl)
def perform(self, request_ids, message=None):
"""
Ignore a request from "list" and "adi" commands until unignored.
"""
requests_ignored = self.api.get_ignored_requests()
length = len(requests_ignored)
for request_id in request_ids:
print('Processing {}'.format(request_id))
check = self.check_and_comment(request_id, message)
if check is not True:
print('- {}'.format(check))
elif request_id not in requests_ignored:
requests_ignored[int(request_id)] = message
diff = len(requests_ignored) - length
if diff > 0:
print('Ignoring {} requests'.format(diff))
self.api.set_ignored_requests(requests_ignored)
else:
print('No new requests to ignore')
return True
def check_and_comment(self, request_id, message=None):
request = get_request(self.api.apiurl, request_id)
if not request:
return 'not found'
if request.actions[0].tgt_project != self.api.project:
return 'not targeting {}'.format(self.api.project)
if message:
self.comment.add_comment(request_id=request_id, comment=message)
return True

View File

@ -22,6 +22,7 @@ class ListCommand:
# Print out the left overs
requests = self.api.get_open_requests()
requests_ignored = self.api.get_ignored_requests()
non_ring_packages = []
change_devel_requests = {}
@ -86,13 +87,15 @@ class ListCommand:
elif source_prj.startswith('home:'):
source_prj = '~' + source_prj[len('home:'):]
result[devel][-1] += ' ({})'.format(source_prj)
if request_id in requests_ignored:
result[devel][-1] += '\nignored: ' + requests_ignored[request_id]
else:
non_ring_packages.append(target_package)
for prj in sorted(result.keys()):
print prj
for line in result[prj]:
print ' ', line
print ' ', line.replace('\n', '\n ')
if len(non_ring_packages):
print "Not in a ring:", ' '.join(sorted(non_ring_packages))

View File

@ -459,6 +459,16 @@ class StagingAPI(object):
return True
return False
def get_ignored_requests(self):
ignore = self.load_file_content('{}:Staging'.format(self.project), 'dashboard', 'ignored_requests')
if ignore is None:
return {}
return yaml.safe_load(ignore)
def set_ignored_requests(self, ignore_requests):
ignore = yaml.dump(ignore_requests, default_flow_style=False)
self.save_file_content('{}:Staging'.format(self.project), 'dashboard', 'ignored_requests', ignore)
def get_open_requests(self):
"""
Get all requests with open review for staging project
@ -746,6 +756,14 @@ class StagingAPI(object):
self.do_change_review_state(request_id, 'accepted',
by_group=self.cstaging_group,
message='Picked {}'.format(project))
# unignore a request selected to a project
requests_ignored = self.get_ignored_requests()
request_id = int(request_id)
if request_id in requests_ignored:
del requests_ignored[request_id]
self.set_ignored_requests(requests_ignored)
return True
def map_ring_package_to_subject(self, project, pkg):

View File

@ -0,0 +1,32 @@
from osc.core import get_request
class UnignoreCommand(object):
def __init__(self, api):
self.api = api
def perform(self, request_ids):
"""
Unignore a request by removing from ignore list.
"""
requests_ignored = self.api.get_ignored_requests()
length = len(requests_ignored)
if len(request_ids) == 1 and request_ids[0] == 'all':
requests_ignored = {}
else:
for request_id in request_ids:
request_id = int(request_id)
if request_id in requests_ignored:
print('Removing {}'.format(request_id))
del requests_ignored[request_id]
diff = length - len(requests_ignored)
if diff > 0:
print('Unignoring {} requests'.format(diff))
self.api.set_ignored_requests(requests_ignored)
else:
print('No requests to unignore')
return True