]>
git.immae.eu Git - perso/Immae/Config/Nix.git/blob - modules/private/buildbot/common/build_helpers.py
1 from buildbot
.plugins
import util
, steps
, schedulers
2 from buildbot_buildslist
import BuildsList
3 from shutil
import which
6 "force_scheduler", "deploy_scheduler", "hook_scheduler",
7 "clean_branch", "package_and_upload", "SlackStatusPush",
8 "XMPPStatusPush", "NixShellCommand"
13 def clean_branch(props
):
14 if props
.hasProperty("branch") and len(props
["branch"]) > 0:
15 return props
["branch"].replace("/", "_")
19 def package_and_upload(package
, package_dest
, package_url
):
21 steps
.ShellCommand(name
="build package",
22 logEnviron
=False, haltOnFailure
=True, workdir
="source",
23 command
=["git", "archive", "HEAD", "-o", package
]),
25 steps
.FileUpload(name
="upload package", workersrc
=package
,
26 workdir
="source", masterdest
=package_dest
,
27 url
=package_url
, mode
=0o644),
29 steps
.ShellCommand(name
="cleanup package", logEnviron
=False,
30 haltOnFailure
=True, workdir
="source", alwaysRun
=True,
31 command
=["rm", "-f", package
]),
35 class NixShellCommand(steps
.ShellCommand
):
36 def __init__(self
, command
=None, pure
=True, nixfile
=None, **kwargs
):
37 assert(isinstance(command
, str))
38 oldpath
= kwargs
.get("env", {}).get("PATH", None)
39 if which("nix-shell", path
=oldpath
) is None:
40 kwargs
["env"] = kwargs
.get("env", {})
41 if isinstance(oldpath
, str):
42 kwargs
["env"]["PATH"] = "/run/current-system/sw/bin:" + oldpath
43 elif isinstance(oldpath
, list):
44 kwargs
["env"]["PATH"] = ["/run/current-system/sw/bin"] + oldpath
45 nixcommand
= ["nix-shell"]
47 nixcommand
.append("--pure")
48 nixcommand
.append("--run")
49 nixcommand
.append(command
)
50 if nixfile
is not None:
51 nixcommand
.append(nixfile
)
52 super().__init
__(command
=nixcommand
, **kwargs
)
55 def force_scheduler(name
, builders
):
56 return schedulers
.ForceScheduler(name
=name
,
57 label
="Force build", buttonName
="Force build",
58 reason
=util
.StringParameter(name
="reason", label
="Reason", default
="Force build"),
60 util
.CodebaseParameter("",
61 branch
=util
.StringParameter(
62 name
="branch", label
="Git reference (tag, branch)", required
=True),
63 revision
=util
.FixedParameter(name
="revision", default
=""),
64 repository
=util
.FixedParameter(name
="repository", default
=""),
65 project
=util
.FixedParameter(name
="project", default
=""),
68 username
=util
.FixedParameter(name
="username", default
="Web button"),
69 builderNames
=builders
)
71 def deploy_scheduler(name
, builders
):
72 return schedulers
.ForceScheduler(name
=name
,
73 builderNames
=builders
,
74 label
="Deploy built package", buttonName
="Deploy",
75 username
=util
.FixedParameter(name
="username", default
="Web button"),
77 util
.CodebaseParameter(codebase
="",
78 branch
=util
.FixedParameter(name
="branch", default
=""),
79 revision
=util
.FixedParameter(name
="revision", default
=""),
80 repository
=util
.FixedParameter(name
="repository", default
=""),
81 project
=util
.FixedParameter(name
="project", default
=""))],
82 reason
=util
.FixedParameter(name
="reason", default
="Deploy"),
84 util
.ChoiceStringParameter(label
="Environment",
85 name
="environment", default
="integration",
86 choices
=["integration", "production"]),
87 BuildsList(label
="Build to deploy", name
="build"),
91 def hook_scheduler(project
, timer
=10):
92 return schedulers
.AnyBranchScheduler(
93 change_filter
=util
.ChangeFilter(category
="hooks", project
=project
),
94 name
=project
, treeStableTimer
=timer
, builderNames
=["{}_build".format(project
)])
96 # Slack/XMPP status push
97 from buildbot
.reporters
.http
import HttpStatusPushBase
98 from twisted
.internet
import defer
99 from twisted
.python
import log
100 from buildbot
.util
import httpclientservice
101 from buildbot
.reporters
import utils
102 from buildbot
.process
import results
103 from twisted
.words
.protocols
.jabber
.jid
import JID
104 from wokkel
import client
, xmppim
105 from functools
import partial
107 class SlackStatusPush(HttpStatusPushBase
):
108 name
= "SlackStatusPush"
110 @defer.inlineCallbacks
111 def reconfigService(self
, serverUrl
, **kwargs
):
112 yield HttpStatusPushBase
.reconfigService(self
, **kwargs
)
113 self
._http
= yield httpclientservice
.HTTPClientService
.getService(
114 self
.master
, serverUrl
)
116 @defer.inlineCallbacks
117 def send(self
, build
):
118 yield utils
.getDetailsForBuild(self
.master
, build
, wantProperties
=True)
119 response
= yield self
._http
.post("", json
=self
.format(build
))
120 if response
.code
!= 200:
121 log
.msg("%s: unable to upload status: %s" %
122 (response
.code
, response
.content
))
124 def format(self
, build
):
127 "#F1E903", # warnings
130 "#000000", # exception
132 "#D02CA9", # cancelled
135 if "environment" in build
["properties"]:
136 msg
= "{} environment".format(build
["properties"]["environment"][0])
137 if "build" in build
["properties"]:
138 msg
= "of archive {} in ".format(build
["properties"]["build"][0]) + msg
139 elif len(build
["buildset"]["sourcestamps"][0]["branch"] or []) > 0:
140 msg
= "revision {}".format(build
["buildset"]["sourcestamps"][0]["branch"])
144 if build
["complete"]:
145 timedelta
= int((build
["complete_at"] - build
["started_at"]).total_seconds())
146 hours
, rest
= divmod(timedelta
, 3600)
147 minutes
, seconds
= divmod(rest
, 60)
149 duration
= "{}h {}min {}s".format(hours
, minutes
, seconds
)
151 duration
= "{}min {}s".format(minutes
, seconds
)
153 duration
= "{}s".format(seconds
)
155 text
= "Build <{}|{}> of {}'s {} was {} in {}.".format(
156 build
["url"], build
["buildid"],
157 build
["builder"]["name"],
159 results
.Results
[build
["results"]],
165 "value": "<{}|{}>".format(build
["url"], build
["buildid"]),
170 "value": build
["builder"]["name"],
174 "title": "Build status",
175 "value": results
.Results
[build
["results"]],
179 "title": "Build duration",
184 if "environment" in build
["properties"]:
186 "title": "Environment",
187 "value": build
["properties"]["environment"][0],
190 if "build" in build
["properties"]:
193 "value": build
["properties"]["build"][0],
198 "color": colors
[build
["results"]],
202 text
= "Build <{}|{}> of {}'s {} started.".format(
203 build
["url"], build
["buildid"],
204 build
["builder"]["name"],
210 "username": "Buildbot",
211 "icon_url": "http://docs.buildbot.net/current/_static/icon.png",
213 "attachments": attachments
,
216 class XMPPStatusPush(HttpStatusPushBase
):
217 name
= "XMPPStatusPush"
219 @defer.inlineCallbacks
220 def reconfigService(self
, password
, recipients
, **kwargs
):
221 yield HttpStatusPushBase
.reconfigService(self
, **kwargs
)
222 self
.password
= password
223 self
.recipients
= recipients
225 @defer.inlineCallbacks
226 def send(self
, build
):
227 yield utils
.getDetailsForBuild(self
.master
, build
, wantProperties
=True)
228 body
= self
.format(build
)
229 factory
= client
.DeferredClientFactory(JID("notify_bot@immae.fr/buildbot"), self
.password
)
230 d
= client
.clientCreator(factory
)
231 def send_message(recipient
, stream
):
232 message
= xmppim
.Message(recipient
=JID(recipient
), body
=body
)
233 message
.stanzaType
= 'chat'
234 stream
.send(message
.toElement())
237 for recipient
in self
.recipients
:
238 d
.addCallback(partial(send_message
, recipient
))
239 d
.addCallback(lambda _
: factory
.streamManager
.xmlstream
.sendFooter())
240 d
.addErrback(log
.err
)
242 def format(self
, build
):
243 if "environment" in build
["properties"]:
244 msg
= "{} environment".format(build
["properties"]["environment"][0])
245 if "build" in build
["properties"]:
246 msg
= "of archive {} in ".format(build
["properties"]["build"][0]) + msg
247 elif len(build
["buildset"]["sourcestamps"][0]["branch"] or []) > 0:
248 msg
= "revision {}".format(build
["buildset"]["sourcestamps"][0]["branch"])
252 if build
["complete"]:
253 timedelta
= int((build
["complete_at"] - build
["started_at"]).total_seconds())
254 hours
, rest
= divmod(timedelta
, 3600)
255 minutes
, seconds
= divmod(rest
, 60)
257 duration
= "{}h {}min {}s".format(hours
, minutes
, seconds
)
259 duration
= "{}min {}s".format(minutes
, seconds
)
261 duration
= "{}s".format(seconds
)
263 text
= "Build {} ( {} ) of {}'s {} was {} in {}.".format(
264 build
["buildid"], build
["url"],
265 build
["builder"]["name"],
267 results
.Results
[build
["results"]],
271 text
= "Build {} ( {} ) of {}'s {} started.".format(
272 build
["buildid"], build
["url"],
273 build
["builder"]["name"],