#!/bin/bash # Build and run Kordant application # Usage: ./run [build|test|run|lsp] [-v|--verbose] [-c|--coverage] [-p|--performance] [-o|--output ] [--no-lsp] # Note: Default action (./run with no args) runs the app with verbose logging enabled set -o pipefail readonly PROJECT="Kordant.xcodeproj" readonly SCHEME="Kordant" readonly CONFIGURATION="Debug" readonly APP_SUBSYSTEM="com.frenocorp.Kordant" readonly BUNDLE_ID="com.frenocorp.lendair" VERBOSE=false OUTPUT_FILE="" SKIP_LSP=false PERFORMANCE=false COVERAGE=false build_xcodebuild_command() { local action="$1" local destination="${2:-generic/platform=iOS}" local extra_flags="${3:-}" local cmd="xcodebuild -project $PROJECT -scheme $SCHEME -configuration $CONFIGURATION -destination '$destination' $extra_flags $action" if [ "$PERFORMANCE" = true ]; then cmd="$cmd -enablePerformanceTestsDiagnostics YES" fi if [ "$COVERAGE" = true ]; then cmd="$cmd -enableCodeCoverage YES" fi echo "$cmd" } kill_existing_lendair_processes() { echo "Checking for existing Kordant processes..." local pids pids=$(pgrep -f "Kordant.app") if [ -n "$pids" ]; then echo "Killing existing Kordant processes (PID(s): $pids)..." kill $pids 2>/dev/null sleep 1 else echo "No existing Kordant processes found" fi } update_lsp_config() { echo "Updating LSP configuration..." if command -v xcode-build-server &> /dev/null; then local build_root build_root=$(ls -td "$HOME/Library/Developer/Xcode/DerivedData/${SCHEME}-"*/Build 2>/dev/null | head -1) local exit_code if [ -n "$build_root" ]; then xcode-build-server config -project "$PROJECT" -scheme "$SCHEME" --build_root "$build_root" > /dev/null 2>&1 exit_code=$? else xcode-build-server config -project "$PROJECT" -scheme "$SCHEME" > /dev/null 2>&1 exit_code=$? fi if [ $exit_code -eq 0 ]; then echo "LSP configuration updated (buildServer.json created)" else echo "Could not update LSP configuration" fi else echo "xcode-build-server not found. Install with: brew install xcode-build-server" echo " This helps Neovim's LSP recognize your Swift modules" fi } get_build_directory() { xcodebuild -project "$PROJECT" -scheme "$SCHEME" -configuration "$CONFIGURATION" \ -showBuildSettings 2>/dev/null | \ grep -m 1 "BUILT_PRODUCTS_DIR" | \ sed 's/.*= //' } handle_build_success() { echo "Build succeeded!" if [ "$SKIP_LSP" != true ]; then update_lsp_config fi } print_errors() { local output="$1" local action_type="$2" echo "" echo "${action_type} Errors:" echo "================================================================================" local errors errors=$(echo "$output" | grep -E "error:|Error |failed|FAIL" | sed 's/^/ /') if [ -n "$errors" ]; then echo "$errors" else echo " No specific error messages found. See full output above." fi echo "================================================================================" } print_warnings() { local output="$1" echo "" echo "Diagnostic Warnings:" echo "================================================================================" local warnings warnings=$(echo "$output" | grep -E "\.swift:[0-9]+:[0-9]+: warning:" | sed 's/^/ /') if [ -n "$warnings" ]; then local count count=$(echo "$warnings" | wc -l | tr -d ' ') echo " Found $count warning(s):" echo "" echo "$warnings" else echo " No warnings found." fi echo "================================================================================" } get_booted_simulator() { xcrun simctl list devices booted 2>/dev/null | grep -oE "[A-F0-9-]{36}" | head -1 } ensure_simulator_booted() { local simulator simulator=$(get_booted_simulator) if [ -z "$simulator" ]; then echo "No booted simulator found, booting first available iPhone..." >&2 simulator=$(xcrun simctl list devices available 2>/dev/null | grep -i "iPhone" | grep -oE "[A-F0-9-]{36}" | head -1) if [ -n "$simulator" ]; then echo "Booting simulator $simulator..." >&2 xcrun simctl boot "$simulator" sleep 5 open -a Simulator 2>/dev/null || true fi fi echo "$simulator" } launch_app() { local build_dir app_path simulator build_dir=$(get_build_directory) app_path="${build_dir}/Kordant.app" simulator=$(ensure_simulator_booted) if [ -z "$simulator" ]; then echo "Error: No iOS simulator available. Boot one with: open -a Simulator" exit 1 fi if [ -d "$app_path" ]; then echo "Installing on simulator $simulator..." xcrun simctl install "$simulator" "$app_path" echo "Launching app..." xcrun simctl launch "$simulator" "$BUNDLE_ID" sleep 2 echo "Streaming simulator logs (Ctrl+C to stop - app keeps running)..." echo "================================================================" xcrun simctl spawn "$simulator" log stream --level debug \ --predicate "subsystem contains \"$APP_SUBSYSTEM\"" \ --style compact 2>/dev/null else echo "App not found at expected location, trying fallback..." local fallback fallback=$(ls -dt "$HOME/Library/Developer/Xcode/DerivedData/Kordant-"*/Build/Products/Debug-iphonesimulator/Lendair.app 2>/dev/null | head -1) if [ -d "$fallback" ]; then echo "Found at: $fallback" xcrun simctl install "$simulator" "$fallback" xcrun simctl launch "$simulator" "$BUNDLE_ID" sleep 2 xcrun simctl spawn "$simulator" log stream --level debug \ --predicate "subsystem contains \"$APP_SUBSYSTEM\"" \ --style compact 2>/dev/null else echo "No app bundle found" exit 1 fi fi } run_with_output() { local cmd="$1" local exit_code if [ "$VERBOSE" = true ] && [ -n "$OUTPUT_FILE" ]; then COMMAND_OUTPUT=$(eval "$cmd" 2>&1 | tee "$OUTPUT_FILE") exit_code=${PIPESTATUS[0]} elif [ "$VERBOSE" = true ]; then if [ -t 1 ]; then COMMAND_OUTPUT=$(eval "$cmd" 2>&1 | tee /dev/tty) exit_code=${PIPESTATUS[0]} else COMMAND_OUTPUT=$(eval "$cmd" 2>&1) exit_code=$? echo "$COMMAND_OUTPUT" fi elif [ -n "$OUTPUT_FILE" ]; then COMMAND_OUTPUT=$(eval "$cmd" 2>&1 | tee "$OUTPUT_FILE") exit_code=${PIPESTATUS[0]} else COMMAND_OUTPUT=$(eval "$cmd" 2>&1) exit_code=$? fi return $exit_code } show_usage() { echo "Usage: $0 [build|test|run|lsp] [-v|--verbose] [-o|--output ] [--no-lsp] [-p|--performance] [-c|--coverage]" echo "" echo "Commands:" echo " build - Build the application" echo " test - Run unit tests" echo " run - Build and run the application on simulator with logging (default)" echo " launch - Launch last-built app on booted simulator" echo " lsp - Update LSP configuration only (buildServer.json)" echo "" echo "Options:" echo " -v, --verbose - Show output in stdout" echo " -o, --output - Write output to log file" echo " --no-lsp - Skip LSP configuration update" echo " -p, --performance - Run tests with performance profiling" echo " -c, --coverage - Run tests with code coverage analysis" echo "" echo "Note: Running './run' with no arguments defaults to 'run' action with verbose logging." echo " Press Ctrl+C to stop log capture and keep the app running." } ACTION="" while [[ $# -gt 0 ]]; do case $1 in -v|--verbose) VERBOSE=true shift ;; -o|--output) OUTPUT_FILE="$2" VERBOSE=true shift 2 ;; --no-lsp) SKIP_LSP=true shift ;; -p|--performance) PERFORMANCE=true shift ;; -c|--coverage) COVERAGE=true shift ;; *) if [ -z "$ACTION" ]; then ACTION="$1" fi shift ;; esac done if [ -z "$ACTION" ]; then ACTION="run" fi echo "=== Kordant Application Script ===" case "$ACTION" in build) echo "Building Kordant project..." run_with_output "$(build_xcodebuild_command build)" if [ $? -eq 0 ]; then handle_build_success echo "The app is located at: $(get_build_directory)/Kordant.app" if [ "$VERBOSE" = true ]; then print_warnings "$COMMAND_OUTPUT" fi else echo "Build failed!" print_errors "$COMMAND_OUTPUT" "Build" exit 1 fi ;; test) echo "Running unit tests (parallel)..." run_with_output "$(build_xcodebuild_command test "platform=iOS Simulator,name=iPhone 16" "-parallel-testing-enabled YES")" if [ $? -eq 0 ]; then echo "Tests passed!" if [ "$VERBOSE" = true ]; then print_warnings "$COMMAND_OUTPUT" fi else echo "Tests failed!" print_errors "$COMMAND_OUTPUT" "Test" exit 1 fi ;; run) echo "Building and running Kordant application..." kill_existing_lendair_processes run_with_output "$(build_xcodebuild_command build "generic/platform=iOS Simulator")" if [ $? -eq 0 ]; then handle_build_success if [ "$VERBOSE" = true ]; then print_warnings "$COMMAND_OUTPUT" fi launch_app else echo "Build failed!" print_errors "$COMMAND_OUTPUT" "Build" exit 1 fi ;; lsp) echo "Updating LSP configuration only..." update_lsp_config ;; launch) echo "Launching last-built app on simulator..." launch_app ;; *) show_usage exit 1 ;; esac