summaryrefslogtreecommitdiff
blob: 5d0c3f86525aa6332554908332ff9d29bdec5d1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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)