You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

494 lines
17 KiB

#!/bin/bash
# build-android.sh
# Author: Matthew Raymer
# Date: 2025-07-11
# Description: Android build script for TimeSafari application
# This script handles the complete Android build process including cleanup,
# web build, Capacitor build, Gradle build, and Android Studio launch.
#
# Usage:
# ./scripts/build-android.sh [options]
#
# Options:
# --dev, --development Build for development environment
# --test Build for testing environment
# --prod, --production Build for production environment
# --debug Build debug APK
# --release Build release APK
# --studio Open Android Studio after build
# --apk Build APK file
# --aab Build AAB (Android App Bundle)
# --clean Clean build artifacts only
# --sync Sync Capacitor only
# --assets Generate assets only
# --deploy Deploy APK to connected device
# -h, --help Show this help message
# -v, --verbose Enable verbose logging
#
# Examples:
# ./scripts/build-android.sh --dev --studio # Development build + open studio
# ./scripts/build-android.sh --prod --apk # Production APK build
# ./scripts/build-android.sh --test --aab # Testing AAB build
# ./scripts/build-android.sh --clean # Clean only
# ./scripts/build-android.sh --sync # Sync only
#
# Exit Codes:
# 1 - Android cleanup failed
# 2 - Web build failed
# 3 - Capacitor build failed
# 4 - Gradle clean failed
# 5 - Gradle assemble failed
# 6 - Capacitor sync failed
# 7 - Asset generation failed
# 8 - Android Studio launch failed
# 9 - Android asset validation failed
# Exit on any error
set -e
# Source common utilities
source "$(dirname "$0")/common.sh"
# Function to validate critical dependencies
validate_dependencies() {
log_info "Validating critical dependencies..."
# Check if node_modules exists
if [ ! -d "node_modules" ]; then
log_error "node_modules directory not found. Please run 'npm install' first."
exit 1
fi
# Check if tsx is available
if [ ! -f "node_modules/.bin/tsx" ]; then
log_error "tsx dependency not found. Please run 'npm install' first."
exit 1
fi
# Check if capacitor-assets is available
if [ ! -f "node_modules/.bin/capacitor-assets" ]; then
log_error "capacitor-assets dependency not found. Please run 'npm install' first."
exit 1
fi
log_success "All critical dependencies validated successfully"
}
# Function to validate Android assets and resources
validate_android_assets() {
log_info "Validating Android assets and resources..."
# Check if source assets exist
local missing_assets=()
if [ ! -f "resources/icon.png" ]; then
missing_assets+=("resources/icon.png")
fi
if [ ! -f "resources/splash.png" ]; then
missing_assets+=("resources/splash.png")
fi
if [ ! -f "resources/splash_dark.png" ]; then
missing_assets+=("resources/splash_dark.png")
fi
if [ ${#missing_assets[@]} -gt 0 ]; then
log_error "Missing source assets:"
for asset in "${missing_assets[@]}"; do
log_error " - $asset"
done
log_error "Please ensure all required assets are present in the resources/ directory."
return 1
fi
# Check if Android drawable resources exist
local missing_drawables=()
if [ ! -f "android/app/src/main/res/drawable/splash.png" ]; then
missing_drawables+=("drawable/splash.png")
fi
# Check if mipmap resources exist
local missing_mipmaps=()
local mipmap_dirs=("mipmap-mdpi" "mipmap-hdpi" "mipmap-xhdpi" "mipmap-xxhdpi" "mipmap-xxxhdpi")
for dir in "${mipmap_dirs[@]}"; do
if [ ! -f "android/app/src/main/res/$dir/ic_launcher.png" ]; then
missing_mipmaps+=("$dir/ic_launcher.png")
fi
if [ ! -f "android/app/src/main/res/$dir/ic_launcher_round.png" ]; then
missing_mipmaps+=("$dir/ic_launcher_round.png")
fi
done
# If any resources are missing, regenerate them
if [ ${#missing_drawables[@]} -gt 0 ] || [ ${#missing_mipmaps[@]} -gt 0 ]; then
log_warn "Missing Android resources detected:"
for resource in "${missing_drawables[@]}" "${missing_mipmaps[@]}"; do
log_warn " - $resource"
done
log_info "Regenerating Android assets..."
# Create assets directory if it doesn't exist
mkdir -p assets
# Copy source assets to assets directory for capacitor-assets
cp resources/icon.png assets/ 2>/dev/null || log_warn "Could not copy icon.png"
cp resources/splash.png assets/ 2>/dev/null || log_warn "Could not copy splash.png"
cp resources/splash_dark.png assets/ 2>/dev/null || log_warn "Could not copy splash_dark.png"
# Generate assets
if npx @capacitor/assets generate >/dev/null 2>&1; then
log_success "Android assets regenerated successfully"
# Clean up temporary assets
rm -f assets/icon.png assets/splash.png assets/splash_dark.png
# Verify the resources were created
local verification_failed=false
if [ ! -f "android/app/src/main/res/drawable/splash.png" ]; then
log_error "Failed to generate drawable/splash.png"
verification_failed=true
fi
for dir in "${mipmap_dirs[@]}"; do
if [ ! -f "android/app/src/main/res/$dir/ic_launcher.png" ]; then
log_error "Failed to generate $dir/ic_launcher.png"
verification_failed=true
fi
if [ ! -f "android/app/src/main/res/$dir/ic_launcher_round.png" ]; then
log_error "Failed to generate $dir/ic_launcher_round.png"
verification_failed=true
fi
done
if [ "$verification_failed" = true ]; then
log_error "Asset generation completed but some resources are still missing."
log_info "You may need to manually create the missing resources or check the asset generation process."
return 1
fi
else
log_error "Failed to generate Android assets"
log_info "You may need to manually create the missing resources:"
for resource in "${missing_drawables[@]}" "${missing_mipmaps[@]}"; do
log_info " - android/app/src/main/res/$resource"
done
return 1
fi
else
log_success "All Android assets and resources validated successfully"
fi
return 0
}
# Default values
BUILD_MODE="development"
BUILD_TYPE="debug"
OPEN_STUDIO=false
BUILD_APK=false
BUILD_AAB=false
CLEAN_ONLY=false
SYNC_ONLY=false
ASSETS_ONLY=false
DEPLOY_APP=false
AUTO_RUN=false
CUSTOM_API_IP=""
# Function to parse Android-specific arguments
parse_android_args() {
local args=("$@")
local i=0
while [ $i -lt ${#args[@]} ]; do
local arg="${args[$i]}"
case $arg in
--dev|--development)
BUILD_MODE="development"
;;
--test)
BUILD_MODE="test"
;;
--prod|--production)
BUILD_MODE="production"
;;
--debug)
BUILD_TYPE="debug"
;;
--release)
BUILD_TYPE="release"
;;
--studio)
OPEN_STUDIO=true
;;
--apk)
BUILD_APK=true
;;
--aab)
BUILD_AAB=true
;;
--clean)
CLEAN_ONLY=true
;;
--sync)
SYNC_ONLY=true
;;
--assets|--assets-only)
ASSETS_ONLY=true
;;
--deploy)
DEPLOY_APP=true
;;
--auto-run)
AUTO_RUN=true
;;
--api-ip)
if [ $((i + 1)) -lt ${#args[@]} ]; then
CUSTOM_API_IP="${args[$((i + 1))]}"
i=$((i + 1)) # Skip the next argument
else
log_error "Error: --api-ip requires an IP address"
exit 1
fi
;;
--api-ip=*)
CUSTOM_API_IP="${arg#*=}"
;;
-h|--help)
print_android_usage
exit 0
;;
-v|--verbose)
set -x
;;
*)
log_warn "Unknown argument: $arg"
;;
esac
i=$((i + 1))
done
}
# Function to print Android-specific usage
print_android_usage() {
echo "Usage: $0 [options]"
echo ""
echo "Android Build Options:"
echo " --dev, --development Build for development environment"
echo " --test Build for testing environment"
echo " --prod, --production Build for production environment"
echo " --debug Build debug APK (default)"
echo " --release Build release APK"
echo " --studio Open Android Studio after build"
echo " --apk Build APK file"
echo " --aab Build AAB (Android App Bundle)"
echo " --clean Clean build artifacts only"
echo " --sync Sync Capacitor only"
echo " --assets Generate assets only"
echo " --deploy Deploy APK to connected device"
echo " --auto-run Auto-run app after build"
echo " --api-ip <ip> Custom IP address for claim API (defaults to 10.0.2.2)"
echo ""
echo "Common Options:"
echo " -h, --help Show this help message"
echo " -v, --verbose Enable verbose logging"
echo ""
echo "Examples:"
echo " $0 --dev --studio # Development build + open studio"
echo " $0 --prod --apk # Production APK build"
echo " $0 --test --aab # Testing AAB build"
echo " $0 --test --auto-run # Test build + auto-run"
echo " $0 --clean # Clean only"
echo " $0 --sync # Sync only"
echo " $0 --deploy # Build and deploy to device"
echo " $0 --dev # Dev build with default 10.0.2.2"
echo " $0 --dev --api-ip 192.168.1.100 # Dev build with custom API IP"
echo ""
}
# Parse command line arguments
parse_android_args "$@"
# Print build header
print_header "TimeSafari Android Build Process"
# Validate dependencies before proceeding
validate_dependencies
# Validate Android assets and resources
validate_android_assets || {
log_error "Android asset validation failed. Please fix the issues above and try again."
exit 9
}
# Log build start
log_info "Starting Android build process at $(date)"
log_info "Build mode: $BUILD_MODE"
log_info "Build type: $BUILD_TYPE"
# Setup environment for Capacitor build
setup_build_env "capacitor" "$BUILD_MODE"
# Override API servers for Android development
if [ "$BUILD_MODE" = "development" ]; then
if [ -n "$CUSTOM_API_IP" ]; then
# Use custom IP for physical device development
export VITE_DEFAULT_ENDORSER_API_SERVER="http://${CUSTOM_API_IP}:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://${CUSTOM_API_IP}:3000"
log_info "Android development mode: Using custom IP ${CUSTOM_API_IP} for physical device"
else
# Use Android emulator IP (10.0.2.2) for Android development
export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000"
export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000"
log_debug "Android development mode: Using 10.0.2.2 for emulator"
fi
fi
# Setup application directories
setup_app_directories
# Load environment from .env file if it exists
load_env_file ".env"
# Handle clean-only mode
if [ "$CLEAN_ONLY" = true ]; then
log_info "Clean-only mode: cleaning build artifacts"
safe_execute "Cleaning Android app" "npm run clean:android" || exit 1
safe_execute "Cleaning dist directory" "clean_build_artifacts dist" || exit 1
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
log_success "Clean completed successfully!"
exit 0
fi
# Handle sync-only mode
if [ "$SYNC_ONLY" = true ]; then
log_info "Sync-only mode: syncing with Capacitor"
safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6
log_success "Sync completed successfully!"
exit 0
fi
# Handle assets-only mode
if [ "$ASSETS_ONLY" = true ]; then
log_info "Assets-only mode: generating assets"
safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7
log_success "Assets generation completed successfully!"
exit 0
fi
# Handle deploy-app mode
if [ "$DEPLOY_APP" = true ]; then
log_info "Deploy-app mode: building APK and deploying to device"
# Check for connected device
if ! adb devices | grep -q $'\tdevice'; then
log_error "No Android device connected. Please connect a device and try again."
exit 1
fi
# Build APK
safe_execute "Building APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5
# Install APK on device
safe_execute "Installing APK on device" "adb install -r android/app/build/outputs/apk/debug/app-debug.apk" || exit 6
log_success "APK deployed successfully to device!"
log_info "You can now run the app with: npx cap run android"
exit 0
fi
# Step 1: Validate asset configuration
safe_execute "Validating asset configuration" "npm run assets:validate" || {
log_warn "Asset validation found issues, but continuing with build..."
log_info "If you encounter build failures, please run 'npm install' first to ensure all dependencies are available."
}
# Step 2: Clean Android app
safe_execute "Cleaning Android app" "npm run clean:android" || exit 1
# Step 3: Clean dist directory
log_info "Cleaning dist directory..."
clean_build_artifacts "dist"
# Step 4: Build Capacitor version with mode
if [ "$BUILD_MODE" = "development" ]; then
safe_execute "Building Capacitor version (development)" "npm run build:capacitor" || exit 3
elif [ "$BUILD_MODE" = "test" ]; then
safe_execute "Building Capacitor version (test)" "npm run build:capacitor -- --mode test" || exit 3
elif [ "$BUILD_MODE" = "production" ]; then
safe_execute "Building Capacitor version (production)" "npm run build:capacitor -- --mode production" || exit 3
fi
# Step 5: Clean Gradle build
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
# Step 6: Build based on type
if [ "$BUILD_TYPE" = "debug" ]; then
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5
elif [ "$BUILD_TYPE" = "release" ]; then
safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease && cd .." || exit 5
fi
# Step 7: Sync with Capacitor
safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6
# Step 8: Generate assets
safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7
# Step 9: Build APK/AAB if requested
if [ "$BUILD_APK" = true ]; then
if [ "$BUILD_TYPE" = "debug" ]; then
safe_execute "Building debug APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5
else
safe_execute "Building release APK" "cd android && ./gradlew assembleRelease && cd .." || exit 5
fi
fi
if [ "$BUILD_AAB" = true ]; then
safe_execute "Building AAB" "cd android && ./gradlew bundleRelease && cd .." || exit 5
fi
# Step 10: Auto-run app if requested
if [ "$AUTO_RUN" = true ]; then
log_step "Auto-running Android app..."
safe_execute "Launching app" "npx cap run android" || {
log_error "Failed to launch Android app"
log_info "You can manually run with: npx cap run android"
exit 9
}
log_success "Android app launched successfully!"
fi
# Step 11: Open Android Studio if requested
if [ "$OPEN_STUDIO" = true ]; then
safe_execute "Opening Android Studio" "npx cap open android" || exit 8
fi
# Print build summary
log_success "Android build completed successfully!"
log_info "Build mode: $BUILD_MODE"
log_info "Build type: $BUILD_TYPE"
if [ "$BUILD_APK" = true ]; then
log_info "APK build: completed"
fi
if [ "$BUILD_AAB" = true ]; then
log_info "AAB build: completed"
fi
if [ "$AUTO_RUN" = true ]; then
log_info "Auto-run: completed"
fi
if [ "$OPEN_STUDIO" = true ]; then
log_info "Android Studio: opened"
fi
# Reminder about dependency management
log_info "💡 Tip: If you encounter dependency issues, run 'npm install' to ensure all packages are up to date."
print_footer "Android Build"
# Exit with success
exit 0