Skip to content

Commit 8b708b5

Browse files
Copilottoby-coleman
andcommitted
Add plugboard process validate CLI command
Adds a new `validate` subcommand to the CLI that checks whether a process defined in a YAML config file is valid. It builds the process and runs all topology validation checks (connected inputs, event matching, cycle detection). Exits with code 0 on success, code 1 with error details on failure. Co-authored-by: toby-coleman <13170610+toby-coleman@users.noreply.github.com>
1 parent d96d7b2 commit 8b708b5

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

plugboard/cli/process/__init__.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from plugboard.diagram import MermaidDiagram
1515
from plugboard.process import Process, ProcessBuilder
16-
from plugboard.schemas import ConfigSpec
16+
from plugboard.schemas import ConfigSpec, validate_process
1717
from plugboard.tune import Tuner
1818
from plugboard.utils import add_sys_path, run_coro_sync
1919

@@ -164,3 +164,31 @@ def diagram(
164164
diagram = MermaidDiagram.from_process(process)
165165
md = Markdown(f"```\n{diagram.diagram}\n```\n[Editable diagram]({diagram.url}) (external link)")
166166
print(md)
167+
168+
169+
@app.command()
170+
def validate(
171+
config: Annotated[
172+
Path,
173+
typer.Argument(
174+
exists=True,
175+
file_okay=True,
176+
dir_okay=False,
177+
writable=False,
178+
readable=True,
179+
resolve_path=True,
180+
help="Path to the YAML configuration file.",
181+
),
182+
],
183+
) -> None:
184+
"""Validate a Plugboard process configuration."""
185+
config_spec = _read_yaml(config)
186+
with add_sys_path(config.parent):
187+
process = _build_process(config_spec)
188+
errors = validate_process(process.dict())
189+
if errors:
190+
stderr.print("[red]Validation failed:[/red]")
191+
for error in errors:
192+
stderr.print(f" • {error}")
193+
raise typer.Exit(1)
194+
print("[green]Validation passed[/green]")

tests/unit/test_cli.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ def test_cli_process_diagram() -> None:
8787
assert "flowchart" in result.stdout
8888

8989

90+
def test_cli_process_validate() -> None:
91+
"""Tests the process validate command."""
92+
result = runner.invoke(app, ["process", "validate", "tests/data/minimal-process.yaml"])
93+
# CLI must run without error for a valid config
94+
assert result.exit_code == 0
95+
assert "Validation passed" in result.stdout
96+
97+
98+
def test_cli_process_validate_invalid() -> None:
99+
"""Tests the process validate command with an invalid process."""
100+
with patch("plugboard.cli.process.validate_process") as mock_validate:
101+
mock_validate.return_value = ["Component 'x' has unconnected inputs: ['in_1']"]
102+
result = runner.invoke(app, ["process", "validate", "tests/data/minimal-process.yaml"])
103+
assert result.exit_code == 1
104+
assert "Validation failed" in result.stderr
105+
106+
90107
def test_cli_server_discover(test_project_dir: Path) -> None:
91108
"""Tests the server discover command."""
92109
with respx.mock:

0 commit comments

Comments
 (0)