dirigent-cli 0.27.0__tar.gz → 0.27.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.
Files changed (38) hide show
  1. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/PKG-INFO +9 -9
  2. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/pyproject.toml +9 -9
  3. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/pyproject.toml.orig +9 -9
  4. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/commands.py +15 -2
  5. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/local.py +12 -1
  6. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/seeding.py +3 -1
  7. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/sources.py +32 -0
  8. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/LICENSE +0 -0
  9. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/README.md +0 -0
  10. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/__init__.py +0 -0
  11. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/aliases.py +0 -0
  12. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/context.py +0 -0
  13. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/explain.py +0 -0
  14. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/formatters.py +0 -0
  15. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/graph.py +0 -0
  16. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/health.py +0 -0
  17. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/init_form.py +0 -0
  18. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/main.py +0 -0
  19. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/messages.py +0 -0
  20. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/output.py +0 -0
  21. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/params.py +0 -0
  22. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/profiles.py +0 -0
  23. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/project.py +0 -0
  24. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/py.typed +0 -0
  25. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/reaper.py +0 -0
  26. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/scaffold.py +0 -0
  27. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/schemas.py +0 -0
  28. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/source_commands.py +0 -0
  29. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/starters.py +0 -0
  30. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/stream.py +0 -0
  31. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/summaries.py +0 -0
  32. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/templates/pack/README.md.tmpl +0 -0
  33. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/templates/pack/__init__.py.tmpl +0 -0
  34. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/templates/pack/operator.py.tmpl +0 -0
  35. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/templates/pack/pyproject.toml.tmpl +0 -0
  36. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/templates/pack/test_plugin.py.tmpl +0 -0
  37. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/src/dirigent_cli/timing.py +0 -0
  38. {dirigent_cli-0.27.0 → dirigent_cli-0.27.1}/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.27.0
3
+ Version: 0.27.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.27.0
10
- Requires-Dist: dirigent-blocks==0.27.0
11
- Requires-Dist: dirigent-client==0.27.0
12
- Requires-Dist: dirigent-common==0.27.0
13
- Requires-Dist: dirigent-core==0.27.0
14
- Requires-Dist: dirigent-examples==0.27.0
15
- Requires-Dist: dirigent-plugin==0.27.0
16
- Requires-Dist: dirigent-server==0.27.0
9
+ Requires-Dist: dirigent-block-execute==0.27.1
10
+ Requires-Dist: dirigent-blocks==0.27.1
11
+ Requires-Dist: dirigent-client==0.27.1
12
+ Requires-Dist: dirigent-common==0.27.1
13
+ Requires-Dist: dirigent-core==0.27.1
14
+ Requires-Dist: dirigent-examples==0.27.1
15
+ Requires-Dist: dirigent-plugin==0.27.1
16
+ Requires-Dist: dirigent-server==0.27.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.27.0"
3
+ version = "0.27.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.27.0",
15
- "dirigent-blocks==0.27.0",
16
- "dirigent-client==0.27.0",
17
- "dirigent-common==0.27.0",
18
- "dirigent-core==0.27.0",
19
- "dirigent-examples==0.27.0",
20
- "dirigent-plugin==0.27.0",
21
- "dirigent-server==0.27.0",
14
+ "dirigent-block-execute==0.27.1",
15
+ "dirigent-blocks==0.27.1",
16
+ "dirigent-client==0.27.1",
17
+ "dirigent-common==0.27.1",
18
+ "dirigent-core==0.27.1",
19
+ "dirigent-examples==0.27.1",
20
+ "dirigent-plugin==0.27.1",
21
+ "dirigent-server==0.27.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.27.0"
3
+ version = "0.27.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.27.0",
15
- "dirigent-blocks==0.27.0",
16
- "dirigent-client==0.27.0",
17
- "dirigent-common==0.27.0",
18
- "dirigent-core==0.27.0",
19
- "dirigent-examples==0.27.0",
20
- "dirigent-plugin==0.27.0",
21
- "dirigent-server==0.27.0",
14
+ "dirigent-block-execute==0.27.1",
15
+ "dirigent-blocks==0.27.1",
16
+ "dirigent-client==0.27.1",
17
+ "dirigent-common==0.27.1",
18
+ "dirigent-core==0.27.1",
19
+ "dirigent-examples==0.27.1",
20
+ "dirigent-plugin==0.27.1",
21
+ "dirigent-server==0.27.1",
22
22
  "httpx2>=2.12.0",
23
23
  "python-dotenv>=1.1.0",
24
24
  "pyyaml>=6.0.3",
@@ -136,6 +136,7 @@ from dirigent_cli.sources import (
136
136
  SourceError,
137
137
  looks_like_a_document,
138
138
  parse_document,
139
+ read_directory,
139
140
  read_document,
140
141
  read_path,
141
142
  )
