#!/bin/bash # # Desktop Build Script Template (Tauri) # Builds the Tauri desktop app for macOS # # Usage: # ./build.sh -dev # Development build (runs from build folder) # ./build.sh -dev -open # Development build and open app # ./build.sh -prod # Production build (builds to build/prod/) # ./build.sh -install # Build and open Finder for drag-to-Applications # ./build.sh clean # Clean build folder (preserves logs) # ./build.sh clean-logs # Clean all logs # ./build.sh logs # View latest console log # # ============================================================ # PROJECT CONFIGURATION - Customize these for your project # ============================================================ APP_NAME="TauriStarter" # Display name of the app BUNDLE_ID_BASE="com.tauristarter.app" # Base bundle ID VERSION="0.1.0" # App version BINARY_NAME="TauriStarter" # Rust binary name (matches productName in tauri.conf.json) # ============================================================ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${BLUE}[Build]${NC} $1"; } success() { echo -e "${GREEN}[Build]${NC} $1"; } warn() { echo -e "${YELLOW}[Build]${NC} $1"; } error() { echo -e "${RED}[Build]${NC} $1"; exit 1; } # Paths TAURI_DIR="$SCRIPT_DIR/src-tauri" # Build folder structure BUILD_DIR="$SCRIPT_DIR/build" DEV_BUILD_DIR="$BUILD_DIR/dev" PROD_BUILD_DIR="$BUILD_DIR/prod" LOGS_DIR="$BUILD_DIR/logs" CONSOLE_DIR="$BUILD_DIR/console" PROD_INSTALL_DIR="$HOME/Applications" # Timestamp for log files TIMESTAMP=$(date +"%Y%m%d_%H%M%S") # Parse arguments BUILD_MODE="" OPEN_APP=false SHOW_INSTALL=false CLEAN_BUILD=false CLEAN_LOGS=false SHOW_LOGS=false SECURE_BUILD=false while [[ $# -gt 0 ]]; do case $1 in -prod|--prod|--production) BUILD_MODE="prod" shift ;; -dev|--dev|--development) BUILD_MODE="dev" shift ;; -open|--open) OPEN_APP=true shift ;; -install|--install) SHOW_INSTALL=true BUILD_MODE="prod" shift ;; -secure|--secure) SECURE_BUILD=true shift ;; clean|--clean) CLEAN_BUILD=true shift ;; clean-logs|--clean-logs) CLEAN_LOGS=true shift ;; logs|--logs) SHOW_LOGS=true shift ;; -h|--help) echo "$APP_NAME Build Script" echo "" echo "Usage: $0 [-dev|-prod] [-open] [-secure] [clean|clean-logs|logs]" echo "" echo "Build Commands:" echo " -dev, --dev Development build (runs from build folder)" echo " -prod, --prod Production build (builds to build/prod/)" echo " -install Build prod and open Finder for drag-to-Applications" echo " -open, --open Open the app after building" echo " -secure Build without automation (no HTTP server on port 9876)" echo "" echo "Other Commands:" echo " clean Clean build artifacts (preserves logs)" echo " clean-logs Remove all logs" echo " logs Show latest console log (last 100 lines)" echo " -h, --help Show this help" echo "" echo "Build folders:" echo " Dev: $DEV_BUILD_DIR" echo " Prod: $PROD_BUILD_DIR (also installs to $PROD_INSTALL_DIR)" echo " Logs: $LOGS_DIR" echo " Console: $CONSOLE_DIR" echo "" echo "Examples:" echo " ./build.sh -dev -open # Build dev and open app" echo " ./build.sh -prod # Build prod to build/prod/" echo " ./build.sh -prod -secure # Build prod without automation" echo " ./build.sh -install # Build and open Finder to drag to Applications" echo " ./build.sh logs # View latest console output" exit 0 ;; *) error "Unknown option: $1. Use --help for usage." ;; esac done # Handle logs command if [ "$SHOW_LOGS" = true ]; then if [ -f "$CONSOLE_DIR/console_latest.log" ]; then log "Latest console log:" echo "----------------------------------------" tail -100 "$CONSOLE_DIR/console_latest.log" else warn "No console logs found at $CONSOLE_DIR/console_latest.log" fi exit 0 fi # Handle clean-logs command if [ "$CLEAN_LOGS" = true ]; then log "Cleaning logs..." rm -rf "$LOGS_DIR" "$CONSOLE_DIR" success "Logs cleaned!" exit 0 fi # Handle clean command if [ "$CLEAN_BUILD" = true ]; then log "Cleaning build folder (preserving logs)..." # Backup logs if [ -d "$LOGS_DIR" ]; then mv "$LOGS_DIR" "$SCRIPT_DIR/.logs_backup" 2>/dev/null || true fi if [ -d "$CONSOLE_DIR" ]; then mv "$CONSOLE_DIR" "$SCRIPT_DIR/.console_backup" 2>/dev/null || true fi # Clean build dir rm -rf "$BUILD_DIR" # Clean other build artifacts rm -rf "$SCRIPT_DIR/dist" rm -rf "$SCRIPT_DIR/.quasar" rm -rf "$TAURI_DIR/target" # Restore logs mkdir -p "$BUILD_DIR" if [ -d "$SCRIPT_DIR/.logs_backup" ]; then mv "$SCRIPT_DIR/.logs_backup" "$LOGS_DIR" fi if [ -d "$SCRIPT_DIR/.console_backup" ]; then mv "$SCRIPT_DIR/.console_backup" "$CONSOLE_DIR" fi success "Build folder cleaned!" exit 0 fi # Require a build mode if not running a command if [ -z "$BUILD_MODE" ]; then error "No build mode specified. Use -dev or -prod. See --help for usage." fi # Create log directories mkdir -p "$LOGS_DIR" mkdir -p "$CONSOLE_DIR" # Set up logging BUILD_LOG="$LOGS_DIR/build_${BUILD_MODE}_$TIMESTAMP.log" CONSOLE_LOG="$CONSOLE_DIR/console_$TIMESTAMP.log" # Create symlinks to latest logs ln -sf "build_${BUILD_MODE}_$TIMESTAMP.log" "$LOGS_DIR/build_latest.log" ln -sf "console_$TIMESTAMP.log" "$CONSOLE_DIR/console_latest.log" # Logging function that writes to both console and file log_both() { echo -e "${BLUE}[Build]${NC} $1" echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$BUILD_LOG" } # Set paths based on build mode if [ "$BUILD_MODE" = "prod" ]; then APP_BUNDLE="$PROD_BUILD_DIR/$APP_NAME.app" INSTALL_BUNDLE="$PROD_INSTALL_DIR/$APP_NAME.app" BUNDLE_ID="$BUNDLE_ID_BASE" else APP_BUNDLE="$DEV_BUILD_DIR/$APP_NAME.app" INSTALL_BUNDLE="" BUNDLE_ID="${BUNDLE_ID_BASE}.dev" fi log_both "==========================================" log_both "Building $APP_NAME ($BUILD_MODE mode)" log_both "==========================================" log_both "Build log: $BUILD_LOG" log_both "Console log: $CONSOLE_LOG" # Create build directories mkdir -p "$DEV_BUILD_DIR" mkdir -p "$PROD_BUILD_DIR" # Kill existing instances before building log_both "Stopping existing instances..." pkill -f "$DEV_BUILD_DIR/$APP_NAME.app/Contents/MacOS" 2>/dev/null || true pkill -f "$PROD_BUILD_DIR/$APP_NAME.app/Contents/MacOS" 2>/dev/null || true pkill -f "$PROD_INSTALL_DIR/$APP_NAME.app/Contents/MacOS" 2>/dev/null || true sleep 1 # Check dependencies command -v node >/dev/null 2>&1 || error "Node.js is required but not installed" command -v cargo >/dev/null 2>&1 || error "Rust/Cargo is required but not installed" # Step 1: Install npm dependencies if needed if [ ! -d "$SCRIPT_DIR/node_modules" ]; then log_both "Installing npm dependencies..." npm install 2>&1 | tee -a "$BUILD_LOG" fi # Step 2: Build with Tauri (builds frontend + Rust + creates bundle) log_both "Building with Tauri (this embeds frontend into binary)..." cd "$SCRIPT_DIR" # Build cargo args for secure mode (no automation) CARGO_ARGS="" if [ "$SECURE_BUILD" = true ]; then log_both "Secure build: disabling automation feature..." CARGO_ARGS="-- --no-default-features --features custom-protocol" fi if [ "$SHOW_INSTALL" = true ]; then # No --ci flag: opens Finder window for drag-to-Applications npx tauri build $CARGO_ARGS 2>&1 | tee -a "$BUILD_LOG" else # --ci flag: suppress Finder window npx tauri build --ci $CARGO_ARGS 2>&1 | tee -a "$BUILD_LOG" fi # Find the generated app bundle TAURI_BUNDLE_DIR="$TAURI_DIR/target/release/bundle/macos" TAURI_APP_BUNDLE="$TAURI_BUNDLE_DIR/$APP_NAME.app" if [ ! -d "$TAURI_APP_BUNDLE" ]; then error "Tauri build failed - app bundle not found at $TAURI_APP_BUNDLE" fi # Step 3: Copy the Tauri-built bundle to our build directory log_both "Copying app bundle to $APP_BUNDLE..." rm -rf "$APP_BUNDLE" cp -R "$TAURI_APP_BUNDLE" "$APP_BUNDLE" # Step 4: Update Info.plist with our bundle ID (dev vs prod) log_both "Updating Info.plist with bundle ID: $BUNDLE_ID..." if [ "$BUILD_MODE" = "dev" ]; then DISPLAY_NAME="$APP_NAME (Dev)" else DISPLAY_NAME="$APP_NAME" fi # Use PlistBuddy to update the plist /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_ID" "$APP_BUNDLE/Contents/Info.plist" /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $DISPLAY_NAME" "$APP_BUNDLE/Contents/Info.plist" 2>/dev/null || \ /usr/libexec/PlistBuddy -c "Add :CFBundleDisplayName string $DISPLAY_NAME" "$APP_BUNDLE/Contents/Info.plist" # Step 5: For prod, also install to ~/Applications if [ "$BUILD_MODE" = "prod" ] && [ -n "$INSTALL_BUNDLE" ]; then log_both "Installing to $INSTALL_BUNDLE..." pkill -f "$INSTALL_BUNDLE/Contents/MacOS" 2>/dev/null || true sleep 1 rm -rf "$INSTALL_BUNDLE" mkdir -p "$PROD_INSTALL_DIR" cp -R "$APP_BUNDLE" "$INSTALL_BUNDLE" touch "$INSTALL_BUNDLE" fi # Touch the app to update Finder cache touch "$APP_BUNDLE" # Build complete log_both "==========================================" success "Build Complete!" log_both "==========================================" echo "" echo " Build folder: $BUILD_DIR" echo " App bundle: $APP_BUNDLE" echo " Mode: $BUILD_MODE" echo " Bundle ID: $BUNDLE_ID" if [ "$SECURE_BUILD" = true ]; then echo " Automation: DISABLED (secure build)" else echo " Automation: enabled (port 9876)" fi if [ "$BUILD_MODE" = "prod" ] && [ -n "$INSTALL_BUNDLE" ]; then echo " Installed to: $INSTALL_BUNDLE" fi echo "" echo " Build log: $BUILD_LOG" echo " Console log: $CONSOLE_LOG" echo "" # Open the app and capture console output if [ "$OPEN_APP" = true ]; then log_both "Opening app (console output will be logged)..." # Run the app in background with output to console log "$APP_BUNDLE/Contents/MacOS/$BINARY_NAME" >> "$CONSOLE_LOG" 2>&1 & APP_PID=$! echo " App started (PID: $APP_PID)" echo " Console log: $CONSOLE_LOG" echo "" echo " View logs with: ./build.sh logs" else echo " To run: open \"$APP_BUNDLE\"" echo " Or with logging: \"$APP_BUNDLE/Contents/MacOS/$BINARY_NAME\" 2>&1 | tee -a \"$CONSOLE_LOG\"" fi echo ""