jolt 0.9.76__py3-none-any.whl → 0.9.429__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. jolt/__init__.py +88 -7
  2. jolt/__main__.py +9 -1
  3. jolt/bin/fstree-darwin-x86_64 +0 -0
  4. jolt/bin/fstree-linux-x86_64 +0 -0
  5. jolt/cache.py +839 -367
  6. jolt/chroot.py +156 -0
  7. jolt/cli.py +362 -143
  8. jolt/common_pb2.py +63 -0
  9. jolt/common_pb2_grpc.py +4 -0
  10. jolt/config.py +99 -42
  11. jolt/error.py +19 -4
  12. jolt/expires.py +2 -2
  13. jolt/filesystem.py +8 -6
  14. jolt/graph.py +705 -117
  15. jolt/hooks.py +63 -1
  16. jolt/influence.py +129 -6
  17. jolt/loader.py +369 -121
  18. jolt/log.py +225 -63
  19. jolt/manifest.py +28 -38
  20. jolt/options.py +35 -10
  21. jolt/pkgs/abseil.py +42 -0
  22. jolt/pkgs/asio.py +25 -0
  23. jolt/pkgs/autoconf.py +41 -0
  24. jolt/pkgs/automake.py +41 -0
  25. jolt/pkgs/b2.py +31 -0
  26. jolt/pkgs/boost.py +111 -0
  27. jolt/pkgs/boringssl.py +32 -0
  28. jolt/pkgs/busybox.py +39 -0
  29. jolt/pkgs/bzip2.py +43 -0
  30. jolt/pkgs/cares.py +29 -0
  31. jolt/pkgs/catch2.py +36 -0
  32. jolt/pkgs/cbindgen.py +17 -0
  33. jolt/pkgs/cista.py +19 -0
  34. jolt/pkgs/clang.py +44 -0
  35. jolt/pkgs/cli11.py +23 -0
  36. jolt/pkgs/cmake.py +48 -0
  37. jolt/pkgs/cpython.py +196 -0
  38. jolt/pkgs/crun.py +29 -0
  39. jolt/pkgs/curl.py +38 -0
  40. jolt/pkgs/dbus.py +18 -0
  41. jolt/pkgs/double_conversion.py +24 -0
  42. jolt/pkgs/fastfloat.py +21 -0
  43. jolt/pkgs/ffmpeg.py +28 -0
  44. jolt/pkgs/flatbuffers.py +29 -0
  45. jolt/pkgs/fmt.py +27 -0
  46. jolt/pkgs/fstree.py +20 -0
  47. jolt/pkgs/gflags.py +18 -0
  48. jolt/pkgs/glib.py +18 -0
  49. jolt/pkgs/glog.py +25 -0
  50. jolt/pkgs/glslang.py +21 -0
  51. jolt/pkgs/golang.py +16 -11
  52. jolt/pkgs/googlebenchmark.py +18 -0
  53. jolt/pkgs/googletest.py +46 -0
  54. jolt/pkgs/gperf.py +15 -0
  55. jolt/pkgs/grpc.py +73 -0
  56. jolt/pkgs/hdf5.py +19 -0
  57. jolt/pkgs/help2man.py +14 -0
  58. jolt/pkgs/inja.py +28 -0
  59. jolt/pkgs/jsoncpp.py +31 -0
  60. jolt/pkgs/libarchive.py +43 -0
  61. jolt/pkgs/libcap.py +44 -0
  62. jolt/pkgs/libdrm.py +44 -0
  63. jolt/pkgs/libedit.py +42 -0
  64. jolt/pkgs/libevent.py +31 -0
  65. jolt/pkgs/libexpat.py +27 -0
  66. jolt/pkgs/libfastjson.py +21 -0
  67. jolt/pkgs/libffi.py +16 -0
  68. jolt/pkgs/libglvnd.py +30 -0
  69. jolt/pkgs/libogg.py +28 -0
  70. jolt/pkgs/libpciaccess.py +18 -0
  71. jolt/pkgs/libseccomp.py +21 -0
  72. jolt/pkgs/libtirpc.py +24 -0
  73. jolt/pkgs/libtool.py +42 -0
  74. jolt/pkgs/libunwind.py +35 -0
  75. jolt/pkgs/libva.py +18 -0
  76. jolt/pkgs/libvorbis.py +33 -0
  77. jolt/pkgs/libxml2.py +35 -0
  78. jolt/pkgs/libxslt.py +17 -0
  79. jolt/pkgs/libyajl.py +16 -0
  80. jolt/pkgs/llvm.py +81 -0
  81. jolt/pkgs/lua.py +54 -0
  82. jolt/pkgs/lz4.py +26 -0
  83. jolt/pkgs/m4.py +14 -0
  84. jolt/pkgs/make.py +17 -0
  85. jolt/pkgs/mesa.py +81 -0
  86. jolt/pkgs/meson.py +17 -0
  87. jolt/pkgs/mstch.py +28 -0
  88. jolt/pkgs/mysql.py +60 -0
  89. jolt/pkgs/nasm.py +49 -0
  90. jolt/pkgs/ncurses.py +30 -0
  91. jolt/pkgs/ng_log.py +25 -0
  92. jolt/pkgs/ninja.py +45 -0
  93. jolt/pkgs/nlohmann_json.py +25 -0
  94. jolt/pkgs/nodejs.py +19 -11
  95. jolt/pkgs/opencv.py +24 -0
  96. jolt/pkgs/openjdk.py +26 -0
  97. jolt/pkgs/openssl.py +103 -0
  98. jolt/pkgs/paho.py +76 -0
  99. jolt/pkgs/patchelf.py +16 -0
  100. jolt/pkgs/perl.py +42 -0
  101. jolt/pkgs/pkgconfig.py +64 -0
  102. jolt/pkgs/poco.py +39 -0
  103. jolt/pkgs/protobuf.py +77 -0
  104. jolt/pkgs/pugixml.py +27 -0
  105. jolt/pkgs/python.py +19 -0
  106. jolt/pkgs/qt.py +35 -0
  107. jolt/pkgs/rapidjson.py +26 -0
  108. jolt/pkgs/rapidyaml.py +28 -0
  109. jolt/pkgs/re2.py +30 -0
  110. jolt/pkgs/re2c.py +17 -0
  111. jolt/pkgs/readline.py +15 -0
  112. jolt/pkgs/rust.py +41 -0
  113. jolt/pkgs/sdl.py +28 -0
  114. jolt/pkgs/simdjson.py +27 -0
  115. jolt/pkgs/soci.py +46 -0
  116. jolt/pkgs/spdlog.py +29 -0
  117. jolt/pkgs/spirv_llvm.py +21 -0
  118. jolt/pkgs/spirv_tools.py +24 -0
  119. jolt/pkgs/sqlite.py +83 -0
  120. jolt/pkgs/ssl.py +12 -0
  121. jolt/pkgs/texinfo.py +15 -0
  122. jolt/pkgs/tomlplusplus.py +22 -0
  123. jolt/pkgs/wayland.py +26 -0
  124. jolt/pkgs/x11.py +58 -0
  125. jolt/pkgs/xerces_c.py +20 -0
  126. jolt/pkgs/xorg.py +360 -0
  127. jolt/pkgs/xz.py +29 -0
  128. jolt/pkgs/yamlcpp.py +30 -0
  129. jolt/pkgs/zeromq.py +47 -0
  130. jolt/pkgs/zlib.py +69 -0
  131. jolt/pkgs/zstd.py +33 -0
  132. jolt/plugins/alias.py +3 -0
  133. jolt/plugins/allure.py +5 -2
  134. jolt/plugins/autotools.py +66 -0
  135. jolt/plugins/cache.py +133 -0
  136. jolt/plugins/cmake.py +74 -6
  137. jolt/plugins/conan.py +238 -0
  138. jolt/plugins/cxx.py +698 -0
  139. jolt/plugins/cxxinfo.py +7 -0
  140. jolt/plugins/dashboard.py +1 -1
  141. jolt/plugins/docker.py +91 -23
  142. jolt/plugins/email.py +5 -2
  143. jolt/plugins/email.xslt +144 -101
  144. jolt/plugins/environ.py +11 -0
  145. jolt/plugins/fetch.py +141 -0
  146. jolt/plugins/gdb.py +44 -21
  147. jolt/plugins/gerrit.py +1 -14
  148. jolt/plugins/git.py +316 -101
  149. jolt/plugins/googletest.py +522 -1
  150. jolt/plugins/http.py +36 -38
  151. jolt/plugins/libtool.py +63 -0
  152. jolt/plugins/linux.py +990 -0
  153. jolt/plugins/logstash.py +4 -4
  154. jolt/plugins/meson.py +61 -0
  155. jolt/plugins/ninja-compdb.py +107 -31
  156. jolt/plugins/ninja.py +929 -134
  157. jolt/plugins/paths.py +11 -1
  158. jolt/plugins/pkgconfig.py +219 -0
  159. jolt/plugins/podman.py +148 -91
  160. jolt/plugins/python.py +137 -0
  161. jolt/plugins/remote_execution/__init__.py +0 -0
  162. jolt/plugins/remote_execution/administration_pb2.py +46 -0
  163. jolt/plugins/remote_execution/administration_pb2_grpc.py +170 -0
  164. jolt/plugins/remote_execution/log_pb2.py +32 -0
  165. jolt/plugins/remote_execution/log_pb2_grpc.py +68 -0
  166. jolt/plugins/remote_execution/scheduler_pb2.py +41 -0
  167. jolt/plugins/remote_execution/scheduler_pb2_grpc.py +141 -0
  168. jolt/plugins/remote_execution/worker_pb2.py +38 -0
  169. jolt/plugins/remote_execution/worker_pb2_grpc.py +112 -0
  170. jolt/plugins/report.py +12 -2
  171. jolt/plugins/rust.py +25 -0
  172. jolt/plugins/scheduler.py +710 -0
  173. jolt/plugins/selfdeploy/setup.py +9 -4
  174. jolt/plugins/selfdeploy.py +138 -88
  175. jolt/plugins/strings.py +35 -22
  176. jolt/plugins/symlinks.py +26 -11
  177. jolt/plugins/telemetry.py +5 -2
  178. jolt/plugins/timeline.py +13 -3
  179. jolt/plugins/volume.py +46 -48
  180. jolt/scheduler.py +591 -191
  181. jolt/tasks.py +1783 -245
  182. jolt/templates/export.sh.template +12 -6
  183. jolt/templates/timeline.html.template +44 -47
  184. jolt/timer.py +22 -0
  185. jolt/tools.py +749 -302
  186. jolt/utils.py +245 -18
  187. jolt/version.py +1 -1
  188. jolt/version_utils.py +2 -2
  189. jolt/xmldom.py +12 -2
  190. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/METADATA +98 -38
  191. jolt-0.9.429.dist-info/RECORD +207 -0
  192. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/WHEEL +1 -1
  193. jolt/plugins/amqp.py +0 -834
  194. jolt/plugins/debian.py +0 -338
  195. jolt/plugins/ftp.py +0 -181
  196. jolt/plugins/ninja-cache.py +0 -64
  197. jolt/plugins/ninjacli.py +0 -271
  198. jolt/plugins/repo.py +0 -253
  199. jolt-0.9.76.dist-info/RECORD +0 -79
  200. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/entry_points.txt +0 -0
  201. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/top_level.txt +0 -0
