RebreakVpnService.onStartCommand crashed with SecurityException because Android 16's validateForegroundServiceType rejects the implicit 2-arg startForeground(). Now passes FOREGROUND_SERVICE_TYPE_SPECIAL_USE explicitly (Google's documented best practice) and guards the call so a failed foreground promotion stops the service cleanly instead of crashing the app. Verified vs reported Galaxy A54 / Android 16 signature (97% of crash events, 1-user crash loop). Bundles pending working-tree work across native/marketing/locales/mac + graphify-out rebuild. gitignore: google-services.json + /screenshots/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
760 lines
26 KiB
Bash
Executable File
760 lines
26 KiB
Bash
Executable File
#!/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 mobile Auto-detect angeschl. iPhone + Android via USB,
|
||
# baut+launcht auf BEIDEN parallel mit shared Metro
|
||
# ./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
|
||
# ./dev.sh magic Build + Launch RebreakMagic.app (macOS Wizard)
|
||
#
|
||
# 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 (mobile):
|
||
# --no-build Beide nur Metro/Install (kein Native-Rebuild)
|
||
# --ios-only Nur iOS bauen (falls Android-Device da aber ignorieren)
|
||
# --android-only Nur Android bauen
|
||
#
|
||
# FLAGS (metro):
|
||
# --keep Cache behalten (kein --clear)
|
||
#
|
||
# FLAGS (clean):
|
||
# --build + iOS build am Ende
|
||
# --xcode + Xcode öffnen am Ende
|
||
#
|
||
# FLAGS (magic):
|
||
# --no-build Nur launchen (App muss schon gebaut sein)
|
||
# --xcode Nur Xcode-Projekt generieren + öffnen
|
||
# --debug Debug-Konfiguration statt Release
|
||
# --no-launch Build, aber App nicht öffnen
|
||
#
|
||
# 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"
|
||
MAGIC_DIR="$SCRIPT_DIR/../rebreak-magic-mac"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
# 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://<LAN-IP>: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"
|
||
}
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Device-Detection Helpers
|
||
# ---------------------------------------------------------------------------
|
||
|
||
detect_ios_device() {
|
||
# Gibt UDID des ersten ONLINE iPhone/iPad zurück, leer wenn keins
|
||
command -v xcrun >/dev/null 2>&1 || { echo ""; return 0; }
|
||
{ xcrun xctrace list devices 2>/dev/null \
|
||
| awk '/^== Devices ==/{f=1; next} /^== /{f=0} f' \
|
||
| grep -E "iPhone|iPad" \
|
||
| grep -v Simulator \
|
||
| head -1 \
|
||
| sed -nE 's/.*\(([0-9A-Fa-f-]{25,})\).*/\1/p'; } || true
|
||
}
|
||
|
||
detect_ios_device_name() {
|
||
command -v xcrun >/dev/null 2>&1 || { echo ""; return 0; }
|
||
{ xcrun xctrace list devices 2>/dev/null \
|
||
| awk '/^== Devices ==/{f=1; next} /^== /{f=0} f' \
|
||
| grep -E "iPhone|iPad" \
|
||
| grep -v Simulator \
|
||
| head -1 \
|
||
| sed -E 's/ *\(.*//'; } || true
|
||
}
|
||
|
||
detect_android_devices() {
|
||
# Gibt alle ADB-Device-IDs (eine pro Zeile)
|
||
command -v adb >/dev/null 2>&1 || return 0
|
||
{ adb devices 2>/dev/null | awk '/\tdevice$/ {print $1}'; } || true
|
||
}
|
||
|
||
start_shared_metro() {
|
||
local LOG="/tmp/rebreak-metro.log"
|
||
log "Killing alte Metro-Instanzen auf 8081..."
|
||
lsof -ti:8081 2>/dev/null | xargs kill -9 2>/dev/null || true
|
||
pkill -f "expo start" 2>/dev/null || true
|
||
sleep 1
|
||
log "Starte Metro im Hintergrund → $LOG"
|
||
(cd "$SCRIPT_DIR" && nohup pnpm expo start --dev-client --clear > "$LOG" 2>&1 &)
|
||
# Warte auf Metro-Bereitschaft
|
||
local tries=0
|
||
while [[ $tries -lt 30 ]]; do
|
||
if curl -s http://localhost:8081/status 2>/dev/null | grep -q packager-status:running; then
|
||
ok "Metro bereit (http://localhost:8081)"
|
||
return 0
|
||
fi
|
||
sleep 1
|
||
tries=$((tries+1))
|
||
done
|
||
warn "Metro-Statuscheck nicht bestätigt nach 30s — trotzdem weiter (Log: $LOG)"
|
||
}
|
||
|
||
cmd_mobile() {
|
||
local BUILD=true
|
||
local IOS_ONLY=false
|
||
local ANDROID_ONLY=false
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--no-build) BUILD=false; shift ;;
|
||
--ios-only) IOS_ONLY=true; shift ;;
|
||
--android-only) ANDROID_ONLY=true; shift ;;
|
||
*) die "Unbekannter Flag für 'mobile': $1" ;;
|
||
esac
|
||
done
|
||
|
||
section "Mobile Dev (Auto-Detect iOS + Android)"
|
||
|
||
# ── Detect ────────────────────────────────────────────────
|
||
local IOS_UDID=""
|
||
local IOS_NAME=""
|
||
local ANDROID_IDS=()
|
||
|
||
# set +e block: detection-Funktionen dürfen leer zurückkommen ohne Script-Abort
|
||
set +e
|
||
if ! $ANDROID_ONLY; then
|
||
IOS_UDID="$(detect_ios_device)"
|
||
IOS_NAME="$(detect_ios_device_name)"
|
||
fi
|
||
if ! $IOS_ONLY; then
|
||
while IFS= read -r line; do
|
||
[[ -n "$line" ]] && ANDROID_IDS+=("$line")
|
||
done < <(detect_android_devices)
|
||
fi
|
||
set -e
|
||
|
||
echo ""
|
||
if [[ -n "$IOS_UDID" ]]; then
|
||
ok "iOS: ${IOS_NAME:-iPhone} (${IOS_UDID})"
|
||
else
|
||
warn "Kein iOS-Device via USB gefunden"
|
||
fi
|
||
if [[ ${#ANDROID_IDS[@]} -gt 0 ]]; then
|
||
for id in "${ANDROID_IDS[@]}"; do
|
||
local model
|
||
model=$(adb -s "$id" shell getprop ro.product.model 2>/dev/null | tr -d '\r')
|
||
ok "Android: ${model:-unknown} ($id)"
|
||
done
|
||
else
|
||
warn "Kein Android-Device via ADB gefunden"
|
||
fi
|
||
echo ""
|
||
|
||
if [[ -z "$IOS_UDID" && ${#ANDROID_IDS[@]} -eq 0 ]]; then
|
||
die "Keine Devices erkannt — USB-Kabel checken, Trust-Dialog auf iPhone, USB-Debugging auf Android"
|
||
fi
|
||
|
||
# ── Shared Metro ──────────────────────────────────────────
|
||
start_shared_metro
|
||
|
||
local IOS_LOG="/tmp/rebreak-ios-build.log"
|
||
local ANDROID_LOG="/tmp/rebreak-android-build.log"
|
||
local IOS_PID=""
|
||
local ANDROID_PID=""
|
||
|
||
# ── iOS Build (parallel, im Hintergrund) ──────────────────
|
||
if [[ -n "$IOS_UDID" ]]; then
|
||
if $BUILD; then
|
||
log "iOS-Build startet → $IOS_LOG"
|
||
(
|
||
cd "$SCRIPT_DIR"
|
||
pnpm expo run:ios --device "$IOS_UDID" --no-bundler
|
||
) > "$IOS_LOG" 2>&1 &
|
||
IOS_PID=$!
|
||
else
|
||
log "iOS — skip Build (--no-build), App muss installiert sein"
|
||
fi
|
||
fi
|
||
|
||
# ── Android Build (parallel) ──────────────────────────────
|
||
if [[ ${#ANDROID_IDS[@]} -gt 0 ]]; then
|
||
if $BUILD; then
|
||
log "Android-Build startet → $ANDROID_LOG"
|
||
(
|
||
cd "$ANDROID_DIR"
|
||
./gradlew assembleDebug --console=plain
|
||
local APK="$ANDROID_DIR/app/build/outputs/apk/debug/app-debug.apk"
|
||
[[ -f "$APK" ]] || { echo "APK nicht gefunden"; exit 1; }
|
||
for id in "${ANDROID_IDS[@]}"; do
|
||
echo "→ install on $id"
|
||
adb -s "$id" install -r -d "$APK"
|
||
adb -s "$id" shell monkey -p org.rebreak.app -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || true
|
||
done
|
||
) > "$ANDROID_LOG" 2>&1 &
|
||
ANDROID_PID=$!
|
||
else
|
||
log "Android — skip Build (--no-build), APK muss installiert sein"
|
||
fi
|
||
fi
|
||
|
||
# ── Live-Logs follow ──────────────────────────────────────
|
||
echo ""
|
||
log "Builds laufen parallel. Live-Logs:"
|
||
[[ -n "$IOS_PID" ]] && echo " iOS : tail -f $IOS_LOG (pid $IOS_PID)"
|
||
[[ -n "$ANDROID_PID" ]] && echo " Android : tail -f $ANDROID_LOG (pid $ANDROID_PID)"
|
||
echo " Metro : tail -f /tmp/rebreak-metro.log"
|
||
echo ""
|
||
|
||
local IOS_RC=0
|
||
local ANDROID_RC=0
|
||
if [[ -n "$IOS_PID" ]]; then
|
||
log "Warte auf iOS-Build..."
|
||
wait "$IOS_PID" && IOS_RC=$? || IOS_RC=$?
|
||
if [[ $IOS_RC -eq 0 ]]; then ok "iOS-Build fertig"; else error "iOS-Build FAIL (rc=$IOS_RC) — $IOS_LOG"; fi
|
||
fi
|
||
if [[ -n "$ANDROID_PID" ]]; then
|
||
log "Warte auf Android-Build..."
|
||
wait "$ANDROID_PID" && ANDROID_RC=$? || ANDROID_RC=$?
|
||
if [[ $ANDROID_RC -eq 0 ]]; then ok "Android-Build fertig"; else error "Android-Build FAIL (rc=$ANDROID_RC) — $ANDROID_LOG"; fi
|
||
fi
|
||
|
||
echo ""
|
||
ok "Mobile-Dev bereit. Metro läuft im Hintergrund (kill via: lsof -ti:8081 | xargs kill)."
|
||
echo "Metro-Log live: tail -f /tmp/rebreak-metro.log"
|
||
}
|
||
|
||
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"
|
||
}
|
||
|
||
cmd_magic() {
|
||
local BUILD=true
|
||
local LAUNCH=true
|
||
local XCODE_ONLY=false
|
||
local CONFIGURATION="Release"
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--no-build) BUILD=false; shift ;;
|
||
--no-launch) LAUNCH=false; shift ;;
|
||
--xcode) XCODE_ONLY=true; shift ;;
|
||
--debug) CONFIGURATION="Debug"; shift ;;
|
||
*) die "Unbekannter Flag für 'magic': $1" ;;
|
||
esac
|
||
done
|
||
|
||
section "RebreakMagic (macOS Wizard)"
|
||
|
||
[[ -d "$MAGIC_DIR" ]] || die "rebreak-magic-mac nicht gefunden: $MAGIC_DIR"
|
||
command -v xcodebuild >/dev/null 2>&1 || die "xcodebuild fehlt — xcode-select --install"
|
||
|
||
cd "$MAGIC_DIR"
|
||
|
||
# Xcode-Projekt aus project.yml generieren (idempotent)
|
||
if command -v xcodegen >/dev/null 2>&1; then
|
||
log "xcodegen generate"
|
||
xcodegen generate >/dev/null
|
||
else
|
||
warn "xcodegen nicht installiert — überspringe project-regeneration (brew install xcodegen)"
|
||
fi
|
||
|
||
if $XCODE_ONLY; then
|
||
log "Opening Xcode..."
|
||
open -a Xcode "$MAGIC_DIR/RebreakMagic.xcodeproj"
|
||
ok "Xcode geöffnet — Cmd+R für Build & Run"
|
||
return 0
|
||
fi
|
||
|
||
local BUILD_DIR="$MAGIC_DIR/build"
|
||
local APP_PATH="$BUILD_DIR/Build/Products/$CONFIGURATION/RebreakMagic.app"
|
||
|
||
if $BUILD; then
|
||
log "xcodebuild ($CONFIGURATION)"
|
||
xcodebuild \
|
||
-project RebreakMagic.xcodeproj \
|
||
-scheme RebreakMagic \
|
||
-configuration "$CONFIGURATION" \
|
||
-derivedDataPath "$BUILD_DIR" \
|
||
build | tail -20
|
||
[[ -d "$APP_PATH" ]] || die "Build fehlgeschlagen — $APP_PATH nicht gefunden"
|
||
ok "Build erfolgreich: $APP_PATH"
|
||
else
|
||
[[ -d "$APP_PATH" ]] || die "App nicht vorhanden ($APP_PATH) — ohne --no-build neu starten"
|
||
fi
|
||
|
||
# Config-Check
|
||
local CONFIG_FILE="$HOME/.config/rebreak-magic/config.json"
|
||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||
warn "Config fehlt: $CONFIG_FILE"
|
||
echo " → Template kopieren: cp $MAGIC_DIR/config.example.json $CONFIG_FILE"
|
||
echo " → Dann editieren: backendBaseUrl=https://staging.rebreak.org"
|
||
fi
|
||
|
||
if $LAUNCH; then
|
||
log "Killing running RebreakMagic instance..."
|
||
pkill -x RebreakMagic 2>/dev/null || true
|
||
sleep 0.5
|
||
log "Launching $APP_PATH"
|
||
open "$APP_PATH"
|
||
ok "RebreakMagic gestartet"
|
||
fi
|
||
}
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
# Main
|
||
# ═══════════════════════════════════════════════════════════════════════════
|
||
|
||
COMMAND="${1:-ios}"
|
||
shift || true
|
||
|
||
# Disk-Guard: warnt vor Builds, wenn der Mac wenig freien Speicher hat (clean.sh).
|
||
[ -x "$SCRIPT_DIR/clean.sh" ] && "$SCRIPT_DIR/clean.sh" --guard 8 || true
|
||
|
||
case "$COMMAND" in
|
||
ios)
|
||
cmd_ios "$@"
|
||
;;
|
||
|
||
android)
|
||
cmd_android "$@"
|
||
;;
|
||
|
||
mobile)
|
||
cmd_mobile "$@"
|
||
;;
|
||
|
||
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
|
||
;;
|
||
|
||
magic)
|
||
cmd_magic "$@"
|
||
;;
|
||
|
||
-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 " mobile Auto-detect iPhone+Android via USB, parallel-Build"
|
||
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 " magic Build + Launch RebreakMagic.app (macOS)"
|
||
echo ""
|
||
echo "Nutze --help für Details"
|
||
|
||
exit 1
|
||
;;
|
||
esac
|