deploy-ready-copilot / env_validator.py
HIMANSHUKUMARJHA's picture
Add 10 major utility improvements
a42b16d
"""Environment variable validation and suggestions."""
from __future__ import annotations
import os
import re
from pathlib import Path
from typing import Any, Dict, List, Optional
class EnvironmentValidator:
"""Validates and suggests environment variables."""
def __init__(self):
self.common_vars = {
"next.js": ["NEXT_PUBLIC_API_URL", "DATABASE_URL", "NEXTAUTH_SECRET", "NEXTAUTH_URL"],
"django": ["SECRET_KEY", "DEBUG", "DATABASE_URL", "ALLOWED_HOSTS"],
"fastapi": ["DATABASE_URL", "SECRET_KEY", "CORS_ORIGINS", "ENVIRONMENT"],
"react": ["REACT_APP_API_URL", "REACT_APP_ENV"],
"express": ["PORT", "NODE_ENV", "DATABASE_URL", "JWT_SECRET"],
"nestjs": ["PORT", "DATABASE_URL", "JWT_SECRET", "NODE_ENV"],
}
def validate_env_file(self, folder_path: str, framework: Optional[str] = None) -> Dict[str, Any]:
"""Validate .env files in codebase."""
path = Path(folder_path)
env_files = list(path.rglob(".env*"))
found_vars = []
missing_vars = []
issues = []
# Read all env files
for env_file in env_files:
if env_file.is_file() and ".git" not in str(env_file):
try:
content = env_file.read_text()
# Extract variable names
var_pattern = r'^([A-Z_][A-Z0-9_]*)\s*='
vars_in_file = re.findall(var_pattern, content, re.MULTILINE)
found_vars.extend(vars_in_file)
except Exception:
pass
# Check for framework-specific requirements
if framework:
required_vars = self.common_vars.get(framework.lower(), [])
for var in required_vars:
if var not in found_vars:
missing_vars.append({
"variable": var,
"required": True,
"description": self._get_var_description(var, framework)
})
# Check for common issues
for env_file in env_files:
try:
content = env_file.read_text()
# Check for hardcoded secrets
if re.search(r'(password|secret|key|token)\s*=\s*["\'][^"\']+["\']', content, re.IGNORECASE):
issues.append({
"file": str(env_file.relative_to(path)),
"severity": "high",
"issue": "Hardcoded secrets detected - use environment variables or secrets manager"
})
# Check for .env in git
gitignore = path / ".gitignore"
if gitignore.exists():
gitignore_content = gitignore.read_text()
if ".env" not in gitignore_content:
issues.append({
"file": ".gitignore",
"severity": "medium",
"issue": ".env files should be in .gitignore"
})
except Exception:
pass
return {
"env_files_found": len(env_files),
"variables_found": len(set(found_vars)),
"missing_required": missing_vars,
"issues": issues,
"status": "valid" if not missing_vars and not issues else "needs_attention",
"recommendations": self._get_recommendations(framework, missing_vars, issues)
}
def suggest_env_vars(self, framework: str, platform: str) -> List[Dict[str, str]]:
"""Suggest environment variables based on framework and platform."""
suggestions = []
# Framework-specific
framework_vars = self.common_vars.get(framework.lower(), [])
for var in framework_vars:
suggestions.append({
"variable": var,
"required": True,
"description": self._get_var_description(var, framework),
"category": "framework"
})
# Platform-specific
platform_vars = {
"vercel": ["VERCEL_URL", "VERCEL_ENV"],
"netlify": ["NETLIFY", "CONTEXT"],
"aws": ["AWS_REGION", "AWS_ACCESS_KEY_ID"],
"gcp": ["GOOGLE_CLOUD_PROJECT", "GCP_REGION"],
"azure": ["AZURE_REGION", "AZURE_SUBSCRIPTION_ID"],
}
platform_vars_list = platform_vars.get(platform.lower(), [])
for var in platform_vars_list:
suggestions.append({
"variable": var,
"required": False,
"description": f"Platform-specific variable for {platform}",
"category": "platform"
})
return suggestions
def _get_var_description(self, var: str, framework: str) -> str:
"""Get description for a variable."""
descriptions = {
"DATABASE_URL": "Database connection string",
"SECRET_KEY": "Secret key for encryption/signing",
"API_URL": "API endpoint URL",
"NODE_ENV": "Node.js environment (development/production)",
"PORT": "Application port number",
"DEBUG": "Debug mode flag",
}
return descriptions.get(var, f"Required for {framework}")
def _get_recommendations(
self,
framework: Optional[str],
missing_vars: List[Dict],
issues: List[Dict]
) -> List[str]:
"""Get recommendations based on validation results."""
recommendations = []
if missing_vars:
recommendations.append(f"Add {len(missing_vars)} missing required environment variables")
if issues:
recommendations.append("Review security issues in environment files")
recommendations.append("Use a secrets manager (AWS Secrets Manager, HashiCorp Vault) for production")
recommendations.append("Never commit .env files to version control")
if framework:
recommendations.append(f"Follow {framework} best practices for environment configuration")
return recommendations