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

View File

@ -6,17 +6,39 @@ from osc.core import makeurl
from osc.core import http_GET from osc.core import http_GET
FACTORY = 'openSUSE:Factory'
STG_PREFIX = 'openSUSE:Factory:Staging:'
class RequestFinder: class RequestFinder:
@classmethod def __init__(self, apiurl):
def find_request_id(self, request, 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 Look up the request by ID to verify if it is correct
:param request: ID of the added request :param request: ID of the added request
:param apiurl: OBS url :param apiurl: OBS url
""" """
url = makeurl(apiurl, ['request', str(request)]) url = makeurl(self.apiurl, ['request', str(request)])
try: try:
f = http_GET(url) f = http_GET(url)
except urllib2.HTTPError: except urllib2.HTTPError:
@ -28,72 +50,100 @@ class RequestFinder:
return None return None
project = root.find('action').find('target').get('project') project = root.find('action').find('target').get('project')
if project != 'openSUSE:Factory': if project != FACTORY and not project.startswith(STG_PREFIX):
raise oscerr.WrongArgs('Request {} is not for openSUSE:Factory, but for {}'.format(request, project)) msg = 'Request {} is not for openSUSE:Factory, but for {}'
self.srids.add(int(request)) 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 return True
@classmethod def find_request_package(self, package):
def find_request_package(self, package, apiurl):
""" """
Look up the package by its name and return the SR# Look up the package by its name and return the SR#
:param package: name of the package :param package: name of the package
:param apiurl: OBS url :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) f = http_GET(url)
root = ET.parse(f).getroot() root = ET.parse(f).getroot()
ret = None ret = None
for x in root.findall('request'): for sr in root.findall('request'):
# TODO: check the package matches - OBS is case insensitive # 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: 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 ret = True
return ret return ret
@classmethod def find_request_project(self, source_project):
def find_request_project(self, source_project, apiurl):
""" """
Look up the source project by its name and return the SR#(s) Look up the source project by its name and return the SR#(s)
:param source_project: name of the source project :param source_project: name of the source project
:param apiurl: OBS url :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) f = http_GET(url)
root = ET.parse(f).getroot() root = ET.parse(f).getroot()
ret = None ret = None
for rq in root.findall('request'): for sr in root.findall('request'):
for a in rq.findall('action'): for act in sr.findall('action'):
s = a.find('source') src = act.find('source')
if s is not None and s.get('project') == source_project: if src is not None and src.get('project') == source_project:
self.srids.add(int(rq.attrib['id'])) 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 ret = True
return ret 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 @classmethod
def find_sr(self, pkgs, apiurl): def find_sr(cls, pkgs, apiurl):
""" """
Search for all various mutations and return list of SR#s Search for all various mutations and return list of SR#s
:param pkgs: mesh of argumets to search for :param pkgs: mesh of argumets to search for
:param apiurl: OBS url :param apiurl: OBS url
""" """
finder = cls(apiurl)
self.srids = set() finder.find(pkgs)
for p in pkgs: return finder.srs
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))

View File

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