#!/bin/bash # Landing Page Cookbook Test Script # Tests the full agent-driven landing page flow from cookbooks/landing-page.md # # Usage: # ./cookbooks/scripts/landing-test.sh run [name] # Run the full flow # ./cookbooks/scripts/landing-test.sh teardown [name] # Clean up test resources # ./cookbooks/scripts/landing-test.sh status [name] # Check current status set -euo pipefail # Configuration API_URL="${RDEV_API_URL:-https://rdev.masq-ops.orchard9.ai}" API_KEY="${RDEV_API_KEY:?RDEV_API_KEY environment variable required}" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[OK]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Timeouts BUILD_TIMEOUT=180 # 3 minutes for Claude to build the site BUILD_POLL_INTERVAL=5 # Check every 5 seconds PIPELINE_TIMEOUT=300 # 5 minutes max wait for CI pipeline PIPELINE_POLL_INTERVAL=10 SITE_TIMEOUT=60 # 1 minute max wait for site to be live api_call() { local method="$1" local endpoint="$2" local data="${3:-}" if [[ -n "$data" ]]; then curl -s -X "$method" "${API_URL}${endpoint}" \ -H "X-API-Key: ${API_KEY}" \ -H "Content-Type: application/json" \ -d "$data" else curl -s -X "$method" "${API_URL}${endpoint}" \ -H "X-API-Key: ${API_KEY}" fi } check_health() { log_info "Checking API health..." local response response=$(curl -s "${API_URL}/health") if echo "$response" | jq -e '.data.status == "ok"' > /dev/null 2>&1; then log_success "API is healthy" return 0 else log_error "API health check failed" echo "$response" | jq . return 1 fi } # Wait for build to complete (Claude building the site) # Returns: 0 on success, 1 on failure/timeout wait_for_build() { local task_id="$1" local start_time=$(date +%s) log_info "Waiting for Claude to build the site (timeout: ${BUILD_TIMEOUT}s)..." while true; do local elapsed=$(($(date +%s) - start_time)) if [[ $elapsed -ge $BUILD_TIMEOUT ]]; then log_error "Build timeout after ${BUILD_TIMEOUT}s" return 1 fi local response response=$(api_call GET "/builds/$task_id" 2>/dev/null || echo "{}") local status status=$(echo "$response" | jq -r '.data.status // "unknown"' 2>/dev/null) case "$status" in completed) local success success=$(echo "$response" | jq -r '.data.result.success // false') if [[ "$success" == "true" ]]; then log_success "Build completed successfully (${elapsed}s)" echo "$response" | jq '.data.result | {success, commit_sha, files_changed, duration_ms}' return 0 else log_error "Build completed but failed" echo "$response" | jq '.data.result' return 1 fi ;; failed) log_error "Build failed" echo "$response" | jq '.data.result // .data' return 1 ;; running) echo -ne "\r${BLUE}[INFO]${NC} Build status: running (${elapsed}s)... " ;; pending) echo -ne "\r${BLUE}[INFO]${NC} Build status: pending (${elapsed}s)... " ;; *) echo -ne "\r${BLUE}[INFO]${NC} Build status: $status (${elapsed}s)... " ;; esac sleep $BUILD_POLL_INTERVAL done } # Wait for pipeline to appear and complete # Returns: 0 on success, 1 on failure/timeout wait_for_pipeline() { local project_name="$1" local start_time=$(date +%s) local pipeline_found=false local pipeline_number="" local pipeline_status="" log_info "Waiting for CI pipeline to start (timeout: ${PIPELINE_TIMEOUT}s)..." while true; do local elapsed=$(($(date +%s) - start_time)) if [[ $elapsed -ge $PIPELINE_TIMEOUT ]]; then log_error "Pipeline timeout after ${PIPELINE_TIMEOUT}s" return 1 fi local response response=$(api_call GET "/projects/$project_name/pipelines" 2>/dev/null || echo "{}") # Check if we have pipelines (API returns array at .data) local pipeline_count pipeline_count=$(echo "$response" | jq -r '.data | length' 2>/dev/null || echo "0") if [[ "$pipeline_count" -gt 0 ]]; then if [[ "$pipeline_found" == "false" ]]; then pipeline_found=true pipeline_number=$(echo "$response" | jq -r '.data[0].number') log_success "Pipeline #$pipeline_number started" fi # Get latest pipeline status pipeline_status=$(echo "$response" | jq -r '.data[0].status') case "$pipeline_status" in success) log_success "Pipeline #$pipeline_number completed successfully (${elapsed}s)" return 0 ;; failure|error|killed|declined) log_error "Pipeline #$pipeline_number failed with status: $pipeline_status" echo "$response" | jq '.data[0]' return 1 ;; running|pending) echo -ne "\r${BLUE}[INFO]${NC} Pipeline #$pipeline_number status: $pipeline_status (${elapsed}s)... " ;; *) echo -ne "\r${BLUE}[INFO]${NC} Pipeline #$pipeline_number status: $pipeline_status (${elapsed}s)... " ;; esac else echo -ne "\r${BLUE}[INFO]${NC} Waiting for pipeline to start (${elapsed}s)... " fi sleep $PIPELINE_POLL_INTERVAL done } # Wait for site to be accessible # Returns: 0 on success, 1 on failure/timeout wait_for_site() { local domain="$1" local start_time=$(date +%s) log_info "Waiting for site to be live: https://$domain (timeout: ${SITE_TIMEOUT}s)..." while true; do local elapsed=$(($(date +%s) - start_time)) if [[ $elapsed -ge $SITE_TIMEOUT ]]; then log_warn "Site timeout after ${SITE_TIMEOUT}s - may still be deploying" return 1 fi local http_code http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "https://$domain" 2>/dev/null || echo "000") if [[ "$http_code" == "200" ]]; then echo "" log_success "Site is live! HTTP $http_code (${elapsed}s)" return 0 elif [[ "$http_code" == "000" ]]; then echo -ne "\r${BLUE}[INFO]${NC} Waiting for site... (${elapsed}s, connection failed) " else echo -ne "\r${BLUE}[INFO]${NC} Waiting for site... (${elapsed}s, HTTP $http_code) " fi sleep 3 done } # Test adding a DNS alias test_dns_alias() { local project_name="$1" local alias_domain="$2" log_info "Testing DNS alias: $alias_domain" local response response=$(api_call POST "/projects/$project_name/domains" "{\"domain\": \"$alias_domain\"}") if echo "$response" | jq -e '.error' > /dev/null 2>&1; then local error_code error_code=$(echo "$response" | jq -r '.error.code // "UNKNOWN"') if [[ "$error_code" == "DOMAIN_EXISTS" ]]; then log_warn "Domain alias already exists: $alias_domain" return 0 fi log_error "Failed to add DNS alias" echo "$response" | jq . return 1 fi log_success "DNS alias added: $alias_domain" echo "$response" | jq '.data | {domain, type, record_type}' return 0 } # Remove a DNS alias remove_dns_alias() { local project_name="$1" local alias_domain="$2" log_info "Removing DNS alias: $alias_domain" local response response=$(api_call DELETE "/projects/$project_name/domains/$alias_domain") if echo "$response" | jq -e '.error' > /dev/null 2>&1; then local error_code error_code=$(echo "$response" | jq -r '.error.code // "UNKNOWN"') if [[ "$error_code" == "NOT_FOUND" ]]; then log_warn "Domain alias not found (already deleted?): $alias_domain" return 0 fi log_warn "Failed to remove DNS alias: $alias_domain" return 1 fi log_success "DNS alias removed: $alias_domain" return 0 } run_flow() { local project_name="${1:-landing-test}" # Default prompt for building a landing page local build_prompt="Build a modern landing page with: dark gradient background (#1a1a2e to #16213e), centered hero section with company name 'Acme Corp' and tagline 'Building the future', email signup form with a submit button, responsive design for mobile. Use vanilla HTML/CSS/JS. Create index.html, styles.css, and a Dockerfile that serves with nginx on port 80." echo "" echo "==========================================" echo " Landing Page Cookbook Test" echo " Project: $project_name" echo " Flow: Agent-driven (create-and-build)" echo "==========================================" echo "" # Step 0: Health check check_health || exit 1 echo "" # Step 1: Create project AND enqueue build in one call log_info "Step 1: Creating project and enqueuing build task..." log_info "Prompt: ${build_prompt:0:80}..." local create_payload create_payload=$(jq -n \ --arg name "$project_name" \ --arg desc "Cookbook test: agent-driven landing page" \ --arg prompt "$build_prompt" \ '{ name: $name, description: $desc, prompt: $prompt, auto_commit: true, auto_push: true }') local create_response create_response=$(api_call POST "/project/create-and-build" "$create_payload") if echo "$create_response" | jq -e '.error' > /dev/null 2>&1; then log_error "Failed to create project" echo "$create_response" | jq . exit 1 fi log_success "Project created and build enqueued" echo "$create_response" | jq '.data | { project_id, domain, url, git: .git.html_url, task_id, status, status_url }' # Extract key info local primary_domain local task_id primary_domain=$(echo "$create_response" | jq -r '.data.domain') task_id=$(echo "$create_response" | jq -r '.data.task_id') if [[ -z "$task_id" || "$task_id" == "null" ]]; then log_error "No task_id returned - build was not enqueued" exit 1 fi log_success "Build task ID: $task_id" echo "" # Step 2: Monitor build progress (Claude building the site) log_info "Step 2: Monitoring build progress..." echo "" local build_success=false if wait_for_build "$task_id"; then build_success=true else log_error "Build did not complete successfully" log_info "Check build details: curl -s \"\$RDEV_API_URL/builds/$task_id\" -H \"X-API-Key: \$RDEV_API_KEY\" | jq ." fi echo "" # Step 3: Monitor CI pipeline (only if build succeeded) local pipeline_success=false if [[ "$build_success" == "true" ]]; then log_info "Step 3: Monitoring CI pipeline..." if wait_for_pipeline "$project_name"; then pipeline_success=true else log_warn "Pipeline did not complete successfully" log_info "Check Woodpecker: https://ci.threesix.ai/threesix/$project_name" fi else log_info "Step 3: Skipping pipeline monitoring (build failed)" fi echo "" # Step 4: Verify site is live local site_live=false if [[ "$pipeline_success" == "true" ]]; then log_info "Step 4: Verifying site is accessible..." if wait_for_site "$primary_domain"; then site_live=true # Show a snippet of the response log_info "Fetching site content preview..." curl -s "https://$primary_domain" | head -20 | grep -E '