2013-09-04 15:11:27 +02:00
#!/usr/bin/env python
2014-01-31 10:29:44 +01:00
# -*- coding: utf-8 -*-
2013-09-04 15:11:27 +02:00
#
2014-02-10 10:19:37 +01:00
# (C) 2014 mhrusecky@suse.cz, openSUSE.org
# (C) 2014 tchvatal@suse.cz, openSUSE.org
2013-09-04 15:11:27 +02:00
# Distribute under GPLv2 or GPLv3
2013-12-10 14:57:00 +01:00
from osc import cmdln
from osc import conf
2013-12-17 13:45:59 +01:00
from osc import commandline
2013-12-10 14:57:00 +01:00
2014-02-05 15:34:21 +01:00
from osc . core import *
import osc
2014-02-10 09:45:22 +01:00
import logging
import yaml
2014-02-05 15:34:21 +01:00
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 )
2014-02-10 13:24:49 +01:00
class StagingApi ( object ) :
2014-02-05 15:34:21 +01:00
"""
2014-02-10 13:24:49 +01:00
Class containing various api calls to work with staging projects .
2014-02-06 09:51:51 +01:00
"""
2014-02-10 13:24:49 +01:00
rings = [ ' openSUSE:Factory:Build ' ,
' openSUSE:Factory:Core ' ,
' openSUSE:Factory:MainDesktops ' ,
' openSUSE:Factory:DVD ' ]
ring_packages = dict ( )
apiurl = " "
def __init__ ( self , apiurl ) :
"""
Initialize global variables
"""
self . apiurl = apiurl
self . ring_packages = self . _generate_ring_packages ( )
def _generate_ring_packages ( self ) :
"""
Generate dictionary with names of the rings
: return dictionary with ring names
"""
ret = dict ( )
for prj in self . rings :
url = makeurl ( self . apiurl , [ ' source ' , prj ] )
root = http_GET ( url )
for entry in ET . parse ( root ) . getroot ( ) . findall ( ' entry ' ) :
ret [ entry . attrib [ ' name ' ] ] = prj
return ret
def get_staging_projects ( self ) :
"""
Get all current running staging projects
: return list of known staging projects
"""
projects = [ ]
url = makeurl ( self . apiurl , [ ' search ' , ' project ' , ' id?match=starts-with(@name, \' openSUSE:Factory:Staging: \' ) ' ] )
projxml = http_GET ( url )
root = ET . parse ( projxml ) . getroot ( )
for val in root . findall ( ' project ' ) :
projects . append ( val . get ( ' name ' ) )
return projects
def staging_change_review_state ( self , id , newstate , message ) :
"""
Change review state of the staging request
: param id : id of the request
: param newstate : state of the new request
: param message : message for the review
"""
""" taken from osc/osc/core.py, improved:
- verbose option added ,
- empty by_user = & removed .
- numeric id can be int ( ) .
"""
query = { ' cmd ' : ' changereviewstate ' ,
' newstate ' : newstate ,
' by_group ' : ' factory-staging ' ,
' comment ' : message }
2014-02-10 13:58:59 +01:00
url = makeurl ( self . apiurl , [ ' request ' , str ( id ) ] , query = query )
f = http_POST ( url , data = message )
2014-02-10 13:24:49 +01:00
2014-02-10 13:37:39 +01:00
def accept_non_ring_request ( self , request ) :
2014-02-10 13:24:49 +01:00
"""
Accept review of requests that are not yet in
any ring so we don ' t delay their testing.
: param request : request to check
"""
# Consolidate all data from request
request_id = int ( request . get ( ' id ' ) )
action = request . findall ( ' action ' )
if not action :
raise oscerr . WrongArgs ( ' Request {0} has no action ' . format ( request_id ) )
# we care only about first action
action = action [ 0 ]
# Where are we targeting the package
target_project = action . find ( ' target ' ) . get ( ' project ' )
target_package = action . find ( ' target ' ) . get ( ' package ' )
# If the values are empty it is no error
2014-02-10 13:39:20 +01:00
if not target_project or not target_package :
2014-02-10 13:37:39 +01:00
logging . info ( ' no target/package in request {0} , action {1} ; ' . format ( id , action ) )
2014-02-10 13:24:49 +01:00
# Verify the package ring
2014-02-10 13:39:20 +01:00
ring = self . ring_packages . get ( target_package , None )
2014-02-10 13:58:59 +01:00
# DVD and main desktops are ignored for now
2014-02-10 13:24:49 +01:00
if ring is None or ring == ' openSUSE:Factory:DVD ' or ring == ' openSUSE:Factory:MainDesktops ' :
# accept the request here
2014-02-10 13:58:59 +01:00
message = " No need for staging, not in tested ring project. "
2014-02-10 13:24:49 +01:00
self . staging_change_review_state ( request_id , ' accepted ' , message )
def get_open_requests ( self ) :
"""
Get all requests with open review for staging project
that are not yet included in any staging project
: return list of pending open review requests
"""
requests = [ ]
# xpath query, using the -m, -r, -s options
where = " @by_group= ' factory-staging ' +and+@state= ' new ' "
url = makeurl ( self . apiurl , [ ' search ' , ' request ' ] , " match=state/@name= ' review ' +and+review[ " + where + " ] " )
f = http_GET ( url )
root = ET . parse ( f ) . getroot ( )
for rq in root . findall ( ' request ' ) :
requests . append ( rq )
return requests
def dispatch_open_requests ( self ) :
"""
Verify all requests and dispatch them to staging projects or approve them
"""
# get all current pending requests
requests = self . get_open_requests ( )
# check if we can reduce it down by accepting some
for rq in requests :
2014-02-10 13:37:39 +01:00
self . accept_non_ring_request ( rq )
2014-02-05 15:34:21 +01:00
2014-02-10 13:24:49 +01:00
# FIXME: dispatch to various staging projects automatically
2014-02-05 15:34:21 +01:00
2014-02-10 14:38:50 +01:00
def get_prj_pseudometa ( self , project ) :
2014-02-10 14:05:18 +01:00
"""
Gets project data from YAML in project description
: param project : project to read data from
: return structured object with metadata
"""
url = make_meta_url ( ' prj ' , project , self . apiurl )
data = http_GET ( url ) . readlines ( )
root = ET . fromstring ( ' ' . join ( data ) )
description = root . find ( ' description ' )
# If YAML parsing fails, load default
# FIXME: Better handling of errors
2014-02-10 14:52:50 +01:00
# * broken description
# * directly linked packages
# * removed linked packages
2014-02-10 14:05:18 +01:00
try :
data = yaml . load ( description . text )
except :
data = yaml . load ( ' requests: [] ' )
return data
2014-02-10 14:38:50 +01:00
def set_prj_pseudometa ( self , project , meta ) :
2014-02-10 14:05:18 +01:00
"""
Sets project description to the YAML of the provided object
: param project : project to save into
: param meta : data to save
"""
# Get current metadata
url = make_meta_url ( ' prj ' , project , self . apiurl )
data = http_GET ( url ) . readlines ( )
root = ET . fromstring ( ' ' . join ( data ) )
# Find description
description = root . find ( ' description ' )
# Replace it with yaml
description . text = yaml . dump ( meta )
# Write XML back
url = make_meta_url ( ' prj ' , project , self . apiurl , force = True )
f = metafile ( url , ET . tostring ( root ) )
http_PUT ( f . url , file = f . filename )
2014-02-10 14:38:50 +01:00
def _add_rq_to_prj_pseudometa ( self , project , request_id , package ) :
2014-02-10 14:05:18 +01:00
"""
Records request as part of the project within metadata
: param project : project to record into
: param request_id : request id to record
: param package : package the request is about
"""
2014-02-10 14:38:50 +01:00
data = self . get_prj_pseudometa ( project )
2014-02-10 14:05:18 +01:00
append = True
for request in data [ ' requests ' ] :
if request [ ' package ' ] == package :
request [ ' id ' ] = request_id
append = False
if append :
data [ ' requests ' ] . append ( { ' id ' : request_id , ' package ' : package } )
2014-02-10 14:38:50 +01:00
self . set_prj_pseudometa ( project , data )
2014-02-10 14:52:50 +01:00
# FIXME Add sr to group request as well
2014-02-10 14:05:18 +01:00
def sr_to_prj ( self , request_id , project ) :
"""
Links sources from request to project
: param request_id : request to link
: param project : project to link into
"""
# read info from sr
req = get_request ( self . apiurl , request_id )
if not req :
2014-02-10 14:52:50 +01:00
raise oscerr . WrongArgs ( " Request {0} not found " . format ( request_id ) )
2014-02-10 14:05:18 +01:00
act = req . get_actions ( " submit " )
if not act :
2014-02-10 14:52:50 +01:00
raise oscerr . WrongArgs ( " Request {0} is not a submit request " . format ( request_id ) )
2014-02-10 14:05:18 +01:00
act = act [ 0 ]
src_prj = act . src_project
src_rev = act . src_rev
src_pkg = act . src_package
# link stuff
2014-02-10 14:38:50 +01:00
self . _add_rq_to_prj_pseudometa ( project , request_id , src_pkg )
2014-02-10 14:05:18 +01:00
link_pac ( src_prj , src_pkg , project , src_pkg , force = True , rev = src_rev )
2014-02-10 14:52:50 +01:00
# FIXME If there are links in parent project, make sure that current
2014-02-10 14:05:18 +01:00
2014-02-10 09:45:22 +01:00
def _get_parent ( apirul , project , repo = " standard " ) :
"""
Finds what is the parent project of the staging project
2014-02-10 10:48:29 +01:00
: param apiurl : url to the OBS api
2014-02-10 09:45:22 +01:00
: param project : staging project to check
: param repo : which repository to follow
2014-02-10 10:48:29 +01:00
: return name of the parent project
2014-02-10 09:45:22 +01:00
"""
url = make_meta_url ( " prj " , project , apiurl )
data = http_GET ( url ) . readlines ( )
root = ET . fromstring ( ' ' . join ( data ) )
p_path = root . find ( " repository[@name= ' %s ' ]/path " % ( repo ) )
if not p_path :
logging . error ( " Project ' %s ' has no repository named ' %s ' " % ( project , repo ) )
return None
return p_path [ ' project ' ]
2013-12-13 13:48:45 +01:00
# Get last build results (optionally only for specified repo/arch)
# Works even when rebuild is triggered
2014-01-31 10:29:44 +01:00
def _get_build_res ( opts , prj , repo = None , arch = None ) :
2013-12-13 13:22:55 +01:00
query = { }
query [ ' lastbuild ' ] = 1
2014-01-06 10:58:30 +01:00
if repo is not None :
2013-12-13 13:22:55 +01:00
query [ ' repository ' ] = repo
2014-01-06 10:58:30 +01:00
if arch is not None :
2013-12-13 13:22:55 +01:00
query [ ' arch ' ] = arch
2014-01-31 10:29:44 +01:00
u = makeurl ( opts . apiurl , [ ' build ' , prj , ' _result ' ] , query = query )
2013-12-13 13:22:55 +01:00
f = http_GET ( u )
return f . readlines ( )
2014-01-31 10:29:44 +01:00
def _get_changed ( opts , project , everything ) :
2013-12-16 15:33:50 +01:00
ret = [ ]
# Check for local changes
2014-01-31 10:29:44 +01:00
for pkg in meta_get_packagelist ( opts . apiurl , project ) :
2013-12-16 15:33:50 +01:00
if len ( ret ) != 0 and not everything :
2013-09-04 15:11:27 +02:00
break
2014-01-31 10:29:44 +01:00
f = http_GET ( makeurl ( opts . apiurl , [ ' source ' , project , pkg ] ) )
2013-09-04 15:11:27 +02:00
linkinfo = ET . parse ( f ) . getroot ( ) . find ( ' linkinfo ' )
if linkinfo is None :
2013-12-16 15:33:50 +01:00
ret . append ( { ' pkg ' : pkg , ' code ' : ' NOT_LINK ' , ' msg ' : ' Not a source link ' } )
2013-09-04 15:11:27 +02:00
continue
if linkinfo . get ( ' error ' ) :
2013-12-16 15:33:50 +01:00
ret . append ( { ' pkg ' : pkg , ' code ' : ' BROKEN ' , ' msg ' : ' Broken source link ' } )
2013-09-04 15:11:27 +02:00
continue
t = linkinfo . get ( ' project ' )
p = linkinfo . get ( ' package ' )
r = linkinfo . get ( ' revision ' )
2014-01-31 10:29:44 +01:00
if len ( server_diff ( opts . apiurl , t , p , r , project , pkg , None , True ) ) > 0 :
2013-12-17 13:45:59 +01:00
ret . append ( { ' pkg ' : pkg , ' code ' : ' MODIFIED ' , ' msg ' : ' Has local modifications ' , ' pprj ' : t , ' ppkg ' : p } )
2013-09-04 15:11:27 +02:00
continue
2013-12-16 15:33:50 +01:00
return ret
# Checks the state of staging repo (local modifications, regressions, ...)
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
"""
ret = 0
2014-02-11 11:08:02 +01:00
chng = _get_changed ( opts , project , check_everything )
2013-12-16 15:33:50 +01:00
if len ( chng ) > 0 :
for pair in chng :
print >> sys . stderr , ' Error: Package " %s " : %s ' % ( pair [ ' pkg ' ] , pair [ ' msg ' ] )
2013-12-10 14:59:26 +01:00
print >> sys . stderr , " Error: Check for local changes failed "
2013-12-16 15:33:50 +01:00
ret = 1
2013-12-10 14:59:26 +01:00
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
2014-01-31 10:29:44 +01:00
f = _get_build_res ( opts . 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
2014-01-31 10:29:44 +01:00
m_url = make_meta_url ( " prj " , project , opts . apiurl )
2013-12-13 10:30:14 +01:00
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
2014-01-06 10:58:30 +01:00
if root is not None and root . find ( ' result ' ) is not 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
2014-01-31 10:29:44 +01:00
f = _get_build_res ( opts . 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
2013-12-13 14:23:35 +01:00
def _staging_create ( self , trg , opts ) :
2013-09-04 15:11:27 +02:00
"""
Creates new staging project based on the submit request .
2013-12-13 14:23:35 +01:00
: param trg : submit request to create staging project for or parent project / package
2013-09-04 15:11:27 +02:00
: param opts : pointer to options
"""
2013-12-13 14:23:35 +01:00
req = None
2013-12-06 11:55:50 +01:00
2013-12-13 14:23:35 +01:00
# We are dealing with sr
if re . match ( ' ^ \ d+$ ' , trg ) :
# read info from sr
2014-01-31 10:29:44 +01:00
req = get_request ( opts . apiurl , trg )
2013-12-13 14:23:35 +01: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
# We are dealing with project
else :
data = re . split ( ' / ' , trg )
o_stg_prj = data [ 0 ]
trg_prj = re . sub ( ' :Staging:.* ' , ' ' , data [ 0 ] )
src_prj = re . sub ( ' :Staging:.* ' , ' ' , data [ 0 ] )
if len ( data ) > 1 :
trg_pkg = data [ 1 ]
src_pkg = data [ 1 ]
else :
trg_pkg = None
src_pkg = None
# Set staging name and maybe parent
2014-01-06 10:58:30 +01:00
if trg_pkg is not None :
2013-12-13 14:23:35 +01:00
stg_prj = trg_prj + " :Staging: " + trg_pkg
if re . search ( ' :Staging: ' , trg ) :
stg_prj = o_stg_prj
2013-09-04 15:11:27 +02:00
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
2014-01-31 10:29:44 +01:00
url = make_meta_url ( ' prj ' , stg_prj , opts . 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 )
2014-02-10 10:19:37 +01:00
2013-09-04 15:11:27 +02:00
# parse metadata from parent project
2014-01-31 10:29:44 +01:00
trg_meta_url = make_meta_url ( " prj " , trg_prj , opts . 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 ( ) )
2014-02-10 10:19:37 +01:00
2013-09-04 15:11:27 +02:00
# add maintainers of source project
2014-01-31 10:29:44 +01:00
trg_meta_url = make_meta_url ( " prj " , src_prj , opts . apiurl )
2013-09-04 15:11:27 +02:00
data = http_GET ( trg_meta_url ) . readlines ( )
2014-01-06 10:58:30 +01:00
perm + = " " . join ( filter ( ( lambda x : ( re . search ( " ^ \ s+(<person|<group) " , x ) is not None ) ) , data ) )
2014-02-10 10:19:37 +01:00
2013-09-04 15:11:27 +02:00
# add maintainers of source package
2014-01-06 10:58:30 +01:00
if src_pkg is not None :
2014-01-31 10:29:44 +01:00
trg_meta_url = make_meta_url ( " pkg " , ( src_prj , src_pkg ) , opts . apiurl )
2013-12-13 14:23:35 +01:00
data = http_GET ( trg_meta_url ) . readlines ( )
2014-01-06 10:58:30 +01:00
perm + = " " . join ( filter ( ( lambda x : ( re . search ( " ^ \ s+(<person|<group) " , x ) is not None ) ) , data ) )
2013-09-04 15:11:27 +02:00
# create xml for new project
2013-12-06 13:18:19 +01:00
new_xml = ' <project name= " %s " > \n ' % ( stg_prj )
2014-01-06 10:58:30 +01:00
if req is not None :
2013-12-13 14:23:35 +01:00
new_xml + = ' <title>Staging project for package %s (sr# %s )</title> \n ' % ( trg_pkg , req . reqid )
else :
new_xml + = ' <title>Staging project " %s " </title> \n ' % ( trg )
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 )
2014-01-06 10:58:30 +01:00
if req is not None :
2013-12-13 14:23:35 +01:00
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 ) )
2014-01-31 10:29:44 +01:00
url = make_meta_url ( ' prj ' , stg_prj , opts . 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
2014-01-06 10:58:30 +01:00
if src_pkg is not None and trg_pkg is not None :
2013-12-13 14:23:35 +01:00
print ( ' Linking package %s / %s -> %s / %s ... ' % ( src_pkg , src_prj , stg_prj , trg_pkg ) )
link_pac ( src_prj , src_pkg , stg_prj , trg_pkg , True )
2013-09-04 15:11:27 +02:00
print
2014-02-10 10:19:37 +01:00
2013-09-04 15:11:27 +02:00
return
def _staging_remove ( self , project , opts ) :
"""
Remove staging project .
: param project : staging project to delete
: param opts : pointer to options
"""
2014-01-31 10:29:44 +01:00
chng = _get_changed ( opts , project , True )
2013-12-16 15:33:50 +01:00
if len ( chng ) > 0 :
print ( ' Staging project " %s " is not clean: ' % ( project ) )
print ( ' ' )
for pair in chng :
print ( ' * %s : %s ' % ( pair [ ' pkg ' ] , pair [ ' msg ' ] ) )
print ( ' ' )
print ( ' Really delete? (N/y) ' )
answer = sys . stdin . readline ( )
if not re . search ( " ^ \ s*[Yy] " , answer ) :
print ( ' Aborting... ' )
exit ( 1 )
2014-01-31 10:29:44 +01:00
delete_project ( opts . apiurl , project , force = True , msg = None )
2013-09-04 15:11:27 +02:00
print ( " Deleted. " )
return
def _staging_submit_devel ( self , project , opts ) :
"""
Generate new review requests for devel - projects based on our staging changes .
: param project : staging project to submit into devel projects
"""
2014-01-31 10:29:44 +01:00
chng = _get_changed ( opts , project , True )
2013-12-17 13:45:59 +01:00
msg = " Fixes from staging project %s " % project
2014-01-06 10:58:30 +01:00
if opts . message is not None :
2013-12-17 13:45:59 +01:00
msg = opts . message
if len ( chng ) > 0 :
for pair in chng :
if pair [ ' code ' ] != ' MODIFIED ' :
print >> sys . stderr , ' 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> ' ;
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> '
2014-01-31 10:29:44 +01:00
u = makeurl ( opts . apiurl , [ ' request ' ] , query = ' cmd=create&addrevision=1 ' )
2013-12-17 13:45:59 +01:00
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 " )
2013-09-04 15:11:27 +02:00
return
2014-01-31 10:29:44 +01:00
def _staging_change_review_state ( self , opts , id , newstate , by_group = ' ' , by_user = ' ' , message = ' ' , supersed = None ) :
""" taken from osc/osc/core.py, improved:
- verbose option added ,
- empty by_user = & removed .
- numeric id can be int ( ) .
"""
query = { ' cmd ' : ' changereviewstate ' , ' newstate ' : newstate }
if by_group : query [ ' by_group ' ] = by_group
if by_user : query [ ' by_user ' ] = by_user
if supersed : query [ ' superseded_by ' ] = supersed
# if message: query['comment'] = message
u = makeurl ( opts . apiurl , [ ' request ' , str ( id ) ] , query = query )
f = http_POST ( u , data = message )
root = ET . parse ( f ) . getroot ( )
return root . attrib [ ' code ' ]
def _staging_get_rings ( self , opts ) :
ret = dict ( )
for prj in [ ' openSUSE:Factory:Build ' , ' openSUSE:Factory:Core ' , ' openSUSE:Factory:MainDesktops ' , ' openSUSE:Factory:DVD ' ] :
u = makeurl ( opts . apiurl , [ ' source ' , prj ] )
f = http_GET ( u )
for entry in ET . parse ( f ) . getroot ( ) . findall ( ' entry ' ) :
ret [ entry . attrib [ ' name ' ] ] = prj
return ret
def _staging_one_request ( self , rq , opts ) :
if ( opts . verbose ) :
ET . dump ( rq )
print ( opts )
id = int ( rq . get ( ' id ' ) )
act_id = 0
approved_actions = 0
actions = rq . findall ( ' action ' )
act = actions [ 0 ]
2014-02-10 10:19:37 +01:00
2014-01-31 10:29:44 +01:00
tprj = act . find ( ' target ' ) . get ( ' project ' )
tpkg = act . find ( ' target ' ) . get ( ' package ' )
e = [ ]
if not tpkg :
e . append ( ' no target/package in request %d , action %d ; ' % ( id , act_id ) )
if not tprj :
e . append ( ' no target/project in request %d , action %d ; ' % ( id , act_id ) )
# it is no error, if the target package dies not exist
ring = self . rings . get ( tpkg , None )
if ring is None or ring == ' openSUSE:Factory:DVD ' or ring == ' openSUSE:Factory:MainDesktops ' :
msg = " ok "
else :
stage_info = self . packages_staged . get ( tpkg , ( ' ' , 0 ) )
if stage_info [ 0 ] == self . letter_to_accept and int ( stage_info [ 1 ] ) == id :
stprj = ' openSUSE:Factory:Staging: %s ' % self . letter_to_accept
msg = ' ok, tested in %s ' % stprj
delete_package ( opts . apiurl , stprj , tpkg , msg = ' done ' )
elif stage_info [ 1 ] != 0 and int ( stage_info [ 1 ] ) != id :
2014-02-10 14:05:18 +01:00
print stage_info
2014-01-31 10:29:44 +01:00
print " osc rqlink %s openSUSE:Factory:Staging: %s " % ( id , stage_info [ 0 ] )
2014-02-10 14:05:18 +01:00
return
2014-01-31 10:29:44 +01:00
elif stage_info [ 1 ] != 0 : # keep silent about those already asigned
return
else :
print " Request( %d ): %s -> %s " % ( id , tpkg , ring )
2014-02-10 14:05:18 +01:00
print " osc rqlink %s openSUSE:Factory:Staging: " % id
2014-01-31 10:29:44 +01:00
return
self . _staging_change_review_state ( opts , id , ' accepted ' , by_group = ' factory-staging ' , message = msg )
def _staging_parse_staging_prjs ( self , opts ) :
self . packages_staged = dict ( )
for letter in range ( ord ( ' A ' ) , ord ( ' J ' ) ) :
prj = " openSUSE:Factory:Staging: %s " % chr ( letter )
u = makeurl ( opts . apiurl , [ ' source ' , prj , ' _meta ' ] )
f = http_GET ( u )
title = ET . parse ( f ) . getroot ( ) . find ( ' title ' ) . text
if title is None : continue
for rq in title . split ( ' , ' ) :
m = re . match ( r " *([ \ w-]+) \ (( \ d+) \ ) " , rq )
if m is None : continue
self . packages_staged [ m . group ( 1 ) ] = ( chr ( letter ) , m . group ( 2 ) )
2014-01-31 14:14:24 +01:00
def _staging_check_one_source ( self , flink , si , opts ) :
package = si . get ( ' package ' )
# we have to check if its a link within the staging project
# in this case we need to keep the link as is, and not freezing
# the target. Otherwise putting kernel-source into staging prj
# won't get updated kernel-default (and many other cases)
for linked in si . findall ( ' linked ' ) :
if linked . get ( ' project ' ) in self . projectlinks :
# take the unexpanded md5 from Factory link
2014-02-03 11:49:15 +01:00
url = makeurl ( opts . apiurl , [ ' source ' , ' openSUSE:Factory ' , package ] , { ' view ' : ' info ' , ' nofilename ' : ' 1 ' } )
2014-01-31 14:14:24 +01:00
#print package, linked.get('package'), linked.get('project')
f = http_GET ( url )
proot = ET . parse ( f ) . getroot ( )
ET . SubElement ( flink , ' package ' , { ' name ' : package , ' srcmd5 ' : proot . get ( ' lsrcmd5 ' ) , ' vrev ' : si . get ( ' vrev ' ) } )
return package
ET . SubElement ( flink , ' package ' , { ' name ' : package , ' srcmd5 ' : si . get ( ' srcmd5 ' ) , ' vrev ' : si . get ( ' vrev ' ) } )
return package
2014-01-31 10:29:44 +01:00
def _staging_receive_sources ( self , prj , sources , flink , opts ) :
2014-02-03 11:49:15 +01:00
url = makeurl ( opts . apiurl , [ ' source ' , prj ] , { ' view ' : ' info ' , ' nofilename ' : ' 1 ' } )
2014-01-31 10:29:44 +01:00
f = http_GET ( url )
root = ET . parse ( f ) . getroot ( )
for si in root . findall ( ' sourceinfo ' ) :
2014-01-31 14:14:24 +01:00
package = self . _staging_check_one_source ( flink , si , opts )
2014-01-31 10:29:44 +01:00
sources [ package ] = 1
return sources
def _staging_freeze_prjlink ( self , prj , opts ) :
url = makeurl ( opts . apiurl , [ ' source ' , prj , ' _meta ' ] )
f = http_GET ( url )
root = ET . parse ( f ) . getroot ( )
sources = dict ( )
flink = ET . Element ( ' frozenlinks ' )
links = root . findall ( ' link ' )
links . reverse ( )
2014-01-31 14:14:24 +01:00
self . projectlinks = [ ]
2014-01-31 10:29:44 +01:00
for link in links :
2014-01-31 14:14:24 +01:00
self . projectlinks . append ( link . get ( ' project ' ) )
for lprj in self . projectlinks :
2014-01-31 10:29:44 +01:00
fl = ET . SubElement ( flink , ' frozenlink ' , { ' project ' : lprj } )
sources = self . _staging_receive_sources ( lprj , sources , fl , opts )
2014-02-10 10:19:37 +01:00
2014-01-31 14:14:24 +01:00
from pprint import pprint
2014-01-31 10:29:44 +01:00
url = makeurl ( opts . apiurl , [ ' source ' , prj , ' _project ' , ' _frozenlinks ' ] , { ' meta ' : ' 1 ' } )
f = http_PUT ( url , data = ET . tostring ( flink ) )
root = ET . parse ( f ) . getroot ( )
print ET . tostring ( root )
2013-09-04 15:11:27 +02:00
2014-02-01 21:00:55 +01:00
def _staging_cleanup_rings ( self , opts ) :
self . bin2src = dict ( )
self . pkgdeps = dict ( )
self . sources = list ( )
self . _staging_check_depinfo_ring ( ' openSUSE:Factory:Build ' , ' openSUSE:Factory:Core ' , opts ) ;
self . _staging_check_depinfo_ring ( ' openSUSE:Factory:Core ' , ' openSUSE:Factory:MainDesktops ' , opts ) ;
def _staging_fill_pkgdeps ( self , prj , repo , arch , opts ) :
url = makeurl ( opts . apiurl , [ ' build ' , prj , repo , arch , ' _builddepinfo ' ] )
f = http_GET ( url )
root = ET . parse ( f ) . getroot ( )
2014-02-10 10:19:37 +01:00
2014-02-01 21:00:55 +01:00
for package in root . findall ( ' package ' ) :
#print ET.tostring(package)
source = package . find ( ' source ' ) . text
if package . attrib [ ' name ' ] . startswith ( ' preinstall ' ) :
continue
self . sources . append ( source )
for subpkg in package . findall ( ' subpkg ' ) :
subpkg = subpkg . text
if self . bin2src . has_key ( subpkg ) :
print " bin $s defined twice $prj $source - $bin2src { $s} \n "
self . bin2src [ subpkg ] = source
2014-02-10 10:19:37 +01:00
2014-02-01 21:00:55 +01:00
for package in root . findall ( ' package ' ) :
source = package . find ( ' source ' ) . text
for pkg in package . findall ( ' pkgdep ' ) :
if not self . bin2src . has_key ( pkg . text ) :
if pkg . text . startswith ( ' texlive- ' ) :
for letter in range ( ord ( ' a ' ) , ord ( ' z ' ) + 1 ) :
self . pkgdeps [ ' texlive-specs- ' + chr ( letter ) ] = ' texlive-specs- ' + chr ( letter )
else :
print " PKG NOT THERE " , pkg . text
continue
b = self . bin2src [ pkg . text ]
self . pkgdeps [ b ] = source
def _staging_check_depinfo_ring ( self , prj , nextprj , opts ) :
self . _staging_fill_pkgdeps ( prj , ' standard ' , ' x86_64 ' , opts )
if prj == ' openSUSE:Factory:Core ' :
url = makeurl ( opts . apiurl , [ ' build ' , prj , ' images ' , ' x86_64 ' , ' Test-DVD-x86_64 ' , ' _buildinfo ' ] )
root = ET . parse ( http_GET ( url ) ) . getroot ( )
for bdep in root . findall ( ' bdep ' ) :
if not bdep . attrib . has_key ( ' name ' ) : continue
b = bdep . attrib [ ' name ' ]
2014-02-02 07:28:12 +01:00
if not self . bin2src . has_key ( b ) : continue
b = self . bin2src [ b ]
2014-02-01 21:00:55 +01:00
self . pkgdeps [ b ] = ' MYdvd '
# if ($prj eq 'openSUSE:Factory:MainDesktops') {
# $dinfo->{MYcds} = {};
# $dinfo->{MYcds}->{pkgdep} = ();
# $dinfo->{MYcds}->{source} = 'MYcds';
# push(@{$dinfo->{MYcds}->{pkgdep}}, 'kiwi-image-livecd-gnome');
# push(@{$dinfo->{MYcds}->{pkgdep}}, 'kiwi-image-livecd-kde');
if prj == ' openSUSE:Factory:Build ' :
url = makeurl ( opts . apiurl , [ ' build ' , prj , ' standard ' , ' _buildconfig ' ] )
for line in http_GET ( url ) . read ( ) . split ( ' \n ' ) :
if line . startswith ( ' Preinstall: ' ) or line . startswith ( ' Support: ' ) :
for prein in line . split ( ' : ' ) [ 1 ] . split ( ) :
if not self . bin2src . has_key ( prein ) : continue
b = self . bin2src [ prein ]
self . pkgdeps [ b ] = ' MYinstall '
for source in self . sources :
# next if ($key =~ m/^MY/ || $key =~ m/^texlive-specs-/ || $key =~ m/^kernel-/);
if not self . pkgdeps . has_key ( source ) :
2014-02-02 08:26:32 +01:00
print " osc rdelete -m cleanup " , prj , source
if nextprj :
print " osc linkpac -c openSUSE:Factory " , source , nextprj
2014-02-01 21:00:55 +01:00
2013-09-04 15:11:27 +02:00
@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-12-17 13:45:59 +01:00
@cmdln.option ( ' -m ' , ' --message ' , metavar = ' TEXT ' ,
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
2014-01-31 10:29:44 +01:00
" freeze " will freeze the sources of the project ' s links (not affecting the packages actually in)
2014-02-10 10:19:37 +01:00
2014-01-31 10:29:44 +01:00
" accept " will accept all requests openSUSE : Factory : Staging : < LETTER >
" list " will pick the requests not in rings
2014-02-10 10:20:16 +01:00
" rqlink " will add request to the project
2013-09-04 15:11:27 +02:00
Usage :
osc staging check [ - - everything ] REPO
2013-12-13 13:48:45 +01:00
osc staging create [ - - parent project ] SR #
2013-12-13 14:23:35 +01:00
osc staging create [ - - parent project ] PROJECT [ / PACKAGE ]
2013-09-04 15:11:27 +02:00
osc staging remove REPO
2014-01-31 10:29:44 +01:00
osc staging submit - devel [ - m message ] REPO
osc staging freeze PROJECT
osc staging list
2014-02-10 10:20:16 +01:00
osc staging rqlink REQUEST PROJECT
2014-01-31 10:29:44 +01:00
osc staging accept LETTER
2014-02-01 21:00:55 +01:00
osc staging cleanup_rings
2013-09-04 15:11:27 +02:00
"""
if opts . version :
self . _print_version ( )
# verify the argument counts match the commands
2014-02-10 19:59:45 +01:00
if len ( args ) == 0 :
raise oscerr . WrongArgs ( ' No command given, see " osc help staging " ! ' )
2013-09-04 15:11:27 +02:00
cmd = args [ 0 ]
2014-02-01 21:00:55 +01:00
if cmd in [ ' submit-devel ' , ' s ' , ' remove ' , ' r ' , ' accept ' , ' freeze ' ] :
2013-09-04 15:11:27 +02:00
min_args , max_args = 1 , 1
elif cmd in [ ' check ' ] :
min_args , max_args = 1 , 2
2014-02-10 09:45:22 +01:00
elif cmd in [ ' rqlink ' ] :
min_args , max_args = 2 , 2
2013-09-04 15:11:27 +02:00
elif cmd in [ ' create ' , ' c ' ] :
2013-12-13 13:48:45 +01:00
min_args , max_args = 1 , 2
2014-02-01 21:00:55 +01:00
elif cmd in [ ' list ' , ' cleanup_rings ' ] :
2014-01-31 10:29:44 +01:00
min_args , max_args = 0 , 0
2013-09-04 15:11:27 +02:00
else :
2014-02-10 19:59:45 +01:00
raise oscerr . WrongArgs ( ' 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
2014-01-31 10:29:44 +01:00
opts . apiurl = self . get_api_url ( )
opts . verbose = False
2013-09-04 15:11:27 +02:00
# check for the opts
staging_check_everything = False
if opts . everything :
staging_check_everything = True
2014-01-31 10:29:44 +01:00
self . _staging_parse_staging_prjs ( opts )
self . rings = self . _staging_get_rings ( opts )
2014-02-10 14:05:18 +01:00
api = StagingApi ( opts . apiurl )
2014-01-31 10:29:44 +01:00
2013-09-04 15:11:27 +02:00
# 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 )
2014-01-31 10:29:44 +01:00
elif cmd in [ ' freeze ' ] :
self . _staging_freeze_prjlink ( args [ 1 ] , opts )
2014-02-10 09:45:22 +01:00
elif cmd in [ ' rqlink ' ] :
2014-02-10 14:05:18 +01:00
api . sr_to_prj ( args [ 1 ] , args [ 2 ] )
2014-02-01 21:00:55 +01:00
elif cmd in [ ' cleanup_rings ' ] :
self . _staging_cleanup_rings ( opts )
2014-01-31 10:29:44 +01:00
elif cmd in [ ' accept ' , ' list ' ] :
self . letter_to_accept = None
if cmd == ' accept ' :
self . letter_to_accept = args [ 1 ]
# xpath query, using the -m, -r, -s options
where = " @by_group= ' factory-staging ' +and+@state= ' new ' "
url = makeurl ( opts . apiurl , [ ' search ' , ' request ' ] , " match=state/@name= ' review ' +and+review[ " + where + " ] " )
f = http_GET ( url )
root = ET . parse ( f ) . getroot ( )
for rq in root . findall ( ' request ' ) :
tprj = rq . find ( ' action/target ' ) . get ( ' project ' )
self . _staging_one_request ( rq , opts )
if self . letter_to_accept :
url = makeurl ( opts . apiurl , [ ' source ' , ' openSUSE:Factory:Staging: %s ' % self . letter_to_accept ] )
f = http_GET ( url )
root = ET . parse ( f ) . getroot ( )
print ET . tostring ( root )