2013-09-04 15:11:27 +02:00
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# (C) 2013 mhrusecky@suse.cz, openSUSE.org
# (C) 2013 tchvatal@suse.cz, openSUSE.org
# Distribute under GPLv2 or GPLv3
2013-12-10 14:57:00 +01:00
from osc import cmdln
from osc import conf
2013-09-04 15:11:27 +02:00
OSC_STAGING_VERSION = ' 0.0.1 '
def _print_version ( self ) :
""" Print version information about this extension. """
2013-12-06 13:18:19 +01:00
print ' %s ' % ( self . OSC_STAGING_VERSION )
2013-09-04 15:11:27 +02:00
quit ( 0 )
2013-12-13 13:48:45 +01:00
# Get last build results (optionally only for specified repo/arch)
# Works even when rebuild is triggered
2013-12-13 13:22:55 +01:00
def _get_build_res ( apiurl , prj , repo = None , arch = None ) :
query = { }
query [ ' lastbuild ' ] = 1
if repo != None :
query [ ' repository ' ] = repo
if arch != None :
query [ ' arch ' ] = arch
u = makeurl ( apiurl , [ ' build ' , prj , ' _result ' ] , query = query )
f = http_GET ( u )
return f . readlines ( )
2013-12-13 13:48:45 +01:00
# Checks the state of staging repo (local modifications, regressions, ...)
2013-09-04 15:11:27 +02:00
def _staging_check ( self , project , check_everything , opts ) :
"""
Checks whether project does not contain local changes
and whether it contains only links
: param project : staging project to check
: param everything : do not stop on first verification failure
: param opts : pointer to options
"""
2013-12-06 11:55:50 +01:00
apiurl = self . get_api_url ( )
2013-09-04 15:11:27 +02:00
ret = 0
2013-12-10 14:57:00 +01:00
# Check whether there are no local changes
2013-12-06 11:55:50 +01:00
for pkg in meta_get_packagelist ( apiurl , project ) :
2013-12-13 10:30:14 +01:00
if ret != 0 and not check_everything :
2013-09-04 15:11:27 +02:00
break
f = http_GET ( makeurl ( apiurl , [ ' source ' , project , pkg ] ) )
linkinfo = ET . parse ( f ) . getroot ( ) . find ( ' linkinfo ' )
if linkinfo is None :
2013-12-06 13:18:19 +01:00
print >> sys . stderr , ' Error: Not a source link: %s / %s ' % ( project , pkg )
2013-09-04 15:11:27 +02:00
ret = 1
continue
if linkinfo . get ( ' error ' ) :
2013-12-06 13:18:19 +01:00
print >> sys . stderr , ' Error: Broken source link: %s / %s ' % ( project , pkg )
2013-09-04 15:11:27 +02:00
ret = 1
continue
t = linkinfo . get ( ' project ' )
p = linkinfo . get ( ' package ' )
r = linkinfo . get ( ' revision ' )
2013-12-06 11:55:50 +01:00
if len ( server_diff ( apiurl , t , p , r , project , pkg , None , True ) ) > 0 :
2013-12-06 13:18:19 +01:00
print >> sys . stderr , ' Error: Has local modifications: %s / %s ' % ( project , pkg )
2013-09-04 15:11:27 +02:00
ret = 1
continue
2013-12-10 14:59:26 +01:00
if ret == 1 :
print >> sys . stderr , " Error: Check for local changes failed "
else :
print " Check for local changes passed "
2013-12-13 13:48:45 +01:00
# Check for regressions
2013-12-13 10:30:14 +01:00
root = None
if ret == 0 or check_everything :
print " Getting build status, this may take a while "
# Get staging project results
2013-12-13 13:22:55 +01:00
f = _get_build_res ( apiurl , project )
2013-12-13 10:30:14 +01:00
root = ET . fromstring ( ' ' . join ( f ) )
2013-12-11 20:14:50 +01:00
2013-12-13 10:30:14 +01:00
# Get parent project
m_url = make_meta_url ( " prj " , project , apiurl )
m_data = http_GET ( m_url ) . readlines ( )
m_root = ET . fromstring ( ' ' . join ( m_data ) )
2013-12-11 20:14:50 +01:00
2013-12-13 10:30:14 +01:00
print " Comparing build statuses, this may take a while "
2013-12-10 14:59:26 +01:00
# Iterate through all repos/archs
2013-12-13 10:30:14 +01:00
if root != None and root . find ( ' result ' ) != None :
2013-12-10 14:59:26 +01:00
for results in root . findall ( ' result ' ) :
2013-12-13 10:30:14 +01:00
if ret != 0 and not check_everything :
break
2013-12-10 14:59:26 +01:00
if results . get ( " state " ) not in [ " published " , " unpublished " ] :
print >> sys . stderr , " Warning: Building not finished yet for %s / %s ( %s )! " % ( results . get ( " repository " ) , results . get ( " arch " ) , results . get ( " state " ) )
ret | = 2
2013-12-11 20:14:50 +01:00
2013-12-13 13:48:45 +01:00
# Get parent project results for this repo/arch
2013-12-11 20:14:50 +01:00
p_project = m_root . find ( " repository[@name= ' %s ' ]/path " % ( results . get ( " repository " ) ) )
if p_project == None :
print >> sys . stderr , " Error: Can ' t get path for ' %s ' ! " % results . get ( " repository " )
ret | = 4
2013-12-13 10:30:14 +01:00
continue
2013-12-13 13:22:55 +01:00
f = _get_build_res ( apiurl , p_project . get ( " project " ) , repo = results . get ( " repository " ) , arch = results . get ( " arch " ) )
2013-12-11 20:14:50 +01:00
p_root = ET . fromstring ( ' ' . join ( f ) )
# Find corresponding set of results in parent project
2013-12-10 14:59:26 +01:00
p_results = p_root . find ( " result[@repository= ' %s ' ][@arch= ' %s ' ] " % ( results . get ( " repository " ) , results . get ( " arch " ) ) )
if p_results == None :
print >> sys . stderr , " Error: Inconsistent setup! "
ret | = 4
else :
# Iterate through packages
for node in results :
2013-12-13 10:30:14 +01:00
if ret != 0 and not check_everything :
break
2013-12-10 14:59:26 +01:00
result = node . get ( " code " )
# Skip not rebuilt
2013-12-13 13:22:55 +01:00
if result in [ " blocked " , " building " , " disabled " " excluded " , " finished " , " unknown " , " unpublished " , " published " ] :
2013-12-13 10:30:14 +01:00
continue
2013-12-10 14:59:26 +01:00
# Get status of package in parent project
p_node = p_results . find ( " status[@package= ' %s ' ] " % ( node . get ( " package " ) ) )
if p_node == None :
p_result = None
else :
p_result = p_node . get ( " code " )
# Skip packages not built in parent project
2013-12-13 13:48:45 +01:00
if p_result in [ None , " disabled " , " excluded " , " unknown " , " unresolvable " ] :
2013-12-13 10:30:14 +01:00
continue
2013-12-10 14:59:26 +01:00
# Find regressions
2013-12-13 13:48:45 +01:00
if result in [ " broken " , " failed " , " unresolvable " ] and p_result not in [ " blocked " , " broken " , " failed " ] :
2013-12-10 14:59:26 +01:00
print >> sys . stderr , " Error: Regression ( %s -> %s ) in package ' %s ' in %s / %s ! " % ( p_result , result , node . get ( " package " ) , results . get ( " repository " ) , results . get ( " arch " ) )
ret | = 8
# Find fixed builds
if result in [ " succeeded " ] and result != p_result :
print " Package ' %s ' fixed ( %s -> %s ) in staging for %s / %s . " % ( node . get ( " package " ) , p_result , result , results . get ( " repository " ) , results . get ( " arch " ) )
2013-12-13 10:30:14 +01:00
if ret != 0 :
print " Staging check failed! "
else :
print " Staging check succeeded! "
2013-09-04 15:11:27 +02:00
return ret
def _staging_create ( self , sr , opts ) :
"""
Creates new staging project based on the submit request .
: param sr : submit request containing package to test directed for openSUSE : Factory
: param opts : pointer to options
"""
2013-12-06 11:55:50 +01:00
apiurl = self . get_api_url ( )
2013-09-04 15:11:27 +02:00
# read info from sr
2013-12-06 11:55:50 +01:00
req = get_request ( apiurl , sr )
2013-09-04 15:11:27 +02:00
act = req . get_actions ( " submit " ) [ 0 ]
trg_prj = act . tgt_project
trg_pkg = act . tgt_package
src_prj = act . src_project
src_pkg = act . src_package
stg_prj = trg_prj + " :Staging: " + trg_pkg
2013-12-13 13:48:45 +01:00
if opts . parent :
trg_prj = opts . parent
2013-09-04 15:11:27 +02:00
# test if staging project exists
found = 1
2013-12-06 11:55:50 +01:00
url = make_meta_url ( ' prj ' , stg_prj , apiurl )
2013-09-04 15:11:27 +02:00
try :
data = http_GET ( url ) . readlines ( )
except HTTPError as e :
if e . code == 404 :
found = 0
else :
raise e
if found == 1 :
2013-12-13 13:48:45 +01:00
print ( ' Staging project " %s " already exists, overwrite? (Y/n) ' % ( stg_prj ) )
2013-09-04 15:11:27 +02:00
answer = sys . stdin . readline ( )
if re . search ( " ^ \ s*[Nn] " , answer ) :
print ( ' Aborting... ' )
exit ( 1 )
# parse metadata from parent project
2013-12-06 11:55:50 +01:00
trg_meta_url = make_meta_url ( " prj " , trg_prj , apiurl )
2013-09-04 15:11:27 +02:00
data = http_GET ( trg_meta_url ) . readlines ( )
dis_repo = [ ]
en_repo = [ ]
repos = [ ]
perm = ' '
in_build = 0
for line in data :
# what repositories are disabled
if in_build == 1 :
if re . search ( " ^ \ s+</build> " , line ) :
in_build = 0
elif re . search ( " ^ \ s+<disable " , line ) :
dis_repo . append ( re . sub ( r ' .*repository= " ([^ " ]+) " .* ' , r ' \ 1 ' , line ) . strip ( ) )
elif re . search ( " ^ \ s+<enable " , line ) :
en_repo . append ( re . sub ( r ' .*repository= " ([^ " ]+) " .* ' , r ' \ 1 ' , line ) . strip ( ) )
elif re . search ( " ^ \ s+<build> " , line ) :
in_build = 1
# what are the rights
elif re . search ( " ^ \ s+(<person|<group) " , line ) :
perm + = line
# what are the repositories
elif re . search ( " ^ \ s+<repository " , line ) :
repos . append ( re . sub ( r ' .*name= " ([^ " ]+) " .* ' , r ' \ 1 ' , line ) . strip ( ) )
# add maintainers of source project
2013-12-06 11:55:50 +01:00
trg_meta_url = make_meta_url ( " prj " , src_prj , apiurl )
2013-09-04 15:11:27 +02:00
data = http_GET ( trg_meta_url ) . readlines ( )
perm + = " " . join ( filter ( ( lambda x : ( re . search ( " ^ \ s+(<person|<group) " , x ) != None ) ) , data ) )
# add maintainers of source package
2013-12-06 11:55:50 +01:00
trg_meta_url = make_meta_url ( " pkg " , ( src_prj , src_pkg ) , apiurl )
2013-09-04 15:11:27 +02:00
data = http_GET ( trg_meta_url ) . readlines ( )
perm + = " " . join ( filter ( ( lambda x : ( re . search ( " ^ \ s+(<person|<group) " , x ) != None ) ) , data ) )
# create xml for new project
2013-12-06 13:18:19 +01:00
new_xml = ' <project name= " %s " > \n ' % ( stg_prj )
new_xml + = ' <title>Staging project for package %s (sr# %s )</title> \n ' % ( trg_pkg , req . reqid )
2013-09-04 15:11:27 +02:00
new_xml + = ' <description></description> \n '
2013-12-06 13:18:19 +01:00
new_xml + = ' <link project= " %s " /> \n ' % ( trg_prj )
new_xml + = ' <person userid= " %s " role= " maintainer " /> \n ' % ( req . get_creator ( ) )
2013-09-04 15:11:27 +02:00
new_xml + = perm
new_xml + = ' <build><enable/></build> \n '
new_xml + = ' <debuginfo><enable/></debuginfo> \n '
new_xml + = ' <publish><disable/></publish> \n '
for repo in repos :
if repo not in dis_repo :
2013-12-10 14:57:00 +01:00
new_xml + = ' <repository name= " %s " rebuild= " direct " linkedbuild= " localdep " > \n ' % ( repo )
2013-12-06 13:18:19 +01:00
new_xml + = ' <path project= " %s " repository= " %s " /> \n ' % ( trg_prj , repo )
2013-09-04 15:11:27 +02:00
new_xml + = ' <arch>i586</arch> \n '
new_xml + = ' <arch>x86_64</arch> \n '
new_xml + = ' </repository> \n '
new_xml + = ' </project> \n '
# creation of new staging project
2013-12-06 13:18:19 +01:00
print ( ' Creating staging project " %s " ... ' % ( stg_prj ) )
2013-12-06 11:55:50 +01:00
url = make_meta_url ( ' prj ' , stg_prj , apiurl , True , False )
2013-09-04 15:11:27 +02:00
f = metafile ( url , new_xml , False )
http_PUT ( f . url , file = f . filename )
# link package there
2013-12-06 13:18:19 +01:00
print ( ' Linking package %s / %s -> %s / %s ... ' % ( src_pkg , src_prj , stg_prj , trg_pkg ) )
2013-09-04 15:11:27 +02:00
link_pac ( src_prj , src_pkg , stg_prj , trg_pkg , True )
print
return
def _staging_remove ( self , project , opts ) :
"""
Remove staging project .
: param project : staging project to delete
: param opts : pointer to options
"""
2013-12-06 11:55:50 +01:00
apiurl = self . get_api_url ( )
delete_project ( apiurl , project , force = True , msg = None )
2013-09-04 15:11:27 +02:00
print ( " Deleted. " )
return
def _staging_push ( self , project , opts ) :
"""
Generate new submit requests group based on staging project .
: param project : staging project to submit
: param opts : pointer to options
"""
2013-12-06 11:55:50 +01:00
apiurl = self . get_api_url ( )
if not self . _staging_check ( apiurl , project ) :
2013-09-04 15:11:27 +02:00
raise oscerr . ServiceRuntimeError ( ' Verification of staging repo failed. ' )
# loop over packages
2013-12-06 11:55:50 +01:00
for pkg in meta_get_packagelist ( apiurl , project ) :
2013-09-04 15:11:27 +02:00
# decompose symlinks
u = makeurl ( apiurl , [ ' source ' , project , pkg ] )
f = http_GET ( u )
root = ET . parse ( f ) . getroot ( )
linkinfo = root . find ( ' linkinfo ' )
if linkinfo == None :
2013-12-06 13:18:19 +01:00
print >> sys . stderr , " Not a source link: %s " % ( pkg )
2013-09-04 15:11:27 +02:00
quit ( 1 )
if linkinfo . get ( ' error ' ) :
2013-12-06 13:18:19 +01:00
print >> sys . stderr , " Broken source link: %s " % ( pkg )
2013-09-04 15:11:27 +02:00
quit ( 1 )
t = linkinfo . get ( ' project ' )
p = linkinfo . get ( ' package ' )
r = linkinfo . get ( ' revision ' )
# Get rid of old requests
2013-12-06 11:55:50 +01:00
for rq in get_exact_request_list ( apiurl , t , project , pkg , pkg , ( ' new ' , ' review ' ) ) :
2013-09-04 15:11:27 +02:00
# obsolete submit requests that contain the package (notify!)
print ( ' . ' )
# sent new submitrequest for the package
def _staging_submit_devel ( self , project , opts ) :
"""
Generate new review requests for devel - projects based on our staging changes .
: param apiurl : pointer to obs api url link
: param project : staging project to submit into devel projects
"""
print ( " Not implemented. " )
return
@cmdln.option ( ' -e ' , ' --everything ' , action = ' store_true ' , dest = ' everything ' ,
help = ' during check do not stop on first first issue and show them all ' )
2013-12-13 13:48:45 +01:00
@cmdln.option ( ' -p ' , ' --parent ' , metavar = ' TARGETPROJECT ' ,
help = ' manually specify different parent project during creation of staging ' )
2013-09-04 15:11:27 +02:00
@cmdln.option ( ' -v ' , ' --version ' , action = ' store_true ' ,
dest = ' version ' ,
help = ' show version of the plugin ' )
def do_staging ( self , subcmd , opts , * args ) :
""" $ {cmd_name} : Commands to work with staging projects
" check " will check if all packages are links without changes
" create " ( or " c " ) will create staging repo from specified submit request
" 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
changes to openSUSE : Factory
Usage :
osc staging check [ - - everything ] REPO
2013-12-13 13:48:45 +01:00
osc staging create [ - - parent project ] SR #
2013-09-04 15:11:27 +02:00
osc staging remove REPO
osc stating submit - devel REPO
"""
if opts . version :
self . _print_version ( )
# available commands
cmds = [ ' check ' , ' push ' , ' p ' , ' create ' , ' c ' , ' remove ' , ' r ' ]
if not args or args [ 0 ] not in cmds :
2013-12-06 13:18:19 +01:00
raise oscerr . WrongArgs ( ' Unknown stagings action. Choose one of the %s . ' % ( ' , ' . join ( cmds ) ) )
2013-09-04 15:11:27 +02:00
# verify the argument counts match the commands
cmd = args [ 0 ]
2013-12-13 10:30:14 +01:00
if cmd in [ ' submit-devel ' , ' s ' , ' remove ' , ' r ' ] :
2013-09-04 15:11:27 +02:00
min_args , max_args = 1 , 1
elif cmd in [ ' check ' ] :
min_args , max_args = 1 , 2
elif cmd in [ ' create ' , ' c ' ] :
2013-12-13 13:48:45 +01:00
min_args , max_args = 1 , 2
2013-09-04 15:11:27 +02:00
else :
2013-12-06 13:18:19 +01:00
raise RuntimeError ( ' Unknown command: %s ' % ( cmd ) )
2013-09-04 15:11:27 +02:00
if len ( args ) - 1 < min_args :
raise oscerr . WrongArgs ( ' Too few arguments. ' )
if not max_args is None and len ( args ) - 1 > max_args :
raise oscerr . WrongArgs ( ' Too many arguments. ' )
# init the obs access
2013-12-06 11:55:50 +01:00
apiurl = self . get_api_url ( )
2013-09-04 15:11:27 +02:00
# check for the opts
staging_check_everything = False
if opts . everything :
staging_check_everything = True
# call the respective command and parse args by need
if cmd in [ ' push ' , ' p ' ] :
project = args [ 1 ]
self . _staging_push ( project , opts )
elif cmd in [ ' create ' , ' c ' ] :
sr = args [ 1 ]
self . _staging_create ( sr , opts )
elif cmd in [ ' check ' ] :
project = args [ 1 ]
2013-12-10 14:59:26 +01:00
return self . _staging_check ( project , staging_check_everything , opts )
2013-09-04 15:11:27 +02:00
elif cmd in [ ' remove ' , ' r ' ] :
project = args [ 1 ]
self . _staging_remove ( project , opts )
elif cmd in [ ' submit-devel ' , ' s ' ] :
project = args [ 1 ]
self . _staging_submit_devel ( project , opts )