dirigent-cli 0.16.4__tar.gz → 0.16.6__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.16.4 → dirigent_cli-0.16.6}/PKG-INFO +10 -8
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/pyproject.toml +12 -8
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/pyproject.toml.orig +12 -8
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/commands.py +33 -8
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/main.py +33 -1
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/output.py +16 -5
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/project.py +11 -6
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/summaries.py +3 -1
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/LICENSE +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/README.md +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/__init__.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/aliases.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/context.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/formatters.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/graph.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/health.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/init_form.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/local.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/params.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/profiles.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/py.typed +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/reaper.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/scaffold.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/schemas.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/seeding.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/sources.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/starters.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/stream.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/timing.py +0 -0
- {dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/triggers.py +0 -0
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dirigent-cli
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.6
|
|
4
4
|
Summary: The dirigent command line interface (dirigent / dg).
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
License-File: LICENSE
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Requires-Dist: dirigent-
|
|
10
|
-
Requires-Dist: dirigent-
|
|
11
|
-
Requires-Dist: dirigent-
|
|
12
|
-
Requires-Dist: dirigent-
|
|
13
|
-
Requires-Dist: dirigent-
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Requires-Dist: dirigent-blocks==0.16.6
|
|
10
|
+
Requires-Dist: dirigent-client==0.16.6
|
|
11
|
+
Requires-Dist: dirigent-common==0.16.6
|
|
12
|
+
Requires-Dist: dirigent-core==0.16.6
|
|
13
|
+
Requires-Dist: dirigent-examples==0.16.6
|
|
14
|
+
Requires-Dist: dirigent-plugin==0.16.6
|
|
15
|
+
Requires-Dist: dirigent-server==0.16.6
|
|
14
16
|
Requires-Dist: httpx2>=2.12.0
|
|
15
17
|
Requires-Dist: python-dotenv>=1.1.0
|
|
16
18
|
Requires-Dist: pyyaml>=6.0.3
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.16.
|
|
3
|
+
version = "0.16.6"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
license = "LicenseRef-Proprietary"
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
]
|
|
9
13
|
dependencies = [
|
|
10
|
-
"dirigent-blocks==0.16.
|
|
11
|
-
"dirigent-client==0.16.
|
|
12
|
-
"dirigent-common==0.16.
|
|
13
|
-
"dirigent-core==0.16.
|
|
14
|
-
"dirigent-examples==0.16.
|
|
15
|
-
"dirigent-plugin==0.16.
|
|
16
|
-
"dirigent-server==0.16.
|
|
14
|
+
"dirigent-blocks==0.16.6",
|
|
15
|
+
"dirigent-client==0.16.6",
|
|
16
|
+
"dirigent-common==0.16.6",
|
|
17
|
+
"dirigent-core==0.16.6",
|
|
18
|
+
"dirigent-examples==0.16.6",
|
|
19
|
+
"dirigent-plugin==0.16.6",
|
|
20
|
+
"dirigent-server==0.16.6",
|
|
17
21
|
"httpx2>=2.12.0",
|
|
18
22
|
"python-dotenv>=1.1.0",
|
|
19
23
|
"pyyaml>=6.0.3",
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "dirigent-cli"
|
|
3
|
-
version = "0.16.
|
|
3
|
+
version = "0.16.6"
|
|
4
4
|
description = "The dirigent command line interface (dirigent / dg)."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
license = "LicenseRef-Proprietary"
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"Programming Language :: Python :: 3.13",
|
|
12
|
+
]
|
|
9
13
|
dependencies = [
|
|
10
|
-
"dirigent-blocks==0.16.
|
|
11
|
-
"dirigent-client==0.16.
|
|
12
|
-
"dirigent-common==0.16.
|
|
13
|
-
"dirigent-core==0.16.
|
|
14
|
-
"dirigent-examples==0.16.
|
|
15
|
-
"dirigent-plugin==0.16.
|
|
16
|
-
"dirigent-server==0.16.
|
|
14
|
+
"dirigent-blocks==0.16.6",
|
|
15
|
+
"dirigent-client==0.16.6",
|
|
16
|
+
"dirigent-common==0.16.6",
|
|
17
|
+
"dirigent-core==0.16.6",
|
|
18
|
+
"dirigent-examples==0.16.6",
|
|
19
|
+
"dirigent-plugin==0.16.6",
|
|
20
|
+
"dirigent-server==0.16.6",
|
|
17
21
|
"httpx2>=2.12.0",
|
|
18
22
|
"python-dotenv>=1.1.0",
|
|
19
23
|
"pyyaml>=6.0.3",
|
|
@@ -62,7 +62,7 @@ from dirigent_cli.project import (
|
|
|
62
62
|
check_choices,
|
|
63
63
|
find_project,
|
|
64
64
|
scaffold,
|
|
65
|
-
|
|
65
|
+
write_instance_env,
|
|
66
66
|
)
|
|
67
67
|
from dirigent_cli.scaffold import ScaffoldedRecord, ScaffoldError, ScaffoldRecord, scaffold_pack
|
|
68
68
|
from dirigent_cli.sources import (
|
|
@@ -170,6 +170,11 @@ def fail(message: str) -> NoReturn:
|
|
|
170
170
|
raise typer.Exit(code=1)
|
|
171
171
|
|
|
172
172
|
|
|
173
|
+
def at_a_terminal() -> bool:
|
|
174
|
+
"""Report whether somebody is there to answer a prompt."""
|
|
175
|
+
return sys.stdin.isatty()
|
|
176
|
+
|
|
177
|
+
|
|
173
178
|
def ask(label: str, *, hide: bool = False) -> str:
|
|
174
179
|
"""Read one value from the terminal, refusing under --json where nothing can answer.
|
|
175
180
|
|
|
@@ -532,9 +537,10 @@ def init_command(
|
|
|
532
537
|
stack's services, packs, and the first admin. The flags answer the same questions for a
|
|
533
538
|
script. `--template local` writes the documents and a `pyproject.toml` pinning the running
|
|
534
539
|
dirigent, then creates the state directory, migrates the schema, creates the first admin
|
|
535
|
-
and mints it a token, kept in `.env`. `--template compose` writes the
|
|
536
|
-
container stack instead, and initialises nothing locally: the instance is
|
|
537
|
-
`--template documents` writes the documents alone, against an instance
|
|
540
|
+
and mints it a token and a secret key, both kept in `.env`. `--template compose` writes the
|
|
541
|
+
documents and a container stack instead, and initialises nothing locally: the instance is
|
|
542
|
+
the containers. `--template documents` writes the documents alone, against an instance
|
|
543
|
+
somebody else runs.
|
|
538
544
|
"""
|
|
539
545
|
import asyncio
|
|
540
546
|
|
|
@@ -611,7 +617,7 @@ def init_command(
|
|
|
611
617
|
migrated = migrations.head_revision(settings) or "none"
|
|
612
618
|
migrations.upgrade("head", settings)
|
|
613
619
|
token = asyncio.run(first_admin(settings, choices.admin, choices.password))
|
|
614
|
-
env_file =
|
|
620
|
+
env_file = write_instance_env(root, token)
|
|
615
621
|
emit_fact(
|
|
616
622
|
"instance.initialised",
|
|
617
623
|
message="initialised",
|
|
@@ -885,11 +891,21 @@ def parse_params(
|
|
|
885
891
|
schema: dict[str, Any] | None = None,
|
|
886
892
|
files: list[Path] | None = None,
|
|
887
893
|
) -> dict[str, Any]:
|
|
888
|
-
"""Build a run's parameters from files and flags, coerced against the pipeline's schema.
|
|
894
|
+
"""Build a run's parameters from files and flags, coerced against the pipeline's schema.
|
|
895
|
+
|
|
896
|
+
An empty value for a secret field means unset: ``--set api_token=`` leaves the field out
|
|
897
|
+
altogether, so nothing stores a credential that every read afterwards reports as set.
|
|
898
|
+
"""
|
|
889
899
|
try:
|
|
890
|
-
|
|
900
|
+
built = build_params(schema or {}, pairs=pairs or [], files=files or [])
|
|
891
901
|
except ParamError as error:
|
|
892
902
|
fail(str(error))
|
|
903
|
+
properties = cast("dict[str, Any]", (schema or {}).get("properties") or {})
|
|
904
|
+
return {
|
|
905
|
+
name: value
|
|
906
|
+
for name, value in built.items()
|
|
907
|
+
if value != "" or not _is_secret_field(cast("dict[str, Any]", properties.get(name) or {}))
|
|
908
|
+
}
|
|
893
909
|
|
|
894
910
|
|
|
895
911
|
#: What separates the two ends of a ``--window`` value.
|
|
@@ -2224,12 +2240,19 @@ def connection_create(
|
|
|
2224
2240
|
"""Create a connection, prompting for the secret fields without echoing them.
|
|
2225
2241
|
|
|
2226
2242
|
Prompting happens only on a terminal; in a script every value must arrive via ``--set``.
|
|
2243
|
+
An invocation carrying ``--set`` is prompted only for what it cannot do without: a
|
|
2244
|
+
required field it left out. A bare invocation is the interactive form and is offered
|
|
2245
|
+
every secret the kind declares.
|
|
2227
2246
|
"""
|
|
2228
2247
|
with client_for(state_of(ctx)) as dg:
|
|
2229
2248
|
schema = _connection_schema(dg.call(dg.blocks.catalog()), kind_id)
|
|
2230
2249
|
config = parse_params(set_value, schema=schema)
|
|
2231
2250
|
required = set(cast("list[str]", schema.get("required") or []))
|
|
2232
|
-
interactive =
|
|
2251
|
+
interactive = at_a_terminal() and not json_mode()
|
|
2252
|
+
# An invocation that carried --set said what it wanted, so only a required secret is
|
|
2253
|
+
# still asked for there: an optional one it left out is left out. With no --set at
|
|
2254
|
+
# all the command is the form, and every secret is offered.
|
|
2255
|
+
offered = not set_value
|
|
2233
2256
|
for field, body in cast("dict[str, Any]", schema.get("properties") or {}).items():
|
|
2234
2257
|
if field in config:
|
|
2235
2258
|
continue
|
|
@@ -2241,6 +2264,8 @@ def connection_create(
|
|
|
2241
2264
|
if not interactive:
|
|
2242
2265
|
continue
|
|
2243
2266
|
if _is_secret_field(body):
|
|
2267
|
+
if not offered and field not in required:
|
|
2268
|
+
continue
|
|
2244
2269
|
value = typer.prompt(f"{field}", hide_input=True, default="", show_default=False)
|
|
2245
2270
|
if value:
|
|
2246
2271
|
config[field] = value
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
5
5
|
import sys
|
|
6
|
-
from collections.abc import Sequence
|
|
6
|
+
from collections.abc import Callable, Sequence
|
|
7
7
|
from datetime import UTC, datetime, timedelta
|
|
8
8
|
from importlib.metadata import PackageNotFoundError, version
|
|
9
9
|
from pathlib import Path
|
|
@@ -1076,8 +1076,15 @@ async def _dev(
|
|
|
1076
1076
|
install_signal_handlers(worker)
|
|
1077
1077
|
server_config = uvicorn.Config(build_app(), host=host, port=port, log_config=None)
|
|
1078
1078
|
api = uvicorn.Server(server_config)
|
|
1079
|
+
|
|
1080
|
+
def shut_down() -> None:
|
|
1081
|
+
"""Ask for the shutdown SIGTERM asks for: the worker drains, and serve returns."""
|
|
1082
|
+
worker.request_stop()
|
|
1083
|
+
api.should_exit = True
|
|
1084
|
+
|
|
1079
1085
|
worker_task = asyncio.create_task(worker.run())
|
|
1080
1086
|
ready = asyncio.create_task(_announce_ready(api))
|
|
1087
|
+
orphaned = asyncio.create_task(watch_parent(os.getppid, shut_down))
|
|
1081
1088
|
seeding = (
|
|
1082
1089
|
asyncio.create_task(_seed(api, local_url(host, port), bearer, seed, installed)) if seed or installed else None
|
|
1083
1090
|
)
|
|
@@ -1085,6 +1092,7 @@ async def _dev(
|
|
|
1085
1092
|
await api.serve()
|
|
1086
1093
|
finally:
|
|
1087
1094
|
ready.cancel()
|
|
1095
|
+
orphaned.cancel()
|
|
1088
1096
|
if seeding is not None:
|
|
1089
1097
|
seeding.cancel()
|
|
1090
1098
|
worker.request_stop()
|
|
@@ -1107,6 +1115,30 @@ async def _announce_ready(api: "uvicorn.Server") -> None:
|
|
|
1107
1115
|
emit(make("process", at=datetime.now(UTC), message="ready", process="dev"))
|
|
1108
1116
|
|
|
1109
1117
|
|
|
1118
|
+
#: How often the instance looks at whether the process that started it is still there.
|
|
1119
|
+
PARENT_POLL_SECONDS = 1.0
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
async def watch_parent(
|
|
1123
|
+
parent: Callable[[], int], stop: Callable[[], None], *, every: float = PARENT_POLL_SECONDS
|
|
1124
|
+
) -> None:
|
|
1125
|
+
"""Shut the instance down once the process that started it is gone.
|
|
1126
|
+
|
|
1127
|
+
``uv run dg dev`` forwards SIGTERM to the instance, but a wrapper that was killed outright
|
|
1128
|
+
cannot, and the instance it leaves behind goes on claiming runs against the same state.
|
|
1129
|
+
macOS has no PR_SET_PDEATHSIG, so the parent pid is polled: a pid that changed means this
|
|
1130
|
+
process was reparented to init. A dev instance therefore does not outlive the process that
|
|
1131
|
+
started it, backgrounding it past the shell that launched it included.
|
|
1132
|
+
"""
|
|
1133
|
+
import asyncio
|
|
1134
|
+
|
|
1135
|
+
started = parent()
|
|
1136
|
+
while parent() == started:
|
|
1137
|
+
await asyncio.sleep(every)
|
|
1138
|
+
emit(make("process", at=datetime.now(UTC), message="parent gone", process="dev", parent=started))
|
|
1139
|
+
stop()
|
|
1140
|
+
|
|
1141
|
+
|
|
1110
1142
|
async def _seed(
|
|
1111
1143
|
api: "uvicorn.Server",
|
|
1112
1144
|
url: str,
|
|
@@ -16,6 +16,7 @@ from pydantic import BaseModel
|
|
|
16
16
|
from rich.console import Console, Group, RenderableType
|
|
17
17
|
from rich.markup import escape
|
|
18
18
|
from rich.table import Table
|
|
19
|
+
from rich.text import Text
|
|
19
20
|
|
|
20
21
|
from dirigent_client.schemas.common import Problem
|
|
21
22
|
from dirigent_core.protocol import Format, Record, as_json, make
|
|
@@ -500,16 +501,26 @@ def elapsed(value: object) -> str:
|
|
|
500
501
|
return f"{int(hours)}h{int(minutes)}m"
|
|
501
502
|
|
|
502
503
|
|
|
504
|
+
def titled(title: str, built: Table) -> RenderableType:
|
|
505
|
+
"""Put a title above a table, on a line of its own.
|
|
506
|
+
|
|
507
|
+
Rich folds a table's own title to the width of the table, which the widest row sets, so a
|
|
508
|
+
title carrying a 36-character run id breaks in half wherever the rows are narrower than
|
|
509
|
+
it. Above the table the title is folded only by the console.
|
|
510
|
+
"""
|
|
511
|
+
return Group(Text.from_markup(title, style="table.title"), built)
|
|
512
|
+
|
|
513
|
+
|
|
503
514
|
def build_table(title: str, columns: Sequence[str], rows: Iterable[Sequence[str]]) -> RenderableType:
|
|
504
515
|
"""Build one rich table, or the quiet line that stands in for an empty one."""
|
|
505
|
-
built = Table(
|
|
516
|
+
built = Table(show_header=True, header_style="bold")
|
|
506
517
|
for column in columns:
|
|
507
518
|
built.add_column(column)
|
|
508
519
|
count = 0
|
|
509
520
|
for row in rows:
|
|
510
521
|
built.add_row(*row)
|
|
511
522
|
count += 1
|
|
512
|
-
return built if count else f"[dim]{title}: nothing to show.[/]"
|
|
523
|
+
return titled(title, built) if count else f"[dim]{title}: nothing to show.[/]"
|
|
513
524
|
|
|
514
525
|
|
|
515
526
|
def table(title: str, columns: Sequence[str], rows: Iterable[Sequence[str]]) -> None:
|
|
@@ -518,13 +529,13 @@ def table(title: str, columns: Sequence[str], rows: Iterable[Sequence[str]]) ->
|
|
|
518
529
|
|
|
519
530
|
|
|
520
531
|
def build_fields(title: str, values: Mapping[str, Any]) -> RenderableType:
|
|
521
|
-
"""Build one record as a two-column table."""
|
|
522
|
-
built = Table(
|
|
532
|
+
"""Build one record as a two-column table under its title."""
|
|
533
|
+
built = Table(show_header=False, box=None, padding=(0, 2, 0, 0))
|
|
523
534
|
built.add_column(style="bold")
|
|
524
535
|
built.add_column()
|
|
525
536
|
for name, value in values.items():
|
|
526
537
|
built.add_row(name, styled(value))
|
|
527
|
-
return built
|
|
538
|
+
return titled(title, built)
|
|
528
539
|
|
|
529
540
|
|
|
530
541
|
def fields(title: str, values: Mapping[str, Any]) -> None:
|
|
@@ -821,14 +821,18 @@ ROOT_IGNORE_TEMPLATE = """\
|
|
|
821
821
|
.venv/
|
|
822
822
|
__pycache__/
|
|
823
823
|
|
|
824
|
-
# This instance's token, or the stack's key and first password. Never shared.
|
|
824
|
+
# This instance's token and key, or the stack's key and first password. Never shared.
|
|
825
825
|
.env
|
|
826
826
|
"""
|
|
827
827
|
|
|
828
|
-
|
|
828
|
+
INSTANCE_ENV_TEMPLATE = """\
|
|
829
829
|
# The token dg init minted for this instance's first admin. The local profile in
|
|
830
830
|
# .dirigent/profiles.yaml reads it from here when the shell does not export it.
|
|
831
831
|
DG_TOKEN=__TOKEN__
|
|
832
|
+
|
|
833
|
+
# The key this instance seals connection secrets with, read by every command run in this
|
|
834
|
+
# directory. What is stored under it cannot be opened under another one.
|
|
835
|
+
DIRIGENT_SECRET_KEY=__SECRET_KEY__
|
|
832
836
|
"""
|
|
833
837
|
|
|
834
838
|
PYPROJECT_TEMPLATE = """\
|
|
@@ -923,9 +927,10 @@ def scaffold(directory: Path, choices: InitChoices, *, version: str = "0.0.0") -
|
|
|
923
927
|
return Scaffolded(files=written, skipped=skipped)
|
|
924
928
|
|
|
925
929
|
|
|
926
|
-
def
|
|
927
|
-
"""Write the ``.env`` holding a new instance's token, readable by its owner alone."""
|
|
928
|
-
|
|
930
|
+
def write_instance_env(directory: Path, token: str) -> Path:
|
|
931
|
+
"""Write the ``.env`` holding a new instance's token and key, readable by its owner alone."""
|
|
932
|
+
document = INSTANCE_ENV_TEMPLATE.replace("__TOKEN__", token).replace("__SECRET_KEY__", _an_instance_key())
|
|
933
|
+
return _write(directory / ".env", document, mode=0o600)
|
|
929
934
|
|
|
930
935
|
|
|
931
936
|
def _record(path: Path, content: str, written: list[Path], skipped: list[Path]) -> None:
|
|
@@ -953,7 +958,7 @@ def _merge_ignore(path: Path, content: str, written: list[Path], skipped: list[P
|
|
|
953
958
|
|
|
954
959
|
|
|
955
960
|
def _an_instance_key() -> str:
|
|
956
|
-
"""Generate the key
|
|
961
|
+
"""Generate the key an instance seals connection secrets with."""
|
|
957
962
|
return generate_key()
|
|
958
963
|
|
|
959
964
|
|
|
@@ -316,7 +316,9 @@ def _initialised(record: Record) -> RenderableType | None:
|
|
|
316
316
|
"\n [bold]uv run dg examples list --starter[/]"
|
|
317
317
|
"\n [bold]uv run dg pipeline new <starter>[/]"
|
|
318
318
|
"\n [bold]uv run dg apply[/]"
|
|
319
|
-
"\n\n[dim]
|
|
319
|
+
"\n\n[dim]DIRIGENT_SECRET_KEY is in .env beside it, and every command run in"
|
|
320
|
+
"\nthis directory reads it: it is what connection secrets are sealed with, and"
|
|
321
|
+
"\nunder another key the instance cannot open what it stored.[/]"
|
|
320
322
|
)
|
|
321
323
|
return Group(*parts)
|
|
322
324
|
|
|
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.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/__init__.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/operator.py.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl
RENAMED
|
File without changes
|
{dirigent_cli-0.16.4 → dirigent_cli-0.16.6}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|