dirigent-cli 0.22.0__tar.gz → 0.23.1__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.22.0 → dirigent_cli-0.23.1}/PKG-INFO +9 -9
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/pyproject.toml +9 -9
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/pyproject.toml.orig +9 -9
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/commands.py +96 -14
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/messages.py +5 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/output.py +5 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/seeding.py +13 -3
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/summaries.py +29 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/triggers.py +2 -2
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/LICENSE +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/README.md +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/__init__.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/aliases.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/context.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/explain.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/formatters.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/graph.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/health.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/init_form.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/local.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/main.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/params.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/profiles.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/project.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/py.typed +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/reaper.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/scaffold.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/schemas.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/sources.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/starters.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/stream.py +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
- {dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/timing.py +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dirigent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.23.1
|
|
4
4
|
Summary: The dirigent command line interface (dirigent / dg).
|
|
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-execute==0.
|
|
10
|
-
Requires-Dist: dirigent-blocks==0.
|
|
11
|
-
Requires-Dist: dirigent-client==0.
|
|
12
|
-
Requires-Dist: dirigent-common==0.
|
|
13
|
-
Requires-Dist: dirigent-core==0.
|
|
14
|
-
Requires-Dist: dirigent-examples==0.
|
|
15
|
-
Requires-Dist: dirigent-plugin==0.
|
|
16
|
-
Requires-Dist: dirigent-server==0.
|
|
9
|
+
Requires-Dist: dirigent-block-execute==0.23.1
|
|
10
|
+
Requires-Dist: dirigent-blocks==0.23.1
|
|
11
|
+
Requires-Dist: dirigent-client==0.23.1
|
|
12
|
+
Requires-Dist: dirigent-common==0.23.1
|
|
13
|
+
Requires-Dist: dirigent-core==0.23.1
|
|
14
|
+
Requires-Dist: dirigent-examples==0.23.1
|
|
15
|
+
Requires-Dist: dirigent-plugin==0.23.1
|
|
16
|
+
Requires-Dist: dirigent-server==0.23.1
|
|
17
17
|
Requires-Dist: httpx2>=2.12.0
|
|
18
18
|
Requires-Dist: python-dotenv>=1.1.0
|
|
19
19
|
Requires-Dist: pyyaml>=6.0.3
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.23.1"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -11,14 +11,14 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.13",
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"dirigent-block-execute==0.
|
|
15
|
-
"dirigent-blocks==0.
|
|
16
|
-
"dirigent-client==0.
|
|
17
|
-
"dirigent-common==0.
|
|
18
|
-
"dirigent-core==0.
|
|
19
|
-
"dirigent-examples==0.
|
|
20
|
-
"dirigent-plugin==0.
|
|
21
|
-
"dirigent-server==0.
|
|
14
|
+
"dirigent-block-execute==0.23.1",
|
|
15
|
+
"dirigent-blocks==0.23.1",
|
|
16
|
+
"dirigent-client==0.23.1",
|
|
17
|
+
"dirigent-common==0.23.1",
|
|
18
|
+
"dirigent-core==0.23.1",
|
|
19
|
+
"dirigent-examples==0.23.1",
|
|
20
|
+
"dirigent-plugin==0.23.1",
|
|
21
|
+
"dirigent-server==0.23.1",
|
|
22
22
|
"httpx2>=2.12.0",
|
|
23
23
|
"python-dotenv>=1.1.0",
|
|
24
24
|
"pyyaml>=6.0.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.23.1"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -11,14 +11,14 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.13",
|
|
12
12
|
]
|
|
13
13
|
dependencies = [
|
|
14
|
-
"dirigent-block-execute==0.
|
|
15
|
-
"dirigent-blocks==0.
|
|
16
|
-
"dirigent-client==0.
|
|
17
|
-
"dirigent-common==0.
|
|
18
|
-
"dirigent-core==0.
|
|
19
|
-
"dirigent-examples==0.
|
|
20
|
-
"dirigent-plugin==0.
|
|
21
|
-
"dirigent-server==0.
|
|
14
|
+
"dirigent-block-execute==0.23.1",
|
|
15
|
+
"dirigent-blocks==0.23.1",
|
|
16
|
+
"dirigent-client==0.23.1",
|
|
17
|
+
"dirigent-common==0.23.1",
|
|
18
|
+
"dirigent-core==0.23.1",
|
|
19
|
+
"dirigent-examples==0.23.1",
|
|
20
|
+
"dirigent-plugin==0.23.1",
|
|
21
|
+
"dirigent-server==0.23.1",
|
|
22
22
|
"httpx2>=2.12.0",
|
|
23
23
|
"python-dotenv>=1.1.0",
|
|
24
24
|
"pyyaml>=6.0.3",
|
|
@@ -74,6 +74,7 @@ from dirigent_cli.messages import (
|
|
|
74
74
|
RUN_SKIPPED,
|
|
75
75
|
SCAFFOLD_REFUSED,
|
|
76
76
|
SCHEMA_NOT_AN_OBJECT,
|
|
77
|
+
SCHEMA_NOTHING_TO_CHANGE,
|
|
77
78
|
SCHEMA_UNREADABLE,
|
|
78
79
|
SET_DG_TOKEN,
|
|
79
80
|
SOURCE_REFUSED,
|
|
@@ -87,6 +88,7 @@ from dirigent_cli.messages import (
|
|
|
87
88
|
from dirigent_cli.messages import WINDOW_GRAMMAR as WINDOW_GRAMMAR_MESSAGE
|
|
88
89
|
from dirigent_cli.output import (
|
|
89
90
|
CONNECTION_HEALTH,
|
|
91
|
+
RUNS_IN_FLIGHT,
|
|
90
92
|
Detail,
|
|
91
93
|
age,
|
|
92
94
|
connection_health,
|
|
@@ -135,6 +137,7 @@ from dirigent_cli.sources import (
|
|
|
135
137
|
from dirigent_cli.stream import Sink, track_steps, use_scratch_prefix
|
|
136
138
|
from dirigent_cli.timing import RunProfile, attempt_timing, by_step, profile, step_timing
|
|
137
139
|
from dirigent_client import (
|
|
140
|
+
CLEAR,
|
|
138
141
|
ApplyResult,
|
|
139
142
|
AttemptEvent,
|
|
140
143
|
AttemptOut,
|
|
@@ -142,6 +145,7 @@ from dirigent_client import (
|
|
|
142
145
|
BackfillAccepted,
|
|
143
146
|
BlockKind,
|
|
144
147
|
Catalog,
|
|
148
|
+
Clear,
|
|
145
149
|
DocumentKind,
|
|
146
150
|
ExampleDetail,
|
|
147
151
|
ExampleOut,
|
|
@@ -159,6 +163,7 @@ from dirigent_client import (
|
|
|
159
163
|
RunReport,
|
|
160
164
|
RunStatus,
|
|
161
165
|
ValidationIssue,
|
|
166
|
+
declared_label,
|
|
162
167
|
)
|
|
163
168
|
from dirigent_common import Issue, JsonMap, Message
|
|
164
169
|
from dirigent_core import migrations
|
|
@@ -823,7 +828,7 @@ def pipeline_list(
|
|
|
823
828
|
return emit_records("pipeline", rows)
|
|
824
829
|
table(
|
|
825
830
|
"pipelines",
|
|
826
|
-
["code", "name", "tags", "version", "active",
|
|
831
|
+
["code", "name", "tags", "version", "active", RUNS_IN_FLIGHT, "updated"],
|
|
827
832
|
[
|
|
828
833
|
[
|
|
829
834
|
row.code,
|
|
@@ -859,7 +864,7 @@ def pipeline_show(
|
|
|
859
864
|
"importance": row.importance.value,
|
|
860
865
|
"active": "yes" if row.active else "no",
|
|
861
866
|
"current version": row.current_version,
|
|
862
|
-
|
|
867
|
+
RUNS_IN_FLIGHT: row.active_runs,
|
|
863
868
|
"created": moment(row.created_at),
|
|
864
869
|
},
|
|
865
870
|
)
|
|
@@ -2465,8 +2470,11 @@ def schema_list(ctx: typer.Context) -> None:
|
|
|
2465
2470
|
return emit_records("schema", rows)
|
|
2466
2471
|
table(
|
|
2467
2472
|
"schemas",
|
|
2468
|
-
["code", "name", "description"],
|
|
2469
|
-
[
|
|
2473
|
+
["code", "name", "description", "used by"],
|
|
2474
|
+
[
|
|
2475
|
+
[row.code, row.name or "-", row.description or "-", str(len(row.used_by)) if row.used_by else "-"]
|
|
2476
|
+
for row in rows
|
|
2477
|
+
],
|
|
2470
2478
|
)
|
|
2471
2479
|
|
|
2472
2480
|
|
|
@@ -2483,6 +2491,19 @@ def schema_create(
|
|
|
2483
2491
|
] = None,
|
|
2484
2492
|
) -> None:
|
|
2485
2493
|
"""Store a locally authored JSON Schema, taking its identity from its own keywords."""
|
|
2494
|
+
schema, document = _read_schema(reference)
|
|
2495
|
+
resolved = code
|
|
2496
|
+
if resolved is None and not (isinstance(schema.get("$id"), str) and code_from_id(cast("str", schema["$id"]))):
|
|
2497
|
+
resolved = code_from_id(Path(document.ref).name) if document.ref != "(stdin)" else None
|
|
2498
|
+
with client_for(state_of(ctx)) as dg:
|
|
2499
|
+
created = dg.call(dg.schemas.create(schema, code=resolved, name=name, description=description))
|
|
2500
|
+
if state_of(ctx).json_output:
|
|
2501
|
+
return emit_fact("schema.created", message="created", code=created.code, name=created.name)
|
|
2502
|
+
console.print(f"[green]stored[/] schema [bold]{created.code}[/]")
|
|
2503
|
+
|
|
2504
|
+
|
|
2505
|
+
def _read_schema(reference: str) -> tuple[JsonMap, Document]:
|
|
2506
|
+
"""Read a locally authored JSON Schema from a file, a URL, or standard input."""
|
|
2486
2507
|
try:
|
|
2487
2508
|
document = read_document(reference)
|
|
2488
2509
|
except SourceError as error:
|
|
@@ -2493,15 +2514,69 @@ def schema_create(
|
|
|
2493
2514
|
fail(SCHEMA_UNREADABLE, label=document.label, detail=str(error))
|
|
2494
2515
|
if not isinstance(body, dict):
|
|
2495
2516
|
fail(SCHEMA_NOT_AN_OBJECT, label=document.label, kind=type(body).__name__)
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2517
|
+
return cast("JsonMap", body), document
|
|
2518
|
+
|
|
2519
|
+
|
|
2520
|
+
def _label_given(text: str | None) -> str | Clear | None:
|
|
2521
|
+
"""Read a label option: not given leaves what is stored, and given empty clears it.
|
|
2522
|
+
|
|
2523
|
+
An empty title is the absence of one rather than a title, and no screen can draw it, so
|
|
2524
|
+
the empty string is never what gets stored.
|
|
2525
|
+
"""
|
|
2526
|
+
if text is None:
|
|
2527
|
+
return None
|
|
2528
|
+
return text.strip() or CLEAR
|
|
2529
|
+
|
|
2530
|
+
|
|
2531
|
+
@schema_app.command("update")
|
|
2532
|
+
def schema_update(
|
|
2533
|
+
ctx: typer.Context,
|
|
2534
|
+
code: Annotated[str, typer.Argument(help="The schema to change; a code is fixed once minted.")],
|
|
2535
|
+
reference: Annotated[
|
|
2536
|
+
str | None,
|
|
2537
|
+
typer.Argument(help="A JSON Schema file, or - for standard input, to replace the stored body with."),
|
|
2538
|
+
] = None,
|
|
2539
|
+
name: Annotated[
|
|
2540
|
+
str | None,
|
|
2541
|
+
typer.Option(help="A human title; taken from the replacing schema's title when omitted, cleared when empty."),
|
|
2542
|
+
] = None,
|
|
2543
|
+
description: Annotated[
|
|
2544
|
+
str | None,
|
|
2545
|
+
typer.Option(help="What the schema is for; taken from the replacing schema's description, cleared when empty."),
|
|
2546
|
+
] = None,
|
|
2547
|
+
) -> None:
|
|
2548
|
+
"""Correct a stored schema: replace the shape, relabel it, or both.
|
|
2549
|
+
|
|
2550
|
+
What this was not given is left as it stands, and an option given empty clears what is
|
|
2551
|
+
stored. Replacing the body takes the new schema's own ``title`` and ``description`` where
|
|
2552
|
+
the options name neither, exactly as storing one does, so the labels belong to the shape
|
|
2553
|
+
the instance now holds rather than to the one it no longer does.
|
|
2554
|
+
|
|
2555
|
+
An edit is never refused for being depended on: a code is fixed once minted, so refusing
|
|
2556
|
+
it would freeze an in-use shape permanently. The answer names the pipelines that validate
|
|
2557
|
+
against it, and the decision was made for them.
|
|
2558
|
+
"""
|
|
2559
|
+
schema = _read_schema(reference)[0] if reference is not None else None
|
|
2560
|
+
label = _label_given(name)
|
|
2561
|
+
about = _label_given(description)
|
|
2562
|
+
if schema is not None:
|
|
2563
|
+
label = declared_label(schema, "title") if label is None else label
|
|
2564
|
+
about = declared_label(schema, "description") if about is None else about
|
|
2565
|
+
changed = [
|
|
2566
|
+
field for field, given in (("name", label), ("description", about), ("body", schema)) if given is not None
|
|
2567
|
+
]
|
|
2568
|
+
if not changed:
|
|
2569
|
+
fail(SCHEMA_NOTHING_TO_CHANGE, code=code)
|
|
2500
2570
|
with client_for(state_of(ctx)) as dg:
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2571
|
+
edited = dg.call(dg.schemas.update(code, body=schema, name=label, description=about))
|
|
2572
|
+
emit_fact(
|
|
2573
|
+
"schema.updated",
|
|
2574
|
+
message="updated",
|
|
2575
|
+
code=edited.code,
|
|
2576
|
+
name=edited.name,
|
|
2577
|
+
changed=changed,
|
|
2578
|
+
used_by=edited.used_by,
|
|
2579
|
+
)
|
|
2505
2580
|
|
|
2506
2581
|
|
|
2507
2582
|
@schema_app.command("show")
|
|
@@ -2511,13 +2586,20 @@ def schema_show(ctx: typer.Context, code: Annotated[str, typer.Argument()]) -> N
|
|
|
2511
2586
|
row = dg.call(dg.schemas.get(code))
|
|
2512
2587
|
if state_of(ctx).json_output:
|
|
2513
2588
|
return emit_one("schema", row)
|
|
2514
|
-
fields(
|
|
2589
|
+
fields(
|
|
2590
|
+
f"schema {code}",
|
|
2591
|
+
{
|
|
2592
|
+
"name": row.name or "-",
|
|
2593
|
+
"description": row.description or "-",
|
|
2594
|
+
"used by": ", ".join(row.used_by) or "-",
|
|
2595
|
+
},
|
|
2596
|
+
)
|
|
2515
2597
|
console.print_json(data=row.body)
|
|
2516
2598
|
|
|
2517
2599
|
|
|
2518
2600
|
@schema_app.command("delete")
|
|
2519
2601
|
def schema_delete(ctx: typer.Context, code: Annotated[str, typer.Argument()]) -> None:
|
|
2520
|
-
"""Remove a schema."""
|
|
2602
|
+
"""Remove a schema, which the instance refuses while a stored pipeline names it."""
|
|
2521
2603
|
with client_for(state_of(ctx)) as dg:
|
|
2522
2604
|
dg.call(dg.schemas.delete(code))
|
|
2523
2605
|
emit_fact("schema.deleted", message="deleted", code=code)
|
|
@@ -189,6 +189,11 @@ SCHEMA_NOT_AN_OBJECT = CLI.define(
|
|
|
189
189
|
"{label} is not a JSON Schema: a schema is an object, and this is {kind}",
|
|
190
190
|
)
|
|
191
191
|
|
|
192
|
+
SCHEMA_NOTHING_TO_CHANGE = CLI.define(
|
|
193
|
+
"schema_nothing_to_change",
|
|
194
|
+
"nothing was given to change about {code}; pass a schema file, --name or --description",
|
|
195
|
+
)
|
|
196
|
+
|
|
192
197
|
# What a profile refuses at validation. Pydantic owns the code a validator's refusal reaches
|
|
193
198
|
# the wire under, so this is rendered into the ``ValueError`` it wraps.
|
|
194
199
|
|
|
@@ -478,6 +478,11 @@ def prioritised(priority: object) -> str:
|
|
|
478
478
|
#: same words out of its own catalogue, and `tests/test_shared_words.py` holds the two together.
|
|
479
479
|
IMPORTANCE_FLOOR = "{importance} and above"
|
|
480
480
|
|
|
481
|
+
#: What a pipeline's unsettled runs are called, wherever a rendering names the count the server
|
|
482
|
+
#: computes from `ACTIVE_RUN_STATUSES`. The web UI heads its own feed of them with the same noun
|
|
483
|
+
#: out of its catalogue, and `tests/test_shared_words.py` fails when the two stop agreeing.
|
|
484
|
+
RUNS_IN_FLIGHT = "runs in flight"
|
|
485
|
+
|
|
481
486
|
|
|
482
487
|
def watching(scope: object, importance: object) -> str:
|
|
483
488
|
"""What an alert rule watches, in one cell: its scope, and the floor it fires at.
|
|
@@ -11,7 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
from typing import Any, cast
|
|
12
12
|
|
|
13
13
|
from dirigent_cli.local import ConnectionSpec
|
|
14
|
-
from dirigent_client import Dirigent, DirigentError, PlanAction, ProvenanceSource
|
|
14
|
+
from dirigent_client import Dirigent, DirigentError, PlanAction, ProvenanceSource, declared_label
|
|
15
15
|
from dirigent_common import JsonMap
|
|
16
16
|
from dirigent_core.documents import CARRIED, SUFFIXES, is_document, readable, safe_load
|
|
17
17
|
from dirigent_core.protocol import Record, make
|
|
@@ -99,11 +99,21 @@ def _schemas(raw: JsonMap) -> dict[str, JsonMap]:
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
async def _schema(client: Dirigent, code: str, body: JsonMap) -> None:
|
|
102
|
-
"""Store one named schema, replacing the
|
|
102
|
+
"""Store one named schema, replacing the shape and the labels of one already held.
|
|
103
|
+
|
|
104
|
+
Storing reads the schema's own ``title`` and ``description`` and editing reads neither, so
|
|
105
|
+
the replacement sends what the document's schema declares about itself; otherwise the same
|
|
106
|
+
corpus would label a schema one way on a fresh instance and another way on a seeded one.
|
|
107
|
+
"""
|
|
103
108
|
try:
|
|
104
109
|
await client.schemas.create(body, code=code)
|
|
105
110
|
except DirigentError:
|
|
106
|
-
await client.schemas.update(
|
|
111
|
+
await client.schemas.update(
|
|
112
|
+
code,
|
|
113
|
+
body=body,
|
|
114
|
+
name=declared_label(body, "title"),
|
|
115
|
+
description=declared_label(body, "description"),
|
|
116
|
+
)
|
|
107
117
|
|
|
108
118
|
|
|
109
119
|
async def _connections(client: Dirigent, declared: Sequence[ConnectionSpec], origin: str) -> AsyncIterator[Record]:
|
|
@@ -52,6 +52,7 @@ BULKY: Final = frozenset(
|
|
|
52
52
|
"token",
|
|
53
53
|
"preflight",
|
|
54
54
|
"requires",
|
|
55
|
+
"used_by",
|
|
55
56
|
}
|
|
56
57
|
)
|
|
57
58
|
|
|
@@ -342,6 +343,33 @@ def _created(record: Record) -> RenderableType | None:
|
|
|
342
343
|
return Group(*parts)
|
|
343
344
|
|
|
344
345
|
|
|
346
|
+
#: What a changed shape reaches. The web UI warns in these words before a save, out of its own
|
|
347
|
+
#: catalogue, and `tests/test_shared_words.py` fails when the two stop agreeing. Written without
|
|
348
|
+
#: terminal punctuation, because the rendering puts the pipelines after it.
|
|
349
|
+
SCHEMA_REACHES = "Every pipeline naming this code checks against this shape from its next run"
|
|
350
|
+
|
|
351
|
+
#: What it does not reach. Said after the pipelines rather than with them: the colon above governs
|
|
352
|
+
#: that list, and a run in flight is not on it. Paired with the web UI's own words by the
|
|
353
|
+
#: same test, and punctuated, because nothing follows it.
|
|
354
|
+
IN_FLIGHT_KEEPS_ITS_SHAPE = "A run in flight keeps the shape it started with."
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _schema_edited(record: Record) -> RenderableType | None:
|
|
358
|
+
"""Render whose pipelines the shape that was just changed was changed for.
|
|
359
|
+
|
|
360
|
+
An edit is never refused for being depended on, so the pipelines checking against the
|
|
361
|
+
code are the ones the edit was a decision about, and they are named rather than counted.
|
|
362
|
+
Where no pipeline names the code, no run checks against it either, so neither line is drawn.
|
|
363
|
+
"""
|
|
364
|
+
named = _texts(record, "used_by")
|
|
365
|
+
if not named:
|
|
366
|
+
return None
|
|
367
|
+
parts: list[RenderableType] = [f"\n{SCHEMA_REACHES}:"]
|
|
368
|
+
parts.extend(f" [yellow]-[/] {escape(one)}" for one in named)
|
|
369
|
+
parts.append(f"\n{IN_FLIGHT_KEEPS_ITS_SHAPE}")
|
|
370
|
+
return Group(*parts)
|
|
371
|
+
|
|
372
|
+
|
|
345
373
|
def _config(record: Record) -> RenderableType | None:
|
|
346
374
|
"""Render the effective configuration as a setting-per-row table."""
|
|
347
375
|
settings = record.get("settings")
|
|
@@ -524,4 +552,5 @@ RENDERERS: Final[Mapping[str, Callable[[Record], RenderableType | None]]] = {
|
|
|
524
552
|
"project.scaffolded": _scaffolded,
|
|
525
553
|
"instance.initialised": _initialised,
|
|
526
554
|
"pipeline.created": _created,
|
|
555
|
+
"schema.updated": _schema_edited,
|
|
527
556
|
}
|
|
@@ -28,7 +28,7 @@ from dirigent_cli.output import (
|
|
|
28
28
|
throttle_note,
|
|
29
29
|
watching,
|
|
30
30
|
)
|
|
31
|
-
from dirigent_client import LOG_NOTIFIER, AlertEvent, AlertScope, WebhookTokenOut
|
|
31
|
+
from dirigent_client import LOG_NOTIFIER, TEST_SUBJECT, AlertEvent, AlertScope, WebhookTokenOut
|
|
32
32
|
from dirigent_common import Message
|
|
33
33
|
|
|
34
34
|
schedule_app = typer.Typer(
|
|
@@ -609,7 +609,7 @@ def alerts_test(
|
|
|
609
609
|
str | None,
|
|
610
610
|
typer.Option("--connection", help="The connection to deliver through; none delivers to the process log."),
|
|
611
611
|
] = None,
|
|
612
|
-
subject: Annotated[str, typer.Option("--subject", help="What the test message says.")] =
|
|
612
|
+
subject: Annotated[str, typer.Option("--subject", help="What the test message says.")] = TEST_SUBJECT,
|
|
613
613
|
) -> None:
|
|
614
614
|
"""Send a test message to one target, on the same queue a real alert takes."""
|
|
615
615
|
with client_for(state_of(ctx)) as dg:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/__init__.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/operator.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.22.0 → dirigent_cli-0.23.1}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|