jolt 0.9.172__py3-none-any.whl → 0.9.435__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 (185) hide show
  1. jolt/__init__.py +80 -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 +596 -252
  6. jolt/chroot.py +36 -11
  7. jolt/cli.py +143 -130
  8. jolt/common_pb2.py +45 -45
  9. jolt/config.py +76 -40
  10. jolt/error.py +19 -4
  11. jolt/filesystem.py +2 -6
  12. jolt/graph.py +400 -82
  13. jolt/influence.py +110 -3
  14. jolt/loader.py +338 -174
  15. jolt/log.py +127 -31
  16. jolt/manifest.py +13 -46
  17. jolt/options.py +35 -11
  18. jolt/pkgs/abseil.py +42 -0
  19. jolt/pkgs/asio.py +25 -0
  20. jolt/pkgs/autoconf.py +41 -0
  21. jolt/pkgs/automake.py +41 -0
  22. jolt/pkgs/b2.py +31 -0
  23. jolt/pkgs/boost.py +111 -0
  24. jolt/pkgs/boringssl.py +32 -0
  25. jolt/pkgs/busybox.py +39 -0
  26. jolt/pkgs/bzip2.py +43 -0
  27. jolt/pkgs/cares.py +29 -0
  28. jolt/pkgs/catch2.py +36 -0
  29. jolt/pkgs/cbindgen.py +17 -0
  30. jolt/pkgs/cista.py +19 -0
  31. jolt/pkgs/clang.py +44 -0
  32. jolt/pkgs/cli11.py +24 -0
  33. jolt/pkgs/cmake.py +48 -0
  34. jolt/pkgs/cpython.py +196 -0
  35. jolt/pkgs/crun.py +29 -0
  36. jolt/pkgs/curl.py +38 -0
  37. jolt/pkgs/dbus.py +18 -0
  38. jolt/pkgs/double_conversion.py +24 -0
  39. jolt/pkgs/fastfloat.py +21 -0
  40. jolt/pkgs/ffmpeg.py +28 -0
  41. jolt/pkgs/flatbuffers.py +29 -0
  42. jolt/pkgs/fmt.py +27 -0
  43. jolt/pkgs/fstree.py +20 -0
  44. jolt/pkgs/gflags.py +18 -0
  45. jolt/pkgs/glib.py +18 -0
  46. jolt/pkgs/glog.py +25 -0
  47. jolt/pkgs/glslang.py +21 -0
  48. jolt/pkgs/golang.py +16 -11
  49. jolt/pkgs/googlebenchmark.py +18 -0
  50. jolt/pkgs/googletest.py +46 -0
  51. jolt/pkgs/gperf.py +15 -0
  52. jolt/pkgs/grpc.py +73 -0
  53. jolt/pkgs/hdf5.py +19 -0
  54. jolt/pkgs/help2man.py +14 -0
  55. jolt/pkgs/inja.py +28 -0
  56. jolt/pkgs/jsoncpp.py +31 -0
  57. jolt/pkgs/libarchive.py +43 -0
  58. jolt/pkgs/libcap.py +44 -0
  59. jolt/pkgs/libdrm.py +44 -0
  60. jolt/pkgs/libedit.py +42 -0
  61. jolt/pkgs/libevent.py +31 -0
  62. jolt/pkgs/libexpat.py +27 -0
  63. jolt/pkgs/libfastjson.py +21 -0
  64. jolt/pkgs/libffi.py +16 -0
  65. jolt/pkgs/libglvnd.py +30 -0
  66. jolt/pkgs/libogg.py +28 -0
  67. jolt/pkgs/libpciaccess.py +18 -0
  68. jolt/pkgs/libseccomp.py +21 -0
  69. jolt/pkgs/libtirpc.py +24 -0
  70. jolt/pkgs/libtool.py +42 -0
  71. jolt/pkgs/libunwind.py +35 -0
  72. jolt/pkgs/libva.py +18 -0
  73. jolt/pkgs/libvorbis.py +33 -0
  74. jolt/pkgs/libxml2.py +35 -0
  75. jolt/pkgs/libxslt.py +17 -0
  76. jolt/pkgs/libyajl.py +16 -0
  77. jolt/pkgs/llvm.py +81 -0
  78. jolt/pkgs/lua.py +54 -0
  79. jolt/pkgs/lz4.py +26 -0
  80. jolt/pkgs/m4.py +14 -0
  81. jolt/pkgs/make.py +17 -0
  82. jolt/pkgs/mesa.py +81 -0
  83. jolt/pkgs/meson.py +17 -0
  84. jolt/pkgs/mstch.py +28 -0
  85. jolt/pkgs/mysql.py +60 -0
  86. jolt/pkgs/nasm.py +49 -0
  87. jolt/pkgs/ncurses.py +30 -0
  88. jolt/pkgs/ng_log.py +25 -0
  89. jolt/pkgs/ninja.py +45 -0
  90. jolt/pkgs/nlohmann_json.py +25 -0
  91. jolt/pkgs/nodejs.py +19 -11
  92. jolt/pkgs/opencv.py +24 -0
  93. jolt/pkgs/openjdk.py +26 -0
  94. jolt/pkgs/openssl.py +103 -0
  95. jolt/pkgs/paho.py +76 -0
  96. jolt/pkgs/patchelf.py +16 -0
  97. jolt/pkgs/perl.py +42 -0
  98. jolt/pkgs/pkgconfig.py +64 -0
  99. jolt/pkgs/poco.py +39 -0
  100. jolt/pkgs/protobuf.py +77 -0
  101. jolt/pkgs/pugixml.py +27 -0
  102. jolt/pkgs/python.py +19 -0
  103. jolt/pkgs/qt.py +35 -0
  104. jolt/pkgs/rapidjson.py +26 -0
  105. jolt/pkgs/rapidyaml.py +28 -0
  106. jolt/pkgs/re2.py +30 -0
  107. jolt/pkgs/re2c.py +17 -0
  108. jolt/pkgs/readline.py +15 -0
  109. jolt/pkgs/rust.py +41 -0
  110. jolt/pkgs/sdl.py +28 -0
  111. jolt/pkgs/simdjson.py +27 -0
  112. jolt/pkgs/soci.py +46 -0
  113. jolt/pkgs/spdlog.py +29 -0
  114. jolt/pkgs/spirv_llvm.py +21 -0
  115. jolt/pkgs/spirv_tools.py +24 -0
  116. jolt/pkgs/sqlite.py +83 -0
  117. jolt/pkgs/ssl.py +12 -0
  118. jolt/pkgs/texinfo.py +15 -0
  119. jolt/pkgs/tomlplusplus.py +22 -0
  120. jolt/pkgs/wayland.py +26 -0
  121. jolt/pkgs/x11.py +58 -0
  122. jolt/pkgs/xerces_c.py +20 -0
  123. jolt/pkgs/xorg.py +360 -0
  124. jolt/pkgs/xz.py +29 -0
  125. jolt/pkgs/yamlcpp.py +30 -0
  126. jolt/pkgs/zeromq.py +47 -0
  127. jolt/pkgs/zlib.py +87 -0
  128. jolt/pkgs/zstd.py +33 -0
  129. jolt/plugins/alias.py +3 -0
  130. jolt/plugins/allure.py +2 -2
  131. jolt/plugins/autotools.py +66 -0
  132. jolt/plugins/cache.py +1 -1
  133. jolt/plugins/cmake.py +74 -6
  134. jolt/plugins/conan.py +238 -0
  135. jolt/plugins/cxxinfo.py +7 -0
  136. jolt/plugins/docker.py +76 -19
  137. jolt/plugins/email.xslt +141 -118
  138. jolt/plugins/environ.py +11 -0
  139. jolt/plugins/fetch.py +141 -0
  140. jolt/plugins/gdb.py +33 -14
  141. jolt/plugins/gerrit.py +0 -13
  142. jolt/plugins/git.py +248 -66
  143. jolt/plugins/googletest.py +1 -1
  144. jolt/plugins/http.py +1 -1
  145. jolt/plugins/libtool.py +63 -0
  146. jolt/plugins/linux.py +990 -0
  147. jolt/plugins/logstash.py +4 -4
  148. jolt/plugins/meson.py +61 -0
  149. jolt/plugins/ninja-compdb.py +96 -28
  150. jolt/plugins/ninja.py +424 -150
  151. jolt/plugins/paths.py +11 -1
  152. jolt/plugins/pkgconfig.py +219 -0
  153. jolt/plugins/podman.py +131 -87
  154. jolt/plugins/python.py +137 -0
  155. jolt/plugins/remote_execution/administration_pb2.py +27 -19
  156. jolt/plugins/remote_execution/log_pb2.py +12 -12
  157. jolt/plugins/remote_execution/scheduler_pb2.py +23 -23
  158. jolt/plugins/remote_execution/worker_pb2.py +19 -19
  159. jolt/plugins/report.py +7 -2
  160. jolt/plugins/rust.py +25 -0
  161. jolt/plugins/scheduler.py +135 -86
  162. jolt/plugins/selfdeploy/setup.py +6 -6
  163. jolt/plugins/selfdeploy.py +49 -31
  164. jolt/plugins/strings.py +35 -22
  165. jolt/plugins/symlinks.py +11 -4
  166. jolt/plugins/telemetry.py +1 -2
  167. jolt/plugins/timeline.py +13 -3
  168. jolt/scheduler.py +467 -165
  169. jolt/tasks.py +427 -111
  170. jolt/templates/timeline.html.template +44 -47
  171. jolt/timer.py +22 -0
  172. jolt/tools.py +527 -188
  173. jolt/utils.py +183 -3
  174. jolt/version.py +1 -1
  175. jolt/xmldom.py +12 -2
  176. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/METADATA +97 -41
  177. jolt-0.9.435.dist-info/RECORD +207 -0
  178. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/WHEEL +1 -1
  179. jolt/plugins/amqp.py +0 -855
  180. jolt/plugins/debian.py +0 -338
  181. jolt/plugins/repo.py +0 -253
  182. jolt/plugins/snap.py +0 -122
  183. jolt-0.9.172.dist-info/RECORD +0 -92
  184. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/entry_points.txt +0 -0
  185. {jolt-0.9.172.dist-info → jolt-0.9.435.dist-info}/top_level.txt +0 -0
