]> git.immae.eu Git - perso/Immae/Config/Nix.git/commitdiff
Add xmpp push notifications to buildbot and gitolite
authorIsmaël Bouya <ismael.bouya@normalesup.org>
Wed, 27 Mar 2019 12:13:00 +0000 (13:13 +0100)
committerIsmaël Bouya <ismael.bouya@normalesup.org>
Wed, 27 Mar 2019 12:13:19 +0000 (13:13 +0100)
nixops/modules/buildbot/apprise.json [new file with mode: 0644]
nixops/modules/buildbot/common/build_helpers.py
nixops/modules/buildbot/default.nix
nixops/modules/buildbot/projects/caldance/__init__.py
nixops/modules/buildbot/projects/test/__init__.py
nixops/modules/gitolite/default.nix

diff --git a/nixops/modules/buildbot/apprise.json b/nixops/modules/buildbot/apprise.json
new file mode 100644 (file)
index 0000000..b408ea4
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "tag": "c9b957c-master",
+  "meta": {
+    "name": "apprise",
+    "url": "https://github.com/caronc/apprise",
+    "branch": "master"
+  },
+  "github": {
+    "owner": "caronc",
+    "repo": "apprise",
+    "rev": "c9b957c4341e7d43e5be6463bd317a7401d083d9",
+    "sha256": "01zz4mh3xvplrm5xalzrcncnh0jq5y51pmdxkr4hdd6dz9wx0mbg",
+    "fetchSubmodules": true
+  }
+}
index f51de5470ff2d0439f953a6485a64dbbf77deb47..63733e7b31ec4b264eba8e62d46657c992940e1e 100644 (file)
@@ -3,7 +3,8 @@ from buildbot_buildslist import BuildsList
 
 __all__ = [
         "force_scheduler", "deploy_scheduler", "hook_scheduler",
-        "clean_branch", "package_and_upload", "SlackStatusPush"
+        "clean_branch", "package_and_upload", "SlackStatusPush",
+        "XMPPStatusPush"
         ]
 
 # Small helpers"
@@ -71,13 +72,16 @@ def hook_scheduler(project, timer=10):
             change_filter=util.ChangeFilter(category="hooks", project=project),
             name=project, treeStableTimer=timer, builderNames=["{}_build".format(project)])
 
-# Slack status push
+# Slack/XMPP status push
 from buildbot.reporters.http import HttpStatusPushBase
 from twisted.internet import defer
 from twisted.python import log
 from buildbot.util import httpclientservice
 from buildbot.reporters import utils
 from buildbot.process import results
+from twisted.words.protocols.jabber.jid import JID
+from wokkel import client, xmppim
+
 
 class SlackStatusPush(HttpStatusPushBase):
     name = "SlackStatusPush"
@@ -188,5 +192,66 @@ class SlackStatusPush(HttpStatusPushBase):
                 "attachments": attachments,
                 }
 
+class XMPPStatusPush(HttpStatusPushBase):
+    name = "XMPPStatusPush"
+
+    @defer.inlineCallbacks
+    def reconfigService(self, password, recipients, **kwargs):
+        yield HttpStatusPushBase.reconfigService(self, **kwargs)
+        self.password = password
+        self.recipients = recipients
+
+    @defer.inlineCallbacks
+    def send(self, build):
+        yield utils.getDetailsForBuild(self.master, build, wantProperties=True)
+        body = self.format(build)
+        factory = client.DeferredClientFactory(JID("notify_bot@immae.fr/buildbot"), self.password)
+        factory.streamManager.logTraffic = True
+        d = client.clientCreator(factory)
+        def send_message(stream):
+            message = xmppim.Message(recipient=JID(recipient), body=body)
+            message.stanzaType = 'chat'
+            stream.send(message.toElement())
+            # To allow chaining
+            return stream
+        for recipient in self.recipients:
+            d.addCallback(send_message)
+        d.addCallback(lambda _: factory.streamManager.xmlstream.sendFooter())
+        d.addErrback(log.err)
+
+    def format(self, build):
+        if "environment" in build["properties"]:
+            msg = "{} environment".format(build["properties"]["environment"][0])
+            if "build" in build["properties"]:
+                msg = "of archive {} in ".format(build["properties"]["build"][0]) + msg
+        elif len(build["buildset"]["sourcestamps"][0]["branch"]) > 0:
+            msg = "revision {}".format(build["buildset"]["sourcestamps"][0]["branch"])
+        else:
+            msg = "build"
+
+        if build["complete"]:
+            timedelta = int((build["complete_at"] - build["started_at"]).total_seconds())
+            hours, rest = divmod(timedelta, 3600)
+            minutes, seconds = divmod(rest, 60)
+            if hours > 0:
+                duration = "{}h {}min {}s".format(hours, minutes, seconds)
+            elif minutes > 0:
+                duration = "{}min {}s".format(minutes, seconds)
+            else:
+                duration = "{}s".format(seconds)
 
+            text = "Build {} ( {} ) of {}'s {} was {} in {}.".format(
+                    build["buildid"], build["url"],
+                    build["builder"]["name"],
+                    msg,
+                    results.Results[build["results"]],
+                    duration,
+                    )
+        else:
+            text = "Build {} ( {} ) of {}'s {} started.".format(
+                    build["buildid"], build["url"],
+                    build["builder"]["name"],
+                    msg,
+                    )
 
