#!/usr/bin/env bash ############################################################################### # Installer Test Script # Simulates the installer and shows which files would fail to download # Usage: ./scripts/tests/test-installer-files.sh [--profile=] [--verbose] ############################################################################### set -e # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # Configuration BRANCH="main" RAW_URL="https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/${BRANCH}" TEMP_DIR="/tmp/opencode-installer-test-$$" # Allow local testing (useful for CI/CD or when testing changes before pushing) USE_LOCAL=false LOCAL_REGISTRY="./registry.json" # Global variables VERBOSE=false TEST_PROFILE="" FAILED_FILES=() SKIPPED_FILES=() SUCCESS_COUNT=0 FAIL_COUNT=0 # Cleanup function # shellcheck disable=SC2329 cleanup() { rm -rf "$TEMP_DIR" 2>/dev/null || true } trap cleanup EXIT INT TERM # Print functions print_header() { echo -e "${CYAN}${BOLD}" echo "╔════════════════════════════════════════════════════════════════╗" echo "║ ║" echo "║ Installer File Test - v1.0.0 ║" echo "║ ║" echo "╚════════════════════════════════════════════════════════════════╝" echo -e "${NC}" } print_success() { echo -e "${GREEN}✓${NC} $1" } print_error() { echo -e "${RED}✗${NC} $1" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" } print_info() { echo -e "${BLUE}ℹ${NC} $1" } print_step() { echo -e "\n${BOLD}$1${NC}" } # Check if URL is accessible check_url() { local url="$1" local component="$2" if curl -fsSL --max-time 10 -I "$url" > /dev/null 2>&1; then return 0 else return 1 fi } # Test a single file test_file() { local path="$1" local component="$2" local url="${RAW_URL}/${path}" if check_url "$url" "$component"; then SUCCESS_COUNT=$((SUCCESS_COUNT + 1)) if [ "$VERBOSE" = true ]; then print_success "Accessible: ${path}" fi return 0 else FAIL_COUNT=$((FAIL_COUNT + 1)) FAILED_FILES+=("${component}|${path}") print_error "NOT FOUND: ${path}" return 1 fi } # Test components from a category test_category() { local category="$1" local registry_file="$2" print_step "Testing ${category}..." local components components=$(jq -r ".components.${category}[]? | \"\(.id)|\(.path)\"" "$registry_file" 2>/dev/null || echo "") if [ -z "$components" ]; then print_warning "No components in category: ${category}" return fi while IFS='|' read -r id path; do [ -z "$id" ] && continue test_file "$path" "${category}:${id}" done <<< "$components" } # Test profile components test_profile() { local profile="$1" local registry_file="$2" print_step "Testing Profile: ${profile}" # Get components from profile local components components=$(jq -r ".profiles.${profile}.components[]?" "$registry_file" 2>/dev/null || echo "") if [ -z "$components" ]; then print_error "Profile not found or empty: ${profile}" return 1 fi print_info "Profile components:" while IFS= read -r component; do [ -z "$component" ] && continue local type="${component%%:*}" local id="${component##*:}" # Handle wildcards if [[ "$id" == *"*"* ]]; then print_info " Wildcard: ${component} (expanding...)" if [ "$type" = "context" ]; then local prefix="${id%%\**}" prefix="${prefix%/}" # Find matching contexts local matches matches=$(jq -r ".components.contexts[]? | select(.path | startswith(\".opencode/context/${prefix}\")) | \"\(.id)|\(.path)\"" "$registry_file") while IFS='|' read -r match_id match_path; do [ -z "$match_id" ] && continue test_file "$match_path" "context:${match_id}" done <<< "$matches" fi continue fi # Get path for component local path path=$(jq -r ".components.${type}s[]? | select(.id == \"${id}\") | .path" "$registry_file") if [ -n "$path" ] && [ "$path" != "null" ]; then test_file "$path" "${component}" else print_warning " Component not found in registry: ${component}" SKIPPED_FILES+=("${component}|not_in_registry") fi done <<< "$components" } # Test all categories test_all() { local registry_file="$1" print_step "Testing All Component Categories" local categories=("agents" "subagents" "commands" "tools" "plugins" "skills" "contexts" "config") for category in "${categories[@]}"; do test_category "$category" "$registry_file" done } # Print summary print_summary() { echo "" echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}" echo -e "${BOLD}Test Summary${NC}" echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}" echo "" echo -e "Total files tested: ${CYAN}$((SUCCESS_COUNT + FAIL_COUNT))${NC}" echo -e "Files accessible: ${GREEN}${SUCCESS_COUNT}${NC}" echo -e "Files NOT FOUND: ${RED}${FAIL_COUNT}${NC}" if [ ${#SKIPPED_FILES[@]} -gt 0 ]; then echo -e "Skipped: ${YELLOW}${#SKIPPED_FILES[@]}${NC}" fi echo "" if [ $FAIL_COUNT -gt 0 ]; then print_error "Found ${FAIL_COUNT} file(s) that would FAIL during installation:" echo "" echo "Failed files:" for entry in "${FAILED_FILES[@]}"; do local component="${entry%%|*}" local path="${entry##*|}" echo " - ${component}" echo " URL: ${RAW_URL}/${path}" done echo "" echo -e "${YELLOW}These files need to be:${NC}" echo " 1. Added to the repository, OR" echo " 2. Removed from registry.json" echo "" return 1 else print_success "All files are accessible and would install successfully!" return 0 fi } # Usage usage() { echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --profile= Test a specific profile (essential, developer, business, full, advanced)" echo " --all Test all components (default)" echo " --verbose Show successful file checks" echo " --local Use local registry.json instead of downloading from GitHub" echo " --help Show this help message" echo "" echo "Examples:" echo " $0 # Test all components (remote registry)" echo " $0 --profile=essential # Test essential profile" echo " $0 --profile=developer --verbose # Test developer profile with details" echo " $0 --local # Test using local registry.json" echo "" exit 0 } # Main main() { print_header # Parse arguments TEST_MODE="all" while [ $# -gt 0 ]; do case "$1" in --profile=*) TEST_PROFILE="${1#*=}" TEST_MODE="profile" ;; --profile) if [ -n "$2" ]; then TEST_PROFILE="$2" TEST_MODE="profile" shift else echo "Error: --profile requires a profile name" exit 1 fi ;; --all) TEST_MODE="all" ;; --verbose) VERBOSE=true ;; --local) USE_LOCAL=true ;; --help|-h) usage ;; *) echo "Unknown option: $1" echo "Run '$0 --help' for usage information" exit 1 ;; esac shift done # Check dependencies if ! command -v curl &> /dev/null; then print_error "curl is required but not installed" exit 1 fi if ! command -v jq &> /dev/null; then print_error "jq is required but not installed" exit 1 fi # Create temp directory mkdir -p "$TEMP_DIR" # Get registry (local or remote) if [ "$USE_LOCAL" = true ]; then print_step "Using local registry..." if [ ! -f "$LOCAL_REGISTRY" ]; then print_error "Local registry not found: $LOCAL_REGISTRY" exit 1 fi cp "$LOCAL_REGISTRY" "${TEMP_DIR}/registry.json" print_success "Local registry loaded" else print_step "Downloading registry from GitHub..." if ! curl -fsSL "${RAW_URL}/registry.json" -o "${TEMP_DIR}/registry.json"; then print_error "Failed to download registry from ${RAW_URL}/registry.json" exit 1 fi print_success "Registry downloaded successfully" fi # Validate registry JSON if ! jq empty "${TEMP_DIR}/registry.json" 2>/dev/null; then print_error "Registry is not valid JSON" exit 1 fi # Run tests echo "" print_info "Testing file accessibility from: ${RAW_URL}" echo "" case "$TEST_MODE" in profile) if [ -z "$TEST_PROFILE" ]; then print_error "No profile specified. Use --profile=" exit 1 fi test_profile "$TEST_PROFILE" "${TEMP_DIR}/registry.json" ;; all) test_all "${TEMP_DIR}/registry.json" ;; esac # Print summary print_summary exit $? } main "$@" # Shellcheck validation passed