This commit captures the current state before implementing the composable monorepo template system. Key changes included: Infrastructure: - Add CockroachDB provisioner adapter for database provisioning - Add Redis provisioner adapter for cache provisioning - Add build events system with PostgreSQL storage - Add WebSocket endpoint for real-time build progress Code agent improvements: - Fix Claude Code adapter to use default allowed tools instead of dangerously-skip-permissions - Add context-aware stream closing for cancellation support - Improve parser tests for edge cases Build system: - Add build event constants and metrics - Remove deprecated git_operations.go (replaced by pod_git_operations.go) - Add rollback logic for multi-step provisioning operations Documentation: - Add composable-monorepo feature documentation - Add DNS/Cloudflare service documentation - Update deployment and troubleshooting guides Cookbooks: - Add fullstack-app cookbook - Refactor landing-test with shared library Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
203 lines
5.6 KiB
Bash
Executable File
203 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Full-Stack App E2E Test Script
|
|
# Usage: ./cookbooks/scripts/fullstack-test.sh <command> <project-name>
|
|
# Commands: run, status, teardown
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/common.sh"
|
|
|
|
COMMAND="${1:-}"
|
|
PROJECT_NAME="${2:-}"
|
|
|
|
if [[ -z "$COMMAND" || -z "$PROJECT_NAME" ]]; then
|
|
echo "Usage: $0 <command> <project-name>"
|
|
echo "Commands:"
|
|
echo " run - Create project and run full-stack build"
|
|
echo " status - Check build and deployment status"
|
|
echo " teardown - Delete the project"
|
|
exit 1
|
|
fi
|
|
|
|
# Full-stack app build prompt
|
|
FULLSTACK_PROMPT='Build a full-stack task management application with the following structure:
|
|
|
|
FRONTEND (Next.js 14 + shadcn/ui):
|
|
- Create a Next.js 14 app with App Router in /frontend
|
|
- Use shadcn/ui for all components (install with npx shadcn-ui@latest init)
|
|
- Dark theme with modern aesthetic
|
|
- Pages: Dashboard showing tasks, Add Task form, Task detail view
|
|
- Use Tailwind CSS for styling
|
|
- Connect to backend API at /api proxy
|
|
|
|
BACKEND (Go):
|
|
- Create a Go HTTP server in /backend using chi router
|
|
- Endpoints: GET /api/tasks, POST /api/tasks, GET /api/tasks/{id}, DELETE /api/tasks/{id}
|
|
- In-memory task storage (no database needed)
|
|
- Structured JSON responses
|
|
- CORS middleware for frontend
|
|
|
|
DOCKER:
|
|
- /frontend/Dockerfile: Multi-stage build for Next.js (node:20-alpine)
|
|
- /backend/Dockerfile: Multi-stage build for Go (golang:1.22-alpine)
|
|
- /docker-compose.yml: Run both services, frontend proxies to backend
|
|
|
|
CI/CD:
|
|
- /.woodpecker.yml: Build both images, push to registry, deploy to k8s
|
|
|
|
Create all necessary files including package.json, go.mod, and configuration files.'
|
|
|
|
# Test backend API
|
|
test_backend_api() {
|
|
local domain="$1"
|
|
|
|
echo "Testing backend API..."
|
|
|
|
# Test GET /api/tasks
|
|
local response
|
|
response=$(curl -s "https://$domain/api/tasks" 2>/dev/null || echo '{"error":"failed"}')
|
|
|
|
if echo "$response" | jq -e '.' > /dev/null 2>&1; then
|
|
echo " GET /api/tasks: OK"
|
|
echo " Response: $response"
|
|
return 0
|
|
else
|
|
echo " GET /api/tasks: FAILED"
|
|
echo " Response: $response"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
run_flow() {
|
|
echo "=== Full-Stack App E2E Test ==="
|
|
echo "Project: $PROJECT_NAME"
|
|
echo ""
|
|
|
|
# Step 1: Create project with build
|
|
echo "Step 1: Creating project and submitting full-stack build..."
|
|
local create_result
|
|
# Build the JSON payload (prompt, auto_commit, auto_push are top-level fields)
|
|
local payload
|
|
payload=$(jq -n \
|
|
--arg name "$PROJECT_NAME" \
|
|
--arg desc "Full-stack app E2E test" \
|
|
--arg prompt "$FULLSTACK_PROMPT" \
|
|
'{
|
|
name: $name,
|
|
description: $desc,
|
|
prompt: $prompt,
|
|
auto_commit: true,
|
|
auto_push: true
|
|
}')
|
|
create_result=$(api_call POST "/project/create-and-build" "$payload")
|
|
|
|
echo "$create_result" | jq '.'
|
|
|
|
local domain
|
|
domain=$(echo "$create_result" | jq -r '.data.domain // .domain // ""')
|
|
local task_id
|
|
task_id=$(echo "$create_result" | jq -r '.data.task_id // .task_id // ""')
|
|
|
|
if [[ -z "$domain" || -z "$task_id" ]]; then
|
|
echo "ERROR: Failed to create project"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "Domain: $domain"
|
|
echo "Build Task: $task_id"
|
|
echo ""
|
|
|
|
# Step 2: Wait for build
|
|
echo "Step 2: Waiting for Claude to build the full-stack app..."
|
|
if ! wait_for_build "$task_id"; then
|
|
echo "ERROR: Build failed"
|
|
exit 1
|
|
fi
|
|
echo ""
|
|
|
|
# Step 3: Wait for CI pipeline
|
|
echo "Step 3: Waiting for CI pipeline to build and deploy..."
|
|
if ! wait_for_pipeline "$PROJECT_NAME"; then
|
|
echo "WARNING: Pipeline may have failed, continuing to check site..."
|
|
fi
|
|
echo ""
|
|
|
|
# Step 4: Wait for site
|
|
echo "Step 4: Verifying site is accessible..."
|
|
if ! wait_for_site "$domain"; then
|
|
echo "ERROR: Site not accessible"
|
|
exit 1
|
|
fi
|
|
echo ""
|
|
|
|
# Step 5: Test backend API
|
|
echo "Step 5: Testing backend API..."
|
|
if ! test_backend_api "$domain"; then
|
|
echo "WARNING: Backend API test failed"
|
|
fi
|
|
echo ""
|
|
|
|
# Summary
|
|
echo "=== E2E Test Results ==="
|
|
echo "Project created: PASS"
|
|
echo "Build completed: PASS"
|
|
echo "CI Pipeline: $(wait_for_pipeline "$PROJECT_NAME" > /dev/null 2>&1 && echo "PASS" || echo "CHECK")"
|
|
echo "Site accessible: PASS"
|
|
echo "Backend API: $(test_backend_api "$domain" > /dev/null 2>&1 && echo "PASS" || echo "CHECK")"
|
|
echo ""
|
|
echo "Site URL: https://$domain"
|
|
echo "Git repo: https://git.threesix.ai/jordan/$PROJECT_NAME"
|
|
echo "CI: https://ci.threesix.ai/jordan/$PROJECT_NAME"
|
|
}
|
|
|
|
check_status() {
|
|
echo "=== Project Status: $PROJECT_NAME ==="
|
|
echo ""
|
|
|
|
# Get project info
|
|
local project_result
|
|
project_result=$(api_call GET "/projects/$PROJECT_NAME")
|
|
echo "Project:"
|
|
echo "$project_result" | jq '.'
|
|
echo ""
|
|
|
|
# Get latest build
|
|
echo "Latest Builds:"
|
|
api_call GET "/projects/$PROJECT_NAME/builds" | jq '.data[:3]'
|
|
echo ""
|
|
|
|
# Get latest pipeline
|
|
echo "Latest Pipelines:"
|
|
api_call GET "/projects/$PROJECT_NAME/pipelines" | jq '.data[:3]'
|
|
}
|
|
|
|
teardown() {
|
|
echo "=== Tearing down: $PROJECT_NAME ==="
|
|
|
|
local result
|
|
result=$(api_call DELETE "/project/$PROJECT_NAME")
|
|
echo "$result" | jq '.'
|
|
|
|
echo ""
|
|
echo "Project deleted. Gitea repo preserved."
|
|
}
|
|
|
|
case "$COMMAND" in
|
|
run)
|
|
run_flow
|
|
;;
|
|
status)
|
|
check_status
|
|
;;
|
|
teardown)
|
|
teardown
|
|
;;
|
|
*)
|
|
echo "Unknown command: $COMMAND"
|
|
echo "Valid commands: run, status, teardown"
|
|
exit 1
|
|
;;
|
|
esac
|