Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 1 | #!/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 "blueprint FOO" or "bp FOO" and updates |
| 18 | # corresponding Launchpad blueprints with links back to the change. |
| 19 | |
| 20 | from launchpadlib.launchpad import Launchpad |
| 21 | from launchpadlib.uris import LPNET_SERVICE_ROOT |
| 22 | import os |
| 23 | import argparse |
| 24 | import re |
| 25 | import subprocess |
| 26 | |
| 27 | import StringIO |
| 28 | import ConfigParser |
| 29 | import MySQLdb |
| 30 | |
| 31 | BASE_DIR = '/home/gerrit2/review_site' |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 32 | GERRIT_CACHE_DIR = os.path.expanduser( |
| 33 | os.environ.get('GERRIT_CACHE_DIR', |
| 34 | '~/.launchpadlib/cache')) |
| 35 | GERRIT_CREDENTIALS = os.path.expanduser( |
| 36 | os.environ.get('GERRIT_CREDENTIALS', |
| 37 | '~/.launchpadlib/creds')) |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 38 | GERRIT_CONFIG = os.environ.get('GERRIT_CONFIG', |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 39 | '/home/gerrit2/review_site/etc/gerrit.config') |
| 40 | GERRIT_SECURE_CONFIG_DEFAULT = '/home/gerrit2/review_site/etc/secure.config' |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 41 | GERRIT_SECURE_CONFIG = os.environ.get('GERRIT_SECURE_CONFIG', |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 42 | GERRIT_SECURE_CONFIG_DEFAULT) |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 43 | SPEC_RE = re.compile(r'(blueprint|bp)\s*[#:]?\s*(\S+)', re.I) |
| 44 | BODY_RE = re.compile(r'^\s+.*$') |
| 45 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 46 | |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 47 | def get_broken_config(filename): |
| 48 | """ gerrit config ini files are broken and have leading tabs """ |
| 49 | text = "" |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 50 | with open(filename, "r") as conf: |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 51 | for line in conf.readlines(): |
| 52 | text = "%s%s" % (text, line.lstrip()) |
| 53 | |
| 54 | fp = StringIO.StringIO(text) |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 55 | c = ConfigParser.ConfigParser() |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 56 | c.readfp(fp) |
| 57 | return c |
| 58 | |
| 59 | GERRIT_CONFIG = get_broken_config(GERRIT_CONFIG) |
| 60 | SECURE_CONFIG = get_broken_config(GERRIT_SECURE_CONFIG) |
| 61 | DB_USER = GERRIT_CONFIG.get("database", "username") |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 62 | DB_PASS = SECURE_CONFIG.get("database", "password") |
| 63 | DB_DB = GERRIT_CONFIG.get("database", "database") |
| 64 | |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 65 | |
| 66 | def update_spec(launchpad, project, name, subject, link, topic=None): |
| 67 | # For testing, if a project doesn't match openstack/foo, use |
| 68 | # the openstack-ci project instead. |
| 69 | group, project = project.split('/') |
| 70 | if group != 'openstack': |
| 71 | project = 'openstack-ci' |
| 72 | |
| 73 | spec = launchpad.projects[project].getSpecification(name=name) |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 74 | if not spec: |
| 75 | return |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 76 | |
| 77 | if spec.whiteboard: |
| 78 | wb = spec.whiteboard.strip() |
| 79 | else: |
| 80 | wb = '' |
| 81 | changed = False |
| 82 | if topic: |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 83 | topiclink = '%s/#q,topic:%s,n,z' % (link[:link.find('/', 8)], |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 84 | topic) |
| 85 | if topiclink not in wb: |
| 86 | wb += "\n\n\nGerrit topic: %(link)s" % dict(link=topiclink) |
| 87 | changed = True |
| 88 | |
| 89 | if link not in wb: |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 90 | wb += ("\n\n\nAddressed by: {link}\n" |
| 91 | " {subject}\n").format(subject=subject, |
| 92 | link=link) |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 93 | changed = True |
| 94 | |
| 95 | if changed: |
| 96 | spec.whiteboard = wb |
| 97 | spec.lp_save() |
| 98 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 99 | |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 100 | def find_specs(launchpad, dbconn, args): |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 101 | git_dir_arg = '--git-dir={base_dir}/git/{project}.git'.format( |
| 102 | base_dir=BASE_DIR, |
| 103 | project=args.project) |
| 104 | git_log = subprocess.Popen(['git', git_dir_arg, 'log', '--no-merges', |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 105 | args.commit + '^1..' + args.commit], |
| 106 | stdout=subprocess.PIPE).communicate()[0] |
| 107 | |
| 108 | cur = dbconn.cursor() |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 109 | cur.execute("select subject, topic from changes where change_key=%s", |
| 110 | args.change) |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 111 | subject, topic = cur.fetchone() |
| 112 | specs = set([m.group(2) for m in SPEC_RE.finditer(git_log)]) |
| 113 | |
| 114 | if topic: |
| 115 | topicspec = topic.split('/')[-1] |
| 116 | specs |= set([topicspec]) |
| 117 | |
| 118 | for spec in specs: |
| 119 | update_spec(launchpad, args.project, spec, subject, |
| 120 | args.change_url, topic) |
| 121 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 122 | |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 123 | def main(): |
| 124 | parser = argparse.ArgumentParser() |
| 125 | parser.add_argument('hook') |
| 126 | #common |
| 127 | parser.add_argument('--change', default=None) |
| 128 | parser.add_argument('--change-url', default=None) |
| 129 | parser.add_argument('--project', default=None) |
| 130 | parser.add_argument('--branch', default=None) |
| 131 | parser.add_argument('--commit', default=None) |
| 132 | #change-merged |
| 133 | parser.add_argument('--submitter', default=None) |
| 134 | # patchset-created |
| 135 | parser.add_argument('--uploader', default=None) |
| 136 | parser.add_argument('--patchset', default=None) |
| 137 | |
| 138 | args = parser.parse_args() |
| 139 | |
| 140 | launchpad = Launchpad.login_with('Gerrit User Sync', LPNET_SERVICE_ROOT, |
| 141 | GERRIT_CACHE_DIR, |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 142 | credentials_file=GERRIT_CREDENTIALS, |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 143 | version='devel') |
| 144 | |
Monty Taylor | da3bada | 2012-11-22 09:38:22 -0800 | [diff] [blame] | 145 | conn = MySQLdb.connect(user=DB_USER, passwd=DB_PASS, db=DB_DB) |
Monty Taylor | 6c9634c | 2012-07-28 11:27:47 -0500 | [diff] [blame] | 146 | |
| 147 | find_specs(launchpad, conn, args) |