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'
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 dsttree.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 if not dsttree.treename.count("archive"):
325 self.__rmotherfromdst(dsttree, test = True, archivetree = archivetree)
327 for pkg in self.marked4moving:
328 pkg.move(dsttree, test = True)
330 def movepkgs(self, dsttree, archivetree = None):
331 if self.do_checkbuild:
332 self.__checkbuild(self.marked4moving)
335 self.__checkarchs(dsttree, self.marked4moving)
338 self.__checksigns(dsttree, self.marked4moving)
341 if not self.treename.count("archive"):
342 self.__rmolderfromsrc()
343 if not dsttree.treename.count("archive"):
344 self.__rmotherfromdst(dsttree, archivetree = archivetree)
346 for pkg in self.marked4moving:
349 def rpmfiles(self, debugfiles = True, sourcefiles = True):
350 if self.do_checkbuild:
351 self.__checkbuild(self.marked4moving)
354 for pkg in self.marked4moving:
355 files += pkg.rpmfiles(debugfiles = debugfiles, sourcefiles = sourcefiles)
358 def removepkgs(self):
359 if self.do_checkbuild:
360 self.__checkbuild(self.marked4removal)
362 for pkg in self.marked4removal:
365 def mark4removal(self, wannabepkgs):
366 self.__mark4something(wannabepkgs, Pkg.mark4removal)
368 def mark4moving(self, wannabepkgs):
369 self.__mark4something(wannabepkgs, Pkg.mark4moving)
371 # Internal functions below
372 def __arch_stringify(self, list):
374 dist = config.ftp_dist;
376 ret.append(dist + '-' + arch)
379 def __loadpkgnames(self):
380 def checkfiletype(name):
381 if name[-13:]=='.src.rpm.info':
385 pkglist = list(filter(checkfiletype, os.listdir(self.basedir+'/SRPMS/.metadata')))
386 self.pkgnames = list(map((lambda x: x[:-13]), pkglist))
388 def __mark4something(self, wannabepkgs, markfunction):
389 def chopoffextension(pkg):
390 found = pkg.find('.src.rpm')
396 for wannabepkg in wannabepkgs:
397 pkgname = chopoffextension(wannabepkg)
398 if pkgname in self.pkgnames:
399 if not pkgname in self.loadedpkgs.keys():
400 self.loadedpkgs[pkgname]=Pkg(pkgname, self)
401 markfunction(self.loadedpkgs[pkgname])
403 perror('%s not found in source tree' % pkgname)
406 def __checkbuild(self, marked):
408 Checks queue file if all arches are built
410 Reads config.builderqueue to grab the info
412 f = urlmess.urlopen(config.builderqueue)
414 reid = re.compile(r'^.*id=(.*) pri.*$')
415 regb = re.compile(r'^group:.*$|builders:.*$', re.M)
416 for i in re.findall(regb, f.read().decode('utf-8')):
418 id = reid.sub(r'\1', i)
421 requests[id] = requests[id] + i
425 for bid in pkg.build.keys():
426 if bid in requests and not requests[bid].find('?') == -1:
427 pkg.error("(buildid %s) building not finished" % bid)
429 def __checkarchs(self, dsttree, marked):
431 Checks marked pkgs it is built on all archs.
434 if len(pkg.files.keys()) <= 1:
435 pkg.error('has only src.rpm built')
437 otherpkgnames = self.__find_other_pkgs(pkg, dsttree)
439 # check if we're not removing some archs
443 for somepkg in otherpkgnames:
444 curarchs.extend(Pkg(somepkg, dsttree).files.keys())
445 for arch in curarchs:
446 if arch not in pkg.files.keys():
447 missingarchs.append(arch)
449 pkg.error('moving would remove archs: %s' % self.__arch_stringify(missingarchs))
451 # warn if a package isn't built for all archs
453 ftp_archs_num = len(config.ftp_archs) + 1
454 if (config.separate_noarch and 'noarch' in pkg.files.keys()):
455 # ftp_archs + SRPMS + noarch subpackages
457 # plain simple noarch package
458 if (len(pkg.files.keys()) == 2):
461 if len(pkg.files.keys()) != ftp_archs_num:
463 for arch in config.ftp_archs:
464 if arch not in pkg.files.keys():
465 missingarchs.append(arch)
466 pkg.warning('not built for archs: %s' % self.__arch_stringify(missingarchs))
468 def __checkduplicates(self, marked):
470 Checks if marked packages contain duplicate packages (with different versions)
473 olderpkgnames = self.__find_older_pkgs(pkg)
474 for i in olderpkgnames:
475 markednames = [str(x) for x in marked]
477 pkg.error('duplicate package: %s' % i)
479 def __rmolderfromsrc(self, test = False):
480 for pkg in self.marked4moving:
481 olderpkgnames = self.__find_older_pkgs(pkg)
482 for i in olderpkgnames:
483 Pkg(i, self).remove(test)
485 def __rmotherfromdst(self, dsttree, test = False, archivetree = None):
486 for pkg in self.marked4moving:
487 pkgnames = self.__find_other_pkgs(pkg, dsttree)
489 if archivetree == None:
490 Pkg(i, dsttree).remove(test)
492 Pkg(i, dsttree).move(archivetree, test = test)
494 # Used more than once filter functions
495 def __find_other_pkgs(self, pkg, tree):
496 escapedpkgname = pkg.name.replace('.', '\.').replace('+', '\+')
497 ziewre = re.compile(escapedpkgname + '-[^-]*-[^-]*$')
498 def filter_other_pkgs(x):
499 if ziewre.match(x) and not x == pkg.nvr:
503 return list(filter(filter_other_pkgs, tree.pkgnames))
505 def __find_older_pkgs(self, pkg):
506 def filter_older_pkgs(x):
508 rc = rpm.labelCompare(('0', pkg.version, pkg.release),
510 if rc == 1: # pkg > x
514 return list(filter(filter_older_pkgs, self.__find_other_pkgs(pkg, self)))
516 def __checksigns(self, tree, pkgs, test = False):
518 Checks if pkgs in tree are all signed.
520 in case of test = true, error flag is set for unsigned packages
522 if not tree.treename in config.signed_trees:
527 for file in pkg.rpmfiles():
528 if not is_signed(file):
534 pkg.warning('%d files not signed' % unsigned)
536 pkg.error('%d files not signed' % unsigned)
538 def __checkforobsoletes(self, tree, pkgs, test = False):
540 Checks queue file if package obsoletes something in destination tree and suggest for removal.
542 Only NAME tag is compared, i.e virtual packages do not get reported.
548 def findbyname(name):
550 return '-'.join(nvr.split('-')[:-2]) == name
551 return list(filter(x, tree.pkgnames))
554 obsoletes = pkg.obsoletes()
558 for pn, setlist in obsoletes.items():
562 pkg.warning('obsoletes %s (via %s) in dest tree, perhaps you want rmpkg' % (p,pn))
564 def __checkforrelease(self, tree, pkgs, test = False):
566 Checks queue file if package release is non integer.
573 if not pkg.is_release():
574 pkg.warning('non-integer release: %s' % pkg.release)