mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
This repo could never sync. There was NO merge-base with tamagui — 372 commits
here against 15,791 there, unrelated histories — because it began as a
copy-paste rebrand with a flattened layout. Every "sync" since was a hand
re-application that drifted, which is why the tree sat on 7.3.1 while upstream
shipped fixes we then rediscovered by hand.
This commit's PARENT IS TAMAGUI. A future sync is `git merge tama/main` then
`node scripts/rebrand.mjs` — a real merge, because the history is now shared.
174 packages: 166 upstream, our 8 restored (chrome, shell, telemetry, recipes,
get, font-geist-{sans,mono}, @hanzo/booker), bento-get gained. 170/170 build.
Our patches to files UPSTREAM ALSO OWNS are carried, which the package-level
delta missed and which was where the value actually was:
- useMedia's server snapshot (`initState ?? getMedia()`). Upstream still
returns undefined there, so every Tamagui Dialog kills a prerender — the
exact crash hanzo.app spent a day on.
- data-slot on web host nodes, absent upstream entirely.
Both applied ONTO upstream's newer files (it rewrote useMedia +150/-63 and
createComponent +160/-55), marked `PATCH (ours, carried across the transplant)`.
Seven traps are encoded in scripts/rebrand.mjs, each found by a gate:
skip itself and lockfiles; special-case the double-branded names in BOTH scoped
and bare form; map path strings, not just paths; drop a level for the flattened
group; check disk rather than a stale index.
Rust build artifacts are NOT committed. An earlier draft of this commit swept
apps/desktop/src-tauri/target/debug in via `git add -A` — 3,859 files including
a 272MB .a, over GitHub's 100MB cap, so the push could never have landed. They
are gitignored now.
Co-Authored-By: Hanzo Dev <[email protected]>
370 lines
9.6 KiB
Bash
Executable File
370 lines
9.6 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# run-detox.sh - Complete Detox E2E test runner
|
|
#
|
|
# Usage:
|
|
# bun run detox:run:ios # run all iOS tests
|
|
# bun run detox:run:ios "Sheet" # run tests matching "Sheet"
|
|
# bun run detox:run:android # run all Android tests
|
|
# bun run detox:run:android "Select" # run tests matching "Select"
|
|
#
|
|
# This script handles the full workflow:
|
|
# 1. Checks if pods need installing (iOS only)
|
|
# 2. Detects if app needs rebuilding (compares sources vs binary)
|
|
# 3. Builds if needed
|
|
# 4. Starts Metro if not running
|
|
# 5. Runs Detox tests
|
|
# 6. Cleans up Metro if we started it
|
|
#
|
|
# Environment variables:
|
|
# FORCE_BUILD=1 - Force rebuild even if app exists
|
|
# SKIP_BUILD=1 - Skip build check entirely
|
|
# SKIP_METRO=1 - Don't start Metro (assume it's running)
|
|
# DETOX_DEVICE=name - iOS simulator device type (default: iPhone 16)
|
|
# DETOX_METRO_PORT=n - Metro port reserved for Detox (default: 8082 on iOS, 8081 on Android)
|
|
#
|
|
|
|
set -e
|
|
|
|
PLATFORM="${1:-ios}" # ios or android
|
|
# dedicated detox metro port - non-default to avoid colliding with other dev servers.
|
|
# Android side: see android/app/src/main/res/values/integers.xml (react_native_dev_server_port)
|
|
DEFAULT_DETOX_METRO_PORT=9034
|
|
export DETOX_METRO_PORT="${DETOX_METRO_PORT:-$DEFAULT_DETOX_METRO_PORT}"
|
|
|
|
detect_android_sdk() {
|
|
if [ -n "$ANDROID_SDK_ROOT" ] && [ -d "$ANDROID_SDK_ROOT" ]; then
|
|
return
|
|
fi
|
|
|
|
for candidate in \
|
|
"$HOME/Library/Android/sdk" \
|
|
"$HOME/Android/Sdk"
|
|
do
|
|
if [ -d "$candidate" ]; then
|
|
export ANDROID_SDK_ROOT="$candidate"
|
|
export ANDROID_HOME="$candidate"
|
|
return
|
|
fi
|
|
done
|
|
}
|
|
|
|
# --- Prerequisites check ---
|
|
check_prerequisites() {
|
|
local missing=false
|
|
|
|
if [ "$PLATFORM" = "android" ]; then
|
|
detect_android_sdk
|
|
fi
|
|
|
|
if ! command -v detox &> /dev/null; then
|
|
echo "detox-cli not found. Install with: npm install -g detox-cli"
|
|
missing=true
|
|
fi
|
|
|
|
if [ "$PLATFORM" = "ios" ] && ! command -v applesimutils &> /dev/null; then
|
|
echo "applesimutils not found. Install with: brew tap wix/brew && brew install applesimutils"
|
|
missing=true
|
|
fi
|
|
|
|
if [ "$PLATFORM" = "android" ] && [ -z "$ANDROID_SDK_ROOT" ]; then
|
|
echo "ANDROID_SDK_ROOT is not set and no local Android SDK was found"
|
|
missing=true
|
|
fi
|
|
|
|
if [ "$missing" = true ]; then
|
|
echo ""
|
|
echo "Install missing dependencies and try again."
|
|
exit 1
|
|
fi
|
|
|
|
# Detox framework cache (needed after Xcode updates)
|
|
if [ "$PLATFORM" = "ios" ] && [ ! -d "$HOME/Library/Detox/ios/framework" ]; then
|
|
echo "Detox framework cache not found. Building..."
|
|
npx detox clean-framework-cache && npx detox build-framework-cache
|
|
echo ""
|
|
fi
|
|
|
|
# iOS directory (expo prebuild)
|
|
if [ "$PLATFORM" = "ios" ] && [ ! -d "ios" ]; then
|
|
echo "No ios/ directory found. Running expo prebuild..."
|
|
npx expo prebuild --platform ios
|
|
echo ""
|
|
fi
|
|
}
|
|
TEST_FILTER="${2:-}" # optional test name filter
|
|
|
|
CONFIG=""
|
|
APP_PATH=""
|
|
BUILD_CMD=""
|
|
|
|
case "$PLATFORM" in
|
|
ios)
|
|
CONFIG="ios.sim.debug"
|
|
APP_PATH="ios/build/Build/Products/Debug-iphonesimulator/hanzoguikitchensink.app"
|
|
BUILD_CMD="bun run detox:build:ios"
|
|
;;
|
|
android)
|
|
CONFIG="android.emu.debug"
|
|
APP_PATH="android/app/build/outputs/apk/debug/app-debug.apk"
|
|
BUILD_CMD="bun run detox:build:android"
|
|
;;
|
|
*)
|
|
echo "Unknown platform: $PLATFORM"
|
|
echo "Usage: $0 [ios|android] [test-filter]"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
METRO_PID=""
|
|
STARTED_METRO=false
|
|
|
|
# invoked by the EXIT trap below
|
|
# shellcheck disable=SC2329
|
|
cleanup() {
|
|
if [ "$STARTED_METRO" = true ] && [ -n "$METRO_PID" ]; then
|
|
echo ""
|
|
echo "=== Cleanup: Stopping Metro (PID: $METRO_PID) ==="
|
|
kill "$METRO_PID" 2>/dev/null || true
|
|
# also kill child processes
|
|
pkill -P "$METRO_PID" 2>/dev/null || true
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
is_metro_running() {
|
|
curl -s "http://localhost:${DETOX_METRO_PORT}/status" > /dev/null 2>&1
|
|
}
|
|
|
|
DID_BUILD=false
|
|
|
|
ios_bundle_id() {
|
|
if [ -e "$APP_PATH/Info.plist" ]; then
|
|
/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$APP_PATH/Info.plist" 2>/dev/null && return
|
|
fi
|
|
|
|
node -e "console.log(require('./app.json').expo.ios.bundleIdentifier)" 2>/dev/null
|
|
}
|
|
|
|
android_package_name() {
|
|
node -e "console.log(require('./app.json').expo.android.package)" 2>/dev/null
|
|
}
|
|
|
|
adb_command() {
|
|
if command -v adb > /dev/null 2>&1; then
|
|
command adb "$@"
|
|
return
|
|
fi
|
|
|
|
"$ANDROID_SDK_ROOT/platform-tools/adb" "$@"
|
|
}
|
|
|
|
shell_quote() {
|
|
printf "'%s'" "$(printf "%s" "$1" | sed "s/'/'\\\\''/g")"
|
|
}
|
|
|
|
is_app_installed() {
|
|
if [ "$PLATFORM" = "ios" ]; then
|
|
local bundle_id
|
|
bundle_id="$(ios_bundle_id)"
|
|
|
|
if [ -z "$bundle_id" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local booted_simulators
|
|
if [ -n "$DETOX_DEVICE_UDID" ]; then
|
|
booted_simulators="$DETOX_DEVICE_UDID"
|
|
else
|
|
booted_simulators="$(xcrun simctl list devices booted | sed -n 's/.*(\([A-F0-9-]\{36\}\)) (Booted).*/\1/p')"
|
|
fi
|
|
|
|
if [ -z "$booted_simulators" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local booted_simulator_count
|
|
booted_simulator_count="$(printf "%s\n" "$booted_simulators" | awk 'NF { count++ } END { print count + 0 }')"
|
|
if [ "$booted_simulator_count" -ne 1 ]; then
|
|
return 1
|
|
fi
|
|
|
|
while IFS= read -r udid; do
|
|
if [ -n "$udid" ] && xcrun simctl get_app_container "$udid" "$bundle_id" app > /dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
done <<< "$booted_simulators"
|
|
|
|
return 1
|
|
fi
|
|
|
|
local package_name
|
|
package_name="$(android_package_name)"
|
|
|
|
if [ -z "$package_name" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local android_devices
|
|
android_devices="$(adb_command devices 2>/dev/null | awk 'NR > 1 && $2 == "device" { print $1 }')"
|
|
|
|
if [ -z "$android_devices" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local android_device_count
|
|
android_device_count="$(printf "%s\n" "$android_devices" | awk 'NF { count++ } END { print count + 0 }')"
|
|
if [ "$android_device_count" -ne 1 ]; then
|
|
return 1
|
|
fi
|
|
|
|
while IFS= read -r serial; do
|
|
if [ -n "$serial" ] && adb_command -s "$serial" shell pm path "$package_name" 2>/dev/null | grep -q '^package:'; then
|
|
return 0
|
|
fi
|
|
done <<< "$android_devices"
|
|
|
|
return 1
|
|
}
|
|
|
|
needs_rebuild() {
|
|
# if app doesn't exist, needs build
|
|
if [ ! -e "$APP_PATH" ]; then
|
|
echo "App not found at $APP_PATH"
|
|
return 0
|
|
fi
|
|
|
|
# if FORCE_BUILD is set, needs build
|
|
if [ "$FORCE_BUILD" = "1" ]; then
|
|
echo "FORCE_BUILD=1 set"
|
|
return 0
|
|
fi
|
|
|
|
# check if Podfile.lock is newer than app (pods changed)
|
|
if [ "$PLATFORM" = "ios" ]; then
|
|
if [ "ios/Podfile.lock" -nt "$APP_PATH" ]; then
|
|
echo "Podfile.lock is newer than app"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# check if package.json is newer (deps changed)
|
|
if [ "package.json" -nt "$APP_PATH" ]; then
|
|
echo "package.json is newer than app"
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
echo "========================================"
|
|
echo " Detox Test Runner"
|
|
echo "========================================"
|
|
echo "Platform: $PLATFORM"
|
|
echo "Config: $CONFIG"
|
|
if [ "$PLATFORM" = "ios" ]; then
|
|
echo "Device: ${DETOX_DEVICE:-iPhone 16}"
|
|
else
|
|
echo "Device: ${DETOX_AVD_NAME:-auto-detected Android emulator}"
|
|
fi
|
|
echo "Metro port: $DETOX_METRO_PORT"
|
|
if [ "$PLATFORM" = "android" ]; then
|
|
echo "Android SDK: ${ANDROID_SDK_ROOT:-<unset>}"
|
|
fi
|
|
echo "Test filter: ${TEST_FILTER:-<all tests>}"
|
|
echo ""
|
|
|
|
# step 0: check prerequisites
|
|
echo "=== Step 0: Checking prerequisites ==="
|
|
check_prerequisites
|
|
echo ""
|
|
|
|
# step 1: check if build needed
|
|
if [ "$SKIP_BUILD" != "1" ]; then
|
|
echo "=== Step 1: Checking if build needed ==="
|
|
if needs_rebuild; then
|
|
echo "Building app..."
|
|
$BUILD_CMD
|
|
DID_BUILD=true
|
|
else
|
|
echo "App is up to date, skipping build"
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# step 2: start metro if needed
|
|
if [ "$SKIP_METRO" != "1" ]; then
|
|
echo "=== Step 2: Metro bundler ==="
|
|
if is_metro_running; then
|
|
echo "Metro already running on port $DETOX_METRO_PORT"
|
|
else
|
|
echo "Starting Metro on port $DETOX_METRO_PORT..."
|
|
EXPO_NO_TELEMETRY=true npx expo start --dev-client --offline --port "$DETOX_METRO_PORT" > /tmp/metro-detox.log 2>&1 &
|
|
METRO_PID=$!
|
|
STARTED_METRO=true
|
|
|
|
# wait for metro to be ready (up to 60s)
|
|
echo -n "Waiting for Metro to start"
|
|
for _ in {1..60}; do
|
|
if is_metro_running; then
|
|
echo " ready!"
|
|
break
|
|
fi
|
|
echo -n "."
|
|
sleep 1
|
|
done
|
|
|
|
if ! is_metro_running; then
|
|
echo " FAILED"
|
|
echo "Metro failed to start. Check /tmp/metro-detox.log:"
|
|
tail -20 /tmp/metro-detox.log
|
|
exit 1
|
|
fi
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# step 3: run detox tests
|
|
echo "=== Step 3: Running Detox tests ==="
|
|
|
|
# use --reuse only if the app is already installed on the active device
|
|
if [ "$DID_BUILD" = true ]; then
|
|
echo "Fresh build detected, will reinstall app on simulator"
|
|
DETOX_ARGS=(-c "$CONFIG" --maxWorkers 1)
|
|
elif is_app_installed; then
|
|
DETOX_ARGS=(-c "$CONFIG" --reuse --maxWorkers 1)
|
|
else
|
|
echo "App is not installed on the active test device, will install before testing"
|
|
DETOX_ARGS=(-c "$CONFIG" --maxWorkers 1)
|
|
fi
|
|
|
|
# add headless flag if requested
|
|
if [ "$HEADLESS" = "1" ]; then
|
|
DETOX_ARGS+=(--headless)
|
|
fi
|
|
|
|
if [ -n "$TEST_FILTER" ]; then
|
|
QUOTED_TEST_FILTER="$(shell_quote "$TEST_FILTER")"
|
|
if [ -f "$TEST_FILTER" ] || [[ "$TEST_FILTER" == *".test."* ]] || [[ "$TEST_FILTER" == e2e/* ]]; then
|
|
DETOX_ARGS+=("$QUOTED_TEST_FILTER")
|
|
else
|
|
DETOX_ARGS+=(-t "$QUOTED_TEST_FILTER")
|
|
fi
|
|
fi
|
|
|
|
set +e
|
|
DETOX_METRO_PORT="$DETOX_METRO_PORT" npx detox test "${DETOX_ARGS[@]}"
|
|
EXIT_CODE=$?
|
|
set -e
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
if [ $EXIT_CODE -eq 0 ]; then
|
|
echo " Tests passed!"
|
|
else
|
|
echo " Tests failed (exit code: $EXIT_CODE)"
|
|
fi
|
|
echo "========================================"
|
|
|
|
exit $EXIT_CODE
|