1 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et
3 import os, config, string, urllib, re, rpm
4 from common import fileexists, noarchcachedir
5 from baseftptree import BasePkg, BaseFtpTree
6 from sign import is_signed
11 class SomeError(Exception):
16 print "","An Error occured!"
20 print "%d error(s) encountered... aborting" % errnum
34 def rm(file, test = False):
36 if not os.path.exists(file):
37 pinfo("TEST os.remove(%s): file doesn't exists" % file)
42 pinfo("os.remove(%s): %s" % (file, e))
45 def mv(src, dst, test = False):
47 fdst = dst + '/' + src.split('/')[-1]
49 if not os.path.exists(src):
50 pinfo("TEST os.rename(%s, %s): source doesn't exists" % (fsrc, fdst))
51 if not os.path.exists(dst):
52 pinfo("TEST destination doesn't exist: %s" % dst)
57 pinfo("os.rename(%s, %s): %s" % (fsrc, fdst, e))
61 def __init__(self, nvr, tree):
62 BasePkg.__init__(self, nvr, tree)
63 self.name = string.join(nvr.split('-')[:-2], '-')
64 self.version = nvr.split('-')[-2]
65 self.release = nvr.split('-')[-1]
66 self.marked4removal = False
67 self.marked4moving = False
68 self.marked4movingpool = []
72 def __cmp__(self, pkg):
73 if self.name > pkg.name:
75 elif self.name < pkg.name:
78 return rpm.labelCompare(('0', self.version, self.release),
79 ('0', pkg.version, pkg.release))
82 # unfortunately can't do new Pkg(NVR), and have no "tree" in this pkg context
83 # so this static function
84 def is_debuginfo(self, nvr):
86 returns true if NVR is debuginfo package and separate debuginfo is enabled
88 if not config.separate_debuginfo:
90 pkg = nvr.split('-')[:-2]
91 return pkg[-1] == 'debuginfo'
93 def mark4moving(self):
94 if not self.marked4moving:
95 # Only one pkg in this pool can be marked for moving
96 for pkg in self.marked4movingpool:
98 self.tree.marked4moving.append(self)
99 self.marked4moving=True
101 def unmark4moving(self):
102 if self.marked4moving:
103 self.tree.marked4moving.remove(self)
104 self.marked4moving=False
106 def mark4removal(self):
107 if not self.marked4removal:
108 self.tree.marked4removal.append(self)
109 self.marked4removal=True
111 def error(self, msg):
112 self.errors.append(msg)
114 perror('%s %s' % (self.nvr, msg))
116 def warning(self, msg):
117 self.warnings.append(msg)
119 pwarning('%s %s' % (self.nvr, msg))
121 def load(self, content=None):
122 BasePkg.load(self, content)
123 if self.info.has_key('move'):
127 f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
128 for bid in self.build.keys():
129 f.write("info:build:%s:requester:%s\ninfo:build:%s:requester_email:%s\n" % (bid, self.build[bid].requester, bid, self.build[bid].requester_email))
130 for key in self.info.keys():
131 f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':')))
132 for arch in self.files.keys():
133 for rpm in self.files[arch]:
134 f.write("file:%s:%s\n" % (arch, rpm))
136 def remove(self, test = False):
138 Remove package from ftp
140 for arch in self.files.keys():
141 for rpm in self.files[arch]:
142 if self.is_debuginfo(rpm):
143 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
145 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
147 if fileexists(noarchcachedir + rpm + '.filelist'):
148 rm(noarchcachedir + rpm + '.filelist', test)
149 if fileexists(noarchcachedir + rpm + '.reqlist'):
150 rm(noarchcachedir + rpm + '.reqlist', test)
151 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
155 Return rpm files related to this package
158 for arch, rpms in self.files.items():
160 if self.is_debuginfo(nvr):
161 files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
163 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
168 Return obsoletes for all packages in Pkg:
170 {'php-geshi': set(['geshi'])}
175 ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
176 fdno = os.open(pkg, os.O_RDONLY)
177 hdr = ts.hdrFromFdno(fdno)
182 for rpmfile in self.rpmfiles():
183 if not os.path.exists(rpmfile):
185 hdr = rpmhdr(rpmfile)
186 if not hdr[rpm.RPMTAG_OBSOLETES]:
189 name = hdr[rpm.RPMTAG_NAME]
190 if not name in obsoletes:
191 obsoletes[name] = set()
193 for tag in hdr[rpm.RPMTAG_OBSOLETES]:
194 obsoletes[name].add(tag)
198 def move(self, dsttree, test = False):
199 if dsttree.has_key(self.nvr):
201 for arch in self.files.keys():
202 if arch in dsttree[self.nvr].files.keys():
206 pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
207 for rpm in self.files[arch]:
208 if self.is_debuginfo(rpm):
209 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
211 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
214 dsttree[self.nvr].files[arch] = self.files[arch]
215 for rpm in self.files[arch]:
216 if self.is_debuginfo(rpm):
217 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
219 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
220 if not test and movedany:
221 for bid in self.build.keys():
222 dsttree[self.nvr].build[bid] = self.build[bid]
223 dsttree[self.nvr].writeinfo()
224 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
227 for arch in self.files.keys():
228 for rpm in self.files[arch]:
229 if self.is_debuginfo(rpm):
230 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
232 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
235 mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
237 class FtpTree(BaseFtpTree):
238 def __init__(self, tree, loadall=False):
239 BaseFtpTree.__init__(self, tree)
241 self.marked4removal = []
242 self.marked4moving = []
244 self.__loadpkgnames()
246 for pkgname in self.pkgnames:
247 self.loadedpkgs[pkgname] = Pkg(pkgname, self)
249 self.do_checkbuild = True
251 def __getitem__(self, key):
252 if self.loadedpkgs.has_key(key):
253 return self.loadedpkgs[key]
254 elif key in self.pkgnames:
256 self.loadedpkgs[key]=pkg
261 def has_key(self, key):
262 if key in self.pkgnames:
271 return self.loadedpkgs.values()
273 def checktree(self, dsttree):
274 self.__checkbuild(self.loadedpkgs.values())
275 self.__checkarchs(dsttree, self.loadedpkgs.values())
277 def testmove(self, dsttree, archivetree = None):
278 self.__checkbuild(self.marked4moving)
279 self.__checkarchs(dsttree, self.marked4moving)
281 self.__checksigns(dsttree, self.marked4moving, test = True)
282 self.__checkforobsoletes(dsttree, self.marked4moving, test = True)
284 self.__rmolderfromsrc(test = True)
285 self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
287 for pkg in self.marked4moving:
288 pkg.move(dsttree, test = True)
290 def movepkgs(self, dsttree, archivetree = None):
291 if self.do_checkbuild:
292 self.__checkbuild(self.marked4moving)
295 self.__checkarchs(dsttree, self.marked4moving)
298 self.__checksigns(dsttree, self.marked4moving)
301 self.__rmolderfromsrc()
302 self.__rmotherfromdst(dsttree, archivetree = archivetree)
304 for pkg in self.marked4moving:
308 if self.do_checkbuild:
309 self.__checkbuild(self.marked4moving)
312 for pkg in self.marked4moving:
313 files += pkg.rpmfiles()
316 def removepkgs(self):
317 if self.do_checkbuild:
318 self.__checkbuild(self.marked4removal)
320 for pkg in self.marked4removal:
323 def mark4removal(self, wannabepkgs):
324 self.__mark4something(wannabepkgs, Pkg.mark4removal)
326 def mark4moving(self, wannabepkgs):
327 self.__mark4something(wannabepkgs, Pkg.mark4moving)
330 # Internal functions below
332 def __loadpkgnames(self):
333 def checkfiletype(name):
334 if name[-13:]=='.src.rpm.info':
338 list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
339 self.pkgnames = map((lambda x: x[:-13]), list)
341 def __mark4something(self, wannabepkgs, markfunction):
342 def chopoffextension(pkg):
343 found = pkg.find('.src.rpm')
349 for wannabepkg in wannabepkgs:
350 pkgname = chopoffextension(wannabepkg)
351 if pkgname in self.pkgnames:
352 if not pkgname in self.loadedpkgs.keys():
353 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
354 markfunction(self.loadedpkgs[pkgname])
356 perror('%s not found in source tree' % pkgname)
359 def __checkbuild(self, marked):
361 Checks queue file if all arches are built
363 Reads config.builderqueue to grab the info
365 f = urllib.urlopen(config.builderqueue)
367 reid = re.compile(r'^.*id=(.*) pri.*$')
368 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
369 for i in re.findall(regb, f.read()):
371 id = reid.sub(r'\1', i)
374 requests[id] = requests[id] + i
378 for bid in pkg.build.keys():
379 if requests.has_key(bid) and not requests[bid].find('?') == -1:
380 pkg.error("(buildid %s) building not finished" % bid)
382 def __checkarchs(self, dsttree, marked):
384 Checks marked pkgs it is built on all archs.
387 if len(pkg.files.keys()) <= 1:
388 pkg.error('has only src.rpm built')
390 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
392 # check if we're not removing some archs
396 for somepkg in otherpkgnames:
397 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
398 for arch in curarchs:
399 if arch not in pkg.files.keys():
400 missingarchs.append(arch)
402 pkg.error('moving would remove archs: %s' % missingarchs)
404 # warn if a package isn't built for all archs
405 if (config.separate_noarch and 'noarch' in pkg.files.keys() and len(pkg.files.keys()) == 2):
407 elif len(pkg.files.keys()) != len(config.ftp_archs) + 1:
409 for arch in config.ftp_archs:
410 if arch not in pkg.files.keys():
411 missingarchs.append(arch)
412 pkg.warning('not built for archs: %s' % missingarchs)
414 def __rmolderfromsrc(self, test = False):
415 for pkg in self.marked4moving:
416 olderpkgnames = self.__find_older_pkgs(pkg)
417 for i in olderpkgnames:
418 Pkg(i, self).remove(test)
420 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
421 for pkg in self.marked4moving:
422 pkgnames = self.__find_other_pkgs(pkg, dsttree)
424 if archivetree == None:
425 Pkg(i, dsttree).remove(test)
427 Pkg(i, dsttree).move(archivetree, test = test)
429 # Used more than once filter functions
430 def __find_other_pkgs(self, pkg, tree):
431 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
432 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
433 def filter_other_pkgs(x):
434 if ziewre.match(x) and not x == pkg.nvr:
438 return filter(filter_other_pkgs, tree.pkgnames)
440 def __find_older_pkgs(self, pkg):
441 def filter_older_pkgs(x):
443 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
445 if rc == 1: # pkg > x
449 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
451 def __checksigns(self, tree, pkgs, test = False):
453 Checks if pkgs in tree are all signed.
455 in case of test = true, error flag is set for unsigned packages
457 if not tree.treename in config.signed_trees:
462 for file in pkg.rpmfiles():
463 if not is_signed(file):
469 pkg.warning('%d files not signed' % unsigned)
471 pkg.error('%d files not signed' % unsigned)
473 def __checkforobsoletes(self, tree, pkgs, test = False):
475 Checks queue file if package obsoletes something in destination tree and suggest for removal.
477 Only NAME tag is compared, i.e virtual packages do not get reported.
483 def findbyname(name):
485 return '-'.join(nvr.split('-')[:-2]) == name
486 return filter(x, tree.pkgnames)
489 obsoletes = pkg.obsoletes()
493 for pn, setlist in obsoletes.items():
497 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))