@@ -494,7 +495,9 @@ def export_command(
494
495
 
495
496
  def validate_command(
496
497
  ctx: typer.Context,
497
- reference: Annotated[str | None, typer.Argument(help="A file, a URL, or - for standard input.")] = None,
498
+ reference: Annotated[
499
+ str | None, typer.Argument(help="A file, a directory of documents, a URL, or - for standard input.")
500
+ ] = None,
498
501
  server: Annotated[
499
502
  bool, typer.Option("--server", help="Also check it against a server's catalog and requirements.")
500
503
  ] = False,
@@ -511,12 +514,21 @@ def validate_command(
511
514
  pipelines it holds, the pipeline a triggers document names, and the parameters its
512
515
  schedules pin.
513
516
 
517
+ A directory is read at every depth, and each YAML or JSON file in it that declares a
518
+ `dirigent/` format is checked; the others are counted as skipped.
519
+
514
520
  `--explain` adds the shape of the work ahead for every pipeline document that is valid:
515
521
  the fan-out widths, the retry budgets and the waits. With `--server` too, a sensor that
516
522
  declares neither cadence nor deadline takes the block's own.
517
523
  """
518
524
  state = state_of(ctx)
519
- documents = _documents_to_apply(reference, None)
525
+ skipped = 0
526
+ if reference is not None and Path(reference).is_dir():
527
+ documents, skipped = read_directory(Path(reference))
528
+ if not documents:
529
+ fail(PROJECT_IS_EMPTY, directory=reference)
530
+ else:
531
+ documents = _documents_to_apply(reference, None)
520
532
  failures = 0
521
533
  parsed: list[tuple[PipelineDefinition | TriggersDefinition, str]] = []
522
534
  for document in documents:
@@ -601,6 +613,7 @@ def validate_command(
601
613
  message="invalid" if failures else "valid",
602
614
  documents=len(documents),
603
615
  invalid=failures,
616
+ skipped=skipped,
604
617
  )
605
618
  if failures:
606
619
  raise typer.Exit(code=1)
@@ -80,6 +80,7 @@ class ConnectionSpec(BaseModel):
80
80
  code: str
81
81
  name: str | None = None
82
82
  kind: str = "http"
83
+ description: str | None = None
83
84
  config: JsonMap = Field(default_factory=dict)
84
85
 
85
86
 
@@ -236,7 +237,16 @@ def connections_for(definitions: Sequence[PipelineDefinition], given: Sequence[C
236
237
  embedded: dict[str, ConnectionSpec] = {}
237
238
  for definition in definitions:
238
239
  for code, carried in definition.connections.items():
239
- embedded.setdefault(code, ConnectionSpec(code=code, kind=carried.kind, config=carried.config))
240
+ embedded.setdefault(
241
+ code,
242
+ ConnectionSpec(
243
+ code=code,
244
+ name=carried.name,
245
+ kind=carried.kind,
246
+ description=carried.description,
247
+ config=carried.config,
248
+ ),
249
+ )
240
250
  replaced = {spec.code for spec in given}
241
251
  return [spec for code, spec in embedded.items() if code not in replaced] + list(given)
242
252
 
@@ -349,6 +359,7 @@ async def _seed_connections(
349
359
  code=spec.code,
350
360
  name=spec.name,
351
361
  kind=spec.kind,
362
+ description=spec.description,
352
363
  config=public,
353
364
  secret_envelope=envelope,
354
365
  secret_key_id=key_id,
@@ -208,7 +208,9 @@ async def _connections(client: Dirigent, declared: Sequence[ConnectionSpec], ori
208
208
  async def _connection(client: Dirigent, spec: ConnectionSpec) -> str:
209
209
  """Store one connection and say whether it was new, letting a refusal through."""
210
210
  try:
211
- await client.connections.create(spec.code, kind=spec.kind, config=spec.config, name=spec.name)
211
+ await client.connections.create(
212
+ spec.code, kind=spec.kind, config=spec.config, name=spec.name, description=spec.description
213
+ )
212
214
  except DirigentError:
213
215
  await client.connections.update(spec.code, config=spec.config)
214
216
  return "updated"
@@ -8,6 +8,7 @@ import httpx2
8
8
  import yaml
9
9
  from pydantic import BaseModel, ConfigDict
10
10
 
11
+ from dirigent_cli.project import DOCUMENT_SUFFIXES
11
12
  from dirigent_client import DEFAULT_CONNECT_TIMEOUT
12
13
  from dirigent_client.enums import ProvenanceSource
13
14
 
@@ -71,6 +72,37 @@ def read_path(path: Path) -> Document:
71
72
  return Document(text=path.read_text(), source=ProvenanceSource.FILE, ref=str(path))
72
73
 
73
74
 
75
+ def read_directory(directory: Path) -> tuple[list[Document], int]:
76
+ """Read every document under a directory, at any depth, and count the files that are not one.
77
+
78
+ A YAML or JSON file is a document when it declares a ``dirigent/`` format, or when it does
79
+ not parse at all, so a broken document is reported rather than passed over.
80
+ """
81
+ documents: list[Document] = []
82
+ skipped = 0
83
+ for path in sorted(directory.rglob("*")):
84
+ if not path.is_file() or path.suffix not in DOCUMENT_SUFFIXES:
85
+ continue
86
+ document = read_path(path)
87
+ if _declares_a_format(document.text):
88
+ documents.append(document)
89
+ else:
90
+ skipped += 1
91
+ return documents, skipped
92
+
93
+
94
+ def _declares_a_format(text: str) -> bool:
95
+ """Whether a file names a dirigent format at its top, or cannot be read well enough to say."""
96
+ try:
97
+ parsed: object = yaml.safe_load(text)
98
+ except yaml.YAMLError:
99
+ return True
100
+ if not isinstance(parsed, dict):
101
+ return False
102
+ declared = cast("dict[str, Any]", parsed).get("format")
103
+ return isinstance(declared, str) and declared.startswith("dirigent/")
104
+
105
+
74
106
  def parse_document(document: Document) -> dict[str, Any]:
75
107
  """Parse a document's text, refusing what is plainly not a pipeline document, by name.
76
108
 
File without changes
File without changes