~andrewjbeach/juju-ci-tools/make-local-patcher

« back to all changes in this revision

Viewing changes to check_blockers.py

  • Committer: Aaron Bentley
  • Date: 2014-02-28 16:40:22 UTC
  • mto: This revision was merged to the branch mainline in revision 257.
  • Revision ID: aaron.bentley@canonical.com-20140228164022-kfip2tphn9m9invi
Add juju-backup script.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
 
3
 
from __future__ import print_function
4
 
 
5
 
from argparse import ArgumentParser
6
 
import json
7
 
import sys
8
 
import urllib2
9
 
 
10
 
from launchpadlib.launchpad import (
11
 
    Launchpad,
12
 
    uris,
13
 
)
14
 
 
15
 
 
16
 
BUG_STATUSES = [
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'
21
 
 
22
 
 
23
 
def get_json(uri):
24
 
    """Return the json dict response for the uri request."""
25
 
    request = urllib2.Request(uri, headers={
26
 
        "Cache-Control": "max-age=0, must-revalidate",
27
 
    })
28
 
    response = urllib2.urlopen(request)
29
 
    data = response.read()
30
 
    if response.getcode() == 200 and data:
31
 
        return json.loads(data)
32
 
    return None
33
 
 
34
 
 
35
 
def get_lp(script_name, credentials_file=None):
36
 
    """Return an LP API client."""
37
 
    lp_args = dict()
38
 
    if credentials_file:
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)
43
 
    return lp
44
 
 
45
 
 
46
 
def parse_args(args=None):
47
 
    parser = ArgumentParser('Check if a branch is blocked from landing')
48
 
    parser.add_argument(
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(
60
 
        'block-ci-testing',
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
76
 
    return args
77
 
 
78
 
 
79
 
def get_lp_bugs(lp, branch, tags):
80
 
    """Return a dict of blocker critical bug tasks for the branch."""
81
 
    if not tags:
82
 
        raise ValueError('tags must be a list of bug tags')
83
 
    bug_tags = tags
84
 
    bugs = {}
85
 
    project = lp.projects['juju-core']
86
 
    if branch == 'master':
87
 
        # Lp implicitly assigns bugs to trunk, which is not a series query.
88
 
        target = project
89
 
    else:
90
 
        target = project.getSeries(name=branch)
91
 
    if not target:
92
 
        return bugs
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
100
 
    return bugs
101
 
 
102
 
 
103
 
def get_reason(bugs, args):
104
 
    """Return the success code and reason why the branch can be merged."""
105
 
    if not bugs:
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']:
116
 
                continue
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)
122
 
        else:
123
 
            return 1, 'Does not match {}'.format(fixes_ids)
124
 
    return 1, 'Could not get {} comments from github'.format(args.pull_request)
125
 
 
126
 
 
127
 
def update_bugs(bugs, branch, build, dry_run=False):
128
 
    """Update the critical blocker+ci bugs for the branch to Fix Released."""
129
 
    changes = []
130
 
    for bug_id, bug_task in bugs.items():
131
 
        changes.append('Updated %s' % bug_task.title)
132
 
        bug_task.status = 'Fix Released'
133
 
        if not dry_run:
134
 
            bug_task.lp_save()
135
 
            subject = 'Fix Released in juju-core %s' % branch
136
 
            content = (
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)
141
 
    return 0, changes
142
 
 
143
 
 
144
 
def main(argv):
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)
150
 
        print(reason)
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)
154
 
        print(reason)
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)
159
 
        print(changes)
160
 
    return code
161
 
 
162
 
 
163
 
if __name__ == '__main__':
164
 
    sys.exit(main(sys.argv[1:]))