| |
| |
| |
| |
| |
|
|
| """ |
| OpenEnv validate command. |
| |
| This module provides the 'openenv validate' command to check if environments |
| are properly configured for multi-mode deployment. |
| """ |
|
|
| import json |
| from pathlib import Path |
| from typing import Annotated |
|
|
| import typer |
| from openenv.cli._validation import ( |
| build_local_validation_json_report, |
| format_validation_report, |
| get_deployment_modes, |
| validate_multi_mode_deployment, |
| validate_running_environment, |
| ) |
|
|
|
|
| def _looks_like_url(value: str) -> bool: |
| """Return True when the value appears to be a URL target.""" |
| candidate = value.strip().lower() |
| return candidate.startswith("http://") or candidate.startswith("https://") |
|
|
|
|
| def validate( |
| target: Annotated[ |
| str | None, |
| typer.Argument( |
| help=( |
| "Path to the environment directory (default: current directory) " |
| "or a running OpenEnv URL (http://... or https://...)" |
| ), |
| ), |
| ] = None, |
| url: Annotated[ |
| str | None, |
| typer.Option( |
| "--url", |
| help="Validate a running OpenEnv server by base URL (e.g. http://localhost:8000)", |
| ), |
| ] = None, |
| json_output: Annotated[ |
| bool, |
| typer.Option( |
| "--json", |
| help="Output local validation report as JSON (runtime validation is JSON by default)", |
| ), |
| ] = False, |
| timeout: Annotated[ |
| float, |
| typer.Option( |
| "--timeout", |
| help="HTTP timeout in seconds for runtime validation", |
| min=0.1, |
| ), |
| ] = 5.0, |
| verbose: Annotated[ |
| bool, typer.Option("--verbose", "-v", help="Show detailed information") |
| ] = False, |
| ) -> None: |
| """ |
| Validate local environments and running OpenEnv servers. |
| |
| Local validation checks if an environment is properly configured with: |
| - Required files (pyproject.toml, openenv.yaml, server/app.py, etc.) |
| - Docker deployment support |
| - uv run server capability |
| - python -m module execution |
| |
| Runtime validation checks if a live OpenEnv server conforms to the |
| versioned runtime API contract and returns a criteria-based JSON report. |
| |
| Examples: |
| # Validate current directory (recommended) |
| $ cd my_env |
| $ openenv validate |
| |
| # Validate a running environment and return JSON criteria |
| $ openenv validate --url http://localhost:8000 |
| $ openenv validate https://my-env.hf.space |
| |
| # Validate with detailed output |
| $ openenv validate --verbose |
| |
| # Validate specific environment |
| $ openenv validate envs/echo_env |
| """ |
| runtime_target = url |
| if ( |
| runtime_target is not None |
| and target is not None |
| and not _looks_like_url(target) |
| ): |
| typer.echo( |
| "Error: Cannot combine a local path argument with --url runtime validation", |
| err=True, |
| ) |
| raise typer.Exit(1) |
|
|
| if target is not None and _looks_like_url(target): |
| if runtime_target is not None and runtime_target != target: |
| typer.echo( |
| "Error: Conflicting runtime targets provided via argument and --url", |
| err=True, |
| ) |
| raise typer.Exit(1) |
| runtime_target = target |
|
|
| if runtime_target is not None: |
| try: |
| report = validate_running_environment(runtime_target, timeout_s=timeout) |
| except ValueError as exc: |
| typer.echo(f"Error: {exc}", err=True) |
| raise typer.Exit(1) from exc |
|
|
| typer.echo(json.dumps(report, indent=2)) |
| if not report.get("passed", False): |
| raise typer.Exit(1) |
| return |
|
|
| |
| if target is None: |
| env_path_obj = Path.cwd() |
| else: |
| env_path_obj = Path(target) |
|
|
| if not env_path_obj.exists(): |
| typer.echo(f"Error: Path does not exist: {env_path_obj}", err=True) |
| raise typer.Exit(1) |
|
|
| if not env_path_obj.is_dir(): |
| typer.echo(f"Error: Path is not a directory: {env_path_obj}", err=True) |
| raise typer.Exit(1) |
|
|
| |
| openenv_yaml = env_path_obj / "openenv.yaml" |
| if not openenv_yaml.exists(): |
| typer.echo( |
| f"Error: Not an OpenEnv environment directory (missing openenv.yaml): {env_path_obj}", |
| err=True, |
| ) |
| typer.echo( |
| "Hint: Run this command from the environment root directory or specify the path", |
| err=True, |
| ) |
| raise typer.Exit(1) |
|
|
| env_name = env_path_obj.name |
| if env_name.endswith("_env"): |
| base_name = env_name[:-4] |
| else: |
| base_name = env_name |
|
|
| |
| is_valid, issues = validate_multi_mode_deployment(env_path_obj) |
| modes = get_deployment_modes(env_path_obj) |
|
|
| if json_output: |
| report = build_local_validation_json_report( |
| env_name=base_name, |
| env_path=env_path_obj, |
| is_valid=is_valid, |
| issues=issues, |
| deployment_modes=modes if verbose else None, |
| ) |
| typer.echo(json.dumps(report, indent=2)) |
| if not is_valid: |
| raise typer.Exit(1) |
| return |
|
|
| |
| report = format_validation_report(base_name, is_valid, issues) |
| typer.echo(report) |
|
|
| |
| if verbose: |
| typer.echo("\nSupported deployment modes:") |
| for mode, supported in modes.items(): |
| status = "[YES]" if supported else "[NO]" |
| typer.echo(f" {status} {mode}") |
|
|
| if is_valid: |
| typer.echo("\nUsage examples:") |
| typer.echo(f" cd {env_path_obj.name} && uv run server") |
| typer.echo(f" cd {env_path_obj.name} && openenv build") |
| typer.echo(f" cd {env_path_obj.name} && openenv push") |
|
|
| if not is_valid: |
| raise typer.Exit(1) |
|
|