Merge pull request #79 from aplanas/master

New approach for action #1658 - move does not need a from argument
This commit is contained in:
Tomáš Chvátal 2014-03-03 18:15:16 +01:00
commit 1b059751b5
3 changed files with 224 additions and 122 deletions

View File

@ -18,7 +18,6 @@ from osc.core import meta_get_packagelist
from osc.core import http_GET
from osc.core import http_POST
from osc.core import server_diff
from osc.core import store_read_project
# Expand sys.path to search modules inside the pluging directory
_plugin_dir = os.path.expanduser('~/.osc-plugins')
@ -26,11 +25,12 @@ sys.path.append(_plugin_dir)
from osclib.stagingapi import StagingAPI
from osclib.request_finder import RequestFinder
OSC_STAGING_VERSION='0.0.1'
OSC_STAGING_VERSION = '0.0.1'
def _print_version(self):
""" Print version information about this extension. """
print('%s'%(self.OSC_STAGING_VERSION))
print(self.OSC_STAGING_VERSION)
quit(0)
@ -43,16 +43,24 @@ def _get_changed(opts, project, everything):
f = http_GET(makeurl(opts.apiurl, ['source', project, pkg]))
linkinfo = ET.parse(f).getroot().find('linkinfo')
if linkinfo is None:
ret.append({'pkg': pkg, 'code': 'NOT_LINK', 'msg': 'Not a source link'})
ret.append({'pkg': pkg, 'code': 'NOT_LINK',
'msg': 'Not a source link'})
continue
if linkinfo.get('error'):
ret.append({'pkg': pkg, 'code': 'BROKEN', 'msg': 'Broken source link'})
ret.append({'pkg': pkg, 'code': 'BROKEN',
'msg': 'Broken source link'})
continue
t = linkinfo.get('project')
p = linkinfo.get('package')
r = linkinfo.get('revision')
if len(server_diff(opts.apiurl, t, p, r, project, pkg, None, True)) > 0:
ret.append({'pkg': pkg, 'code': 'MODIFIED', 'msg': 'Has local modifications', 'pprj': t, 'ppkg': p})
if server_diff(opts.apiurl, t, p, r, project, pkg, None, True):
ret.append({
'pkg': pkg,
'code': 'MODIFIED',
'msg': 'Has local modifications',
'pprj': t,
'ppkg': p
})
continue
return ret
@ -65,10 +73,10 @@ def _staging_remove(self, project, opts):
"""
chng = _get_changed(opts, project, True)
if len(chng) > 0:
print('Staging project "%s" is not clean:'%(project))
print('Staging project "%s" is not clean:' % project)
print('')
for pair in chng:
print(' * %s : %s'%(pair['pkg'],pair['msg']))
print(' * %s : %s' % (pair['pkg'], pair['msg']))
print('')
print('Really delete? (N/y)')
answer = sys.stdin.readline()
@ -82,7 +90,8 @@ def _staging_remove(self, project, opts):
def _staging_submit_devel(self, project, opts):
"""
Generate new review requests for devel-projects based on our staging changes.
Generate new review requests for devel-projects based on our
staging changes.
:param project: staging project to submit into devel projects
"""
chng = _get_changed(opts, project, True)
@ -92,22 +101,23 @@ def _staging_submit_devel(self, project, opts):
if len(chng) > 0:
for pair in chng:
if pair['code'] != 'MODIFIED':
print('Error: Package "%s": %s'%(pair['pkg'],pair['msg']))
print('Error: Package "%s": %s' % (pair['pkg'], pair['msg']))
else:
print('Sending changes back %s/%s -> %s/%s'%(project,pair['pkg'],pair['pprj'],pair['ppkg']))
action_xml = '<request>';
print('Sending changes back %s/%s -> %s/%s' % (project, pair['pkg'], pair['pprj'], pair['ppkg']))
action_xml = '<request>'
action_xml += ' <action type="submit"> <source project="%s" package="%s" /> <target project="%s" package="%s" />' % (project, pair['pkg'], pair['pprj'], pair['ppkg'])
action_xml += ' </action>'
action_xml += ' <state name="new"/> <description>%s</description>' % msg
action_xml += '</request>'
u = makeurl(opts.apiurl, ['request'], query='cmd=create&addrevision=1')
u = makeurl(opts.apiurl, ['request'],
query='cmd=create&addrevision=1')
f = http_POST(u, data=action_xml)
root = ET.parse(f).getroot()
print("Created request %s" % (root.get('id')))
else:
print("No changes to submit")
print('No changes to submit')
return
@ -117,6 +127,8 @@ def _staging_submit_devel(self, project, opts):
help='manually specify different parent project during creation of staging')
@cmdln.option('-m', '--message', metavar='TEXT',
help='manually specify different parent project during creation of staging')
@cmdln.option('-n', '--move', action='store_true',
help='force the selection to become a move')
@cmdln.option('-f', '--from', dest='from_', metavar='FROMPROJECT',
help='manually specify different source project during request moving')
@cmdln.option('-v', '--version', action='store_true',
@ -126,32 +138,33 @@ def do_staging(self, subcmd, opts, *args):
"check" will check if all packages are links without changes
"remove" (or "r") will delete the staging project into submit requests for openSUSE:Factory
"remove" (or "r") will delete the staging project into submit
requests for openSUSE:Factory
"submit-devel" (or "s") will create review requests for changed packages in staging project
into their respective devel projects to obtain approval from maitnainers for pushing the
"submit-devel" (or "s") will create review requests for changed
packages in staging project into their respective devel
projects to obtain approval from maitnainers for pushing the
changes to openSUSE:Factory
"freeze" will freeze the sources of the project's links (not affecting the packages actually in)
"freeze" will freeze the sources of the project's links (not
affecting the packages actually in)
"accept" will accept all requests in openSUSE:Factory:Staging:<LETTER> (into Factory)
"accept" will accept all requests in
openSUSE:Factory:Staging:<LETTER> (into Factory)
"list" will pick the requests not in rings
"select" will add requests to the project
"unselect" will remove them project - pushing them back to the backlog
"move" will move a list of REQUESTs from the current project to PROJECT
Usage:
osc staging check [--everything] REPO
osc staging remove REPO
osc staging submit-devel [-m message] REPO
osc staging freeze PROJECT
osc staging list
osc staging select LETTER REQUEST...
osc staging select [--move [-from PROJECT]] LETTER REQUEST...
osc staging unselect LETTER REQUEST...
osc staging move [--from PROJECT] PROJECT REQUEST...
osc staging accept LETTER
osc staging cleanup_rings
"""
@ -164,16 +177,14 @@ def do_staging(self, subcmd, opts, *args):
cmd = args[0]
if cmd in ['submit-devel', 's', 'remove', 'r', 'accept', 'freeze']:
min_args, max_args = 1, 1
elif cmd in ['check']:
elif cmd == 'check':
min_args, max_args = 0, 2
elif cmd in ['select', 'unselect']:
min_args, max_args = 2, None
elif cmd in ['move']:
min_args, max_args = 2, None
elif cmd in ['list', 'cleanup_rings']:
min_args, max_args = 0, 0
else:
raise oscerr.WrongArgs('Unknown command: %s'%(cmd))
raise oscerr.WrongArgs('Unknown command: %s' % cmd)
if len(args) - 1 < min_args:
raise oscerr.WrongArgs('Too few arguments.')
if not max_args is None and len(args) - 1 > max_args:
@ -190,15 +201,15 @@ def do_staging(self, subcmd, opts, *args):
# FIXME: de-duplicate and use function when cleaning up this file
if len(args) > 1:
prj = api.prj_from_letter(args[1])
state = api.check_project_status(prj, True)
state = api.check_project_status(prj, True)
# If the state is green we do nothing
if not state:
print('Skipping empty staging project: {0}'.format(prj))
print('Skipping empty staging project: {}'.format(prj))
print('')
return True
print('Checking staging project: {0}'.format(prj))
print('Checking staging project: {}'.format(prj))
if type(state) is list:
print(' -- Project still neeeds attention')
for i in state:
@ -213,11 +224,11 @@ def do_staging(self, subcmd, opts, *args):
# If the state is green we do nothing
if not state:
print('Skipping empty staging project: {0}'.format(prj))
print('Skipping empty staging project: {}'.format(prj))
print('')
continue
print('Checking staging project: {0}'.format(prj))
print('Checking staging project: {}'.format(prj))
if type(state) is list:
print(' -- Project still neeeds attention')
for i in state:
@ -235,30 +246,41 @@ def do_staging(self, subcmd, opts, *args):
elif cmd in ['freeze']:
import osclib.freeze_command
for prj in args[1:]:
osclib.freeze_command.FreezeCommand(api).perform(api.prj_from_letter(prj))
osclib.freeze_command.FreezeCommand(api).perform(api. prj_from_letter(prj))
elif cmd in ['accept']:
import osclib.accept_command
osclib.accept_command.AcceptCommand(api).perform(api.prj_from_letter(args[1]))
osclib.accept_command.AcceptCommand(api).perform(api. prj_from_letter(args[1]))
elif cmd in ['select', 'unselect']:
stprj = api.prj_from_letter(args[1])
if not api.prj_frozen_enough(stprj):
print "Freeze the prj first"
tprj = api.prj_from_letter(args[1])
if not api.prj_frozen_enough(tprj):
print('Freeze the prj first')
return False
for rq in RequestFinder.find_sr(args[2:], opts.apiurl):
if cmd == 'select':
api.rq_to_prj(rq, stprj)
else:
api.rm_from_prj(stprj, request_id=rq)
for rq, rq_prj in RequestFinder.find_sr(args[2:], opts.apiurl).items():
if cmd == 'select' and 'staging' not in rq_prj:
# Normal 'select' command
api.rq_to_prj(rq, tprj)
elif cmd == 'select' and 'staging' in rq_prj and opts.move:
# 'select' command becomes a 'move'
fprj = None
if opts.from_:
fprj = api.prj_from_letter(opts.from_)
else:
fprj = rq_prj['staging']
print('Moving "{}" from "{}" to "{}"'.format(rq, fprj, tprj))
api.move_between_project(fprj, rq, tprj)
elif cmd == 'select' and 'staging' in rq_prj and not opts.move:
# Previously selected, but not explicit move
msg = 'Request "{}" is actually in "{}".\n'
msg = msg.format(rq, rq_prj['staging'])
msg += 'Use --move (-n) modifier to move the request from "{}" to "{}"'
msg = msg.format(rq_prj['staging'], tprj)
print(msg)
elif cmd == 'unselect':
api.rm_from_prj(tprj, request_id=rq)
api.add_review(rq, by_group='factory-staging',
msg='Please recheck')
api.build_switch_prj(stprj, 'enable')
elif cmd in ['move']:
cprj = store_read_project(os.getcwd())
sprj = api.prj_from_letter(opts.from_) if opts.from_ else cprj
tprj = api.prj_from_letter(args[1])
for rq in RequestFinder.find_sr(args[2:], opts.apiurl):
api.move_between_project(sprj, rq, tprj)
else:
raise oscerr.WrongArgs('Arguments for select are not correct.')
elif cmd in ['cleanup_rings']:
import osclib.cleanup_rings
osclib.cleanup_rings.CleanupRings(opts.apiurl).perform()

