Permalink
Browse files

Initial push of the git hooks.

  • Loading branch information...
zukhan committed Feb 17, 2012
1 parent 75b98d1 commit 249af6c6a8b328451228dead5679ebb391bd1b9a
View
@@ -0,0 +1,37 @@
+Copyright (c) 2012 by Delphix.
+All rights reserved.
+
+The Git SCM executes certain 'hooks' for various operations. We customize some
+of these hooks in our Git repository to provide custom checks and to take
+actions such as sending automated emails.
+
+See githooks(5) for information about the semantics of each individual hook.
+
+The gate configuration is stored in the git configuration file. There is the
+default [gate] section optional [gate "base"] sections where top-level sub-
+directory configuration can be stored. This allows different top-level sub-
+directories to behave slightly differently in terms of the checks required and
+the notification lists. The required fields are marked with a *.
+
+[gate]
+ name* The name of the gate (must match the directory name)
+ notify* The email address[es] to which notification is sent
+ shortname An identifier for pushes to the gate
+ approvers The reviewboard approver group required for pushes
+ user-check Set to 'skip' to allow non-git user pushes
+ commit-check Set to 'skip' to allow multiple commits per push
+ comment-check Set to 'skip' to allow free-form (non-bug) comments
+ review-check Set to 'skip' to allow unreviewed pushes
+
+ mail-debug [debug-only] Print email subjects, but don't send
+ bug-update [debug-only] Set to 'skip' to not close bugs on push
+
+[gate "base"]
+ shortname An alternate identifier for this base
+ notify* The email address[es] to which notification is sent
+ user-check Set to 'skip' to allow non-git user pushes
+ commit-check Set to 'skip' to allow multiple commits per push
+ comment-check Set to 'skip' to allow free-form (non-bug) comments
+ review-check Set to 'skip' to allow unreviewed pushes
+
+ bug-update [debug-only] Set to 'skip' to not close bugs on push
View
@@ -0,0 +1,112 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2012 by Delphix.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# - Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# This script checks a list of bugs to verify that they are properly formed,
+# that the summaries match in Bugzilla. The list of bugs is provided via stdin.
+#
+# If there's an error, it will emit a message about the changes required.
+#
+# With the -l options, this script will emit the list of bugs, stripping out
+# those that pertain to follow-on fixes.
+#
+
+import xmlrpclib
+import pyzilla
+import sys
+import re
+from getopt import getopt
+
+
+def usage():
+ print "Usage: check-bugs [-l]"
+ sys.exit(2)
+
+(opts, args) = getopt(sys.argv[1:], "l")
+
+if args:
+ usage()
+
+lines = sys.stdin.readlines()
+badlines = []
+
+ids = []
+summaries = []
+for i in range(0, len(lines)):
+ try:
+ line = lines[i].rstrip("\n")
+ (bugid, summary) = line.split(" ", 1)
+ ids.append(int(bugid))
+ summaries.append(summary)
+ except ValueError:
+ badlines.append(line)
+
+server = pyzilla.BugZilla("<bugzilla_url>")
+server.login("<username>", "<password>")
+
+try:
+ bugs = server.Bug.get({"ids": ids})["bugs"]
+except xmlrpclib.Fault as (e):
+ print e
+ sys.exit(1)
+
+goodbugs = []
+error = False
+for i in range(0, len(bugs)):
+ bug = bugs[i]
+ bugid = bug["id"]
+ summary = bug["summary"]
+ regex = " \(.+\)$"
+
+ #
+ # If the summary matches exactly, remember the bug id. If the summary
+ # beings with the correct text and is followed by a comment within
+ # parentheses, we treat this as a follow-on push. Otherwise, identify the
+ # error along with the correct summary.
+ #
+ if summary == summaries[i]:
+ goodbugs.append(bugid)
+ elif not (summaries[i].startswith(summary) and
+ re.match(regex, summaries[i][len(summary):])):
+ if not error:
+ print "Bug summaries must match with data in Bugzilla"
+ error = True
+ print "-", ids[i], summaries[i]
+ print "+", bugid, summary
+
+if len(badlines) != 0:
+ error = True
+ print "commit lines must be of the form <id> <summary>"
+ for i in range(0, len(badlines)):
+ print ">", badlines[i]
+
+if error:
+ sys.exit(1)
+
+if opts:
+ print " ".join(map(str, goodbugs))
+
+sys.exit(0)
@@ -0,0 +1,79 @@
+#!/usr/bin/python2.6
+#
+# Copyright (c) 2011 by Delphix.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# - Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# This script marks bugs as integrated and adds a comment to note the gate
+# that they were pushed to.
+#
+
+import xmlrpclib
+import pyzilla
+import sys
+import os
+import re
+
+
+def usage():
+ print "Usage: mark-integrated <bugid> ..."
+ sys.exit(2)
+
+if len(sys.argv) <= 1:
+ usage()
+
+ids = sys.argv[1:]
+
+git_dir = os.getenv("GIT_DIR")
+if not git_dir:
+ print "GIT_DIR is not set"
+ sys.exit(1)
+
+p = re.compile(".*/([^/]+)/.git$")
+m = p.match(git_dir)
+
+if not m:
+ print "GIT_DIR is invalid: " + git_dir
+ sys.exit(1)
+
+gate = m.group(1)
+
+server = pyzilla.BugZilla("<bugzilla_url>")
+server.login("<username>", "<password>")
+
+try:
+ server.Bug.update({
+ "ids": ids,
+ "status": "INTEGRATED",
+ "comment": {
+ "body": "pushed to " + gate
+ }
+ })
+except xmlrpclib.Fault as (e):
+ print e
+ sys.exit(1)
+
+print "bugs marked as integrated to " + gate
+
+sys.exit(0)
View
@@ -0,0 +1,111 @@
+#
+# Copyright (c) 2011 by Delphix.
+# All rights reserved.
+#
+
+# pyzilla.py is a Python wrapper for the xmlrpc interface of bugzilla
+# Copyright (C) <2010> <Noufal Ibrahim>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#
+# Note: changed SafeTransport to Transport since our Bugzilla is not over https
+#
+
+import os
+import sys
+import xmlrpclib
+import urllib2
+import logging
+import cookielib
+
+def create_user_agent():
+ ma, mi, rel = sys.version_info[:3]
+ return "xmlrpclib - Python-%s.%s.%s"%(ma, mi, rel)
+
+class CookieAuthXMLRPCTransport(xmlrpclib.Transport):
+ """
+ xmlrpclib.Transport that caches authentication cookies in a
+ local cookie jar and reuses them.
+
+ Based off `this recipe
+ <http://code.activestate.com/recipes/501148-xmlrpc-serverclient-which-does-cookie-handling-and/>`_
+
+ """
+
+ def __init__(self, cookiefile = False, user_agent = False):
+ self.cookiefile = cookiefile or "cookies.txt"
+ self.user_agent = user_agent or create_user_agent()
+ xmlrpclib.Transport.__init__(self)
+
+ def send_cookie_auth(self, connection):
+ """Include Cookie Authentication data in a header"""
+ logging.debug("Sending cookie")
+ cj = cookielib.LWPCookieJar()
+ cj.load(self.cookiefile)
+ for cookie in cj:
+ connection.putheader("Cookie", "%s=%s" % (cookie.name,cookie.value))
+
+ ## override the send_host hook to also send authentication info
+ def send_host(self, connection, host):
+ xmlrpclib.Transport.send_host(self, connection, host)
+ if os.path.exists(self.cookiefile):
+ logging.debug(" Sending back cookie header")
+ self.send_cookie_auth(connection)
+
+ def request(self, host, handler, request_body, verbose=0):
+ # dummy request class for extracting cookies
+ class CookieRequest(urllib2.Request):
+ pass
+ # dummy response class for extracting cookies
+ class CookieResponse:
+ def __init__(self, headers):
+ self.headers = headers
+ def info(self):
+ return self.headers
+ crequest = CookieRequest('http://'+host+'/')
+ # issue XML-RPC request
+ h = self.make_connection(host)
+ if verbose:
+ h.set_debuglevel(1)
+ self.send_request(h, handler, request_body)
+ self.send_host(h, host)
+ self.send_user_agent(h)
+ # creating a cookie jar for my cookies
+ cj = cookielib.LWPCookieJar()
+ self.send_content(h, request_body)
+ errcode, errmsg, headers = h.getreply()
+ cresponse = CookieResponse(headers)
+ cj.extract_cookies(cresponse, crequest)
+ if len(cj) >0 and not os.path.exists(self.cookiefile):
+ logging.debug("Saving cookies in cookie jar")
+ cj.save(self.cookiefile)
+ if errcode != 200:
+ raise xmlrpclib.ProtocolError(host + handler,
+ errcode, errmsg,headers)
+ self.verbose = verbose
+ try:
+ sock = h._conn.sock
+ except AttributeError:
+ sock = None
+ return self._parse_response(h.getfile(), sock)
+
+class BugZilla(xmlrpclib.Server):
+ def __init__(self, url, verbose = False):
+ xmlrpclib.Server.__init__(self, url, CookieAuthXMLRPCTransport(),
+ verbose = verbose)
+
+ def login(self, username, password):
+ self.User.login (dict(login=username,
+ password = password))
Oops, something went wrong.

0 comments on commit 249af6c

Please sign in to comment.