jolt 0.9.76__py3-none-any.whl → 0.9.429__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jolt might be problematic. Click here for more details.
- jolt/__init__.py +88 -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 +839 -367
- jolt/chroot.py +156 -0
- jolt/cli.py +362 -143
- jolt/common_pb2.py +63 -0
- jolt/common_pb2_grpc.py +4 -0
- jolt/config.py +99 -42
- jolt/error.py +19 -4
- jolt/expires.py +2 -2
- jolt/filesystem.py +8 -6
- jolt/graph.py +705 -117
- jolt/hooks.py +63 -1
- jolt/influence.py +129 -6
- 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 +23 -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 +69 -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 +91 -23
- jolt/plugins/email.py +5 -2
- jolt/plugins/email.xslt +144 -101
- jolt/plugins/environ.py +11 -0
- jolt/plugins/fetch.py +141 -0
- jolt/plugins/gdb.py +44 -21
- jolt/plugins/gerrit.py +1 -14
- jolt/plugins/git.py +316 -101
- jolt/plugins/googletest.py +522 -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 +107 -31
- jolt/plugins/ninja.py +929 -134
- jolt/plugins/paths.py +11 -1
- jolt/plugins/pkgconfig.py +219 -0
- jolt/plugins/podman.py +148 -91
- 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 +9 -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 +591 -191
- jolt/tasks.py +1783 -245
- jolt/templates/export.sh.template +12 -6
- jolt/templates/timeline.html.template +44 -47
- jolt/timer.py +22 -0
- jolt/tools.py +749 -302
- jolt/utils.py +245 -18
- jolt/version.py +1 -1
- jolt/version_utils.py +2 -2
- jolt/xmldom.py +12 -2
- {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/METADATA +98 -38
- jolt-0.9.429.dist-info/RECORD +207 -0
- {jolt-0.9.76.dist-info → jolt-0.9.429.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/ninja-cache.py +0 -64
- jolt/plugins/ninjacli.py +0 -271
- jolt/plugins/repo.py +0 -253
- jolt-0.9.76.dist-info/RECORD +0 -79
- {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/entry_points.txt +0 -0
- {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/top_level.txt +0 -0
jolt/cli.py
CHANGED
|
@@ -1,10 +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
8
|
import uuid
|
|
6
9
|
import webbrowser
|
|
7
|
-
from os import _exit, environ, getcwd
|
|
8
10
|
|
|
9
11
|
from jolt.tasks import Task, TaskRegistry, Parameter
|
|
10
12
|
from jolt import scheduler
|
|
@@ -16,13 +18,12 @@ from jolt import log
|
|
|
16
18
|
from jolt import __version__
|
|
17
19
|
from jolt.log import logfile
|
|
18
20
|
from jolt import config
|
|
19
|
-
from jolt.loader import JoltLoader
|
|
21
|
+
from jolt.loader import JoltLoader, import_workspace
|
|
20
22
|
from jolt import tools
|
|
21
23
|
from jolt import utils
|
|
22
24
|
from jolt.influence import HashInfluenceRegistry
|
|
23
25
|
from jolt.options import JoltOptions
|
|
24
26
|
from jolt import hooks
|
|
25
|
-
from jolt.manifest import JoltManifest
|
|
26
27
|
from jolt.error import JoltError
|
|
27
28
|
from jolt.error import raise_error
|
|
28
29
|
from jolt.error import raise_error_if
|
|
@@ -30,7 +31,7 @@ from jolt.error import raise_task_error_if
|
|
|
30
31
|
from jolt.plugins import report
|
|
31
32
|
|
|
32
33
|
debug_enabled = False
|
|
33
|
-
workdir = getcwd()
|
|
34
|
+
workdir = os.getcwd()
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
class ArgRequiredUnless(click.Argument):
|
|
@@ -54,10 +55,12 @@ class PluginGroup(click.Group):
|
|
|
54
55
|
|
|
55
56
|
if cmd_name in ["export", "inspect"]:
|
|
56
57
|
log.set_level(log.SILENCE)
|
|
57
|
-
elif ctx.params.get("verbose"
|
|
58
|
-
log.set_level(log.
|
|
59
|
-
elif ctx.params.get("
|
|
58
|
+
elif ctx.params.get("verbose") >= 3:
|
|
59
|
+
log.set_level(log.EXCEPTION)
|
|
60
|
+
elif ctx.params.get("verbose") >= 2:
|
|
60
61
|
log.set_level(log.DEBUG)
|
|
62
|
+
elif ctx.params.get("verbose") >= 1:
|
|
63
|
+
log.set_level(log.VERBOSE)
|
|
61
64
|
|
|
62
65
|
config_files = ctx.params.get("config_file") or []
|
|
63
66
|
for config_file in config_files:
|
|
@@ -72,10 +75,12 @@ class PluginGroup(click.Group):
|
|
|
72
75
|
|
|
73
76
|
@click.group(cls=PluginGroup, invoke_without_command=True)
|
|
74
77
|
@click.version_option(__version__)
|
|
75
|
-
@click.option("-v", "--verbose",
|
|
76
|
-
@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).")
|
|
77
79
|
@click.option("-c", "--config", "config_file", multiple=True, type=str,
|
|
78
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)
|
|
79
84
|
@click.option("-d", "--debugger", is_flag=True,
|
|
80
85
|
help="Attach debugger on exception.")
|
|
81
86
|
@click.option("-p", "--profile", is_flag=True, hidden=True,
|
|
@@ -86,6 +91,7 @@ class PluginGroup(click.Group):
|
|
|
86
91
|
help="Add salt as task influence.")
|
|
87
92
|
@click.option("-g", "--debug", is_flag=True, default=False, hidden=True,
|
|
88
93
|
help="Start debug shell before executing task.")
|
|
94
|
+
@click.option("-m", "--mute", is_flag=True, help="Display task log only if it fails.")
|
|
89
95
|
@click.option("-n", "--network", is_flag=True, default=False, hidden=True,
|
|
90
96
|
help="Build on network.")
|
|
91
97
|
@click.option("-l", "--local", is_flag=True, default=False, hidden=True,
|
|
@@ -96,8 +102,8 @@ class PluginGroup(click.Group):
|
|
|
96
102
|
help="Number of tasks allowed to execute in parallel (1). ")
|
|
97
103
|
@click.option("-h", "--help", is_flag=True, help="Show this message and exit.")
|
|
98
104
|
@click.pass_context
|
|
99
|
-
def cli(ctx, verbose,
|
|
100
|
-
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):
|
|
101
107
|
"""
|
|
102
108
|
A task execution tool.
|
|
103
109
|
|
|
@@ -121,11 +127,24 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
121
127
|
global debug_enabled
|
|
122
128
|
debug_enabled = debugger
|
|
123
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__)
|
|
124
142
|
log.verbose("Jolt command: {}", " ".join([fs.path.basename(sys.argv[0])] + sys.argv[1:]))
|
|
125
|
-
log.verbose("Jolt host: {}", environ.get("HOSTNAME", "localhost"))
|
|
143
|
+
log.verbose("Jolt host: {}", os.environ.get("HOSTNAME", "localhost"))
|
|
126
144
|
log.verbose("Jolt install path: {}", fs.path.dirname(__file__))
|
|
145
|
+
log.verbose("Jolt workdir: {}", workdir)
|
|
127
146
|
|
|
128
|
-
if ctx.invoked_subcommand in ["config"]:
|
|
147
|
+
if ctx.invoked_subcommand in ["config", "executor", "log"]:
|
|
129
148
|
# Don't attempt to load any task recipes as they might require
|
|
130
149
|
# plugins that are not yet configured.
|
|
131
150
|
return
|
|
@@ -137,27 +156,13 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
137
156
|
print(ctx.get_help())
|
|
138
157
|
sys.exit(0)
|
|
139
158
|
|
|
140
|
-
|
|
141
|
-
utils.call_and_catch(manifest.parse)
|
|
142
|
-
manifest.process_import()
|
|
143
|
-
ctx.obj["manifest"] = manifest
|
|
144
|
-
|
|
145
|
-
if manifest.version:
|
|
146
|
-
from jolt.version_utils import requirement, version
|
|
147
|
-
req = requirement(manifest.version)
|
|
148
|
-
ver = version(__version__)
|
|
149
|
-
raise_error_if(not req.satisfied(ver),
|
|
150
|
-
"This project requires Jolt version {} (running {})",
|
|
151
|
-
req, __version__)
|
|
152
|
-
|
|
159
|
+
registry = TaskRegistry.get()
|
|
153
160
|
loader = JoltLoader.get()
|
|
154
|
-
|
|
155
|
-
for cls in tasks:
|
|
156
|
-
TaskRegistry.get().add_task_class(cls)
|
|
161
|
+
loader.load(registry)
|
|
157
162
|
|
|
158
163
|
if ctx.invoked_subcommand in ["build", "clean"] and loader.joltdir:
|
|
159
164
|
ctx.obj["workspace_lock"] = utils.LockFile(
|
|
160
|
-
|
|
165
|
+
loader.build_path,
|
|
161
166
|
log.info, "Workspace is locked by another process, please wait...")
|
|
162
167
|
atexit.register(ctx.obj["workspace_lock"].close)
|
|
163
168
|
|
|
@@ -166,8 +171,8 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
166
171
|
if ctx.invoked_subcommand is None:
|
|
167
172
|
task = config.get("jolt", "default", "default")
|
|
168
173
|
taskname, _ = utils.parse_task_name(task)
|
|
169
|
-
if
|
|
170
|
-
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,
|
|
171
176
|
network=network, local=local, keep_going=keep_going, jobs=jobs)
|
|
172
177
|
else:
|
|
173
178
|
print(cli.get_help(ctx))
|
|
@@ -175,11 +180,8 @@ def cli(ctx, verbose, extra_verbose, config_file, debugger, profile,
|
|
|
175
180
|
|
|
176
181
|
|
|
177
182
|
def _autocomplete_tasks(ctx, args, incomplete):
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
manifest.process_import()
|
|
181
|
-
|
|
182
|
-
tasks = JoltLoader.get().load()
|
|
183
|
+
loader = JoltLoader.get()
|
|
184
|
+
tasks = loader.load()
|
|
183
185
|
tasks = [task.name for task in tasks if task.name.startswith(incomplete or '')]
|
|
184
186
|
return sorted(tasks)
|
|
185
187
|
|
|
@@ -197,10 +199,14 @@ def _autocomplete_tasks(ctx, args, incomplete):
|
|
|
197
199
|
@click.option("-l", "--local", is_flag=True, default=False, help="Disable remote cache access.")
|
|
198
200
|
@click.option("-n", "--network", is_flag=True, default=False, help="Distribute tasks to network workers.")
|
|
199
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).")
|
|
200
204
|
@click.option("--result", type=click.Path(), hidden=True,
|
|
201
205
|
help="Write result manifest to this file.")
|
|
202
206
|
@click.option("--no-download", is_flag=True, default=False,
|
|
203
|
-
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)")
|
|
204
210
|
@click.option("--no-upload", is_flag=True, default=False,
|
|
205
211
|
help="Don't upload artifacts to remote storage")
|
|
206
212
|
@click.option("--download", is_flag=True, default=False,
|
|
@@ -211,11 +217,13 @@ def _autocomplete_tasks(ctx, args, incomplete):
|
|
|
211
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.")
|
|
212
218
|
@click.option("--worker", is_flag=True, default=False,
|
|
213
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)
|
|
214
221
|
@click.pass_context
|
|
215
222
|
@hooks.cli_build
|
|
216
223
|
def build(ctx, task, network, keep_going, default, local,
|
|
217
|
-
no_download, no_upload, download, upload, worker, force,
|
|
218
|
-
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):
|
|
219
227
|
"""
|
|
220
228
|
Build task artifact.
|
|
221
229
|
|
|
@@ -247,6 +255,7 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
247
255
|
are removed before execution starts.
|
|
248
256
|
|
|
249
257
|
"""
|
|
258
|
+
|
|
250
259
|
raise_error_if(network and local,
|
|
251
260
|
"The -n and -l flags are mutually exclusive")
|
|
252
261
|
|
|
@@ -259,8 +268,14 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
259
268
|
raise_error_if(no_upload and upload,
|
|
260
269
|
"The --upload and --no-upload flags are mutually exclusive")
|
|
261
270
|
|
|
262
|
-
|
|
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)
|
|
263
277
|
|
|
278
|
+
ts_start = utils.duration()
|
|
264
279
|
task = list(task)
|
|
265
280
|
task = [utils.stable_task_name(t) for t in task]
|
|
266
281
|
|
|
@@ -270,6 +285,7 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
270
285
|
else:
|
|
271
286
|
_download = config.getboolean("jolt", "download", True)
|
|
272
287
|
_upload = config.getboolean("jolt", "upload", True)
|
|
288
|
+
_download_session = _download
|
|
273
289
|
|
|
274
290
|
if local:
|
|
275
291
|
_download = False
|
|
@@ -277,23 +293,60 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
277
293
|
else:
|
|
278
294
|
if no_download:
|
|
279
295
|
_download = False
|
|
296
|
+
_download_session = False
|
|
297
|
+
if no_download_persistent:
|
|
298
|
+
_download = False
|
|
280
299
|
if no_upload:
|
|
281
300
|
_upload = False
|
|
282
301
|
if download:
|
|
283
302
|
_download = True
|
|
303
|
+
_download_session = True
|
|
284
304
|
if upload:
|
|
285
305
|
_upload = True
|
|
286
306
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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)
|
|
297
350
|
|
|
298
351
|
acache = cache.ArtifactCache.get(options)
|
|
299
352
|
|
|
@@ -315,29 +368,29 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
315
368
|
for params in default:
|
|
316
369
|
registry.set_default_parameters(params)
|
|
317
370
|
|
|
318
|
-
|
|
371
|
+
log.info("Started: {}", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
|
319
372
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
task.append(mt.name)
|
|
323
|
-
for mt in mb.defaults:
|
|
324
|
-
registry.set_default_parameters(mt.name)
|
|
373
|
+
gb = graph.GraphBuilder(registry, acache, options, progress=True, buildenv=buildenv)
|
|
374
|
+
dag = gb.build(task)
|
|
325
375
|
|
|
376
|
+
# If asked to force rebuild, taint all goal tasks
|
|
326
377
|
if force:
|
|
327
|
-
for goal in
|
|
328
|
-
|
|
378
|
+
for goal in dag.goals:
|
|
379
|
+
goal.get_extended_task().taint()
|
|
329
380
|
|
|
330
|
-
|
|
331
|
-
dag =
|
|
381
|
+
# Collect information about artifact presence before starting prune or build
|
|
382
|
+
acache.precheck(dag.persistent_artifacts, remote=not local)
|
|
332
383
|
|
|
384
|
+
# Prune the graph to remove tasks that are already available locally or remotely
|
|
333
385
|
if not no_prune:
|
|
334
|
-
gp = graph.GraphPruner(strategy)
|
|
386
|
+
gp = graph.GraphPruner(acache, strategy)
|
|
335
387
|
dag = gp.prune(dag)
|
|
336
388
|
|
|
337
389
|
goal_tasks = dag.goals
|
|
338
390
|
goal_task_duration = 0
|
|
339
391
|
|
|
340
|
-
|
|
392
|
+
session = executors.create_session(dag) if options.network else {}
|
|
393
|
+
queue = scheduler.TaskQueue()
|
|
341
394
|
|
|
342
395
|
try:
|
|
343
396
|
if not dag.has_tasks():
|
|
@@ -351,16 +404,20 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
351
404
|
debug=debug)
|
|
352
405
|
|
|
353
406
|
with progress:
|
|
354
|
-
|
|
407
|
+
in_progress = set()
|
|
408
|
+
|
|
409
|
+
while dag.has_tasks() or not queue.empty():
|
|
355
410
|
# Find all tasks ready to be executed
|
|
356
|
-
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)
|
|
357
412
|
|
|
358
413
|
# Order the tasks by their weights to improve build times
|
|
359
414
|
leafs.sort(key=lambda x: x.weight)
|
|
360
415
|
|
|
361
416
|
while leafs:
|
|
362
417
|
task = leafs.pop()
|
|
363
|
-
|
|
418
|
+
executor = strategy.create_executor(session, task)
|
|
419
|
+
queue.submit(executor)
|
|
420
|
+
in_progress.add(task)
|
|
364
421
|
|
|
365
422
|
task, error = queue.wait()
|
|
366
423
|
|
|
@@ -370,6 +427,7 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
370
427
|
elif task.is_goal() and task.duration_running:
|
|
371
428
|
goal_task_duration += task.duration_running.seconds
|
|
372
429
|
|
|
430
|
+
# Unpack tasks with overridden unpack() method
|
|
373
431
|
if not task.is_resource():
|
|
374
432
|
if no_prune and task.task.unpack.__func__ is not Task.unpack:
|
|
375
433
|
with acache.get_context(task):
|
|
@@ -377,35 +435,50 @@ def build(ctx, task, network, keep_going, default, local,
|
|
|
377
435
|
|
|
378
436
|
progress.update(1)
|
|
379
437
|
|
|
438
|
+
if no_prune and task.is_workspace_resource():
|
|
439
|
+
task.task.acquire_ws(force=True)
|
|
440
|
+
|
|
380
441
|
if not keep_going and error is not None:
|
|
381
442
|
queue.abort()
|
|
443
|
+
executors.shutdown()
|
|
444
|
+
task.raise_for_status()
|
|
382
445
|
raise error
|
|
383
446
|
|
|
384
|
-
if dag.failed:
|
|
447
|
+
if dag.failed or dag.unstable:
|
|
385
448
|
log.error("List of failed tasks")
|
|
386
|
-
for failed in dag.failed:
|
|
449
|
+
for failed in dag.failed + dag.unstable:
|
|
387
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:
|
|
388
455
|
raise_error("No more tasks could be executed")
|
|
389
456
|
|
|
390
|
-
for goal in goal_tasks:
|
|
391
|
-
if acache.is_available_locally(goal):
|
|
392
|
-
with acache.get_artifact(goal) as artifact:
|
|
393
|
-
log.info("Location: {0}", artifact.path)
|
|
394
|
-
if copy:
|
|
395
|
-
artifact.copy("*", utils.as_dirpath(fs.path.join(workdir, click.format_filename(copy))), symlinks=True)
|
|
396
457
|
except KeyboardInterrupt:
|
|
397
458
|
print()
|
|
398
459
|
log.warning("Interrupted by user")
|
|
399
460
|
try:
|
|
400
461
|
queue.abort()
|
|
462
|
+
executors.shutdown()
|
|
401
463
|
sys.exit(1)
|
|
402
464
|
except KeyboardInterrupt:
|
|
403
465
|
print()
|
|
404
466
|
log.warning("Interrupted again, exiting")
|
|
405
|
-
_exit(1)
|
|
467
|
+
os._exit(1)
|
|
406
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
|
+
|
|
479
|
+
log.info("Ended: {}", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
|
407
480
|
log.info("Total execution time: {0} {1}",
|
|
408
|
-
str(
|
|
481
|
+
str(ts_start),
|
|
409
482
|
str(queue.duration_acc) if network else '')
|
|
410
483
|
if result:
|
|
411
484
|
with report.update() as manifest:
|
|
@@ -441,7 +514,7 @@ def clean(ctx, task, deps, expired):
|
|
|
441
514
|
if task:
|
|
442
515
|
task = [utils.stable_task_name(t) for t in task]
|
|
443
516
|
registry = TaskRegistry.get()
|
|
444
|
-
dag = graph.GraphBuilder(registry,
|
|
517
|
+
dag = graph.GraphBuilder(registry, acache).build(task)
|
|
445
518
|
if deps:
|
|
446
519
|
tasks = dag.tasks
|
|
447
520
|
else:
|
|
@@ -576,20 +649,20 @@ def _config(ctx, list, delete, global_, user, key, value):
|
|
|
576
649
|
|
|
577
650
|
@cli.command()
|
|
578
651
|
@click.argument("task", type=str, nargs=-1, required=False, shell_complete=_autocomplete_tasks)
|
|
579
|
-
@click.option("-r", "--reverse", type=str, help="Display consumers of REVERSE if TASK is executed.")
|
|
580
652
|
@click.option("-c", "--cached", "show_cache", is_flag=True, help="Highlight cache presence with colors.")
|
|
653
|
+
@click.option("-r", "--reverse", type=str, help="Display consumers of REVERSE if TASK is executed.")
|
|
654
|
+
@click.option("-p", "--prune", "prune", is_flag=True, help="Do not repeat tasks. An already visited task's name is printed inside [square brackets] and its deps are omitted.")
|
|
581
655
|
@click.pass_context
|
|
582
|
-
def display(ctx, task, reverse=None, show_cache=False):
|
|
656
|
+
def display(ctx, task, reverse=None, show_cache=False, prune=False):
|
|
583
657
|
"""
|
|
584
658
|
Display a task and its dependencies visually.
|
|
585
659
|
|
|
586
660
|
"""
|
|
587
661
|
registry = TaskRegistry.get()
|
|
588
|
-
gb = graph.GraphBuilder(registry, ctx.obj["manifest"])
|
|
589
|
-
dag = gb.build(task, influence=show_cache)
|
|
590
|
-
|
|
591
662
|
options = JoltOptions()
|
|
592
663
|
acache = cache.ArtifactCache.get(options)
|
|
664
|
+
gb = graph.GraphBuilder(registry, acache)
|
|
665
|
+
dag = gb.build(task, influence=show_cache)
|
|
593
666
|
|
|
594
667
|
if reverse:
|
|
595
668
|
def iterator(task):
|
|
@@ -604,6 +677,8 @@ def display(ctx, task, reverse=None, show_cache=False):
|
|
|
604
677
|
tasklist = dag.requested_goals
|
|
605
678
|
|
|
606
679
|
if dag.has_tasks():
|
|
680
|
+
processed = set()
|
|
681
|
+
|
|
607
682
|
def _display(task, indent=0, last=None):
|
|
608
683
|
header = ""
|
|
609
684
|
if indent > 0:
|
|
@@ -624,10 +699,22 @@ def display(ctx, task, reverse=None, show_cache=False):
|
|
|
624
699
|
else:
|
|
625
700
|
colorize = colors.green
|
|
626
701
|
|
|
627
|
-
|
|
702
|
+
if prune and task.short_qualified_name in processed:
|
|
703
|
+
def prune_marker(n):
|
|
704
|
+
return "[" + n + "]"
|
|
705
|
+
else:
|
|
706
|
+
def prune_marker(n):
|
|
707
|
+
return n
|
|
708
|
+
|
|
709
|
+
print(header + colorize(prune_marker(task.short_qualified_name)))
|
|
628
710
|
children = iterator(task)
|
|
629
|
-
|
|
630
|
-
|
|
711
|
+
if not prune or task.short_qualified_name not in processed:
|
|
712
|
+
for i in range(0, len(children)):
|
|
713
|
+
_display(children[i], indent + 1, last=(last or []) + [i + 1 != len(children)])
|
|
714
|
+
|
|
715
|
+
if prune:
|
|
716
|
+
processed.add(task.short_qualified_name)
|
|
717
|
+
|
|
631
718
|
for task in tasklist:
|
|
632
719
|
_display(task)
|
|
633
720
|
else:
|
|
@@ -639,58 +726,103 @@ def docs():
|
|
|
639
726
|
"""
|
|
640
727
|
Opens the Jolt documentation in the default webbrowser.
|
|
641
728
|
"""
|
|
642
|
-
|
|
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.")
|
|
643
737
|
|
|
644
738
|
|
|
645
|
-
@cli.command(
|
|
646
|
-
@click.argument("task", type=str, nargs=-1, required=True)
|
|
647
|
-
@click.option("-
|
|
648
|
-
|
|
649
|
-
@click.option("-
|
|
739
|
+
@cli.command()
|
|
740
|
+
@click.argument("task", type=str, nargs=-1, required=True, shell_complete=_autocomplete_tasks)
|
|
741
|
+
@click.option("-c", "--copy", type=click.Path(),
|
|
742
|
+
help="Copy artifact content to directory PATH.")
|
|
743
|
+
@click.option("-ca", "--copy-all", type=click.Path(), help="Copy artifacts, including dependency artifacts, to directory PATH. Implies --deps.")
|
|
744
|
+
@click.option("-d", "--deps", is_flag=True, help="Download dependencies.")
|
|
650
745
|
@click.pass_context
|
|
651
|
-
|
|
746
|
+
@hooks.cli_download
|
|
747
|
+
def download(ctx, task, deps, copy, copy_all):
|
|
652
748
|
"""
|
|
653
|
-
|
|
749
|
+
Download task artifacts from remote caches.
|
|
750
|
+
|
|
751
|
+
No attempt to build the artifact is made if it is not present
|
|
752
|
+
in configured remote caches.
|
|
654
753
|
|
|
655
|
-
|
|
754
|
+
Task dependencies may optionally be downloaded by passing the -d option.
|
|
656
755
|
"""
|
|
657
|
-
manifest = ctx.obj["manifest"]
|
|
658
756
|
|
|
659
|
-
|
|
757
|
+
raise_error_if(copy and copy_all, "--copy and --copy-all are mutually exclusive")
|
|
758
|
+
if copy_all:
|
|
759
|
+
deps = True
|
|
760
|
+
|
|
761
|
+
options = JoltOptions()
|
|
660
762
|
acache = cache.ArtifactCache.get(options)
|
|
661
|
-
|
|
763
|
+
hooks.TaskHookRegistry.get(options)
|
|
764
|
+
executors = scheduler.ExecutorRegistry.get(options)
|
|
662
765
|
registry = TaskRegistry.get()
|
|
766
|
+
strategy = scheduler.DownloadStrategy(executors, acache)
|
|
767
|
+
queue = scheduler.TaskQueue()
|
|
768
|
+
gb = graph.GraphBuilder(registry, acache, options, progress=True)
|
|
769
|
+
dag = gb.build(task)
|
|
663
770
|
|
|
664
|
-
|
|
665
|
-
|
|
771
|
+
if not deps:
|
|
772
|
+
for task in dag.tasks:
|
|
773
|
+
if not task.is_goal():
|
|
774
|
+
task.pruned()
|
|
666
775
|
|
|
667
|
-
|
|
668
|
-
|
|
776
|
+
all_tasks = dag.tasks
|
|
777
|
+
goal_tasks = dag.goals
|
|
669
778
|
|
|
670
|
-
|
|
671
|
-
(
|
|
672
|
-
|
|
673
|
-
for t in dag.tasks if t.is_cacheable()]
|
|
779
|
+
try:
|
|
780
|
+
with log.progress("Progress", dag.number_of_tasks(), " tasks", estimates=False, debug=False) as p:
|
|
781
|
+
in_progress = set()
|
|
674
782
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
not remove and not available, task,
|
|
678
|
-
"Task artifact is not available in any cache, build it first")
|
|
783
|
+
while dag.has_tasks() or not queue.empty():
|
|
784
|
+
leafs = dag.select(lambda graph, task: task.is_ready() and task not in in_progress)
|
|
679
785
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
manifest.remove_task(manifest_task)
|
|
686
|
-
continue
|
|
687
|
-
if not remove:
|
|
688
|
-
if not manifest_task:
|
|
689
|
-
manifest_task = manifest.create_task()
|
|
690
|
-
manifest_task.name = task.qualified_name
|
|
691
|
-
manifest_task.identity = task.identity
|
|
786
|
+
while leafs:
|
|
787
|
+
task = leafs.pop()
|
|
788
|
+
executor = strategy.create_executor({}, task)
|
|
789
|
+
queue.submit(executor)
|
|
790
|
+
in_progress.add(task)
|
|
692
791
|
|
|
693
|
-
|
|
792
|
+
task, error = queue.wait()
|
|
793
|
+
p.update(1)
|
|
794
|
+
|
|
795
|
+
copy_tasks = goal_tasks if not copy_all else all_tasks
|
|
796
|
+
for goal in copy_tasks:
|
|
797
|
+
if goal.is_available_locally():
|
|
798
|
+
for artifact in goal.artifacts:
|
|
799
|
+
if copy:
|
|
800
|
+
log.info("Copying: {0}", artifact.path)
|
|
801
|
+
artifact.copy("*", utils.as_dirpath(fs.path.join(workdir, click.format_filename(copy))), symlinks=True)
|
|
802
|
+
elif copy_all:
|
|
803
|
+
log.info("Copying: {0}", artifact.path)
|
|
804
|
+
artifact.copy("*", utils.as_dirpath(fs.path.join(workdir, click.format_filename(copy_all))), symlinks=True)
|
|
805
|
+
elif goal.is_goal():
|
|
806
|
+
log.info("Location: {0}", artifact.path)
|
|
807
|
+
|
|
808
|
+
except KeyboardInterrupt:
|
|
809
|
+
print()
|
|
810
|
+
log.warning("Interrupted by user")
|
|
811
|
+
try:
|
|
812
|
+
queue.abort()
|
|
813
|
+
executors.shutdown()
|
|
814
|
+
sys.exit(1)
|
|
815
|
+
except KeyboardInterrupt:
|
|
816
|
+
print()
|
|
817
|
+
log.warning("Interrupted again, exiting")
|
|
818
|
+
os._exit(1)
|
|
819
|
+
|
|
820
|
+
except Exception as e:
|
|
821
|
+
log.set_interactive(True)
|
|
822
|
+
raise e
|
|
823
|
+
|
|
824
|
+
finally:
|
|
825
|
+
queue.shutdown()
|
|
694
826
|
|
|
695
827
|
|
|
696
828
|
@cli.command(name="list")
|
|
@@ -713,6 +845,8 @@ def _list(ctx, task=None, all=False, reverse=None):
|
|
|
713
845
|
|
|
714
846
|
raise_error_if(not task and reverse, "TASK required with --reverse")
|
|
715
847
|
|
|
848
|
+
options = JoltOptions()
|
|
849
|
+
acache = cache.ArtifactCache.get(options)
|
|
716
850
|
registry = TaskRegistry.get()
|
|
717
851
|
|
|
718
852
|
if not task:
|
|
@@ -726,7 +860,7 @@ def _list(ctx, task=None, all=False, reverse=None):
|
|
|
726
860
|
reverse = [utils.stable_task_name(t) for t in utils.as_list(reverse or [])]
|
|
727
861
|
|
|
728
862
|
try:
|
|
729
|
-
dag = graph.GraphBuilder(registry,
|
|
863
|
+
dag = graph.GraphBuilder(registry, acache).build(task, influence=False)
|
|
730
864
|
except JoltError as e:
|
|
731
865
|
raise e
|
|
732
866
|
except Exception:
|
|
@@ -760,16 +894,21 @@ def _log(follow, delete):
|
|
|
760
894
|
Display the Jolt log file.
|
|
761
895
|
|
|
762
896
|
"""
|
|
897
|
+
if not log.logfiles:
|
|
898
|
+
print("No logs exist")
|
|
899
|
+
return
|
|
900
|
+
|
|
763
901
|
if follow:
|
|
764
|
-
subprocess.call("tail -f {0}".format(
|
|
902
|
+
subprocess.call("tail -f {0}".format(log.logfiles[-1]), shell=True)
|
|
765
903
|
elif delete:
|
|
766
|
-
|
|
904
|
+
for file in log.logfiles:
|
|
905
|
+
fs.unlink(file)
|
|
767
906
|
else:
|
|
768
907
|
t = tools.Tools()
|
|
769
|
-
configured_pager = config.get("jolt", "pager", environ.get("PAGER", None))
|
|
908
|
+
configured_pager = config.get("jolt", "pager", os.environ.get("PAGER", None))
|
|
770
909
|
for pager in [configured_pager, "less", "more", "cat"]:
|
|
771
910
|
if pager and t.which(pager):
|
|
772
|
-
return subprocess.call("{1} {0}".format(
|
|
911
|
+
return subprocess.call("{1} {0}".format(log.logfiles[-1], pager), shell=True)
|
|
773
912
|
print(t.read_file(logfile))
|
|
774
913
|
|
|
775
914
|
|
|
@@ -821,9 +960,8 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
|
|
|
821
960
|
|
|
822
961
|
print()
|
|
823
962
|
print(" Requirements")
|
|
824
|
-
manifest = ctx.obj["manifest"]
|
|
825
963
|
try:
|
|
826
|
-
task = task_registry.get_task(task_name
|
|
964
|
+
task = task_registry.get_task(task_name)
|
|
827
965
|
for req in sorted(utils.as_list(utils.call_or_return(task, task.requires))):
|
|
828
966
|
print(" {0}".format(task.tools.expand(req)))
|
|
829
967
|
if not task.requires:
|
|
@@ -843,8 +981,9 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
|
|
|
843
981
|
task.taint = salt
|
|
844
982
|
|
|
845
983
|
if artifact:
|
|
984
|
+
options = JoltOptions(salt=salt)
|
|
846
985
|
acache = cache.ArtifactCache.get()
|
|
847
|
-
builder = graph.GraphBuilder(task_registry,
|
|
986
|
+
builder = graph.GraphBuilder(task_registry, acache, options)
|
|
848
987
|
dag = builder.build([task.qualified_name])
|
|
849
988
|
tasks = dag.select(lambda graph, node: node.task is task)
|
|
850
989
|
assert len(tasks) == 1, "graph produced multiple tasks, one expected"
|
|
@@ -853,14 +992,14 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
|
|
|
853
992
|
|
|
854
993
|
print(" Cache")
|
|
855
994
|
print(" Identity {0}".format(proxy.identity))
|
|
856
|
-
if
|
|
857
|
-
|
|
995
|
+
if proxy.is_available_locally():
|
|
996
|
+
for artifact in filter(lambda a: not a.is_session(), proxy.artifacts):
|
|
858
997
|
print(" Location {0}".format(artifact.path))
|
|
859
998
|
print(" Local True ({0})".format(
|
|
860
|
-
utils.as_human_size(
|
|
999
|
+
utils.as_human_size(sum([artifact.get_size() for artifact in proxy.artifacts]))))
|
|
861
1000
|
else:
|
|
862
1001
|
print(" Local False")
|
|
863
|
-
print(" Remote {0}".format(
|
|
1002
|
+
print(" Remote {0}".format(proxy.is_available_remotely(cache=False)))
|
|
864
1003
|
print()
|
|
865
1004
|
|
|
866
1005
|
if influence:
|
|
@@ -908,10 +1047,10 @@ def _export(ctx, task):
|
|
|
908
1047
|
executors = scheduler.ExecutorRegistry.get()
|
|
909
1048
|
strategy = scheduler.LocalStrategy(executors, acache)
|
|
910
1049
|
|
|
911
|
-
dag = graph.GraphBuilder(registry,
|
|
1050
|
+
dag = graph.GraphBuilder(registry, acache)
|
|
912
1051
|
dag = dag.build(task)
|
|
913
1052
|
|
|
914
|
-
gp = graph.GraphPruner(strategy)
|
|
1053
|
+
gp = graph.GraphPruner(acache, strategy)
|
|
915
1054
|
dag = gp.prune(dag)
|
|
916
1055
|
|
|
917
1056
|
class Export(object):
|
|
@@ -936,17 +1075,97 @@ def _export(ctx, task):
|
|
|
936
1075
|
context = Context(tasks)
|
|
937
1076
|
|
|
938
1077
|
for task in context.tasks:
|
|
939
|
-
artifact
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
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")
|
|
943
1082
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1083
|
+
visitor = Export()
|
|
1084
|
+
cache.visit_artifact(task, artifact, visitor)
|
|
1085
|
+
context.add_export(task, visitor)
|
|
947
1086
|
|
|
948
1087
|
script = utils.render(
|
|
949
1088
|
"export.sh.template",
|
|
950
1089
|
ctx=context)
|
|
951
1090
|
|
|
952
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)
|