View File

@ -6,17 +6,39 @@ from osc.core import makeurl
from osc.core import http_GET
FACTORY = 'openSUSE:Factory'
STG_PREFIX = 'openSUSE:Factory:Staging:'
class RequestFinder:
@classmethod
def find_request_id(self, request, apiurl):
def __init__(self, apiurl):
# Store the list of submit request together with the source project
#
# Example:
#
# {
# 212454: {
# 'project': 'openSUSE:Factory',
# },
#
# 223870: {
# 'project': 'openSUSE:Factory',
# 'staging': 'openSUSE:Factory:Staging:A',
# }
# }
#
self.apiurl = apiurl
self.srs = {}
def find_request_id(self, request):
"""
Look up the request by ID to verify if it is correct
:param request: ID of the added request
:param apiurl: OBS url
"""
url = makeurl(apiurl, ['request', str(request)])
url = makeurl(self.apiurl, ['request', str(request)])
try:
f = http_GET(url)
except urllib2.HTTPError:
@ -28,72 +50,100 @@ class RequestFinder:
return None
project = root.find('action').find('target').get('project')
if project != 'openSUSE:Factory':
raise oscerr.WrongArgs('Request {} is not for openSUSE:Factory, but for {}'.format(request, project))
self.srids.add(int(request))
if project != FACTORY and not project.startswith(STG_PREFIX):
msg = 'Request {} is not for openSUSE:Factory, but for {}'
msg = msg.format(request, project)
raise oscerr.WrongArgs(msg)
self.srs[int(request)] = {'project': project}
for review in root.findall('review'):
if review.get('by_project'):
self.srs[int(request)]['staging'] = review.get('by_project')
break
return True
@classmethod
def find_request_package(self, package, apiurl):
def find_request_package(self, package):
"""
Look up the package by its name and return the SR#
:param package: name of the package
:param apiurl: OBS url
"""
url = makeurl(apiurl, ['request'], 'states=new,review,declined&project=openSUSE:Factory&view=collection&package={}'.format(package))
query = 'states=new,review,declined&project=openSUSE:Factory&view=collection&package={}'
query = query.format(package)
url = makeurl(self.apiurl, ['request'], query)
f = http_GET(url)
root = ET.parse(f).getroot()
ret = None
for x in root.findall('request'):
for sr in root.findall('request'):
# TODO: check the package matches - OBS is case insensitive
self.srids.add(int(x.get('id')))
request = int(sr.get('id'))
self.srs[request] = {'project': 'openSUSE:Factory'}
for review in sr.findall('review'):
if review.get('by_project'):
self.srs[request]['staging'] = review.get('by_project')
break
if ret:
raise oscerr.WrongArgs('There are multiple requests for package "{}": {}'.format(package, ', '.join(map(str, self.srids))))
msg = 'There are multiple requests for package "{}": {}'
msg = msg.format(package, ', '.join(map(str, self.srs.keys())))
raise oscerr.WrongArgs(msg)
ret = True
return ret
@classmethod
def find_request_project(self, source_project, apiurl):
def find_request_project(self, source_project):
"""
Look up the source project by its name and return the SR#(s)
:param source_project: name of the source project
:param apiurl: OBS url
"""
url = makeurl(apiurl, ['request'], 'states=new,review&project=openSUSE:Factory&view=collection')
query = 'states=new,review&project=openSUSE:Factory&view=collection'
url = makeurl(self.apiurl, ['request'], query)
f = http_GET(url)
root = ET.parse(f).getroot()
ret = None
for rq in root.findall('request'):
for a in rq.findall('action'):
s = a.find('source')
if s is not None and s.get('project') == source_project:
self.srids.add(int(rq.attrib['id']))
for sr in root.findall('request'):
for act in sr.findall('action'):
src = act.find('source')
if src is not None and src.get('project') == source_project:
request = int(sr.attrib['id'])
self.srs[request] = {'project': 'openSUSE:Factory'}
for review in sr.findall('review'):
if review.get('by_project'):
self.srs[request]['staging'] = review.get('by_project')
break
ret = True
return ret
def find(self, pkgs):
"""
Search for all various mutations and return list of SR#s
:param pkgs: mesh of argumets to search for
This function is only called for its side effect.
"""
for p in pkgs:
if self.find_request_package(p):
continue
if self.find_request_id(p):
continue
if self.find_request_project(p):
continue
raise oscerr.WrongArgs('No SR# found for: {}'.format(p))
@classmethod
def find_sr(self, pkgs, apiurl):
def find_sr(cls, pkgs, apiurl):
"""
Search for all various mutations and return list of SR#s
:param pkgs: mesh of argumets to search for
:param apiurl: OBS url
"""
self.srids = set()
for p in pkgs:
if self.find_request_package(p, apiurl):
continue
if self.find_request_id(p, apiurl):
continue
if self.find_request_project(p, apiurl):
continue
raise oscerr.WrongArgs('No SR# found for: {0}'.format(p))
# this is needed in order to ensure we have one level list not nested one
return sorted(list(self.srids))
finder = cls(apiurl)
finder.find(pkgs)
return finder.srs

