dirigent-cli 0.18.2__tar.gz → 0.18.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.
Files changed (37) hide show
  1. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/PKG-INFO +9 -9
  2. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/pyproject.toml +9 -9
  3. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/pyproject.toml.orig +9 -9
  4. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/main.py +49 -0
  5. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/messages.py +4 -0
  6. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/project.py +16 -24
  7. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/LICENSE +0 -0
  8. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/README.md +0 -0
  9. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/__init__.py +0 -0
  10. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/aliases.py +0 -0
  11. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/commands.py +0 -0
  12. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/context.py +0 -0
  13. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/explain.py +0 -0
  14. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/formatters.py +0 -0
  15. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/graph.py +0 -0
  16. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/health.py +0 -0
  17. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/init_form.py +0 -0
  18. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/local.py +0 -0
  19. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/output.py +0 -0
  20. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/params.py +0 -0
  21. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/profiles.py +0 -0
  22. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/py.typed +0 -0
  23. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/reaper.py +0 -0
  24. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/scaffold.py +0 -0
  25. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/schemas.py +0 -0
  26. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/seeding.py +0 -0
  27. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/sources.py +0 -0
  28. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/starters.py +0 -0
  29. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/stream.py +0 -0
  30. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/summaries.py +0 -0
  31. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
  32. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
  33. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
  34. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
  35. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
  36. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/timing.py +0 -0
  37. {dirigent_cli-0.18.2 → dirigent_cli-0.18.3}/src/dirigent_cli/triggers.py +0 -0
@@ -1,19 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dirigent-cli
3
- Version: 0.18.2
3
+ Version: 0.18.3
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.18.2
10
- Requires-Dist: dirigent-blocks==0.18.2
11
- Requires-Dist: dirigent-client==0.18.2
12
- Requires-Dist: dirigent-common==0.18.2
13
- Requires-Dist: dirigent-core==0.18.2
14
- Requires-Dist: dirigent-examples==0.18.2
15
- Requires-Dist: dirigent-plugin==0.18.2
16
- Requires-Dist: dirigent-server==0.18.2
9
+ Requires-Dist: dirigent-block-execute==0.18.3
10
+ Requires-Dist: dirigent-blocks==0.18.3
11
+ Requires-Dist: dirigent-client==0.18.3
12
+ Requires-Dist: dirigent-common==0.18.3
13
+ Requires-Dist: dirigent-core==0.18.3
14
+ Requires-Dist: dirigent-examples==0.18.3
15
+ Requires-Dist: dirigent-plugin==0.18.3
16
+ Requires-Dist: dirigent-server==0.18.3
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.18.2"
3
+ version = "0.18.3"
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.18.2",
15
- "dirigent-blocks==0.18.2",
16
- "dirigent-client==0.18.2",
17
- "dirigent-common==0.18.2",
18
- "dirigent-core==0.18.2",
19
- "dirigent-examples==0.18.2",
20
- "dirigent-plugin==0.18.2",
21
- "dirigent-server==0.18.2",
14
+ "dirigent-block-execute==0.18.3",
15
+ "dirigent-blocks==0.18.3",
16
+ "dirigent-client==0.18.3",
17
+ "dirigent-common==0.18.3",
18
+ "dirigent-core==0.18.3",
19
+ "dirigent-examples==0.18.3",
20
+ "dirigent-plugin==0.18.3",
21
+ "dirigent-server==0.18.3",
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.18.2"
3
+ version = "0.18.3"
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.18.2",
15
- "dirigent-blocks==0.18.2",
16
- "dirigent-client==0.18.2",
17
- "dirigent-common==0.18.2",
18
- "dirigent-core==0.18.2",
19
- "dirigent-examples==0.18.2",
20
- "dirigent-plugin==0.18.2",
21
- "dirigent-server==0.18.2",
14
+ "dirigent-block-execute==0.18.3",
15
+ "dirigent-blocks==0.18.3",
16
+ "dirigent-client==0.18.3",
17
+ "dirigent-common==0.18.3",
18
+ "dirigent-core==0.18.3",
19
+ "dirigent-examples==0.18.3",
20
+ "dirigent-plugin==0.18.3",
21
+ "dirigent-server==0.18.3",
22
22
  "httpx2>=2.12.0",
23
23
  "python-dotenv>=1.1.0",
24
24
  "pyyaml>=6.0.3",