jolt/plugins/cxxinfo.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from jolt.cache import ArtifactListAttribute
2
+ from jolt.cache import ArtifactStringAttribute
2
3
  from jolt.cache import ArtifactAttributeSet
3
4
  from jolt.cache import ArtifactAttributeSetProvider
4
5
 
@@ -7,6 +8,10 @@ class CppInfoListVariable(ArtifactListAttribute):
7
8
  pass
8
9
 
9
10
 
11
+ class CppInfoStringVariable(ArtifactStringAttribute):
12
+ pass
13
+
14
+
10
15
  class CppInfoDictVariable(ArtifactListAttribute):
11
16
  def __init__(self, artifact, name):
12
17
  super(CppInfoDictVariable, self).__init__(artifact, name)
@@ -26,6 +31,8 @@ class CppInfo(ArtifactAttributeSet):
26
31
  return CppInfoListVariable(self._artifact, "asflags")
27
32
  if name == "cflags":
28
33
  return CppInfoListVariable(self._artifact, "cflags")
34
+ if name == "crt":
35
+ return CppInfoStringVariable(self._artifact, "crt")
29
36
  if name == "cxxflags":
30
37
  return CppInfoListVariable(self._artifact, "cxxflags")
31
38
  if name == "incpaths":
jolt/plugins/dashboard.py CHANGED
@@ -10,7 +10,7 @@ log.verbose("[Dashboard] Loaded")
10
10
 
