Last updated: Sep 11, 2025, 03:42 PM UTC

Claude Code Deployment Automation Guide

Generated: 2025-08-12 UTC
Purpose: Enable Claude Code to perform Sliplane deployments without user prompts

Overview

This guide documents how to configure Claude Code for automated Sliplane deployments, based on lessons learned from the sasha-main deployment.

Claude Code Permission Configuration

Project Settings File

Create .claude/settings.json in your project root:

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      // Navigation
      "Bash(cd /Users/lindsaysmith/Documents/lambda1.nosync/sasha/client-management*)",
      
      // Deployment scripts
      "Bash(./scripts/test-api.sh*)",
      "Bash(./scripts/check-status.sh*)",
      "Bash(./scripts/deploy-with-api.sh*)",
      "Bash(./scripts/update-config.sh*)",
      
      // API calls
      "Bash(curl*https://ctrl.sliplane.io*)",
      "Bash(curl*https://linzoid-sasha-studio.sliplane.app*)",
      "Bash(curl*https://api.sliplane.io*)",
      
      // Utility commands
      "Bash(echo*)",
      "Bash(cat*)",
      "Bash(tee*)",
      "Bash(jq*)",
      "Bash(grep*)",
      "Bash(source .env*)",
      "Bash(bash -c*)",
      "Bash(time*)",
      "Bash(sleep*)",
      
      // File operations
      "Edit(*/client-management/clients/*/config.json)",
      "Edit(*/client-management/lib/sliplane-api.sh)",
      "Write(*/client-management/clients/*/config.json)",
      "Write(/tmp/*.log)",
      "Write(/tmp/*.json)",
      
      // Documentation
      "Write(*/docs/deployment/*.md)",
      "Write(*/docs/guides/*.md)"
    ]
  }
}

Benefits of This Configuration

  1. No prompting for deployment operations
  2. Full logging capabilities to /tmp
  3. Configuration editing without approval
  4. API debugging tools available
  5. Documentation creation automated

Critical Script Fixes Required

1. Fix API Endpoints in lib/sliplane-api.sh

All service operations must use project path:

# ❌ Wrong
api_call POST "services" "$payload"
api_call PATCH "services/$service_id" "$data"

# βœ… Correct
api_call POST "projects/$SLIPLANE_PROJECT_ID/services" "$payload"
api_call PATCH "projects/$SLIPLANE_PROJECT_ID/services/$service_id" "$data"

2. Fix Deployment Payload Format

# ❌ Wrong format
"deployment": {
    "type": "image",
    "image": "$image"
}

# βœ… Correct format
"deployment": {
    "url": "docker.io/$image"
}

3. Add Network Configuration for Public Services

# Required for public access
"network": {
    "public": true,
    "protocol": "http"
}

4. Include Deployment Field in PATCH Operations

# All PATCH operations need deployment field
api_call PATCH "projects/$SLIPLANE_PROJECT_ID/services/$service_id" \
  "{\"environmentVariables\": $env_json, \"deployment\": {\"url\": \"docker.io/linzoid/sasha-studio:latest\"}}"

Improved Deployment Process

Pre-Deployment Checklist

#!/bin/bash
# Add to deploy-with-api.sh

pre_deployment_check() {
    echo "β†’ Pre-deployment validation..."
    
    # Check Docker Hub connectivity
    if ! curl -s https://hub.docker.com/v2/repositories/linzoid/sasha-studio/tags | grep -q "1.0.22"; then
        echo "❌ Docker image not found in registry"
        return 1
    fi
    
    # Verify API credentials
    if ! validate_env; then
        return 1
    fi
    
    # Check if ANTHROPIC_API_KEY is in config
    if ! jq -e '.environment.ANTHROPIC_API_KEY' "$CONFIG" > /dev/null; then
        echo "⚠️ Warning: ANTHROPIC_API_KEY not configured"
    fi
    
    echo "βœ… Pre-deployment checks passed"
    return 0
}

Enhanced Error Handling

# Add to api_call function
api_call() {
    local method=$1
    local endpoint=$2
    local data=$3
    
    echo "[DEBUG] $method $API_BASE/$endpoint" >&2
    
    local response
    response=$(curl -s -X "$method" "$API_BASE/$endpoint" \
        -H "Authorization: Bearer $SLIPLANE_API_TOKEN" \
        -H "X-Organization-ID: $SLIPLANE_ORG_ID" \
        -H "Content-Type: application/json" \
        ${data:+-d "$data"} \
        -w "\n%{http_code}" \
        --connect-timeout 10 \
        --max-time 30)
    
    local http_code=$(echo "$response" | tail -n1)
    local body=$(echo "$response" | sed '$d')
    
    # Log all responses for debugging
    echo "[RESPONSE] HTTP $http_code" >&2
    [ -n "$body" ] && echo "[BODY] $body" >&2
    
    case "$http_code" in
        200|201|202) 
            echo "$body"
            return 0
            ;;
        *)
            echo "❌ API request failed (HTTP $http_code)" >&2
            [ -n "$body" ] && echo "   $body" >&2
            return 1
            ;;
    esac
}

Deployment Verification