@@ -39,6 +39,8 @@ from dirigent_cli.messages import (
39
39
  RUN_DB_UPGRADE,
40
40
  SCHEDULER_NEEDS_POSTGRES,
41
41
  SERVER_NEEDS_A_LEADER,
42
+ STORE_UNCONFIGURED,
43
+ STORE_UNREACHABLE,
42
44
  UNKNOWN_CONNECTION_KIND,
43
45
  USE_DG_DEV,
44
46
  USE_DG_DEV_STANDALONE,
@@ -67,6 +69,7 @@ if TYPE_CHECKING:
67
69
  from dirigent_core import retention
68
70
  from dirigent_core.migrations import SchemaDifference
69
71
  from dirigent_core.worker import Worker
72
+ from dirigent_plugin import ContainerResult
70
73
 
71
74
  #: Help is capped rather than stretched: a panel the width of a wide terminal is unreadable.
72
75
  rich_utils.MAX_WIDTH = 100
@@ -112,6 +115,7 @@ app = typer.Typer(
112
115
 
113
116
  db_app = typer.Typer(name="db", help="Database schema management.", no_args_is_help=True)
114
117
  config_app = typer.Typer(name="config", help="Inspect the effective configuration.", no_args_is_help=True)
118
+ storage_app = typer.Typer(name="storage", help="The store this instance keeps artifacts in.", no_args_is_help=True)
115
119
  health_app = typer.Typer(name="health", help="Process-side checks: everything this host runs, or one part.")
116
120
  docker_app = typer.Typer(name="docker", help="The docker daemon this host's worker uses.", no_args_is_help=True)
117
121
 
@@ -127,6 +131,7 @@ app.add_typer(triggers.trigger_document_app, rich_help_panel=TRIGGER_PANEL)
127
131
  app.add_typer(triggers.alerts_app, rich_help_panel=TRIGGER_PANEL)
128
132
  app.add_typer(docker_app, rich_help_panel=PROCESS_PANEL)
129
133
  app.add_typer(db_app, rich_help_panel=ADMIN_PANEL)
134
+ app.add_typer(storage_app, rich_help_panel=ADMIN_PANEL)
130
135
  app.add_typer(config_app, rich_help_panel=ADMIN_PANEL)
131
136
  app.add_typer(commands.auth_app, rich_help_panel=ADMIN_PANEL)
132
137
  commands.system_app.add_typer(health_app)
@@ -357,6 +362,50 @@ def db_history() -> None:
357
362
  emit_fact("db.history", message="migration history", history=migrations.history(get_settings()).rstrip())
358
363
 
359
364
 
365
+ @storage_app.command("ensure")
366
+ def storage_ensure() -> None:
367
+ """Create the container the artifact root addresses, unless it is already there.
368
+
369
+ Process-side the way ``dg db upgrade`` is: it reads the artifact root and the connection
370
+ this instance configures that scheme from rather than a token, so a one-shot container
371
+ makes the bucket before anything writes to it. A root on a filesystem has no container to
372
+ make, and says so.
373
+ """
374
+ import asyncio
375
+
376
+ settings = get_settings()
377
+ made = asyncio.run(_ensure_container(settings))
378
+ emit_fact(
379
+ "storage.ensured",
380
+ message="created" if made.created else "already there" if made.container else "nothing to do",
381
+ root=settings.artifact_root,
382
+ bucket=made.container,
383
+ created=made.created,
384
+ endpoint=made.endpoint,
385
+ )
386
+
387
+
388
+ async def _ensure_container(settings: Settings) -> "ContainerResult":
389
+ """Ask the backend the artifact root belongs to for its container, on this instance's connection."""
390
+ from dirigent_core.database import create_engine, create_session_factory, session_scope
391
+ from dirigent_core.engine.services import EngineServices
392
+ from dirigent_core.errors import DomainError
393
+ from dirigent_core.plugins import load_plugin_host
394
+
395
+ services = EngineServices.build(settings, load_plugin_host())
396
+ engine = create_engine(settings)
397
+ try:
398
+ async with session_scope(create_session_factory(engine)) as session:
399
+ storage = await services.bound_storage(session)
400
+ return await storage.ensure_container(settings.artifact_root)
401
+ except DomainError as error:
402
+ commands.fail(STORE_UNCONFIGURED, root=settings.artifact_root, detail=str(error))
403
+ except Exception as error:
404
+ commands.fail(STORE_UNREACHABLE, root=settings.artifact_root, detail=f"{type(error).__name__}: {error}")
405
+ finally:
406
+ await engine.dispose()
407
+
408
+
360
409
  @commands.connection_app.command("ensure")
361
410
  def connection_ensure(
362
411
  kind_id: Annotated[str, typer.Argument(metavar="KIND", help="The connection kind, such as s3.")],
@@ -55,6 +55,10 @@ NO_DOCKER_DAEMON = CLI.define(
55
55
 
56
56
  AUTH_REFUSED = CLI.define("auth_refused", "{detail}")
57
57
 
58
+ STORE_UNREACHABLE = CLI.define("store_unreachable", "the store behind {root} did not answer: {detail}")
59
+
60
+ STORE_UNCONFIGURED = CLI.define("store_unconfigured", "{root} cannot be reached from this instance: {detail}")
61
+
58
62
  DEV_IS_SQLITE_ONLY = CLI.define("dev_is_sqlite_only", "dg dev is the SQLite standalone mode")
59
63
 
60
64
  USE_DG_SERVER = CLI.define("use_dg_server", "use dg server on PostgreSQL")
@@ -355,8 +355,8 @@ COMPOSE_ARTIFACTS_VOLUME = """\
355
355
  """
356
356
 
357
357
  COMPOSE_DEPENDS_S3 = """\
358
- s3-bucket:
359
- condition: service_completed_successfully
358
+ s3:
359
+ condition: service_healthy
360
360
  """
361
361
 
362
362
  COMPOSE_MIGRATE = """\
@@ -373,13 +373,14 @@ __MIGRATE_S3____MIGRATE_BROKERS__ environment:
373
373
  __MIGRATE_ENV__ depends_on:
374
374
  postgres:
375
375
  condition: service_healthy
376
-
376
+ __MIGRATE_DEPENDS__
377
377
  """
378
378
 
379
379
  COMPOSE_MIGRATE_S3_WHAT = """, and ensures the connection the artifact root resolves
380
- # through. That is a row, and a container has no API token, so `dg connection ensure`
381
- # writes it process-side and seals its secret half with the instance key, exactly as the
382
- # API would. Re-running the stack brings the row to whatever the environment now says."""
380
+ # through, and then the bucket that root addresses. That connection is a row, and a
381
+ # container has no API token, so `dg connection ensure` writes it process-side and seals
382
+ # its secret half with the instance key, exactly as the API would. Re-running the stack
383
+ # brings the row to whatever the environment now says."""
383
384
 
384
385
  COMPOSE_MIGRATE_KAFKA = """\
385
386
  dg connection ensure kafka kafka \\
@@ -410,6 +411,12 @@ COMPOSE_MIGRATE_S3 = """\
410
411
  --set access_key_id="$$S3_ACCESS_KEY" \\
411
412
  --set secret_access_key="$$S3_SECRET_KEY" \\
412
413
  --set path_style=true
414
+ dg storage ensure
415
+ """
416
+
417
+ COMPOSE_MIGRATE_DEPENDS_S3 = """\
418
+ s3:
419
+ condition: service_healthy
413
420
  """
414
421
 
415
422
  COMPOSE_MIGRATE_ENV_S3 = """\
@@ -442,22 +449,6 @@ COMPOSE_S3 = """\
442
449
  retries: 30
443
450
  start_period: 5s
444
451
 
445
- # The bucket has to exist before a run writes to it, and nothing else creates it.
446
- s3-bucket:
447
- image: quay.io/minio/mc:RELEASE.2025-04-16T18-13-26Z
448
- depends_on:
449
- s3:
450
- condition: service_healthy
451
- entrypoint: ["/bin/sh", "-ec"]
452
- command:
453
- - |
454
- mc alias set fs http://s3:9000 "$$S3_ACCESS_KEY" "$$S3_SECRET_KEY"
455
- mc mb --ignore-existing "fs/$$S3_BUCKET"
456
- environment:
457
- S3_ACCESS_KEY: ${S3_ACCESS_KEY:-dirigent}
458
- S3_SECRET_KEY: ${S3_SECRET_KEY:-dirigent}
459
- S3_BUCKET: ${S3_BUCKET:-dirigent}
460
-
461
452
  """
462
453
 
463
454
  COMPOSE_SERVER = """\
@@ -637,7 +628,7 @@ COMPOSE_WORKER_DOCKER_VOLUMES = """\
637
628
  """
638
629
 
639
630
  COMPOSE_DEPENDS = {
640
- "s3": " s3-bucket:\n condition: service_completed_successfully\n",
631
+ "s3": " s3:\n condition: service_healthy\n",
641
632
  "docker": " docker:\n condition: service_healthy\n",
642
633
  "kafka": " kafka-topic:\n condition: service_completed_successfully\n",
643
634
  "rabbitmq": " rabbitmq-queue:\n condition: service_completed_successfully\n",
@@ -671,7 +662,8 @@ def compose_document(choices: InitChoices, version: str) -> str:
671
662
  .replace(
672
663
  "__MIGRATE_ENV__",
673
664
  (COMPOSE_MIGRATE_ENV_S3 if s3 else "") + (COMPOSE_MIGRATE_ENV_RABBITMQ if choices.has("rabbitmq") else ""),
674
- ),
665
+ )
666
+ .replace("__MIGRATE_DEPENDS__", COMPOSE_MIGRATE_DEPENDS_S3 if s3 else ""),
675
667
  COMPOSE_S3 if s3 else "",
676
668
  COMPOSE_SERVER.replace("__ARTIFACTS_MOUNT__", artifacts_mount),
677
669
  COMPOSE_DOCKER if docker else "",
File without changes
File without changes