From 6f2f2e0dde5c8a1f593fe523e8e72b6eeff5cbb9 Mon Sep 17 00:00:00 2001 From: "Pawel Hajdan, Jr" Date: Mon, 21 Nov 2011 09:34:19 +0100 Subject: Automated finding candidates for stabilization. --- stabilization-candidates.py | 122 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100755 stabilization-candidates.py diff --git a/stabilization-candidates.py b/stabilization-candidates.py new file mode 100755 index 0000000..5d0c3f8 --- /dev/null +++ b/stabilization-candidates.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# Copyright 2011 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import datetime +import optparse +import os.path +import re +import subprocess + +import bugz.bugzilla +from portage.package.ebuild.getmaskingstatus import getmaskingstatus +from portage.xml.metadata import MetaDataXML +import portage.versions + +class MyBugz(bugz.bugzilla.Bugz): + def get_input(self, prompt): + return raw_input(prompt) + +if __name__ == "__main__": + parser = optparse.OptionParser() + parser.add_option("--arch", dest="arch", action="append", help="Gentoo arch to use, e.g. x86, amd64, ... Can be passed multiple times.") + parser.add_option("--days", dest="days", type=int, default=30, help="Number of days in the tree after stabilization is possible.") + parser.add_option("--repo", dest="repo", help="Path to portage CVS repository") + parser.add_option("--category", dest="category", help="Portage category filter (default is all categories)") + + (options, args) = parser.parse_args() + if not options.arch: + parser.error("--arch option is required") + if not options.repo: + parser.error("--repo option is required") + if args: + parser.error("unrecognized command-line args") + + url = 'https://bugs.gentoo.org' + print 'You may be prompted for your Gentoo Bugzilla username and password (%s).' % url + bugzilla = MyBugz(url) + bugzilla.auth() + + now = datetime.datetime.now() + for cp in portage.portdb.cp_all(): + if options.category and not cp.startswith(options.category + "/"): + continue + best_stable = portage.versions.best(portage.portdb.match(cp)) + if not best_stable: + continue + candidates = [] + for cpv in portage.portdb.cp_list(cp): + # Only consider higher versions than best stable. + if portage.versions.pkgcmp(portage.versions.pkgsplit(cpv), portage.versions.pkgsplit(best_stable)) != 1: + continue + + # Eliminate alpha, beta, pre, rc, and so on packages. + is_unstable = False + for suffix in portage.versions.endversion_keys: + if ("_" + suffix) in portage.versions.pkgsplit(cpv)[1]: + is_unstable = True + break + if is_unstable: + continue + + # Eliminate hard masked packages among others. + if getmaskingstatus(cpv) not in [[u'~%s keyword' % arch] for arch in options.arch]: + continue + + pv = portage.versions.catsplit(cpv)[1] + with open(os.path.join(options.repo, cp, 'ChangeLog')) as changelog_file: + regex = '\*%s \((.*)\)' % re.escape(pv) + match = re.search(regex, changelog_file.read()) + if not match: + continue + changelog_date = datetime.datetime.strptime(match.group(1), '%d %b %Y') + if now - changelog_date < datetime.timedelta(days=options.days): + continue + + candidates.append(cpv) + if not candidates: + continue + candidates.sort(key=portage.versions.cpv_sort_key()) + print '\t\tWorking on %s. Candidates: %s' % (cp, ', '.join(candidates)) + candidates.reverse() + best_candidate = None + cvs_path = os.path.join(options.repo, cp) + for candidate in candidates: + ebuild_name = portage.versions.catsplit(candidate)[1] + ".ebuild" + ebuild_path = os.path.join(cvs_path, ebuild_name) + manifest_path = os.path.join(cvs_path, 'Manifest') + original_contents = open(ebuild_path).read() + manifest_contents = open(manifest_path).read() + try: + for arch in options.arch: + subprocess.check_output(["ekeyword", arch, ebuild_name], cwd=cvs_path) + subprocess.check_output(["repoman", "manifest"], cwd=cvs_path) + subprocess.check_output(["repoman", "full"], cwd=cvs_path) + except subprocess.CalledProcessError: + continue + finally: + f = open(ebuild_path, "w") + f.write(original_contents) + f.close() + f = open(manifest_path, "w") + f.write(manifest_contents) + f.close() + best_candidate = candidate + break + if best_candidate: + # Do not risk trying to stabilize a package with known bugs. + bugs = bugzilla.search(cp, status=None) + if bugs: + continue + + # Protection against filing a stabilization bug twice. + bugs = bugzilla.search(best_candidate) + if bugs: + continue + + metadata = MetaDataXML(os.path.join(cvs_path, 'metadata.xml'), '/usr/portage/metadata/herds.xml') + + # Spam protection (strip @ and domain name). + maintainer_string = re.sub('@[^\s]*', '', metadata.format_maintainer_string()) + + print (cp, best_stable, best_candidate, maintainer_string) -- cgit v1.2.3-65-gdbad