# Add after deployment
verify_deployment() {
    local service_id=$1
    
    echo "β†’ Verifying deployment..."
    
    # Check service configuration
    local service=$(api_call GET "projects/$SLIPLANE_PROJECT_ID/services" | \
        jq ".[] | select(.id==\"$service_id\")")
    
    # Verify environment variables
    local env_count=$(echo "$service" | jq '.environmentVariables | length')
    if [ "$env_count" -eq 0 ]; then
        echo "⚠️ No environment variables configured"
    else
        echo "βœ… $env_count environment variables set"
    fi
    
    # Verify volumes
    local volume_count=$(echo "$service" | jq '.volumes | length')
    if [ "$volume_count" -eq 0 ]; then
        echo "⚠️ No volumes configured"
    else
        echo "βœ… $volume_count volumes mounted"
    fi
    
    # Check public access
    local is_public=$(echo "$service" | jq -r '.network.public')
    if [ "$is_public" = "true" ]; then
        echo "βœ… Service is publicly accessible"
    else
        echo "⚠️ Service is not public"
    fi
    
    # Test URL accessibility
    local url=$(echo "$service" | jq -r '.network.managedDomain')
    if [ -n "$url" ] && [ "$url" != "null" ]; then
        local status=$(curl -s -o /dev/null -w "%{http_code}" "https://$url" || echo "000")
        echo "   URL: https://$url (HTTP $status)"
    fi
}

Automation Best Practices

1. Comprehensive Logging

# Start every deployment with logging
LOG_FILE="/tmp/sasha-deploy-$(date +%Y%m%d-%H%M%S).log"
exec 2>&1 | tee -a "$LOG_FILE"

echo "=== Deployment Log ===" 
echo "Started: $(date)"
echo "Client: $CLIENT"
echo "Image: $DOCKER_IMAGE"

2. Idempotent Operations

# Check before creating
SERVICE=$(get_service "$SERVICE_NAME")
if [ -n "$SERVICE" ]; then
    echo "Service already exists, updating..."
    SERVICE_ID=$(echo "$SERVICE" | jq -r '.id')
    # Update existing service
else
    echo "Creating new service..."
    # Create new service
fi

3. Retry Logic

# Add retry capability
retry_command() {
    local max_attempts=3
    local attempt=1
    
    while [ $attempt -le $max_attempts ]; do
        if "$@"; then
            return 0
        fi
        
        echo "Attempt $attempt failed, retrying..."
        attempt=$((attempt + 1))
        sleep 2
    done
    
    return 1
}

# Usage
retry_command create_service "$SERVICE_NAME" "$DOCKER_IMAGE"

Common Issues and Solutions

Issue 1: Silent Failures

Problem: Functions return empty without error indication
Solution: Add explicit error checking and logging

Issue 2: API Endpoint Confusion

Problem: Inconsistent endpoint paths
Solution: Always use project-scoped endpoints

Issue 3: Missing Required Fields

Problem: API returns "invalid_request" without details
Solution: Check existing services for field requirements

Issue 4: Deployment Not Triggered

Problem: Service created but container not running
Solution: Explicit deployment trigger needed (endpoint TBD)

Testing Strategy

1. Unit Test API Functions

# test-api-functions.sh
source lib/sliplane-api.sh

# Test service creation (dry-run)
DRY_RUN=true
create_service "test-service" "linzoid/sasha-studio:latest"

# Test environment update
ENV_JSON='[{"key": "TEST", "value": "value", "secret": false}]'
DRY_RUN=true
update_service_env "test-id" "$ENV_JSON"

2. Integration Test Full Deployment

# test-deployment.sh
./scripts/deploy-with-api.sh test-client --dry-run

3. Verification Test

# verify-deployment.sh
./scripts/check-status.sh sasha-main
curl -s https://linzoid-sasha-studio.sliplane.app/api/health | jq '.'

Monitoring and Alerting

Health Check Script

#!/bin/bash
# monitor-deployment.sh

check_health() {
    local url=$1
    local response=$(curl -s "$url/api/health" || echo "{}")
    local status=$(echo "$response" | jq -r '.status' || echo "unknown")
    
    case "$status" in
        healthy)
            echo "βœ… Service healthy"
            return 0
            ;;
        degraded)
            echo "⚠️ Service degraded"
            return 1
            ;;
        *)
            echo "❌ Service unhealthy or unreachable"
            return 2
            ;;
    esac
}

# Monitor every 60 seconds
while true; do
    check_health "https://linzoid-sasha-studio.sliplane.app"
    sleep 60
done

Future Enhancements

  1. Automated rollback on deployment failure
  2. Blue-green deployments for zero downtime
  3. Deployment webhooks for notifications
  4. Performance metrics collection
  5. Automated testing before deployment

Conclusion

With proper Claude Code permissions and script improvements, Sliplane deployments can be fully automated. The key requirements are:

  1. Comprehensive permission configuration
  2. Fixed API endpoints and payload formats
  3. Proper error handling and logging
  4. Deployment verification steps
  5. Deployment trigger mechanism (needs investigation)

Following this guide will enable Claude Code to perform deployments autonomously while maintaining visibility through comprehensive logging and verification.