diff --git a/envschema/cli.py b/envschema/cli.py new file mode 100644 index 0000000..3e9c424 --- /dev/null +++ b/envschema/cli.py @@ -0,0 +1,152 @@ +"""CLI interface for EnvSchema.""" + +import sys +from pathlib import Path + +import click + +from envschema import __version__ +from envschema.schema import load_schema_from_file, Schema +from envschema.core import validate_environment +from envschema.generator import generate_env_example_to_file +from envschema.formatters import format_result + + +@click.group() +@click.version_option(version=__version__) +def cli(): + """EnvSchema - Validate environment variables against a schema.""" + pass + + +@cli.command() +@click.argument("schema", type=click.Path(exists=True)) +@click.option( + "--file", + "-f", + "env_file", + type=click.Path(), + help="Path to .env file to validate", +) +@click.option( + "--env/--no-env", + default=True, + help="Include os.environ in validation", +) +@click.option( + "--format", + "-o", + "output_format", + type=click.Choice(["text", "json"]), + default="text", + help="Output format", +) +@click.option( + "--ci", + is_flag=True, + help="CI mode (cleaner output)", +) +@click.option( + "--strict", + is_flag=True, + help="Fail on warnings", +) +def validate(schema, env_file, env, output_format, ci, strict): + """Validate environment variables against a schema. + + SCHEMA is the path to the schema file (JSON or YAML). + """ + try: + result = validate_environment( + schema_path=schema, + env_file=env_file, + use_environment=env, + ) + + output = format_result( + result, + output_format=output_format, + color=not ci, + ci_mode=ci, + ) + click.echo(output) + + if not result.is_valid: + sys.exit(1) + + if strict and result.warnings: + click.echo("Warnings found in strict mode", err=True) + sys.exit(1) + + sys.exit(0) + + except FileNotFoundError as e: + click.echo(f"Error: {e}", err=True) + sys.exit(2) + except ValueError as e: + click.echo(f"Error: {e}", err=True) + sys.exit(2) + + +@cli.command() +@click.argument("schema", type=click.Path(exists=True)) +@click.option( + "--output", + "-o", + "output_file", + type=click.Path(), + help="Output file path (default: .env.example)", +) +@click.option( + "--no-comments", + is_flag=True, + help="Don't include description comments", +) +def generate(schema, output_file, no_comments): + """Generate .env.example from a schema. + + SCHEMA is the path to the schema file (JSON or YAML). + """ + try: + schema_obj = load_schema_from_file(schema) + + output_path = output_file or ".env.example" + + generate_env_example_to_file( + schema_obj, + output_path, + include_descriptions=not no_comments, + ) + + click.echo(f"Generated {output_path}") + + except FileNotFoundError as e: + click.echo(f"Error: {e}", err=True) + sys.exit(2) + except ValueError as e: + click.echo(f"Error: {e}", err=True) + sys.exit(2) + + +@cli.command() +@click.argument("schema", type=click.Path(exists=True)) +def check(schema): + """Check if a schema file is valid. + + SCHEMA is the path to the schema file (JSON or YAML). + """ + try: + schema_obj = load_schema_from_file(schema) + click.echo(f"Schema is valid (version: {schema_obj.version})") + click.echo(f"Found {len(schema_obj.envvars)} environment variables") + sys.exit(0) + except FileNotFoundError as e: + click.echo(f"Error: {e}", err=True) + sys.exit(2) + except ValueError as e: + click.echo(f"Error: {e}", err=True) + sys.exit(2) + + +if __name__ == "__main__": + cli() \ No newline at end of file