openenv/coding_env
21
1# Copyright (c) Meta Platforms, Inc. and affiliates.2# All rights reserved.3#4# This source code is licensed under the BSD-style license found in the5# LICENSE file in the root directory of this source tree.6 7"""8OpenEnv validate command.9 10This module provides the 'openenv validate' command to check if environments11are properly configured for multi-mode deployment.12"""13 14import json15from pathlib import Path16from typing import Annotated17 18import typer19from openenv.cli._validation import (20 build_local_validation_json_report,21 format_validation_report,22 get_deployment_modes,23 validate_multi_mode_deployment,24 validate_running_environment,25)26 27 28def _looks_like_url(value: str) -> bool:29 """Return True when the value appears to be a URL target."""30 candidate = value.strip().lower()31 return candidate.startswith("http://") or candidate.startswith("https://")32 33 34def validate(35 target: Annotated[36 str | None,37 typer.Argument(38 help=(39 "Path to the environment directory (default: current directory) "40 "or a running OpenEnv URL (http://... or https://...)"41 ),42 ),43 ] = None,44 url: Annotated[45 str | None,46 typer.Option(47 "--url",48 help="Validate a running OpenEnv server by base URL (e.g. http://localhost:8000)",49 ),50 ] = None,51 json_output: Annotated[52 bool,53 typer.Option(54 "--json",55 help="Output local validation report as JSON (runtime validation is JSON by default)",56 ),57 ] = False,58 timeout: Annotated[59 float,60 typer.Option(61 "--timeout",62 help="HTTP timeout in seconds for runtime validation",63 min=0.1,64 ),65 ] = 5.0,66 verbose: Annotated[67 bool, typer.Option("--verbose", "-v", help="Show detailed information")68 ] = False,69) -> None:70 """71 Validate local environments and running OpenEnv servers.72 73 Local validation checks if an environment is properly configured with:74 - Required files (pyproject.toml, openenv.yaml, server/app.py, etc.)75 - Docker deployment support76 - uv run server capability77 - python -m module execution78 79 Runtime validation checks if a live OpenEnv server conforms to the80 versioned runtime API contract and returns a criteria-based JSON report.81 82 Examples:83 # Validate current directory (recommended)84 $ cd my_env85 $ openenv validate86 87 # Validate a running environment and return JSON criteria88 $ openenv validate --url http://localhost:800089 $ openenv validate https://my-env.hf.space90 91 # Validate with detailed output92 $ openenv validate --verbose93 94 # Validate specific environment95 $ openenv validate envs/echo_env96 """97 runtime_target = url98 if (99 runtime_target is not None100 and target is not None101 and not _looks_like_url(target)102 ):103 typer.echo(104 "Error: Cannot combine a local path argument with --url runtime validation",105 err=True,106 )107 raise typer.Exit(1)108 109 if target is not None and _looks_like_url(target):110 if runtime_target is not None and runtime_target != target:111 typer.echo(112 "Error: Conflicting runtime targets provided via argument and --url",113 err=True,114 )115 raise typer.Exit(1)116 runtime_target = target117 118 if runtime_target is not None:119 try:120 report = validate_running_environment(runtime_target, timeout_s=timeout)121 except ValueError as exc:122 typer.echo(f"Error: {exc}", err=True)123 raise typer.Exit(1) from exc124 125 typer.echo(json.dumps(report, indent=2))126 if not report.get("passed", False):127 raise typer.Exit(1)128 return129 130 # Determine environment path (default to current directory)131 if target is None:132 env_path_obj = Path.cwd()133 else:134 env_path_obj = Path(target)135 136 if not env_path_obj.exists():137 typer.echo(f"Error: Path does not exist: {env_path_obj}", err=True)138 raise typer.Exit(1)139 140 if not env_path_obj.is_dir():141 typer.echo(f"Error: Path is not a directory: {env_path_obj}", err=True)142 raise typer.Exit(1)143 144 # Check for openenv.yaml to confirm this is an environment directory145 openenv_yaml = env_path_obj / "openenv.yaml"146 if not openenv_yaml.exists():147 typer.echo(148 f"Error: Not an OpenEnv environment directory (missing openenv.yaml): {env_path_obj}",149 err=True,150 )151 typer.echo(152 "Hint: Run this command from the environment root directory or specify the path",153 err=True,154 )155 raise typer.Exit(1)156 157 env_name = env_path_obj.name158 if env_name.endswith("_env"):159 base_name = env_name[:-4]160 else:161 base_name = env_name162 163 # Run validation164 is_valid, issues = validate_multi_mode_deployment(env_path_obj)165 modes = get_deployment_modes(env_path_obj)166 167 if json_output:168 report = build_local_validation_json_report(169 env_name=base_name,170 env_path=env_path_obj,171 is_valid=is_valid,172 issues=issues,173 deployment_modes=modes if verbose else None,174 )175 typer.echo(json.dumps(report, indent=2))176 if not is_valid:177 raise typer.Exit(1)178 return179 180 # Show validation report181 report = format_validation_report(base_name, is_valid, issues)182 typer.echo(report)183 184 # Show deployment modes if verbose185 if verbose:186 typer.echo("\nSupported deployment modes:")187 for mode, supported in modes.items():188 status = "[YES]" if supported else "[NO]"189 typer.echo(f" {status} {mode}")190 191 if is_valid:192 typer.echo("\nUsage examples:")193 typer.echo(f" cd {env_path_obj.name} && uv run server")194 typer.echo(f" cd {env_path_obj.name} && openenv build")195 typer.echo(f" cd {env_path_obj.name} && openenv push")196 197 if not is_valid:198 raise typer.Exit(1)199 