diff --git a/NEWS b/NEWS
index bd1a44de..37f06129 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,5 @@
0.164
- -
+ - add support for approved requests (requires OBS 2.10)
0.163
- add sendsysrq command (requires OBS 2.10)
diff --git a/osc/commandline.py b/osc/commandline.py
index 90a9ffc9..e2236244 100644
--- a/osc/commandline.py
+++ b/osc/commandline.py
@@ -2130,6 +2130,9 @@ Please submit there instead, or use --nodevelproject to force direct submission.
the actual submit process. That would normally be a server-side copy of
the source package to the target package.
+ "approve" marks a requests in "review" state as approved. This request will get accepted
+ automatically when the last review got accepted.
+
"checkout" will checkout the request's source package ("submit" requests only).
"priorize" change the prioritity of a request to either "critical", "important", "moderate" or "low"
@@ -2153,6 +2156,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
osc request [show] [-d] [-b] ID
osc request accept [-m TEXT] ID
+ osc request approve [-m TEXT] ID
+ osc request cancelapprove [-m TEXT] ID
osc request decline [-m TEXT] ID
osc request revoke [-m TEXT] ID
osc request reopen [-m TEXT] ID
@@ -2199,7 +2204,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
if args[0] == 'help':
return self.do_help(['help', 'request'])
- cmds = ['list', 'ls', 'log', 'show', 'decline', 'reopen', 'clone', 'accept', 'approvenew', 'wipe', 'setincident', 'supersede', 'revoke', 'checkout', 'co', 'priorize']
+ cmds = ['list', 'ls', 'log', 'show', 'decline', 'reopen', 'clone', 'accept', 'approve', 'cancelapproval',
+ 'approvenew', 'wipe', 'setincident', 'supersede', 'revoke', 'checkout', 'co', 'priorize']
if subcmd != 'review' and args[0] not in cmds:
raise oscerr.WrongArgs('Unknown request action %s. Choose one of %s.' \
% (args[0], ', '.join(cmds)))
@@ -2259,7 +2265,8 @@ Please submit there instead, or use --nodevelproject to force direct submission.
elif cmd == 'priorize':
reqid = args[0]
priority = args[1]
- elif cmd in ['log', 'add', 'show', 'decline', 'reopen', 'clone', 'accept', 'wipe', 'revoke', 'checkout', 'co']:
+ elif cmd in ['log', 'add', 'show', 'decline', 'reopen', 'clone', 'accept', 'wipe', 'revoke', 'checkout',
+ 'co', 'approve', 'cancelapproval']:
reqid = args[0]
# clone all packages from a given request
@@ -2267,6 +2274,13 @@ Please submit there instead, or use --nodevelproject to force direct submission.
# should we force a message?
print('Cloned packages are available in project: %s' % clone_request(apiurl, reqid, opts.message))
+ # approve request
+ elif cmd == 'approve' or cmd == 'cancelapproval':
+ query = { 'cmd': cmd }
+ url = makeurl(apiurl, ['request', reqid], query)
+ r = http_POST(url, data=opts.message)
+ print(ET.parse(r).getroot().get('code'))
+
# change incidents
elif cmd == 'setincident':
query = { 'cmd': 'setincident', 'incident': incident }
diff --git a/osc/core.py b/osc/core.py
index 52a0dba9..f506da23 100644
--- a/osc/core.py
+++ b/osc/core.py
@@ -2586,6 +2586,7 @@ class RequestState(AbstractState):
self.name = state_node.get('name')
self.who = state_node.get('who')
self.when = state_node.get('when')
+ self.approver = state_node.get('approver')
if state_node.find('description') is None:
# OBS 2.6 has it always, before it did not exist
self.description = state_node.get('description')
@@ -2595,7 +2596,7 @@ class RequestState(AbstractState):
self.comment = state_node.find('comment').text.strip()
def get_node_attrs(self):
- return ('name', 'who', 'when')
+ return ('name', 'who', 'when', 'approver')
def get_comment(self):
return self.comment
@@ -2943,7 +2944,10 @@ class Request:
def list_view(self):
"""return "list view" format"""
import textwrap
- lines = ['%6s State:%-10s By:%-12s When:%-19s' % (self.reqid, self.state.name, self.state.who, self.state.when)]
+ status = self.state.name
+ if self.state.name == 'review' and self.state.approver:
+ status += "(approved)"
+ lines = ['%6s State:%-10s By:%-12s When:%-19s' % (self.reqid, status, self.state.who, self.state.when)]
tmpl = ' %(type)-16s %(source)-50s %(target)s'
for action in self.actions:
lines.append(tmpl % self.format_action(action))
@@ -2967,6 +2971,8 @@ class Request:
lines.append(' *** This request will get automatically accepted after '+self.accept_at+' ! ***\n')
if self.priority in [ 'critical', 'important' ] and self.state.name in [ 'new', 'review' ]:
lines.append(' *** This request has classified as '+self.priority+' ! ***\n')
+ if self.state.approver and self.state.name == 'review':
+ lines.append(' *** This request got approved by '+self.state.approver+'. It will get automatically accepted after last review got accepted! ***\n')
for action in self.actions:
tmpl = ' %(type)-13s %(source)s %(target)s'
diff --git a/tests/request_fixtures/test_read_request2.xml b/tests/request_fixtures/test_read_request2.xml
index 16033791..5aa983e4 100644
--- a/tests/request_fixtures/test_read_request2.xml
+++ b/tests/request_fixtures/test_read_request2.xml
@@ -11,7 +11,7 @@
-
+
review start
diff --git a/tests/test_request.py b/tests/test_request.py
index 5746cea0..96b5ba40 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -341,6 +341,7 @@ class TestRequest(OscTestCase):
self.assertEqual(r.state.name, 'accepted')
self.assertEqual(r.state.when, '2010-12-27T01:36:29')
self.assertEqual(r.state.who, 'user1')
+ self.assertEqual(r.state.approver, None)
self.assertEqual(r.state.comment, '')
self.assertEqual(r.statehistory[0].when, '2010-12-13T13:02:03')
self.assertEqual(r.statehistory[0].who, 'creator')
@@ -373,6 +374,7 @@ class TestRequest(OscTestCase):
self.assertTrue(r.actions[1].tgt_package is None)
self.assertEqual(r.state.name, 'review')
self.assertEqual(r.state.when, '2010-12-27T01:36:29')
+ self.assertEqual(r.state.approver, 'someone')
self.assertEqual(r.state.who, 'abc')
self.assertEqual(r.state.comment, '')
self.assertEqual(r.reviews[0].state, 'new')