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 is_sourcefile(self, file):
95 returns true if file is source package
97 return file[-8:] == '.src.rpm'
99 # returns true if package build is integer
100 def is_release(self):
102 To account Release tags with subver macros, we consider integer release
103 if it contains odd number of dots:
107 0.%{subver}.%{rel}, %{rel} = 1 -> 0.20010.1 -> True
108 0.%{subver}.%{rel}, %{rel} = 0.1 -> 0.20010.0.1 -> False
110 return self.release.count('.') % 2 == 0
112 def mark4moving(self):
113 if not self.marked4moving:
114 # Only one pkg in this pool can be marked for moving
115 for pkg in self.marked4movingpool:
117 self.tree.marked4moving.append(self)
118 self.marked4moving=True
120 def unmark4moving(self):
121 if self.marked4moving:
122 self.tree.marked4moving.remove(self)
123 self.marked4moving=False
125 def mark4removal(self):
126 if not self.marked4removal:
127 self.tree.marked4removal.append(self)
128 self.marked4removal=True
130 def error(self, msg):
131 self.errors.append(msg)
133 perror('%s %s' % (self.nvr, msg))
135 def warning(self, msg):
136 self.warnings.append(msg)
138 pwarning('%s %s' % (self.nvr, msg))
140 def load(self, content=None):
141 BasePkg.load(self, content)
142 if self.info.has_key('move'):
146 f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
147 for bid in self.build.keys():
148 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))
149 for key in self.info.keys():
150 f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':')))
151 for arch in self.files.keys():
152 for rpm in self.files[arch]:
153 f.write("file:%s:%s\n" % (arch, rpm))
155 def remove(self, test = False):
157 Remove package from ftp
159 for arch in self.files.keys():
160 for rpm in self.files[arch]:
161 if self.is_debuginfo(rpm):
162 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
164 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
166 if fileexists(noarchcachedir + rpm + '.filelist'):
167 rm(noarchcachedir + rpm + '.filelist', test)
168 if fileexists(noarchcachedir + rpm + '.reqlist'):
169 rm(noarchcachedir + rpm + '.reqlist', test)
170 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
172 def rpmfiles(self, debugfiles = True, sourcefiles = True):
174 Return rpm files related to this package
177 for arch, rpms in self.files.items():
179 if self.is_debuginfo(nvr):
181 files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
183 if self.is_sourcefile(nvr):
185 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
187 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
192 Return obsoletes for all packages in Pkg:
194 {'php-geshi': set(['geshi'])}
199 fdno = os.open(pkg, os.O_RDONLY)
200 hdr = ts.hdrFromFdno(fdno)
205 for rpmfile in self.rpmfiles():
206 if not os.path.exists(rpmfile):
208 hdr = rpmhdr(rpmfile)
209 if not hdr[rpm.RPMTAG_OBSOLETES]:
212 name = hdr[rpm.RPMTAG_NAME]
213 if not name in obsoletes:
214 obsoletes[name] = set()
216 for tag in hdr[rpm.RPMTAG_OBSOLETES]:
217 obsoletes[name].add(tag)
221 def move(self, dsttree, test = False):
222 if dsttree.has_key(self.nvr):
224 for arch in self.files.keys():
225 if arch in dsttree[self.nvr].files.keys():
229 pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
230 for rpm in self.files[arch]:
231 if self.is_debuginfo(rpm):
232 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
234 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
237 dsttree[self.nvr].files[arch] = self.files[arch]
238 for rpm in self.files[arch]:
239 if self.is_debuginfo(rpm):
240 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
242 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
243 if not test and movedany:
244 for bid in self.build.keys():
245 dsttree[self.nvr].build[bid] = self.build[bid]
246 dsttree[self.nvr].writeinfo()
247 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
250 for arch in self.files.keys():
251 for rpm in self.files[arch]:
252 if self.is_debuginfo(rpm):
253 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
255 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
258 mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
260 class FtpTree(BaseFtpTree):
261 def __init__(self, tree, loadall=False):
262 BaseFtpTree.__init__(self, tree)
264 self.marked4removal = []
265 self.marked4moving = []
267 self.__loadpkgnames()
269 for pkgname in self.pkgnames:
270 self.loadedpkgs[pkgname] = Pkg(pkgname, self)
272 self.do_checkbuild = True
274 def __getitem__(self, key):
275 if self.loadedpkgs.has_key(key):
276 return self.loadedpkgs[key]
277 elif key in self.pkgnames:
279 self.loadedpkgs[key]=pkg
284 def has_key(self, key):
285 if key in self.pkgnames:
294 return self.loadedpkgs.values()
296 def checktree(self, dsttree):
297 self.__checkbuild(self.loadedpkgs.values())
298 self.__checkarchs(dsttree, self.loadedpkgs.values())
300 def testmove(self, dsttree, archivetree = None):
301 self.__checkbuild(self.marked4moving)
302 self.__checkarchs(dsttree, self.marked4moving)
303 self.__checkduplicates(self.marked4moving)
305 self.__checksigns(dsttree, self.marked4moving, test = True)
306 self.__checkforobsoletes(dsttree, self.marked4moving, test = True)
307 self.__checkforrelease(dsttree, self.marked4moving, test = True)
309 if not self.treename.count("archive"):
310 self.__rmolderfromsrc(test = True)
312 self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
314 for pkg in self.marked4moving:
315 pkg.move(dsttree, test = True)
317 def movepkgs(self, dsttree, archivetree = None):
318 if self.do_checkbuild:
319 self.__checkbuild(self.marked4moving)
322 self.__checkarchs(dsttree, self.marked4moving)
325 self.__checksigns(dsttree, self.marked4moving)
328 if not self.treename.count("archive"):
329 self.__rmolderfromsrc()
331 self.__rmotherfromdst(dsttree, archivetree = archivetree)
333 for pkg in self.marked4moving:
336 def rpmfiles(self, debugfiles = True, sourcefiles = True):
337 if self.do_checkbuild:
338 self.__checkbuild(self.marked4moving)
341 for pkg in self.marked4moving:
342 files += pkg.rpmfiles(debugfiles = debugfiles, sourcefiles = sourcefiles)
345 def removepkgs(self):
346 if self.do_checkbuild:
347 self.__checkbuild(self.marked4removal)
349 for pkg in self.marked4removal:
352 def mark4removal(self, wannabepkgs):
353 self.__mark4something(wannabepkgs, Pkg.mark4removal)
355 def mark4moving(self, wannabepkgs):
356 self.__mark4something(wannabepkgs, Pkg.mark4moving)
359 # Internal functions below
360 def __arch_stringify(self, list):
362 dist = config.ftp_dist;
364 ret.append(dist + '-' + arch)
367 def __loadpkgnames(self):
368 def checkfiletype(name):
369 if name[-13:]=='.src.rpm.info':
373 list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
374 self.pkgnames = map((lambda x: x[:-13]), list)
376 def __mark4something(self, wannabepkgs, markfunction):
377 def chopoffextension(pkg):
378 found = pkg.find('.src.rpm')
384 for wannabepkg in wannabepkgs:
385 pkgname = chopoffextension(wannabepkg)
386 if pkgname in self.pkgnames:
387 if not pkgname in self.loadedpkgs.keys():
388 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
389 markfunction(self.loadedpkgs[pkgname])
391 perror('%s not found in source tree' % pkgname)
394 def __checkbuild(self, marked):
396 Checks queue file if all arches are built
398 Reads config.builderqueue to grab the info
400 f = urllib.urlopen(config.builderqueue)
402 reid = re.compile(r'^.*id=(.*) pri.*$')
403 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
404 for i in re.findall(regb, f.read()):
406 id = reid.sub(r'\1', i)
409 requests[id] = requests[id] + i
413 for bid in pkg.build.keys():
414 if requests.has_key(bid) and not requests[bid].find('?') == -1:
415 pkg.error("(buildid %s) building not finished" % bid)
417 def __checkarchs(self, dsttree, marked):
419 Checks marked pkgs it is built on all archs.
422 if len(pkg.files.keys()) <= 1:
423 pkg.error('has only src.rpm built')
425 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
427 # check if we're not removing some archs
431 for somepkg in otherpkgnames:
432 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
433 for arch in curarchs:
434 if arch not in pkg.files.keys():
435 missingarchs.append(arch)
437 pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs))
439 # warn if a package isn't built for all archs
440 if (config.separate_noarch and 'noarch' in pkg.files.keys() and len(pkg.files.keys()) == 2):
442 elif len(pkg.files.keys()) != len(config.ftp_archs) + 1:
444 for arch in config.ftp_archs:
445 if arch not in pkg.files.keys():
446 missingarchs.append(arch)
447 pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs))
449 def __checkduplicates(self, marked):
451 Checks if marked packages contain duplicate packages (with different versions)
454 olderpkgnames = self.__find_older_pkgs(pkg)
455 for i in olderpkgnames:
456 markednames = [str(x) for x in marked]
458 pkg.error('duplicate package: %s' % i)
460 def __rmolderfromsrc(self, test = False):
461 for pkg in self.marked4moving:
462 olderpkgnames = self.__find_older_pkgs(pkg)
463 for i in olderpkgnames:
464 Pkg(i, self).remove(test)
466 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
467 for pkg in self.marked4moving:
468 pkgnames = self.__find_other_pkgs(pkg, dsttree)
470 if archivetree == None:
471 Pkg(i, dsttree).remove(test)
473 Pkg(i, dsttree).move(archivetree, test = test)
475 # Used more than once filter functions
476 def __find_other_pkgs(self, pkg, tree):
477 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
478 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
479 def filter_other_pkgs(x):
480 if ziewre.match(x) and not x == pkg.nvr:
484 return filter(filter_other_pkgs, tree.pkgnames)
486 def __find_older_pkgs(self, pkg):
487 def filter_older_pkgs(x):
489 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
491 if rc == 1: # pkg > x
495 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
497 def __checksigns(self, tree, pkgs, test = False):
499 Checks if pkgs in tree are all signed.
501 in case of test = true, error flag is set for unsigned packages
503 if not tree.treename in config.signed_trees:
508 for file in pkg.rpmfiles():
509 if not is_signed(file):
515 pkg.warning('%d files not signed' % unsigned)
517 pkg.error('%d files not signed' % unsigned)
519 def __checkforobsoletes(self, tree, pkgs, test = False):
521 Checks queue file if package obsoletes something in destination tree and suggest for removal.
523 Only NAME tag is compared, i.e virtual packages do not get reported.
529 def findbyname(name):
531 return '-'.join(nvr.split('-')[:-2]) == name
532 return filter(x, tree.pkgnames)
535 obsoletes = pkg.obsoletes()
539 for pn, setlist in obsoletes.items():
543 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
545 def __checkforrelease(self, tree, pkgs, test = False):
547 Checks queue file if package release is non integer.
554 if not pkg.is_release():
555 pkg.warning('non-integer release: %s' % pkg.release)