11
11
  class DashboardHooks(telemetry.TelemetryHooks):
12
12
  def __init__(self, uri=None):
13
- uri = config.get("dashboard", "uri", "http://dashboard")
13
+ uri = config.get("dashboard", "uri", "http://dashboard.")
14
14
  error.raise_error_if(not uri, "dashboard.uri not configured")
15
15
  super().__init__(plugin="dashboard", uri=uri + "/api/v1/tasks", local=False)
16
16
 
jolt/plugins/docker.py CHANGED
@@ -12,9 +12,9 @@ from jolt.cache import ArtifactListAttribute
12
12
  from jolt.cache import ArtifactAttributeSet
13
13
  from jolt.cache import ArtifactAttributeSetProvider
14
14
 
15
+ import contextlib
15
16
  import json
16
17
  from os import path
17
-
18
18
  from platform import system
19
19
  import tarfile
20
20
 
@@ -25,8 +25,6 @@ class DockerListVariable(ArtifactListAttribute):
25
25
 
26
26
  class DockerLoadListVariable(DockerListVariable):
27
27
  def apply(self, task, artifact):
28
- if isinstance(task, Resource):
29
- return
30
28
  for image in self.items():
31
29
  task.tools.run(
32
30
  "docker load -i {}",
@@ -35,16 +33,12 @@ class DockerLoadListVariable(DockerListVariable):
35
33
 
36
34
  class DockerPullListVariable(DockerListVariable):
37
35
  def apply(self, task, artifact):
38
- if isinstance(task, Resource):
39
- return
40
36
  for image in self.items():
41
37
  task.tools.run("docker pull {}", image, output_on_error=True)
42
38
 
43
39
 
44
40
  class DockerRmiListVariable(DockerListVariable):
45
41
  def unapply(self, task, artifact):
46
- if isinstance(task, Resource):
47
- return
48
42
  for image in self.items():
49
43
  task.tools.run("docker rmi -f {}", image, output_on_error=True)
50
44
 
@@ -109,8 +103,8 @@ class DockerClient(Download):
109
103
  url = "https://download.docker.com/{host}/static/stable/{arch}/docker-{version}.tgz"
110
104
  """ URL of binaries """
111
105
 
112
- version = Parameter("20.10.13", help="Docker version")
113
- """ Docker version [20.10.13] """
106
+ version = Parameter("27.4.1", help="Docker version")
107
+ """ Docker version [27.4.1] """
114
108
 
115
109
  def publish(self, artifact, tools):
116
110
  super().publish(artifact, tools)
@@ -132,6 +126,9 @@ class DockerContainer(Resource):
132
126
  cap_drops = []
133
127
  """ A list of capabilities to remove from the container """
134
128
 
129
+ chroot = False
130
+ """ Use as chroot - resource consumers will execute all commands in container """
131
+
135
132
  entrypoint = None
136
133
  """ Container entrypoint """
137
134
 
@@ -170,6 +167,40 @@ class DockerContainer(Resource):
170
167
  Alternatively, assign ``True`` to publish all exposed ports to random ports.
171
168
  """
172
169
 
170
+ release_on_error = True
171
+ """ Stop and remove container on error to avoid resource leaks. """
172
+
173
+ security_opts = []
174
+ """
175
+ A list of security options.
176
+
177
+ By default, the container is started with the default security profile.
178
+
179
+ Example:
180
+
181
+ .. code-block:: python
182
+
183
+ security_opts = [
184
+ "seccomp:unconfined",
185
+ ]
186
+
187
+ """
188
+
189
+ stop_timeout = 10
190
+ """ Timeout in seconds for stopping the container .
191
+
192
+ When stopping the container, the task will wait for the container to stop
193
+ for the specified number of seconds before forcefully killing it.
194
+
195
+ Default: 10 seconds.
196
+ """
197
+
198
+ stop_signal = "SIGTERM"
199
+ """ Signal to send to the container when stopping it.
200
+
201
+ Default: ``SIGTERM``.
202
+ """
203
+
173
204
  volumes = []
174
205
  """
175
206
  A list of volumes to mount.
@@ -196,6 +227,9 @@ class DockerContainer(Resource):
196
227
  Defaults to the current user.
197
228
  """
198
229
 
230
+ workdir = None
231
+ """ The container working directory. """
232
+
199
233
  @property
200
234
  def _arguments(self):
201
235
  return " ".join(self.arguments)
@@ -220,7 +254,7 @@ class DockerContainer(Resource):
220
254
  def _image(self):
221
255
  registry = TaskRegistry.get()
222
256
  tool = tools.Tools(self)
223
- if registry.get_task_class(tool.expand(self.image)):
257
+ if registry.has_task(tool.expand(self.image)):
224
258
  return [self.image]
225
259
  return []
226
260
 
@@ -245,6 +279,14 @@ class DockerContainer(Resource):
245
279
  return "-P"
246
280
  return " ".join([utils.option("-p ", self.tools.expand(port)) for port in self.ports])
247
281
 
282
+ @property
283
+ def _security_opts(self):
284
+ return " ".join([utils.option("--security-opt ", self.tools.expand(opt)) for opt in self.security_opts])
285
+
286
+ @property
287
+ def _stop_signal(self):
288
+ return f" -s {self.stop_signal}" if self.stop_signal else ""
289
+
248
290
  @property
249
291
  def _user(self):
250
292
  if self.user:
@@ -260,7 +302,13 @@ class DockerContainer(Resource):
260
302
  return " ".join([utils.option("-v ", self.tools.expand(vol))
261
303
  for vol in self.volumes_default + self.volumes])
262
304
 
305
+ @property
306
+ def _workdir(self):
307
+ return "--workdir " + self.tools.expand(self.workdir) if self.workdir else ""
308
+
263
309
  def acquire(self, artifact, deps, tools, owner):
310
+ self._context_stack = None
311
+ self.container = None
264
312
  self.joltcachedir = config.get_cachedir()
265
313
  try:
266
314
  image = deps[self.image]
@@ -269,21 +317,34 @@ class DockerContainer(Resource):
269
317
  image = tools.expand(self.image)
270
318
 
271
319
  self._info(f"Creating container from image '{image}'")
272
- self.container = tools.run(
273
- "docker run -i -d {_cap_adds} {_cap_drops} {_entrypoint} {_labels} {_ports} {_privileged} {_user} {_environment} {_volumes} {image} {_arguments}",
274
- image=image, output_on_error=True)
320
+ with utils.delayed_interrupt():
321
+ self.container = tools.run(
322
+ "docker run -i -d {_cap_adds} {_cap_drops} {_entrypoint} {_labels} {_ports} {_privileged} {_security_opts} {_user} {_environment} {_volumes} {_workdir} {image} {_arguments}",
323
+ image=image, output_on_error=True)
275
324
 
276
325
  self._info("Created container '{container}'")
277
326
  info = tools.run("docker inspect {container}", output_on_error=True)
278
327
  artifact.container = self.container
279
328
  artifact.info = json.loads(info)[0]
280
329
 
330
+ if self.chroot:
331
+ self._context_stack = contextlib.ExitStack()
332
+ self._context_stack.enter_context(
333
+ owner.tools.runprefix(f"docker exec -i {artifact.container}"))
334
+
281
335
  def release(self, artifact, deps, tools, owner):
282
- self._info("Stopping container '{container}'")
283
- tools.run("docker stop {container}", output_on_error=True)
336
+ if self.chroot and self._context_stack:
337
+ self._context_stack.close()
284
338
 
285
- self._info("Deleting container '{container}'")
286
- tools.run("docker rm {container}", output_on_error=True)
339
+ if not self.container:
340
+ return
341
+
342
+ try:
343
+ self._info("Stopping container '{container}'")
344
+ tools.run("docker stop{_stop_signal} -t {stop_timeout} {container}", output_on_error=True)
345
+ finally:
346
+ self._info("Deleting container '{container}'")
347
+ tools.run("docker rm -f {container}", output_on_error=True)
287
348
 
288
349
 
289
350
  class DockerLogin(Resource):
@@ -432,6 +493,9 @@ class DockerImage(Task):
432
493
  """
433
494
  abstract = True
434
495
 
496
+ annotations = []
497
+ """ A list of image annotations """
498
+
435
499
  autoload = True
436
500
  """
437
501
  Automatically load image file into local registry when the artifact is
@@ -484,9 +548,6 @@ class DockerImage(Task):
484
548
  labels = []
485
549
  """ A list of image metadata labels """
486
550
 
487
- platform = None
488
- """ Target platform, e.g. linux/arm/v7. """
489
-
490
551
  pull = True
491
552
  """
492
553
  Always pull images when building.
@@ -508,6 +569,9 @@ class DockerImage(Task):
508
569
  tags = ["{canonical_name}:{identity}"]
509
570
  """ Optional list of image tags. Defaults to task's canonical name. """
510
571
 
572
+ target = None
573
+ """ Target platform, e.g. linux/arm/v7. """
574
+
511
575
  def __init__(self, *args, **kwargs):
512
576
  super().__init__(*args, **kwargs)
513
577
 
@@ -518,6 +582,10 @@ class DockerImage(Task):
518
582
  with _Tarfile.open(layerpath, 'r') as tar:
519
583
  tar.extractall(targetpath)
520
584
 
585
+ @property
586
+ def _annotations(self):
587
+ return " ".join([utils.option("--annotation ", self.tools.expand(an)) for an in self.annotations])
588
+
521
589
  @property
522
590
  def _buildargs(self):
523
591
  return " ".join([utils.option("--build-arg ", self.tools.expand(ba)) for ba in self.buildargs])
@@ -528,7 +596,7 @@ class DockerImage(Task):
528
596
 
529
597
  @property
530
598
  def _platform(self):
531
- platform = self.tools.expand(self.platform) if self.platform else None
599
+ platform = self.tools.expand(self.target) if self.target else None
532
600
  return utils.option("--platform ", platform)
533
601
 
534
602
  @property
@@ -559,7 +627,7 @@ class DockerImage(Task):
559
627
  tools.expand_relpath(context))
