__all__ = [
"force_scheduler", "deploy_scheduler", "hook_scheduler",
- "clean_branch", "package_and_upload", "SlackStatusPush"
+ "clean_branch", "package_and_upload", "SlackStatusPush",
+ "XMPPStatusPush"
]
# Small helpers"
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"
"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
};
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;
});
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
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",
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"
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"
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)