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 # count elements by splitting by dot
105 parts = self.release.split('.')
106 return len(parts) % 2 == 1
108 def mark4moving(self):
109 if not self.marked4moving:
110 # Only one pkg in this pool can be marked for moving
111 for pkg in self.marked4movingpool:
113 self.tree.marked4moving.append(self)
114 self.marked4moving=True
116 def unmark4moving(self):
117 if self.marked4moving:
118 self.tree.marked4moving.remove(self)
119 self.marked4moving=False
121 def mark4removal(self):
122 if not self.marked4removal:
123 self.tree.marked4removal.append(self)
124 self.marked4removal=True
126 def error(self, msg):
127 self.errors.append(msg)
129 perror('%s %s' % (self.nvr, msg))
131 def warning(self, msg):
132 self.warnings.append(msg)
134 pwarning('%s %s' % (self.nvr, msg))
136 def load(self, content=None):
137 BasePkg.load(self, content)
138 if self.info.has_key('move'):
142 f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
143 for bid in self.build.keys():
144 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))
145 for key in self.info.keys():
146 f.write("info:%s:%s\n" % (key, string.join(self.info[key], ':')))
147 for arch in self.files.keys():
148 for rpm in self.files[arch]:
149 f.write("file:%s:%s\n" % (arch, rpm))
151 def remove(self, test = False):
153 Remove package from ftp
155 for arch in self.files.keys():
156 for rpm in self.files[arch]:
157 if self.is_debuginfo(rpm):
158 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
160 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
162 if fileexists(noarchcachedir + rpm + '.filelist'):
163 rm(noarchcachedir + rpm + '.filelist', test)
164 if fileexists(noarchcachedir + rpm + '.reqlist'):
165 rm(noarchcachedir + rpm + '.reqlist', test)
166 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
170 Return rpm files related to this package
173 for arch, rpms in self.files.items():
175 if self.is_debuginfo(nvr):
176 files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
178 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
183 Return obsoletes for all packages in Pkg:
185 {'php-geshi': set(['geshi'])}
190 ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
191 fdno = os.open(pkg, os.O_RDONLY)
192 hdr = ts.hdrFromFdno(fdno)
197 for rpmfile in self.rpmfiles():
198 if not os.path.exists(rpmfile):
200 hdr = rpmhdr(rpmfile)
201 if not hdr[rpm.RPMTAG_OBSOLETES]:
204 name = hdr[rpm.RPMTAG_NAME]
205 if not name in obsoletes:
206 obsoletes[name] = set()
208 for tag in hdr[rpm.RPMTAG_OBSOLETES]:
209 obsoletes[name].add(tag)
213 def move(self, dsttree, test = False):
214 if dsttree.has_key(self.nvr):
216 for arch in self.files.keys():
217 if arch in dsttree[self.nvr].files.keys():
221 pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
222 for rpm in self.files[arch]:
223 if self.is_debuginfo(rpm):
224 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
226 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
229 dsttree[self.nvr].files[arch] = self.files[arch]
230 for rpm in self.files[arch]:
231 if self.is_debuginfo(rpm):
232 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
234 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
235 if not test and movedany:
236 for bid in self.build.keys():
237 dsttree[self.nvr].build[bid] = self.build[bid]
238 dsttree[self.nvr].writeinfo()
239 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
242 for arch in self.files.keys():
243 for rpm in self.files[arch]:
244 if self.is_debuginfo(rpm):
245 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
247 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
250 mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
252 class FtpTree(BaseFtpTree):
253 def __init__(self, tree, loadall=False):
254 BaseFtpTree.__init__(self, tree)
256 self.marked4removal = []
257 self.marked4moving = []
259 self.__loadpkgnames()
261 for pkgname in self.pkgnames:
262 self.loadedpkgs[pkgname] = Pkg(pkgname, self)
264 self.do_checkbuild = True
266 def __getitem__(self, key):
267 if self.loadedpkgs.has_key(key):
268 return self.loadedpkgs[key]
269 elif key in self.pkgnames:
271 self.loadedpkgs[key]=pkg
276 def has_key(self, key):
277 if key in self.pkgnames:
286 return self.loadedpkgs.values()
288 def checktree(self, dsttree):
289 self.__checkbuild(self.loadedpkgs.values())
290 self.__checkarchs(dsttree, self.loadedpkgs.values())
292 def testmove(self, dsttree, archivetree = None):
293 self.__checkbuild(self.marked4moving)
294 self.__checkarchs(dsttree, 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 __rmolderfromsrc(self, test = False):
438 for pkg in self.marked4moving:
439 olderpkgnames = self.__find_older_pkgs(pkg)
440 for i in olderpkgnames:
441 Pkg(i, self).remove(test)
443 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
444 for pkg in self.marked4moving:
445 pkgnames = self.__find_other_pkgs(pkg, dsttree)
447 if archivetree == None:
448 Pkg(i, dsttree).remove(test)
450 Pkg(i, dsttree).move(archivetree, test = test)
452 # Used more than once filter functions
453 def __find_other_pkgs(self, pkg, tree):
454 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
455 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
456 def filter_other_pkgs(x):
457 if ziewre.match(x) and not x == pkg.nvr:
461 return filter(filter_other_pkgs, tree.pkgnames)
463 def __find_older_pkgs(self, pkg):
464 def filter_older_pkgs(x):
466 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
468 if rc == 1: # pkg > x
472 return filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self))
474 def __checksigns(self, tree, pkgs, test = False):
476 Checks if pkgs in tree are all signed.
478 in case of test = true, error flag is set for unsigned packages
480 if not tree.treename in config.signed_trees:
485 for file in pkg.rpmfiles():
486 if not is_signed(file):
492 pkg.warning('%d files not signed' % unsigned)
494 pkg.error('%d files not signed' % unsigned)
496 def __checkforobsoletes(self, tree, pkgs, test = False):
498 Checks queue file if package obsoletes something in destination tree and suggest for removal.
500 Only NAME tag is compared, i.e virtual packages do not get reported.
506 def findbyname(name):
508 return '-'.join(nvr.split('-')[:-2]) == name
509 return filter(x, tree.pkgnames)
512 obsoletes = pkg.obsoletes()
516 for pn, setlist in obsoletes.items():
520 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
522 def __checkforrelease(self, tree, pkgs, test = False):
524 Checks queue file if package release is non integer.
531 if not pkg.is_release():
532 pkg.warning('non-integer release: %s' % pkg.release)