560
628
 
561
629
  with tools.cwd(context):
562
- tools.run("docker build {_platform} . -f {} {_buildargs} {_labels} {_tags} {pull}{squash}",
630
+ tools.run("docker build {_platform} . -f {} {_annotations} {_buildargs} {_labels} {_tags} {pull}{squash}",
563
631
  utils.quote(dockerfile), pull=pull, squash=squash)
564
632
 
565
633
  try:
@@ -609,4 +677,4 @@ class DockerImage(Task):
609
677
  if self._autoload:
610
678
  artifact.docker.load.append("{_imagefile}")
611
679
  if self._autoload:
612
- artifact.docker.rmi.append(artifact.strings.tag.get_value())
680
+ artifact.docker.rmi.append(artifact.strings.tag)
jolt/plugins/email.py CHANGED
@@ -18,6 +18,7 @@ class EmailHooks(CliHook):
18
18
  self._server = config.get("email", "server")
19
19
  self._to = config.get("email", "to")
20
20
  self._cc = config.get("email", "cc")
21
+ self._bcc = config.get("email", "bcc")
21
22
  self._from = config.get("email", "from", "jolt@localhost")
22
23
  self._subject = config.get("email", "subject", "Jolt Build Report")
23
24
  self._stylesheet = config.get(
@@ -55,11 +56,11 @@ class EmailHooks(CliHook):
55
56
  with open(self._artifact, "w") as f:
56
57
  f.write(report.transform(self._stylesheet))
57
58
 
58
- if report.has_failure():
59
+ if report.has_failure() or report.has_unstable():
59
60
  if not self._failure:
60
61
  return
61
62
  else:
62
- if not self._success:
63
+ if not self._success or not report.has_tasks():
63
64
  return
64
65
 
65
66
  msg = EmailMessage()
@@ -68,6 +69,8 @@ class EmailHooks(CliHook):
68
69
  msg['To'] = ", ".join(utils.unique_list(self._to.split()))
69
70
  if self._cc:
70
71
  msg['Cc'] = ", ".join(utils.unique_list(self._cc.split()))
72
+ if self._bcc:
73
+ msg['Bcc'] = ", ".join(utils.unique_list(self._bcc.split()))
71
74
  msg.set_content("Your e-mail client cannot display HTML formatted e-mails.")
72
75
  msg.add_alternative(report.transform(self._stylesheet), subtype='html')
73
76
 
jolt/plugins/email.xslt CHANGED
@@ -1,13 +1,18 @@
1
1
  <?xml version="1.0"?>
2
2
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
3
 
4
- <xsl:variable name="Failures" select="jolt-manifest/task[result='FAILED']"/>
5
- <xsl:variable name="Successful" select="jolt-manifest/task[result='SUCCESS']"/>
6
- <xsl:variable name="Tasks" select="jolt-manifest/task"/>
7
- <xsl:variable name="Goals" select="jolt-manifest/task[goal='true']"/>
8
- <xsl:variable name="Hours" select="floor(jolt-manifest/duration div 3600)"/>
9
- <xsl:variable name="Minutes" select="floor((jolt-manifest/duration mod 3600) div 60)"/>
10
- <xsl:variable name="Seconds" select="floor(jolt-manifest/duration mod 60)"/>
4
+ <xsl:variable name="Failures" select="jolt-manifest/task[result='FAILED']" />
5
+ <xsl:variable name="Unstable" select="jolt-manifest/task[result='UNSTABLE']" />
6
+ <xsl:variable name="FailuresAndUnstable"
7
+ select="jolt-manifest/task[result='UNSTABLE' or result='FAILED']" />
8
+ <xsl:variable name="Executed"
9
+ select="jolt-manifest/task[result!='CANCELLED' and result!='SKIPPED']" />
10
+ <xsl:variable name="Successful" select="jolt-manifest/task[result='SUCCESS']" />
11
+ <xsl:variable name="Tasks" select="jolt-manifest/task" />
12
+ <xsl:variable name="Goals" select="jolt-manifest/task[goal='true']" />
13
+ <xsl:variable name="Hours" select="floor(jolt-manifest/duration div 3600)" />
14
+ <xsl:variable name="Minutes" select="floor((jolt-manifest/duration mod 3600) div 60)" />
15
+ <xsl:variable name="Seconds" select="floor(jolt-manifest/duration mod 60)" />
11
16
 
12
17
  <xsl:template match="/">
13
18
  <html>
@@ -15,25 +20,26 @@
15
20
  <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css" />
16
21
  <style type="text/css">
17
22
  pre {
18
- margin-bottom: 0px;
19
- margin-top: 0px;
20
- overflow-x: auto;
21
- white-space: pre-wrap;
22
- white-space: -moz-pre-wrap;
23
- white-space: -pre-wrap;
24
- white-space: -o-pre-wrap;
25
- word-wrap: break-word;
23
+ margin-bottom: 0px;
24
+ margin-top: 0px;
25
+ overflow-x: auto;
26
+ white-space: pre-wrap;
27
+ white-space: -moz-pre-wrap;
28
+ white-space: -pre-wrap;
29
+ white-space: -o-pre-wrap;
30
+ word-wrap: break-word;
26
31
  }
27
32
  a {
28
- color: #f44336;
33
+ color: #f44336;
29
34
  }
30
35
  a.log {
31
- color: white;
36
+ color: white;
32
37
  }
33
38
  </style>
34
39
  </head>
35
40
  <body>
36
- <table width="100%" bgcolor="#f1f1f1" style="border-bottom: 1px solid #c1c1c1; border-top: 1px solid #c1c1c1; fixed">
41
+ <table width="100%" bgcolor="#f1f1f1"
42
+ style="border-bottom: 1px solid #c1c1c1; border-top: 1px solid #c1c1c1; fixed">
37
43
  <tr>
38
44
  <td>
39
45
  <table width="720" cellpadding="10px">
@@ -50,30 +56,46 @@
50
56
  </td>
51
57
  <td width="25%" align="center" style="border-left: 1px solid #c0c0c0">
52
58
  <table>
53
- <tr><td width="100%" align="center">Completed</td></tr>
54
- <tr><td align="center"><h2><xsl:value-of select="count($Tasks)"/></h2></td></tr>
59
+ <tr>
60
+ <td width="100%" align="center">Completed</td>
61
+ </tr>
62
+ <tr>
63
+ <td align="center">
64
+ <h2>
65
+ <xsl:value-of select="count($Executed)" />
66
+ </h2>
67
+ </td>
68
+ </tr>
55
69
  </table>
56
70
  </td>
57
71
  <td width="25%" align="center" style="border-left: 1px solid #c0c0c0">
58
72
  <table>
59
- <tr><td width="100%" align="center">Failed</td></tr>
60
- <tr><td align="center"><h2><xsl:value-of select="count($Failures)"/></h2></td></tr>
73
+ <tr>
74
+ <td width="100%" align="center">Failed</td>
75
+ </tr>
76
+ <tr>
77
+ <td align="center">
78
+ <h2>
79
+ <xsl:value-of select="count($FailuresAndUnstable)" />
80
+ </h2>
81
+ </td>
82
+ </tr>
61
83
  </table>
62
84
  </td>
63
85
  <td width="25%" align="center" style="border-left: 1px solid #c0c0c0">
64
86
  <table>
65
- <tr><td width="100%" align="center">Duration</td></tr>
87
+ <tr>
88
+ <td width="100%" align="center">Duration</td>
89
+ </tr>
66
90
  <tr>
67
91
  <td align="center">
68
92
  <h2>
69
93
  <xsl:if test="$Hours > 0">
70
- <xsl:value-of select="$Hours"/>h
71
- </xsl:if>
94
+ <xsl:value-of select="$Hours" />h </xsl:if>
72
95
  <xsl:if test="$Minutes > 0">
73
- <xsl:value-of select="$Minutes"/>min
74
- </xsl:if>
75
- <xsl:value-of select="$Seconds"/>s
76
- </h2>
96
+ <xsl:value-of select="$Minutes" />min </xsl:if>
97
+ <xsl:value-of
98
+ select="$Seconds" />s </h2>
77
99
  </td>
78
100
  </tr>
79
101
  </table>
@@ -86,101 +108,122 @@
86
108
 
87
109
  <xsl:choose>
88
110
  <xsl:when test="count($Failures) > 0">
89
- <p>
90
- This is an automated build report from Jolt. The build failed.
91
- <xsl:call-template name="jenkins-url" />
111
+ <p> This is an automated build report from Jolt. The build failed. <xsl:call-template
112
+ name="jenkins-url" />
92
113
  <xsl:call-template name="gerrit-url" />
93
114
  </p>
94
115
  </xsl:when>
116
+ <xsl:when test="count($Unstable) > 0 and count($Failures) = 0">
117
+ <p> This is an automated build report from Jolt. The build was successful, but unstable
118
+ task failures were ignored. <xsl:call-template name="jenkins-url" />
119
+ <xsl:call-template
120
+ name="gerrit-url" />
121
+ </p>
122
+ </xsl:when>
95
123
  <xsl:otherwise>
96
- <p>
97
- This is an automated build report from Jolt. The build was successful.
98
- <xsl:call-template name="jenkins-url" />
124
+ <p> This is an automated build report from Jolt. The build was successful. <xsl:call-template
125
+ name="jenkins-url" />
99
126
  <xsl:call-template name="gerrit-url" />
100
127
  </p>
101
128
  </xsl:otherwise>
102
129
  </xsl:choose>
103
130
 
104
131
  <xsl:for-each select="$Failures">
105
- <table width="100%" cellspacing="0" cellpadding="0" bgcolor="#f1f1f1">
106
- <tr bgcolor="#f44336">
107
- <td style="padding: 10px">
108
- <table cellpadding="0" cellspacing="0">
109
- <tr>
110
- <td width="100%" style="font-size: 16pt; color: white;">
111
- <xsl:value-of select="@name"/>
112
- </td>
113
- <td>
114
- <xsl:if test="logstash != ''">
115
- <xsl:element name="a">
116
- <xsl:attribute name="class">log</xsl:attribute>
117
- <xsl:attribute name="href">
118
- <xsl:value-of select="logstash"/>
119
- </xsl:attribute>
120
- Log
121
- </xsl:element>
122
- </xsl:if>
123
- </td>
124
- </tr>
125
- </table>
126
- </td>
127
- </tr>
128
- <xsl:for-each select="error">
129
- <tr>
130
- <td>
131
- <table width="100%">
132
- <tr>
133
- <td width="10%" style="padding: 5px; border-bottom: 1px solid #c1c1c1;"><xsl:value-of select="type"/></td>
134
- <td width="90%" style="padding: 5px; border-left: 1px solid #c1c1c1; border-bottom: 1px solid #c1c1c1;"><xsl:value-of select="location"/></td>
135
- </tr>
136
- </table>
137
- </td>
138
- </tr>
139
- <tr>
140
- <td style="padding: 5px; border-bottom: 1px solid #c1c1c1;"><pre><xsl:value-of select="message"/></pre></td>
141
- </tr>
142
- <xsl:if test="details != ''">
143
- <tr>
144
- <td style="padding: 5px;">
145
- <pre><xsl:value-of select="details"/></pre>
146
- </td>
147
- </tr>
148
- </xsl:if>
149
- <tr>
150
- <td style="padding: 0px; border-bottom: 2px solid #f44336;"></td>
151
- </tr>
152
- </xsl:for-each>
153
- </table>
154
- <br/>
132
+ <xsl:call-template name="task" />
133
+ </xsl:for-each>
134
+
135
+ <xsl:for-each select="$Unstable">
136
+ <xsl:call-template name="task" />
155
137
  </xsl:for-each>
138
+
156
139
  </body>
157
140
  </html>
158
141
  </xsl:template>
159
142
 
160
143
  <xsl:template name="jenkins-url">
161
- <xsl:if test="jolt-manifest/parameter[@key='BUILD_URL']/@value != ''">
162
- See
163
- <xsl:element name="a">
144
+ <xsl:if test="jolt-manifest/parameter[@key='BUILD_URL']/@value != ''"> See <xsl:element name="a">
164
145
  <xsl:attribute name="href">
165
- <xsl:value-of select="jolt-manifest/parameter[@key='BUILD_URL']/@value"/>
146
+ <xsl:value-of select="jolt-manifest/parameter[@key='BUILD_URL']/@value" />
166
147
  </xsl:attribute>
167
- full build log
168
- </xsl:element>
169
- for details.
170
- </xsl:if>
148
+ full build log </xsl:element> for details. </xsl:if>
171
149
  </xsl:template>
172
150
 
173
151
  <xsl:template name="gerrit-url">
174
- <xsl:if test="jolt-manifest/parameter[@key='GERRIT_URL']/@value != ''">
175
- This
176
- <xsl:element name="a">
152
+ <xsl:if test="jolt-manifest/parameter[@key='GERRIT_URL']/@value != ''"> This <xsl:element
153
+ name="a">
177
154
  <xsl:attribute name="href">
178
- <xsl:value-of select="jolt-manifest/parameter[@key='GERRIT_URL']/@value"/>
155
+ <xsl:value-of select="jolt-manifest/parameter[@key='GERRIT_URL']/@value" />
179
156
  </xsl:attribute>
180
- Gerrit change
157
+ Gerrit change </xsl:element> was built. </xsl:if>
158
+ </xsl:template>
159
+
160
+ <xsl:template name="task">
161
+ <table width="100%" cellspacing="0" cellpadding="0" bgcolor="#f1f1f1">
162
+ <xsl:element name="tr">
163
+ <xsl:choose>
164
+ <xsl:when test="result='FAILED'">
165
+ <xsl:attribute name="bgcolor">#f44336</xsl:attribute>
166
+ </xsl:when>
167
+ <xsl:otherwise>
168
+ <xsl:attribute name="bgcolor">#FF7E00</xsl:attribute>
169
+ </xsl:otherwise>
170
+ </xsl:choose>
181
171
  </xsl:element>
182
- was built.
183
- </xsl:if>
172
+ <td style="padding: 10px">
173
+ <table cellpadding="0" cellspacing="0">
174
+ <tr>
175
+ <td width="100%" style="font-size: 16pt; color: white;">
176
+ <xsl:value-of select="@name" />
177
+ </td>
178
+ <td>
179
+ <xsl:if test="logstash != ''">
180
+ <xsl:element name="a">
181
+ <xsl:attribute name="class">log</xsl:attribute>
182
+ <xsl:attribute name="href">
183
+ <xsl:value-of select="logstash" />
184
+ </xsl:attribute> Log </xsl:element>
185
+ </xsl:if>
186
+ </td>
187
+ </tr>
188
+ </table>
189
+ </td>
190
+ <xsl:element name="tr" />
191
+
192
+ <xsl:for-each select="error">
193
+ <tr>
194
+ <td>
195
+ <table width="100%">
196
+ <tr>
197
+ <td width="10%" style="padding: 5px; border-bottom: 1px solid #c1c1c1;">
198
+ <xsl:value-of select="type" />
199
+ </td>
200
+ <td width="90%"
201
+ style="padding: 5px; border-left: 1px solid #c1c1c1; border-bottom: 1px solid #c1c1c1;">
202
+ <xsl:value-of select="location" />
203
+ </td>
204
+ </tr>
205
+ </table>
206
+ </td>
207
+ </tr>
208
+ <tr>
209
+ <td style="padding: 5px; border-bottom: 1px solid #c1c1c1;">
210
+ <pre><xsl:value-of select="message"/></pre>
211
+ </td>
212
+ </tr>
213
+ <xsl:if test="details != ''">
214
+ <tr>
215
+ <td style="padding: 5px;">
216
+ <pre><xsl:value-of select="details"/></pre>
217
+ </td>
218
+ </tr>
219
+ </xsl:if>
220
+ <tr>
221
+ <td style="padding: 0px; border-bottom: 2px solid #f44336;"></td>
222
+ </tr>
223
+ </xsl:for-each>
224
+
225
+ </table>
226
+ <br />
184
227
  </xsl:template>
185
228
 
186
- </xsl:stylesheet>
229
+ </xsl:stylesheet>
jolt/plugins/environ.py CHANGED
@@ -55,6 +55,17 @@ class PathEnvironmentVariable(EnvironmentVariable):
55
55
 
56
56
 
57
57
  class EnvironmentVariableSet(ArtifactAttributeSet):
58
+ """ A set of environment variables for an artifact.
59
+
60
+ Example:
61
+
62
+ .. code-block:: python
63
+
64
+ artifact.environ.FOO = "bar"
65
+ artifact.environ.PATH.append("bin")
66
+
67
+ """
68
+
58
69
  def __init__(self, artifact):
59
70
  super(EnvironmentVariableSet, self).__init__()
60
71
  super(ArtifactAttributeSet, self).__setattr__("_artifact", artifact)