Repository: buildbot/buildbot

Issue:
Add context field to GitHubStatus updates
Adds support for 'contexts' for the GitHub status API.
The default context string is 'continuous-integration/buildbot/'
followed by the builder name.
The context can be configured by passing it to the constructor.
(refs [#2876](http://trac.buildbot.net/ticket/2876))



Discussion:

Resolving patch:
diff --git a/master/buildbot/changes/gerritchangesource.py b/master/buildbot/changes/gerritchangesource.py
--- a/master/buildbot/changes/gerritchangesource.py
+++ b/master/buildbot/changes/gerritchangesource.py
@@ -88,31 +88,43 @@ def lineReceived(self, line):
             log.msg("bad json line: %s" % (line,))
             return
 
-        if type(event) == type({}) and "type" in event:
-            if event["type"] in [u"change-merged", u"patchset-created"]:
-                # flatten the event dictionary, for easy access with WithProperties
-                def flatten(event, base, d):
-                    for k, v in d.items():
-                        if type(v) == dict:
-                            flatten(event, base + "." + k, v)
-                        else: # already there
-                            event[base + "." + k] = v
-
-                properties = {}
-                flatten(properties, "event", event)
-
+        if type(event) == type({}) and "type" in event and event["type"] in ["patchset-created", "ref-updated"]:
+            # flatten the event dictionary, for easy access with WithProperties
+            def flatten(event, base, d):
+                for k, v in d.items():
+                    if type(v) == dict:
+                        flatten(event, base + "." + k, v)
+                    else: # already there
+                        event[base + "." + k] = v
+
+            properties = {}
+            flatten(properties, "event", event)
+
+            if event["type"] == "patchset-created":
                 change = event["change"]
-                patchset = event["patchSet"]
-
                 c = changes.Change(who="%s <%s>" % (change["owner"]["name"], change["owner"]["email"]),
                                    project=change["project"],
                                    branch=change["branch"],
-                                   revision=patchset["revision"],
+                                   revision=event["patchSet"]["revision"],
                                    revlink=change["url"],
                                    comments=change["subject"],
                                    files=["unknown"],
+                                   category=event["type"],
                                    properties=properties)
-                self.parent.addChange(c)
+            elif event["type"] == "ref-updated":
+                ref = event["refUpdate"]
+                c = changes.Change(who="%s <%s>" % (event["submitter"]["name"], event["submitter"]["email"]),
+                                   project=ref["project"],
+                                   branch=ref["refName"],
+                                   revision=ref["newRev"],
+                                   comments="Gerrit: patchset(s) merged.",
+                                   files=["unknown"],
+                                   category=event["type"],
+                                   properties=properties)
+            else:
+                return # this shouldn't happen anyway
+
+            self.parent.addChange(c)
 
     def streamProcessStopped(self):
         self.process = None
@@ -135,7 +147,7 @@ def streamProcessStopped(self):
             # good startup, but lost connection; restart immediately, and set the timeout
             # to its minimum
             self.startStreamProcess()
-            self.streamProcessTimeout = self.STREAM_BACKOUT_MIN
+            self.streamProcessTimeout = self.STREAM_BACKOFF_MIN
 
     def startStreamProcess(self):
         log.msg("starting 'gerrit stream-events'")
diff --git a/master/buildbot/status/status_gerrit.py b/master/buildbot/status/status_gerrit.py
--- a/master/buildbot/status/status_gerrit.py
+++ b/master/buildbot/status/status_gerrit.py
@@ -21,34 +21,38 @@
 import os
 
 from buildbot.status.base import StatusReceiverMultiService
+from buildbot.status.builder import Results
 from twisted.internet import reactor
 from twisted.internet.protocol import ProcessProtocol
 
-def defaultMessageCB(buildername, build, results):
-    message = "buildbot finished compiling your patchset\n"
-    message += "on configuration %s "%(buildername)
-    message += "the result is %s\n"%(results)
-    message += "more details: http://%s:8010/builders/%s/builds/%d\n"%(os.uname()[1],buildername,build.getNumber())
-    return message,0,0
+def defaultReviewCB(builderName, build, result, arg):
+    message =  "Buildbot finished compiling your patchset\n"
+    message += "on configuration: %s\n" % builderName
+    message += "The result is: %s\n" % Results[result].upper()
+
+    # message, verified, reviewed
+    return message, (result == 0 or -1), 0
 
 class GerritStatusPush(StatusReceiverMultiService):
     """Event streamer to a gerrit ssh server."""
 
-    def __init__(self, server, username, messageCB=defaultMessageCB, port=29418,
+    def __init__(self, server, username, reviewCB=defaultReviewCB, port=29418, reviewArg=None,
                  **kwargs):
         """
-        @server:   server address to be used to push events notifications.
-        @username: ssh userid to use to connect to server.
-        @messageCB: callable that is called each time a build is finished, and that is used to define the message,
-        	    and note depending on the build results
-        @port:     ssh port to use to connect to server.
+        @param server:    Gerrit SSH server's address to use for push event notifications.
+        @param username:  Gerrit SSH server's username.
+        @param reviewCB:  Callback that is called each time a build is finished, and that is used
+                          to define the message and review approvals depending on the build result.
+        @param port:      Gerrit SSH server's port.
+        @param reviewArg: Optional argument that is passed to the callback.
         """
         StatusReceiverMultiService.__init__(self)
         # Parameters.
-        self.gerritserver = server
-        self.username = username
-        self.messageCB = messageCB
-        self.gerritport = port
+        self.gerrit_server = server
+        self.gerrit_username = username
+        self.gerrit_port = port
+        self.reviewCB = reviewCB
+        self.reviewArg = reviewArg
 
     class LocalPP(ProcessProtocol):
         def __init__(self, status):
@@ -75,44 +79,57 @@ def setServiceParent(self, parent):
     def builderAdded(self, name, builder):
         return self # subscribe to this builder
 
-    def buildFinished(self, builderName, build, results):
+    def buildFinished(self, builderName, build, result):
         """Do the SSH gerrit verify command to the server."""
+        repo, git = False, False
+
+        # Gerrit + Repo
         try:
-            # Gerrit + Repo
             downloads = build.getProperty("repo_downloads")
             downloaded = build.getProperty("repo_downloaded").split(" ")
+            repo = True
+        except KeyError:
+            pass
 
+        if repo:
             if downloads and 2 * len(downloads) == len(downloaded):
-                message, verified, reviewed = self.messageCB(builderName, build, results)
+                message, verified, reviewed = self.reviewCB(builderName, build, result, self.reviewArg)
                 for i in range(0, len(downloads)):
-                    project, change1 = downloads[i].split(" ")
+                    try:
+                        project, change1 = downloads[i].split(" ")
+                    except ValueError:
+                        return # something is wrong, abort
                     change2 = downloaded[2 * i]
                     revision = downloaded[2 * i + 1]
                     if change1 == change2:
-                        self.sendCodeReview(str(project), revision, message, verified, reviewed)
+                        self.sendCodeReview(project, revision, message, verified, reviewed)
+                    else:
+                        return # something is wrong, abort
             return
+
+        # Gerrit + Git
+        try:
+            build.getProperty("gerrit_branch") # used only to verify Gerrit source
+            project = build.getProperty("project")
+            revision = build.getProperty("got_revision")
+            git = True
         except KeyError:
-            try:
-                # Gerrit + Git
-                build.getProperty("gerrit_branch") # used only to verify Gerrit source
-                project = build.getProperty("project")
-                revision = build.getProperty("got_revision")
-
-                message, verified, reviewed = self.messageCB(builderName, build, results)
-                self.sendCodeReview(project, revision, message, verified, reviewed)
-                return
-            except KeyError:
-                return
+            pass
+
+        if git:
+            message, verified, reviewed = self.reviewCB(builderName, build, result, self.reviewArg)
+            self.sendCodeReview(project, revision, message, verified, reviewed)
+            return
 
     def sendCodeReview(self, project, revision, message=None, verified=0, reviewed=0):
-        command = ["ssh", self.username + "@" + self.gerritserver, "-p", str(self.gerritport),
-                   "gerrit", "review", "--project", project]
+        command = ["ssh", self.gerrit_username + "@" + self.gerrit_server, "-p %d" % self.gerrit_port,
+                   "gerrit", "review", "--project %s" % str(project)]
         if message:
             command.append("--message '%s'" % message)
         if verified:
-            command.extend(["--verified", str(verified)])
+            command.extend(["--verified %d" % int(verified)])
         if reviewed:
-            command.extend(["--code-review", str(reviewed)])
+            command.extend(["--code-review %d" % int(reviewed)])
         command.append(str(revision))
         print command
         reactor.spawnProcess(self.LocalPP(self), "ssh", command)


Test patch: