1 # vi: encoding=utf-8 ts=8 sts=4 sw=4 et
3 from __future__ import print_function
9 import urllib.request as urlmess
11 import urllib as urlmess
15 from common import fileexists, noarchcachedir
16 from baseftptree import BasePkg, BaseFtpTree
17 from sign import is_signed
22 class SomeError(Exception):
27 return "An Error occured!"
31 print("%d error(s) encountered... aborting" % errnum)
45 def rm(file, test = False):
47 if not os.path.exists(file):
48 pinfo("TEST os.remove(%s): file doesn't exists" % file)
53 pinfo("os.remove(%s): %s" % (file, e))
56 def mv(src, dst, test = False):
58 fdst = dst + '/' + src.split('/')[-1]
60 if not os.path.exists(fsrc):
61 pinfo("TEST os.rename(%s, %s): source doesn't exists" % (fsrc, fdst))
62 if not os.path.exists(dst):
63 pinfo("TEST destination doesn't exist: %s" % dst)
68 pinfo("os.rename(%s, %s): %s" % (fsrc, fdst, e))
72 def __init__(self, nvr, tree):
73 BasePkg.__init__(self, nvr, tree)
74 self.name = '-'.join(nvr.split('-')[:-2])
75 self.version = nvr.split('-')[-2]
76 self.release = nvr.split('-')[-1]
77 self.marked4removal = False
78 self.marked4moving = False
79 self.marked4movingpool = []
83 def __cmp__(self, pkg):
84 if self.name > pkg.name:
86 elif self.name < pkg.name:
89 return rpm.labelCompare(('0', self.version, self.release),
90 ('0', pkg.version, pkg.release))
93 # unfortunately can't do new Pkg(NVR), and have no "tree" in this pkg context
94 # so this static function
95 def is_debuginfo(self, nvr):
97 returns true if NVR is debuginfo package and separate debuginfo is enabled
99 if not config.separate_debuginfo:
101 pkg = nvr.split('-')[:-2]
102 return pkg[-1] == 'debuginfo' or pkg[-1] == 'debugsource'
104 def is_sourcefile(self, file):
106 returns true if file is source package
108 return file[-8:] == '.src.rpm'
110 # returns true if package build is integer
111 def is_release(self):
113 To account Release tags with subver macros, we consider integer release
114 if it contains odd number of dots:
118 0.%{subver}.%{rel}, %{rel} = 1 -> 0.20010.1 -> True
119 0.%{subver}.%{rel}, %{rel} = 0.1 -> 0.20010.0.1 -> False
121 return self.release.count('.') % 2 == 0
123 def mark4moving(self):
124 if not self.marked4moving:
125 # Only one pkg in this pool can be marked for moving
126 for pkg in self.marked4movingpool:
128 self.tree.marked4moving.append(self)
129 self.marked4moving=True
131 def unmark4moving(self):
132 if self.marked4moving:
133 self.tree.marked4moving.remove(self)
134 self.marked4moving=False
136 def mark4removal(self):
137 if not self.marked4removal:
138 self.tree.marked4removal.append(self)
139 self.marked4removal=True
141 def error(self, msg):
142 self.errors.append(msg)
144 perror('%s %s' % (self.nvr, msg))
146 def warning(self, msg):
147 self.warnings.append(msg)
149 pwarning('%s %s' % (self.nvr, msg))
151 def load(self, content=None):
152 BasePkg.load(self, content)
153 if 'move' in self.info:
157 f = open(self.tree.basedir+'/SRPMS/.metadata/'+self.nvr+'.src.rpm.info', 'w')
158 for bid in self.build.keys():
159 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))
160 for key in self.info.keys():
161 f.write("info:%s:%s\n" % (key, ':'.join(self.info[key])))
162 for arch in self.files.keys():
163 for rpm in self.files[arch]:
164 f.write("file:%s:%s\n" % (arch, rpm))
166 def remove(self, test = False):
168 Remove package from ftp
170 for arch in self.files.keys():
171 for rpm in self.files[arch]:
172 if self.is_debuginfo(rpm):
173 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
175 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
177 if fileexists(noarchcachedir + rpm + '.filelist'):
178 rm(noarchcachedir + rpm + '.filelist', test)
179 if fileexists(noarchcachedir + rpm + '.reqlist'):
180 rm(noarchcachedir + rpm + '.reqlist', test)
181 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
183 def rpmfiles(self, debugfiles = True, sourcefiles = True):
185 Return rpm files related to this package
188 for arch, rpms in self.files.items():
190 if self.is_debuginfo(nvr):
192 files.append(self.tree.basedir + '/' + arch + '/debuginfo/' + nvr)
194 if self.is_sourcefile(nvr):
196 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
198 files.append(self.tree.basedir + '/' + arch + '/RPMS/' + nvr)
203 Return obsoletes for all packages in Pkg:
205 {'php-geshi': set(['geshi'])}
210 ts.setVSFlags(rpm.RPMVSF_NODSAHEADER)
211 fdno = os.open(pkg, os.O_RDONLY)
212 hdr = ts.hdrFromFdno(fdno)
217 for rpmfile in self.rpmfiles():
218 if not os.path.exists(rpmfile):
220 hdr = rpmhdr(rpmfile)
221 if not hdr[rpm.RPMTAG_OBSOLETES]:
224 name = hdr[rpm.RPMTAG_NAME]
225 if not name in obsoletes:
226 obsoletes[name] = set()
228 for tag in hdr[rpm.RPMTAG_OBSOLETES]:
229 obsoletes[name].add(tag)
233 def move(self, dsttree, test = False):
234 if dsttree.has_key(self.nvr):
236 for arch in self.files.keys():
237 if arch in dsttree[self.nvr].files.keys():
241 pinfo("%sArch %s for %s is already present in dest tree; removing from srctree" % (msg, arch, self.nvr))
242 for rpm in self.files[arch]:
243 if self.is_debuginfo(rpm):
244 rm(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, test)
246 rm(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, test)
249 dsttree[self.nvr].files[arch] = self.files[arch]
250 for rpm in self.files[arch]:
251 if self.is_debuginfo(rpm):
252 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
254 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
255 if not test and movedany:
256 for bid in self.build.keys():
257 dsttree[self.nvr].build[bid] = self.build[bid]
258 dsttree[self.nvr].writeinfo()
259 rm(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', test)
262 for arch in self.files.keys():
263 for rpm in self.files[arch]:
264 if self.is_debuginfo(rpm):
265 mv(self.tree.basedir + '/' + arch + '/debuginfo/' + rpm, dsttree.basedir + '/' + arch + '/debuginfo/', test)
267 mv(self.tree.basedir + '/' + arch + '/RPMS/' + rpm, dsttree.basedir + '/' + arch + '/RPMS/', test)
270 mv(self.tree.basedir + '/SRPMS/.metadata/' + self.nvr + '.src.rpm.info', dsttree.basedir + '/SRPMS/.metadata/', test)
272 class FtpTree(BaseFtpTree):
273 def __init__(self, tree, loadall=False):
274 BaseFtpTree.__init__(self, tree)
276 self.marked4removal = []
277 self.marked4moving = []
279 self.__loadpkgnames()
281 for pkgname in self.pkgnames:
282 self.loadedpkgs[pkgname] = Pkg(pkgname, self)
284 self.do_checkbuild = True
286 def __getitem__(self, key):
287 if key in self.loadedpkgs:
288 return self.loadedpkgs[key]
289 elif key in self.pkgnames:
291 self.loadedpkgs[key]=pkg
296 def has_key(self, key):
297 if key in self.pkgnames:
306 return self.loadedpkgs.values()
308 def checktree(self, dsttree):
309 self.__checkbuild(self.loadedpkgs.values())
310 self.__checkarchs(dsttree, self.loadedpkgs.values())
312 def testmove(self, dsttree, archivetree = None):
313 self.__checkbuild(self.marked4moving)
314 self.__checkarchs(dsttree, self.marked4moving)
315 if not self.treename.count("archive"):
316 self.__checkduplicates(self.marked4moving)
318 self.__checksigns(dsttree, self.marked4moving, test = True)
319 self.__checkforobsoletes(dsttree, self.marked4moving, test = True)
320 self.__checkforrelease(dsttree, self.marked4moving, test = True)
322 if not self.treename.count("archive"):
323 self.__rmolderfromsrc(test = True)
324 self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
326 for pkg in self.marked4moving:
327 pkg.move(dsttree, test = True)
329 def movepkgs(self, dsttree, archivetree = None):
330 if self.do_checkbuild:
331 self.__checkbuild(self.marked4moving)
334 self.__checkarchs(dsttree, self.marked4moving)
337 self.__checksigns(dsttree, self.marked4moving)
340 if not self.treename.count("archive"):
341 self.__rmolderfromsrc()
342 self.__rmotherfromdst(dsttree, archivetree = archivetree)
344 for pkg in self.marked4moving:
347 def rpmfiles(self, debugfiles = True, sourcefiles = True):
348 if self.do_checkbuild:
349 self.__checkbuild(self.marked4moving)
352 for pkg in self.marked4moving:
353 files += pkg.rpmfiles(debugfiles = debugfiles, sourcefiles = sourcefiles)
356 def removepkgs(self):
357 if self.do_checkbuild:
358 self.__checkbuild(self.marked4removal)
360 for pkg in self.marked4removal:
363 def mark4removal(self, wannabepkgs):
364 self.__mark4something(wannabepkgs, Pkg.mark4removal)
366 def mark4moving(self, wannabepkgs):
367 self.__mark4something(wannabepkgs, Pkg.mark4moving)
369 # Internal functions below
370 def __arch_stringify(self, list):
372 dist = config.ftp_dist;
374 ret.append(dist + '-' + arch)
377 def __loadpkgnames(self):
378 def checkfiletype(name):
379 if name[-13:]=='.src.rpm.info':
383 pkglist = list(filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata')))
384 self.pkgnames = list(map((lambda x: x[:-13]), pkglist))
386 def __mark4something(self, wannabepkgs, markfunction):
387 def chopoffextension(pkg):
388 found = pkg.find('.src.rpm')
394 for wannabepkg in wannabepkgs:
395 pkgname = chopoffextension(wannabepkg)
396 if pkgname in self.pkgnames:
397 if not pkgname in self.loadedpkgs.keys():
398 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
399 markfunction(self.loadedpkgs[pkgname])
401 perror('%s not found in source tree' % pkgname)
404 def __checkbuild(self, marked):
406 Checks queue file if all arches are built
408 Reads config.builderqueue to grab the info
410 f = urlmess.urlopen(config.builderqueue)
412 reid = re.compile(r'^.*id=(.*) pri.*$')
413 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
414 for i in re.findall(regb, f.read().decode('utf-8')):
416 id = reid.sub(r'\1', i)
419 requests[id] = requests[id] + i
423 for bid in pkg.build.keys():
424 if bid in requests and not requests[bid].find('?') == -1:
425 pkg.error("(buildid %s) building not finished" % bid)
427 def __checkarchs(self, dsttree, marked):
429 Checks marked pkgs it is built on all archs.
432 if len(pkg.files.keys()) <= 1:
433 pkg.error('has only src.rpm built')
435 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
437 # check if we're not removing some archs
441 for somepkg in otherpkgnames:
442 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
443 for arch in curarchs:
444 if arch not in pkg.files.keys():
445 missingarchs.append(arch)
447 pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs))
449 # warn if a package isn't built for all archs
451 ftp_archs_num = len(config.ftp_archs) + 1
452 if (config.separate_noarch and 'noarch' in pkg.files.keys()):
453 # ftp_archs + SRPMS + noarch subpackages
455 # plain simple noarch package
456 if (len(pkg.files.keys()) == 2):
459 if len(pkg.files.keys()) != ftp_archs_num:
461 for arch in config.ftp_archs:
462 if arch not in pkg.files.keys():
463 missingarchs.append(arch)
464 pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs))
466 def __checkduplicates(self, marked):
468 Checks if marked packages contain duplicate packages (with different versions)
471 olderpkgnames = self.__find_older_pkgs(pkg)
472 for i in olderpkgnames:
473 markednames = [str(x) for x in marked]
475 pkg.error('duplicate package: %s' % i)
477 def __rmolderfromsrc(self, test = False):
478 for pkg in self.marked4moving:
479 olderpkgnames = self.__find_older_pkgs(pkg)
480 for i in olderpkgnames:
481 Pkg(i, self).remove(test)
483 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
484 for pkg in self.marked4moving:
485 pkgnames = self.__find_other_pkgs(pkg, dsttree)
487 if archivetree == None:
488 Pkg(i, dsttree).remove(test)
490 Pkg(i, dsttree).move(archivetree, test = test)
492 # Used more than once filter functions
493 def __find_other_pkgs(self, pkg, tree):
494 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
495 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
496 def filter_other_pkgs(x):
497 if ziewre.match(x) and not x == pkg.nvr:
501 return list(filter(filter_other_pkgs, tree.pkgnames))
503 def __find_older_pkgs(self, pkg):
504 def filter_older_pkgs(x):
506 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
508 if rc == 1: # pkg > x
512 return list(filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self)))
514 def __checksigns(self, tree, pkgs, test = False):
516 Checks if pkgs in tree are all signed.
518 in case of test = true, error flag is set for unsigned packages
520 if not tree.treename in config.signed_trees:
525 for file in pkg.rpmfiles():
526 if not is_signed(file):
532 pkg.warning('%d files not signed' % unsigned)
534 pkg.error('%d files not signed' % unsigned)
536 def __checkforobsoletes(self, tree, pkgs, test = False):
538 Checks queue file if package obsoletes something in destination tree and suggest for removal.
540 Only NAME tag is compared, i.e virtual packages do not get reported.
546 def findbyname(name):
548 return '-'.join(nvr.split('-')[:-2]) == name
549 return list(filter(x, tree.pkgnames))
552 obsoletes = pkg.obsoletes()
556 for pn, setlist in obsoletes.items():
560 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
562 def __checkforrelease(self, tree, pkgs, test = False):
564 Checks queue file if package release is non integer.
571 if not pkg.is_release():
572 pkg.warning('non-integer release: %s' % pkg.release)