dirigent-block-execute 0.17.2__tar.gz → 0.17.3__tar.gz
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.
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/PKG-INFO +4 -4
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/pyproject.toml +4 -4
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/pyproject.toml.orig +4 -4
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/build.py +2 -6
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/compose.py +6 -14
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/docker.py +2 -7
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/git.py +3 -6
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/shell.py +1 -6
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/subprocess.py +27 -20
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/LICENSE +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/README.md +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/__init__.py +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/capture.py +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/environment.py +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/messages.py +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/py.typed +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/reap.py +0 -0
- {dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/secrets.py +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dirigent-block-execute
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.3
|
|
4
4
|
Summary: The execute block family for dirigent: shell, docker, compose, buildx, and a checkout.
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
License-File: LICENSE
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
-
Requires-Dist: dirigent-block-http==0.17.
|
|
10
|
-
Requires-Dist: dirigent-common==0.17.
|
|
11
|
-
Requires-Dist: dirigent-plugin==0.17.
|
|
9
|
+
Requires-Dist: dirigent-block-http==0.17.3
|
|
10
|
+
Requires-Dist: dirigent-common==0.17.3
|
|
11
|
+
Requires-Dist: dirigent-plugin==0.17.3
|
|
12
12
|
Requires-Dist: httpx2>=2.12.0
|
|
13
13
|
Requires-Python: >=3.13
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-block-execute"
|
|
3
|
-
version = "0.17.
|
|
3
|
+
version = "0.17.3"
|
|
4
4
|
description = "The execute block family for dirigent: shell, docker, compose, buildx, and a checkout."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -11,9 +11,9 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.13",
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"dirigent-block-http==0.17.
|
|
15
|
-
"dirigent-common==0.17.
|
|
16
|
-
"dirigent-plugin==0.17.
|
|
14
|
+
"dirigent-block-http==0.17.3",
|
|
15
|
+
"dirigent-common==0.17.3",
|
|
16
|
+
"dirigent-plugin==0.17.3",
|
|
17
17
|
"httpx2>=2.12.0",
|
|
18
18
|
]
|
|
19
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-block-execute"
|
|
3
|
-
version = "0.17.
|
|
3
|
+
version = "0.17.3"
|
|
4
4
|
description = "The execute block family for dirigent: shell, docker, compose, buildx, and a checkout."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -11,9 +11,9 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.13",
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"dirigent-block-http==0.17.
|
|
15
|
-
"dirigent-common==0.17.
|
|
16
|
-
"dirigent-plugin==0.17.
|
|
14
|
+
"dirigent-block-http==0.17.3",
|
|
15
|
+
"dirigent-common==0.17.3",
|
|
16
|
+
"dirigent-plugin==0.17.3",
|
|
17
17
|
"httpx2>=2.12.0",
|
|
18
18
|
]
|
|
19
19
|
|
{dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/build.py
RENAMED
|
@@ -176,22 +176,18 @@ class DockerBuildOperator(Operator[DockerBuildConfig, DockerBuildOutput]):
|
|
|
176
176
|
if config.push and (settings is None or not settings.authenticates):
|
|
177
177
|
raise BlockFailure(NO_REGISTRY_CREDENTIAL, error_class=ErrorClass.REJECTED)
|
|
178
178
|
timeout = config.timeout.total_seconds()
|
|
179
|
-
artifacts = subprocess.prefix(ctx, "build")
|
|
180
|
-
stdout_uri = f"{artifacts}-stdout.txt"
|
|
181
|
-
build_log_uri = f"{artifacts}-build.log"
|
|
182
179
|
|
|
183
180
|
with sealed(settings, root, isolate_config=config.push) as material:
|
|
184
181
|
environ = daemon_environment(
|
|
185
182
|
subprocess.environment([*DAEMON_ENV, *config.env_allowlist], config.env, root), material
|
|
186
183
|
)
|
|
187
184
|
write_cli_config(root / ".docker")
|
|
188
|
-
code, out, err = await subprocess.run(
|
|
185
|
+
code, out, err, stdout_uri, build_log_uri = await subprocess.run(
|
|
189
186
|
directory=root,
|
|
190
187
|
ctx=ctx,
|
|
191
|
-
stdout_uri=stdout_uri,
|
|
192
|
-
stderr_uri=build_log_uri,
|
|
193
188
|
timeout_seconds=timeout,
|
|
194
189
|
environ=environ,
|
|
190
|
+
stderr_name="build.log",
|
|
195
191
|
argv=build_argv(config, context, iidfile),
|
|
196
192
|
what="docker buildx build",
|
|
197
193
|
redact=material.secrets,
|
|
@@ -342,7 +342,6 @@ class DockerComposeUpOperator(Operator[DockerComposeUpConfig, DockerComposeUpOut
|
|
|
342
342
|
assert compose_file is not None
|
|
343
343
|
project = config.project_name or _default_project(ctx)
|
|
344
344
|
timeout = config.timeout.total_seconds()
|
|
345
|
-
stdout_uri, stderr_uri = _artifact_uris(ctx, "up")
|
|
346
345
|
|
|
347
346
|
with sealed(_connection(config.connection, ctx), root) as material:
|
|
348
347
|
environ = daemon_environment(
|
|
@@ -350,13 +349,13 @@ class DockerComposeUpOperator(Operator[DockerComposeUpConfig, DockerComposeUpOut
|
|
|
350
349
|
)
|
|
351
350
|
write_cli_config(root / ".docker")
|
|
352
351
|
try:
|
|
353
|
-
code, out, err = await subprocess.run(
|
|
352
|
+
code, out, err, stdout_uri, stderr_uri = await subprocess.run(
|
|
354
353
|
directory=root,
|
|
355
354
|
ctx=ctx,
|
|
356
|
-
stdout_uri=stdout_uri,
|
|
357
|
-
stderr_uri=stderr_uri,
|
|
358
355
|
timeout_seconds=timeout,
|
|
359
356
|
environ=environ,
|
|
357
|
+
stdout_name="up-stdout",
|
|
358
|
+
stderr_name="up-stderr",
|
|
360
359
|
argv=up_argv(config, project, compose_file, root),
|
|
361
360
|
what="docker compose up",
|
|
362
361
|
)
|
|
@@ -411,20 +410,19 @@ class DockerComposeDownOperator(Operator[DockerComposeDownConfig, DockerComposeD
|
|
|
411
410
|
compose_file = _compose_file(config.file, config.content, ctx, root)
|
|
412
411
|
project = config.project_name or _default_project(ctx)
|
|
413
412
|
timeout = config.timeout.total_seconds()
|
|
414
|
-
stdout_uri, stderr_uri = _artifact_uris(ctx, "down")
|
|
415
413
|
|
|
416
414
|
with sealed(_connection(config.connection, ctx), root) as material:
|
|
417
415
|
environ = daemon_environment(
|
|
418
416
|
subprocess.environment([*DAEMON_ENV, *config.env_allowlist], config.env, root), material
|
|
419
417
|
)
|
|
420
418
|
write_cli_config(root / ".docker")
|
|
421
|
-
code, out, err = await subprocess.run(
|
|
419
|
+
code, out, err, stdout_uri, stderr_uri = await subprocess.run(
|
|
422
420
|
directory=root,
|
|
423
421
|
ctx=ctx,
|
|
424
|
-
stdout_uri=stdout_uri,
|
|
425
|
-
stderr_uri=stderr_uri,
|
|
426
422
|
timeout_seconds=timeout,
|
|
427
423
|
environ=environ,
|
|
424
|
+
stdout_name="down-stdout",
|
|
425
|
+
stderr_name="down-stderr",
|
|
428
426
|
argv=down_argv(config, project, compose_file, root),
|
|
429
427
|
what="docker compose down",
|
|
430
428
|
)
|
|
@@ -656,12 +654,6 @@ def _work_relative(path: Path, root: Path) -> str:
|
|
|
656
654
|
return str(path.relative_to(root))
|
|
657
655
|
|
|
658
656
|
|
|
659
|
-
def _artifact_uris(ctx: StepContext, action: str) -> tuple[str, str]:
|
|
660
|
-
"""Where the CLI's two streams are written for this attempt."""
|
|
661
|
-
root = f"{subprocess.prefix(ctx, 'compose')}-{action}"
|
|
662
|
-
return f"{root}-stdout.txt", f"{root}-stderr.txt"
|
|
663
|
-
|
|
664
|
-
|
|
665
657
|
def _default_project(ctx: StepContext) -> str:
|
|
666
658
|
"""A project name that is the same across a run's up, drive and down, and unique between runs."""
|
|
667
659
|
return f"dirigent-{ctx.run_id.hex}"
|
{dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/docker.py
RENAMED
|
@@ -582,21 +582,16 @@ class DockerRunOperator(Operator[DockerRunConfig, DockerRunOutput]):
|
|
|
582
582
|
|
|
583
583
|
async def fetch(self, handle: RemoteHandle, config: DockerRunConfig, ctx: StepContext) -> DockerRunOutput:
|
|
584
584
|
"""Collect the exit code, the streams, and the declared files, then drop the container."""
|
|
585
|
-
artifacts = subprocess.prefix(ctx, "docker")
|
|
586
|
-
stdout_uri = f"{artifacts}-stdout.txt"
|
|
587
|
-
stderr_uri = f"{artifacts}-stderr.txt"
|
|
588
585
|
with _material(config, ctx) as environ:
|
|
589
586
|
async with _daemon(config, environ) as daemon:
|
|
590
587
|
inspected = await daemon.inspect(handle.ref)
|
|
591
588
|
if inspected is None:
|
|
592
589
|
raise BlockFailure(CONTAINER_GONE, error_class=ErrorClass.TRANSIENT, container=handle.ref[:12])
|
|
593
|
-
async with (
|
|
594
|
-
ctx.storage.open_write(stdout_uri) as out_sink,
|
|
595
|
-
ctx.storage.open_write(stderr_uri) as err_sink,
|
|
596
|
-
):
|
|
590
|
+
async with ctx.capture("stdout") as out_sink, ctx.capture("stderr") as err_sink:
|
|
597
591
|
stdout, stderr = await drain_logs(
|
|
598
592
|
daemon, handle.ref, ctx.inline_capture, sinks=(out_sink, err_sink)
|
|
599
593
|
)
|
|
594
|
+
stdout_uri, stderr_uri = out_sink.uri, err_sink.uri
|
|
600
595
|
collected = await _collect_outputs(config, handle, ctx)
|
|
601
596
|
# The run log gets only what the probes have not already streamed: the segment
|
|
602
597
|
# between the last committed cursor and the exit. The full read above is for
|
{dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/git.py
RENAMED
|
@@ -381,16 +381,13 @@ class _Git:
|
|
|
381
381
|
artifact rather than a buffer in the worker. Each command writes its own pair, named for
|
|
382
382
|
its subcommand; the pair the last one wrote is what the output carries.
|
|
383
383
|
"""
|
|
384
|
-
|
|
385
|
-
stdout_uri = f"{artifacts}-stdout.txt"
|
|
386
|
-
stderr_uri = f"{artifacts}-stderr.txt"
|
|
387
|
-
code, out, err = await subprocess.run(
|
|
384
|
+
code, out, err, stdout_uri, stderr_uri = await subprocess.run(
|
|
388
385
|
directory=directory,
|
|
389
386
|
ctx=self.ctx,
|
|
390
|
-
stdout_uri=stdout_uri,
|
|
391
|
-
stderr_uri=stderr_uri,
|
|
392
387
|
timeout_seconds=self.timeout,
|
|
393
388
|
environ=self.environ,
|
|
389
|
+
stdout_name=f"{argv[0]}-stdout",
|
|
390
|
+
stderr_name=f"{argv[0]}-stderr",
|
|
394
391
|
argv=[self.binary, *argv],
|
|
395
392
|
what=f"git {argv[0]}",
|
|
396
393
|
redact=self.secrets,
|
{dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/shell.py
RENAMED
|
@@ -136,14 +136,9 @@ class ShellRunOperator(Operator[ShellRunConfig, ShellRunOutput]):
|
|
|
136
136
|
workspace = subprocess.workspace(ctx, "shell")
|
|
137
137
|
directory = workspace / config.cwd if config.cwd else workspace
|
|
138
138
|
directory.mkdir(parents=True, exist_ok=True)
|
|
139
|
-
|
|
140
|
-
stdout_uri = f"{artifacts}-stdout.txt"
|
|
141
|
-
stderr_uri = f"{artifacts}-stderr.txt"
|
|
142
|
-
code, out, err = await subprocess.run(
|
|
139
|
+
code, out, err, stdout_uri, stderr_uri = await subprocess.run(
|
|
143
140
|
directory=directory,
|
|
144
141
|
ctx=ctx,
|
|
145
|
-
stdout_uri=stdout_uri,
|
|
146
|
-
stderr_uri=stderr_uri,
|
|
147
142
|
timeout_seconds=config.timeout.total_seconds(),
|
|
148
143
|
environ=_environment(config, directory),
|
|
149
144
|
argv=config.argv or None,
|
|
@@ -14,6 +14,7 @@ import os
|
|
|
14
14
|
import signal
|
|
15
15
|
from collections.abc import Sequence
|
|
16
16
|
from pathlib import Path
|
|
17
|
+
from typing import NamedTuple
|
|
17
18
|
|
|
18
19
|
from dirigent_block_execute.capture import Drained, LiveLines, drain
|
|
19
20
|
from dirigent_block_execute.environment import allowed
|
|
@@ -24,22 +25,26 @@ from dirigent_plugin import BlockFailure, ErrorClass, StepContext
|
|
|
24
25
|
BASELINE_ENV = ("PATH", "LANG", "LC_ALL", "TZ")
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
class Ran(NamedTuple):
|
|
29
|
+
"""What one child process left behind: how it exited, both streams, and where they were written."""
|
|
30
|
+
|
|
31
|
+
code: int
|
|
32
|
+
out: Drained
|
|
33
|
+
err: Drained
|
|
34
|
+
stdout_uri: str
|
|
35
|
+
stderr_uri: str
|
|
36
|
+
|
|
37
|
+
|
|
27
38
|
def segment(ctx: StepContext, name: str) -> str:
|
|
28
|
-
"""The path this attempt's files sit at,
|
|
39
|
+
"""The path this attempt's files sit at, inside the run's work directory.
|
|
29
40
|
|
|
30
|
-
``{name}/{step}[/{item}]/attempt-{n}``.
|
|
31
|
-
|
|
32
|
-
out of each other's files and working directories.
|
|
41
|
+
``{name}/{step}[/{item}]/attempt-{n}``. The step key, and inside a fan-out the item id,
|
|
42
|
+
are what keep two steps of one run and two items of one step out of each other's files.
|
|
33
43
|
"""
|
|
34
44
|
item = f"/{ctx.run_item_id}" if ctx.run_item_id is not None else ""
|
|
35
45
|
return f"{name}/{ctx.step}{item}/attempt-{ctx.attempt}"
|
|
36
46
|
|
|
37
47
|
|
|
38
|
-
def prefix(ctx: StepContext, name: str) -> str:
|
|
39
|
-
"""The storage URI this attempt's artifact names are built on, under the run's scratch prefix."""
|
|
40
|
-
return f"{ctx.scratch.rstrip('/')}/{segment(ctx, name)}"
|
|
41
|
-
|
|
42
|
-
|
|
43
48
|
def workspace(ctx: StepContext, name: str) -> Path:
|
|
44
49
|
"""This attempt's working directory inside the run's work directory, made if absent."""
|
|
45
50
|
directory = ctx.work / segment(ctx, name)
|
|
@@ -59,22 +64,24 @@ async def run(
|
|
|
59
64
|
*,
|
|
60
65
|
directory: Path,
|
|
61
66
|
ctx: StepContext,
|
|
62
|
-
stdout_uri: str,
|
|
63
|
-
stderr_uri: str,
|
|
64
67
|
timeout_seconds: float,
|
|
65
68
|
environ: dict[str, str],
|
|
69
|
+
stdout_name: str = "stdout",
|
|
70
|
+
stderr_name: str = "stderr",
|
|
66
71
|
argv: list[str] | None = None,
|
|
67
72
|
command: str | None = None,
|
|
68
73
|
what: str = "the command",
|
|
69
74
|
redact: Sequence[str] = (),
|
|
70
|
-
) ->
|
|
75
|
+
) -> Ran:
|
|
71
76
|
"""Start the process, drain it under the timeout, and leave nothing of it behind.
|
|
72
77
|
|
|
73
|
-
Both pipes are read as the process writes them and written straight through to
|
|
74
|
-
so what is held is two bounded ends of each stream rather
|
|
75
|
-
prints more than the worker has memory for is a file, not
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
Both pipes are read as the process writes them and written straight through to a capture
|
|
79
|
+
the context opens and names, so what is held is two bounded ends of each stream rather
|
|
80
|
+
than all of it: a process that prints more than the worker has memory for is a file, not
|
|
81
|
+
a dead worker. The two names distinguish one command's streams from another's where a
|
|
82
|
+
step runs more than one. Each stream's first lines go into the run log where they are
|
|
83
|
+
printed, so a long command is visible working; ``redact`` names the strings it was
|
|
84
|
+
handed that must not reach one of them.
|
|
78
85
|
|
|
79
86
|
The process leads a session of its own, so what it starts is killable with it: a command
|
|
80
87
|
is usually ``sh -c``, and killing the shell alone leaves the children doing the work.
|
|
@@ -106,7 +113,7 @@ async def run(
|
|
|
106
113
|
pgid = group_of(process)
|
|
107
114
|
try:
|
|
108
115
|
async with asyncio.timeout(timeout_seconds):
|
|
109
|
-
async with ctx.
|
|
116
|
+
async with ctx.capture(stdout_name) as out_sink, ctx.capture(stderr_name) as err_sink:
|
|
110
117
|
out, err = await asyncio.gather(
|
|
111
118
|
drain(
|
|
112
119
|
process.stdout,
|
|
@@ -126,7 +133,7 @@ async def run(
|
|
|
126
133
|
raise BlockFailure(TIMED_OUT, error_class=ErrorClass.TRANSIENT, what=what, seconds=timeout_seconds) from error
|
|
127
134
|
finally:
|
|
128
135
|
await end(process, pgid)
|
|
129
|
-
return process.returncode or 0, out, err
|
|
136
|
+
return Ran(process.returncode or 0, out, err, out_sink.uri, err_sink.uri)
|
|
130
137
|
|
|
131
138
|
|
|
132
139
|
async def output(
|
|
@@ -207,12 +214,12 @@ def kill(pgid: int, pid: int) -> None:
|
|
|
207
214
|
|
|
208
215
|
__all__ = [
|
|
209
216
|
"BASELINE_ENV",
|
|
217
|
+
"Ran",
|
|
210
218
|
"end",
|
|
211
219
|
"environment",
|
|
212
220
|
"group_of",
|
|
213
221
|
"kill",
|
|
214
222
|
"output",
|
|
215
|
-
"prefix",
|
|
216
223
|
"run",
|
|
217
224
|
"segment",
|
|
218
225
|
"workspace",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/py.typed
RENAMED
|
File without changes
|
{dirigent_block_execute-0.17.2 → dirigent_block_execute-0.17.3}/src/dirigent_block_execute/reap.py
RENAMED
|
File without changes
|
|
File without changes
|