View File

@ -82,7 +82,8 @@ class StagingAPI(object):
package_info['package'] = root.attrib['package']
return package_info
def move_between_project(self, source_project, req_id, destination_project):
def move_between_project(self, source_project, req_id,
destination_project):
"""
Move selected package from one staging to another
:param source_project: Source project
@ -114,16 +115,16 @@ class StagingAPI(object):
projects = []
url = self.makeurl(['search', 'project',
'id?match=starts-with(@name,\'openSUSE:Factory:Staging:\')'])
query = "id?match=starts-with(@name,'openSUSE:Factory:Staging:')"
url = self.makeurl(['search', 'project', query])
projxml = http_GET(url)
root = ET.parse(projxml).getroot()
for val in root.findall('project'):
projects.append(val.get('name'))
return projects
def change_review_state(self, request_id, newstate, message=None, by_group=None,
by_user=None, by_project=None):
def change_review_state(self, request_id, newstate, message=None,
by_group=None, by_user=None, by_project=None):
"""
Change review state of the staging request
:param request_id: id of the request
@ -145,7 +146,8 @@ class StagingAPI(object):
review.state == 'new':
# call osc's function
return change_review_state(self.apiurl, str(request_id), newstate,
return change_review_state(self.apiurl, str(request_id),
newstate,
by_group=by_group,
by_user=by_user,
by_project=by_project)
@ -163,7 +165,8 @@ class StagingAPI(object):
request_id = int(request.get('id'))
action = request.findall('action')
if not action:
raise oscerr.WrongArgs('Request {} has no action'.format(request_id))
msg = 'Request {} has no action'.format(request_id)
raise oscerr.WrongArgs(msg)
# we care only about first action
action = action[0]
@ -173,14 +176,17 @@ class StagingAPI(object):
# If the values are empty it is no error
if not target_project or not target_package:
logging.info('no target/package in request {}, action {}; '.format(request_id, action))
msg = 'no target/package in request {}, action {}; '
msg = msg.format(request_id, action)
logging.info(msg)
# Verify the package ring
ring = self.ring_packages.get(target_package, None)
if not ring:
# accept the request here
message = "No need for staging, not in tested ring project."
self.change_review_state(request_id, 'accepted', message=message, by_group='factory-staging')
message = 'No need for staging, not in tested ring project.'
self.change_review_state(request_id, 'accepted', message=message,
by_group='factory-staging')
def get_open_requests(self):
"""
@ -194,7 +200,8 @@ class StagingAPI(object):
# xpath query, using the -m, -r, -s options
where = "@by_group='factory-staging'+and+@state='new'"
url = self.makeurl(['search', 'request'], "match=state/@name='review'+and+review[%s]" % where)
query = "match=state/@name='review'+and+review[{}]".format(where)
url = self.makeurl(['search', 'request'], query)
f = http_GET(url)
root = ET.parse(f).getroot()
@ -204,7 +211,9 @@ class StagingAPI(object):
def dispatch_open_requests(self):
"""
Verify all requests and dispatch them to staging projects or approve them
Verify all requests and dispatch them to staging projects or
approve them
"""
# get all current pending requests
@ -320,7 +329,8 @@ class StagingAPI(object):
self.set_prj_pseudometa(project, data)
# FIXME Add sr to group request as well
def rm_from_prj(self, project, package=None, request_id=None, msg=None, review='accepted'):
def rm_from_prj(self, project, package=None, request_id=None,
msg=None, review='accepted'):
"""
Delete request from the project
:param project: project to remove from
@ -346,9 +356,11 @@ class StagingAPI(object):
Creates a package container without any fields in project/package
:param project: project to create it
:param package: package name
:param disable_build: should the package be created with build flag disabled
:param disable_build: should the package be created with build
flag disabled
"""
dst_meta = '<package name="%s"><title/><description/></package>' % package
dst_meta = '<package name="{}"><title/><description/></package>'
dst_meta = dst_meta.format(package)
if disable_build:
root = ET.fromstring(dst_meta)
elm = ET.SubElement(root, 'build')
@ -360,10 +372,12 @@ class StagingAPI(object):
def check_one_request(self, request, project):
"""
Check if a staging request is ready to be approved. Reviews for the project
are ignored, other open reviews will block the acceptance
Check if a staging request is ready to be approved. Reviews for
the project are ignored, other open reviews will block the
acceptance
:param project: staging project
:param request_id: request id to check
"""
f = http_GET(self.makeurl(['request', str(request)]))
@ -376,9 +390,10 @@ class StagingAPI(object):
if state in ['declined', 'superseded', 'revoked']:
return '{}: {}'.format(package, state)
# instead of just printing the state of the whole request find out who is
# remaining on the review and print it out, otherwise print out that it is
# ready for approval and waiting on others from GR to be accepted
# instead of just printing the state of the whole request find
# out who is remaining on the review and print it out,
# otherwise print out that it is ready for approval and
# waiting on others from GR to be accepted
review_state = root.findall('review')
failing_groups = []
for i in review_state:
@ -399,10 +414,11 @@ class StagingAPI(object):
def check_project_status(self, project, verbose=False):
"""
Checks a staging project for acceptance. Checks all open requests for open reviews
and build status
Checks a staging project for acceptance. Checks all open
requests for open reviews and build status
:param project: project to check
:return true (ok)/false (empty prj) or list of strings with informations)
:return true (ok)/false (empty prj) or list of strings with
informations)
"""
# Report
@ -412,7 +428,8 @@ class StagingAPI(object):
requests = self.list_requests_in_prj(project)
open_requests = set(requests)
# all tracked requests - some of them might be declined, so we don't see them above
# all tracked requests - some of them might be declined, so we
# don't see them above
meta = self.get_prj_pseudometa(project)
for req in meta['requests']:
req = req['id']
@ -489,7 +506,8 @@ class StagingAPI(object):
# don't look here - we will replace that once we have OBS<->openQA sync
baseurl = 'http://opensuseqa.suse.de/openqa/testresults/openSUSE-Factory-staging'
url = baseurl + '_' + project.split(':')[-1].lower() + '-x86_64-Build'
result = re.match('Test-([\d\.]+)-Build(\d+)\.(\d+)-Media.iso', filename)
result = re.match('Test-([\d\.]+)-Build(\d+)\.(\d+)-Media.iso',
filename)
url += result.group(1)
bn = int(result.group(2)) * 100 + int(result.group(3))
url += '.{}'.format(bn)
@ -531,7 +549,8 @@ class StagingAPI(object):
building = False
if results.get('state') not in ('published', 'unpublished') or results.get('dirty') == 'true':
working.append({
'path': '{}/{}'.format(results.get('repository'), results.get('arch')),
'path': '{}/{}'.format(results.get('repository'),
results.get('arch')),
'state': results.get('state')
})
building = True
@ -543,7 +562,8 @@ class StagingAPI(object):
broken.append({
'pkg': node.get('package'),
'state': result,
'path': '{}/{}'.format(results.get('repository'), results.get('arch'))
'path': '{}/{}'.format(results.get('repository'),
results.get('arch'))
})
# Print the results
@ -573,7 +593,8 @@ class StagingAPI(object):
if len(broken) != 0:
retval.append('Following packages are broken:')
for i in broken:
retval.append(' {} ({}): {}'.format(i['pkg'], i['path'], i['state']))
retval.append(' {} ({}): {}'.format(i['pkg'], i['path'],
i['state']))
return retval
@ -599,7 +620,9 @@ class StagingAPI(object):
tar_pkg = self.delete_to_prj(act[0], project)
if not tar_pkg:
raise oscerr.WrongArgs('Request {} is not a submit or delete request'.format(request_id))
msg = 'Request {} is not a submit or delete request'
msg = msg.format(request_id)
raise oscerr.WrongArgs(msg)
# register the package name
self._add_rq_to_prj_pseudometa(project, int(request_id), tar_pkg)
@ -624,7 +647,8 @@ class StagingAPI(object):
# create build disabled package
self.create_package_container(project, tar_pkg, disable_build=True)
# now trigger wipebinaries to emulate a delete
url = self.makeurl(['build', project], {'cmd': 'wipe', 'package': tar_pkg})
url = self.makeurl(['build', project],
{'cmd': 'wipe', 'package': tar_pkg})
http_POST(url)
return tar_pkg
@ -644,17 +668,21 @@ class StagingAPI(object):
disable_build = False
if not self.ring_packages.get(tar_pkg):
disable_build = True
self.create_package_container(project, tar_pkg, disable_build=disable_build)
self.create_package_container(project, tar_pkg,
disable_build=disable_build)
# expand the revision to a md5
url = self.makeurl(['source', src_prj, src_pkg], {'rev': src_rev, 'expand': 1})
url = self.makeurl(['source', src_prj, src_pkg],
{'rev': src_rev, 'expand': 1})
f = http_GET(url)
root = ET.parse(f).getroot()
src_rev = root.attrib['srcmd5']
src_vrev = root.attrib.get('vrev')
# link stuff - not using linkpac because linkpac copies meta from source
root = ET.Element('link', package=src_pkg, project=src_prj, rev=src_rev)
# link stuff - not using linkpac because linkpac copies meta
# from source
root = ET.Element('link', package=src_pkg, project=src_prj,
rev=src_rev)
if src_vrev:
root.attrib['vrev'] = src_vrev
url = self.makeurl(['source', project, tar_pkg, '_link'])
@ -698,7 +726,8 @@ class StagingAPI(object):
if by_project:
query['by_project'] = by_project
if not msg:
msg = 'Being evaluated by staging project "{}"'.format(by_project)
msg = 'Being evaluated by staging project "{}"'
msg = msg.format(by_project)
if by_group:
query['by_group'] = by_group
if not msg:
@ -723,7 +752,8 @@ class StagingAPI(object):
if i.by_project == project and i.state == 'new':
cont = True
if cont:
msg = 'Reviewed by staging project "{}" with result: "{}"'.format(project, state)
msg = 'Reviewed by staging project "{}" with result: "{}"'
msg = msg.format(project, state)
self.change_review_state(request_id, state, by_project=project,
message=msg)