rdev/cookbooks/scripts/aeries-daeya-test.sh
jordan 32d50a6952
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat: make infra provisioning idempotent + aeries-daeya public discovery feed
- Make postgres and redis provisioning idempotent: return success when already
  provisioned with credentials stored, allowing cookbook trees to safely include
  explicit add-db/add-redis steps alongside auto-provisioned project creation
- Update tests to reflect new idempotent behavior
- Consolidate docs CI into single multi-stage Docker build (remove separate
  build-docs step; Dockerfile.nginx now builds Slate then serves with nginx)
- Delete redundant skeleton docs/Dockerfile (replaced by multi-stage nginx image)
- Add watch verb to woodpecker-deployer RBAC (required by kubectl rollout status)
- Aeries Daeya cookbook: add public discovery feed (/) + character profiles (/c/:handle),
  characters.published/handle/tagline fields, dark pink design system, /studio/* routes,
  verify-public-discovery + verify-otp-endpoint smoke test steps
- Fix Input.tsx: remove non-existent --border-hover CSS variable hover effect
2026-02-28 17:32:21 -07:00

173 lines
6.9 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
# Aeries Daeya E2E Test Script
# Privacy-first avatar social platform: character creation, AI styling from photos,
# mutation explorer, album management.
#
# Usage: ./cookbooks/scripts/aeries-daeya-test.sh <command> <project-name>
# Commands: run, status, diagnose, teardown
#
# Examples:
# ./cookbooks/scripts/aeries-daeya-test.sh run my-daeya
# ./cookbooks/scripts/aeries-daeya-test.sh run my-daeya --auto-teardown
# AUTO_TEARDOWN=true ./cookbooks/scripts/aeries-daeya-test.sh run my-daeya
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
# Parse --auto-teardown flag from args
ARGS=("$@")
for i in "${!ARGS[@]}"; do
if [[ "${ARGS[$i]}" == "--auto-teardown" ]]; then
AUTO_TEARDOWN="true"
unset 'ARGS[$i]'
fi
done
ARGS=("${ARGS[@]}")
COMMAND="${ARGS[0]:-}"
PROJECT_NAME="${ARGS[1]:-}"
register_cleanup_trap
if [[ -z "$COMMAND" || -z "$PROJECT_NAME" ]]; then
echo "Usage: $0 <command> <project-name>"
echo "Commands:"
echo " run - Deploy full aeries-daeya platform via tree runner"
echo " status - Check project and component status"
echo " diagnose - Deep diagnostic of pipeline and site issues"
echo " teardown - Delete the project and all resources"
exit 1
fi
run_flow() {
print_header "Aeries Daeya: Privacy-First Avatar Platform"
echo "Running tree: aeries-daeya for project: $PROJECT_NAME"
echo ""
echo " Phase 1 — Infrastructure (DB + Redis + service + worker + app)"
echo " Phase 2 — Avatar & Look Data Model (characters + public discovery endpoints)"
echo " Phase 3 — AI Generation Pipeline (portrait gen, outfit styling, mutation explorer)"
echo " Phase 4 — Studio UI (public feed + character profiles + studio + dark pink theme)"
echo ""
"$SCRIPT_DIR/tree-runner.sh" run aeries-daeya \
--project-name "$PROJECT_NAME" \
$([[ "$AUTO_TEARDOWN" == "true" ]] && echo "--auto-teardown")
DOMAIN=$(api_call GET "/project/$PROJECT_NAME" | jq -r '.data.domain // empty')
if [[ -n "$DOMAIN" ]]; then
print_success "Aeries Daeya is live at https://$DOMAIN"
echo ""
echo " Discovery: https://$DOMAIN (public — no login)"
echo " Studio: https://$DOMAIN/studio (requires login)"
echo " API health: https://$DOMAIN/api/daeya-api/health"
echo " Public feed: https://$DOMAIN/api/daeya-api/characters/public"
echo ""
echo " Flow:"
echo " 1. Open https://$DOMAIN → browse published characters (no login needed)"
echo " 2. Click 'Create Your Character' → OTP login → /studio"
echo " 3. Create Character → 4-step wizard (describe, shape, soul, generate)"
echo " 4. In studio: toggle 'Public' → character appears on discovery feed"
echo " 5. Open Character → Mutation Explorer (skin tone, background, lighting, style)"
echo " 6. Add Look → upload a photo of an outfit to style on your character"
fi
}
check_status() {
print_header "Project Status: $PROJECT_NAME"
local project
project=$(api_call GET "/project/$PROJECT_NAME")
local domain
domain=$(echo "$project" | jq -r '.data.domain // "unknown"')
local status
status=$(echo "$project" | jq -r '.data.status // "unknown"')
echo " Domain: $domain"
echo " Status: $status"
echo ""
print_header "Components"
api_call GET "/projects/$PROJECT_NAME/components" | jq -r '.data[] | " \(.name) (\(.type)): \(.status)"' 2>/dev/null || echo " (no components)"
echo ""
print_header "Recent Pipelines"
api_call GET "/projects/$PROJECT_NAME/pipelines" | jq -r '.data[0:3][] | " [\(.status)] \(.number) — \(.created_at)"' 2>/dev/null || echo " (no pipelines)"
echo ""
if [[ "$domain" != "unknown" ]]; then
local health_status
health_status=$(curl -sf "https://$domain/api/daeya-api/health" 2>/dev/null | jq -r '.data.status // "unreachable"' || echo "unreachable")
echo " daeya-api health: $health_status"
fi
}
diagnose() {
print_header "Diagnostic: $PROJECT_NAME"
local domain
domain=$(api_call GET "/project/$PROJECT_NAME" | jq -r '.data.domain // empty')
diagnose_pipeline_failure "$PROJECT_NAME"
if [[ -n "$domain" ]]; then
diagnose_site_failure "$domain" "$PROJECT_NAME"
fi
print_diagnostic_header "Notify / Email Checks"
echo ""
echo " If OTP email never arrives:"
print_fix "Check NOTIFY_API_KEY is injected into the daeya-api pod"
print_cmd "kubectl exec -n projects deploy/$PROJECT_NAME-daeya-api -- env | grep NOTIFY"
echo ""
print_fix "If NOTIFY_API_KEY is empty, notify provisioning failed during project creation"
print_cmd "curl -s -H 'X-API-Key: \$RDEV_API_KEY' \$RDEV_API_URL/projects/$PROJECT_NAME/notify/status | jq"
echo ""
print_fix "Test OTP endpoint directly (200/202/429 = notify working; 404/500 = broken)"
print_cmd "curl -s -o /dev/null -w '%{http_code}' -X POST https://$domain/api/daeya-api/auth/otp/send -H 'Content-Type: application/json' -d '{\"email\":\"test@example.com\"}'"
print_diagnostic_header "AI Generation Checks"
echo ""
echo " If characters are stuck in 'pending' status:"
print_fix "media-worker may not be running or Redis connection may be broken"
print_cmd "kubectl logs -n projects -l project=$PROJECT_NAME,component=media-worker --tail=50"
echo ""
echo " If mutation explorer generates but never shows result:"
print_fix "SSE channel:daeya subscription may not be connected"
print_cmd "curl -N -H 'Authorization: Bearer <token>' https://$domain/api/daeya-api/events/channel:daeya"
echo ""
echo " If look generation fails with photo upload:"
print_fix "GEMINI_API_KEY may not be injected or vision endpoint unreachable"
print_cmd "kubectl exec -n projects deploy/$PROJECT_NAME-media-worker -- env | grep GEMINI"
print_diagnostic_header "Public Discovery Checks"
echo ""
echo " If /characters/public returns 404:"
print_fix "implement-avatar-model build may not have created public endpoints"
print_cmd "curl -s https://$domain/api/daeya-api/characters/public | jq"
echo ""
echo " If published characters don't show on the discovery feed:"
print_fix "Character may not be toggled to published=true in the studio"
print_cmd "curl -s -H 'Authorization: Bearer <token>' https://$domain/api/daeya-api/characters | jq '.[].published'"
}
teardown() {
print_header "Tearing down: $PROJECT_NAME"
local result
result=$(api_call DELETE "/project/$PROJECT_NAME")
print_success "Project $PROJECT_NAME deleted"
echo "$result" | jq -r '.message // empty' 2>/dev/null || true
}
case "$COMMAND" in
run) run_flow ;;
status) check_status ;;
diagnose) diagnose ;;
teardown) teardown ;;
*) echo "Unknown command: $COMMAND"; exit 1 ;;
esac