Spaces:
Running
Running
| # Copyright (c) Meta Platforms, Inc. and affiliates. | |
| # All rights reserved. | |
| # | |
| # This source code is licensed under the BSD-style license found in the | |
| # LICENSE file in the root directory of this source tree. | |
| """ | |
| 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 | |
| # Determine environment path (default to current directory) | |
| 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) | |
| # Check for openenv.yaml to confirm this is an environment directory | |
| 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 | |
| # Run validation | |
| 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 | |
| # Show validation report | |
| report = format_validation_report(base_name, is_valid, issues) | |
| typer.echo(report) | |
| # Show deployment modes if verbose | |
| 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) | |