-from buildbot.plugins import util, steps, schedulers
-from buildbot_buildslist import BuildsList
-
-__all__ = [
- "force_scheduler", "deploy_scheduler", "hook_scheduler",
- "clean_branch", "package_and_upload", "SlackStatusPush",
- "XMPPStatusPush"
- ]
-
-# Small helpers"
-@util.renderer
-def clean_branch(props):
- if props.hasProperty("branch") and len(props["branch"]) > 0:
- return props["branch"].replace("/", "_")
- else:
- return "HEAD"
-
-def package_and_upload(package, package_dest, package_url):
- return [
- steps.ShellCommand(name="build package",
- logEnviron=False, haltOnFailure=True, workdir="source",
- command=["git", "archive", "HEAD", "-o", package]),
-
- steps.FileUpload(name="upload package", workersrc=package,
- workdir="source", masterdest=package_dest,
- url=package_url, mode=0o644),
-
- steps.ShellCommand(name="cleanup package", logEnviron=False,
- haltOnFailure=True, workdir="source", alwaysRun=True,
- command=["rm", "-f", package]),
- ]
-
-# Schedulers
-def force_scheduler(name, builders):
- return schedulers.ForceScheduler(name=name,
- label="Force build", buttonName="Force build",
- reason=util.StringParameter(name="reason", label="Reason", default="Force build"),
- codebases=[
- util.CodebaseParameter("",
- branch=util.StringParameter(
- name="branch", label="Git reference (tag, branch)", required=True),
- revision=util.FixedParameter(name="revision", default=""),
- repository=util.FixedParameter(name="repository", default=""),
- project=util.FixedParameter(name="project", default=""),
- ),
- ],
- username=util.FixedParameter(name="username", default="Web button"),
- builderNames=builders)
-
-def deploy_scheduler(name, builders):
- return schedulers.ForceScheduler(name=name,
- builderNames=builders,
- label="Deploy built package", buttonName="Deploy",
- username=util.FixedParameter(name="username", default="Web button"),
- codebases=[
- util.CodebaseParameter(codebase="",
- branch=util.FixedParameter(name="branch", default=""),
- revision=util.FixedParameter(name="revision", default=""),
- repository=util.FixedParameter(name="repository", default=""),
- project=util.FixedParameter(name="project", default=""))],
- reason=util.FixedParameter(name="reason", default="Deploy"),
- properties=[
- util.ChoiceStringParameter(label="Environment",
- name="environment", default="integration",
- choices=["integration", "production"]),
- BuildsList(label="Build to deploy", name="build"),
- ]
- )
-
-def hook_scheduler(project, timer=10):
- return schedulers.AnyBranchScheduler(
- change_filter=util.ChangeFilter(category="hooks", project=project),
- name=project, treeStableTimer=timer, builderNames=["{}_build".format(project)])
-
-# 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
-from functools import partial
-
-class SlackStatusPush(HttpStatusPushBase):
- name = "SlackStatusPush"
-
- @defer.inlineCallbacks
- def reconfigService(self, serverUrl, **kwargs):
- yield HttpStatusPushBase.reconfigService(self, **kwargs)
- self._http = yield httpclientservice.HTTPClientService.getService(
- self.master, serverUrl)
-
- @defer.inlineCallbacks
- def send(self, build):
- yield utils.getDetailsForBuild(self.master, build, wantProperties=True)
- response = yield self._http.post("", json=self.format(build))
- if response.code != 200:
- log.msg("%s: unable to upload status: %s" %
- (response.code, response.content))
-
- def format(self, build):
- colors = [
- "#36A64F", # success
- "#F1E903", # warnings
- "#DA0505", # failure
- "#FFFFFF", # skipped
- "#000000", # exception
- "#FFFFFF", # retry
- "#D02CA9", # cancelled
- ]
-
- 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["url"], build["buildid"],
- build["builder"]["name"],
- msg,
- results.Results[build["results"]],
- duration,
- )
- fields = [
- {
- "title": "Build",
- "value": "<{}|{}>".format(build["url"], build["buildid"]),
- "short": True,
- },
- {
- "title": "Project",
- "value": build["builder"]["name"],
- "short": True,
- },
- {
- "title": "Build status",
- "value": results.Results[build["results"]],
- "short": True,
- },
- {
- "title": "Build duration",
- "value": duration,
- "short": True,
- },
- ]
- if "environment" in build["properties"]:
- fields.append({
- "title": "Environment",
- "value": build["properties"]["environment"][0],
- "short": True,
- })
- if "build" in build["properties"]:
- fields.append({
- "title": "Archive",
- "value": build["properties"]["build"][0],
- "short": True,
- })
- attachments = [{
- "fallback": "",
- "color": colors[build["results"]],
- "fields": fields
- }]
- else:
- text = "Build <{}|{}> of {}'s {} started.".format(
- build["url"], build["buildid"],
- build["builder"]["name"],
- msg,
- )
- attachments = []
-
- return {
- "username": "Buildbot",
- "icon_url": "http://docs.buildbot.net/current/_static/icon.png",
- "text": text,
- "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)
- d = client.clientCreator(factory)
- def send_message(recipient, 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(partial(send_message, recipient))
- 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