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.
- jolt/__init__.py +80 -7
- jolt/__main__.py +9 -1
- jolt/bin/fstree-darwin-x86_64 +0 -0
- jolt/bin/fstree-linux-x86_64 +0 -0
- jolt/cache.py +832 -362
- jolt/chroot.py +156 -0
- jolt/cli.py +281 -162
- jolt/common_pb2.py +63 -0
- jolt/common_pb2_grpc.py +4 -0
- jolt/config.py +98 -41
- jolt/error.py +19 -4
- jolt/filesystem.py +2 -6
- jolt/graph.py +705 -117
- jolt/hooks.py +43 -0
- jolt/influence.py +122 -3
- jolt/loader.py +369 -121
- jolt/log.py +225 -63
- jolt/manifest.py +28 -38
- jolt/options.py +35 -10
- jolt/pkgs/abseil.py +42 -0
- jolt/pkgs/asio.py +25 -0
- jolt/pkgs/autoconf.py +41 -0
- jolt/pkgs/automake.py +41 -0
- jolt/pkgs/b2.py +31 -0
- jolt/pkgs/boost.py +111 -0
- jolt/pkgs/boringssl.py +32 -0
- jolt/pkgs/busybox.py +39 -0
- jolt/pkgs/bzip2.py +43 -0
- jolt/pkgs/cares.py +29 -0
- jolt/pkgs/catch2.py +36 -0
- jolt/pkgs/cbindgen.py +17 -0
- jolt/pkgs/cista.py +19 -0
- jolt/pkgs/clang.py +44 -0
- jolt/pkgs/cli11.py +24 -0
- jolt/pkgs/cmake.py +48 -0
- jolt/pkgs/cpython.py +196 -0
- jolt/pkgs/crun.py +29 -0
- jolt/pkgs/curl.py +38 -0
- jolt/pkgs/dbus.py +18 -0
- jolt/pkgs/double_conversion.py +24 -0
- jolt/pkgs/fastfloat.py +21 -0
- jolt/pkgs/ffmpeg.py +28 -0
- jolt/pkgs/flatbuffers.py +29 -0
- jolt/pkgs/fmt.py +27 -0
- jolt/pkgs/fstree.py +20 -0
- jolt/pkgs/gflags.py +18 -0
- jolt/pkgs/glib.py +18 -0
- jolt/pkgs/glog.py +25 -0
- jolt/pkgs/glslang.py +21 -0
- jolt/pkgs/golang.py +16 -11
- jolt/pkgs/googlebenchmark.py +18 -0
- jolt/pkgs/googletest.py +46 -0
- jolt/pkgs/gperf.py +15 -0
- jolt/pkgs/grpc.py +73 -0
- jolt/pkgs/hdf5.py +19 -0
- jolt/pkgs/help2man.py +14 -0
- jolt/pkgs/inja.py +28 -0
- jolt/pkgs/jsoncpp.py +31 -0
- jolt/pkgs/libarchive.py +43 -0
- jolt/pkgs/libcap.py +44 -0
- jolt/pkgs/libdrm.py +44 -0
- jolt/pkgs/libedit.py +42 -0
- jolt/pkgs/libevent.py +31 -0
- jolt/pkgs/libexpat.py +27 -0
- jolt/pkgs/libfastjson.py +21 -0
- jolt/pkgs/libffi.py +16 -0
- jolt/pkgs/libglvnd.py +30 -0
- jolt/pkgs/libogg.py +28 -0
- jolt/pkgs/libpciaccess.py +18 -0
- jolt/pkgs/libseccomp.py +21 -0
- jolt/pkgs/libtirpc.py +24 -0
- jolt/pkgs/libtool.py +42 -0
- jolt/pkgs/libunwind.py +35 -0
- jolt/pkgs/libva.py +18 -0
- jolt/pkgs/libvorbis.py +33 -0
- jolt/pkgs/libxml2.py +35 -0
- jolt/pkgs/libxslt.py +17 -0
- jolt/pkgs/libyajl.py +16 -0
- jolt/pkgs/llvm.py +81 -0
- jolt/pkgs/lua.py +54 -0
- jolt/pkgs/lz4.py +26 -0
- jolt/pkgs/m4.py +14 -0
- jolt/pkgs/make.py +17 -0
- jolt/pkgs/mesa.py +81 -0
- jolt/pkgs/meson.py +17 -0
- jolt/pkgs/mstch.py +28 -0
- jolt/pkgs/mysql.py +60 -0
- jolt/pkgs/nasm.py +49 -0
- jolt/pkgs/ncurses.py +30 -0
- jolt/pkgs/ng_log.py +25 -0
- jolt/pkgs/ninja.py +45 -0
- jolt/pkgs/nlohmann_json.py +25 -0
- jolt/pkgs/nodejs.py +19 -11
- jolt/pkgs/opencv.py +24 -0
- jolt/pkgs/openjdk.py +26 -0
- jolt/pkgs/openssl.py +103 -0
- jolt/pkgs/paho.py +76 -0
- jolt/pkgs/patchelf.py +16 -0
- jolt/pkgs/perl.py +42 -0
- jolt/pkgs/pkgconfig.py +64 -0
- jolt/pkgs/poco.py +39 -0
- jolt/pkgs/protobuf.py +77 -0
- jolt/pkgs/pugixml.py +27 -0
- jolt/pkgs/python.py +19 -0
- jolt/pkgs/qt.py +35 -0
- jolt/pkgs/rapidjson.py +26 -0
- jolt/pkgs/rapidyaml.py +28 -0
- jolt/pkgs/re2.py +30 -0
- jolt/pkgs/re2c.py +17 -0
- jolt/pkgs/readline.py +15 -0
- jolt/pkgs/rust.py +41 -0
- jolt/pkgs/sdl.py +28 -0
- jolt/pkgs/simdjson.py +27 -0
- jolt/pkgs/soci.py +46 -0
- jolt/pkgs/spdlog.py +29 -0
- jolt/pkgs/spirv_llvm.py +21 -0
- jolt/pkgs/spirv_tools.py +24 -0
- jolt/pkgs/sqlite.py +83 -0
- jolt/pkgs/ssl.py +12 -0
- jolt/pkgs/texinfo.py +15 -0
- jolt/pkgs/tomlplusplus.py +22 -0
- jolt/pkgs/wayland.py +26 -0
- jolt/pkgs/x11.py +58 -0
- jolt/pkgs/xerces_c.py +20 -0
- jolt/pkgs/xorg.py +360 -0
- jolt/pkgs/xz.py +29 -0
- jolt/pkgs/yamlcpp.py +30 -0
- jolt/pkgs/zeromq.py +47 -0
- jolt/pkgs/zlib.py +87 -0
- jolt/pkgs/zstd.py +33 -0
- jolt/plugins/alias.py +3 -0
- jolt/plugins/allure.py +5 -2
- jolt/plugins/autotools.py +66 -0
- jolt/plugins/cache.py +133 -0
- jolt/plugins/cmake.py +74 -6
- jolt/plugins/conan.py +238 -0
- jolt/plugins/cxx.py +698 -0
- jolt/plugins/cxxinfo.py +7 -0
- jolt/plugins/dashboard.py +1 -1
- jolt/plugins/docker.py +80 -23
- jolt/plugins/email.py +2 -2
- jolt/plugins/email.xslt +144 -101
- jolt/plugins/environ.py +11 -0
- jolt/plugins/fetch.py +141 -0
- jolt/plugins/gdb.py +39 -19
- jolt/plugins/gerrit.py +1 -14
- jolt/plugins/git.py +283 -85
- jolt/plugins/googletest.py +2 -1
- jolt/plugins/http.py +36 -38
- jolt/plugins/libtool.py +63 -0
- jolt/plugins/linux.py +990 -0
- jolt/plugins/logstash.py +4 -4
- jolt/plugins/meson.py +61 -0
- jolt/plugins/ninja-compdb.py +99 -30
- jolt/plugins/ninja.py +468 -166
- jolt/plugins/paths.py +11 -1
- jolt/plugins/pkgconfig.py +219 -0
- jolt/plugins/podman.py +136 -92
- jolt/plugins/python.py +137 -0
- jolt/plugins/remote_execution/__init__.py +0 -0
- jolt/plugins/remote_execution/administration_pb2.py +46 -0
- jolt/plugins/remote_execution/administration_pb2_grpc.py +170 -0
- jolt/plugins/remote_execution/log_pb2.py +32 -0
- jolt/plugins/remote_execution/log_pb2_grpc.py +68 -0
- jolt/plugins/remote_execution/scheduler_pb2.py +41 -0
- jolt/plugins/remote_execution/scheduler_pb2_grpc.py +141 -0
- jolt/plugins/remote_execution/worker_pb2.py +38 -0
- jolt/plugins/remote_execution/worker_pb2_grpc.py +112 -0
- jolt/plugins/report.py +12 -2
- jolt/plugins/rust.py +25 -0
- jolt/plugins/scheduler.py +710 -0
- jolt/plugins/selfdeploy/setup.py +8 -4
- jolt/plugins/selfdeploy.py +138 -88
- jolt/plugins/strings.py +35 -22
- jolt/plugins/symlinks.py +26 -11
- jolt/plugins/telemetry.py +5 -2
- jolt/plugins/timeline.py +13 -3
- jolt/plugins/volume.py +46 -48
- jolt/scheduler.py +589 -192
- jolt/tasks.py +625 -121
- jolt/templates/timeline.html.template +44 -47
- jolt/timer.py +22 -0
- jolt/tools.py +638 -282
- jolt/utils.py +211 -7
- jolt/version.py +1 -1
- jolt/xmldom.py +12 -2
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/METADATA +97 -38
- jolt-0.9.435.dist-info/RECORD +207 -0
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/WHEEL +1 -1
- jolt/plugins/amqp.py +0 -834
- jolt/plugins/debian.py +0 -338
- jolt/plugins/ftp.py +0 -181
- jolt/plugins/repo.py +0 -253
- jolt-0.9.123.dist-info/RECORD +0 -77
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/entry_points.txt +0 -0
- {jolt-0.9.123.dist-info → jolt-0.9.435.dist-info}/top_level.txt +0 -0
jolt/cli.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import atexit
|
|
2
2
|
import click
|
|
3
|
+
import datetime
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
3
6
|
import subprocess
|
|
4
7
|
import sys
|
|
5
|
-
import datetime
|
|
6
8
|
import uuid
|
|
7
9
|
import webbrowser
|
|
8
|
-
from os import _exit, environ, getcwd
|
|
9
10
|
|
|
10
11
|
from jolt.tasks import Task, TaskRegistry, Parameter
|
|
11
12
|
from jolt import scheduler
|
|
@@ -17,13 +18,12 @@ from jolt import log
|
|
|
17
18
|
from jolt import __version__
|
|
18
19
|
from jolt.log import logfile
|
|
19
20
|
from jolt import config
|
|
20
|
-
from jolt.loader import JoltLoader
|
|
21
|
+
from jolt.loader import JoltLoader, import_workspace
|
|
21
22
|
from jolt import tools
|
|
22
23
|
from jolt import utils
|
|
23
24
|
from jolt.influence import HashInfluenceRegistry
|
|
24
25
|
from jolt.options import JoltOptions
|
|
25
26
|
from jolt import hooks
|
|
26
|
-
from jolt.manifest import JoltManifest
|
|
27
27
|
from jolt.error import JoltError
|
|
28
28
|
from jolt.error import raise_error
|
|
29
29
|
from jolt.error import raise_error_if
|
|
@@ -31,7 +31,7 @@ from jolt.error import raise_task_error_if
|
|
|
31
31
|
from jolt.plugins import report
|
|
32
32
|
|
|
33
33
|
debug_enabled = False
|
|
34
|
-
workdir = getcwd()
|
|
34
|
+
workdir = os.getcwd()
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class ArgRequiredUnless(click.Argument):
|
|
@@ -55,10 +55,12 @@ class PluginGroup(click.Group):
|
|
|
55
55
|
|
|
56
56
|
if cmd_name in ["export", "inspect"]:
|
|
57
57
|
log.set_level(log.SILENCE)
|
|
58
|
-
elif ctx.params.get("verbose"
|
|
59
|
-
log.set_level(log.
|
|
60
|
-
elif ctx.params.get("
|
|
58
|
+
elif ctx.params.get("verbose") >= 3:
|
|
59
|
+
log.set_level(log.EXCEPTION)
|
|
60
|
+
elif ctx.params.get("verbose") >= 2:
|
|
61
61
|
log.set_level(log.DEBUG)
|
|
62
|
+
elif ctx.params.get("verbose") >= 1:
|
|
63
|
+
log.set_level(log.VERBOSE)
|
|
62
64
|
|
|
63
65
|
config_files = ctx.params.get("config_file") or []
|
|
64
66
|
for config_file in config_files:
|
|
@@ -73,10 +75,12 @@ class PluginGroup(click.Group):
|
|
|
73
75
|
|
|
74
76
|
@click.group(cls=PluginGroup, invoke_without_command=True)
|
|
75
77
|
@click.version_option(__version__)
|
|
76
|
-
@click.option("-v", "--verbose",
|
|
77
|
-
@click.option("-vv", "--extra-verbose", is_flag=True, help="Extra verbose output.")
|
|
78
|
+
@click.option("-v", "--verbose", count=True, help="Verbose output (repeat to raise verbosity).")
|
|
78
79
|
@click.option("-c", "--config", "config_file", multiple=True, type=str,
|
|
79
80
|
help="Load a configuration file or set a configuration key.")
|
|
81
|
+
@click.option("-C", "--chdir", type=str,
|
|
82
|
+
help="Change working directory before executing command.")
|
|
83
|
+
@click.option("--interpreter", "machine_interface", type=str, help="Used for debugging.", hidden=True)
|
|
80
84
|
@click.option("-d", "--debugger", is_flag=True,
|
|
81
85
|
help="Attach debugger on exception.")
|
|
82
86
|
@click.option("-p", "--profile", is_flag=True, hidden=True,
|
|
@@ -87,6 +91,7 @@ class PluginGroup(click.Group):
|
|
|
87
91
|
help="Add salt as task influence.")
|
|
88
92
|
@click.option("-g", "--debug", is_flag=True, default=False, hidden=True,
|
|
89
93
|
help="Start debug shell before executing task.")
|
|
94
|
+
@click.option("-m", "--mute", is_flag=True, help="Display task log only if it fails.")
|
|
90
95
|
@click.option("-n", "--network", is_flag=True, default=False, hidden=True,
|
|
91
96
|
help="Build on network.")
|
|
92
97
|
@click.option("-l", "--local", is_flag=True, default=False, hidden=True,
|
|
@@ -97,8 +102,8 @@ class PluginGroup(click.Group):
|
|
|
97
102
|
help="Number of tasks allowed to execute in parallel (1). ")
|
|
98
103
|
@click.option("-h", "--help", is_flag=True, help="Show this message and exit.")
|
|
99
104
|
@click.pass_context
|
|
100
|
-
def cli(ctx, verbose,
|
|
101
|
-
force, salt, debug, network, local, keep_going, jobs, help):
|
|
105
|
+
def cli(ctx, verbose, config_file, debugger, profile,
|
|
106
|
+
force, salt, debug, mute, network, local, keep_going, jobs, help, machine_interface, chdir):
|
|
102
107
|
"""
|
|
103
108
|
A task execution tool.
|
|
104
109
|
|
|
@@ -122,11 +127,24 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
122
127
|
global debug_enabled
|
|
123
128
|
debug_enabled = debugger
|
|
124
129
|
|
|
130
|
+
if machine_interface:
|
|
131
|
+
log.enable_gdb()
|
|
132
|
+
|
|
133
|
+
if ctx.invoked_subcommand not in ["log", "report"]:
|
|
134
|
+
log.start_file_log()
|
|
135
|
+
|
|
136
|
+
if chdir:
|
|
137
|
+
global workdir
|
|
138
|
+
workdir = chdir
|
|
139
|
+
os.chdir(workdir)
|
|
140
|
+
|
|
141
|
+
log.verbose("Jolt version: {}", __version__)
|
|
125
142
|
log.verbose("Jolt command: {}", " ".join([fs.path.basename(sys.argv[0])] + sys.argv[1:]))
|
|
126
|
-
log.verbose("Jolt host: {}", environ.get("HOSTNAME", "localhost"))
|
|
143
|
+
log.verbose("Jolt host: {}", os.environ.get("HOSTNAME", "localhost"))
|
|
127
144
|
log.verbose("Jolt install path: {}", fs.path.dirname(__file__))
|
|
145
|
+
log.verbose("Jolt workdir: {}", workdir)
|
|
128
146
|
|
|
129
|
-
if ctx.invoked_subcommand in ["config"]:
|
|
147
|
+
if ctx.invoked_subcommand in ["config", "executor", "log"]:
|
|
130
148
|
# Don't attempt to load any task recipes as they might require
|
|
131
149
|
# plugins that are not yet configured.
|
|
132
150
|
return
|
|
@@ -138,27 +156,13 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
138
156
|
print(ctx.get_help())
|
|
139
157
|
sys.exit(0)
|
|
140
158
|
|
|
141
|
-
|
|
142
|
-
utils.call_and_catch(manifest.parse)
|
|
143
|
-
manifest.process_import()
|
|
144
|
-
ctx.obj["manifest"] = manifest
|
|
145
|
-
|
|
146
|
-
if manifest.version:
|
|
147
|
-
from jolt.version_utils import requirement, version
|
|
148
|
-
req = requirement(manifest.version)
|
|
149
|
-
ver = version(__version__)
|
|
150
|
-
raise_error_if(not req.satisfied(ver),
|
|
151
|
-
"This project requires Jolt version {} (running {})",
|
|
152
|
-
req, __version__)
|
|
153
|
-
|
|
159
|
+
registry = TaskRegistry.get()
|
|
154
160
|
loader = JoltLoader.get()
|
|
155
|
-
|
|
156
|
-
for cls in tasks:
|
|
157
|
-
TaskRegistry.get().add_task_class(cls)
|
|
161
|
+
loader.load(registry)
|
|
158
162
|
|
|
159
163
|
if ctx.invoked_subcommand in ["build", "clean"] and loader.joltdir:
|
|
160
164
|
ctx.obj["workspace_lock"] = utils.LockFile(
|
|
161
|
-
|
|
165
|
+
loader.build_path,
|
|
162
166
|
log.info, "Workspace is locked by another process, please wait...")
|
|
163
167
|
atexit.register(ctx.obj["workspace_lock"].close)
|
|
164
168
|
|
|
@@ -167,8 +171,8 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
167
171
|
if ctx.invoked_subcommand is None:
|
|
168
172
|
task = config.get("jolt", "default", "default")
|
|
169
173
|
taskname, _ = utils.parse_task_name(task)
|
|
170
|
-
if
|
|
171
|
-
ctx.invoke(build, task=[task], force=force, salt=salt, debug=debug,
|
|
174
|
+
if registry.get_task_class(taskname) is not None:
|
|
175
|
+
ctx.invoke(build, task=[task], force=force, salt=salt, debug=debug, mute=mute,
|
|
172
176
|
network=network, local=local, keep_going=keep_going, jobs=jobs)
|
|
173
177
|
else:
|
|
174
178
|
print(cli.get_help(ctx))
|
|
@@ -176,11 +180,8 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
176
180
|
|
|
177
181
|
|
|
178
182
|
def _autocomplete_tasks(ctx, args, incomplete):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
manifest.process_import()
|
|
182
|
-
|
|
183
|
-
tasks = JoltLoader.get().load()
|
|
183
|
+
loader = JoltLoader.get()
|
|
184
|
+
tasks = loader.load()
|
|
184
185
|
tasks = [task.name for task in tasks if task.name.startswith(incomplete or '')]
|
|
185
186
|
return sorted(tasks)
|
|
186
187
|
|
|
@@ -198,10 +199,14 @@ def _autocomplete_tasks(ctx, args, incomplete):
|
|
|
198
199
|
@click.option("-l", "--local", is_flag=True, default=False, help="Disable remote cache access.")
|
|
199
200
|
@click.option("-n", "--network", is_flag=True, default=False, help="Distribute tasks to network workers.")
|
|
200
201
|
@click.option("-s", "--salt", type=str, help="Add salt as hash influence for all tasks in dependency tree.", metavar="SALT")
|
|
202
|
+
@click.option("-m", "--mute", is_flag=True, help="Display task log only if it fails.")
|
|
203
|
+
@click.option("-v", "--verbose", count=True, help="Verbose output (repeat to raise verbosity).")
|
|
201
204
|
@click.option("--result", type=click.Path(), hidden=True,
|
|
202
205
|
help="Write result manifest to this file.")
|
|
203
206
|
@click.option("--no-download", is_flag=True, default=False,
|
|
204
|
-
help="Don't download artifacts from remote storage")
|
|
207
|
+
help="Don't download any artifacts from remote storage")
|
|
208
|
+
@click.option("--no-download-persistent", is_flag=True, default=False,
|
|
209
|
+
help="Don't download persistent artifacts from remote storage (only session artifacts)")
|
|
205
210
|
@click.option("--no-upload", is_flag=True, default=False,
|
|
206
211
|
help="Don't upload artifacts to remote storage")
|
|
207
212
|
@click.option("--download", is_flag=True, default=False,
|
|
@@ -212,11 +217,13 @@ def _autocomplete_tasks(ctx, args, incomplete):
|
|
|
212
217
|
help="Don't prune cached artifacts from the build graph. This option can be used to populate the local cache with remotely cached dependency artifacts.")
|
|
213
218
|
@click.option("--worker", is_flag=True, default=False,
|
|
214
219
|
help="Run with the worker build strategy", hidden=True)
|
|
220
|
+
@click.option("--environ", type=click.Path(), help="Import build environment from protobuf", hidden=True)
|
|
215
221
|
@click.pass_context
|
|
216
222
|
@hooks.cli_build
|
|
217
223
|
def build(ctx, task, network, keep_going, default, local,
|
|
218
|
-
no_download, no_upload, download, upload, worker, force,
|
|
219
|
-
salt, copy, debug, result, jobs, no_prune
|
|
224
|
+
no_download, no_download_persistent, no_upload, download, upload, worker, force,
|
|
225
|
+
salt, copy, debug, result, jobs, no_prune, verbose,
|
|
226
|
+
mute, environ):
|
|
220
227
|
"""
|
|
221
228
|
Build task artifact.
|
|
222
229
|
|
|
@@ -248,6 +255,7 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
248
255
|
are removed before execution starts.
|
|
249
256
|
|
|
250
257
|
"""
|
|
258
|
+
|
|
251
259
|
raise_error_if(network and local,
|
|
252
260
|
"The -n and -l flags are mutually exclusive")
|
|
253
261
|
|
|
@@ -260,8 +268,14 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
260
268
|
raise_error_if(no_upload and upload,
|
|
261
269
|
"The --upload and --no-upload flags are mutually exclusive")
|
|
262
270
|
|
|
263
|
-
|
|
271
|
+
if verbose >= 3:
|
|
272
|
+
log.set_level(log.EXCEPTION)
|
|
273
|
+
elif verbose >= 2:
|
|
274
|
+
log.set_level(log.DEBUG)
|
|
275
|
+
elif verbose >= 1:
|
|
276
|
+
log.set_level(log.VERBOSE)
|
|
264
277
|
|
|
278
|
+
ts_start = utils.duration()
|
|
265
279
|
task = list(task)
|
|
266
280
|
task = [utils.stable_task_name(t) for t in task]
|
|
267
281
|
|
|
@@ -271,6 +285,7 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
271
285
|
else:
|
|
272
286
|
_download = config.getboolean("jolt", "download", True)
|
|
273
287
|
_upload = config.getboolean("jolt", "upload", True)
|
|
288
|
+
_download_session = _download
|
|
274
289
|
|
|
275
290
|
if local:
|
|
276
291
|
_download = False
|
|
@@ -278,23 +293,60 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
278
293
|
else:
|
|
279
294
|
if no_download:
|
|
280
295
|
_download = False
|
|
296
|
+
_download_session = False
|
|
297
|
+
if no_download_persistent:
|
|
298
|
+
_download = False
|
|
281
299
|
if no_upload:
|
|
282
300
|
_upload = False
|
|
283
301
|
if download:
|
|
284
302
|
_download = True
|
|
303
|
+
_download_session = True
|
|
285
304
|
if upload:
|
|
286
305
|
_upload = True
|
|
287
306
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
307
|
+
if keep_going:
|
|
308
|
+
config.set_keep_going(True)
|
|
309
|
+
|
|
310
|
+
# Import build environment from protobuf if provided
|
|
311
|
+
buildenv = None
|
|
312
|
+
if environ:
|
|
313
|
+
with open(environ, "rb") as f:
|
|
314
|
+
from jolt import common_pb2 as common_pb
|
|
315
|
+
buildenv = common_pb.BuildEnvironment()
|
|
316
|
+
try:
|
|
317
|
+
buildenv.ParseFromString(f.read())
|
|
318
|
+
except Exception as e:
|
|
319
|
+
raise_error("Failed to parse build environment protobuf: {}", e)
|
|
320
|
+
|
|
321
|
+
# Import log level
|
|
322
|
+
log.set_level_pb(buildenv.loglevel)
|
|
323
|
+
|
|
324
|
+
# Import workspace
|
|
325
|
+
import_workspace(buildenv)
|
|
326
|
+
|
|
327
|
+
# Import configuration snippet
|
|
328
|
+
config.import_config(buildenv.config)
|
|
329
|
+
|
|
330
|
+
# Import configuration parameters (-c params.key)
|
|
331
|
+
config.import_params({param.key: param.value for param in buildenv.parameters})
|
|
332
|
+
|
|
333
|
+
# Import default parameters (-d taskname:param=value)
|
|
334
|
+
default = utils.as_list(default)
|
|
335
|
+
default += buildenv.task_default_parameters
|
|
336
|
+
|
|
337
|
+
options = JoltOptions(
|
|
338
|
+
network=network,
|
|
339
|
+
local=local,
|
|
340
|
+
download=_download,
|
|
341
|
+
download_session=_download_session,
|
|
342
|
+
upload=_upload,
|
|
343
|
+
keep_going=keep_going,
|
|
344
|
+
default=default,
|
|
345
|
+
worker=worker,
|
|
346
|
+
debug=debug,
|
|
347
|
+
salt=salt,
|
|
348
|
+
jobs=jobs,
|
|
349
|
+
mute=mute)
|
|
298
350
|
|
|
299
351
|
acache = cache.ArtifactCache.get(options)
|
|
300
352
|
|
|
@@ -316,31 +368,29 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
316
368
|
for params in default:
|
|
317
369
|
registry.set_default_parameters(params)
|
|
318
370
|
|
|
319
|
-
|
|
371
|
+
log.info("Started: {}", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
|
320
372
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
task.append(mt.name)
|
|
324
|
-
for mt in mb.defaults:
|
|
325
|
-
registry.set_default_parameters(mt.name)
|
|
373
|
+
gb = graph.GraphBuilder(registry, acache, options, progress=True, buildenv=buildenv)
|
|
374
|
+
dag = gb.build(task)
|
|
326
375
|
|
|
376
|
+
# If asked to force rebuild, taint all goal tasks
|
|
327
377
|
if force:
|
|
328
|
-
for goal in
|
|
329
|
-
|
|
378
|
+
for goal in dag.goals:
|
|
379
|
+
goal.get_extended_task().taint()
|
|
330
380
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
gb = graph.GraphBuilder(registry, manifest, options, progress=True)
|
|
334
|
-
dag = gb.build(task)
|
|
381
|
+
# Collect information about artifact presence before starting prune or build
|
|
382
|
+
acache.precheck(dag.persistent_artifacts, remote=not local)
|
|
335
383
|
|
|
384
|
+
# Prune the graph to remove tasks that are already available locally or remotely
|
|
336
385
|
if not no_prune:
|
|
337
|
-
gp = graph.GraphPruner(strategy)
|
|
386
|
+
gp = graph.GraphPruner(acache, strategy)
|
|
338
387
|
dag = gp.prune(dag)
|
|
339
388
|
|
|
340
389
|
goal_tasks = dag.goals
|
|
341
390
|
goal_task_duration = 0
|
|
342
391
|
|
|
343
|
-
|
|
392
|
+
session = executors.create_session(dag) if options.network else {}
|
|
393
|
+
queue = scheduler.TaskQueue()
|
|
344
394
|
|
|
345
395
|
try:
|
|
346
396
|
if not dag.has_tasks():
|
|
@@ -354,16 +404,20 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
354
404
|
debug=debug)
|
|
355
405
|
|
|
356
406
|
with progress:
|
|
407
|
+
in_progress = set()
|
|
408
|
+
|
|
357
409
|
while dag.has_tasks() or not queue.empty():
|
|
358
410
|
# Find all tasks ready to be executed
|
|
359
|
-
leafs = dag.select(lambda graph, task: task.is_ready())
|
|
411
|
+
leafs = dag.select(lambda graph, task: task.is_ready() and task not in in_progress)
|
|
360
412
|
|
|
361
413
|
# Order the tasks by their weights to improve build times
|
|
362
414
|
leafs.sort(key=lambda x: x.weight)
|
|
363
415
|
|
|
364
416
|
while leafs:
|
|
365
417
|
task = leafs.pop()
|
|
366
|
-
|
|
418
|
+
executor = strategy.create_executor(session, task)
|
|
419
|
+
queue.submit(executor)
|
|
420
|
+
in_progress.add(task)
|
|
367
421
|
|
|
368
422
|
task, error = queue.wait()
|
|
369
423
|
|
|
@@ -373,6 +427,7 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
373
427
|
elif task.is_goal() and task.duration_running:
|
|
374
428
|
goal_task_duration += task.duration_running.seconds
|
|
375
429
|
|
|
430
|
+
# Unpack tasks with overridden unpack() method
|
|
376
431
|
if not task.is_resource():
|
|
377
432
|
if no_prune and task.task.unpack.__func__ is not Task.unpack:
|
|
378
433
|
with acache.get_context(task):
|
|
@@ -380,36 +435,50 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
380
435
|
|
|
381
436
|
progress.update(1)
|
|
382
437
|
|
|
438
|
+
if no_prune and task.is_workspace_resource():
|
|
439
|
+
task.task.acquire_ws(force=True)
|
|
440
|
+
|
|
383
441
|
if not keep_going and error is not None:
|
|
384
442
|
queue.abort()
|
|
443
|
+
executors.shutdown()
|
|
444
|
+
task.raise_for_status()
|
|
385
445
|
raise error
|
|
386
446
|
|
|
387
|
-
if dag.failed:
|
|
447
|
+
if dag.failed or dag.unstable:
|
|
388
448
|
log.error("List of failed tasks")
|
|
389
|
-
for failed in dag.failed:
|
|
449
|
+
for failed in dag.failed + dag.unstable:
|
|
390
450
|
log.error("- {}", failed.log_name.strip("()"))
|
|
451
|
+
|
|
452
|
+
for failed_task in dag.failed:
|
|
453
|
+
failed_task.raise_for_status()
|
|
454
|
+
if dag.failed:
|
|
391
455
|
raise_error("No more tasks could be executed")
|
|
392
456
|
|
|
393
|
-
for goal in goal_tasks:
|
|
394
|
-
if acache.is_available_locally(goal):
|
|
395
|
-
with acache.get_artifact(goal) as artifact:
|
|
396
|
-
log.info("Location: {0}", artifact.path)
|
|
397
|
-
if copy:
|
|
398
|
-
artifact.copy("*", utils.as_dirpath(fs.path.join(workdir, click.format_filename(copy))), symlinks=True)
|
|
399
457
|
except KeyboardInterrupt:
|
|
400
458
|
print()
|
|
401
459
|
log.warning("Interrupted by user")
|
|
402
460
|
try:
|
|
403
461
|
queue.abort()
|
|
462
|
+
executors.shutdown()
|
|
404
463
|
sys.exit(1)
|
|
405
464
|
except KeyboardInterrupt:
|
|
406
465
|
print()
|
|
407
466
|
log.warning("Interrupted again, exiting")
|
|
408
|
-
_exit(1)
|
|
467
|
+
os._exit(1)
|
|
409
468
|
finally:
|
|
469
|
+
queue.shutdown()
|
|
470
|
+
|
|
471
|
+
for task in goal_tasks:
|
|
472
|
+
for artifact in task.artifacts:
|
|
473
|
+
if acache.is_available_locally(artifact):
|
|
474
|
+
log.info("Location: {0}", artifact.path)
|
|
475
|
+
if copy:
|
|
476
|
+
dst = utils.as_dirpath(fs.path.join(workdir, click.format_filename(copy)))
|
|
477
|
+
artifact.copy("*", dst, symlinks=True)
|
|
478
|
+
|
|
410
479
|
log.info("Ended: {}", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
|
411
480
|
log.info("Total execution time: {0} {1}",
|
|
412
|
-
str(
|
|
481
|
+
str(ts_start),
|
|
413
482
|
str(queue.duration_acc) if network else '')
|
|
414
483
|
if result:
|
|
415
484
|
with report.update() as manifest:
|
|
@@ -445,7 +514,7 @@ def clean(ctx, task, deps, expired):
|
|
|
445
514
|
if task:
|
|
446
515
|
task = [utils.stable_task_name(t) for t in task]
|
|
447
516
|
registry = TaskRegistry.get()
|
|
448
|
-
dag = graph.GraphBuilder(registry,
|
|
517
|
+
dag = graph.GraphBuilder(registry, acache).build(task)
|
|
449
518
|
if deps:
|
|
450
519
|
tasks = dag.tasks
|
|
451
520
|
else:
|
|
@@ -590,11 +659,10 @@ def display(ctx, task, reverse=None, show_cache=False, prune=False):
|
|
|
590
659
|
|
|
591
660
|
"""
|
|
592
661
|
registry = TaskRegistry.get()
|
|
593
|
-
gb = graph.GraphBuilder(registry, ctx.obj["manifest"])
|
|
594
|
-
dag = gb.build(task, influence=show_cache)
|
|
595
|
-
|
|
596
662
|
options = JoltOptions()
|
|
597
663
|
acache = cache.ArtifactCache.get(options)
|
|
664
|
+
gb = graph.GraphBuilder(registry, acache)
|
|
665
|
+
dag = gb.build(task, influence=show_cache)
|
|
598
666
|
|
|
599
667
|
if reverse:
|
|
600
668
|
def iterator(task):
|
|
@@ -658,7 +726,14 @@ def docs():
|
|
|
658
726
|
"""
|
|
659
727
|
Opens the Jolt documentation in the default webbrowser.
|
|
660
728
|
"""
|
|
661
|
-
|
|
729
|
+
url = config.get("jolt", "docs", "http://jolt.readthedocs.io/")
|
|
730
|
+
success = False
|
|
731
|
+
try:
|
|
732
|
+
success = webbrowser.open(url)
|
|
733
|
+
except Exception:
|
|
734
|
+
pass
|
|
735
|
+
if not success:
|
|
736
|
+
print(f"Failed to open web browser. Visit {url} manually.")
|
|
662
737
|
|
|
663
738
|
|
|
664
739
|
@cli.command()
|
|
@@ -683,15 +758,14 @@ def download(ctx, task, deps, copy, copy_all):
|
|
|
683
758
|
if copy_all:
|
|
684
759
|
deps = True
|
|
685
760
|
|
|
686
|
-
manifest = ctx.obj["manifest"]
|
|
687
761
|
options = JoltOptions()
|
|
688
762
|
acache = cache.ArtifactCache.get(options)
|
|
689
763
|
hooks.TaskHookRegistry.get(options)
|
|
690
764
|
executors = scheduler.ExecutorRegistry.get(options)
|
|
691
765
|
registry = TaskRegistry.get()
|
|
692
766
|
strategy = scheduler.DownloadStrategy(executors, acache)
|
|
693
|
-
queue = scheduler.TaskQueue(
|
|
694
|
-
gb = graph.GraphBuilder(registry,
|
|
767
|
+
queue = scheduler.TaskQueue()
|
|
768
|
+
gb = graph.GraphBuilder(registry, acache, options, progress=True)
|
|
695
769
|
dag = gb.build(task)
|
|
696
770
|
|
|
697
771
|
if not deps:
|
|
@@ -704,20 +778,24 @@ def download(ctx, task, deps, copy, copy_all):
|
|
|
704
778
|
|
|
705
779
|
try:
|
|
706
780
|
with log.progress("Progress", dag.number_of_tasks(), " tasks", estimates=False, debug=False) as p:
|
|
781
|
+
in_progress = set()
|
|
782
|
+
|
|
707
783
|
while dag.has_tasks() or not queue.empty():
|
|
708
|
-
leafs = dag.select(lambda graph, task: task.is_ready())
|
|
784
|
+
leafs = dag.select(lambda graph, task: task.is_ready() and task not in in_progress)
|
|
709
785
|
|
|
710
786
|
while leafs:
|
|
711
787
|
task = leafs.pop()
|
|
712
|
-
|
|
788
|
+
executor = strategy.create_executor({}, task)
|
|
789
|
+
queue.submit(executor)
|
|
790
|
+
in_progress.add(task)
|
|
713
791
|
|
|
714
792
|
task, error = queue.wait()
|
|
715
793
|
p.update(1)
|
|
716
794
|
|
|
717
795
|
copy_tasks = goal_tasks if not copy_all else all_tasks
|
|
718
796
|
for goal in copy_tasks:
|
|
719
|
-
if
|
|
720
|
-
|
|
797
|
+
if goal.is_available_locally():
|
|
798
|
+
for artifact in goal.artifacts:
|
|
721
799
|
if copy:
|
|
722
800
|
log.info("Copying: {0}", artifact.path)
|
|
723
801
|
artifact.copy("*", utils.as_dirpath(fs.path.join(workdir, click.format_filename(copy))), symlinks=True)
|
|
@@ -732,65 +810,19 @@ def download(ctx, task, deps, copy, copy_all):
|
|
|
732
810
|
log.warning("Interrupted by user")
|
|
733
811
|
try:
|
|
734
812
|
queue.abort()
|
|
813
|
+
executors.shutdown()
|
|
735
814
|
sys.exit(1)
|
|
736
815
|
except KeyboardInterrupt:
|
|
737
816
|
print()
|
|
738
817
|
log.warning("Interrupted again, exiting")
|
|
739
|
-
_exit(1)
|
|
818
|
+
os._exit(1)
|
|
819
|
+
|
|
740
820
|
except Exception as e:
|
|
741
821
|
log.set_interactive(True)
|
|
742
822
|
raise e
|
|
743
823
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
@click.argument("task", type=str, nargs=-1, required=True)
|
|
747
|
-
@click.option("-r", "--remove", is_flag=True, help="Remove tasks from existing manifest.")
|
|
748
|
-
@click.option("-d", "--default", type=str, multiple=True, help="Override default parameter values.")
|
|
749
|
-
@click.option("-o", "--output", type=str, default="default.joltxmanifest", help="Manifest filename.")
|
|
750
|
-
@click.pass_context
|
|
751
|
-
def freeze(ctx, task, default, output, remove):
|
|
752
|
-
"""
|
|
753
|
-
Freeze the identity of a task.
|
|
754
|
-
|
|
755
|
-
<WIP>
|
|
756
|
-
"""
|
|
757
|
-
manifest = ctx.obj["manifest"]
|
|
758
|
-
|
|
759
|
-
options = JoltOptions(default=default)
|
|
760
|
-
acache = cache.ArtifactCache.get(options)
|
|
761
|
-
scheduler.ExecutorRegistry.get(options)
|
|
762
|
-
registry = TaskRegistry.get()
|
|
763
|
-
|
|
764
|
-
for params in default:
|
|
765
|
-
registry.set_default_parameters(params)
|
|
766
|
-
|
|
767
|
-
gb = graph.GraphBuilder(registry, manifest)
|
|
768
|
-
dag = gb.build(task)
|
|
769
|
-
|
|
770
|
-
available_in_cache = [
|
|
771
|
-
(t.is_available_locally(acache) or (
|
|
772
|
-
t.is_available_remotely(acache) and acache.download_enabled()), t)
|
|
773
|
-
for t in dag.tasks if t.is_cacheable()]
|
|
774
|
-
|
|
775
|
-
for available, task in available_in_cache:
|
|
776
|
-
raise_task_error_if(
|
|
777
|
-
not remove and not available, task,
|
|
778
|
-
"Task artifact is not available in any cache, build it first")
|
|
779
|
-
|
|
780
|
-
for task in dag.tasks:
|
|
781
|
-
if task.is_resource() or not task.is_cacheable():
|
|
782
|
-
continue
|
|
783
|
-
manifest_task = manifest.find_task(task)
|
|
784
|
-
if remove and manifest_task:
|
|
785
|
-
manifest.remove_task(manifest_task)
|
|
786
|
-
continue
|
|
787
|
-
if not remove:
|
|
788
|
-
if not manifest_task:
|
|
789
|
-
manifest_task = manifest.create_task()
|
|
790
|
-
manifest_task.name = task.qualified_name
|
|
791
|
-
manifest_task.identity = task.identity
|
|
792
|
-
|
|
793
|
-
manifest.write(fs.path.join(JoltLoader.get().joltdir, output))
|
|
824
|
+
finally:
|
|
825
|
+
queue.shutdown()
|
|
794
826
|
|
|
795
827
|
|
|
796
828
|
@cli.command(name="list")
|
|
@@ -813,6 +845,8 @@ def _list(ctx, task=None, all=False, reverse=None):
|
|
|
813
845
|
|
|
814
846
|
raise_error_if(not task and reverse, "TASK required with --reverse")
|
|
815
847
|
|
|
848
|
+
options = JoltOptions()
|
|
849
|
+
acache = cache.ArtifactCache.get(options)
|
|
816
850
|
registry = TaskRegistry.get()
|
|
817
851
|
|
|
818
852
|
if not task:
|
|
@@ -826,7 +860,7 @@ def _list(ctx, task=None, all=False, reverse=None):
|
|
|
826
860
|
reverse = [utils.stable_task_name(t) for t in utils.as_list(reverse or [])]
|
|
827
861
|
|
|
828
862
|
try:
|
|
829
|
-
dag = graph.GraphBuilder(registry,
|
|
863
|
+
dag = graph.GraphBuilder(registry, acache).build(task, influence=False)
|
|
830
864
|
except JoltError as e:
|
|
831
865
|
raise e
|
|
832
866
|
except Exception:
|
|
@@ -860,16 +894,21 @@ def _log(follow, delete):
|
|
|
860
894
|
Display the Jolt log file.
|
|
861
895
|
|
|
862
896
|
"""
|
|
897
|
+
if not log.logfiles:
|
|
898
|
+
print("No logs exist")
|
|
899
|
+
return
|
|
900
|
+
|
|
863
901
|
if follow:
|
|
864
|
-
subprocess.call("tail -f {0}".format(
|
|
902
|
+
subprocess.call("tail -f {0}".format(log.logfiles[-1]), shell=True)
|
|
865
903
|
elif delete:
|
|
866
|
-
|
|
904
|
+
for file in log.logfiles:
|
|
905
|
+
fs.unlink(file)
|
|
867
906
|
else:
|
|
868
907
|
t = tools.Tools()
|
|
869
|
-
configured_pager = config.get("jolt", "pager", environ.get("PAGER", None))
|
|
908
|
+
configured_pager = config.get("jolt", "pager", os.environ.get("PAGER", None))
|
|
870
909
|
for pager in [configured_pager, "less", "more", "cat"]:
|
|
871
910
|
if pager and t.which(pager):
|
|
872
|
-
return subprocess.call("{1} {0}".format(
|
|
911
|
+
return subprocess.call("{1} {0}".format(log.logfiles[-1], pager), shell=True)
|
|
873
912
|
print(t.read_file(logfile))
|
|
874
913
|
|
|
875
914
|
|
|
@@ -921,9 +960,8 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
|
|
|
921
960
|
|
|
922
961
|
print()
|
|
923
962
|
print(" Requirements")
|
|
924
|
-
manifest = ctx.obj["manifest"]
|
|
925
963
|
try:
|
|
926
|
-
task = task_registry.get_task(task_name
|
|
964
|
+
task = task_registry.get_task(task_name)
|
|
927
965
|
for req in sorted(utils.as_list(utils.call_or_return(task, task.requires))):
|
|
928
966
|
print(" {0}".format(task.tools.expand(req)))
|
|
929
967
|
if not task.requires:
|
|
@@ -943,8 +981,9 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
|
|
|
943
981
|
task.taint = salt
|
|
944
982
|
|
|
945
983
|
if artifact:
|
|
984
|
+
options = JoltOptions(salt=salt)
|
|
946
985
|
acache = cache.ArtifactCache.get()
|
|
947
|
-
builder = graph.GraphBuilder(task_registry,
|
|
986
|
+
builder = graph.GraphBuilder(task_registry, acache, options)
|
|
948
987
|
dag = builder.build([task.qualified_name])
|
|
949
988
|
tasks = dag.select(lambda graph, node: node.task is task)
|
|
950
989
|
assert len(tasks) == 1, "graph produced multiple tasks, one expected"
|
|
@@ -953,14 +992,14 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
|
|
|
953
992
|
|
|
954
993
|
print(" Cache")
|
|
955
994
|
print(" Identity {0}".format(proxy.identity))
|
|
956
|
-
if
|
|
957
|
-
|
|
995
|
+
if proxy.is_available_locally():
|
|
996
|
+
for artifact in filter(lambda a: not a.is_session(), proxy.artifacts):
|
|
958
997
|
print(" Location {0}".format(artifact.path))
|
|
959
998
|
print(" Local True ({0})".format(
|
|
960
|
-
utils.as_human_size(
|
|
999
|
+
utils.as_human_size(sum([artifact.get_size() for artifact in proxy.artifacts]))))
|
|
961
1000
|
else:
|
|
962
1001
|
print(" Local False")
|
|
963
|
-
print(" Remote {0}".format(
|
|
1002
|
+
print(" Remote {0}".format(proxy.is_available_remotely(cache=False)))
|
|
964
1003
|
print()
|
|
965
1004
|
|
|
966
1005
|
if influence:
|
|
@@ -1008,10 +1047,10 @@ def _export(ctx, task):
|
|
|
1008
1047
|
executors = scheduler.ExecutorRegistry.get()
|
|
1009
1048
|
strategy = scheduler.LocalStrategy(executors, acache)
|
|
1010
1049
|
|
|
1011
|
-
dag = graph.GraphBuilder(registry,
|
|
1050
|
+
dag = graph.GraphBuilder(registry, acache)
|
|
1012
1051
|
dag = dag.build(task)
|
|
1013
1052
|
|
|
1014
|
-
gp = graph.GraphPruner(strategy)
|
|
1053
|
+
gp = graph.GraphPruner(acache, strategy)
|
|
1015
1054
|
dag = gp.prune(dag)
|
|
1016
1055
|
|
|
1017
1056
|
class Export(object):
|
|
@@ -1036,17 +1075,97 @@ def _export(ctx, task):
|
|
|
1036
1075
|
context = Context(tasks)
|
|
1037
1076
|
|
|
1038
1077
|
for task in context.tasks:
|
|
1039
|
-
artifact
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1078
|
+
for artifact in task.artifacts:
|
|
1079
|
+
raise_task_error_if(
|
|
1080
|
+
not task.is_resource() and artifact.is_temporary(), task,
|
|
1081
|
+
"Task artifact not found in local cache, build it first")
|
|
1043
1082
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1083
|
+
visitor = Export()
|
|
1084
|
+
cache.visit_artifact(task, artifact, visitor)
|
|
1085
|
+
context.add_export(task, visitor)
|
|
1047
1086
|
|
|
1048
1087
|
script = utils.render(
|
|
1049
1088
|
"export.sh.template",
|
|
1050
1089
|
ctx=context)
|
|
1051
1090
|
|
|
1052
1091
|
print(script)
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
@cli.command(name="report")
|
|
1095
|
+
@click.pass_context
|
|
1096
|
+
@hooks.cli_report
|
|
1097
|
+
def _report(ctx):
|
|
1098
|
+
"""Create and publish a report with system information and logs.
|
|
1099
|
+
|
|
1100
|
+
The purpose of the report command is to aid troubleshooting by
|
|
1101
|
+
providing users a method to quickly generate and share a report
|
|
1102
|
+
with information about the environment and previous activity.
|
|
1103
|
+
|
|
1104
|
+
The report is uploaded to remote caches if configured, otherwise
|
|
1105
|
+
a report archive is created in the current working directory.
|
|
1106
|
+
"""
|
|
1107
|
+
|
|
1108
|
+
options = JoltOptions()
|
|
1109
|
+
acache = cache.ArtifactCache.get(options)
|
|
1110
|
+
|
|
1111
|
+
with tools.Tools() as t:
|
|
1112
|
+
|
|
1113
|
+
artifact = cache.Artifact(
|
|
1114
|
+
acache,
|
|
1115
|
+
None,
|
|
1116
|
+
identity=str(uuid.uuid4()),
|
|
1117
|
+
name="jolt",
|
|
1118
|
+
session=True,
|
|
1119
|
+
tools=t)
|
|
1120
|
+
|
|
1121
|
+
with t.tmpdir("report") as tmp, t.cwd(tmp):
|
|
1122
|
+
log.info("Collecting environment")
|
|
1123
|
+
env = ""
|
|
1124
|
+
for key, val in os.environ.items():
|
|
1125
|
+
env += f"{key} = {val}\n"
|
|
1126
|
+
t.write_file("environ.txt", env, expand=False)
|
|
1127
|
+
|
|
1128
|
+
log.info("Collecting configuration")
|
|
1129
|
+
config.save(tmp)
|
|
1130
|
+
artifact.collect("*.conf", "configs/")
|
|
1131
|
+
|
|
1132
|
+
log.info("Collecting platform information")
|
|
1133
|
+
uname = platform.uname()
|
|
1134
|
+
libc = " ".join(platform.libc_ver())
|
|
1135
|
+
pyver = platform.python_version()
|
|
1136
|
+
pyimpl = platform.python_implementation()
|
|
1137
|
+
pfm = f"""System: {uname.system}
|
|
1138
|
+
Node: {uname.node}
|
|
1139
|
+
Release: {uname.release}
|
|
1140
|
+
Version: {uname.version}
|
|
1141
|
+
Machine: {uname.machine}
|
|
1142
|
+
Libc: {libc}
|
|
1143
|
+
Python: {pyimpl} {pyver}
|
|
1144
|
+
"""
|
|
1145
|
+
t.write_file("platform.txt", pfm, expand=False)
|
|
1146
|
+
|
|
1147
|
+
def collect_command(what, cmd):
|
|
1148
|
+
try:
|
|
1149
|
+
log.info("Collecting {}", what)
|
|
1150
|
+
t.run(cmd, output=False)
|
|
1151
|
+
except Exception:
|
|
1152
|
+
pass
|
|
1153
|
+
|
|
1154
|
+
collect_command("pip modules", "pip list > packages-pip.txt")
|
|
1155
|
+
if uname.system == "Linux":
|
|
1156
|
+
collect_command("apt packages", "apt list --installed > packages-apt.txt")
|
|
1157
|
+
artifact.collect("/etc/os-release", "os-release.txt")
|
|
1158
|
+
artifact.collect("*.txt")
|
|
1159
|
+
|
|
1160
|
+
with t.cwd(log.logpath):
|
|
1161
|
+
artifact.collect("*.log", "logs/")
|
|
1162
|
+
|
|
1163
|
+
acache.commit(artifact)
|
|
1164
|
+
if acache.upload_enabled():
|
|
1165
|
+
assert acache.upload(artifact)
|
|
1166
|
+
url = acache.location(artifact)
|
|
1167
|
+
else:
|
|
1168
|
+
url = f"{artifact.identity}.tgz"
|
|
1169
|
+
t.archive(artifact.path, url)
|
|
1170
|
+
|
|
1171
|
+
log.info("Location: {}", url)
|