#!/bin/bash # dev.sh — ReBreak Native Development Tooling # # Konsolidiert: dev-ios.sh + dev-iphone.sh + metro.sh (alle gelöscht). # # SUBCOMMANDS: # ./dev.sh default: ios --device (physisches iPhone USB + Build) # ./dev.sh ios iOS Dev (Default: USB-Device mit Build) # ./dev.sh android Android Dev (Gradle Build + Install + Launch) # ./dev.sh metro Nur Metro starten # ./dev.sh clean iOS: Nuclear clean (Pods, DerivedData, Archives) # ./dev.sh install ios Build Release + Install auf iPhone USB # ./dev.sh install android Build Debug APK + Install auf Android Device # # FLAGS (ios): # --device Build auf physisches iPhone via USB (DEFAULT) # --simulator Build auf iOS Simulator # --xcode Nur Xcode öffnen (manueller Build) # --wifi Metro mit --host lan (WiFi-Dev, KEIN Native-Build) # --no-build KEIN Native-Rebuild → nur Metro starten # (für schnellen UI/JS-Reload — App muss installiert sein) # # FLAGS (android): # --no-build KEIN Gradle-Rebuild → nur Metro starten # (für schnellen UI/JS-Reload — APK muss installiert sein) # --no-launch Build+Install, aber kein Auto-Launch # --wifi Metro mit --host lan (nur in Kombi mit --no-build) # # FLAGS (metro): # --keep Cache behalten (kein --clear) # # FLAGS (clean): # --build + iOS build am Ende # --xcode + Xcode öffnen am Ende # # BEISPIELE: # # Default: iPhone USB + Native-Build: # ./dev.sh # # # Schneller UI-Loop (App schon installiert, nur JS-Reload): # ./dev.sh ios --no-build # ./dev.sh android --no-build # # # WiFi-Dev (Kabel ab, Metro über LAN): # ./dev.sh ios --wifi # # # iOS Simulator: # ./dev.sh ios --simulator # # # Nur Xcode öffnen: # ./dev.sh ios --xcode # # # iOS Clean + Rebuild: # ./dev.sh clean --build # # # Release-Build auf iPhone installieren: # ./dev.sh install ios set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" IOS_DIR="$SCRIPT_DIR/ios" ANDROID_DIR="$SCRIPT_DIR/android" # ═══════════════════════════════════════════════════════════════════════════ # Color Output # ═══════════════════════════════════════════════════════════════════════════ if [[ -t 1 ]]; then BOLD=$(tput bold) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) RED=$(tput setaf 1) BLUE=$(tput setaf 4) RESET=$(tput sgr0) else BOLD="" GREEN="" YELLOW="" RED="" BLUE="" RESET="" fi log() { echo "${BLUE}==>${RESET} ${BOLD}$*${RESET}"; } ok() { echo "${GREEN}✓${RESET} $*"; } warn() { echo "${YELLOW}⚠${RESET} $*" >&2; } error() { echo "${RED}✗${RESET} ${BOLD}$*${RESET}" >&2; } die() { error "$*"; exit 1; } section() { echo "" echo "${BOLD}$*${RESET}" echo "${BOLD}────────────────────────────────────────────────────────────${RESET}" } # ═══════════════════════════════════════════════════════════════════════════ # ENV Defaults # ═══════════════════════════════════════════════════════════════════════════ export REBREAK_ENABLE_FAMILY_CONTROLS="${REBREAK_ENABLE_FAMILY_CONTROLS:-1}" export EXPO_PUBLIC_ENABLE_DEBUG="${EXPO_PUBLIC_ENABLE_DEBUG:-1}" export REBREAK_DEV="${REBREAK_DEV:-0}" # ═══════════════════════════════════════════════════════════════════════════ # Commands # ═══════════════════════════════════════════════════════════════════════════ cmd_ios() { local MODE="device" # Default: physisches iPhone via USB local WIFI=false local BUILD=true # Default: nativen Build laufen lassen while [[ $# -gt 0 ]]; do case "$1" in --device) MODE="device"; shift ;; --simulator) MODE="simulator"; shift ;; --xcode) MODE="xcode"; shift ;; --wifi) WIFI=true; shift ;; --no-build) BUILD=false; shift ;; *) die "Unbekannter Flag für 'ios': $1" ;; esac done section "iOS Dev Mode" # ───────────────────────────────────────────────────────────── # --no-build / WiFi: KEIN Native-Rebuild, nur Metro + Dev-Client # → schnellster Loop für reine UI/JS-Änderungen # → App muss schon auf dem Device/Simulator installiert sein # ───────────────────────────────────────────────────────────── if ! $BUILD || $WIFI; then local HOST_FLAG="" if $WIFI; then HOST_FLAG="--host lan" log "Metro: WiFi-Modus (--host lan)" echo "" echo "Mac LAN-IP:" ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || echo " (kein WiFi/Ethernet detected)" echo "" echo "Falls dev-client Metro nicht automatisch findet:" echo " im iPhone-Launcher → 'Enter URL manually' → http://:8081" else log "Metro: USB/Local-Modus (kein Native-Rebuild)" echo "App auf Device/Simulator muss schon installiert sein." echo "Beim Öffnen connected dev-client automatisch zu Metro." fi echo "" log "Killing old Metro on port 8081..." lsof -ti:8081 2>/dev/null | xargs kill -9 2>/dev/null || true pkill -f "expo start" 2>/dev/null || true echo "" exec pnpm expo start $HOST_FLAG --clear --dev-client fi case "$MODE" in xcode) log "Opening Xcode Workspace..." osascript -e 'tell application "Xcode" to close every window whose name contains "Rebreak.xcodeproj"' 2>/dev/null || true open -a Xcode "$IOS_DIR/ReBreak.xcworkspace" ok "Xcode geöffnet — Cmd+R für Build & Run" echo "" echo "ℹ️ Metro: separat starten via './dev.sh metro' falls noch nicht läuft" ;; device) log "Building für physisches iPhone (USB)..." echo "ℹ️ Für schnellen UI-Reload ohne Rebuild: './dev.sh ios --no-build'" pnpm expo run:ios --device ;; simulator) log "Building für iOS Simulator..." pnpm expo run:ios ;; esac } cmd_android() { local BUILD=true local LAUNCH=true local WIFI=false while [[ $# -gt 0 ]]; do case "$1" in --no-build) BUILD=false; shift ;; --no-launch) LAUNCH=false; shift ;; --wifi) WIFI=true; shift ;; *) die "Unbekannter Flag für 'android': $1" ;; esac done section "Android Dev Mode" # ───────────────────────────────────────────────────────────── # --no-build: KEIN Gradle-Rebuild, nur Metro + Dev-Client # → schnellster Loop für reine UI/JS-Änderungen # → APK muss schon auf dem Device installiert sein # ───────────────────────────────────────────────────────────── if ! $BUILD; then local HOST_FLAG="" if $WIFI; then HOST_FLAG="--host lan" log "Metro: WiFi-Modus (--host lan)" echo "" echo "Mac LAN-IP:" ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || echo " (kein WiFi/Ethernet detected)" else log "Metro: USB/ADB-Modus (kein Gradle-Rebuild)" echo "APK muss schon auf dem Device installiert sein." fi echo "" log "Killing old Metro on port 8081..." lsof -ti:8081 2>/dev/null | xargs kill -9 2>/dev/null || true pkill -f "expo start" 2>/dev/null || true echo "" exec pnpm expo start $HOST_FLAG --clear --dev-client fi command -v adb >/dev/null 2>&1 || die "adb nicht gefunden — brew install --cask android-platform-tools" local DEVICE_COUNT DEVICE_COUNT=$(adb devices | grep -E '[[:space:]]device$' | grep -c '.' || true) if [[ "$DEVICE_COUNT" -eq 0 ]]; then warn "Kein Android-Gerät via ADB verbunden" echo "" adb devices echo "" echo "Mögliche Ursachen:" echo " - USB nicht angeschlossen / Kabel nur Strom" echo " - USB-Debugging auf dem Phone aus" echo " - 'Diesem Computer vertrauen?' Dialog noch nicht bestätigt" exit 1 fi log "Building Debug APK..." echo "ℹ️ Für schnellen UI-Reload ohne Rebuild: './dev.sh android --no-build'" (cd "$ANDROID_DIR" && ./gradlew assembleDebug --console=plain) local APK="$ANDROID_DIR/app/build/outputs/apk/debug/app-debug.apk" [[ -f "$APK" ]] || die "APK nicht gefunden: $APK" log "Installing APK..." adb install -r -d "$APK" if $LAUNCH; then log "Launching App..." adb shell monkey -p org.rebreak.app -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || { warn "Launch via monkey schlug fehl — App ist installiert, manuell öffnen" } fi ok "Android Dev Build abgeschlossen" } cmd_metro() { local CLEAR_FLAG="--clear" while [[ $# -gt 0 ]]; do case "$1" in --keep) CLEAR_FLAG=""; shift ;; *) die "Unbekannter Flag für 'metro': $1" ;; esac done section "Metro Bundler" log "Killing existing Metro on port 8081..." lsof -iTCP:8081 -sTCP:LISTEN -n -P 2>/dev/null | awk 'NR>1 {print $2}' | sort -u | xargs kill -9 2>/dev/null || true pkill -f "expo start" 2>/dev/null || true pkill -f "react-native/cli/build" 2>/dev/null || true if [[ -n "$CLEAR_FLAG" ]]; then log "Starting Metro mit --clear (Cache reset)..." else log "Starting Metro (Cache behalten)..." fi exec npx expo start $CLEAR_FLAG } cmd_clean() { local POST_ACTION="" while [[ $# -gt 0 ]]; do case "$1" in --build) POST_ACTION="build"; shift ;; --xcode) POST_ACTION="xcode"; shift ;; *) die "Unbekannter Flag für 'clean': $1" ;; esac done section "iOS Nuclear Clean" log "rm -rf ios/Pods ios/Podfile.lock ios/build" rm -rf "$IOS_DIR/Pods" "$IOS_DIR/Podfile.lock" "$IOS_DIR/build" local DERIVED_DATA="$HOME/Library/Developer/Xcode/DerivedData" if [[ -d "$DERIVED_DATA" ]]; then log "rm -rf DerivedData/Rebreak-*" rm -rf "$DERIVED_DATA"/Rebreak-* 2>/dev/null || true fi local ARCHIVES_DIR="$HOME/Library/Developer/Xcode/Archives" if [[ -d "$ARCHIVES_DIR" ]]; then local before_count before_count=$(find "$ARCHIVES_DIR" -maxdepth 2 -type d -iname "*rebreak*.xcarchive" 2>/dev/null | wc -l | tr -d ' ') log "Cleaning Xcode Archives (Rebreak, >24h) — vorher: $before_count Stk" find "$ARCHIVES_DIR" -maxdepth 2 -type d -iname "*rebreak*.xcarchive" -mtime +1 -exec rm -rf {} + 2>/dev/null || true find "$ARCHIVES_DIR" -maxdepth 1 -type d -empty -delete 2>/dev/null || true fi log "pnpm expo prebuild --clean" pnpm expo prebuild --clean log "cd ios && pod install" (cd "$IOS_DIR" && pod install) ok "Clean abgeschlossen" case "$POST_ACTION" in build) echo "" log "Building + Running..." pnpm ios ;; xcode) echo "" log "Opening Xcode..." osascript -e 'tell application "Xcode" to close every window whose name contains "Rebreak.xcodeproj"' 2>/dev/null || true open -a Xcode "$IOS_DIR/ReBreak.xcworkspace" ok "Xcode geöffnet — Cmd+R für Build & Run" ;; "") echo "" echo "Nächste Schritte:" echo " ./dev.sh ios # Build & Run" echo " ./dev.sh ios --xcode # Xcode öffnen" ;; esac } cmd_install_ios() { section "iOS Standalone Install" local CONFIGURATION="Release" command -v xcrun >/dev/null 2>&1 || die "Xcode Command-Line-Tools fehlen — xcode-select --install" local XCTRACE_OUT XCTRACE_OUT=$(xcrun xctrace list devices 2>&1) local ONLINE ONLINE=$(printf '%s\n' "$XCTRACE_OUT" \ | awk '/^== Devices ==/{f=1; next} /^== /{f=0} f' \ | grep -E "iPhone|iPad" || true) if [[ -z "$ONLINE" ]]; then error "Kein iPhone/iPad ONLINE" echo "" echo "Setup:" echo " - iPhone via USB anschließen + entsperren" echo " - 'Diesem Computer vertrauen?' am iPhone bestätigen" echo " - Xcode öffnen, dort 'Use for Development' aktivieren" exit 1 fi log "Gerät online: $(printf '%s\n' "$ONLINE" | head -1)" log "Building iOS Release bundle + installing on device..." echo "" echo "(Erster Release-Build dauert 5-10 min wegen Pod-Install + Bundle)" echo "" npx expo run:ios --device --configuration "$CONFIGURATION" ok "App läuft jetzt standalone auf deinem iPhone" echo "Backend: https://staging.rebreak.org" echo "Free-Account: 7 Tage gültig, danach Skript erneut laufen lassen" } cmd_install_android() { section "Android Standalone Install" local SKIP_BUILD=false local LAUNCH=true while [[ $# -gt 0 ]]; do case "$1" in --no-build) SKIP_BUILD=true; shift ;; --no-launch) LAUNCH=false; shift ;; *) die "Unbekannter Flag für 'install android': $1" ;; esac done command -v adb >/dev/null 2>&1 || die "adb nicht gefunden — brew install --cask android-platform-tools" local DEVICE_COUNT DEVICE_COUNT=$(adb devices | grep -E '[[:space:]]device$' | grep -c '.' || true) if [[ "$DEVICE_COUNT" -eq 0 ]]; then error "Kein Android-Gerät via ADB" adb devices exit 1 fi local APK="$ANDROID_DIR/app/build/outputs/apk/debug/app-debug.apk" if ! $SKIP_BUILD; then log "Building debug APK..." (cd "$ANDROID_DIR" && ./gradlew assembleDebug --console=plain) fi [[ -f "$APK" ]] || die "APK nicht gefunden: $APK" log "Installing $APK..." adb install -r -d "$APK" if $LAUNCH; then log "Launching App..." adb shell monkey -p org.rebreak.app -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || { warn "Launch schlug fehl — App ist installiert, manuell öffnen" } fi ok "Fertig" } # ═══════════════════════════════════════════════════════════════════════════ # Main # ═══════════════════════════════════════════════════════════════════════════ COMMAND="${1:-ios}" shift || true case "$COMMAND" in ios) cmd_ios "$@" ;; android) cmd_android "$@" ;; metro) cmd_metro "$@" ;; clean) cmd_clean "$@" ;; install) local PLATFORM="${1:-}" shift || true case "$PLATFORM" in ios) cmd_install_ios "$@" ;; android) cmd_install_android "$@" ;; *) error "Unbekannte install-Platform: $PLATFORM" echo "Verfügbar: ios, android" exit 1 ;; esac ;; -h|--help) awk '/^#!/{next} /^#/{sub(/^# ?/, ""); print; next} {exit}' "$0" exit 0 ;; *) error "Unbekanntes Subcommand: $COMMAND" echo "" echo "Verfügbare Commands:" echo " ios iOS Dev (Metro + Xcode/Simulator/Device)" echo " android Android Dev (Metro + Gradle + Install)" echo " metro Nur Metro starten" echo " clean iOS Nuclear Clean" echo " install ios Release-Build auf iPhone installieren" echo " install android Debug-APK auf Android installieren" echo "" echo "Nutze --help für Details" exit 1 ;; esac