jolt/plugins/scheduler.py CHANGED
@@ -2,6 +2,7 @@ import click
2
2
  import grpc
3
3
  import queue
4
4
  from threading import Lock
5
+ import time
5
6
 
6
7
  from google.protobuf.timestamp_pb2 import Timestamp
7
8
 
@@ -12,11 +13,10 @@ from jolt import config
12
13
  from jolt import hooks
13
14
  from jolt import loader
14
15
  from jolt import log
15
- from jolt import manifest
16
16
  from jolt import common_pb2 as common_pb
17
17
  from jolt import scheduler
18
18
  from jolt import utils
19
- from jolt.error import JoltError, raise_error, raise_error_if, raise_task_error, raise_task_error_if
19
+ from jolt.error import LoggedJoltError, JoltError, raise_error, raise_error_if, raise_task_error, raise_task_error_if
20
20
  from jolt.graph import GraphBuilder
21
21
  from jolt.scheduler import ExecutorRegistry, JoltEnvironment, NetworkExecutor, NetworkExecutorFactory, WorkerStrategy
22
22
  from jolt.tasks import TaskRegistry
@@ -81,7 +81,7 @@ class LogHandler(object):
81
81
  loglines=[
82
82
  common_pb.LogLine(
83
83
  context=self.task.task_id[:8],
84
- level=record.levelno,
84
+ level=log.level_to_pb(record.levelno),
85
85
  time=timestamp,
86
86
  message=record.message,
87
87
  ),
@@ -108,12 +108,17 @@ class Queue(object):
108
108
  def __next__(self):
109
109
  """ Get the next item from the queue. """
110
110
  data = self.q.get()
111
+ if data is None:
112
+ raise StopIteration
111
113
  return data
112
114
 
113
115
  def push(self, item):
114
116
  """ Push an item to the queue. """
115
117
  self.q.put(item)
116
118
 
119
+ def close(self):
120
+ self.q.put(None)
121
+
117
122
 
118
123
  class RemoteExecutor(NetworkExecutor):
119
124
  """
@@ -139,6 +144,15 @@ class RemoteExecutor(NetworkExecutor):
139
144
  self.session = session
140
145
  self.task = task
141
146
 
147
+ def schedule(self, env):
148
+ """
149
+ Schedule the task for execution.
150
+
151
+ The task is marked as in progress before scheduling.
152
+ """
153
+ self.task.set_in_progress()
154
+ return super().schedule(env)
155
+
142
156
  def cancel(self):
143
157
  """
144
158
  Cancel the build session.
@@ -169,12 +183,17 @@ class RemoteExecutor(NetworkExecutor):
169
183
  self.download_session_artifacts(extension)
170
184
  if not task.has_artifact():
171
185
  return
172
- if not task.cache.download_enabled():
186
+ if not task.cache.download_session_enabled():
173
187
  return
174
188
  if not task.is_downloadable():
175
189
  return
176
190
  if not task.download(session_only=True):
177
191
  task.warning("Failed to download session artifact")
192
+ if not task.is_resource():
193
+ # Tasks also download session artifacts of consumed resources
194
+ for resource in filter(lambda task: task.is_resource() and not task.is_workspace_resource(), task.children):
195
+ if not resource.is_available_locally(persistent_only=False):
196
+ self.download_session_artifacts(resource)
178
197
 
179
198
  def download_log(self, task):
180
199
  """ Download log and transfer lines into local logging system. """
@@ -184,7 +203,7 @@ class RemoteExecutor(NetworkExecutor):
184
203
  for response in self.session.logs.ReadLog(request):
185
204
  for line in response.loglines:
186
205
  log.log(
187
- line.level,
206
+ log.pb_to_level(line.level),
188
207
  line.message,
189
208
  created=line.time.ToMicroseconds() / 1000000,
190
209
  context=line.context[:7],
@@ -196,10 +215,19 @@ class RemoteExecutor(NetworkExecutor):
196
215
 
197
216
  def run(self, env):
198
217
  """ Run the task. """
218
+ if self.is_aborted():
219
+ return
199
220
  try:
200
- self.run_build(env)
201
- except grpc.RpcError as rpc_error:
202
- raise_task_error(self.task, "Scheduler error: {}", rpc_error.details())
221
+ with hooks.task_run([self.task] + self.task.extensions), self.task.run_resources():
222
+ try:
223
+ self.run_build(env)
224
+ except (grpc.RpcError, grpc._channel._MultiThreadedRendezvous) as rpc_error:
225
+ raise_task_error(self.task, rpc_error.details(), type="Scheduler Error")
226
+ except Exception as e:
227
+ if not self.task.is_unstable:
228
+ raise e
229
+ finally:
230
+ self.download_session_artifacts(self.task)
203
231
 
204
232
  @utils.retried.on_exception(grpc.RpcError)
205
233
  def run_build(self, env):
@@ -219,10 +247,7 @@ class RemoteExecutor(NetworkExecutor):
219
247
  response = self.session.exec.ScheduleTask(request)
220
248
 
221
249
  self.update_logstash(self.task)
222
-
223
- with hooks.task_run([self.task] + self.task.extensions):
224
- self.run_task(env, response)
225
-
250
+ self.run_task(env, response)
226
251
  self.download_persistent_artifacts(self.task)
227
252
 
228
253
  self.task.finished_execution(remote=True)
@@ -232,15 +257,23 @@ class RemoteExecutor(NetworkExecutor):
232
257
  except TaskCancelledException:
233
258
  pass
234
259
 
235
- except grpc.RpcError as rpc_error:
236
- if rpc_error.code() != grpc.StatusCode.UNAVAILABLE:
237
- raise_task_error(self.task, "Scheduler error: {}", rpc_error.details())
238
- log.warning("Scheduler error: {}", rpc_error.details())
239
- self.session.clear_build_request()
260
+ except (grpc.RpcError, grpc._channel._MultiThreadedRendezvous) as rpc_error:
261
+ if self.is_aborted():
262
+ if self.task.is_running():
263
+ self.task.failed_execution(remote=True, interrupt=True)
264
+ for extension in self.task.extensions:
265
+ extension.failed_execution(remote=True, interrupt=True)
266
+ return
267
+
268
+ if rpc_error.code() not in [grpc.StatusCode.NOT_FOUND, grpc.StatusCode.UNAVAILABLE]:
269
+ raise_task_error(self.task, rpc_error.details(), type="Scheduler Error")
270
+
271
+ self.session.clear_build_request(f"Scheduler Error: {rpc_error.details()}")
240
272
  raise rpc_error
241
273
 
242
274
  except Exception as e:
243
- log.exception()
275
+ if not isinstance(e, LoggedJoltError):
276
+ log.exception()
244
277
 
245
278
  if self.factory.options.mute:
246
279
  try:
@@ -251,11 +284,8 @@ class RemoteExecutor(NetworkExecutor):
251
284
  self.task.failed_execution(remote=True)
252
285
  for extension in self.task.extensions:
253
286
  extension.failed_execution(remote=True)
254
- if not self.task.is_unstable:
255
- raise e
256
287
 
257
- finally:
258
- self.download_session_artifacts(self.task)
288
+ raise e
259
289
 
260
290
  def run_task(self, env, response):
261
291
  """ Run the task.
@@ -276,18 +306,27 @@ class RemoteExecutor(NetworkExecutor):
276
306
  for progress in response:
277
307
  for line in progress.loglines:
278
308
  log.log(
279
- line.level,
309
+ log.pb_to_level(line.level),
280
310
  line.message,
281
311
  created=line.time.ToMicroseconds() / 1000000,
282
312
  context=line.context[:7],
283
313
  prefix=True)
284
314
 
315
+ if progress.worker:
316
+ self.task.worker = progress.worker.hostname
317
+
285
318
  if progress.status in [common_pb.TaskStatus.TASK_RUNNING] \
286
319
  and progress.status != self.task.status():
287
320
  self.task.running_execution(remote=True)
288
321
  for extension in self.task.extensions:
289
322
  extension.running_execution(remote=True)
290
323
 
324
+ if progress.status in [common_pb.TaskStatus.TASK_QUEUED]:
325
+ if last_status in [common_pb.TaskStatus.TASK_RUNNING]:
326
+ self.task.restarted_execution(remote=True)
327
+ for extension in self.task.extensions:
328
+ extension.restarted_execution(remote=True)
329
+
291
330
  if progress.status in [
292
331
  common_pb.TaskStatus.TASK_PASSED,
293
332
  common_pb.TaskStatus.TASK_DOWNLOADED,
@@ -296,14 +335,17 @@ class RemoteExecutor(NetworkExecutor):
296
335
  ]:
297
336
  break
298
337
 
338
+ if progress.status in [common_pb.TaskStatus.TASK_CANCELLED]:
339
+ if last_status in [common_pb.TaskStatus.TASK_RUNNING]:
340
+ self.task.failed_execution(remote=True, interrupt=True)
341
+ for extension in self.task.extensions:
342
+ extension.failed_execution(remote=True, interrupt=True)
343
+ raise TaskCancelledException()
344
+
299
345
  if progress.status in [
300
- common_pb.TaskStatus.TASK_CANCELLED,
301
346
  common_pb.TaskStatus.TASK_FAILED,
302
347
  common_pb.TaskStatus.TASK_UNSTABLE,
303
348
  ]:
304
- if last_status in [common_pb.TaskStatus.TASK_QUEUED]:
305
- raise TaskCancelledException()
306
-
307
349
  for error in progress.errors:
308
350
  with self.task.task.report() as report:
309
351
  report.add_error(
@@ -312,14 +354,19 @@ class RemoteExecutor(NetworkExecutor):
312
354
  error.message,
313
355
  error.details,
314
356
  )
315
- with self.task.task.report() as report:
316
- for error in report.errors:
317
- raise_error(f"{error.type}: {error.message}")
357
+ self.task.raise_for_status()
318
358
  raise raise_error("Remote execution failed")
319
359
 
320
360
  if progress.status in [
321
361
  common_pb.TaskStatus.TASK_ERROR,
322
362
  ]:
363
+ log.log(
364
+ log.VERBOSE,
365
+ f"Host: {progress.worker.hostname}",
366
+ created=time.time(),
367
+ context=self.task.identity[:7],
368
+ prefix=True)
369
+
323
370
  for error in progress.errors:
324
371
  with self.task.task.report() as report:
325
372
  report.add_error(
@@ -328,11 +375,7 @@ class RemoteExecutor(NetworkExecutor):
328
375
  error.message,
329
376
  error.details,
330
377
  )
331
- with self.task.task.report() as report:
332
- for error in report.errors:
333
- for line in error.details.splitlines():
334
- log.error(line)
335
- raise_error(f"{error.type}: {error.message}")
378
+ self.task.raise_for_status(log_details=not self.factory.options.mute)
336
379
  raise raise_error("Remote execution failed")
337
380
 
338
381
  last_status = progress.status
@@ -384,31 +427,16 @@ class RemoteSession(object):
384
427
  # Lock to ensure only one build is registered at a time.
385
428
  self.lock = Lock()
386
429
 
430
+ # The build environment: client, workspace, etc.
431
+ self.buildenv = None
432
+
387
433
  def initialize(self, graph):
388
434
  """ Initialize the session with the scheduler. """
389
-
390
- registry = ExecutorRegistry.get()
391
-
392
- # Create a list of parameters to send to the scheduler.
393
- parameters = []
394
- for key, value in registry.get_network_parameters(None).items():
395
- parameters.append(common_pb.Property(key=key, value=value))
396
-
397
- # Add parameters from the config / command line (-c params.key).
398
- parameters.extend(config.export_params())
399
-
400
- # Create the build environment.
401
- self.buildenv = common_pb.BuildEnvironment(
402
- client=selfdeploy.get_client(),
403
- parameters=parameters,
404
- task_default_parameters=scheduler.export_task_default_params(graph.tasks),
405
- tasks=scheduler.export_tasks(graph.tasks + graph.pruned),
406
- workspace=loader.export_workspace(graph.tasks),
407
- loglevel=log.get_level(),
408
- config=config.export_config(),
409
- )
435
+ self.tasks = graph.tasks
436
+ self.pruned = graph.pruned
410
437
 
411
438
  @locked
439
+ @utils.retried.on_exception(grpc.RpcError)
412
440
  def make_build_request(self):
413
441
  """ Create a build request with the scheduler. """
414
442
 
@@ -416,6 +444,18 @@ class RemoteSession(object):
416
444
  if self.build:
417
445
  return
418
446
 
447
+ if not self.buildenv:
448
+ # Create the build environment.
449
+ self.buildenv = common_pb.BuildEnvironment(
450
+ client=selfdeploy.get_client(),
451
+ parameters=config.export_params(),
452
+ task_default_parameters=scheduler.export_task_default_params(self.tasks),
453
+ tasks=scheduler.export_tasks(self.tasks + self.pruned),
454
+ workspace=loader.export_workspace(self.tasks),
455
+ loglevel=log.get_level_pb(),
456
+ config=config.export_config(),
457
+ )
458
+
419
459
  # Create the build request.
420
460
  req = scheduler_pb.BuildRequest(
421
461
  environment=self.buildenv,
@@ -439,12 +479,15 @@ class RemoteSession(object):
439
479
  log.info(colors.blue("Build registered with scheduler, waiting for worker"))
440
480
  return self.build
441
481
 
442
- def clear_build_request(self):
482
+ @locked
483
+ def clear_build_request(self, message=None):
443
484
  """ Clear the build request. Called when a build fails. """
444
485
 
445
486
  # Close grpc server response stream
446
487
  if self.build:
447
488
  self.build.cancel()
489
+ if message:
490
+ log.warning(message)
448
491
  self.build = None
449
492
  self.build_id = None
450
493
 
@@ -510,7 +553,7 @@ class RemoteExecutionFactory(NetworkExecutorFactory):
510
553
  log.verbose("[Remote] Loaded")
511
554
 
512
555
 
513
- @cli.cli.command()
556
+ @cli.cli.command(hidden=True)
514
557
  @click.option("-w", "--worker", required=True, help="Worker identifier.")
515
558
  @click.option("-b", "--build", required=True, help="Build identifier to enlist for.")
516
559
  @click.argument("request", required=True)
@@ -533,13 +576,19 @@ def executor(ctx, worker, build, request):
533
576
 
534
577
  # Set log level
535
578
  loglevel = request.environment.loglevel
536
- log.set_level(loglevel)
579
+ log.set_level_pb(loglevel)
580
+
581
+ # Import workspace
582
+ loader.import_workspace(request.environment)
537
583
 
538
- # Import protobuf build description
539
- manifest.ManifestExtensionRegistry.import_protobuf(request.environment)
584
+ # Import configuration snippet
585
+ config.import_config(request.environment.config)
586
+
587
+ # Import configuration parameters (-c params.key)
588
+ config.import_params({param.key: param.value for param in request.environment.parameters})
540
589
 
541
590
  options = JoltOptions(
542
- network=False,
591
+ network=True,
543
592
  local=False,
544
593
  download=config.getboolean("network", "download", True),
545
594
  upload=config.getboolean("network", "upload", True),
@@ -569,12 +618,13 @@ def executor(ctx, worker, build, request):
569
618
 
570
619
  # Build the graph of tasks
571
620
  gb = GraphBuilder(registry, acache, options=options, progress=True, buildenv=request.environment)
572
- dag = gb.build(request.environment.tasks.keys())
621
+ task_names = [task.name for task in request.environment.tasks.values()]
622
+ dag = gb.build(task_names)
573
623
 
574
624
  # Enlist to execute build tasks from the scheduler
575
625
  enlist_msg = scheduler_pb.TaskUpdate(
576
626
  request=scheduler_pb.TaskRequest(build_id=build),
577
- worker=scheduler_pb.WorkerAllocation(id=worker),
627
+ worker=scheduler_pb.WorkerAllocation(id=worker, hostname=utils.hostname()),
578
628
  )
579
629
 
580
630
  # A queue to send updates to the scheduler
@@ -586,11 +636,12 @@ def executor(ctx, worker, build, request):
586
636
 
587
637
  # Subscribe to tasks
588
638
  for task in sched.GetTasks(updates):
589
- log.set_level(loglevel)
639
+ log.set_level_pb(loglevel)
590
640
 
591
641
  log.info("Queuing {}", task.task_id)
592
642
  graph_task = dag.get_task_by_identity(task.task_id)
593
643
  executor = None
644
+ status = None
594
645
 
595
646
  try:
596
647
  session = {}
@@ -600,27 +651,30 @@ def executor(ctx, worker, build, request):
600
651
 
601
652
  # Run the task
602
653
  with log.handler(LogHandler(updates, task)):
603
- executor.run(JoltEnvironment(cache=acache))
604
-
605
- except KeyboardInterrupt as interrupt:
606
- log.info("Task cancelled")
654
+ executor.run(JoltEnvironment(cache=acache, queue=None, worker=True))
607
655
 
656
+ except KeyboardInterrupt:
608
657
  # Send an update to the scheduler
609
658
  update = scheduler_pb.TaskUpdate(
610
659
  request=task,
611
660
  status=common_pb.TaskStatus.TASK_CANCELLED,
612
661
  )
613
662
  updates.push(update)
663
+ continue
614
664
 
615
- # Interrupt sent to process, not only the running task
616
- raise interrupt
665
+ except Exception:
666
+ status = common_pb.TaskStatus.TASK_FAILED
617
667
 
618
- except Exception as e:
619
- log.set_level(log.EXCEPTION)
620
- log.exception(e)
668
+ else:
669
+ status = graph_task.status()
621
670
 
671
+ finally:
622
672
  errors = []
623
673
 
674
+ # If the task status remains queued, mark it as failed
675
+ if status in [common_pb.TaskStatus.TASK_QUEUED]:
676
+ status = common_pb.TaskStatus.TASK_FAILED
677
+
624
678
  # Add errors from the task to the update sent to the scheduler
625
679
  with graph_task.task.report() as report:
626
680
  for error in report.errors:
@@ -634,24 +688,19 @@ def executor(ctx, worker, build, request):
634
688
  # Send an update to the scheduler
635
689
  update = scheduler_pb.TaskUpdate(
636
690
  request=task,
637
- status=common_pb.TaskStatus.TASK_FAILED,
691
+ status=status,
638
692
  errors=errors,
639
693
  )
640
694
  updates.push(update)
641
695
 
642
- else:
643
- status = graph_task.status()
696
+ # Release references to cache artifacts
697
+ acache.release()
644
698
 
645
- # If the task status remains queued, mark it as failed
646
- if status in [common_pb.TaskStatus.TASK_QUEUED]:
647
- status = common_pb.TaskStatus.TASK_FAILED
699
+ except grpc.RpcError as rpc_error:
700
+ log.warning("Scheduler Error: {}", rpc_error.details())
648
701
 
649
- # Send an update to the scheduler
650
- update = scheduler_pb.TaskUpdate(
651
- request=task,
652
- status=status,
653
- )
654
- updates.push(update)
702
+ except KeyboardInterrupt:
703
+ log.info("Interrupted, exiting")
655
704
 
656
705
  except Exception as e:
657
706
  log.set_level(log.EXCEPTION)
@@ -74,32 +74,32 @@ setup(
74
74
  "click>=8.1",
75
75
  "colorama",
76
76
  "fasteners",
77
- "grpcio",
77
+ "grpcio>=1.62.2",
78
78
  "jinja2",
79
79
  "keyring",
80
80
  "keyrings.alt",
81
81
  "importlib_metadata",
82
82
  "lxml",
83
83
  "multi_key_dict",
84
- "ninja-syntax",
84
+ "ninja",
85
85
  "protobuf",
86
86
  "psutil",
87
87
  "pygit2",
88
+ "py7zr",
88
89
  "requests",
90
+ "zstandard",
89
91
  "tqdm",
90
- "unshare",
91
92
  ],
92
93
  dependency_links=[],
93
94
  extras_require={
94
95
  "allure": ["allure-python-commons"],
95
- "amqp": ["pika"],
96
- "conan": ["conan<2.0"],
96
+ "conan": ["conan>=2.0"],
97
97
  "dev": ["check-manifest"],
98
98
  "doc": ["sphinx-click", "sphinx-rtd-theme"],
99
99
  "test": ["coverage"],
100
100
  },
101
101
  package_data={
102
- "jolt": ["**/*.sh", "**/*.xslt", "**/*.template"],
102
+ "jolt": ["**/*.sh", "**/*.xslt", "**/*.template", "**/fstree-*-x86_64"],
103
103
  },
104
104
  entry_points={
105
105
  "console_scripts": [
@@ -1,4 +1,5 @@
1
- import pkg_resources
1
+ import importlib_metadata
2
+ import os
2
3
 
3
4
  from jolt import config
4
5
  from jolt import filesystem as fs
@@ -11,12 +12,9 @@ from jolt.cache import ArtifactCache
11
12
  from jolt.error import raise_error_if
12
13
  from jolt.graph import GraphBuilder
13
14
  from jolt.loader import JoltLoader
14
- from jolt.manifest import JoltManifest
15
15
  from jolt.scheduler import JoltEnvironment
16
16
  from jolt.scheduler import LocalExecutor
17
17
  from jolt.scheduler import LocalExecutorFactory
18
- from jolt.scheduler import NetworkExecutorExtension
19
- from jolt.scheduler import NetworkExecutorExtensionFactory
20
18
  from jolt.tasks import Task, TaskRegistry
21
19
 
22
20
 
@@ -90,6 +88,7 @@ class Jolt(Task):
90
88
  artifact.collect('jolt/*/*/*.py')
91
89
  artifact.collect('jolt/*/*.xslt')
92
90
  artifact.collect('jolt/*/*.template')
91
+ artifact.collect('jolt/bin')
93
92
  artifact.collect('jolt/plugins/selfdeploy/README.rst', flatten=True)
94
93
  artifact.collect('jolt/plugins/selfdeploy/setup.py', flatten=True)
95
94
  for e in self.extra_files:
@@ -97,43 +96,37 @@ class Jolt(Task):
97
96
  artifact.collect(fs.path.basename(e))
98
97
 
99
98
 
100
- class SelfDeployExtension(NetworkExecutorExtension):
101
- @utils.cached.instance
102
- def get_parameters(self, _):
103
- client = get_client()
104
- params = {}
105
- if client.identity:
106
- params["jolt_identity"] = client.identity
107
- if client.url:
108
- params["jolt_url"] = client.url
109
- return params
110
-
111
-
112
- @NetworkExecutorExtensionFactory.Register
113
- class SelfDeployExtensionFactory(NetworkExecutorExtensionFactory):
114
- def create(self):
115
- return SelfDeployExtension()
116
-
117
-
118
99
  @utils.cached.method
119
100
  def get_dependencies(packages=None):
120
101
  reqs = packages or ["jolt"]
121
102
  pkgs = {}
103
+ skip = set()
122
104
 
123
105
  while reqs:
124
106
  req = reqs.pop()
125
107
 
126
- dist = pkg_resources.working_set.by_key.get(req)
108
+ try:
109
+ dist = importlib_metadata.distribution(req)
110
+ except (ImportError, importlib_metadata.PackageNotFoundError):
111
+ dist = None
112
+ except Exception:
113
+ dist = None
127
114
  if dist is None:
128
- log.debug("[SelfDeploy] Dependency not found: {}", req)
129
- pkgs[req] = req
115
+ skip.add(req)
130
116
  continue
131
117
 
132
- for dep in dist.requires():
133
- if dep.name not in pkgs:
134
- reqs.append(dep.name)
118
+ for dep in dist.requires or []:
119
+ dep = dep.split(" ", 1)[0].strip()
120
+ dep = dep.split("[", 1)[0].strip()
121
+ dep = dep.split(";", 1)[0].strip()
122
+ dep = dep.split(">", 1)[0].strip()
123
+ dep = dep.split("=", 1)[0].strip()
124
+ dep = dep.split("<", 1)[0].strip()
125
+ dep = dep.split("!", 1)[0].strip()
126
+ if dep not in pkgs and dep not in skip:
127
+ reqs.append(dep)
135
128
 
136
- pkgs[req] = f"{dist.project_name}=={dist.version}"
129
+ pkgs[req] = f"{dist.name}=={dist.version}"
137
130
 
138
131
  try:
139
132
  del pkgs["jolt"]
@@ -154,8 +147,8 @@ def publish_artifact():
154
147
  registry = TaskRegistry()
155
148
  registry.add_task_class(Jolt)
156
149
  acache = ArtifactCache.get()
157
- env = JoltEnvironment(cache=acache)
158
- gb = GraphBuilder(registry, acache, JoltManifest())
150
+ env = JoltEnvironment(cache=acache, queue=None)
151
+ gb = GraphBuilder(registry, acache)
159
152
  dag = gb.build(["jolt"])
160
153
  task = dag.select(lambda graph, task: True)
161
154
  assert len(task) == 1, "Too many selfdeploy tasks found"
@@ -189,8 +182,33 @@ def get_pinned_version():
189
182
  )
190
183
 
191
184
 
185
+ def get_nix_version():
186
+ return common_pb.Client(
187
+ requirements=get_extra_dependencies(),
188
+ version=version.__version__,
189
+ nix=True,
190
+ )
191
+
192
+
192
193
  def get_client():
194
+ # Floating version is a special case where we want to deploy the Jolt
195
+ # source code to a remote cache and use that URL as the client URL
196
+ # for workers.
193
197
  floating = config.getboolean("selfdeploy", "floating", False)
194
198
  if floating:
195
199
  return get_floating_version()
200
+
201
+ # If Nix has been explicitly disabled, we want to pin versions.
202
+ if not config.getboolean("selfdeploy", "nix", True):
203
+ return get_pinned_version()
204
+
205
+ # If Nix has been explicitly enabled, we want to use the Nix shell.
206
+ if config.getboolean("selfdeploy", "nix", False):
207
+ return get_nix_version()
208
+
209
+ # If we are in a Nix shell, we want to use the Nix shell.
210
+ if os.environ.get("IN_NIX_SHELL"):
211
+ return get_nix_version()
212
+
213
+ # Default to pinned version
196
214
  return get_pinned_version()