3
from __future__ import print_function
5
from argparse import ArgumentParser
10
from launchpadlib.launchpad import (
17
'Incomplete', 'Confirmed', 'Triaged', 'In Progress', 'Fix Committed']
18
BUG_IMPORTANCES = ['Critical']
19
BUG_TAGS = ['blocker']
20
GH_COMMENTS = 'https://api.github.com/repos/juju/juju/issues/{}/comments'
24
"""Return the json dict response for the uri request."""
25
request = urllib2.Request(uri, headers={
26
"Cache-Control": "max-age=0, must-revalidate",
28
response = urllib2.urlopen(request)
29
data = response.read()
30
if response.getcode() == 200 and data:
31
return json.loads(data)
35
def get_lp(script_name, credentials_file=None):
36
"""Return an LP API client."""
39
lp_args['credentials_file'] = credentials_file
40
lp = Launchpad.login_with(
41
script_name, service_root=uris.LPNET_SERVICE_ROOT,
42
version='devel', **lp_args)
46
def parse_args(args=None):
47
parser = ArgumentParser('Check if a branch is blocked from landing')
49
"-c", "--credentials-file", default=None,
50
help="Launchpad credentials file.")
51
subparsers = parser.add_subparsers(help='sub-command help', dest="command")
52
check_parser = subparsers.add_parser(
53
'check', help='Check if merges are blocked for a branch.')
54
check_parser.add_argument(
55
'branch', default='master', nargs='?', type=str.lower,
56
help='The branch to merge into.')
57
check_parser.add_argument('pull_request', default=None, nargs='?',
58
help='The pull request to be merged')
59
block_ci_testing_parser = subparsers.add_parser(
61
help='Check if ci testing is blocked for the branch.')
62
block_ci_testing_parser.add_argument(
63
'branch', type=str.lower, help='The branch to merge into.')
64
update_parser = subparsers.add_parser(
65
'update', help='Update blocking for a branch that passed CI.')
66
update_parser.add_argument(
67
'-d', '--dry-run', action='store_true', default=False,
68
help='Do not make changes.')
69
update_parser.add_argument(
70
'branch', type=str.lower, help='The branch that passed.')
71
update_parser.add_argument(
72
'build', help='The build-revision build number.')
73
args = parser.parse_args(args)
74
if not getattr(args, 'pull_request', None):
75
args.pull_request = None
79
def get_lp_bugs(lp, branch, tags):
80
"""Return a dict of blocker critical bug tasks for the branch."""
82
raise ValueError('tags must be a list of bug tags')
85
project = lp.projects['juju-core']
86
if branch == 'master':
87
# Lp implicitly assigns bugs to trunk, which is not a series query.
90
target = project.getSeries(name=branch)
93
bug_tasks = target.searchTasks(
94
status=BUG_STATUSES, importance=BUG_IMPORTANCES,
95
tags=bug_tags, tags_combinator='All')
96
for bug_task in bug_tasks:
97
# Avoid an extra network call to get the bug report.
98
bug_id = bug_task.self_link.split('/')[-1]
99
bugs[bug_id] = bug_task
103
def get_reason(bugs, args):
104
"""Return the success code and reason why the branch can be merged."""
106
return 0, 'No blocking bugs'
107
fixes_ids = ['fixes-{}'.format(bug_id) for bug_id in bugs]
108
if args.pull_request is None:
109
return 1, 'Blocked waiting on {}'.format(fixes_ids)
110
uri = GH_COMMENTS.format(args.pull_request)
111
comments = get_json(uri)
112
if comments is not None:
113
for comment in comments:
114
user = comment['user']
115
if user['login'] == 'jujubot' or 'Juju bot' in comment['body']:
117
if '__JFDI__' in comment['body']:
118
return 0, 'Engineer says JFDI'
119
for fid in fixes_ids:
120
if fid in comment['body']:
121
return 0, 'Matches {}'.format(fid)
123
return 1, 'Does not match {}'.format(fixes_ids)
124
return 1, 'Could not get {} comments from github'.format(args.pull_request)
127
def update_bugs(bugs, branch, build, dry_run=False):
128
"""Update the critical blocker+ci bugs for the branch to Fix Released."""
130
for bug_id, bug_task in bugs.items():
131
changes.append('Updated %s' % bug_task.title)
132
bug_task.status = 'Fix Released'
135
subject = 'Fix Released in juju-core %s' % branch
137
'Juju-CI verified that this issue is %s:\n'
138
' http://reports.vapour.ws/releases/%s' % (subject, build))
139
bug_task.bug.newMessage(subject=subject, content=content)
140
changes = '\n'.join(changes)
145
args = parse_args(argv)
146
lp = get_lp('check_blockers', credentials_file=args.credentials_file)
147
if args.command == 'check':
148
bugs = get_lp_bugs(lp, args.branch, ['blocker'])
149
code, reason = get_reason(bugs, args)
151
if args.command == 'block-ci-testing':
152
bugs = get_lp_bugs(lp, args.branch, ['block-ci-testing'])
153
code, reason = get_reason(bugs, args)
155
elif args.command == 'update':
156
bugs = get_lp_bugs(lp, args.branch, ['blocker', 'ci'])
157
code, changes = update_bugs(
158
bugs, args.branch, args.build, dry_run=args.dry_run)
163
if __name__ == '__main__':
164
sys.exit(main(sys.argv[1:]))