+        return text
index 21b784bb50b7c8b006b8a04cc706e5491a413bc1..c670b7d4e66b521acec2d395c8bcda46ee7b8f81 100644 (file)
@@ -57,6 +57,33 @@ in
   };
 
   config = lib.mkIf config.services.buildbot.enable {
+    nixpkgs.overlays = [ (self: super: rec {
+      python3 = super.python3.override {
+        packageOverrides = python-self: python-super: {
+          wokkel = python-self.buildPythonPackage rec {
+            pname = "wokkel";
+            version = "18.0.0";
+            src = python-self.fetchPypi {
+              inherit pname version;
+              sha256 = "1spq44gg8gsviqx1dvlmjpgfc0wk0jpyx4ap01y2pad1ai9cw016";
+            };
+            propagatedBuildInputs = with python-self; [ twisted.extras.tls twisted incremental dateutil ];
+            doChecks = false;
+          };
+          apprise = python-self.buildPythonPackage rec {
+            pname = "apprise";
+            version = "0.7.4";
+            src = (mylibs.fetchedGithub ./apprise.json).src;
+            propagatedBuildInputs = with python-self; [ decorator
+            requests requests_oauthlib oauthlib urllib3 six click
+            markdown pyyaml sleekxmpp
+            ];
+            doChecks = false;
+          };
+        };
+      };
+    }) ];
+
     ids.uids.buildbot = myconfig.env.buildbot.user.uid;
     ids.gids.buildbot = myconfig.env.buildbot.user.gid;
 
@@ -171,6 +198,7 @@ in
         });
         HOME = "${varDir}/${project.name}";
         PYTHONPATH = "${buildbot.pythonModule.withPackages (self: project.pythonPackages self pkgs ++ [
+          pkgs.python3Packages.wokkel
           pkgs.python3Packages.treq pkgs.python3Packages.ldap3 buildbot
           pkgs.python3Packages.buildbot-worker
           buildbot_common buildbot_config
index 1069c3584e090b518cb1625352461df39c9d2e70..2c0bad5b8bcb370d1369a034d7174d9915734e30 100644 (file)
@@ -19,6 +19,7 @@ class E():
     LDAP_HOST     = "ldap.immae.eu"
     LDAP_DN       = "cn=buildbot,ou=services,dc=immae,dc=eu"
     LDAP_ROLES_BASE = "ou=roles,ou=hosts,dc=immae,dc=eu"
+    XMPP_RECIPIENTS = os.environ["BUILDBOT_XMPP_RECIPIENTS"].split(" ")
 
     PUPPET_HOST = {
             "integration": "root@caldance.immae.eu",
@@ -89,6 +90,11 @@ def configure(c):
         name="slack_status_caldance",
         builders=["Caldance_build", "Caldance_deploy"],
         serverUrl=open(E.SECRETS_FILE + "/slack_webhook", "r").read().rstrip()))
+    c['services'].append(XMPPStatusPush(
+        name="xmpp_status_caldance",
+        builders=["Caldance_build", "Caldance_deploy"],
+        recipients=E.XMPP_RECIPIENTS,
+        password=open(E.SECRETS_FILE + "/notify_xmpp_password", "r").read().rstrip()))
 
 def factory(project, ignore_fails=False):
     release_file = "{1}/{0}_%(kw:clean_branch)s.tar.gz"
index 0644920f8d07518ad68ddf2fb23a1e484eec9186..e6b8d51474ca40bd4b6d8cbdfd57a6fd43e4babe 100644 (file)
@@ -19,6 +19,7 @@ class E():
     LDAP_HOST     = "ldap.immae.eu"
     LDAP_DN       = "cn=buildbot,ou=services,dc=immae,dc=eu"
     LDAP_ROLES_BASE = "ou=roles,ou=hosts,dc=immae,dc=eu"
+    XMPP_RECIPIENTS = os.environ["BUILDBOT_XMPP_RECIPIENTS"].split(" ")
 
     # master.cfg
     SECRETS_FILE       = os.getcwd() + "/secrets"
@@ -84,6 +85,11 @@ def configure(c):
         name="slack_status_test_project",
         builders=["TestProject_build", "TestProject_deploy"],
         serverUrl=open(E.SECRETS_FILE + "/slack_webhook", "r").read().rstrip()))
+    c['services'].append(XMPPStatusPush(
+        name="xmpp_status_test_project",
+        builders=["TestProject_build", "TestProject_deploy"],
+        recipients=E.XMPP_RECIPIENTS,
+        password=open(E.SECRETS_FILE + "/notify_xmpp_password", "r").read().rstrip()))
 
 def factory():
     package = util.Interpolate("test_%(kw:clean_branch)s.tar.gz", clean_branch=clean_branch)
index 844bd0fd8dcd7a2450c8aa8b7275083027792ea7..8703fbb9cb397990d5a77af64a5c66f6a829ece1 100644 (file)
@@ -48,6 +48,7 @@ in {
       python-packages = python-packages: with python-packages; [
         simplejson
         urllib3
+        sleekxmpp
       ];
     in
       [