[Xfce4-commits] <xfce-server-scripts:master> Add git_buildbot.py.

Jannis Pohlmann jannis at xfce.org
Wed Sep 16 16:04:01 CEST 2009


Updating branch refs/heads/master
         to 13ce80b005981d4ce0df1cc97443e11eb781501b (commit)
       from 72b8afea4bbdda43ac187e26e84590bdff01e382 (commit)

commit 13ce80b005981d4ce0df1cc97443e11eb781501b
Author: Jannis Pohlmann <jannis at xfce.org>
Date:   Wed Sep 16 16:03:16 2009 +0200

    Add git_buildbot.py.

 bin/git_buildbot.py         |  311 +++++++++++++++++++++++++++++++++++++++++++
 xfce-server-scripts.gemspec |    5 +-
 2 files changed, 314 insertions(+), 2 deletions(-)

diff --git a/bin/git_buildbot.py b/bin/git_buildbot.py
new file mode 100755
index 0000000..1f70a70
--- /dev/null
+++ b/bin/git_buildbot.py
@@ -0,0 +1,311 @@
+#! /usr/bin/env python
+
+# This script expects one line for each new revision on the form
+#   <oldrev> <newrev> <refname>
+#
+# For example:
+#   aa453216d1b3e49e7f6f98441fa56946ddcd6a20
+#   68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
+#
+# Each of these changes will be passed to the buildbot server along
+# with any other change information we manage to extract from the
+# repository.
+#
+# This script is meant to be run from hooks/post-receive in the git
+# repository. It can also be run at client side with hooks/post-merge
+# after using this wrapper:
+
+#!/bin/sh
+# PRE=$(git rev-parse 'HEAD@{1}')
+# POST=$(git rev-parse HEAD)
+# SYMNAME=$(git rev-parse --symbolic-full-name HEAD)
+# echo "$PRE $POST $SYMNAME" | git_buildbot.py
+#
+# Largely based on contrib/hooks/post-receive-email from git.
+
+import commands
+import logging
+import os
+import re
+import sys
+
+from twisted.spread import pb
+from twisted.cred import credentials
+from twisted.internet import reactor
+
+from buildbot.scripts import runner
+from optparse import OptionParser
+
+# Modify this to fit your setup, or pass in --master server:host on the
+# command line
+
+master = "localhost:9989"
+
+# When sending the notification, send this category iff
+# it's set (via --category)
+
+category = None
+
+
+# The GIT_DIR environment variable must have been set up so that any
+# git commands that are executed will operate on the repository we're
+# installed in.
+
+changes = []
+
+
+def connectFailed(error):
+    logging.error("Could not connect to %s: %s"
+            % (master, error.getErrorMessage()))
+    return error
+
+
+def addChange(dummy, remote, changei):
+    logging.debug("addChange %s, %s" % (repr(remote), repr(changei)))
+    try:
+        c = changei.next()
+    except StopIteration:
+        remote.broker.transport.loseConnection()
+        return None
+
+    logging.info("New revision: %s" % c['revision'][:8])
+    for key, value in c.iteritems():
+        logging.debug("  %s: %s" % (key, value))
+
+    d = remote.callRemote('addChange', c)
+    d.addCallback(addChange, remote, changei)
+    return d
+
+
+def connected(remote):
+    return addChange(None, remote, changes.__iter__())
+
+
+def grab_commit_info(c, rev):
+    # Extract information about committer and files using git show
+    f = os.popen("git show --raw --pretty=full %s" % rev, 'r')
+
+    files = []
+
+    while True:
+        line = f.readline()
+        if not line:
+            break
+
+        m = re.match(r"^:.*[MAD]\s+(.+)$", line)
+        if m:
+            logging.debug("Got file: %s" % m.group(1))
+            files.append(m.group(1))
+            continue
+
+        m = re.match(r"^Author:\s+(.+)$", line)
+        if m:
+            logging.debug("Got author: %s" % m.group(1))
+            c['who'] = m.group(1)
+
+        if re.match(r"^Merge: .*$", line):
+            files.append('merge')
+
+    c['files'] = files
+    status = f.close()
+    if status:
+        logging.warning("git show exited with status %d" % status)
+
+
+def gen_changes(input, branch):
+    while True:
+        line = input.readline()
+        if not line:
+            break
+
+        logging.debug("Change: %s" % line)
+
+        m = re.match(r"^([0-9a-f]+) (.*)$", line.strip())
+        c = {'revision': m.group(1),
+             'comments': m.group(2),
+             'branch': branch,
+        }
+        if category:
+            c['category'] = category
+        grab_commit_info(c, m.group(1))
+        changes.append(c)
+
+
+def gen_create_branch_changes(newrev, refname, branch):
+    # A new branch has been created. Generate changes for everything
+    # up to `newrev' which does not exist in any branch but `refname'.
+    #
+    # Note that this may be inaccurate if two new branches are created
+    # at the same time, pointing to the same commit, or if there are
+    # commits that only exists in a common subset of the new branches.
+
+    logging.info("Branch `%s' created" % branch)
+
+    f = os.popen("git rev-parse --not --branches"
+            + "| grep -v $(git rev-parse %s)" % refname
+            + "| git rev-list --reverse --pretty=oneline --stdin %s" % newrev,
+            'r')
+
+    gen_changes(f, branch)
+
+    status = f.close()
+    if status:
+        logging.warning("git rev-list exited with status %d" % status)
+
+
+def gen_update_branch_changes(oldrev, newrev, refname, branch):
+    # A branch has been updated. If it was a fast-forward update,
+    # generate Change events for everything between oldrev and newrev.
+    #
+    # In case of a forced update, first generate a "fake" Change event
+    # rewinding the branch to the common ancestor of oldrev and
+    # newrev. Then, generate Change events for each commit between the
+    # common ancestor and newrev.
+
+    logging.info("Branch `%s' updated %s .. %s"
+            % (branch, oldrev[:8], newrev[:8]))
+
+    baserev = commands.getoutput("git merge-base %s %s" % (oldrev, newrev))
+    logging.debug("oldrev=%s newrev=%s baserev=%s" % (oldrev, newrev, baserev))
+    if baserev != oldrev:
+        c = {'revision': baserev,
+             'comments': "Rewind branch",
+             'branch': branch,
+             'who': "dummy",
+        }
+        logging.info("Branch %s was rewound to %s" % (branch, baserev[:8]))
+        files = []
+        f = os.popen("git diff --raw %s..%s" % (oldrev, baserev), 'r')
+        while True:
+            line = f.readline()
+            if not line:
+                break
+
+            file = re.match(r"^:.*[MAD]\s*(.+)$", line).group(1)
+            logging.debug("  Rewound file: %s" % file)
+            files.append(file)
+
+        status = f.close()
+        if status:
+            logging.warning("git diff exited with status %d" % status)
+
+        if category:
+            c['category'] = category
+
+        if files:
+            c['files'] = files
+            changes.append(c)
+
+    if newrev != baserev:
+        # Not a pure rewind
+        f = os.popen("git rev-list --reverse --pretty=oneline %s..%s"
+                % (baserev, newrev), 'r')
+        gen_changes(f, branch)
+
+        status = f.close()
+        if status:
+            logging.warning("git rev-list exited with status %d" % status)
+
+
+def cleanup(res):
+    reactor.stop()
+
+
+def process_changes():
+    # Read branch updates from stdin and generate Change events
+    while True:
+        line = sys.stdin.readline()
+        if not line:
+            break
+
+        [oldrev, newrev, refname] = line.split(None, 2)
+
+        # We only care about regular heads, i.e. branches
+        m = re.match(r"^refs\/heads\/(.+)$", refname)
+        if not m:
+            logging.info("Ignoring refname `%s': Not a branch" % refname)
+            continue
+
+        branch = m.group(1)
+
+        # Find out if the branch was created, deleted or updated. Branches
+        # being deleted aren't really interesting.
+        if re.match(r"^0*$", newrev):
+            logging.info("Branch `%s' deleted, ignoring" % branch)
+            continue
+        elif re.match(r"^0*$", oldrev):
+            gen_create_branch_changes(newrev, refname, branch)
+        else:
+            gen_update_branch_changes(oldrev, newrev, refname, branch)
+
+    # Submit the changes, if any
+    if not changes:
+        logging.warning("No changes found")
+        return
+
+    host, port = master.split(':')
+    port = int(port)
+
+    f = pb.PBClientFactory()
+    d = f.login(credentials.UsernamePassword("change", "changepw"))
+    reactor.connectTCP(host, port, f)
+
+    d.addErrback(connectFailed)
+    d.addCallback(connected)
+    d.addBoth(cleanup)
+
+    reactor.run()
+
+
+def parse_options():
+    parser = OptionParser()
+    parser.add_option("-l", "--logfile", action="store", type="string",
+            help="Log to the specified file")
+    parser.add_option("-v", "--verbose", action="count",
+            help="Be more verbose. Ignored if -l is not specified.")
+    master_help = ("Build master to push to. Default is %(master)s" % 
+                   { 'master' : master })
+    parser.add_option("-m", "--master", action="store", type="string",
+            help=master_help)
+    parser.add_option("-c", "--category", action="store",
+                      type="string", help="Scheduler category to notify.")
+    options, args = parser.parse_args()
+    return options
+
+
+# Log errors and critical messages to stderr. Optionally log
+# information to a file as well (we'll set that up later.)
+stderr = logging.StreamHandler(sys.stderr)
+fmt = logging.Formatter("git_buildbot: %(levelname)s: %(message)s")
+stderr.setLevel(logging.ERROR)
+stderr.setFormatter(fmt)
+logging.getLogger().addHandler(stderr)
+logging.getLogger().setLevel(logging.DEBUG)
+
+try:
+    options = parse_options()
+    level = logging.WARNING
+    if options.verbose:
+        level -= 10 * options.verbose
+        if level < 0:
+            level = 0
+
+    if options.logfile:
+        logfile = logging.FileHandler(options.logfile)
+        logfile.setLevel(level)
+        fmt = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
+        logfile.setFormatter(fmt)
+        logging.getLogger().addHandler(logfile)
+
+    if options.master:
+        master=options.master
+
+    if options.category:
+        category = options.category
+
+    process_changes()
+except SystemExit:
+    pass
+except:
+    logging.exception("Unhandled exception")
+    sys.exit(1)
diff --git a/xfce-server-scripts.gemspec b/xfce-server-scripts.gemspec
index c8f187d..fc3464c 100644
--- a/xfce-server-scripts.gemspec
+++ b/xfce-server-scripts.gemspec
@@ -6,12 +6,13 @@ Gem::Specification.new do |s|
 
   s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
   s.authors = ["Jannis Pohlmann"]
-  s.date = %q{2009-09-03}
+  s.date = %q{2009-09-16}
   s.email = %q{jannis at xfce.org}
-  s.executables = ["xfce-check-git-commit-permissions", "xfce-manage-git-permissions"]
+  s.executables = ["xfce-check-git-commit-permissions", "xfce-manage-git-permissions", "git_buildbot.py"]
   s.files = [
     "Rakefile",
      "VERSION.yml",
+     "bin/git_buildbot.py",
      "bin/xfce-check-git-commit-permissions",
      "bin/xfce-manage-git-permissions",
      "lib/xfce-server-scripts.rb",



More information about the Xfce4-commits mailing list