Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Automated deployment script for Hugging Face Spaces | |
| """ | |
| import os | |
| import subprocess | |
| import sys | |
| import shutil | |
| from pathlib import Path | |
| import json | |
| import requests | |
| from datetime import datetime | |
| class HuggingFaceDeployer: | |
| def __init__(self, username: str, space_name: str, token: str = None): | |
| self.username = username | |
| self.space_name = space_name | |
| self.token = token | |
| self.space_url = f"https://huggingface.co/spaces/{username}/{space_name}" | |
| self.git_url = f"https://huggingface.co/spaces/{username}/{space_name}.git" | |
| def check_requirements(self): | |
| """Check if all required files exist""" | |
| required_files = [ | |
| 'app.py', | |
| 'requirements.txt', | |
| 'Dockerfile', | |
| 'README.md', | |
| 'Synthetic_Email_Dataset.csv' | |
| ] | |
| missing_files = [] | |
| for file in required_files: | |
| if not os.path.exists(file): | |
| missing_files.append(file) | |
| if missing_files: | |
| print(f"β Missing required files: {', '.join(missing_files)}") | |
| return False | |
| print("β All required files found") | |
| return True | |
| def setup_huggingface_space(self): | |
| """Create Hugging Face Space via API""" | |
| if not self.token: | |
| print("β οΈ No Hugging Face token provided. You'll need to create the space manually.") | |
| print(f"Go to: https://huggingface.co/new-space") | |
| input("Press Enter after creating the space...") | |
| return True | |
| headers = {"Authorization": f"Bearer {self.token}"} | |
| data = { | |
| "type": "space", | |
| "name": self.space_name, | |
| "sdk": "docker", | |
| "private": False | |
| } | |
| response = requests.post( | |
| f"https://huggingface.co/api/repos/create", | |
| headers=headers, | |
| json=data | |
| ) | |
| if response.status_code == 201: | |
| print(f"β Space created successfully: {self.space_url}") | |
| return True | |
| elif response.status_code == 409: | |
| print(f"βΉοΈ Space already exists: {self.space_url}") | |
| return True | |
| else: | |
| print(f"β Failed to create space: {response.text}") | |
| return False | |
| def prepare_files(self): | |
| """Prepare files for deployment""" | |
| print("π Preparing files...") | |
| # Create README.md with proper header | |
| readme_header = f'''--- | |
| title: {self.space_name.replace('-', ' ').title()} | |
| emoji: π§ | |
| colorFrom: blue | |
| colorTo: green | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| license: mit | |
| tags: | |
| - email-classification | |
| - naive-bayes | |
| - text-classification | |
| - nlp | |
| - fastapi | |
| --- | |
| ''' | |
| # Read existing README and add header | |
| if os.path.exists('README.md'): | |
| with open('README.md', 'r', encoding='utf-8') as f: | |
| existing_content = f.read() | |
| # Remove existing header if present | |
| if existing_content.startswith('---'): | |
| parts = existing_content.split('---', 2) | |
| if len(parts) > 2: | |
| existing_content = parts[2].strip() | |
| with open('README.md', 'w', encoding='utf-8') as f: | |
| f.write(readme_header + existing_content) | |
| print("β Files prepared") | |
| return True | |
| def clone_and_deploy(self): | |
| """Clone the space repository and deploy""" | |
| print("π Cloning repository...") | |
| # Create temporary directory | |
| temp_dir = f"temp_{self.space_name}" | |
| if os.path.exists(temp_dir): | |
| shutil.rmtree(temp_dir) | |
| try: | |
| # Clone the repository | |
| result = subprocess.run([ | |
| 'git', 'clone', self.git_url, temp_dir | |
| ], capture_output=True, text=True) | |
| if result.returncode != 0: | |
| print(f"β Failed to clone repository: {result.stderr}") | |
| return False | |
| # Copy files to the cloned repository | |
| files_to_copy = [ | |
| 'app.py', | |
| 'requirements.txt', | |
| 'Dockerfile', | |
| 'README.md', | |
| 'train_model.py', | |
| 'Synthetic_Email_Dataset.csv' | |
| ] | |
| for file in files_to_copy: | |
| if os.path.exists(file): | |
| shutil.copy2(file, temp_dir) | |
| print(f"π Copied {file}") | |
| # Change to the repository directory | |
| os.chdir(temp_dir) | |
| # Configure git | |
| subprocess.run(['git', 'config', 'user.email', 'deployer@example.com']) | |
| subprocess.run(['git', 'config', 'user.name', 'Auto Deployer']) | |
| # Add all files | |
| subprocess.run(['git', 'add', '.']) | |
| # Commit changes | |
| commit_message = f"Deploy Email Classifier API - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" | |
| result = subprocess.run(['git', 'commit', '-m', commit_message], | |
| capture_output=True, text=True) | |
| if result.returncode != 0: | |
| print(f"βΉοΈ Nothing to commit or commit failed: {result.stderr}") | |
| # Push changes | |
| if self.token: | |
| # Use token for authentication | |
| auth_url = self.git_url.replace('https://', f'https://oauth2:{self.token}@') | |
| result = subprocess.run(['git', 'push', auth_url, 'main'], | |
| capture_output=True, text=True) | |
| else: | |
| result = subprocess.run(['git', 'push', 'origin', 'main'], | |
| capture_output=True, text=True) | |
| if result.returncode == 0: | |
| print("β Successfully pushed to Hugging Face!") | |
| print(f"π Your API will be available at: {self.space_url}") | |
| return True | |
| else: | |
| print(f"β Failed to push: {result.stderr}") | |
| return False | |
| except Exception as e: | |
| print(f"β Deployment failed: {str(e)}") | |
| return False | |
| finally: | |
| # Clean up | |
| os.chdir('..') | |
| if os.path.exists(temp_dir): | |
| shutil.rmtree(temp_dir) | |
| def test_deployment(self): | |
| """Test the deployed API""" | |
| print("π§ͺ Testing deployed API...") | |
| api_url = f"{self.space_url.replace('/spaces/', '/spaces/')}" | |
| # Wait a bit for deployment | |
| import time | |
| print("β³ Waiting for deployment to complete...") | |
| time.sleep(30) | |
| try: | |
| # Test health endpoint | |
| response = requests.get(f"{api_url}/health", timeout=30) | |
| if response.status_code == 200: | |
| print("β Health check passed") | |
| # Test prediction endpoint | |
| test_response = requests.post( | |
| f"{api_url}/predict", | |
| json={"message": "Please find attached the document."}, | |
| timeout=30 | |
| ) | |
| if test_response.status_code == 200: | |
| result = test_response.json() | |
| print(f"β Prediction test passed: {result['prediction_label']}") | |
| return True | |
| else: | |
| print(f"β Prediction test failed: {test_response.status_code}") | |
| else: | |
| print(f"β Health check failed: {response.status_code}") | |
| except requests.exceptions.RequestException as e: | |
| print(f"β οΈ API not yet accessible (this is normal for new deployments): {str(e)}") | |
| print("π The API may take a few minutes to become available") | |
| return False | |
| def deploy(self): | |
| """Full deployment process""" | |
| print("π Starting deployment to Hugging Face Spaces...") | |
| print(f"π¦ Space: {self.space_url}") | |
| print("="*60) | |
| # Check requirements | |
| if not self.check_requirements(): | |
| return False | |
| # Setup Hugging Face Space | |
| if not self.setup_huggingface_space(): | |
| return False | |
| # Prepare files | |
| if not self.prepare_files(): | |
| return False | |
| # Deploy | |
| if not self.clone_and_deploy(): | |
| return False | |
| print("\n" + "="*60) | |
| print("π DEPLOYMENT COMPLETED!") | |
| print("="*60) | |
| print(f"π§ API URL: {self.space_url}") | |
| print(f"π Documentation: {self.space_url}/docs") | |
| print(f"π Interactive API: {self.space_url}/redoc") | |
| print("\nβ³ Note: It may take a few minutes for the API to become fully available.") | |
| # Test deployment | |
| self.test_deployment() | |
| return True | |
| def main(): | |
| """Main deployment function""" | |
| print("π§ Hugging Face Spaces Deployment Tool") | |
| print("="*40) | |
| # Get user input | |
| username = input("Enter your Hugging Face username: ").strip() | |
| if not username: | |
| print("β Username is required") | |
| sys.exit(1) | |
| space_name = input("Enter space name (e.g., email-classifier-api): ").strip() | |
| if not space_name: | |
| print("β Space name is required") | |
| sys.exit(1) | |
| token = input("Enter your Hugging Face token (optional, press Enter to skip): ").strip() | |
| if not token: | |
| print("βΉοΈ No token provided. You'll need to create the space manually.") | |
| # Create deployer and deploy | |
| deployer = HuggingFaceDeployer(username, space_name, token) | |
| if deployer.deploy(): | |
| print("\nβ Deployment successful!") | |
| print("\nπ Next steps:") | |
| print("1. Wait 2-3 minutes for the build to complete") | |
| print("2. Check the build logs in your Hugging Face Space") | |
| print("3. Test your API endpoints") | |
| print("4. Share your API with others!") | |
| else: | |
| print("\nβ Deployment failed!") | |
| print("\nπ§ Troubleshooting:") | |
| print("1. Check that all required files are present") | |
| print("2. Verify your Hugging Face credentials") | |
| print("3. Ensure git is installed and configured") | |
| print("4. Check the build logs in your Hugging Face Space") | |
| if __name__ == "__main__": | |
| main() |