Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Local Docker Test Script for Hugging Face Spaces | |
| Run this before deploying to verify everything works | |
| """ | |
| import os | |
| import sys | |
| import subprocess | |
| import time | |
| import requests | |
| from pathlib import Path | |
| # ANSI color codes | |
| GREEN = '\033[92m' | |
| RED = '\033[91m' | |
| YELLOW = '\033[93m' | |
| BLUE = '\033[94m' | |
| RESET = '\033[0m' | |
| def print_status(message, status="info"): | |
| """Print colored status message""" | |
| colors = { | |
| "success": GREEN, | |
| "error": RED, | |
| "warning": YELLOW, | |
| "info": BLUE | |
| } | |
| color = colors.get(status, RESET) | |
| symbol = "β " if status == "success" else "β" if status == "error" else "β οΈ" if status == "warning" else "βΉοΈ" | |
| print(f"{color}{symbol} {message}{RESET}") | |
| def check_docker(): | |
| """Check if Docker is installed and running""" | |
| try: | |
| result = subprocess.run(['docker', '--version'], capture_output=True, text=True) | |
| if result.returncode == 0: | |
| print_status(f"Docker installed: {result.stdout.strip()}", "success") | |
| return True | |
| except FileNotFoundError: | |
| print_status("Docker is not installed. Please install Docker first.", "error") | |
| return False | |
| return False | |
| def check_required_files(): | |
| """Check if all required files exist""" | |
| required_files = [ | |
| 'Dockerfile', | |
| 'requirements.txt', | |
| 'main.py', | |
| 'app/__init__.py', | |
| 'app/config.py', | |
| 'app/database.py', | |
| ] | |
| missing_files = [] | |
| for file in required_files: | |
| if not Path(file).exists(): | |
| missing_files.append(file) | |
| print_status(f"Missing: {file}", "error") | |
| else: | |
| print_status(f"Found: {file}", "success") | |
| return len(missing_files) == 0 | |
| def check_env_variables(): | |
| """Check if required environment variables are set""" | |
| required_vars = ['DATABASE_URL', 'SECRET_KEY'] | |
| missing_vars = [] | |
| for var in required_vars: | |
| if os.getenv(var): | |
| print_status(f"{var} is set", "success") | |
| else: | |
| missing_vars.append(var) | |
| print_status(f"{var} is NOT set", "warning") | |
| if missing_vars: | |
| print_status("Some environment variables are missing. App will use fallback values.", "warning") | |
| print_status("This is OK for local testing, but REQUIRED for production.", "info") | |
| return True | |
| def build_docker_image(): | |
| """Build Docker image""" | |
| print_status("Building Docker image (this may take 5-10 minutes)...", "info") | |
| try: | |
| result = subprocess.run( | |
| ['docker', 'build', '-t', 'rating-prediction-test', '.'], | |
| capture_output=False, | |
| text=True | |
| ) | |
| if result.returncode == 0: | |
| print_status("Docker image built successfully", "success") | |
| return True | |
| else: | |
| print_status("Docker build failed", "error") | |
| return False | |
| except Exception as e: | |
| print_status(f"Build error: {str(e)}", "error") | |
| return False | |
| def run_docker_container(): | |
| """Run Docker container""" | |
| print_status("Starting Docker container...", "info") | |
| # Prepare environment variables | |
| env_vars = [] | |
| if os.getenv('DATABASE_URL'): | |
| env_vars.extend(['-e', f"DATABASE_URL={os.getenv('DATABASE_URL')}"]) | |
| if os.getenv('SECRET_KEY'): | |
| env_vars.extend(['-e', f"SECRET_KEY={os.getenv('SECRET_KEY')}"]) | |
| cmd = [ | |
| 'docker', 'run', '-d', | |
| '--name', 'rating-prediction-test', | |
| '-p', '7860:7860', | |
| *env_vars, | |
| 'rating-prediction-test' | |
| ] | |
| try: | |
| result = subprocess.run(cmd, capture_output=True, text=True) | |
| if result.returncode == 0: | |
| container_id = result.stdout.strip() | |
| print_status(f"Container started: {container_id[:12]}", "success") | |
| return container_id | |
| else: | |
| print_status(f"Container start failed: {result.stderr}", "error") | |
| return None | |
| except Exception as e: | |
| print_status(f"Run error: {str(e)}", "error") | |
| return None | |
| def wait_for_app(max_wait=60): | |
| """Wait for app to become ready""" | |
| print_status(f"Waiting for app to start (max {max_wait}s)...", "info") | |
| start_time = time.time() | |
| while time.time() - start_time < max_wait: | |
| try: | |
| response = requests.get('http://localhost:7860/docs', timeout=5) | |
| if response.status_code == 200: | |
| print_status("App is ready!", "success") | |
| return True | |
| except requests.exceptions.RequestException: | |
| pass | |
| time.sleep(2) | |
| print(".", end="", flush=True) | |
| print() | |
| print_status("App did not start in time", "error") | |
| return False | |
| def test_endpoints(): | |
| """Test key API endpoints""" | |
| print_status("Testing API endpoints...", "info") | |
| tests = [ | |
| ('GET', '/docs', 200, 'Swagger UI'), | |
| ('GET', '/api/auth/login', 405, 'Auth endpoint (405 expected for GET)'), | |
| ] | |
| passed = 0 | |
| failed = 0 | |
| for method, endpoint, expected_status, description in tests: | |
| try: | |
| url = f'http://localhost:7860{endpoint}' | |
| if method == 'GET': | |
| response = requests.get(url, timeout=10) | |
| else: | |
| response = requests.post(url, timeout=10) | |
| if response.status_code == expected_status: | |
| print_status(f"{description}: {response.status_code}", "success") | |
| passed += 1 | |
| else: | |
| print_status(f"{description}: Expected {expected_status}, got {response.status_code}", "error") | |
| failed += 1 | |
| except Exception as e: | |
| print_status(f"{description}: {str(e)}", "error") | |
| failed += 1 | |
| return passed, failed | |
| def show_logs(): | |
| """Show container logs""" | |
| print_status("Container logs (last 20 lines):", "info") | |
| try: | |
| result = subprocess.run( | |
| ['docker', 'logs', '--tail', '20', 'rating-prediction-test'], | |
| capture_output=True, | |
| text=True | |
| ) | |
| print(result.stdout) | |
| if result.stderr: | |
| print(result.stderr) | |
| except Exception as e: | |
| print_status(f"Could not fetch logs: {str(e)}", "error") | |
| def cleanup(): | |
| """Clean up test container and image""" | |
| print_status("Cleaning up...", "info") | |
| # Stop and remove container | |
| subprocess.run(['docker', 'stop', 'rating-prediction-test'], | |
| capture_output=True, stderr=subprocess.DEVNULL) | |
| subprocess.run(['docker', 'rm', 'rating-prediction-test'], | |
| capture_output=True, stderr=subprocess.DEVNULL) | |
| # Optionally remove image | |
| response = input("\nRemove Docker image? (y/N): ") | |
| if response.lower() == 'y': | |
| subprocess.run(['docker', 'rmi', 'rating-prediction-test'], | |
| capture_output=True) | |
| print_status("Image removed", "success") | |
| def main(): | |
| """Main test flow""" | |
| print(f"\n{BLUE}{'='*60}{RESET}") | |
| print(f"{BLUE}Hugging Face Spaces - Local Docker Test{RESET}") | |
| print(f"{BLUE}{'='*60}{RESET}\n") | |
| # Step 1: Check Docker | |
| if not check_docker(): | |
| sys.exit(1) | |
| # Step 2: Check files | |
| print("\n--- Checking Required Files ---") | |
| if not check_required_files(): | |
| print_status("Some required files are missing. Cannot proceed.", "error") | |
| sys.exit(1) | |
| # Step 3: Check environment variables | |
| print("\n--- Checking Environment Variables ---") | |
| check_env_variables() | |
| # Step 4: Build image | |
| print("\n--- Building Docker Image ---") | |
| if not build_docker_image(): | |
| sys.exit(1) | |
| # Step 5: Run container | |
| print("\n--- Starting Container ---") | |
| container_id = run_docker_container() | |
| if not container_id: | |
| sys.exit(1) | |
| try: | |
| # Step 6: Wait for app | |
| print("\n--- Waiting for Application ---") | |
| if not wait_for_app(): | |
| show_logs() | |
| sys.exit(1) | |
| # Step 7: Test endpoints | |
| print("\n--- Testing Endpoints ---") | |
| passed, failed = test_endpoints() | |
| # Step 8: Show logs | |
| print("\n--- Container Logs ---") | |
| show_logs() | |
| # Results | |
| print(f"\n{BLUE}{'='*60}{RESET}") | |
| print(f"{GREEN if failed == 0 else RED}Test Results:{RESET}") | |
| print(f" β Passed: {passed}") | |
| print(f" β Failed: {failed}") | |
| print(f"{BLUE}{'='*60}{RESET}\n") | |
| if failed == 0: | |
| print_status("All tests passed! Ready for Hugging Face deployment. π", "success") | |
| print_status("Access the app at: http://localhost:7860", "info") | |
| print_status("Press Ctrl+C when done testing", "info") | |
| # Keep container running | |
| input("\nPress Enter to stop container and cleanup...") | |
| else: | |
| print_status("Some tests failed. Review logs above.", "error") | |
| finally: | |
| # Cleanup | |
| cleanup() | |
| if __name__ == '__main__': | |
| try: | |
| main() | |
| except KeyboardInterrupt: | |
| print("\n\nInterrupted by user") | |
| cleanup() | |
| except Exception as e: | |
| print_status(f"Unexpected error: {str(e)}", "error") | |
| cleanup() | |
| sys.exit(1) | |