blob: 64a2e544d193bd43042964f1a759c6425a73d360 [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 "blueprint FOO" or "bp FOO" and updates
18# corresponding Launchpad blueprints with links back to the change.
19
20from launchpadlib.launchpad import Launchpad
21from launchpadlib.uris import LPNET_SERVICE_ROOT
22import os
23import argparse
24import re
25import subprocess
26
27import StringIO
28import ConfigParser
29import MySQLdb
30
31BASE_DIR = '/home/gerrit2/review_site'
Monty Taylorda3bada2012-11-22 09:38:22 -080032GERRIT_CACHE_DIR = os.path.expanduser(
33 os.environ.get('GERRIT_CACHE_DIR',
34 '~/.launchpadlib/cache'))
35GERRIT_CREDENTIALS = os.path.expanduser(
36 os.environ.get('GERRIT_CREDENTIALS',
37 '~/.launchpadlib/creds'))
Monty Taylor6c9634c2012-07-28 11:27:47 -050038GERRIT_CONFIG = os.environ.get('GERRIT_CONFIG',
Monty Taylorda3bada2012-11-22 09:38:22 -080039 '/home/gerrit2/review_site/etc/gerrit.config')
40GERRIT_SECURE_CONFIG_DEFAULT = '/home/gerrit2/review_site/etc/secure.config'
Monty Taylor6c9634c2012-07-28 11:27:47 -050041GERRIT_SECURE_CONFIG = os.environ.get('GERRIT_SECURE_CONFIG',
Monty Taylorda3bada2012-11-22 09:38:22 -080042 GERRIT_SECURE_CONFIG_DEFAULT)
Monty Taylor6c9634c2012-07-28 11:27:47 -050043SPEC_RE = re.compile(r'(blueprint|bp)\s*[#:]?\s*(\S+)', re.I)
44BODY_RE = re.compile(r'^\s+.*$')
45
Monty Taylorda3bada2012-11-22 09:38:22 -080046
Monty Taylor6c9634c2012-07-28 11:27:47 -050047def get_broken_config(filename):
48 """ gerrit config ini files are broken and have leading tabs """
49 text = ""
Monty Taylorda3bada2012-11-22 09:38:22 -080050 with open(filename, "r") as conf:
Monty Taylor6c9634c2012-07-28 11:27:47 -050051 for line in conf.readlines():
52 text = "%s%s" % (text, line.lstrip())
53
54 fp = StringIO.StringIO(text)
Monty Taylorda3bada2012-11-22 09:38:22 -080055 c = ConfigParser.ConfigParser()
Monty Taylor6c9634c2012-07-28 11:27:47 -050056 c.readfp(fp)
57 return c
58
59GERRIT_CONFIG = get_broken_config(GERRIT_CONFIG)
60SECURE_CONFIG = get_broken_config(GERRIT_SECURE_CONFIG)
61DB_USER = GERRIT_CONFIG.get("database", "username")
Monty Taylorda3bada2012-11-22 09:38:22 -080062DB_PASS = SECURE_CONFIG.get("database", "password")
63DB_DB = GERRIT_CONFIG.get("database", "database")
64
Monty Taylor6c9634c2012-07-28 11:27:47 -050065
66def 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 Taylorda3bada2012-11-22 09:38:22 -080074 if not spec:
75 return
Monty Taylor6c9634c2012-07-28 11:27:47 -050076
77 if spec.whiteboard:
78 wb = spec.whiteboard.strip()
79 else:
80 wb = ''
81 changed = False
82 if topic:
Monty Taylorda3bada2012-11-22 09:38:22 -080083 topiclink = '%s/#q,topic:%s,n,z' % (link[:link.find('/', 8)],
Monty Taylor6c9634c2012-07-28 11:27:47 -050084 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 Taylorda3bada2012-11-22 09:38:22 -080090 wb += ("\n\n\nAddressed by: {link}\n"
91 " {subject}\n").format(subject=subject,
92 link=link)
Monty Taylor6c9634c2012-07-28 11:27:47 -050093 changed = True
94
95 if changed:
96 spec.whiteboard = wb
97 spec.lp_save()
98
Monty Taylorda3bada2012-11-22 09:38:22 -080099
Monty Taylor6c9634c2012-07-28 11:27:47 -0500100def find_specs(launchpad, dbconn, args):
Monty Taylorda3bada2012-11-22 09:38:22 -0800101 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 Taylor6c9634c2012-07-28 11:27:47 -0500105 args.commit + '^1..' + args.commit],
106 stdout=subprocess.PIPE).communicate()[0]
107
108 cur = dbconn.cursor()
Monty Taylorda3bada2012-11-22 09:38:22 -0800109 cur.execute("select subject, topic from changes where change_key=%s",
110 args.change)
Monty Taylor6c9634c2012-07-28 11:27:47 -0500111 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 Taylorda3bada2012-11-22 09:38:22 -0800122
Monty Taylor6c9634c2012-07-28 11:27:47 -0500123def 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 Taylorda3bada2012-11-22 09:38:22 -0800142 credentials_file=GERRIT_CREDENTIALS,
Monty Taylor6c9634c2012-07-28 11:27:47 -0500143 version='devel')
144
Monty Taylorda3bada2012-11-22 09:38:22 -0800145 conn = MySQLdb.connect(user=DB_USER, passwd=DB_PASS, db=DB_DB)
Monty Taylor6c9634c2012-07-28 11:27:47 -0500146
147 find_specs(launchpad, conn, args)