#!/usr/bin/env python # Global Modules from subprocess import * import sys import re import os import portage from portage.dep import dep_getcpv import base64 import requests from tatt.gentooPackage import gentooPackage as gP import tatt.packageFinder as packageFinder from tatt.scriptwriter import writeusecombiscript as writeUSE from tatt.scriptwriter import writerdepscript as writeRdeps from tatt.scriptwriter import writesucessreportscript as writeSuccess from tatt.scriptwriter import writecommitscript as writeCommit from tatt.scriptwriter import writeCleanUpScript as writeCleanup from tatt.tattConfig import tattConfig as tattConfig from tatt.job import job as job from tatt.tool import get_repo_dir ##### Generate a global config obj, reading from ~/.tatt ##### config = tattConfig() session = requests.Session() session.params.update({'Bugzilla_api_key': config['bugzilla-key']}) ######### Main program starts here ############### ### USAGE and OPTIONS ### from optparse import OptionParser parser=OptionParser() parser.add_option("-d", "--depend", help="Determine stable rdeps", dest="depend", action="store_true", default = False) parser.add_option("-u", "--use", "--usecombis", help="Determine use flag combinations", dest="usecombi", action="store_true", default = False) parser.add_option("-f", "--file", help="Input File containing packages", dest="infile", action="store" ) parser.add_option("-j", "--jobname", help="name for the job, prefix of output files", dest="jobname", action="store") parser.add_option("-b", "--bug", help="do the full program for a given stable request bug", dest="bugnum", # type="int", # We could test this here, but in most cases the bugnumber # is actually used as a string, so we validate further down # and leave it as a string action="store") parser.add_option("-s", "--success", help="Comment that the program was successfully tested", dest="succbugnum", action="store") parser.add_option("-r", "--resolve", help="Resolve the given bugnumber, needs a message", dest="resolvenum", action="store") parser.add_option("-c", "--close", help="Resolve the given bugnumber with closing it, needs to be combined with -r", dest="close", action="store_true") parser.add_option("-k", "--keywording", help="search for keywording packages, needs to be combined with -f", dest="keywording", action="store_true", default = False) parser.add_option("-m", "--message", help="Message for bug resolution.", dest="resolvemessage", action="store") parser.add_option("-v", "--verbose", help="Print informative output.", dest="verbose", action="store_true", default = False) (options,args) = parser.parse_args() ## Messing with the configuration: # Save verbosity level config['verbose']=options.verbose # Normalize the template dir: config['template-dir']=os.path.abspath(config['template-dir'])+os.sep # If given, test if the bugnumber represents an int try: int(options.bugnum) except ValueError: print ("The bugnumber you gave with -b should be an integer.") sys.exit(1) except TypeError: # This occurs if bugnum is None, that is, -b was not given pass ## If safedir is set, check for the current directory if config['safedir'] != "": if os.getcwd().find(config['safedir']) == -1: # Safedir not found print ("Your safedir variable is set to '" + config['safedir'] + "',") print ("but you are in " + os.getcwd()) print ("Exiting.") sys.exit (1) ## -s and a bugnumber was given ? if options.succbugnum: print("Reporting success for bug number " + options.succbugnum) retcode = call(['bugz', 'modify', options.succbugnum, '-c', config['successmessage']]) if retcode == 0: print("Success!"); sys.exit (0) else: print("Failure commenting on Bugzilla") sys.exit(1) # get a job object to save things to myJob = job() ## If -f and a filename have been given: if options.infile: try: packfile=open(options.infile, 'r') except IOError: print("Given filename not found !") sys.exit(1) packraw = packfile.read() packfile.close() targetarch = config['arch'] if options.keywording: targetarch = '~' + targetarch myJob.type="keyword" else: myJob.type="stable" myJob.packageList = packageFinder.findPackages(packraw, targetarch, get_repo_dir(config['repodir'])) ## -b and a bugnumber was given ? if options.bugnum: print("Bugnumber: " + options.bugnum) myJob.bugnumber=options.bugnum params = {"id": options.bugnum} response = session.get(config["bugzilla-url"] + "/rest/bug", params=params).json() if "message" in response: print(response["message"]) sys.exit(1) if len(response["bugs"]) == 0: print("bug " + options.bugnum + " not found in bugzilla") sys.exit(1) response = response["bugs"][0] if "KEYWORDREQ" in response["keywords"] or response["component"] == "Keywording": # This is a keywording bug: print ("Keywording bug detected.") myJob.type="keyword" elif "STABLEREQ" in response["keywords"] or response["component"] == "Stabilization" or response["component"] == "Vulnerabilities": # Stablebug print ("Stabilization bug detected.") myJob.type="stable" else: print ("Could not detect bug's type, is the 'Keywords' field set?") sys.exit(1) if myJob.packageList==None: if response["cf_stabilisation_atoms"]: myJob.packageList = packageFinder.findPackages(response["cf_stabilisation_atoms"], config['arch'], get_repo_dir(config['repodir']), options.bugnum) if len(myJob.packageList) == 0 and ("KEYWORDREQ" in response["keywords"] or response["component"] == "Keywording"): myJob.packageList = packageFinder.findPackages(response["cf_stabilisation_atoms"], config['arch'], get_repo_dir(config['repodir']), options.bugnum) else: response = session.get(config["bugzilla-url"] + "/rest/bug/{}/attachment".format(options.bugnum), params=params).json()["bugs"][str(options.bugnum)] for attachment in response: if attachment["is_obsolete"] == 1: continue for flag in attachment['flags']: if flag["name"] == "stabilization-list" and flag["status"] == '+': myJob.packageList = packageFinder.findPackages(base64.b64decode(attachment["data"]).decode("utf8"), config['arch'], get_repo_dir(config['repodir']), options.bugnum) # joint code for -f and -b ########################## if myJob.packageList is not None and len(myJob.packageList) > 0: ## Assigning jobname if options.jobname: myJob.name = options.jobname elif options.infile: myJob.name = options.infile elif options.bugnum: myJob.name = myJob.packageList[0].packageName() + '-' + options.bugnum else: myJob.name = myJob.packageList[0].packageName() print ("Jobname: " + myJob.name) ## Determine jobtype port = portage.db[portage.root]["porttree"].dbapi filteredPackages = [] # for keywording bugs the packages that already have the keyword still need # to be unmasked so they can be used by the other packages that still need work kwPackages = [] if config['arch']: targetarch = config['arch'] if myJob.type == "keyword": targetarch = '~' + targetarch for p in myJob.packageList: print("Found the following package atom : " + p.packageString()) # check if the package already has the needed keywords kw = port.aux_get(dep_getcpv(p.packageString()), ["KEYWORDS"]) if len(kw) > 0: kwl = kw[0].split() try: kwl.index(config['arch']) # the list of keywords in portage already contains the arch # as stable, skip this package print("\talready stable for " + config['arch']) continue except ValueError: pass try: kwl.index(targetarch) # the list of keywords in portage already contains the target keyword, # skip this package from building, but remember it for unmasking print("\talready keyworded as " + targetarch) kwPackages.append(p) continue except ValueError: filteredPackages.append(p) if len(filteredPackages) == 0: print("\nno packages left") sys.exit(0) myJob.packageList = filteredPackages # Unmasking: unmaskname=config['unmaskdir'] if os.path.exists(unmaskname) and not os.path.isdir(unmaskname): print ("unmaskdir '", unmaskname, "' exists and is no directory") sys.exit(1) elif not os.path.exists(unmaskname): os.mkdir(unmaskname, 0o755) unmaskname=unmaskname+"/tatt_"+myJob.name try: unmaskfile=open(unmaskname, 'r+') except IOError: try: unmaskfile=open(unmaskname, 'w') unmaskfile.write(" ") unmaskfile.close() except IOError: # If we can't write to the file, then it should be configured differently print (" ".join(["Can not write to ",unmaskname])) print ("Maybe you don't have permission or the location is invalid.") print (" ".join(["Is",config['unmaskdir'],"a writeable directory?"])) print ("Probably you want to configure a different unmaskfile") print ("in your ~/.tatt. Exiting") sys.exit(1) unmaskfile=open(unmaskname, 'r+') unmaskfileContent = unmaskfile.read() for p in myJob.packageList: # Test if unmaskfile already contains the atom if re.search(re.escape(p.packageString()), unmaskfileContent): print (p.packageString() + " already in "+unmaskname) else: unmaskfile.write(p.packageString()) if myJob.type=="stable": pass elif myJob.type=="keyword": unmaskfile.write(" ** ") else: print ("Uh Oh, no job.type? Tell tomka@gentoo.org to fix this!") unmaskfile.write(" # Job " + myJob.name + "\n") print ("Unmasked " + p.packageString()+ " in "+unmaskname) # now write the remaining packages for keywording for p in kwPackages: # Test if unmaskfile already contains the atom if re.search(re.escape(p.packageString()), unmaskfileContent): print (p.packageString() + " already in "+unmaskname) else: unmaskfile.write(p.packageString() + " # Job " + myJob.name + "\n") print ("Unmasked " + p.packageString() + " in " + unmaskname) unmaskfile.close() ## Write the scripts writeUSE(myJob, config) writeRdeps(myJob, config) writeCleanup (myJob, config, unmaskname) ## Successscript can only be written if we have a bugnumber if myJob.bugnumber: writeSuccess(myJob, config) writeCommit(myJob, config) sys.exit (0) # Code for resolving bugs (-r and -m) ##################################### if options.resolvenum: if not options.resolvemessage: print("Please call with a message per -m") sys.exit (1) print("Resolving bug number " + options.resolvenum) calllist = ['bugz', 'modify', options.resolvenum, '-c', options.resolvemessage, '--remove-cc', config['arch']+"@gentoo.org"] if options.close: calllist = calllist + ['--fixed'] retcode = call(calllist) if retcode == 0: print("Success!"); sys.exit (0) else: print("Failure accessing bugzilla.") sys.exit(1) ## If we arrive here then a package atom should be given try: myJob.packageList=[gP(args[0])] myJob.name=myJob.packageList[0].packageName() except IndexError: print("Please call with complete package atom (including version) as argument.") sys.exit (1) if options.depend: writeRdeps(myJob, config) if options.usecombi: writeUSE(myJob, config) ## That's all folks ##