blob: d17bbdd8f15a90bc5eb3592011e19921efbcc5ea [file] [log] [blame]
Monty Taylor6c9634c2012-07-28 11:27:47 -05001#!/usr/bin/env python
2# Copyright (c) 2011 OpenStack, LLC.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16# This is designed to be called by a gerrit hook. It searched new
17# patchsets for strings like "bug FOO" and updates corresponding Launchpad
18# bugs status.
19
20from launchpadlib.launchpad import Launchpad
21from launchpadlib.uris import LPNET_SERVICE_ROOT
22import os
23import argparse
24import re
25import subprocess
26
27
28BASE_DIR = '/home/gerrit2/review_site'
Monty Taylorda3bada2012-11-22 09:38:22 -080029GERRIT_CACHE_DIR = os.path.expanduser(
30 os.environ.get('GERRIT_CACHE_DIR',
31 '~/.launchpadlib/cache'))
32GERRIT_CREDENTIALS = os.path.expanduser(
33 os.environ.get('GERRIT_CREDENTIALS',
34 '~/.launchpadlib/creds'))
Monty Taylor6c9634c2012-07-28 11:27:47 -050035
36
37def add_change_proposed_message(bugtask, change_url, project, branch):
38 subject = 'Fix proposed to %s (%s)' % (short_project(project), branch)
39 body = 'Fix proposed to branch: %s\nReview: %s' % (branch, change_url)
40 bugtask.bug.newMessage(subject=subject, content=body)
41
42
43def add_change_merged_message(bugtask, change_url, project, commit,
44 submitter, branch, git_log):
45 subject = 'Fix merged to %s (%s)' % (short_project(project), branch)
46 git_url = 'http://github.com/%s/commit/%s' % (project, commit)
47 body = '''Reviewed: %s
48Committed: %s
49Submitter: %s
50Branch: %s\n''' % (change_url, git_url, submitter, branch)
51 body = body + '\n' + git_log
52 bugtask.bug.newMessage(subject=subject, content=body)
53
54
55def set_in_progress(bugtask, launchpad, uploader, change_url):
56 """Set bug In progress with assignee being the uploader"""
57
58 # Retrieve uploader from Launchpad. Use email as search key if
59 # provided, and only set if there is a clear match.
60 try:
61 searchkey = uploader[uploader.rindex("(") + 1:-1]
62 except ValueError:
63 searchkey = uploader
64 persons = launchpad.people.findPerson(text=searchkey)
65 if len(persons) == 1:
66 bugtask.assignee = persons[0]
67
68 bugtask.status = "In Progress"
69 bugtask.lp_save()
70
71
72def set_fix_committed(bugtask):
73 """Set bug fix committed"""
74
75 bugtask.status = "Fix Committed"
76 bugtask.lp_save()
77
78
79def set_fix_released(bugtask):
80 """Set bug fix released"""
81
82 bugtask.status = "Fix Released"
83 bugtask.lp_save()
84
85
86def release_fixcommitted(bugtask):
87 """Set bug FixReleased if it was FixCommitted"""
88
89 if bugtask.status == u'Fix Committed':
90 set_fix_released(bugtask)
91
92
93def tag_in_branchname(bugtask, branch):
94 """Tag bug with in-branch-name tag (if name is appropriate)"""
95
96 lp_bug = bugtask.bug
97 branch_name = branch.replace('/', '-')
98 if branch_name.replace('-', '').isalnum():
99 lp_bug.tags = lp_bug.tags + ["in-%s" % branch_name]
100 lp_bug.tags.append("in-%s" % branch_name)
101 lp_bug.lp_save()
102
103
104def short_project(full_project_name):
105 """Return the project part of the git repository name"""
106 return full_project_name.split('/')[-1]
107
108
109def git2lp(full_project_name):
110 """Convert Git repo name to Launchpad project"""
111 project_map = {
112 'openstack/openstack-ci-puppet': 'openstack-ci',
113 'openstack-ci/devstack-gate': 'openstack-ci',
114 'openstack-ci/gerrit': 'openstack-ci',
115 'openstack-ci/lodgeit': 'openstack-ci',
116 'openstack-ci/meetbot': 'openstack-ci',
Monty Taylorda3bada2012-11-22 09:38:22 -0800117 }
Monty Taylor6c9634c2012-07-28 11:27:47 -0500118 return project_map.get(full_project_name, short_project(full_project_name))
119
120
121def is_direct_release(full_project_name):
122 """Test against a list of projects who directly release changes."""
123 return full_project_name in [
124 'openstack-ci/devstack-gate',
125 'openstack-ci/lodgeit',
126 'openstack-ci/meetbot',
127 'openstack-dev/devstack',
128 'openstack/openstack-ci',
129 'openstack/openstack-ci-puppet',
130 'openstack/openstack-manuals',
Thierry Carrez101d17e2012-09-28 11:54:38 +0200131 'openstack/tempest',
Monty Taylorda3bada2012-11-22 09:38:22 -0800132 ]
Monty Taylor6c9634c2012-07-28 11:27:47 -0500133
134
135def process_bugtask(launchpad, bugtask, git_log, args):
136 """Apply changes to bugtask, based on hook / branch..."""
137
138 if args.hook == "change-merged":
139 if args.branch == 'master':
140 if is_direct_release(args.project):
141 set_fix_released(bugtask)
142 else:
Thierry Carrez8ea78ac2012-11-14 15:47:57 +0100143 if bugtask.status != u'Fix Released':
144 set_fix_committed(bugtask)
Monty Taylor6c9634c2012-07-28 11:27:47 -0500145 elif args.branch == 'milestone-proposed':
146 release_fixcommitted(bugtask)
147 elif args.branch.startswith('stable/'):
148 series = args.branch[7:]
149 # Look for a related task matching the series
150 for reltask in bugtask.related_tasks:
Thierry Carrez8ea78ac2012-11-14 15:47:57 +0100151 if (reltask.bug_target_name.endswith("/" + series) and
Monty Taylorda3bada2012-11-22 09:38:22 -0800152 reltask.status != u'Fix Released'):
Monty Taylor6c9634c2012-07-28 11:27:47 -0500153 # Use fixcommitted if there is any
154 set_fix_committed(reltask)
155 break
156 else:
157 # Use tagging if there isn't any
158 tag_in_branchname(bugtask, args.branch)
159
160 add_change_merged_message(bugtask, args.change_url, args.project,
161 args.commit, args.submitter, args.branch,
162 git_log)
163
164 if args.hook == "patchset-created":
165 if args.branch == 'master':
Thierry Carrez8ea78ac2012-11-14 15:47:57 +0100166 if bugtask.status not in [u'Fix Committed', u'Fix Released']:
167 set_in_progress(bugtask, launchpad, args.uploader,
168 args.change_url)
Monty Taylor6c9634c2012-07-28 11:27:47 -0500169 elif args.branch.startswith('stable/'):
170 series = args.branch[7:]
171 for reltask in bugtask.related_tasks:
Thierry Carrez8ea78ac2012-11-14 15:47:57 +0100172 if (reltask.bug_target_name.endswith("/" + series) and
Monty Taylorda3bada2012-11-22 09:38:22 -0800173 reltask.status not in [u'Fix Committed',
174 u'Fix Released']):
Monty Taylor6c9634c2012-07-28 11:27:47 -0500175 set_in_progress(reltask, launchpad,
176 args.uploader, args.change_url)
177 break
178
179 if args.patchset == '1':
180 add_change_proposed_message(bugtask, args.change_url,
181 args.project, args.branch)
182
183
184def find_bugs(launchpad, git_log, args):
185 """Find bugs referenced in the git log and return related bugtasks"""
186
187 bug_regexp = r'([Bb]ug|[Ll][Pp])[\s#:]*(\d+)'
188 tokens = re.split(bug_regexp, git_log)
189
190 # Extract unique bug tasks
191 bugtasks = {}
192 for token in tokens:
193 if re.match('^\d+$', token) and (token not in bugtasks):
194 try:
195 lp_bug = launchpad.bugs[token]
196 for lp_task in lp_bug.bug_tasks:
197 if lp_task.bug_target_name == git2lp(args.project):
198 bugtasks[token] = lp_task
199 break
200 except KeyError:
201 # Unknown bug
202 pass
203
204 return bugtasks.values()
205
206
207def extract_git_log(args):
208 """Extract git log of all merged commits"""
209 cmd = ['git',
210 '--git-dir=' + BASE_DIR + '/git/' + args.project + '.git',
211 'log', '--no-merges', args.commit + '^1..' + args.commit]
212 return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
213
214
215def main():
216 parser = argparse.ArgumentParser()
217 parser.add_argument('hook')
218 #common
219 parser.add_argument('--change', default=None)
220 parser.add_argument('--change-url', default=None)
221 parser.add_argument('--project', default=None)
222 parser.add_argument('--branch', default=None)
223 parser.add_argument('--commit', default=None)
224 #change-merged
225 parser.add_argument('--submitter', default=None)
226 #patchset-created
227 parser.add_argument('--uploader', default=None)
228 parser.add_argument('--patchset', default=None)
229
230 args = parser.parse_args()
231
232 # Connect to Launchpad
233 launchpad = Launchpad.login_with('Gerrit User Sync', LPNET_SERVICE_ROOT,
234 GERRIT_CACHE_DIR,
235 credentials_file=GERRIT_CREDENTIALS,
236 version='devel')
237
238 # Get git log
239 git_log = extract_git_log(args)
240
241 # Process bugtasks found in git log
242 for bugtask in find_bugs(launchpad, git_log, args):
243 process_bugtask(launchpad, bugtask, git_log, args)