#!/bin/bash # Claude Code Stack Deployment Script for TrueNAS (SSH Method) # This script automates the deployment via SSH set -e # Color output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Configuration TRUENAS_IP="${1:-}" TRUENAS_USER="${2:-root}" POOL_NAME="${3:-tank}" PROJECT_DIR="/mnt/${POOL_NAME}/docker/claude-code-stack" DOCKER_COMPOSE_VERSION="3.8" # Functions print_info() { echo -e "${GREEN}[INFO]${NC} $1" } print_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } check_prerequisites() { print_info "Checking prerequisites..." # Check if ssh is available if ! command -v ssh &> /dev/null; then print_error "SSH client is not installed" exit 1 fi # Check if ANTHROPIC_API_KEY is set if [ -z "$ANTHROPIC_API_KEY" ]; then print_warn "ANTHROPIC_API_KEY environment variable not set" read -p "Enter your Anthropic API Key: " -r ANTHROPIC_API_KEY export ANTHROPIC_API_KEY fi print_info "Prerequisites check passed" } validate_input() { if [ -z "$TRUENAS_IP" ]; then print_error "Usage: $0 [TRUENAS_USER] [POOL_NAME]" print_info "Example: $0 192.168.1.100 root tank" exit 1 fi } test_ssh_connection() { print_info "Testing SSH connection to $TRUENAS_USER@$TRUENAS_IP..." if ssh -o ConnectTimeout=5 "$TRUENAS_USER@$TRUENAS_IP" "echo 'SSH connection successful'" > /dev/null 2>&1; then print_info "SSH connection successful" else print_error "Failed to connect to TrueNAS via SSH" print_info "Please verify:" print_info " - TRUENAS_IP is correct: $TRUENAS_IP" print_info " - SSH is enabled on TrueNAS" print_info " - You have the correct credentials" exit 1 fi } create_project_directory() { print_info "Creating project directory on TrueNAS..." ssh "$TRUENAS_USER@$TRUENAS_IP" << 'EOF' POOL_NAME="$1" PROJECT_DIR="/mnt/${POOL_NAME}/docker/claude-code-stack" # Create directory mkdir -p "$PROJECT_DIR" chmod 755 "$PROJECT_DIR" # Verify if [ -d "$PROJECT_DIR" ]; then echo "Directory created: $PROJECT_DIR" else echo "Failed to create directory" exit 1 fi EOF } deploy_docker_files() { print_info "Uploading Docker configuration files..." # Check if files exist locally local files=( "docker-compose.yml" "claude-code-stack.env" "nginx.conf" ) for file in "${files[@]}"; do if [ ! -f "./$file" ]; then print_warn "Local file not found: $file" print_info "Creating minimal $file on remote..." case $file in "docker-compose.yml") # Create minimal compose file on remote ssh "$TRUENAS_USER@$TRUENAS_IP" "cat > ${PROJECT_DIR}/docker-compose.yml << 'COMPOSE_EOF' version: '3.8' services: agents-ui: image: node:20-alpine container_name: claude-agents-ui restart: unless-stopped ports: - \"3000:3000\" environment: ANTHROPIC_API_KEY: \${ANTHROPIC_API_KEY} volumes: - claude-config:/root/.claude - workspace:/workspace networks: - claude-stack volumes: claude-config: driver: local workspace: driver: local networks: claude-stack: driver: bridge COMPOSE_EOF" ;; "claude-code-stack.env") ssh "$TRUENAS_USER@$TRUENAS_IP" "cat > ${PROJECT_DIR}/.env << 'ENV_EOF' ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} POSTGRES_PASSWORD=changeMe123! SESSION_SECRET=$(openssl rand -base64 32) NODE_ENV=production ENV_EOF" ;; "nginx.conf") ssh "$TRUENAS_USER@$TRUENAS_IP" "touch ${PROJECT_DIR}/nginx.conf" ;; esac else # Upload file scp "./$file" "$TRUENAS_USER@$TRUENAS_IP:$PROJECT_DIR/" print_info "Uploaded: $file" fi done } configure_environment() { print_info "Configuring environment variables..." ssh "$TRUENAS_USER@$TRUENAS_IP" << EOF PROJECT_DIR="$PROJECT_DIR" # Create .env file if it doesn't exist if [ ! -f "$PROJECT_DIR/.env" ]; then cat > "$PROJECT_DIR/.env" << 'ENV_EOF' ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY POSTGRES_PASSWORD=$(openssl rand -base64 16) SESSION_SECRET=$(openssl rand -base64 32) NODE_ENV=production CLAUDE_MODEL=claude-opus-4-1 RUN_CLAUDE_CODE=true LOG_LEVEL=info ENV_EOF print_info "Created .env file" else # Update ANTHROPIC_API_KEY in existing .env sed -i.bak "s/ANTHROPIC_API_KEY=.*/ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY/" "$PROJECT_DIR/.env" print_info "Updated ANTHROPIC_API_KEY in .env" fi chmod 600 "$PROJECT_DIR/.env" EOF } deploy_stack() { print_info "Deploying Docker stack..." ssh "$TRUENAS_USER@$TRUENAS_IP" << EOF cd $PROJECT_DIR # Pull latest images echo "Pulling Docker images..." docker-compose pull # Build (if Dockerfile present) if [ -f "Dockerfile" ]; then echo "Building custom image..." docker-compose build --no-cache fi # Deploy echo "Starting services..." docker-compose up -d # Wait for services to be ready echo "Waiting for services to be ready..." sleep 10 # Show status docker-compose ps EOF } verify_deployment() { print_info "Verifying deployment..." # Give services time to stabilize sleep 5 print_info "Checking service status..." ssh "$TRUENAS_USER@$TRUENAS_IP" "cd $PROJECT_DIR && docker-compose ps" print_info "Testing Agents UI..." if curl -s -f "http://$TRUENAS_IP:3000" > /dev/null 2>&1; then print_info "✓ Agents UI is responsive" else print_warn "! Agents UI is not yet responding (may take a few moments)" fi } setup_monitoring() { print_info "Setting up basic monitoring..." ssh "$TRUENAS_USER@$TRUENAS_IP" << EOF PROJECT_DIR="$PROJECT_DIR" # Create monitoring script cat > "$PROJECT_DIR/monitor.sh" << 'MONITOR_EOF' #!/bin/bash while true; do clear echo "=== Claude Code Stack Status ===" echo "Time: \$(date)" echo "" docker-compose ps echo "" echo "Resource Usage:" docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" echo "" echo "Press Ctrl+C to exit" sleep 10 done MONITOR_EOF chmod +x "$PROJECT_DIR/monitor.sh" print_info "Monitoring script created at: $PROJECT_DIR/monitor.sh" EOF } print_summary() { echo "" echo -e "${GREEN}========================================${NC}" echo -e "${GREEN} Claude Code Stack Deployed!${NC}" echo -e "${GREEN}========================================${NC}" echo "" echo "Access the Agents UI at:" echo -e " ${YELLOW}http://$TRUENAS_IP:3000${NC}" echo "" echo "Project Directory on TrueNAS:" echo -e " ${YELLOW}$PROJECT_DIR${NC}" echo "" echo "Useful commands:" echo " SSH into TrueNAS:" echo -e " ${YELLOW}ssh $TRUENAS_USER@$TRUENAS_IP${NC}" echo "" echo " View logs:" echo -e " ${YELLOW}cd $PROJECT_DIR && docker-compose logs -f${NC}" echo "" echo " Stop services:" echo -e " ${YELLOW}cd $PROJECT_DIR && docker-compose down${NC}" echo "" echo " Monitor:" echo -e " ${YELLOW}cd $PROJECT_DIR && ./monitor.sh${NC}" echo "" echo "Next steps:" echo " 1. Configure your API keys in the UI or .env" echo " 2. Set up agents via the web interface" echo " 3. Test with a simple agent execution" echo "" } # Main execution main() { echo -e "${GREEN}Claude Code Stack - TrueNAS Deployment${NC}" echo "========================================" echo "" validate_input check_prerequisites test_ssh_connection create_project_directory deploy_docker_files configure_environment deploy_stack verify_deployment setup_monitoring print_summary } # Run main function main "$@"