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