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
331 def __arch_stringify(self, list):
333 # XXX: is dist line in any config?
336 ret.append(dist + '-' + arch)
339 def __loadpkgnames(self):
340 def checkfiletype(name):
341 if name[-13:]=='.src.rpm.info':
345 list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
346 self.pkgnames = map((lambda x: x[:-13]), list)
348 def __mark4something(self, wannabepkgs, markfunction):
349 def chopoffextension(pkg):
350 found = pkg.find('.src.rpm')
356 for wannabepkg in wannabepkgs:
357 pkgname = chopoffextension(wannabepkg)
358 if pkgname in self.pkgnames:
359 if not pkgname in self.loadedpkgs.keys():
360 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
361 markfunction(self.loadedpkgs[pkgname])
363 perror('%s not found in source tree' % pkgname)
366 def __checkbuild(self, marked):
368 Checks queue file if all arches are built
370 Reads config.builderqueue to grab the info
372 f = urllib.urlopen(config.builderqueue)
374 reid = re.compile(r'^.*id=(.*) pri.*$')
375 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
376 for i in re.findall(regb, f.read()):
378 id = reid.sub(r'\1', i)
381 requests[id] = requests[id] + i
385 for bid in pkg.build.keys():
386 if requests.has_key(bid) and not requests[bid].find('?') == -1:
387 pkg.error("(buildid %s) building not finished" % bid)
389 def __checkarchs(self, dsttree, marked):
391 Checks marked pkgs it is built on all archs.
394 if len(pkg.files.keys()) <= 1:
395 pkg.error('has only src.rpm built')
397 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
399 # check if we're not removing some archs
403 for somepkg in otherpkgnames:
404 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
405 for arch in curarchs:
406 if arch not in pkg.files.keys():
407 missingarchs.append(arch)
409 pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs))
411 # warn if a package isn't built for all archs
412 if (config.separate_noarch and 'noarch' in pkg.files.keys() and len(pkg.files.keys()) == 2):
414 elif len(pkg.files.keys()) != len(config.ftp_archs) + 1:
416 for arch in config.ftp_archs:
417 if arch not in pkg.files.keys():
418 missingarchs.append(arch)
419 pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs))
421 def __rmolderfromsrc(self, test = False):
422 for pkg in self.marked4moving:
423 olderpkgnames = self.__find_older_pkgs(pkg)
424 for i in olderpkgnames:
425 Pkg(i, self).remove(test)
427 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
428 for pkg in self.marked4moving:
429 pkgnames = self.__find_other_pkgs(pkg, dsttree)
431 if archivetree == None:
432 Pkg(i, dsttree).remove(test)
434 Pkg(i, dsttree).move(archivetree, test = test)
436 # Used more than once filter functions
437 def __find_other_pkgs(self, pkg, tree):
438 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
439 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
440 def filter_other_pkgs(x):
441 if ziewre.match(x) and not x == pkg.nvr:
445 return filter(filter_other_pkgs, tree.pkgnames)
447 def __find_older_pkgs(self, pkg):
448 def filter_older_pkgs(x):
450 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
452 if rc == 1: # pkg > x
456 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
458 def __checksigns(self, tree, pkgs, test = False):
460 Checks if pkgs in tree are all signed.
462 in case of test = true, error flag is set for unsigned packages
464 if not tree.treename in config.signed_trees:
469 for file in pkg.rpmfiles():
470 if not is_signed(file):
476 pkg.warning('%d files not signed' % unsigned)
478 pkg.error('%d files not signed' % unsigned)
480 def __checkforobsoletes(self, tree, pkgs, test = False):
482 Checks queue file if package obsoletes something in destination tree and suggest for removal.
484 Only NAME tag is compared, i.e virtual packages do not get reported.
490 def findbyname(name):
492 return '-'.join(nvr.split('-')[:-2]) == name
493 return filter(x, tree.pkgnames)
496 obsoletes = pkg.obsoletes()
500 for pn, setlist in obsoletes.items():
504 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))