jolt 0.9.123__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 (196) 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 +832 -362
  6. jolt/chroot.py +156 -0
  7. jolt/cli.py +281 -162
  8. jolt/common_pb2.py +63 -0
  9. jolt/common_pb2_grpc.py +4 -0
  10. jolt/config.py +98 -41
  11. jolt/error.py +19 -4
  12. jolt/filesystem.py +2 -6
  13. jolt/graph.py +705 -117
  14. jolt/hooks.py +43 -0
  15. jolt/influence.py +122 -3
  16. jolt/loader.py +369 -121
  17. jolt/log.py +225 -63
  18. jolt/manifest.py +28 -38
  19. jolt/options.py +35 -10
  20. jolt/pkgs/abseil.py +42 -0
  21. jolt/pkgs/asio.py +25 -0
  22. jolt/pkgs/autoconf.py +41 -0
  23. jolt/pkgs/automake.py +41 -0
  24. jolt/pkgs/b2.py +31 -0
  25. jolt/pkgs/boost.py +111 -0
  26. jolt/pkgs/boringssl.py +32 -0
  27. jolt/pkgs/busybox.py +39 -0
  28. jolt/pkgs/bzip2.py +43 -0
  29. jolt/pkgs/cares.py +29 -0
  30. jolt/pkgs/catch2.py +36 -0
  31. jolt/pkgs/cbindgen.py +17 -0
  32. jolt/pkgs/cista.py +19 -0
  33. jolt/pkgs/clang.py +44 -0
  34. jolt/pkgs/cli11.py +24 -0
  35. jolt/pkgs/cmake.py +48 -0
  36. jolt/pkgs/cpython.py +196 -0
  37. jolt/pkgs/crun.py +29 -0
  38. jolt/pkgs/curl.py +38 -0
  39. jolt/pkgs/dbus.py +18 -0
  40. jolt/pkgs/double_conversion.py +24 -0
  41. jolt/pkgs/fastfloat.py +21 -0
  42. jolt/pkgs/ffmpeg.py +28 -0
  43. jolt/pkgs/flatbuffers.py +29 -0
  44. jolt/pkgs/fmt.py +27 -0
  45. jolt/pkgs/fstree.py +20 -0
  46. jolt/pkgs/gflags.py +18 -0
  47. jolt/pkgs/glib.py +18 -0
  48. jolt/pkgs/glog.py +25 -0
  49. jolt/pkgs/glslang.py +21 -0
  50. jolt/pkgs/golang.py +16 -11
  51. jolt/pkgs/googlebenchmark.py +18 -0
  52. jolt/pkgs/googletest.py +46 -0
  53. jolt/pkgs/gperf.py +15 -0
  54. jolt/pkgs/grpc.py +73 -0
  55. jolt/pkgs/hdf5.py +19 -0
  56. jolt/pkgs/help2man.py +14 -0
  57. jolt/pkgs/inja.py +28 -0
  58. jolt/pkgs/jsoncpp.py +31 -0
  59. jolt/pkgs/libarchive.py +43 -0
  60. jolt/pkgs/libcap.py +44 -0
  61. jolt/pkgs/libdrm.py +44 -0
  62. jolt/pkgs/libedit.py +42 -0
  63. jolt/pkgs/libevent.py +31 -0
  64. jolt/pkgs/libexpat.py +27 -0
  65. jolt/pkgs/libfastjson.py +21 -0
  66. jolt/pkgs/libffi.py +16 -0
  67. jolt/pkgs/libglvnd.py +30 -0
  68. jolt/pkgs/libogg.py +28 -0
  69. jolt/pkgs/libpciaccess.py +18 -0
  70. jolt/pkgs/libseccomp.py +21 -0
  71. jolt/pkgs/libtirpc.py +24 -0
  72. jolt/pkgs/libtool.py +42 -0
  73. jolt/pkgs/libunwind.py +35 -0
  74. jolt/pkgs/libva.py +18 -0
  75. jolt/pkgs/libvorbis.py +33 -0
  76. jolt/pkgs/libxml2.py +35 -0
  77. jolt/pkgs/libxslt.py +17 -0
  78. jolt/pkgs/libyajl.py +16 -0
  79. jolt/pkgs/llvm.py +81 -0
  80. jolt/pkgs/lua.py +54 -0
  81. jolt/pkgs/lz4.py +26 -0
  82. jolt/pkgs/m4.py +14 -0
  83. jolt/pkgs/make.py +17 -0
  84. jolt/pkgs/mesa.py +81 -0
  85. jolt/pkgs/meson.py +17 -0
  86. jolt/pkgs/mstch.py +28 -0
  87. jolt/pkgs/mysql.py +60 -0
  88. jolt/pkgs/nasm.py +49 -0
  89. jolt/pkgs/ncurses.py +30 -0
  90. jolt/pkgs/ng_log.py +25 -0
  91. jolt/pkgs/ninja.py +45 -0
  92. jolt/pkgs/nlohmann_json.py +25 -0
  93. jolt/pkgs/nodejs.py +19 -11
  94. jolt/pkgs/opencv.py +24 -0
  95. jolt/pkgs/openjdk.py +26 -0
  96. jolt/pkgs/openssl.py +103 -0
  97. jolt/pkgs/paho.py +76 -0
  98. jolt/pkgs/patchelf.py +16 -0
  99. jolt/pkgs/perl.py +42 -0
  100. jolt/pkgs/pkgconfig.py +64 -0
  101. jolt/pkgs/poco.py +39 -0
  102. jolt/pkgs/protobuf.py +77 -0
  103. jolt/pkgs/pugixml.py +27 -0
  104. jolt/pkgs/python.py +19 -0
  105. jolt/pkgs/qt.py +35 -0
  106. jolt/pkgs/rapidjson.py +26 -0
  107. jolt/pkgs/rapidyaml.py +28 -0
  108. jolt/pkgs/re2.py +30 -0
  109. jolt/pkgs/re2c.py +17 -0
  110. jolt/pkgs/readline.py +15 -0
  111. jolt/pkgs/rust.py +41 -0
  112. jolt/pkgs/sdl.py +28 -0
  113. jolt/pkgs/simdjson.py +27 -0
  114. jolt/pkgs/soci.py +46 -0
  115. jolt/pkgs/spdlog.py +29 -0
  116. jolt/pkgs/spirv_llvm.py +21 -0
  117. jolt/pkgs/spirv_tools.py +24 -0
  118. jolt/pkgs/sqlite.py +83 -0
  119. jolt/pkgs/ssl.py +12 -0
  120. jolt/pkgs/texinfo.py +15 -0
  121. jolt/pkgs/tomlplusplus.py +22 -0
  122. jolt/pkgs/wayland.py +26 -0
  123. jolt/pkgs/x11.py +58 -0
  124. jolt/pkgs/xerces_c.py +20 -0
  125. jolt/pkgs/xorg.py +360 -0
  126. jolt/pkgs/xz.py +29 -0
  127. jolt/pkgs/yamlcpp.py +30 -0
  128. jolt/pkgs/zeromq.py +47 -0
  129. jolt/pkgs/zlib.py +87 -0
  130. jolt/pkgs/zstd.py +33 -0
  131. jolt/plugins/alias.py +3 -0
  132. jolt/plugins/allure.py +5 -2
  133. jolt/plugins/autotools.py +66 -0
  134. jolt/plugins/cache.py +133 -0
  135. jolt/plugins/cmake.py +74 -6
  136. jolt/plugins/conan.py +238 -0
  137. jolt/plugins/cxx.py +698 -0
  138. jolt/plugins/cxxinfo.py +7 -0
  139. jolt/plugins/dashboard.py +1 -1
  140. jolt/plugins/docker.py +80 -23
  141. jolt/plugins/email.py +2 -2
  142. jolt/plugins/email.xslt +144 -101
  143. jolt/plugins/environ.py +11 -0
  144. jolt/plugins/fetch.py +141 -0
  145. jolt/plugins/gdb.py +39 -19
  146. jolt/plugins/gerrit.py +1 -14
  147. jolt/plugins/git.py +283 -85
  148. jolt/plugins/googletest.py +2 -1
  149. jolt/plugins/http.py +36 -38
  150. jolt/plugins/libtool.py +63 -0
  151. jolt/plugins/linux.py +990 -0
  152. jolt/plugins/logstash.py +4 -4
  153. jolt/plugins/meson.py +61 -0
  154. jolt/plugins/ninja-compdb.py +99 -30
  155. jolt/plugins/ninja.py +468 -166
  156. jolt/plugins/paths.py +11 -1
  157. jolt/plugins/pkgconfig.py +219 -0
  158. jolt/plugins/podman.py +136 -92
  159. jolt/plugins/python.py +137 -0
  160. jolt/plugins/remote_execution/__init__.py +0 -0
  161. jolt/plugins/remote_execution/administration_pb2.py +46 -0
  162. jolt/plugins/remote_execution/administration_pb2_grpc.py +170 -0
  163. jolt/plugins/remote_execution/log_pb2.py +32 -0
  164. jolt/plugins/remote_execution/log_pb2_grpc.py +68 -0
  165. jolt/plugins/remote_execution/scheduler_pb2.py +41 -0
  166. jolt/plugins/remote_execution/scheduler_pb2_grpc.py +141 -0
  167. jolt/plugins/remote_execution/worker_pb2.py +38 -0
  168. jolt/plugins/remote_execution/worker_pb2_grpc.py +112 -0
  169. jolt/plugins/report.py +12 -2
  170. jolt/plugins/rust.py +25 -0
  171. jolt/plugins/scheduler.py +710 -0
  172. jolt/plugins/selfdeploy/setup.py +8 -4
  173. jolt/plugins/selfdeploy.py +138 -88
  174. jolt/plugins/strings.py +35 -22
  175. jolt/plugins/symlinks.py +26 -11
  176. jolt/plugins/telemetry.py +5 -2
  177. jolt/plugins/timeline.py +13 -3
  178. jolt/plugins/volume.py +46 -48
  179. jolt/scheduler.py +589 -192
  180. jolt/tasks.py +625 -121
  181. jolt/templates/timeline.html.template +44 -47
  182. jolt/timer.py +22 -0
  183. jolt/tools.py +638 -282
  184. jolt/utils.py +211 -7
  185. jolt/version.py +1 -1
  186. jolt/xmldom.py +12 -2
  187. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/METADATA +97 -38
  188. jolt-0.9.435.dist-info/RECORD +207 -0
  189. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/WHEEL +1 -1
  190. jolt/plugins/amqp.py +0 -834
  191. jolt/plugins/debian.py +0 -338
  192. jolt/plugins/ftp.py +0 -181
  193. jolt/plugins/repo.py +0 -253
  194. jolt-0.9.123.dist-info/RECORD +0 -77
  195. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/entry_points.txt +0 -0
  196. {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/top_level.txt +0 -0
@@ -74,28 +74,32 @@ setup(
74
74
  "click>=8.1",
75
75
  "colorama",
76
76
  "fasteners",
77
+ "grpcio>=1.62.2",
77
78
  "jinja2",
78
79
  "keyring",
79
80
  "keyrings.alt",
81
+ "importlib_metadata",
80
82
  "lxml",
81
83
  "multi_key_dict",
82
- "ninja-syntax",
84
+ "ninja",
85
+ "protobuf",
83
86
  "psutil",
84
87
  "pygit2",
88
+ "py7zr",
85
89
  "requests",
90
+ "zstandard",
86
91
  "tqdm",
87
92
  ],
88
93
  dependency_links=[],
89
94
  extras_require={
90
95
  "allure": ["allure-python-commons"],
91
- "amqp": ["pika"],
92
- "conan": ["conan<2.0"],
96
+ "conan": ["conan>=2.0"],
93
97
  "dev": ["check-manifest"],
94
98
  "doc": ["sphinx-click", "sphinx-rtd-theme"],
95
99
  "test": ["coverage"],
96
100
  },
97
101
  package_data={
98
- "jolt": ["**/*.sh", "**/*.xslt", "**/*.template"],
102
+ "jolt": ["**/*.sh", "**/*.xslt", "**/*.template", "**/fstree-*-x86_64"],
99
103
  },
100
104
  entry_points={
101
105
  "console_scripts": [
@@ -1,19 +1,21 @@
1
- from jolt.tasks import Task, TaskRegistry
2
- from jolt.cache import ArtifactCache
3
- from jolt.graph import GraphBuilder
4
- from jolt.error import raise_error_if
5
- from jolt.manifest import JoltManifest
6
- from jolt.scheduler import JoltEnvironment
7
- from jolt.scheduler import LocalExecutor
8
- from jolt.scheduler import LocalExecutorFactory
9
- from jolt.scheduler import NetworkExecutorExtension
10
- from jolt.scheduler import NetworkExecutorExtensionFactory
11
- from jolt.loader import JoltLoader
1
+ import importlib_metadata
2
+ import os
3
+
12
4
  from jolt import config
13
5
  from jolt import filesystem as fs
14
6
  from jolt import influence
15
7
  from jolt import log
8
+ from jolt import common_pb2 as common_pb
16
9
  from jolt import utils
10
+ from jolt import version
11
+ from jolt.cache import ArtifactCache
12
+ from jolt.error import raise_error_if
13
+ from jolt.graph import GraphBuilder
14
+ from jolt.loader import JoltLoader
15
+ from jolt.scheduler import JoltEnvironment
16
+ from jolt.scheduler import LocalExecutor
17
+ from jolt.scheduler import LocalExecutorFactory
18
+ from jolt.tasks import Task, TaskRegistry
17
19
 
18
20
 
19
21
  log.verbose("[SelfDeploy] Loaded")
@@ -27,6 +29,8 @@ _path = fs.path.dirname(_path)
27
29
  @influence.files(fs.path.join(_path, "**", "*.sh"))
28
30
  @influence.files(fs.path.join(_path, "**", "*.xslt"))
29
31
  @influence.files(fs.path.join(_path, "**", "*.template"))
32
+ @influence.attribute("dependencies")
33
+ @influence.attribute("extra_dependencies")
30
34
  class Jolt(Task):
31
35
  name = "jolt"
32
36
 
@@ -52,11 +56,7 @@ class Jolt(Task):
52
56
 
53
57
  @property
54
58
  def extra_dependencies(self):
55
- req = config.get("selfdeploy", "requires", "")
56
- return req.split() if req else []
57
-
58
- def info(self, fmt, *args, **kwargs):
59
- log.verbose(fmt, *args, **kwargs)
59
+ return get_extra_dependencies()
60
60
 
61
61
  @property
62
62
  def dependencies(self):
@@ -71,52 +71,14 @@ class Jolt(Task):
71
71
  no version pinning will be performed. Instead, workers will install
72
72
  Jolt with its default loose version requirements.
73
73
  """
74
- def get_installed_distributions():
75
- try:
76
- from pip._internal.metadata import get_environment
77
- except ImportError:
78
- from pip._internal.utils import misc
79
- return {
80
- dist.project_name.lower(): dist
81
- for dist in misc.get_installed_distributions()
82
- }
83
- else:
84
- dists = get_environment(None).iter_installed_distributions()
85
- return {dist._dist.project_name.lower(): dist._dist for dist in dists}
86
-
87
- dists = get_installed_distributions()
88
- reqs = ["jolt"] + [dep.lower() for dep in self.extra_dependencies]
89
- pkgs = {}
90
-
91
- while reqs:
92
- req = reqs.pop()
93
-
94
- dist = dists.get(req)
95
- if dist is None:
96
- self.info("[SelfDeploy] Dependency not found: {}", req)
97
- req = req.partition("=")[0].partition("<")[0].partition(">")[0]
98
- pkgs[req] = req
99
- continue
100
-
101
- for dep in dist.requires():
102
- name = dep.project_name.lower()
103
- if name not in pkgs:
104
- reqs.append(name)
105
-
106
- pkgs[req] = f"{dist.project_name}=={dist.version}"
107
-
108
- del pkgs["jolt"]
109
- return pkgs.values()
74
+ return get_dependencies(["jolt"] + self.extra_dependencies)
110
75
 
111
76
  def publish(self, artifact, tools):
112
77
  with tools.cwd(tools.builddir()):
113
- try:
114
- pinned_reqs = self.dependencies
115
- if pinned_reqs:
116
- tools.write_file("requirements.txt", "\n".join(pinned_reqs))
117
- artifact.collect("requirements.txt")
118
- except Exception:
119
- log.exception()
78
+ pinned_reqs = self.dependencies
79
+ if pinned_reqs:
80
+ tools.write_file("requirements.txt", "\n".join(pinned_reqs))
81
+ artifact.collect("requirements.txt")
120
82
  with tools.cwd(_path):
121
83
  artifact.collect('README.rst')
122
84
  artifact.collect('setup.py')
@@ -126,6 +88,7 @@ class Jolt(Task):
126
88
  artifact.collect('jolt/*/*/*.py')
127
89
  artifact.collect('jolt/*/*.xslt')
128
90
  artifact.collect('jolt/*/*.template')
91
+ artifact.collect('jolt/bin')
129
92
  artifact.collect('jolt/plugins/selfdeploy/README.rst', flatten=True)
130
93
  artifact.collect('jolt/plugins/selfdeploy/setup.py', flatten=True)
131
94
  for e in self.extra_files:
@@ -133,32 +96,119 @@ class Jolt(Task):
133
96
  artifact.collect(fs.path.basename(e))
134
97
 
135
98
 
136
- class SelfDeployExtension(NetworkExecutorExtension):
137
- @utils.cached.instance
138
- def get_parameters(self, task):
139
- registry = TaskRegistry()
140
- registry.add_task_class(Jolt)
141
- acache = ArtifactCache.get()
142
- env = JoltEnvironment(cache=acache)
143
- gb = GraphBuilder(registry, JoltManifest())
144
- dag = gb.build(["jolt"])
145
- task = dag.select(lambda graph, task: True)
146
- assert len(task) == 1, "too many selfdeploy tasks found"
147
- task = task[0]
148
- if not acache.is_available_remotely(task):
149
- factory = LocalExecutorFactory()
150
- executor = LocalExecutor(factory, task, force_upload=True)
151
- executor.run(env)
152
- jolt_url = acache.location(task)
153
- raise_error_if(not jolt_url, "failed to deploy jolt to a remote cache")
154
- return {
155
- "jolt_url": jolt_url,
156
- "jolt_identity": task.identity[:8],
157
- "jolt_requires": config.get("selfdeploy", "requires", "")
158
- }
159
-
160
-
161
- @NetworkExecutorExtensionFactory.Register
162
- class SelfDeployExtensionFactory(NetworkExecutorExtensionFactory):
163
- def create(self):
164
- return SelfDeployExtension()
99
+ @utils.cached.method
100
+ def get_dependencies(packages=None):
101
+ reqs = packages or ["jolt"]
102
+ pkgs = {}
103
+ skip = set()
104
+
105
+ while reqs:
106
+ req = reqs.pop()
107
+
108
+ try:
109
+ dist = importlib_metadata.distribution(req)
110
+ except (ImportError, importlib_metadata.PackageNotFoundError):
111
+ dist = None
112
+ except Exception:
113
+ dist = None
114
+ if dist is None:
115
+ skip.add(req)
116
+ continue
117
+
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)
128
+
129
+ pkgs[req] = f"{dist.name}=={dist.version}"
130
+
131
+ try:
132
+ del pkgs["jolt"]
133
+ except KeyError:
134
+ pass
135
+
136
+ return list(sorted(pkgs.values()))
137
+
138
+
139
+ @utils.cached.method
140
+ def get_extra_dependencies():
141
+ req = config.get("selfdeploy", "requires", "")
142
+ return req.split() if req else []
143
+
144
+
145
+ @utils.cached.method
146
+ def publish_artifact():
147
+ registry = TaskRegistry()
148
+ registry.add_task_class(Jolt)
149
+ acache = ArtifactCache.get()
150
+ env = JoltEnvironment(cache=acache, queue=None)
151
+ gb = GraphBuilder(registry, acache)
152
+ dag = gb.build(["jolt"])
153
+ task = dag.select(lambda graph, task: True)
154
+ assert len(task) == 1, "Too many selfdeploy tasks found"
155
+ task = task[0]
156
+ if not task.is_available_remotely(cache=False):
157
+ factory = LocalExecutorFactory()
158
+ executor = LocalExecutor(factory, task, force_upload=True)
159
+ executor.run(env)
160
+ jolt_url = acache.location(task.artifacts[0])
161
+ raise_error_if(not jolt_url, "Failed to deploy jolt to a remote cache")
162
+ cacheUrl = config.get("http", "uri", config.get("cache", "uri", "") + "/files")
163
+ substituteUrl = config.get("selfdeploy", "baseUri")
164
+ if cacheUrl and substituteUrl:
165
+ return task.identity, jolt_url.replace(cacheUrl, substituteUrl)
166
+ return task.identity, jolt_url
167
+
168
+
169
+ def get_floating_version():
170
+ identity, url = publish_artifact()
171
+ return common_pb.Client(
172
+ identity=identity,
173
+ url=url,
174
+ version=version.__version__,
175
+ )
176
+
177
+
178
+ def get_pinned_version():
179
+ return common_pb.Client(
180
+ requirements=get_extra_dependencies(),
181
+ version=version.__version__,
182
+ )
183
+
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
+
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.
197
+ floating = config.getboolean("selfdeploy", "floating", False)
198
+ if floating:
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
214
+ return get_pinned_version()
jolt/plugins/strings.py CHANGED
@@ -1,31 +1,50 @@
1
- from jolt.cache import ArtifactAttributeSet
2
1
  from jolt.cache import ArtifactAttributeSetProvider
3
- from jolt.cache import ArtifactStringAttribute
4
2
 
5
3
 
6
- class StringVariable(ArtifactStringAttribute):
7
- def __init__(self, artifact, name):
8
- super(StringVariable, self).__init__(artifact, name)
9
- self._old_value = None
4
+ class StringVariableSet(object):
5
+ """
6
+ A set of string variables for an artifact.
10
7
 
11
- def apply(self, task, artifact):
12
- pass
8
+ Example:
13
9
 
14
- def unapply(self, task, artifact):
15
- pass
10
+ .. code-block:: python
16
11
 
12
+ artifact.strings.foo = "bar"
13
+ print(artifact.strings.foo)
14
+
15
+ """
17
16
 
18
- class StringVariableSet(ArtifactAttributeSet):
19
17
  def __init__(self, artifact):
20
- super(StringVariableSet, self).__init__()
21
- super(ArtifactAttributeSet, self).__setattr__("_artifact", artifact)
18
+ super(StringVariableSet, self).__setattr__("_attributes", {})
19
+ super(StringVariableSet, self).__setattr__("_artifact", artifact)
20
+
21
+ def _get_attributes(self):
22
+ return self._attributes
23
+
24
+ def __getattr__(self, name):
25
+ attributes = self._get_attributes()
26
+ return attributes.get(name, None)
27
+
28
+ def __setattr__(self, name, value):
29
+ if not isinstance(value, str):
30
+ raise ValueError(f"Value assigned to artifact.strings.{name} must be a string, got {type(value)}")
31
+ attributes = self._get_attributes()
32
+ attributes[name] = self._artifact.tools.expand(value)
33
+ return value
22
34
 
23
- def create(self, name):
24
- return StringVariable(self._artifact, name)
35
+ def __dict__(self):
36
+ return {key: str(value) for key, value in self.items()}
37
+
38
+ def items(self):
39
+ return self._get_attributes().items()
25
40
 
26
41
 
27
42
  @ArtifactAttributeSetProvider.Register
28
43
  class StringVariableSetProvider(ArtifactAttributeSetProvider):
44
+ """
45
+ A provider of string variable sets.
46
+ """
47
+
29
48
  def create(self, artifact):
30
49
  setattr(artifact, "strings", StringVariableSet(artifact))
31
50
 
@@ -34,7 +53,7 @@ class StringVariableSetProvider(ArtifactAttributeSetProvider):
34
53
  return
35
54
 
36
55
  for key, value in content["strings"].items():
37
- getattr(artifact.strings, key).set_value(value, expand=False)
56
+ setattr(artifact.strings, key, value)
38
57
 
39
58
  def format(self, artifact, content):
40
59
  if "strings" not in content:
@@ -42,9 +61,3 @@ class StringVariableSetProvider(ArtifactAttributeSetProvider):
42
61
 
43
62
  for key, value in artifact.strings.items():
44
63
  content["strings"][key] = str(value)
45
-
46
- def apply(self, task, artifact):
47
- artifact.strings.apply(task, artifact)
48
-
49
- def unapply(self, task, artifact):
50
- artifact.strings.unapply(task, artifact)
jolt/plugins/symlinks.py CHANGED
@@ -1,5 +1,4 @@
1
1
 
2
- from jolt import cache
3
2
  from jolt import config
4
3
  from jolt import filesystem as fs
5
4
  from jolt import loader
@@ -19,20 +18,36 @@ class SymlinkTaskHooks(TaskHook):
19
18
  self._path = config.get("symlinks", "path", "artifacts")
20
19
  raise_error_if(not self._path, "symlinks.path not configured")
21
20
 
21
+ @property
22
+ def rootpath(self):
23
+ return fs.path.normpath(
24
+ fs.path.join(
25
+ fs.path.dirname(loader.JoltLoader.get().build_path),
26
+ self._path
27
+ )
28
+ )
29
+
22
30
  def task_finished(self, task):
23
31
  if not task.has_artifact():
24
32
  return
25
33
 
26
- srcpath = cache.ArtifactCache.get().get_path(task)
27
- destpath = fs.path.join(
28
- loader.get_workspacedir(),
29
- self._path,
30
- utils.canonical(task.short_qualified_name))
31
-
32
- if fs.path.exists(srcpath):
33
- fs.unlink(destpath, ignore_errors=True)
34
- fs.makedirs(fs.path.dirname(destpath))
35
- fs.symlink(srcpath, destpath)
34
+ for artifact in task.artifacts:
35
+ srcpath = artifact.final_path
36
+ if artifact.name == "main":
37
+ destpath = fs.path.join(
38
+ self.rootpath,
39
+ utils.canonical(task.short_qualified_name),
40
+ )
41
+ else:
42
+ destpath = fs.path.join(
43
+ self.rootpath,
44
+ artifact.name + "@" + utils.canonical(task.short_qualified_name),
45
+ )
46
+
47
+ if fs.path.exists(srcpath):
48
+ fs.unlink(destpath, ignore_errors=True)
49
+ fs.makedirs(fs.path.dirname(destpath))
50
+ fs.symlink(srcpath, destpath)
36
51
 
37
52
  def task_pruned(self, task):
38
53
  self.task_finished(task)
jolt/plugins/telemetry.py CHANGED
@@ -1,4 +1,3 @@
1
- from socket import gethostname
2
1
  from requests.exceptions import RequestException
3
2
  from requests.api import post
4
3
 
@@ -38,9 +37,10 @@ class TelemetryHooks(TaskHook):
38
37
  "name": task.short_qualified_name,
39
38
  "identity": task.identity,
40
39
  "instance": task.task._instance.value,
41
- "hostname": gethostname(),
40
+ "hostname": utils.hostname(),
42
41
  "role": "client" if client else "worker",
43
42
  "event": event,
43
+ "routing_key": getattr(task.task, "routing_key", "default")
44
44
  }
45
45
  if hasattr(task, "logstash"):
46
46
  data["log"] = task.logstash
@@ -74,6 +74,9 @@ class TelemetryHooks(TaskHook):
74
74
  if self._network and self._failed:
75
75
  self.post(task, "failed", client=True)
76
76
 
77
+ def task_unstable(self, task):
78
+ self.task_failed(task)
79
+
77
80
  def task_finished(self, task):
78
81
  if not self._finished:
79
82
  return
jolt/plugins/timeline.py CHANGED
@@ -1,15 +1,25 @@
1
1
  from datetime import datetime
2
2
  import os
3
3
 
4
+ from jolt import log
5
+ from jolt import tools
4
6
  from jolt import utils
5
7
  from jolt.hooks import TaskHook, TaskHookFactory
6
- from jolt.config import get_workdir
8
+ from jolt import config
9
+
10
+
11
+ NAME_LOG = "Timeline"
7
12
 
8
13
 
9
14
  class TimelineHooks(TaskHook):
10
15
  def __init__(self):
16
+ self.path = os.path.join(
17
+ config.get_workdir(),
18
+ config.get("timeline", "path", "timeline.html")
19
+ )
11
20
  self.tasks = []
12
21
  self.task_ids = {}
22
+ self.tools = tools.Tools()
13
23
 
14
24
  def started(self, task):
15
25
  task._timeline_started = datetime.now().isoformat()
@@ -27,8 +37,7 @@ class TimelineHooks(TaskHook):
27
37
  enumerate=enumerate,
28
38
  tasks=self.tasks)
29
39
 
30
- with open(os.path.join(get_workdir(), "timeline.html"), "w") as f:
31
- f.write(timeline)
40
+ self.tools.write_file(self.path, timeline, expand=False)
32
41
 
33
42
  def deps(self, task):
34
43
  ids = []
@@ -59,4 +68,5 @@ class TimelineHooks(TaskHook):
59
68
  @TaskHookFactory.register
60
69
  class TimelineFactory(TaskHookFactory):
61
70
  def create(self, env):
71
+ log.verbose(NAME_LOG + " Loaded")
62
72
  return TimelineHooks()
jolt/plugins/volume.py CHANGED
@@ -28,78 +28,76 @@ class DiskVolume(cache.StorageProvider):
28
28
  self._upload = config.getboolean(NAME, "upload", True)
29
29
  self._download = config.getboolean(NAME, "download", True)
30
30
 
31
- def _get_path(self, node, artifact):
32
- return "{path}/{name}/{file}".format(
31
+ def _get_path(self, artifact):
32
+ return artifact.tools.expand(
33
+ "{path}/{name}/{file}",
33
34
  path=self._path,
34
- name=node.name,
35
+ name=artifact.task.name,
35
36
  file=fs.path.basename(artifact.get_archive_path()))
36
37
 
37
- def _get_temp(self, node, artifact):
38
- return "{path}/{name}/{file}".format(
38
+ def _get_temp(self, artifact):
39
+ return artifact.tools.expand(
40
+ "{path}/{name}/{file}",
39
41
  path=self._path,
40
- name=node.name,
41
42
  file=uuid.uuid4())
42
43
 
43
44
  @utils.retried.on_exception(StaleFileHandleError)
44
- def download(self, node, force=False):
45
+ def download(self, artifact, force=False):
45
46
  if not self._download and not force:
46
47
  return False
47
48
 
48
- with self._cache.get_artifact(node) as artifact:
49
- path = self._get_path(node, artifact)
50
- try:
51
- log.verbose("[VOLUME] Copying {}", path)
52
- fs.copy(path, artifact.get_archive_path())
53
- return True
54
- except OSError as e:
55
- if e.errno == errno.ESTALE:
56
- log.verbose("[VOLUME] got stale file handle, retrying...")
57
- raise StaleFileHandleError(e)
58
- else:
59
- log.exception()
60
- except Exception:
49
+ path = self._get_path(artifact)
50
+ try:
51
+ log.verbose("[VOLUME] Copying {}", path)
52
+ fs.copy(path, artifact.get_archive_path())
53
+ return True
54
+ except OSError as e:
55
+ if e.errno == errno.ESTALE:
56
+ log.verbose("[VOLUME] got stale file handle, retrying...")
57
+ raise StaleFileHandleError(e)
58
+ else:
61
59
  log.exception()
60
+ except Exception:
61
+ log.exception()
62
62
 
63
63
  return False
64
64
 
65
65
  def download_enabled(self):
66
66
  return self._download
67
67
 
68
- def upload(self, node, force=False):
68
+ def upload(self, artifact, force=False):
69
69
  if not self._upload and not force:
70
70
  return True
71
- with self._cache.get_artifact(node) as artifact:
72
- path = self._get_path(node, artifact)
73
- temp = self._get_temp(node, artifact)
74
- try:
75
- log.verbose("[VOLUME] Copying {}", path)
76
- fs.copy(artifact.get_archive_path(), temp)
77
- # To avoid race-condition, make sure that the artifact still is missing before moving it into place.
78
- if not fs.exists(path):
79
- fs.rename(temp, path)
80
- else:
81
- fs.unlink(temp)
82
- return True
83
- except OSError as e:
84
- if e.errno != errno.EEXIST:
85
- log.verbose("[VOLUME] Failed to copy artifact, errno={}", os.strerror(e.errno))
86
- return e.errno == errno.EEXIST
87
- except Exception:
88
- log.exception()
89
- finally:
90
- fs.unlink(temp, ignore_errors=True)
71
+ path = self._get_path(artifact)
72
+ temp = self._get_temp(artifact)
73
+ try:
74
+ log.verbose("[VOLUME] Copying {}", path)
75
+ fs.copy(artifact.get_archive_path(), temp)
76
+ # To avoid race-condition, make sure that the artifact still is
77
+ # missing before moving it into place.
78
+ if not fs.exists(path):
79
+ fs.rename(temp, path)
80
+ else:
81
+ fs.unlink(temp)
82
+ return True
83
+ except OSError as e:
84
+ if e.errno != errno.EEXIST:
85
+ log.verbose("[VOLUME] Failed to copy artifact, errno={}", os.strerror(e.errno))
86
+ return e.errno == errno.EEXIST
87
+ except Exception:
88
+ log.exception()
89
+ finally:
90
+ fs.unlink(temp, ignore_errors=True)
91
91
  return False
92
92
 
93
93
  def upload_enabled(self):
94
94
  return self._upload
95
95
 
96
- def location(self, node):
97
- with self._cache.get_artifact(node) as artifact:
98
- path = self._get_path(node, artifact)
99
- avail = fs.path.exists(path)
100
- log.debug("[VOLUME] {} is{} present", path, "" if avail else " not")
101
- return avail
102
- return False
96
+ def location(self, artifact):
97
+ path = self._get_path(artifact)
98
+ avail = fs.path.exists(path)
99
+ log.debug("[VOLUME] {} is{} present", path, "" if avail else " not")
100
+ return avail
103
101
 
104
102
 
105
103
  @cache.RegisterStorage