#!/bin/bash # HuggingFace deployment script for PuppyCompanion FastAPI # Usage: ./deploy_hf.sh [--space-name SPACE_NAME] set -e # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${GREEN}[DEPLOY]${NC} $1" } warn() { echo -e "${YELLOW}[WARN]${NC} $1" } error() { echo -e "${RED}[ERROR]${NC} $1" } info() { echo -e "${BLUE}[INFO]${NC} $1" } # Default configuration SPACE_NAME="puppycompanion-v3" APP_DIR="." HF_USERNAME="JTh34" # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --space-name) SPACE_NAME="$2" shift 2 ;; *) error "Unknown option: $1" exit 1 ;; esac done # Ask for space name if not provided (with default) if [ -z "$SPACE_NAME" ]; then read -p "HuggingFace space name [puppycompanion-v3]: " SPACE_NAME SPACE_NAME=${SPACE_NAME:-puppycompanion-v3} fi if [ -z "$SPACE_NAME" ]; then error "Space name required" exit 1 fi # Check that the app directory exists if [ ! -d "$APP_DIR" ]; then error "Application directory not found: $APP_DIR" exit 1 fi cd "$APP_DIR" log "Preparing deployment for FastAPI space: $HF_USERNAME/$SPACE_NAME" # Pre-deployment checks log "Pre-deployment checks..." # Check required files for FastAPI application REQUIRED_FILES=( "main.py" "Dockerfile" "README.MD" "all_books_preprocessed_chunks.json" "rag_system.py" "agent_workflow.py" "embedding_models.py" "books_config.json" "static/index.html" ) for file in "${REQUIRED_FILES[@]}"; do if [ ! -f "$file" ]; then error "Missing required file: $file" exit 1 fi done log "✅ All required files found" # Check that API keys are not in files ENV_FILES=(".env" ".env.local" ".env.prod" ".env.development" ".env.staging") for env_file in "${ENV_FILES[@]}"; do if [ -f "$env_file" ]; then if grep -q "sk-\|OPENAI_API_KEY\|TAVILY_API_KEY" "$env_file" 2>/dev/null; then warn "API keys detected in $env_file - file will be excluded from deployment" warn "Make sure to configure your secrets on HuggingFace Spaces" fi fi done # Check for PDF files that should not be deployed if ls data/*.pdf >/dev/null 2>&1; then warn "PDF files detected in data/ directory" warn "These will be excluded from deployment for security" fi # Validate chunks file size if [ -f "all_books_preprocessed_chunks.json" ]; then FILE_SIZE=$(stat -f%z "all_books_preprocessed_chunks.json" 2>/dev/null || stat -c%s "all_books_preprocessed_chunks.json" 2>/dev/null) FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024)) log "Chunks file size: ${FILE_SIZE_MB}MB" if [ $FILE_SIZE_MB -gt 100 ]; then warn "Large chunks file detected (${FILE_SIZE_MB}MB)" warn "This may cause slower deployment and startup times" fi fi # Create requirements.txt from pyproject.toml if needed and requirements.txt doesn't exist if [ ! -f "requirements.txt" ] && [ -f "pyproject.toml" ]; then log "Generating requirements.txt from pyproject.toml..." if command -v pip-compile &> /dev/null; then pip-compile pyproject.toml else warn "pip-compile not found. Basic requirements.txt generation..." # Basic extraction of dependencies from pyproject.toml if command -v python &> /dev/null; then python -c " import tomllib with open('pyproject.toml', 'rb') as f: data = tomllib.load(f) deps = data.get('project', {}).get('dependencies', []) with open('requirements.txt', 'w') as f: for dep in deps: f.write(dep + '\n') " 2>/dev/null || { warn "Could not generate requirements.txt automatically" warn "Please create the requirements.txt file manually" exit 1 } fi fi elif [ -f "requirements.txt" ]; then log "✅ requirements.txt found" fi # Validate Dockerfile for FastAPI if grep -q "main.py" Dockerfile && grep -q "7860" Dockerfile; then log "✅ Dockerfile configured for FastAPI" else warn "Dockerfile may not be properly configured for FastAPI deployment" fi # Check SSH connection to HuggingFace log "Checking HuggingFace SSH connection..." if ! ssh -T git@hf.co -o ConnectTimeout=10 -o BatchMode=yes 2>/dev/null; then warn "SSH connection to HuggingFace failed" info "Check your SSH configuration or use: ssh-keygen -t ed25519 -C 'your_email@example.com'" info "Then add the public key to your HuggingFace profile" # Fallback to HTTPS if available if command -v huggingface-cli &> /dev/null && huggingface-cli whoami &> /dev/null; then warn "Using HTTPS as fallback..." USE_SSH=false else error "No authentication method available" exit 1 fi else log "✅ HuggingFace SSH connection OK" USE_SSH=true fi # Create the space if it does not exist (requires huggingface-cli) if command -v huggingface-cli &> /dev/null; then log "Creating/updating HuggingFace space..." huggingface-cli repo create "$SPACE_NAME" --type space --space_sdk docker 2>/dev/null || true else warn "huggingface-cli not available, make sure the space exists" fi # Clone or update the repo TEMP_DIR="/tmp/hf_deploy_$$" log "Cloning repository..." if [ "$USE_SSH" = true ]; then # Use SSH git clone "git@hf.co:spaces/$HF_USERNAME/$SPACE_NAME" "$TEMP_DIR" || { error "SSH clone failed. Check username and space name." exit 1 } else # Use HTTPS git clone "https://huggingface.co/spaces/$HF_USERNAME/$SPACE_NAME" "$TEMP_DIR" || { error "HTTPS clone failed. Check username and space name." exit 1 } fi # Copy files with FastAPI-specific exclusions log "Copying FastAPI application files..." rsync -av \ --exclude='.git' \ --exclude='venv_*' \ --exclude='.env*' \ --exclude='__pycache__' \ --exclude='.chainlit' \ --exclude='*.pdf' \ --exclude='*.PDF' \ --exclude='data/*.pdf' \ --exclude='data/*.PDF' \ --exclude='.DS_Store' \ --exclude='*.log' \ --exclude='qdrant_storage' \ --exclude='deploy_hf.sh' \ --exclude='.gitattributes' \ --exclude='uv.lock' \ --exclude='document_loader_preproc.py' \ ./ "$TEMP_DIR/" # Go to temp directory cd "$TEMP_DIR" # Configure Git if needed git config user.email "action@github.com" || true git config user.name "Deploy Script" || true # Verify critical files are present log "Verifying deployment files..." CRITICAL_FILES=("main.py" "Dockerfile" "all_books_preprocessed_chunks.json") for file in "${CRITICAL_FILES[@]}"; do if [ ! -f "$file" ]; then error "Critical file missing after copy: $file" exit 1 fi done log "✅ All critical files verified" # Add and commit log "Committing changes..." git add . git commit -m "Deploy PuppyCompanion FastAPI $(date '+%Y-%m-%d %H:%M:%S')" || { warn "No changes detected" } # Push to HuggingFace log "Pushing to HuggingFace..." git push # Clean up cd - > /dev/null rm -rf "$TEMP_DIR" log "🚀 FastAPI deployment completed successfully!" info "Your app will be available at: https://huggingface.co/spaces/$HF_USERNAME/$SPACE_NAME" info "Deployment may take a few minutes to build and start..." info "Check the logs on HuggingFace Spaces for any issues"