#!/bin/bash set -euo pipefail # Template Validation Script # Validates all rdev templates by: # 1. Creating a test project # 2. Adding each component type # 3. Verifying files exist and compile # 4. Running CI pipeline # 5. Cleaning up # # Usage: ./cookbooks/scripts/template-validation.sh # Commands: run, quick, cleanup SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/common.sh" COMMAND="${1:-run}" PROJECT_NAME="template-validation-$(date +%s)" # Component types to test COMPONENT_TYPES=( "service" "worker" "app-astro" "app-react" "app-nextjs" "cli" ) print_banner() { echo "" echo -e "${BLUE}╔══════════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ rdev Template Validation - Full Integration Test ║${NC}" echo -e "${BLUE}╚══════════════════════════════════════════════════════════════════╝${NC}" echo "" } validate_template_compilation() { local project_id="$1" local git_owner git_owner=$(get_git_owner) print_header "Validating Template Compilation" local failures=0 # Check each key file exists echo "Checking skeleton files..." local files_to_check=( "CLAUDE.md" "README.md" "go.work" "pnpm-workspace.yaml" ".woodpecker.yml" ".golangci.yml" "docker-compose.yml" "pkg/README.md" "scripts/dev.sh" "scripts/discover.sh" "scripts/quality.sh" "scripts/install.sh" ) for file in "${files_to_check[@]}"; do local check check=$(curl -s -o /dev/null -w "%{http_code}" \ "https://git.threesix.ai/api/v1/repos/$git_owner/$project_id/contents/$file" \ -H "Authorization: token ${GITEA_TOKEN:-}" 2>/dev/null) if [[ "$check" == "200" ]]; then print_success "$file" else print_error "$file (HTTP $check)" ((failures++)) fi done # Check pkg/ packages echo "" echo "Checking pkg/ packages..." local pkg_dirs=( "pkg/app" "pkg/httperror" "pkg/httpresponse" "pkg/httpvalidation" "pkg/middleware" "pkg/auth" ) for pkg_dir in "${pkg_dirs[@]}"; do local check check=$(curl -s "https://git.threesix.ai/api/v1/repos/$git_owner/$project_id/contents/$pkg_dir" \ -H "Authorization: token ${GITEA_TOKEN:-}" 2>/dev/null | jq -r 'if type == "array" then "ok" else "not found" end') if [[ "$check" == "ok" ]]; then print_success "$pkg_dir/" else print_warning "$pkg_dir/ (may be optional)" fi done # Check packages/ echo "" echo "Checking packages/..." local packages=( "packages/ui" "packages/layout" "packages/auth" "packages/logger" "packages/api-client" ) for pkg in "${packages[@]}"; do local check check=$(curl -s "https://git.threesix.ai/api/v1/repos/$git_owner/$project_id/contents/$pkg/package.json" \ -H "Authorization: token ${GITEA_TOKEN:-}" 2>/dev/null | jq -r '.name // "not found"') if [[ "$check" == "package.json" ]]; then print_success "$pkg/" else print_warning "$pkg/ (may be optional)" fi done echo "" if [[ $failures -gt 0 ]]; then print_error "Template compilation validation: $failures failures" return 1 else print_success "Template compilation validation: all required files present" return 0 fi } validate_component() { local project_id="$1" local comp_type="$2" local comp_name="$3" local git_owner git_owner=$(get_git_owner) echo "" echo "Validating $comp_type component..." # Determine expected directory local comp_dir case "$comp_type" in service) comp_dir="services/$comp_name" ;; worker) comp_dir="workers/$comp_name" ;; app-astro|app-react|app-nextjs) comp_dir="apps/$comp_name" ;; cli) comp_dir="cli/$comp_name" ;; esac # Check component directory exists local check check=$(curl -s "https://git.threesix.ai/api/v1/repos/$git_owner/$project_id/contents/$comp_dir" \ -H "Authorization: token ${GITEA_TOKEN:-}" 2>/dev/null | jq -r 'if type == "array" then "ok" else "not found" end') if [[ "$check" == "ok" ]]; then print_success "$comp_type: $comp_dir/ exists" else print_error "$comp_type: $comp_dir/ not found" return 1 fi # Check component.yaml exists local yaml_check yaml_check=$(curl -s -o /dev/null -w "%{http_code}" \ "https://git.threesix.ai/api/v1/repos/$git_owner/$project_id/contents/$comp_dir/component.yaml" \ -H "Authorization: token ${GITEA_TOKEN:-}" 2>/dev/null) if [[ "$yaml_check" == "200" ]]; then print_success "$comp_type: component.yaml exists" else print_warning "$comp_type: component.yaml not found" fi # Check Dockerfile exists local docker_check docker_check=$(curl -s -o /dev/null -w "%{http_code}" \ "https://git.threesix.ai/api/v1/repos/$git_owner/$project_id/contents/$comp_dir/Dockerfile" \ -H "Authorization: token ${GITEA_TOKEN:-}" 2>/dev/null) if [[ "$docker_check" == "200" ]]; then print_success "$comp_type: Dockerfile exists" else print_warning "$comp_type: Dockerfile not found (may be optional)" fi return 0 } add_component() { local comp_type="$1" local comp_name="$2" echo "Adding $comp_type component: $comp_name" local payload payload=$(jq -n \ --arg type "$comp_type" \ --arg name "$comp_name" \ '{type: $type, name: $name}') local result result=$(api_call POST "/projects/$PROJECT_NAME/components" "$payload") local path path=$(echo "$result" | jq -r '.data.path // .path // ""') if [[ -z "$path" ]]; then print_error "Failed to add $comp_type component" echo "$result" | jq '.' return 1 fi print_success "Added $comp_type/$comp_name at $path" return 0 } run_full_validation() { print_banner # Step 1: Create project print_header "Step 1: Creating Test Project" echo "Project name: $PROJECT_NAME" local create_payload create_payload=$(jq -n \ --arg name "$PROJECT_NAME" \ --arg desc "Template validation test project" \ '{name: $name, description: $desc}') local create_result create_result=$(api_call POST "/project" "$create_payload") local domain domain=$(echo "$create_result" | jq -r '.data.domain // .domain // ""') if [[ -z "$domain" ]]; then print_error "Failed to create project" echo "$create_result" | jq '.' exit 1 fi print_success "Project created with domain: $domain" # Step 2: Add all component types print_header "Step 2: Adding Components" local comp_names=() for comp_type in "${COMPONENT_TYPES[@]}"; do local comp_name case "$comp_type" in service) comp_name="api" ;; worker) comp_name="jobs" ;; app-astro) comp_name="landing" ;; app-react) comp_name="web" ;; app-nextjs) comp_name="dashboard" ;; cli) comp_name="ctl" ;; esac if add_component "$comp_type" "$comp_name"; then comp_names+=("$comp_type:$comp_name") fi done # Wait for git to sync print_header "Step 3: Waiting for Git Sync" echo "Waiting 15 seconds for git to propagate..." sleep 15 # Step 4: Validate skeleton files print_header "Step 4: Validating Skeleton" validate_template_compilation "$PROJECT_NAME" || true # Step 5: Validate each component print_header "Step 5: Validating Components" for comp in "${comp_names[@]}"; do local type="${comp%%:*}" local name="${comp##*:}" validate_component "$PROJECT_NAME" "$type" "$name" || true done # Step 6: Check CI pipeline print_header "Step 6: Checking CI Pipeline" if wait_for_pipeline "$PROJECT_NAME" 60 5; then print_success "CI pipeline completed successfully" else print_warning "CI pipeline did not complete (may need investigation)" fi # Summary print_header "Validation Summary" echo "Project: $PROJECT_NAME" echo "Domain: https://$domain" echo "Git: https://git.threesix.ai/$(get_git_owner)/$PROJECT_NAME" echo "CI: https://ci.threesix.ai/$(get_git_owner)/$PROJECT_NAME" echo "" echo "Components tested:" for comp in "${comp_names[@]}"; do echo " - $comp" done echo "" print_warning "Remember to clean up: $0 cleanup $PROJECT_NAME" } run_quick_validation() { print_banner echo "Quick validation: checking template API responses only" echo "" # Check skeleton template info print_header "Checking Skeleton Template" local skeleton_info skeleton_info=$(api_call GET "/templates/skeleton") echo "$skeleton_info" | jq '.data // .' # Check component templates print_header "Checking Component Templates" local comp_templates comp_templates=$(api_call GET "/templates/components") echo "$comp_templates" | jq '.data.components // .data // .' # Verify each component type exists print_header "Verifying Component Types" for comp_type in "${COMPONENT_TYPES[@]}"; do local info info=$(api_call GET "/templates/components/$comp_type" 2>/dev/null || echo '{}') local found found=$(echo "$info" | jq -r '.data.type // ""') if [[ "$found" == "$comp_type" ]]; then print_success "$comp_type template available" else print_error "$comp_type template not found" fi done } cleanup() { local project_to_delete="${2:-$PROJECT_NAME}" print_header "Cleaning Up: $project_to_delete" local result result=$(api_call DELETE "/project/$project_to_delete") echo "$result" | jq '.' print_success "Project deleted. Gitea repo preserved." } case "$COMMAND" in run) run_full_validation ;; quick) run_quick_validation ;; cleanup) cleanup "$@" ;; *) echo "Usage: $0 " echo "Commands:" echo " run - Full validation (creates project, adds all components, checks CI)" echo " quick - Quick check (API responses only, no project creation)" echo " cleanup - Delete test project (optionally specify project name)" exit 1 ;; esac