diff --git a/.cursor/rules/environment-configuration.mdc b/.cursor/rules/environment-configuration.mdc new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/.cursor/rules/environment-configuration.mdc @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.env.development.example b/.env.development.example new file mode 100644 index 0000000..1046e78 --- /dev/null +++ b/.env.development.example @@ -0,0 +1,46 @@ +# Development Environment Configuration +# Copy this file to .env.development and modify as needed + +# Application Settings +APP_NAME=MyApp +APP_ENV=development +APP_DEBUG=true +APP_PORT=8000 +APP_SECRET_KEY=dev_secret_key_replace_in_real_file + +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=myapp_dev_db +DB_USER=dev_user +DB_PASSWORD=dev_password +DB_SSL=false +DB_MAX_CONNECTIONS=10 + +# API Keys and External Services +# Use development/test keys for external services +OPENAI_API_KEY=your_dev_openai_api_key +ANTHROPIC_API_KEY=your_dev_anthropic_api_key +EXTERNAL_SERVICE_URL=https://dev-api.example.com + +# Logging and Monitoring +LOG_LEVEL=debug +LOG_FILE=logs/dev-app.log +ENABLE_METRICS=true +METRICS_PORT=9090 + +# Security Settings +SSL_CERT_PATH= +SSL_KEY_PATH= +ALLOWED_HOSTS=localhost,127.0.0.1 +CORS_ORIGINS=http://localhost:3000 + +# Feature Flags +ENABLE_FEATURE_X=true +ENABLE_FEATURE_Y=true + +# Cache Settings +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_DB=1 +CACHE_TTL=60 \ No newline at end of file diff --git a/.env.example b/.env.example index 121e76c..8c96cac 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,42 @@ -OPENAI_API_KEY=your_openai_api_key_here \ No newline at end of file +# Application Settings +APP_NAME=MyApp +APP_ENV=development +APP_DEBUG=true +APP_PORT=8000 +APP_SECRET_KEY=replace_with_secure_random_key + +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=myapp_db +DB_USER=db_user +DB_PASSWORD=db_password +DB_SSL=false +DB_MAX_CONNECTIONS=10 + +# API Keys and External Services +OPENAI_API_KEY=your_openai_api_key +ANTHROPIC_API_KEY=your_anthropic_api_key +EXTERNAL_SERVICE_URL=https://api.example.com + +# Logging and Monitoring +LOG_LEVEL=info +LOG_FILE=logs/app.log +ENABLE_METRICS=false +METRICS_PORT=9090 + +# Security Settings +SSL_CERT_PATH= +SSL_KEY_PATH= +ALLOWED_HOSTS=localhost,127.0.0.1 +CORS_ORIGINS=http://localhost:3000 + +# Feature Flags +ENABLE_FEATURE_X=false +ENABLE_FEATURE_Y=true + +# Cache Settings +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_DB=0 +CACHE_TTL=3600 \ No newline at end of file diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..8460fad --- /dev/null +++ b/.env.production.example @@ -0,0 +1,52 @@ +# Production Environment Configuration +# Copy this file to .env.production and modify as needed + +# Application Settings +APP_NAME=MyApp +APP_ENV=production +APP_DEBUG=false +APP_PORT=8000 +APP_SECRET_KEY=prod_secret_key_replace_with_secure_value + +# Database Configuration +DB_HOST=db.production.example.com +DB_PORT=5432 +DB_NAME=myapp_prod_db +DB_USER=prod_user +DB_PASSWORD=prod_password_replace_with_secure_value +DB_SSL=true +DB_MAX_CONNECTIONS=100 + +# API Keys and External Services +# Use production keys for external services +OPENAI_API_KEY=your_prod_openai_api_key +ANTHROPIC_API_KEY=your_prod_anthropic_api_key +EXTERNAL_SERVICE_URL=https://api.example.com + +# Logging and Monitoring +LOG_LEVEL=warning +LOG_FILE=/var/log/myapp/app.log +ENABLE_METRICS=true +METRICS_PORT=9090 + +# Security Settings +SSL_CERT_PATH=/etc/ssl/certs/myapp.crt +SSL_KEY_PATH=/etc/ssl/private/myapp.key +ALLOWED_HOSTS=myapp.example.com,api.myapp.example.com +CORS_ORIGINS=https://myapp.example.com + +# Feature Flags +ENABLE_FEATURE_X=true +ENABLE_FEATURE_Y=true + +# Cache Settings +REDIS_HOST=redis.production.example.com +REDIS_PORT=6379 +REDIS_DB=0 +CACHE_TTL=3600 + +# Production-specific Settings +RATE_LIMIT_ENABLED=true +RATE_LIMIT_REQUESTS=100 +RATE_LIMIT_PERIOD=60 +ENABLE_REQUEST_LOGGING=true \ No newline at end of file diff --git a/.env.test.example b/.env.test.example new file mode 100644 index 0000000..fac2e2e --- /dev/null +++ b/.env.test.example @@ -0,0 +1,51 @@ +# Test Environment Configuration +# Copy this file to .env.test and modify as needed + +# Application Settings +APP_NAME=MyApp +APP_ENV=test +APP_DEBUG=true +APP_PORT=8001 +APP_SECRET_KEY=test_secret_key_replace_in_real_file + +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=myapp_test_db +DB_USER=test_user +DB_PASSWORD=test_password +DB_SSL=false +DB_MAX_CONNECTIONS=5 + +# API Keys and External Services +# Use mock services or test keys for external services +OPENAI_API_KEY=your_test_openai_api_key +ANTHROPIC_API_KEY=your_test_anthropic_api_key +EXTERNAL_SERVICE_URL=https://test-api.example.com + +# Logging and Monitoring +LOG_LEVEL=error +LOG_FILE=logs/test-app.log +ENABLE_METRICS=false +METRICS_PORT=9091 + +# Security Settings +SSL_CERT_PATH= +SSL_KEY_PATH= +ALLOWED_HOSTS=localhost,127.0.0.1 +CORS_ORIGINS=http://localhost:3000 + +# Feature Flags +ENABLE_FEATURE_X=true +ENABLE_FEATURE_Y=true + +# Cache Settings +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_DB=2 +CACHE_TTL=10 + +# Test-specific Settings +TEST_TIMEOUT=30 +MOCK_EXTERNAL_SERVICES=true +SKIP_SLOW_TESTS=false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7c6fd9f..d367f7a 100644 --- a/.gitignore +++ b/.gitignore @@ -191,3 +191,33 @@ session_*.json !.cursor/snippets/ !.cursor/metrics/ !.cursor/README.md + +# Environment files +.env.development +.env.test +.env.production +.env.backup.* + +# Keep example files +!.env.example +!.env.*.example + +# Logs +logs/ + +# Virtual Environment +venv/ +.venv/ +ENV/ + +# IDE files +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store + +# Temporary files +.commit_msg +.pr_body +.temp_* diff --git a/docs/environment-management.md b/docs/environment-management.md new file mode 100644 index 0000000..b7773e9 --- /dev/null +++ b/docs/environment-management.md @@ -0,0 +1,213 @@ +# Environment Management Guide + +This guide explains how to manage environment configurations in this project using the provided tools and following best practices. + +## Environment Files + +The project uses different environment files for different deployment contexts: + +- `.env.example` - Template with all variables (committed to repository) +- `.env.development.example` - Development environment template (committed) +- `.env.test.example` - Test environment template (committed) +- `.env.production.example` - Production environment template (committed) +- `.env` - Active environment file (not committed, created by copying or linking) + +## Setting Up Your Environment + +1. Copy the appropriate example file to create your environment file: + + ```bash + # For development + cp .env.development.example .env.development + + # For testing + cp .env.test.example .env.test + + # For production + cp .env.production.example .env.production + ``` + +2. Edit the copied file to set your specific values: + + ```bash + # Edit with your favorite editor + vim .env.development + ``` + +3. Activate the environment you want to use: + + ```bash + # Using the env_manager.py tool + python tools/env_manager.py switch development + ``` + +## Environment Manager Tool + +The `env_manager.py` tool provides several commands to help manage your environment configurations: + +### Switching Environments + +```bash +# Switch to development environment +python tools/env_manager.py switch development + +# Switch to test environment +python tools/env_manager.py switch test + +# Switch to production environment +python tools/env_manager.py switch production +``` + +### Validating Environment Configuration + +```bash +# Validate current environment +python tools/env_manager.py validate + +# Validate specific environment +python tools/env_manager.py validate development +``` + +### Managing Backups + +```bash +# Create a backup of the current environment +python tools/env_manager.py backup + +# List all environment backups +python tools/env_manager.py list-backups + +# Restore from a backup +python tools/env_manager.py restore .env.backup.20230101_120000 +``` + +### Creating Templates + +```bash +# Create a new .env.example template +python tools/env_manager.py create-template +``` + +## Best Practices + +### Security + +1. Never commit `.env` files with real credentials to version control +2. Use different credentials for each environment +3. Regularly rotate secrets, especially in production +4. Use environment variable encryption for highly sensitive data + +### Environment Isolation + +1. Use separate databases for each environment +2. Never connect development tools to production resources +3. Use different API keys for each environment +4. Implement network isolation between environments + +### Validation + +1. Always validate environment variables at application startup +2. Run environment-specific validation before deployment +3. Document all required variables and their acceptable values + +### Automation + +1. Use the provided tools to switch environments +2. Automate environment setup in CI/CD pipelines +3. Include environment validation in your build process + +## Environment-Specific Guidelines + +### Development Environment + +- Enable debug mode for detailed error information +- Use verbose logging to aid in development +- Mock external services when possible to avoid costs +- Use local resources (database, cache, etc.) + +### Test Environment + +- Use isolated databases to prevent test interference +- Mock external services for deterministic tests +- Minimize logging to focus on test output +- Use in-memory or ephemeral resources when possible + +### Production Environment + +- Disable debug mode to prevent information leakage +- Use appropriate logging levels (warning/error) +- Implement proper security measures (SSL, firewalls, etc.) +- Set up monitoring and alerting +- Use rate limiting to prevent abuse + +## Troubleshooting + +### Common Issues + +1. **Missing environment variables** + - Check if you've copied the correct example file + - Validate your environment using the validation tool + +2. **Environment switching issues** + - Ensure the target environment file exists + - Check for validation errors in the target environment + +3. **Application startup failures** + - Verify all required variables are set + - Check environment-specific requirements (e.g., SSL in production) + +### Getting Help + +If you encounter issues with environment configuration: + +1. Run the validation tool to identify problems +2. Check the application logs for specific error messages +3. Consult the project documentation for environment requirements + +## Contributing Environment Changes + +When making changes to environment configurations, follow these guidelines: + +### Using Git Helpers + +Always use the provided git helper functions for commits and PRs to ensure proper formatting and avoid issues with multi-line messages: + +```bash +# Source the helper functions +source tools/git_helpers.sh + +# Create a feature branch +create_feature_branch "update-env-config" + +# Make your changes... + +# Create a commit with a multi-line message +create_commit "Update environment configuration" " +- Added new required variables for feature X +- Updated validation rules for production +- Added documentation for new variables +" + +# Push the branch +push_branch + +# Create a PR +create_pr "Update environment configuration" " +## Changes +- Added new required variables for feature X +- Updated validation rules for production +- Added documentation for new variables + +## Testing +- Validated in development and test environments +- Confirmed backward compatibility +" +``` + +### Checklist for Environment Changes + +1. **Update All Environments**: When adding a new variable, update all environment example files +2. **Update Documentation**: Always update this document when making changes +3. **Update Validation**: Ensure the validation logic in `env_manager.py` is updated for new requirements +4. **Test All Environments**: Verify your changes work in all environments +5. **Review Security**: Ensure sensitive values are properly protected \ No newline at end of file diff --git a/tools/env_manager.py b/tools/env_manager.py new file mode 100644 index 0000000..a92e0b6 --- /dev/null +++ b/tools/env_manager.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 + +import argparse +import os +import shutil +import sys +from datetime import datetime +from pathlib import Path +from typing import Dict, List, Optional + +def validate_environment(env_type: str, env_vars: Dict[str, str]) -> List[str]: + """ + Validate environment variables based on environment type. + Returns a list of validation errors. + """ + errors = [] + + # Common required variables for all environments + required_vars = [ + 'APP_ENV', 'APP_NAME', 'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD' + ] + + # Check for missing required variables + for var in required_vars: + if var not in env_vars or not env_vars[var]: + errors.append(f"Missing required environment variable: {var}") + + # Environment-specific validation + if env_type == 'production': + # Production-specific checks + if env_vars.get('APP_DEBUG', '').lower() == 'true': + errors.append("APP_DEBUG should be set to 'false' in production") + + if not env_vars.get('SSL_CERT_PATH'): + errors.append("SSL_CERT_PATH is required in production") + + if not env_vars.get('SSL_KEY_PATH'): + errors.append("SSL_KEY_PATH is required in production") + + elif env_type == 'test': + # Test-specific checks + if not env_vars.get('DB_NAME', '').endswith('_test'): + errors.append("Test database name should end with '_test'") + + return errors + +def parse_env_file(file_path: Path) -> Dict[str, str]: + """Parse environment file and return variables as dictionary.""" + if not file_path.exists(): + return {} + + env_vars = {} + with open(file_path, 'r') as f: + for line in f: + line = line.strip() + if not line or line.startswith('#'): + continue + + if '=' in line: + key, value = line.split('=', 1) + env_vars[key.strip()] = value.strip() + + return env_vars + +def backup_env(env_path: Path) -> Path: + """Create a backup of the current environment file.""" + if not env_path.exists(): + return None + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_path = env_path.with_suffix(f".backup.{timestamp}") + shutil.copy2(env_path, backup_path) + return backup_path + +def restore_env_backup(backup_path: Path) -> None: + """Restore environment from a backup file.""" + env_path = Path.cwd() / '.env' + + if not backup_path.exists(): + raise FileNotFoundError(f"Backup file {backup_path} not found") + + shutil.copy2(backup_path, env_path) + print(f"Restored environment from {backup_path}") + +def list_env_backups() -> List[Path]: + """List all environment backup files.""" + root_dir = Path.cwd() + return sorted(root_dir.glob('.env.backup.*')) + +def switch_environment(env_type: str) -> None: + """Switch to a different environment configuration.""" + root_dir = Path.cwd() + env_path = root_dir / '.env' + target_env = root_dir / f'.env.{env_type}' + + if not target_env.exists(): + raise FileNotFoundError(f"Environment file .env.{env_type} not found") + + # Parse target environment to validate + env_vars = parse_env_file(target_env) + validation_errors = validate_environment(env_type, env_vars) + + if validation_errors: + print(f"Validation errors in {target_env}:", file=sys.stderr) + for error in validation_errors: + print(f" - {error}", file=sys.stderr) + + if input("Continue despite validation errors? (y/N): ").lower() != 'y': + print("Environment switch aborted.") + return + + # Create backup of current .env if it exists + if env_path.exists(): + backup_path = backup_env(env_path) + if backup_path: + print(f"Backed up current .env to {backup_path}") + + # Copy the target environment file to .env + shutil.copy2(target_env, env_path) + print(f"Switched to {env_type} environment") + + # Print active environment variables + print("\nActive environment variables:") + for key, value in sorted(env_vars.items()): + # Mask sensitive values + if any(sensitive in key.lower() for sensitive in ['password', 'secret', 'key']): + value = '*' * 8 + print(f" {key}={value}") + +def create_env_template() -> None: + """Create a new .env.example template file.""" + root_dir = Path.cwd() + template_path = root_dir / '.env.example' + + if template_path.exists(): + if input(f"{template_path} already exists. Overwrite? (y/N): ").lower() != 'y': + print("Template creation aborted.") + return + + with open(template_path, 'w') as f: + f.write("""# Application Settings +APP_NAME=MyApp +APP_ENV=development +APP_DEBUG=true +APP_PORT=8000 + +# Database Configuration +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=myapp_db +DB_USER=db_user +DB_PASSWORD=db_password + +# API Keys and External Services +API_KEY=your_api_key +EXTERNAL_SERVICE_URL=https://api.example.com + +# Logging and Monitoring +LOG_LEVEL=info +ENABLE_METRICS=false +""") + + print(f"Created environment template at {template_path}") + +def main(): + parser = argparse.ArgumentParser(description="Environment configuration manager") + subparsers = parser.add_subparsers(dest='command', help='Command to run') + + # Switch command + switch_parser = subparsers.add_parser('switch', help='Switch to a different environment') + switch_parser.add_argument('environment', choices=['development', 'test', 'production'], + help='Target environment') + + # Validate command + validate_parser = subparsers.add_parser('validate', help='Validate environment configuration') + validate_parser.add_argument('environment', nargs='?', + help='Environment to validate (defaults to current .env)') + + # Backup command + backup_parser = subparsers.add_parser('backup', help='Create a backup of the current environment') + + # Restore command + restore_parser = subparsers.add_parser('restore', help='Restore from a backup') + restore_parser.add_argument('backup', help='Backup file to restore from') + + # List backups command + subparsers.add_parser('list-backups', help='List all environment backups') + + # Create template command + subparsers.add_parser('create-template', help='Create a new .env.example template') + + args = parser.parse_args() + + try: + if args.command == 'switch': + switch_environment(args.environment) + elif args.command == 'validate': + env_path = Path.cwd() / (f'.env.{args.environment}' if args.environment else '.env') + if not env_path.exists(): + print(f"Environment file {env_path} not found", file=sys.stderr) + sys.exit(1) + + env_vars = parse_env_file(env_path) + env_type = args.environment or env_vars.get('APP_ENV', 'development') + + errors = validate_environment(env_type, env_vars) + if errors: + print(f"Validation errors in {env_path}:", file=sys.stderr) + for error in errors: + print(f" - {error}", file=sys.stderr) + sys.exit(1) + else: + print(f"Environment configuration is valid for {env_type}") + elif args.command == 'backup': + env_path = Path.cwd() / '.env' + if not env_path.exists(): + print("No .env file found to backup", file=sys.stderr) + sys.exit(1) + + backup_path = backup_env(env_path) + print(f"Created backup at {backup_path}") + elif args.command == 'restore': + backup_path = Path(args.backup) + restore_env_backup(backup_path) + elif args.command == 'list-backups': + backups = list_env_backups() + if not backups: + print("No environment backups found") + else: + print("Available environment backups:") + for i, backup in enumerate(backups, 1): + timestamp = backup.suffix.split('.')[-1] + print(f" {i}. {backup.name} (created: {timestamp})") + elif args.command == 'create-template': + create_env_template() + else: + parser.print_help() + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/tools/git_helpers.sh b/tools/git_helpers.sh new file mode 100755 index 0000000..cf4f180 --- /dev/null +++ b/tools/git_helpers.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +# Helper functions for git operations using hidden temporary files + +# Constants +COMMIT_MSG_FILE=".commit_msg" +PR_BODY_FILE=".pr_body" + +# Function to create a commit with a multi-line message +# Usage: create_commit "Title" "Body" [files...] +create_commit() { + local title="$1" + local body="$2" + shift 2 + local files=("$@") + + # Create the commit message with Cursor prefix + echo "[Cursor] $title" > "$COMMIT_MSG_FILE" + echo "" >> "$COMMIT_MSG_FILE" + echo "$body" >> "$COMMIT_MSG_FILE" + + # Add files if specified, otherwise add all changes + if [ ${#files[@]} -gt 0 ]; then + git add "${files[@]}" + else + git add . + fi + + # Create the commit + git commit -F "$COMMIT_MSG_FILE" + + # Clean up + rm -f "$COMMIT_MSG_FILE" +} + +# Function to create a PR with a multi-line description +# Usage: create_pr "Title" "Body" [base_branch] +create_pr() { + local title="$1" + local body="$2" + local base_branch="${3:-main}" + + # Create the PR body file with Cursor prefix + echo "[Cursor] $title" > "$PR_BODY_FILE" + echo "" >> "$PR_BODY_FILE" + echo "$body" >> "$PR_BODY_FILE" + + # Create the PR + gh pr create --title "[Cursor] $title" --body-file "$PR_BODY_FILE" --base "$base_branch" + + # Clean up + rm -f "$PR_BODY_FILE" +} + +# Function to create a feature branch +# Usage: create_feature_branch "feature-name" +create_feature_branch() { + local branch_name="$1" + + # Ensure branch name is valid + branch_name=$(echo "$branch_name" | tr '[:upper:]' '[:lower:]' | tr ' ' '-') + + # Create and checkout the branch + git checkout -b "$branch_name" + echo "Created and switched to branch: $branch_name" +} + +# Function to push a branch and set upstream +# Usage: push_branch [branch_name] +push_branch() { + local branch_name="${1:-$(git branch --show-current)}" + + git push -u origin "$branch_name" +} + +# Display help if no arguments provided +if [ $# -eq 0 ]; then + echo "Git Helper Functions" + echo "-------------------" + echo "Usage:" + echo " source tools/git_helpers.sh" + echo "" + echo "Available functions:" + echo " create_commit \"Title\" \"Body\" [files...]" + echo " create_pr \"Title\" \"Body\" [base_branch]" + echo " create_feature_branch \"feature-name\"" + echo " push_branch [branch_name]" +fi \ No newline at end of file