1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
|
#!/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)
## -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)
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 ##
|