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 # returns true if package build is integer
96 To account Release tags with subver macros, we consider integer release
97 if it contains odd number of dots:
101 0.%{subver}.%{rel}, %{rel} = 1 -> 0.20010.1 -> True
102 0.%{subver}.%{rel}, %{rel} = 0.1 -> 0.20010.0.1 -> False
104 return self.release.count('.') % 2 == 0
106 def mark4moving(self):
107 if not self.marked4moving:
108 # Only one pkg in this pool can be marked for moving
109 for pkg in self.marked4movingpool:
111 self.tree.marked4moving.append(self)
112 self.marked4moving=True
114 def unmark4moving(self):
115 if self.marked4moving:
116 self.tree.marked4moving.remove(self)
117 self.marked4moving=False
119 def mark4removal(self):
120 if not self.marked4removal:
121 self.tree.marked4removal.append(self)
122 self.marked4removal=True
124 def error(self, msg):
125 self.errors.append(msg)
127 perror('%s %s' % (self.nvr, msg))
129 def warning(self, msg):
130 self.warnings.append(msg)
132 pwarning('%s %s' % (self.nvr, msg))
134 def load(self, content=None):
135 BasePkg.load(self, content)
136 if self.info.has_key('move'):
140 f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
141 for bid in self.build.keys():
142 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))
143 for key in self.info.keys():
144 f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':')))
145 for arch in self.files.keys():
146 for rpm in self.files[arch]:
147 f.write("file:%s:%s\n" % (arch, rpm))
149 def remove(self, test = False):
151 Remove package from ftp
153 for arch in self.files.keys():
154 for rpm in self.files[arch]:
155 if self.is_debuginfo(rpm):
156 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
158 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
160 if fileexists(noarchcachedir + rpm + '.filelist'):
161 rm(noarchcachedir + rpm + '.filelist', test)
162 if fileexists(noarchcachedir + rpm + '.reqlist'):
163 rm(noarchcachedir + rpm + '.reqlist', test)
164 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
166 def rpmfiles(self, debugfiles = True):
168 Return rpm files related to this package
171 for arch, rpms in self.files.items():
173 if self.is_debuginfo(nvr):
175 files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
177 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
182 Return obsoletes for all packages in Pkg:
184 {'php-geshi': set(['geshi'])}
189 ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
190 fdno = os.open(pkg, os.O_RDONLY)
191 hdr = ts.hdrFromFdno(fdno)
196 for rpmfile in self.rpmfiles():
197 if not os.path.exists(rpmfile):
199 hdr = rpmhdr(rpmfile)
200 if not hdr[rpm.RPMTAG_OBSOLETES]:
203 name = hdr[rpm.RPMTAG_NAME]
204 if not name in obsoletes:
205 obsoletes[name] = set()
207 for tag in hdr[rpm.RPMTAG_OBSOLETES]:
208 obsoletes[name].add(tag)
212 def move(self, dsttree, test = False):
213 if dsttree.has_key(self.nvr):
215 for arch in self.files.keys():
216 if arch in dsttree[self.nvr].files.keys():
220 pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
221 for rpm in self.files[arch]:
222 if self.il-page-warningus_debuginfo(rpm):
223 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
225 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
228 dsttree[self.nvr].files[arch] = self.files[arch]
229 for rpm in self.files[arch]:
230 if self.is_debuginfo(rpm):
231 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
233 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
234 if not test and movedany:
235 for bid in self.build.keys():
236 dsttree[self.nvr].build[bid] = self.build[bid]
237 dsttree[self.nvr].writeinfo()
238 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
241 for arch in self.files.keys():
242 for rpm in self.files[arch]:
243 if self.is_debuginfo(rpm):
244 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
246 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
249 mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
251 class FtpTree(BaseFtpTree):
252 def __init__(self, tree, loadall=False):
253 BaseFtpTree.__init__(self, tree)
255 self.marked4removal = []
256 self.marked4moving = []
258 self.__loadpkgnames()
260 for pkgname in self.pkgnames:
261 self.loadedpkgs[pkgname] = Pkg(pkgname, self)
263 self.do_checkbuild = True
265 def __getitem__(self, key):
266 if self.loadedpkgs.has_key(key):
267 return self.loadedpkgs[key]
268 elif key in self.pkgnames:
270 self.loadedpkgs[key]=pkg
275 def has_key(self, key):
276 if key in self.pkgnames:
285 return self.loadedpkgs.values()
287 def checktree(self, dsttree):
288 self.__checkbuild(self.loadedpkgs.values())
289 self.__checkarchs(dsttree, self.loadedpkgs.values())
291 def testmove(self, dsttree, archivetree = None):
292 self.__checkbuild(self.marked4moving)
293 self.__checkarchs(dsttree, self.marked4moving)
294 self.__checkduplicates(self.marked4moving)
296 self.__checksigns(dsttree, self.marked4moving, test = True)
297 self.__checkforobsoletes(dsttree, self.marked4moving, test = True)
298 self.__checkforrelease(dsttree, self.marked4moving, test = True)
300 self.__rmolderfromsrc(test = True)
301 self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
303 for pkg in self.marked4moving:
304 pkg.move(dsttree, test = True)
306 def movepkgs(self, dsttree, archivetree = None):
307 if self.do_checkbuild:
308 self.__checkbuild(self.marked4moving)
311 self.__checkarchs(dsttree, self.marked4moving)
314 self.__checksigns(dsttree, self.marked4moving)
317 self.__rmolderfromsrc()
318 self.__rmotherfromdst(dsttree, archivetree = archivetree)
320 for pkg in self.marked4moving:
324 if self.do_checkbuild:
325 self.__checkbuild(self.marked4moving)
328 for pkg in self.marked4moving:
329 files += pkg.rpmfiles()
332 def removepkgs(self):
333 if self.do_checkbuild:
334 self.__checkbuild(self.marked4removal)
336 for pkg in self.marked4removal:
339 def mark4removal(self, wannabepkgs):
340 self.__mark4something(wannabepkgs, Pkg.mark4removal)
342 def mark4moving(self, wannabepkgs):
343 self.__mark4something(wannabepkgs, Pkg.mark4moving)
346 # Internal functions below
347 def __arch_stringify(self, list):
349 # XXX: is dist line in any config?
352 ret.append(dist + '-' + arch)
355 def __loadpkgnames(self):
356 def checkfiletype(name):
357 if name[-13:]=='.src.rpm.info':
361 list = filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata'))
362 self.pkgnames = map((lambda x: x[:-13]), list)
364 def __mark4something(self, wannabepkgs, markfunction):
365 def chopoffextension(pkg):
366 found = pkg.find('.src.rpm')
372 for wannabepkg in wannabepkgs:
373 pkgname = chopoffextension(wannabepkg)
374 if pkgname in self.pkgnames:
375 if not pkgname in self.loadedpkgs.keys():
376 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
377 markfunction(self.loadedpkgs[pkgname])
379 perror('%s not found in source tree' % pkgname)
382 def __checkbuild(self, marked):
384 Checks queue file if all arches are built
386 Reads config.builderqueue to grab the info
388 f = urllib.urlopen(config.builderqueue)
390 reid = re.compile(r'^.*id=(.*) pri.*$')
391 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
392 for i in re.findall(regb, f.read()):
394 id = reid.sub(r'\1', i)
397 requests[id] = requests[id] + i
401 for bid in pkg.build.keys():
402 if requests.has_key(bid) and not requests[bid].find('?') == -1:
403 pkg.error("(buildid %s) building not finished" % bid)
405 def __checkarchs(self, dsttree, marked):
407 Checks marked pkgs it is built on all archs.
410 if len(pkg.files.keys()) <= 1:
411 pkg.error('has only src.rpm built')
413 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
415 # check if we're not removing some archs
419 for somepkg in otherpkgnames:
420 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
421 for arch in curarchs:
422 if arch not in pkg.files.keys():
423 missingarchs.append(arch)
425 pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs))
427 # warn if a package isn't built for all archs
428 if (config.separate_noarch and 'noarch' in pkg.files.keys() and len(pkg.files.keys()) == 2):
430 elif len(pkg.files.keys()) != len(config.ftp_archs) + 1:
432 for arch in config.ftp_archs:
433 if arch not in pkg.files.keys():
434 missingarchs.append(arch)
435 pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs))
437 def __checkduplicates(self, marked):
439 Checks if marked packages contain duplicate packages (with different versions)
442 olderpkgnames = self.__find_older_pkgs(pkg)
443 for i in olderpkgnames:
444 pkg.error('duplicate package: %s' % i)
446 def __rmolderfromsrc(self, test = False):
447 for pkg in self.marked4moving:
448 olderpkgnames = self.__find_older_pkgs(pkg)
449 for i in olderpkgnames:
450 Pkg(i, self).remove(test)
452 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
453 for pkg in self.marked4moving:
454 pkgnames = self.__find_other_pkgs(pkg, dsttree)
456 if archivetree == None:
457 Pkg(i, dsttree).remove(test)
459 Pkg(i, dsttree).move(archivetree, test = test)
461 # Used more than once filter functions
462 def __find_other_pkgs(self, pkg, tree):
463 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
464 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
465 def filter_other_pkgs(x):
466 if ziewre.match(x) and not x == pkg.nvr:
470 return filter(filter_other_pkgs, tree.pkgnames)
472 def __find_older_pkgs(self, pkg):
473 def filter_older_pkgs(x):
475 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
477 if rc == 1: # pkg > x
481 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
483 def __checksigns(self, tree, pkgs, test = False):
485 Checks if pkgs in tree are all signed.
487 in case of test = true, error flag is set for unsigned packages
489 if not tree.treename in config.signed_trees:
494 for file in pkg.rpmfiles():
495 if not is_signed(file):
501 pkg.warning('%d files not signed' % unsigned)
503 pkg.error('%d files not signed' % unsigned)
505 def __checkforobsoletes(self, tree, pkgs, test = False):
507 Checks queue file if package obsoletes something in destination tree and suggest for removal.
509 Only NAME tag is compared, i.e virtual packages do not get reported.
515 def findbyname(name):
517 return '-'.join(nvr.split('-')[:-2]) == name
518 return filter(x, tree.pkgnames)
521 obsoletes = pkg.obsoletes()
525 for pn, setlist in obsoletes.items():
529 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
531 def __checkforrelease(self, tree, pkgs, test = False):
533 Checks queue file if package release is non integer.
540 if not pkg.is_release():
541 pkg.warning('non-integer release: %s' % pkg.release)