dirigent-cli 0.10.2__tar.gz → 0.12.0__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_cli-0.10.2 → dirigent_cli-0.12.0}/PKG-INFO +1 -1
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/pyproject.toml +1 -1
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/pyproject.toml.orig +1 -1
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/commands.py +11 -9
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/context.py +0 -7
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/main.py +38 -19
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/summaries.py +6 -1
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/LICENSE +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/README.md +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/__init__.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/aliases.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/formatters.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/graph.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/health.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/init_form.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/local.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/output.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/params.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/profiles.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/project.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/py.typed +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/reaper.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/scaffold.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/schemas.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/sources.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/stream.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/timing.py +0 -0
- {dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/triggers.py +0 -0
|
@@ -32,7 +32,6 @@ from dirigent_cli.local import (
|
|
|
32
32
|
from dirigent_cli.output import (
|
|
33
33
|
Detail,
|
|
34
34
|
age,
|
|
35
|
-
configure,
|
|
36
35
|
console,
|
|
37
36
|
elapsed,
|
|
38
37
|
emit_event,
|
|
@@ -510,10 +509,6 @@ def init_command(
|
|
|
510
509
|
|
|
511
510
|
from dirigent_core.auth import MIN_PASSWORD_LENGTH, WeakPassword
|
|
512
511
|
|
|
513
|
-
state = state_of(ctx)
|
|
514
|
-
# Run once by a person at a terminal, so it renders unless records were asked for.
|
|
515
|
-
if not state.chosen:
|
|
516
|
-
configure(output="console")
|
|
517
512
|
root = directory.resolve()
|
|
518
513
|
version = cli_version()
|
|
519
514
|
unasked = template is None and service is None and pack is None and not workflow
|
|
@@ -1523,7 +1518,7 @@ def log_events(entries: list[LogEntryOut], labels: dict[UUID, str] | None = None
|
|
|
1523
1518
|
|
|
1524
1519
|
def print_events(events: list[WatchEvent], level: Detail = Detail.SUMMARY, *, out: Sink | None = None) -> None:
|
|
1525
1520
|
"""Write transitions and log lines in the order they happened."""
|
|
1526
|
-
writer = out or Sink()
|
|
1521
|
+
writer = out or Sink(output_mode())
|
|
1527
1522
|
for when, _, subject, label in sorted(events, key=lambda event: (event[0], event[1])):
|
|
1528
1523
|
if isinstance(subject, LogEntryOut):
|
|
1529
1524
|
if subject.level is LogLevel.DEBUG and level is not Detail.FULL:
|
|
@@ -2365,7 +2360,7 @@ def workers_command(
|
|
|
2365
2360
|
def system_info(
|
|
2366
2361
|
ctx: typer.Context,
|
|
2367
2362
|
) -> None:
|
|
2368
|
-
"""Describe the instance, and
|
|
2363
|
+
"""Describe the instance, and repeat what each connection said when it was last checked."""
|
|
2369
2364
|
with client_for(state_of(ctx)) as dg:
|
|
2370
2365
|
info = dg.call(dg.system.info())
|
|
2371
2366
|
if state_of(ctx).json_output:
|
|
@@ -2388,9 +2383,16 @@ def system_info(
|
|
|
2388
2383
|
console.print()
|
|
2389
2384
|
table(
|
|
2390
2385
|
"connections",
|
|
2391
|
-
["code", "name", "kind", "
|
|
2386
|
+
["code", "name", "kind", "last check", "healthy", "detail"],
|
|
2392
2387
|
[
|
|
2393
|
-
[
|
|
2388
|
+
[
|
|
2389
|
+
row.code,
|
|
2390
|
+
row.name or "-",
|
|
2391
|
+
row.kind,
|
|
2392
|
+
moment(row.last_check_at),
|
|
2393
|
+
render_bool(row.last_check_healthy),
|
|
2394
|
+
row.last_check_detail or "-",
|
|
2395
|
+
]
|
|
2394
2396
|
for row in info.connections
|
|
2395
2397
|
],
|
|
2396
2398
|
)
|
|
@@ -40,13 +40,6 @@ class CliState(BaseModel):
|
|
|
40
40
|
debug_all: bool = False
|
|
41
41
|
output: Format = "json"
|
|
42
42
|
|
|
43
|
-
chosen: bool = False
|
|
44
|
-
"""Whether a flag or the environment named the output, rather than it being the default.
|
|
45
|
-
|
|
46
|
-
A command run once by a person -- ``dg init`` -- renders unless it was asked for records,
|
|
47
|
-
and it can only tell the difference if the default is distinguishable from a choice.
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
43
|
@property
|
|
51
44
|
def json_output(self) -> bool:
|
|
52
45
|
"""Report whether this invocation writes records rather than a rendering."""
|
|
@@ -7,7 +7,7 @@ from collections.abc import Sequence
|
|
|
7
7
|
from datetime import UTC, datetime, timedelta
|
|
8
8
|
from importlib.metadata import PackageNotFoundError, version
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import TYPE_CHECKING, Annotated, Any, cast
|
|
10
|
+
from typing import TYPE_CHECKING, Annotated, Any, TextIO, cast
|
|
11
11
|
|
|
12
12
|
import typer
|
|
13
13
|
from pydantic import ValidationError
|
|
@@ -23,7 +23,7 @@ from dirigent_client.enums import UserRole
|
|
|
23
23
|
from dirigent_core import migrations
|
|
24
24
|
from dirigent_core.config import STATE_DIR, Settings, get_settings, redacted_url, reset_settings_cache
|
|
25
25
|
from dirigent_core.logging import LOG_FORMAT_ENV, configure_logging, silence_stdout
|
|
26
|
-
from dirigent_core.protocol import FORMATS, Format, Record,
|
|
26
|
+
from dirigent_core.protocol import FORMATS, Format, Record, make
|
|
27
27
|
from dirigent_core.telemetry import configure_telemetry
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
@@ -158,22 +158,22 @@ def main_callback(
|
|
|
158
158
|
verbose=verbose,
|
|
159
159
|
debug=debug,
|
|
160
160
|
debug_all=debug_all,
|
|
161
|
-
output=
|
|
162
|
-
chosen=output is not None or json_output or LOG_FORMAT_ENV in os.environ,
|
|
161
|
+
output=resolve_output(output, json_output=json_output),
|
|
163
162
|
)
|
|
164
163
|
ctx.obj.configure_output()
|
|
165
164
|
|
|
166
165
|
|
|
167
|
-
def
|
|
168
|
-
"""Resolve the output: the flag, then the environment, then
|
|
166
|
+
def resolve_output(named: str | None, *, json_output: bool) -> Format:
|
|
167
|
+
"""Resolve the output: the flag, then the environment, then the terminal.
|
|
169
168
|
|
|
170
169
|
``--json`` is the same request as ``--output json``, and the environment is where a
|
|
171
|
-
container says it once.
|
|
172
|
-
|
|
170
|
+
container says it once. Unasked, a terminal gets the rendering and anything else gets
|
|
171
|
+
NDJSON: a pipe, a container's log, an agent's shell and CI are never terminals, so a
|
|
172
|
+
script reads records without asking, and a person reads lines without asking.
|
|
173
173
|
"""
|
|
174
174
|
chosen = named or ("json" if json_output else None) or os.environ.get(LOG_FORMAT_ENV)
|
|
175
175
|
if chosen is None:
|
|
176
|
-
return "json"
|
|
176
|
+
return "console" if sys.stdout.isatty() else "json"
|
|
177
177
|
resolved = chosen.lower()
|
|
178
178
|
if resolved not in FORMATS:
|
|
179
179
|
# The output that was asked for is exactly what is missing, so the refusal is written
|
|
@@ -643,7 +643,7 @@ def server(
|
|
|
643
643
|
os.environ[UI_ENV] = "true" if ui else "false"
|
|
644
644
|
reset_settings_cache()
|
|
645
645
|
settings = get_settings()
|
|
646
|
-
|
|
646
|
+
_process_logging(_level(ctx, settings), cap_foreign=_cap_foreign(ctx))
|
|
647
647
|
if settings.is_sqlite and settings.scheduler_enabled:
|
|
648
648
|
refuse(
|
|
649
649
|
"dg server embeds the scheduler, and leadership is a PostgreSQL advisory lock: on SQLite "
|
|
@@ -687,7 +687,7 @@ def docker_reap(
|
|
|
687
687
|
from dirigent_cli.stream import Sink
|
|
688
688
|
|
|
689
689
|
settings = get_settings()
|
|
690
|
-
|
|
690
|
+
_process_logging(_level(ctx, settings), cap_foreign=_cap_foreign(ctx), stream=sys.stderr)
|
|
691
691
|
if not reaper.reachable():
|
|
692
692
|
refuse(
|
|
693
693
|
"docker is not on this host's PATH, so there is no daemon to reap stacks from",
|
|
@@ -830,11 +830,9 @@ def dev(
|
|
|
830
830
|
settings = get_settings()
|
|
831
831
|
# A developer convenience starts its own logging at WARNING and -v is what asks for more,
|
|
832
832
|
# while a named DIRIGENT_LOG_LEVEL still wins.
|
|
833
|
-
|
|
833
|
+
_process_logging(
|
|
834
834
|
state.level if state is not None else "WARNING",
|
|
835
|
-
"json",
|
|
836
835
|
cap_foreign=state is None or not state.debug_all,
|
|
837
|
-
stream=sys.stdout,
|
|
838
836
|
)
|
|
839
837
|
if not settings.is_sqlite:
|
|
840
838
|
refuse(
|
|
@@ -919,18 +917,39 @@ def dev_started(
|
|
|
919
917
|
|
|
920
918
|
|
|
921
919
|
def emit(record: Record) -> None:
|
|
922
|
-
"""Write one record to the stream
|
|
920
|
+
"""Write one record to the stream in this invocation's output, and flush it.
|
|
923
921
|
|
|
924
922
|
A reader that has gone ends the stream rather than the process: the pipe is closed and
|
|
925
923
|
every later write goes to the void, while shutdown runs to completion.
|
|
926
924
|
"""
|
|
925
|
+
from dirigent_cli.output import output_mode
|
|
926
|
+
from dirigent_cli.stream import Sink
|
|
927
|
+
|
|
927
928
|
try:
|
|
928
|
-
|
|
929
|
-
sys.stdout.flush()
|
|
929
|
+
Sink(output_mode()).write(record)
|
|
930
930
|
except BrokenPipeError:
|
|
931
931
|
silence_stdout()
|
|
932
932
|
|
|
933
933
|
|
|
934
|
+
def _process_logging(level: str, *, cap_foreign: bool, stream: TextIO | None = None) -> None:
|
|
935
|
+
"""Point a process's logging at its stream, in this invocation's output.
|
|
936
|
+
|
|
937
|
+
The console grammar is the one records are rendered in, so a process read at a terminal
|
|
938
|
+
is one stream of lines, and a process read by a pipe is one stream of records.
|
|
939
|
+
"""
|
|
940
|
+
from dirigent_cli.output import output_mode
|
|
941
|
+
from dirigent_cli.stream import ansi
|
|
942
|
+
|
|
943
|
+
mode = output_mode()
|
|
944
|
+
configure_logging(
|
|
945
|
+
level,
|
|
946
|
+
mode,
|
|
947
|
+
cap_foreign=cap_foreign,
|
|
948
|
+
stream=stream if stream is not None else sys.stdout,
|
|
949
|
+
paint=ansi if mode == "console" else None,
|
|
950
|
+
)
|
|
951
|
+
|
|
952
|
+
|
|
934
953
|
async def dev_admin(settings: Settings) -> tuple[str | None, str | None]:
|
|
935
954
|
"""Name the admin to log in as, and mint it a token where one is owed.
|
|
936
955
|
|
|
@@ -1007,7 +1026,7 @@ def worker(
|
|
|
1007
1026
|
import asyncio
|
|
1008
1027
|
|
|
1009
1028
|
settings = get_settings()
|
|
1010
|
-
|
|
1029
|
+
_process_logging(_level(ctx, settings), cap_foreign=_cap_foreign(ctx))
|
|
1011
1030
|
configure_telemetry(settings)
|
|
1012
1031
|
if settings.is_sqlite:
|
|
1013
1032
|
refuse(
|
|
@@ -1042,7 +1061,7 @@ def scheduler_command(ctx: typer.Context) -> None:
|
|
|
1042
1061
|
import asyncio
|
|
1043
1062
|
|
|
1044
1063
|
settings = get_settings()
|
|
1045
|
-
|
|
1064
|
+
_process_logging(_level(ctx, settings), cap_foreign=_cap_foreign(ctx))
|
|
1046
1065
|
configure_telemetry(settings)
|
|
1047
1066
|
if settings.is_sqlite:
|
|
1048
1067
|
refuse(
|
|
@@ -217,7 +217,11 @@ def _kept(record: Record) -> list[RenderableType]:
|
|
|
217
217
|
|
|
218
218
|
|
|
219
219
|
def _issued(record: Record) -> RenderableType | None:
|
|
220
|
-
"""Hand over a minted token: the form it is used in, and that it is shown once.
|
|
220
|
+
"""Hand over a minted token: the form it is used in, and that it is shown once.
|
|
221
|
+
|
|
222
|
+
A login's ``token.issued`` and ``dg dev``'s ``starting`` both carry one exactly once; a
|
|
223
|
+
process record without a token, ``ready`` or ``state cleared``, is its line alone.
|
|
224
|
+
"""
|
|
221
225
|
token = record.get("token")
|
|
222
226
|
if not token:
|
|
223
227
|
return None
|
|
@@ -404,6 +408,7 @@ RENDERERS: Final[Mapping[str, Callable[[Record], RenderableType | None]]] = {
|
|
|
404
408
|
"validation": _validation,
|
|
405
409
|
"error": _refusal,
|
|
406
410
|
"token.issued": _issued,
|
|
411
|
+
"process": _issued,
|
|
407
412
|
"project.scaffolded": _scaffolded,
|
|
408
413
|
"instance.initialised": _initialised,
|
|
409
414
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/__init__.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/operator.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.10.2 → dirigent_cli-0.12.0}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|